From 26ea17517d89aa8904dffd6cf6d7bab3b44bee39 Mon Sep 17 00:00:00 2001 From: ThomasBreuer Date: Thu, 8 Jan 2026 18:26:36 +0100 Subject: [PATCH 1/4] add `ConformalSymplecticGroup` and some generalizations Add `ConformalSymplecticGroup`, essentially analogous to the implementation of `SymplecticGroup`: - introduce a plain function `ConformalSymplecticGroup` and a constructor `ConformalSymplecticGroupCons`, - install various methods for `ConformalSymplecticGroupCons`, - introduce a property `IsFullSubgroupGLRespectingBilinearFormUpToScalars` that is set in the groups returned by `ConformalSymplecticGroup`, - install a `\in` method for a matrix and a group in `IsFullSubgroupGLRespectingBilinearFormUpToScalars`, - add tests for the new code. The proposed implementation differs from that of `SymplecticGroup` and is intended as a pattern for changing the implementation of `SymplecticGroup` and the other classical groups, and for adding the other types of conformal groups. These are the differences: - Do *not* delegate from the variants where a finite field is given to the variants with given size of the field, but the other way round. This way, it will be possible to use elements from `StandardFiniteField( p, n )` instead of `GF( p, n )` in the matrices. - We might recommend to choose an `IsMatrixObj` representation for the matrices if the entries are from `StandardFiniteField( p, n )`. For that (and also in general), an "experimental" global option `ConstructingFilter` is supported that prescribes the internal format of the matrices. Currently one can use for example `IsPlistRepMatrix` as the value for this option, such that one can make experiments with matrix groups whose elements are in `IsMatrixObj`. More useful `IsMatrixObj` representations will hopefully become available. (Note: We can view `IsPlistRep` as the default for the `ConstructingFilter` option, but then the matrices need not be in `IsPlistRep` because the `ImmutableMatrix` calls in the code convert the generators to `IsGF2MatrixRep` or `Is8BitMatrixRep` if possible.) Once we decide about the setup that we want for `ConformalSymplecticGroup`, the implementation of `SymplecticGroup` etc. in the GAP library and the relevant methods in the Forms package can be rewritten accordingly. --- init.g | 1 + lib/conformal.gd | 152 ++++++++++++++++ lib/conformal.gi | 412 ++++++++++++++++++++++++++++++++++++++++++ read.g | 1 + tst/adv/conformal.tst | 63 +++++++ 5 files changed, 629 insertions(+) create mode 100644 lib/conformal.gd create mode 100644 lib/conformal.gi create mode 100644 tst/adv/conformal.tst diff --git a/init.g b/init.g index 6f21851..d01ab28 100644 --- a/init.g +++ b/init.g @@ -14,3 +14,4 @@ ReadPackage("forms","lib/forms.gd"); ReadPackage("forms","lib/recognition.gd"); +ReadPackage("forms","lib/conformal.gd"); diff --git a/lib/conformal.gd b/lib/conformal.gd new file mode 100644 index 0000000..1151306 --- /dev/null +++ b/lib/conformal.gd @@ -0,0 +1,152 @@ +############################################################################# +## +#A InvariantBilinearFormUpToScalars( ) +## +## <#GAPDoc Label="InvariantBilinearFormUpToScalars"> +## +## +## +## +## This attribute describes a bilinear form that is invariant up to scalars +## under the matrix group matgrp. +## The form is given by a record with the component matrix +## which is a matrix J such that for every generator g of +## matgrp the equation g \cdot J \cdot g^{tr} = \lambda(g) J +## holds, for \lambda(g) in the +## value of matgrp. +## +## +## <#/GAPDoc> +## +DeclareAttribute( "InvariantBilinearFormUpToScalars", IsMatrixGroup ); + + +############################################################################# +## +#P IsFullSubgroupGLRespectingBilinearFormUpToScalars( ) +## +## <#GAPDoc Label="IsFullSubgroupGLRespectingBilinearFormUpToScalars"> +## +## +## +## +## This property tests whether the matrix group matgrp is the full +## subgroup of GL respecting, up to scalars, the form stored as the value of +## for matgrp. +## +## +## <#/GAPDoc> +## +DeclareProperty( "IsFullSubgroupGLRespectingBilinearFormUpToScalars", + IsMatrixGroup ); + +InstallTrueMethod( IsGroup, + IsFullSubgroupGLRespectingBilinearFormUpToScalars ); + + +############################################################################# +## +#O ConformalSymplecticGroupCons( ,
) +#O ConformalSymplecticGroupCons( , ) +#O ConformalSymplecticGroupCons( , ) +#O ConformalSymplecticGroupCons( , , ) +#O ConformalSymplecticGroupCons( , , , ) +#O ConformalSymplecticGroupCons( , , , ) +#O ConformalSymplecticGroupCons( , , , ) +#O ConformalSymplecticGroupCons( , , ) +#O ConformalSymplecticGroupCons( , , , ) +#O ConformalSymplecticGroupCons( , , , ) +#O ConformalSymplecticGroupCons( , , , ) +## +DeclareConstructor( "ConformalSymplecticGroupCons", [ IsGroup, IsBilinearForm ] ); +DeclareConstructor( "ConformalSymplecticGroupCons", [ IsGroup, IsMatrixOrMatrixObj ] ); +DeclareConstructor( "ConformalSymplecticGroupCons", [ IsGroup, IsGroup ] ); +DeclareConstructor( "ConformalSymplecticGroupCons", [ IsGroup, IsPosInt, IsRing ] ); +DeclareConstructor( "ConformalSymplecticGroupCons", [ IsGroup, IsPosInt, IsRing, IsBilinearForm ] ); +DeclareConstructor( "ConformalSymplecticGroupCons", [ IsGroup, IsPosInt, IsRing, IsMatrixOrMatrixObj ] ); +DeclareConstructor( "ConformalSymplecticGroupCons", [ IsGroup, IsPosInt, IsRing, IsGroup ] ); +DeclareConstructor( "ConformalSymplecticGroupCons", [ IsGroup, IsPosInt, IsPosInt ] ); +DeclareConstructor( "ConformalSymplecticGroupCons", [ IsGroup, IsPosInt, IsPosInt, IsBilinearForm ] ); +DeclareConstructor( "ConformalSymplecticGroupCons", [ IsGroup, IsPosInt, IsPosInt, IsMatrixOrMatrixObj ] ); +DeclareConstructor( "ConformalSymplecticGroupCons", [ IsGroup, IsPosInt, IsPosInt, IsGroup ] ); + + +############################################################################# +## +#F ConformalSymplecticGroup( [, ], [, ] ) conf. sympl. gp. +#F ConformalSymplecticGroup( [, ], [, ] ) conf. sympl. gp. +#F ConformalSymplecticGroup( [, ] ) conformal symplectic group +#F CSp( [, ], [, ] ) conformal symplectic group +#F CSp( [, ], [, ] ) conformal symplectic group +#F CSp( [, ] ) conformal symplectic group +## +## <#GAPDoc Label="ConformalSymplecticGroup"> +## +## ConformalSymplecticGroup +## +## +## +## +## +## +## +## +## constructs a group isomorphic to the conformal symplectic group +## CSp( d, R ) of those d \times d +## matrices over the ring R or the field with q elements, +## respectively, +## that respect a fixed nondegenerate symplectic form up to scalars, +## in the category given by the filter filt. +##

+## Currently only finite fields R are supported. +##

+## If filt is not given it defaults to +## , +## and the returned group is the conformal symplectic group itself. +## Another supported value for filt is +## ; +## in this case, the argument form is not supported. +##

+## If the arguments describe a matrix group over a finite field then +## the desired bilinear form can be specified via form, +## which can be either a matrix +## or a form object in +## or a group with stored +## or +## value +## (and then this form is taken). +##

+## A given form determines and d, and also R +## except if form is a matrix that does not store its +## value. +## These parameters can be entered, and an error is signalled if they do +## not fit to the given form. +##

+## If form is not given then a default is chosen as described in the +## introduction to Section . +##

+## g:= ConformalSymplecticGroup( 4, 2 ); +## CSp(4,2) +## gap> Size( g ); +## 720 +## gap> StructureDescription( g ); +## "S6" +## gap> ConformalSymplecticGroup( IsPermGroup, 4, 2 ); +## Perm_CSp(4,2) +## ]]> +## +## +## <#/GAPDoc> +## +DeclareGlobalFunction( "ConformalSymplecticGroup" ); + +DeclareSynonym( "CSp", ConformalSymplecticGroup ); + diff --git a/lib/conformal.gi b/lib/conformal.gi new file mode 100644 index 0000000..4792ee3 --- /dev/null +++ b/lib/conformal.gi @@ -0,0 +1,412 @@ + +# auxiliary function analogous to `DescribesInvariantBilinearForm` +BindGlobal( "DescribesInvariantBilinearFormUpToScalars", + obj -> IsMatrixOrMatrixObj( obj ) or + IsBilinearForm( obj ) or + ( IsGroup( obj ) and HasInvariantBilinearForm( obj ) ) or + ( IsGroup( obj ) and HasInvariantBilinearFormUpToScalars( obj ) ) ); + + +############################################################################# +## +#F ConformalSymplecticGroup( [, ], [, ] ) conf. sympl. gp. +#F ConformalSymplecticGroup( [, ], [, ] ) conf. sympl. gp. +#F ConformalSymplecticGroup( [, ] ) conformal symplectic group +#F CSp( [, ], [, ] ) conformal symplectic group +#F CSp( [, ], [, ] ) conformal symplectic group +#F CSp( [, ] ) conformal symplectic group +## +InstallGlobalFunction( ConformalSymplecticGroup, function ( arg ) + local filt, form; + + if IsFilter( First( arg ) ) then + filt:= Remove( arg, 1 ); + else + filt:= IsMatrixGroup; + fi; + if DescribesInvariantBilinearFormUpToScalars( Last( arg ) ) then + # interpret this argument (matrix or form or group with stored form) + # as "up to scalars" + form:= Remove( arg ); + if Length( arg ) = 0 then + # ( [, ] ) + return ConformalSymplecticGroupCons( filt, form ); + elif Length( arg ) = 2 and IsPosInt( arg[1] ) + and ( IsRing( arg[2] ) or IsPosInt( arg[2] ) ) then + # ( [, ], , ) or ( [, ], , ) + return ConformalSymplecticGroupCons( filt, arg[1], arg[2], form ); + fi; + elif Length( arg ) = 2 and IsPosInt( arg[1] ) + and ( IsRing( arg[2] ) or IsPosInt( arg[2] ) ) then + # ( [, ], ) or ( [, ], ) + return ConformalSymplecticGroupCons( filt, arg[1], arg[2] ); + fi; + Error( "usage: ConformalSymplecticGroup( [, ], [, ] )\n", + "or ConformalSymplecticGroup( [, ], [, ] )\n", + "or ConformalSymplecticGroup( [, ] )" ); +end ); + + +############################################################################# +## +#M ConformalSymplecticGroupCons( , , ) +## +InstallMethod( ConformalSymplecticGroupCons, + "matrix group for dimension and finite field", + [ "IsMatrixGroup and IsFinite", + "IsPosInt", + "IsField and IsFinite" ], + function( filter, d, F ) + local q, z, o, filt, g, mat1, mat2, i, mat3, size, qi, c; + + # the dimension must be even + if d mod 2 = 1 then + Error( "the dimension must be even" ); + fi; + q:= Size( F ); + z:= PrimitiveRoot( F ); + o:= One( F ); + + # Decide about the internal representation of group generators. + filt:= ValueOption( "ConstructingFilter" ); + if filt = fail then + filt:= IsPlistRep; + fi; + + # if the dimension is two it is a general linear group + if d = 2 then + g:= GL( 2, F ); +#T TODO/Note: +#T Currently `filt` is ignored here. +#T This will be fixed automatically as soon as +#T `GL` also supports the global option. + c:= List( GeneratorsOfGroup( g ), x -> Matrix( filt, F, x ) ); + c:= GroupWithGenerators( c ); + SetDimensionOfMatrixGroup( c, d ); + SetFieldOfMatrixGroup( c, F ); + SetName( c, Name( g ) ); + SetSize( c, Size( g ) ); + g:= c; + else + # CSp(4,2) + if d = 4 and q = 2 then + mat1:= Matrix( filt, F, [1,0,1,1,1,0,0,1,0,1,0,1,1,1,1,1] * o, 4 ); + mat2:= Matrix( filt, F, [0,0,1,0,1,0,0,0,0,0,0,1,0,1,0,0] * o, 4 ); + + # CSp(d,q) + else + mat1 := IdentityMatrix( filt, F, d ); + mat2 := ZeroMatrix( filt, F, d, d ); + for i in [ 2 .. d/2 ] do mat2[i,i-1]:= o; od; + for i in [ d/2+1 .. d-1 ] do mat2[i,i+1]:= o; od; + + if q mod 2 = 1 then + mat1[ 1, 1] := z; + mat1[ d, d] := z^-1; + mat2[ 1, 1] := o; + mat2[ 1,d/2+1] := o; + mat2[d-1, d/2] := o; + mat2[ d, d/2] := -o; + + elif q <> 2 then + mat1[ 1, 1] := z; + mat1[ d/2, d/2] := z; + mat1[d/2+1,d/2+1] := z^-1; + mat1[ d, d] := z^-1; + mat2[ 1,d/2-1] := o; + mat2[ 1, d/2] := o; + mat2[ 1,d/2+1] := o; + mat2[d/2+1, d/2] := o; + mat2[ d, d/2] := o; + + else + mat1[ 1, d/2] := o; + mat1[ 1, d] := o; + mat1[d/2+1, d] := o; + mat2[ 1,d/2+1] := o; + mat2[ d, d/2] := o; + fi; + fi; + + mat3:= IdentityMatrix( filt, F, d ); + for i in [ 1 .. d/2 ] do + mat3[i, i]:= z; + od; + + mat1:= ImmutableMatrix( F, mat1, true ); + mat2:= ImmutableMatrix( F, mat2, true ); + mat3:= ImmutableMatrix( F, mat3, true ); + + # avoid to call 'Group' because this would check invertibility ... + g:= GroupWithGenerators( [ mat1, mat2, mat3 ] ); + SetName( g, Concatenation( "CSp(", String(d), ",", String(q), ")" ) ); + SetDimensionOfMatrixGroup( g, d ); + SetFieldOfMatrixGroup( g, F ); + + # add the size + size := 1; + qi := 1; + for i in [ 1 .. d/2 ] do + qi := qi * q^2; + size := size * (qi-1); + od; + SetSize( g, q^((d/2)^2) * size * (q-1) ); + fi; + + # construct the form + c:= ZeroMatrix( filt, F, d, d ); + for i in [ 1 .. d/2 ] do + c[i,d-i+1]:= o; + c[d/2+i,d/2-i+1]:= -o; + od; + SetInvariantBilinearFormUpToScalars( g, + rec( matrix:= ImmutableMatrix( F, c, true ) ) ); + + SetIsFullSubgroupGLRespectingBilinearFormUpToScalars( g, true ); + + # and return + return g; +end ); + + +############################################################################# +## +#M ConformalSymplecticGroupCons( , , ) +## +InstallMethod( ConformalSymplecticGroupCons, + "matrix group for dimension and finite field size", + [ "IsMatrixGroup and IsFinite", + "IsPosInt", + "IsPosInt" ], + { filt, n, q } -> ConformalSymplecticGroupCons( filt, n, GF(q) ) ); + + +############################################################################# +## +#M ConformalSymplecticGroupCons( , ) +## +InstallMethod( ConformalSymplecticGroupCons, + "matrix group for matrix of form", + [ "IsMatrixGroup and IsFinite", "IsMatrixOrMatrixObj" ], + { filt, mat } -> ConformalSymplecticGroupCons( filt, + BilinearFormByMatrix( mat, BaseDomain( mat ) ) ) ); + +InstallMethod( ConformalSymplecticGroupCons, + "matrix group for group with form", + [ "IsMatrixGroup and IsFinite", "IsGroup and HasInvariantBilinearForm" ], + { filt, G } -> ConformalSymplecticGroupCons( filt, + BilinearFormByMatrix( + InvariantBilinearForm( G ).matrix, + FieldOfMatrixGroup( G ) ) ) ); + +InstallMethod( ConformalSymplecticGroupCons, + "matrix group for group with form", + [ "IsMatrixGroup and IsFinite", + "IsGroup and HasInvariantBilinearFormUpToScalars" ], + { filt, G } -> ConformalSymplecticGroupCons( filt, + BilinearFormByMatrix( + InvariantBilinearFormUpToScalars( G ).matrix, + FieldOfMatrixGroup( G ) ) ) ); + +InstallMethod( ConformalSymplecticGroupCons, + "matrix group for form", + [ "IsMatrixGroup and IsFinite", "IsBilinearForm" ], + { filt, form } -> ConformalSymplecticGroupCons( filt, + NumberRows( form!.matrix ), + form!.basefield, form ) ); + + +############################################################################# +## +#M ConformalSymplecticGroupCons( , , , ) +## +InstallMethod( ConformalSymplecticGroupCons, + "matrix group for dimension, finite field size, matrix of form", + [ "IsMatrixGroup and IsFinite", + "IsPosInt", + "IsPosInt", + "IsMatrixOrMatrixObj" ], + { filt, d, q, mat } -> ConformalSymplecticGroupCons( filt, d, GF(q), + BilinearFormByMatrix( mat, GF(q) ) ) ); + +InstallMethod( ConformalSymplecticGroupCons, + "matrix group for dimension, finite field size, group with form", + [ "IsMatrixGroup and IsFinite", + "IsPosInt", + "IsPosInt", + "IsGroup and HasInvariantBilinearForm" ], + { filt, d, q, G } -> ConformalSymplecticGroupCons( filt, d, GF(q), + BilinearFormByMatrix( + InvariantBilinearForm( G ).matrix, GF(q) ) ) ); + +InstallMethod( ConformalSymplecticGroupCons, + "matrix group for dimension, finite field size, group with form", + [ "IsMatrixGroup and IsFinite", + "IsPosInt", + "IsPosInt", + "IsGroup and HasInvariantBilinearFormUpToScalars" ], + { filt, d, q, G } -> ConformalSymplecticGroupCons( filt, d, GF(q), + BilinearFormByMatrix( + InvariantBilinearFormUpToScalars( G ).matrix, GF(q) ) ) ); + +InstallMethod( ConformalSymplecticGroupCons, + "matrix group for dimension, finite field size, form", + [ "IsMatrixGroup and IsFinite", + "IsPosInt", + "IsPosInt", + "IsBilinearForm" ], + { filt, d, q, form } -> ConformalSymplecticGroupCons( filt, d, GF(q), form ) ); + + +############################################################################# +## +#M ConformalSymplecticGroupCons( , , , ) +## +InstallMethod( ConformalSymplecticGroupCons, + "matrix group for dimension, finite field, matrix of form", + [ "IsMatrixGroup and IsFinite", + "IsPosInt", + "IsField and IsFinite", + "IsMatrixOrMatrixObj" ], + { filt, d, F, form } -> ConformalSymplecticGroupCons( filt, d, F, + BilinearFormByMatrix( form, F ) ) ); + +InstallMethod( ConformalSymplecticGroupCons, + "matrix group for dimension, finite field, group with form", + [ "IsMatrixGroup and IsFinite", + "IsPosInt", + "IsField and IsFinite", + "IsGroup and HasInvariantBilinearForm" ], + { filt, d, F, G } -> ConformalSymplecticGroupCons( filt, d, F, + BilinearFormByMatrix( + InvariantBilinearForm( G ).matrix, F ) ) ); + +InstallMethod( ConformalSymplecticGroupCons, + "matrix group for dimension, finite field, group with form", + [ "IsMatrixGroup and IsFinite", + "IsPosInt", + "IsField and IsFinite", + "IsGroup and HasInvariantBilinearFormUpToScalars" ], + { filt, d, F, G } -> ConformalSymplecticGroupCons( filt, d, F, + BilinearFormByMatrix( + InvariantBilinearFormUpToScalars( G ).matrix, F ) ) ); + +InstallMethod( ConformalSymplecticGroupCons, + "matrix group for dimension, finite field, form", + [ "IsMatrixGroup and IsFinite", + "IsPosInt", + "IsField and IsFinite", + "IsBilinearForm" ], + function( filt, d, F, form ) + local g, stored, wanted, mat1, mat2, mat, matinv, gens, gg; + + # Create the default generators and form. + g:= ConformalSymplecticGroupCons( filt, d, F ); + stored:= InvariantBilinearFormUpToScalars( g ).matrix; + + # If the prescribed form fits then just return. + if stored = form!.matrix then + return g; + fi; + + # Compute a base change matrix. + # (Check that the canonical forms are equal.) + wanted:= BilinearFormByMatrix( stored, F ); + mat1:= BaseChangeToCanonical( form ); + mat2:= BaseChangeToCanonical( wanted ); + if mat1 * form!.matrix * TransposedMat( mat1 ) <> + mat2 * stored * TransposedMat( mat2 ) then + Error( "canonical forms of and differ" ); + fi; + mat:= mat2^-1 * mat1; + matinv:= mat^-1; + + # Create the group w.r.t. the prescribed form. + gens:= List( GeneratorsOfGroup( g ), + x -> Matrix( matinv * x * mat, stored ) ); + gg:= GroupWithGenerators( gens ); + + UseIsomorphismRelation( g, gg ); + + if HasName( g ) then + SetName( gg, Name( g ) ); + fi; + + SetInvariantBilinearFormUpToScalars( gg, + rec( matrix:= Matrix( form!.matrix, stored ) ) ); + + if HasIsFullSubgroupGLRespectingBilinearFormUpToScalars( g ) then + SetIsFullSubgroupGLRespectingBilinearFormUpToScalars( gg, + IsFullSubgroupGLRespectingBilinearFormUpToScalars( g ) ); + fi; + + return gg; +end ); + + +############################################################################# +## +## Support `IsPermGroup` as first argument in `ConformalSymplecticGroup`. +## +PermConstructor( ConformalSymplecticGroupCons, + [ IsPermGroup, IsInt, IsObject ], + IsMatrixGroup and IsFinite); + + +############################################################################# +## +#M \in( , ) . . . . . . . . membership test method based on form +## +InstallMethod( \in, "respecting bilinear form", IsElmsColls, + [ "IsMatrixOrMatrixObj", + "IsFullSubgroupGLRespectingBilinearFormUpToScalars" ], + {} -> RankFilter( IsHandledByNiceMonomorphism ), # override nice mon. method + function( mat, G ) + local inv; + + if not IsSubset( FieldOfMatrixGroup( G ), + FieldOfMatrixList( [ mat ] ) ) then + return false; + fi; + inv:= InvariantBilinearFormUpToScalars( G ).matrix; + return _IsEqualModScalars( inv, mat * inv * TransposedMat( mat ) ); +end ); + + +############################################################################# +## +## The following methods are currently needed to make the code work +## in case one creates groups whose elements are in `IsMatrixObj`. +## Eventually we want to get rid of them (or or to add something similar +## to the GAP library). +## + +# Strictly speaking, the following is not correct, +# according to the definition of `DegreeFFE`. +# Eventually we should fix the use of `FieldOfMatrixList` +# and `FieldOfMatrixGroup`, then `DegreeFFE` will not be important anymore. +InstallOtherMethod( DegreeFFE, + [ "IsMatrixObj and IsFFECollColl" ], + mat -> DegreeOverPrimeField( BaseDomain( mat ) ) ); + +# This is a really ugly hack. +# Without this method, multiplying a matrix object with a matrix +# is possible but yields the matrix product of with the *transposed* +# of . +# (This is because GAP regards as a scalar and computes the list of +# products of with the rows of .) +InstallOtherMethod( \*, + [ "IsMatrixObj", "IsMatrix" ], + { matobj, mat } -> Unpack( matobj ) * mat ); + + +InstallOtherMethod( BilinearFormByMatrix, + "for a ffe matrix object and a field", + [ "IsMatrixObj and IsFFECollColl", "IsField and IsFinite" ], + { m, F } -> BilinearFormByMatrix( Unpack( m ), F ) ); + +InstallOtherMethod( BilinearFormByMatrix, + "for a ffe matrix object", + [ "IsMatrixObj and IsFFECollColl" ], + m -> BilinearFormByMatrix( Unpack( m ) ) ); + diff --git a/read.g b/read.g index abe27b6..08fb100 100644 --- a/read.g +++ b/read.g @@ -16,3 +16,4 @@ ReadPackage("forms", "lib/forms.gi"); ReadPackage("forms", "lib/recognition.gi"); ReadPackage("forms", "lib/classic.gi"); ReadPackage("forms", "lib/recognition_new.gi"); +ReadPackage("forms", "lib/conformal.gi"); diff --git a/tst/adv/conformal.tst b/tst/adv/conformal.tst new file mode 100644 index 0000000..0d913ba --- /dev/null +++ b/tst/adv/conformal.tst @@ -0,0 +1,63 @@ +#@local is_equal, q, F, d, filt, g, stored, pi, permmat, form, gg, pg + +gap> START_TEST( "Forms: conformal.tst" ); + +# Provide an auxiliary function (until GAP's '=' gets fast). +gap> is_equal:= function( G1, G2 ) +> return IsSubset( G1, GeneratorsOfGroup( G2 ) ) and +> IsSubset( G2, GeneratorsOfGroup( G1 ) ); +> end;; + +# Test the creation of conformal symplectic groups. +gap> for q in [ 2, 3, 4, 5, 7, 8, 9, 11, 13, 16, 17, 19, 23, 25 ] do +> F:= GF(q); +> for d in [ 2, 4 .. 8 ] do +> for filt in [ IsPlistRep, IsPlistMatrixRep ] do +> PushOptions( rec( ConstructingFilter:= filt ) ); +> +> g:= ConformalSymplecticGroup( d, q ); +> stored:= InvariantBilinearFormUpToScalars( g ).matrix; +> pi:= Matrix( PermutationMat( (1,2), d, F ), stored ); +> permmat:= pi * stored * TransposedMat( pi ); +> form:= BilinearFormByMatrix( stored, F ); +> gg:= ConformalSymplecticGroup( d, q, permmat ); +> if not ( is_equal( g, ConformalSymplecticGroup( g ) ) and +> ( is_equal( g, ConformalSymplecticGroup( stored ) ) or +> BaseDomain( stored ) <> F ) and +> is_equal( g, ConformalSymplecticGroup( d, F ) ) and +> is_equal( g, ConformalSymplecticGroup( form ) ) and +> is_equal( g, ConformalSymplecticGroup( d, q, g ) ) and +> is_equal( g, ConformalSymplecticGroup( d, q, stored ) ) and +> is_equal( g, ConformalSymplecticGroup( d, q, form ) ) and +> is_equal( g, ConformalSymplecticGroup( d, F, g ) ) and +> is_equal( g, ConformalSymplecticGroup( d, F, stored ) ) and +> is_equal( g, ConformalSymplecticGroup( d, F, form ) ) and +> IsSubset( gg, GeneratorsOfGroup( gg ) ) and +> IsSubset( g, List( GeneratorsOfGroup( gg ), x -> x^pi ) ) ) then +> Error( "problem with CSp(", d, ",", q, ")" ); +> fi; +> +> if Size( g ) < 10^7 and filt = IsPlistRep then +> #TODO: Make this work for `IsPlistMatrixRep` +> pg:= ConformalSymplecticGroup( IsPermGroup, d, q ); +> if Size( g ) <> Size( pg ) then +> Error( "problem with CSp(IsPermGroup, ", d, ",", q, ")" ); +> fi; +> fi; +> +> #TODO: Once `SymplecticGroup` supports matrix objects, +> # the following will work. +> # sp:= SymplecticGroup( d, q, permmat ); +> # g:= ConformalSymplecticGroup( sp ); +> # if not IsSubset( g, sp ) then +> # Error( "problem with CSp(", d, ",", q, ")" ); +> # fi; +> +> PushOptions( rec( ConstructingFilter:= fail ) ); +> od; +> od; +> od; + +## +gap> STOP_TEST( "conformal.tst" ); + From 5c3aefdb98a97c24faf790e058b9e23d971912ec Mon Sep 17 00:00:00 2001 From: ThomasBreuer Date: Thu, 29 Jan 2026 17:37:38 +0100 Subject: [PATCH 2/4] reworked the proposal Now the declaration of `CSp` and those methods for it that do not involve forms will be added to the GAP library. Also, the implementation of `Sp` in GAP admits already an optional filter that defines the representation of the matrices, and the delegation between its methods has been changed as for `CSp` (the methods with argument `q` delegate to those with argument `GF(q)`, not the other way round). For this pull request, this means that - the files involving code and tests involving `CSp` are read only if GAP provides the variable `CSp`, - the delegation between methods for `Sp` that are provided by the Forms package has been changed as well, - the methods in the Forms package now support a `baseDomain` component in the attributes describing invariant forms, - calls of `FieldOfMatrixGroup` are replaced by access to the new `baseDomain` component of form records where applicable (for backwards compatibility with older GAP versions, the helper function `Forms_FieldOfDefinition` calls `FieldOfMatrixGroup` if no `baseDomain` component is available). --- init.g | 5 +- lib/classic.gi | 62 ++++++----- lib/conformal.gd | 148 ++----------------------- lib/conformal.gi | 249 +++--------------------------------------- read.g | 5 +- tst/adv/classic.tst | 59 ++++++---- tst/adv/conformal.tst | 38 ++++--- tst/testall.g | 5 + 8 files changed, 132 insertions(+), 439 deletions(-) diff --git a/init.g b/init.g index d01ab28..7fcd697 100644 --- a/init.g +++ b/init.g @@ -14,4 +14,7 @@ ReadPackage("forms","lib/forms.gd"); ReadPackage("forms","lib/recognition.gd"); -ReadPackage("forms","lib/conformal.gd"); + +if IsBound( ConformalSymplecticGroup ) then + ReadPackage("forms","lib/conformal.gd"); +fi; diff --git a/lib/classic.gi b/lib/classic.gi index 7bcfc71..c1cee90 100644 --- a/lib/classic.gi +++ b/lib/classic.gi @@ -109,7 +109,7 @@ BindGlobal("Forms_OrthogonalGroup", fi; mat:= matinv * InvariantBilinearForm( g ).matrix * TransposedMat( matinv ); - SetInvariantBilinearForm( gg, rec( matrix:= mat ) ); + SetInvariantBilinearForm( gg, rec( matrix:= mat, baseDomain:= gf ) ); if Characteristic( gf ) <> 2 and HasIsFullSubgroupGLorSLRespectingBilinearForm( g ) then SetIsFullSubgroupGLorSLRespectingBilinearForm( gg, @@ -832,8 +832,8 @@ end ); ############################################################################# ## #O SymplecticGroupCons( , ) -#O SymplecticGroupCons( , , , ) #O SymplecticGroupCons( , , , ) +#O SymplecticGroupCons( , , , ) ## ## 'SymplecticGroup' is a plain function that is defined in the GAP ## library. @@ -845,10 +845,10 @@ Perform( [ IsMatrixOrMatrixObj, IsBilinearForm, IsGroup and HasInvariantBilinearForm ], function( obj ) DeclareConstructor( "SymplecticGroupCons", [ IsGroup, obj ] ); - DeclareConstructor( "SymplecticGroupCons", - [ IsGroup, IsPosInt, IsPosInt, obj ] ); DeclareConstructor( "SymplecticGroupCons", [ IsGroup, IsPosInt, IsRing, obj ] ); + DeclareConstructor( "SymplecticGroupCons", + [ IsGroup, IsPosInt, IsPosInt, obj ] ); end ); @@ -875,7 +875,7 @@ InstallMethod( SymplecticGroupCons, [ IsMatrixGroup and IsFinite, IsBilinearForm ], { filt, form } -> SymplecticGroupCons( filt, NumberRows( form!.matrix ), - Size( form!.basefield ), form ) ); + form!.basefield, form ) ); ############################################################################# @@ -888,7 +888,7 @@ InstallMethod( SymplecticGroupCons, IsPosInt, IsPosInt, IsMatrixOrMatrixObj ], - { filt, d, q, mat } -> SymplecticGroupCons( filt, d, q, + { filt, d, q, mat } -> SymplecticGroupCons( filt, d, GF(q), BilinearFormByMatrix( mat, GF(q) ) ) ); InstallMethod( SymplecticGroupCons, @@ -897,7 +897,7 @@ InstallMethod( SymplecticGroupCons, IsPosInt, IsPosInt, IsGroup and HasInvariantBilinearForm ], - { filt, d, q, G } -> SymplecticGroupCons( filt, d, q, + { filt, d, q, G } -> SymplecticGroupCons( filt, d, GF(q), BilinearFormByMatrix( InvariantBilinearForm( G ).matrix, GF(q) ) ) ); @@ -907,15 +907,32 @@ InstallMethod( SymplecticGroupCons, IsPosInt, IsPosInt, IsBilinearForm ], - function( filt, d, q, form ) - local g, stored, F, wanted, mat1, mat2, mat, matinv, gens, gg; + { filt, d, q, form } -> SymplecticGroupCons( filt, d, GF(q), form ) ); + + +############################################################################# +## +#M SymplecticGroupCons( , , , ) +## +InstallMethod( SymplecticGroupCons, + "matrix group for dimension, finite field, form", + [ IsMatrixGroup and IsFinite, + IsPosInt, + IsField and IsFinite, + IsBilinearForm ], + function( filt, d, F, form ) + local q, g, stored, form_matrix, wanted, mat1, mat2, mat, matinv, gens, gg; # Create the default generators and form. - g:= SymplecticGroupCons( filt, d, q ); + q:= Size( F ); + g:= SymplecticGroupCons( filt, d, F ); stored:= InvariantBilinearForm( g ).matrix; # If the prescribed form fits then just return. - if stored = form!.matrix then + form_matrix:= Matrix( form!.matrix, stored ); +#T This 'Matrix' call should become unnecessary. +#T For that, the functions used below have to support 'IsMatrixObj' arguments. + if stored = form_matrix then return g; fi; @@ -928,9 +945,9 @@ InstallMethod( SymplecticGroupCons, # Compute a base change matrix. # (Check that the canonical forms are equal.) wanted:= BilinearFormByMatrix( stored, F ); - mat1:= BaseChangeToCanonical( form ); - mat2:= BaseChangeToCanonical( wanted ); - if mat1 * form!.matrix * TransposedMat( mat1 ) <> + mat1:= Matrix( BaseChangeToCanonical( form ), stored ); + mat2:= Matrix( BaseChangeToCanonical( wanted ), stored ); + if mat1 * form_matrix * TransposedMat( mat1 ) <> mat2 * stored * TransposedMat( mat2 ) then Error( "canonical forms of and differ" ); fi; @@ -957,18 +974,13 @@ InstallMethod( SymplecticGroupCons, return gg; end ); - -############################################################################# -## -#M SymplecticGroupCons( , , , ) -## InstallMethod( SymplecticGroupCons, "matrix group for dimension, finite field, matrix of form", [ IsMatrixGroup and IsFinite, IsPosInt, IsField and IsFinite, IsMatrixOrMatrixObj ], - { filt, d, F, form } -> SymplecticGroupCons( filt, d, Size( F ), + { filt, d, F, form } -> SymplecticGroupCons( filt, d, F, BilinearFormByMatrix( form, F ) ) ); InstallMethod( SymplecticGroupCons, @@ -977,14 +989,6 @@ InstallMethod( SymplecticGroupCons, IsPosInt, IsField and IsFinite, IsGroup and HasInvariantBilinearForm ], - { filt, d, F, G } -> SymplecticGroupCons( filt, d, Size( F ), + { filt, d, F, G } -> SymplecticGroupCons( filt, d, F, BilinearFormByMatrix( InvariantBilinearForm( G ).matrix, F ) ) ); - -InstallMethod( SymplecticGroupCons, - "matrix group for dimension, finite field, form", - [ IsMatrixGroup and IsFinite, - IsPosInt, - IsField and IsFinite, - IsBilinearForm ], - { filt, d, F, form } -> SymplecticGroupCons( filt, d, Size( F ), form ) ); diff --git a/lib/conformal.gd b/lib/conformal.gd index 1151306..e943d89 100644 --- a/lib/conformal.gd +++ b/lib/conformal.gd @@ -1,152 +1,28 @@ ############################################################################# ## -#A InvariantBilinearFormUpToScalars( ) +## conformal.gd 'Forms' package ## -## <#GAPDoc Label="InvariantBilinearFormUpToScalars"> -## -## -## -## -## This attribute describes a bilinear form that is invariant up to scalars -## under the matrix group matgrp. -## The form is given by a record with the component matrix -## which is a matrix J such that for every generator g of -## matgrp the equation g \cdot J \cdot g^{tr} = \lambda(g) J -## holds, for \lambda(g) in the -## value of matgrp. -## -## -## <#/GAPDoc> -## -DeclareAttribute( "InvariantBilinearFormUpToScalars", IsMatrixGroup ); - - -############################################################################# -## -#P IsFullSubgroupGLRespectingBilinearFormUpToScalars( ) -## -## <#GAPDoc Label="IsFullSubgroupGLRespectingBilinearFormUpToScalars"> -## -## -## -## -## This property tests whether the matrix group matgrp is the full -## subgroup of GL respecting, up to scalars, the form stored as the value of -## for matgrp. -## -## -## <#/GAPDoc> -## -DeclareProperty( "IsFullSubgroupGLRespectingBilinearFormUpToScalars", - IsMatrixGroup ); - -InstallTrueMethod( IsGroup, - IsFullSubgroupGLRespectingBilinearFormUpToScalars ); - ############################################################################# ## #O ConformalSymplecticGroupCons( , ) #O ConformalSymplecticGroupCons( , ) #O ConformalSymplecticGroupCons( , ) -#O ConformalSymplecticGroupCons( , , ) #O ConformalSymplecticGroupCons( , , , ) #O ConformalSymplecticGroupCons( , , , ) #O ConformalSymplecticGroupCons( , , , ) -#O ConformalSymplecticGroupCons( , , ) #O ConformalSymplecticGroupCons( , , , ) #O ConformalSymplecticGroupCons( , , , ) #O ConformalSymplecticGroupCons( , , , ) ## -DeclareConstructor( "ConformalSymplecticGroupCons", [ IsGroup, IsBilinearForm ] ); -DeclareConstructor( "ConformalSymplecticGroupCons", [ IsGroup, IsMatrixOrMatrixObj ] ); -DeclareConstructor( "ConformalSymplecticGroupCons", [ IsGroup, IsGroup ] ); -DeclareConstructor( "ConformalSymplecticGroupCons", [ IsGroup, IsPosInt, IsRing ] ); -DeclareConstructor( "ConformalSymplecticGroupCons", [ IsGroup, IsPosInt, IsRing, IsBilinearForm ] ); -DeclareConstructor( "ConformalSymplecticGroupCons", [ IsGroup, IsPosInt, IsRing, IsMatrixOrMatrixObj ] ); -DeclareConstructor( "ConformalSymplecticGroupCons", [ IsGroup, IsPosInt, IsRing, IsGroup ] ); -DeclareConstructor( "ConformalSymplecticGroupCons", [ IsGroup, IsPosInt, IsPosInt ] ); -DeclareConstructor( "ConformalSymplecticGroupCons", [ IsGroup, IsPosInt, IsPosInt, IsBilinearForm ] ); -DeclareConstructor( "ConformalSymplecticGroupCons", [ IsGroup, IsPosInt, IsPosInt, IsMatrixOrMatrixObj ] ); -DeclareConstructor( "ConformalSymplecticGroupCons", [ IsGroup, IsPosInt, IsPosInt, IsGroup ] ); - - -############################################################################# -## -#F ConformalSymplecticGroup( [, ], [, ] ) conf. sympl. gp. -#F ConformalSymplecticGroup( [, ], [, ] ) conf. sympl. gp. -#F ConformalSymplecticGroup( [, ] ) conformal symplectic group -#F CSp( [, ], [, ] ) conformal symplectic group -#F CSp( [, ], [, ] ) conformal symplectic group -#F CSp( [, ] ) conformal symplectic group -## -## <#GAPDoc Label="ConformalSymplecticGroup"> -## -## ConformalSymplecticGroup -## -## -## -## -## -## -## -## -## constructs a group isomorphic to the conformal symplectic group -## CSp( d, R ) of those d \times d -## matrices over the ring R or the field with q elements, -## respectively, -## that respect a fixed nondegenerate symplectic form up to scalars, -## in the category given by the filter filt. -##

-## Currently only finite fields R are supported. -##

-## If filt is not given it defaults to -## , -## and the returned group is the conformal symplectic group itself. -## Another supported value for filt is -## ; -## in this case, the argument form is not supported. -##

-## If the arguments describe a matrix group over a finite field then -## the desired bilinear form can be specified via form, -## which can be either a matrix -## or a form object in -## or a group with stored -## or -## value -## (and then this form is taken). -##

-## A given form determines and d, and also R -## except if form is a matrix that does not store its -## value. -## These parameters can be entered, and an error is signalled if they do -## not fit to the given form. -##

-## If form is not given then a default is chosen as described in the -## introduction to Section . -##

-## g:= ConformalSymplecticGroup( 4, 2 ); -## CSp(4,2) -## gap> Size( g ); -## 720 -## gap> StructureDescription( g ); -## "S6" -## gap> ConformalSymplecticGroup( IsPermGroup, 4, 2 ); -## Perm_CSp(4,2) -## ]]> -## -## -## <#/GAPDoc> -## -DeclareGlobalFunction( "ConformalSymplecticGroup" ); - -DeclareSynonym( "CSp", ConformalSymplecticGroup ); - +## Declare the variants involving a bilinear form as an argument. +## +Perform( + [ IsMatrixOrMatrixObj, IsBilinearForm, IsGroup ], + function( obj ) + DeclareConstructor( "ConformalSymplecticGroupCons", [ IsGroup, obj ] ); + DeclareConstructor( "ConformalSymplecticGroupCons", + [ IsGroup, IsPosInt, IsRing, obj ] ); + DeclareConstructor( "ConformalSymplecticGroupCons", + [ IsGroup, IsPosInt, IsPosInt, obj ] ); + end ); diff --git a/lib/conformal.gi b/lib/conformal.gi index 4792ee3..3502fdd 100644 --- a/lib/conformal.gi +++ b/lib/conformal.gi @@ -1,186 +1,3 @@ - -# auxiliary function analogous to `DescribesInvariantBilinearForm` -BindGlobal( "DescribesInvariantBilinearFormUpToScalars", - obj -> IsMatrixOrMatrixObj( obj ) or - IsBilinearForm( obj ) or - ( IsGroup( obj ) and HasInvariantBilinearForm( obj ) ) or - ( IsGroup( obj ) and HasInvariantBilinearFormUpToScalars( obj ) ) ); - - -############################################################################# -## -#F ConformalSymplecticGroup( [, ], [, ] ) conf. sympl. gp. -#F ConformalSymplecticGroup( [, ], [, ] ) conf. sympl. gp. -#F ConformalSymplecticGroup( [, ] ) conformal symplectic group -#F CSp( [, ], [, ] ) conformal symplectic group -#F CSp( [, ], [, ] ) conformal symplectic group -#F CSp( [, ] ) conformal symplectic group -## -InstallGlobalFunction( ConformalSymplecticGroup, function ( arg ) - local filt, form; - - if IsFilter( First( arg ) ) then - filt:= Remove( arg, 1 ); - else - filt:= IsMatrixGroup; - fi; - if DescribesInvariantBilinearFormUpToScalars( Last( arg ) ) then - # interpret this argument (matrix or form or group with stored form) - # as "up to scalars" - form:= Remove( arg ); - if Length( arg ) = 0 then - # ( [, ] ) - return ConformalSymplecticGroupCons( filt, form ); - elif Length( arg ) = 2 and IsPosInt( arg[1] ) - and ( IsRing( arg[2] ) or IsPosInt( arg[2] ) ) then - # ( [, ], , ) or ( [, ], , ) - return ConformalSymplecticGroupCons( filt, arg[1], arg[2], form ); - fi; - elif Length( arg ) = 2 and IsPosInt( arg[1] ) - and ( IsRing( arg[2] ) or IsPosInt( arg[2] ) ) then - # ( [, ], ) or ( [, ], ) - return ConformalSymplecticGroupCons( filt, arg[1], arg[2] ); - fi; - Error( "usage: ConformalSymplecticGroup( [, ], [, ] )\n", - "or ConformalSymplecticGroup( [, ], [, ] )\n", - "or ConformalSymplecticGroup( [, ] )" ); -end ); - - -############################################################################# -## -#M ConformalSymplecticGroupCons( , , ) -## -InstallMethod( ConformalSymplecticGroupCons, - "matrix group for dimension and finite field", - [ "IsMatrixGroup and IsFinite", - "IsPosInt", - "IsField and IsFinite" ], - function( filter, d, F ) - local q, z, o, filt, g, mat1, mat2, i, mat3, size, qi, c; - - # the dimension must be even - if d mod 2 = 1 then - Error( "the dimension must be even" ); - fi; - q:= Size( F ); - z:= PrimitiveRoot( F ); - o:= One( F ); - - # Decide about the internal representation of group generators. - filt:= ValueOption( "ConstructingFilter" ); - if filt = fail then - filt:= IsPlistRep; - fi; - - # if the dimension is two it is a general linear group - if d = 2 then - g:= GL( 2, F ); -#T TODO/Note: -#T Currently `filt` is ignored here. -#T This will be fixed automatically as soon as -#T `GL` also supports the global option. - c:= List( GeneratorsOfGroup( g ), x -> Matrix( filt, F, x ) ); - c:= GroupWithGenerators( c ); - SetDimensionOfMatrixGroup( c, d ); - SetFieldOfMatrixGroup( c, F ); - SetName( c, Name( g ) ); - SetSize( c, Size( g ) ); - g:= c; - else - # CSp(4,2) - if d = 4 and q = 2 then - mat1:= Matrix( filt, F, [1,0,1,1,1,0,0,1,0,1,0,1,1,1,1,1] * o, 4 ); - mat2:= Matrix( filt, F, [0,0,1,0,1,0,0,0,0,0,0,1,0,1,0,0] * o, 4 ); - - # CSp(d,q) - else - mat1 := IdentityMatrix( filt, F, d ); - mat2 := ZeroMatrix( filt, F, d, d ); - for i in [ 2 .. d/2 ] do mat2[i,i-1]:= o; od; - for i in [ d/2+1 .. d-1 ] do mat2[i,i+1]:= o; od; - - if q mod 2 = 1 then - mat1[ 1, 1] := z; - mat1[ d, d] := z^-1; - mat2[ 1, 1] := o; - mat2[ 1,d/2+1] := o; - mat2[d-1, d/2] := o; - mat2[ d, d/2] := -o; - - elif q <> 2 then - mat1[ 1, 1] := z; - mat1[ d/2, d/2] := z; - mat1[d/2+1,d/2+1] := z^-1; - mat1[ d, d] := z^-1; - mat2[ 1,d/2-1] := o; - mat2[ 1, d/2] := o; - mat2[ 1,d/2+1] := o; - mat2[d/2+1, d/2] := o; - mat2[ d, d/2] := o; - - else - mat1[ 1, d/2] := o; - mat1[ 1, d] := o; - mat1[d/2+1, d] := o; - mat2[ 1,d/2+1] := o; - mat2[ d, d/2] := o; - fi; - fi; - - mat3:= IdentityMatrix( filt, F, d ); - for i in [ 1 .. d/2 ] do - mat3[i, i]:= z; - od; - - mat1:= ImmutableMatrix( F, mat1, true ); - mat2:= ImmutableMatrix( F, mat2, true ); - mat3:= ImmutableMatrix( F, mat3, true ); - - # avoid to call 'Group' because this would check invertibility ... - g:= GroupWithGenerators( [ mat1, mat2, mat3 ] ); - SetName( g, Concatenation( "CSp(", String(d), ",", String(q), ")" ) ); - SetDimensionOfMatrixGroup( g, d ); - SetFieldOfMatrixGroup( g, F ); - - # add the size - size := 1; - qi := 1; - for i in [ 1 .. d/2 ] do - qi := qi * q^2; - size := size * (qi-1); - od; - SetSize( g, q^((d/2)^2) * size * (q-1) ); - fi; - - # construct the form - c:= ZeroMatrix( filt, F, d, d ); - for i in [ 1 .. d/2 ] do - c[i,d-i+1]:= o; - c[d/2+i,d/2-i+1]:= -o; - od; - SetInvariantBilinearFormUpToScalars( g, - rec( matrix:= ImmutableMatrix( F, c, true ) ) ); - - SetIsFullSubgroupGLRespectingBilinearFormUpToScalars( g, true ); - - # and return - return g; -end ); - - -############################################################################# -## -#M ConformalSymplecticGroupCons( , , ) -## -InstallMethod( ConformalSymplecticGroupCons, - "matrix group for dimension and finite field size", - [ "IsMatrixGroup and IsFinite", - "IsPosInt", - "IsPosInt" ], - { filt, n, q } -> ConformalSymplecticGroupCons( filt, n, GF(q) ) ); - - ############################################################################# ## #M ConformalSymplecticGroupCons( , ) @@ -197,7 +14,7 @@ InstallMethod( ConformalSymplecticGroupCons, { filt, G } -> ConformalSymplecticGroupCons( filt, BilinearFormByMatrix( InvariantBilinearForm( G ).matrix, - FieldOfMatrixGroup( G ) ) ) ); + Forms_FieldOfDefinition( G, InvariantBilinearForm( G ) ) ) ) ); InstallMethod( ConformalSymplecticGroupCons, "matrix group for group with form", @@ -206,7 +23,7 @@ InstallMethod( ConformalSymplecticGroupCons, { filt, G } -> ConformalSymplecticGroupCons( filt, BilinearFormByMatrix( InvariantBilinearFormUpToScalars( G ).matrix, - FieldOfMatrixGroup( G ) ) ) ); + Forms_FieldOfDefinition( G, InvariantBilinearFormUpToScalars( G ) ) ) ) ); InstallMethod( ConformalSymplecticGroupCons, "matrix group for form", @@ -333,7 +150,7 @@ InstallMethod( ConformalSymplecticGroupCons, fi; SetInvariantBilinearFormUpToScalars( gg, - rec( matrix:= Matrix( form!.matrix, stored ) ) ); + rec( matrix:= Matrix( form!.matrix, stored ), baseDomain:= F ) ); if HasIsFullSubgroupGLRespectingBilinearFormUpToScalars( g ) then SetIsFullSubgroupGLRespectingBilinearFormUpToScalars( gg, @@ -344,62 +161,13 @@ InstallMethod( ConformalSymplecticGroupCons, end ); -############################################################################# -## -## Support `IsPermGroup` as first argument in `ConformalSymplecticGroup`. -## -PermConstructor( ConformalSymplecticGroupCons, - [ IsPermGroup, IsInt, IsObject ], - IsMatrixGroup and IsFinite); - - -############################################################################# -## -#M \in( , ) . . . . . . . . membership test method based on form -## -InstallMethod( \in, "respecting bilinear form", IsElmsColls, - [ "IsMatrixOrMatrixObj", - "IsFullSubgroupGLRespectingBilinearFormUpToScalars" ], - {} -> RankFilter( IsHandledByNiceMonomorphism ), # override nice mon. method - function( mat, G ) - local inv; - - if not IsSubset( FieldOfMatrixGroup( G ), - FieldOfMatrixList( [ mat ] ) ) then - return false; - fi; - inv:= InvariantBilinearFormUpToScalars( G ).matrix; - return _IsEqualModScalars( inv, mat * inv * TransposedMat( mat ) ); -end ); - - ############################################################################# ## ## The following methods are currently needed to make the code work ## in case one creates groups whose elements are in `IsMatrixObj`. -## Eventually we want to get rid of them (or or to add something similar -## to the GAP library). +## Eventually we must support `IsMatrixObj` matrices in form objects. ## -# Strictly speaking, the following is not correct, -# according to the definition of `DegreeFFE`. -# Eventually we should fix the use of `FieldOfMatrixList` -# and `FieldOfMatrixGroup`, then `DegreeFFE` will not be important anymore. -InstallOtherMethod( DegreeFFE, - [ "IsMatrixObj and IsFFECollColl" ], - mat -> DegreeOverPrimeField( BaseDomain( mat ) ) ); - -# This is a really ugly hack. -# Without this method, multiplying a matrix object with a matrix -# is possible but yields the matrix product of with the *transposed* -# of . -# (This is because GAP regards as a scalar and computes the list of -# products of with the rows of .) -InstallOtherMethod( \*, - [ "IsMatrixObj", "IsMatrix" ], - { matobj, mat } -> Unpack( matobj ) * mat ); - - InstallOtherMethod( BilinearFormByMatrix, "for a ffe matrix object and a field", [ "IsMatrixObj and IsFFECollColl", "IsField and IsFinite" ], @@ -410,3 +178,12 @@ InstallOtherMethod( BilinearFormByMatrix, [ "IsMatrixObj and IsFFECollColl" ], m -> BilinearFormByMatrix( Unpack( m ) ) ); + +# The following is apparently needed in the tests in `tst/adv/conformal.tst`. +# Strictly speaking, the following is not correct, +# according to the definition of `DegreeFFE`. +# Eventually we should fix the use of `FieldOfMatrixList` +# and `FieldOfMatrixGroup`, then `DegreeFFE` will not be important anymore. +InstallOtherMethod( DegreeFFE, + [ "IsMatrixObj and IsFFECollColl" ], + mat -> DegreeOverPrimeField( BaseDomain( mat ) ) ); diff --git a/read.g b/read.g index 08fb100..1acdfa5 100644 --- a/read.g +++ b/read.g @@ -16,4 +16,7 @@ ReadPackage("forms", "lib/forms.gi"); ReadPackage("forms", "lib/recognition.gi"); ReadPackage("forms", "lib/classic.gi"); ReadPackage("forms", "lib/recognition_new.gi"); -ReadPackage("forms", "lib/conformal.gi"); + +if IsBound( ConformalSymplecticGroup ) then + ReadPackage("forms", "lib/conformal.gi"); +fi; diff --git a/tst/adv/classic.tst b/tst/adv/classic.tst index 0ade472..c8be837 100644 --- a/tst/adv/classic.tst +++ b/tst/adv/classic.tst @@ -1,4 +1,4 @@ -#@local is_equal, q, F, d, es, e, g, stored, pi, permmat, form, gg, F2, mat +#@local is_equal, q, F, d, es, e, g, filt, stored, pi, permmat, form, gg, F2, mat gap> START_TEST( "Forms: classic.tst" ); @@ -167,26 +167,41 @@ gap> for q in [ 2, 3, 4, 5, 7, 8, 9, 11, 13, 16, 25 ] do gap> for q in [ 2, 3, 4, 5, 7, 8, 9, 11, 13, 16, 17, 19, 23, 25 ] do > F:= GF(q); > for d in [ 2, 4 .. 8 ] do -> g:= SymplecticGroup( d, q ); -> stored:= InvariantBilinearForm( g ).matrix; -> pi:= PermutationMat( (1,2), d, F ); -> permmat:= pi * stored * TransposedMat( pi ); -> form:= BilinearFormByMatrix( stored, F ); -> gg:= SymplecticGroup( d, q, permmat ); -> if not ( is_equal( g, SymplecticGroup( g ) ) and -> ( is_equal( g, SymplecticGroup( stored ) ) or -> BaseDomain( stored ) <> F ) and -> is_equal( g, SymplecticGroup( form ) ) and -> is_equal( g, SymplecticGroup( d, q, g ) ) and -> is_equal( g, SymplecticGroup( d, q, stored ) ) and -> is_equal( g, SymplecticGroup( d, q, form ) ) and -> is_equal( g, SymplecticGroup( d, F, g ) ) and -> is_equal( g, SymplecticGroup( d, F, stored ) ) and -> is_equal( g, SymplecticGroup( d, F, form ) ) and -> IsSubset( gg, GeneratorsOfGroup( gg ) ) and -> IsSubset( g, List( GeneratorsOfGroup( gg ), x -> x^pi ) ) ) then -> Error( "problem with Sp(", d, ",", q, ")" ); -> fi; +> for filt in [ IsPlistRep, IsPlistMatrixRep ] do +> PushOptions( rec( ConstructingFilter:= filt ) ); +> +> g:= SymplecticGroup( d, q ); +> if filt <> IsPlistRep and not filt( One( g ) ) then +> Error( "wrong repres. of matrices", [ q, d, filt ] ); +> fi; +> stored:= InvariantBilinearForm( g ).matrix; +> if filt <> IsPlistRep and not filt( stored ) then +> Error( "wrong repres. of matrices" ); +> fi; +> pi:= Matrix( PermutationMat( (1,2), d, F ), stored ); +> permmat:= pi * stored * TransposedMat( pi ); +> form:= BilinearFormByMatrix( stored, F ); +> gg:= SymplecticGroup( d, q, permmat ); +> if filt <> IsPlistRep and not filt( One( gg ) ) then +> Error( "wrong repres. of matrices" ); +> fi; +> if not ( is_equal( g, SymplecticGroup( g ) ) and +> ( is_equal( g, SymplecticGroup( stored ) ) or +> BaseDomain( stored ) <> F ) and +> is_equal( g, SymplecticGroup( form ) ) and +> is_equal( g, SymplecticGroup( d, q, g ) ) and +> is_equal( g, SymplecticGroup( d, q, stored ) ) and +> is_equal( g, SymplecticGroup( d, q, form ) ) and +> is_equal( g, SymplecticGroup( d, F, g ) ) and +> is_equal( g, SymplecticGroup( d, F, stored ) ) and +> is_equal( g, SymplecticGroup( d, F, form ) ) and +> IsSubset( gg, GeneratorsOfGroup( gg ) ) and +> IsSubset( g, List( GeneratorsOfGroup( gg ), x -> x^pi ) ) ) then +> Error( "problem with Sp(", d, ",", q, ") for ", filt ); +> fi; +> +> PushOptions( rec( ConstructingFilter:= fail ) ); +> od; > od; > od; @@ -232,4 +247,4 @@ gap> _IsEqualModScalars( mat, NullMat( 3, 2, GF(5) ) ); false ## -gap> STOP_TEST( "classic.tst" ); +gap> STOP_TEST( "Forms: classic.tst" ); diff --git a/tst/adv/conformal.tst b/tst/adv/conformal.tst index 0d913ba..4f7f33e 100644 --- a/tst/adv/conformal.tst +++ b/tst/adv/conformal.tst @@ -1,4 +1,4 @@ -#@local is_equal, q, F, d, filt, g, stored, pi, permmat, form, gg, pg +#@local is_equal, q, F, d, filt, g, stored, pi, permmat, form, gg, pg, sp gap> START_TEST( "Forms: conformal.tst" ); @@ -15,16 +15,25 @@ gap> for q in [ 2, 3, 4, 5, 7, 8, 9, 11, 13, 16, 17, 19, 23, 25 ] do > for filt in [ IsPlistRep, IsPlistMatrixRep ] do > PushOptions( rec( ConstructingFilter:= filt ) ); > -> g:= ConformalSymplecticGroup( d, q ); +> g:= ConformalSymplecticGroup( d, F ); +> if filt <> IsPlistRep and not filt( One( g ) ) then +> Error( "wrong repres. of matrices", [ q, d, filt ] ); +> fi; > stored:= InvariantBilinearFormUpToScalars( g ).matrix; +> if filt <> IsPlistRep and not filt( stored ) then +> Error( "wrong repres. of matrices" ); +> fi; > pi:= Matrix( PermutationMat( (1,2), d, F ), stored ); > permmat:= pi * stored * TransposedMat( pi ); > form:= BilinearFormByMatrix( stored, F ); -> gg:= ConformalSymplecticGroup( d, q, permmat ); +> gg:= ConformalSymplecticGroup( d, F, permmat ); +> if filt <> IsPlistRep and not filt( One( gg ) ) then +> Error( "wrong repres. of matrices" ); +> fi; > if not ( is_equal( g, ConformalSymplecticGroup( g ) ) and > ( is_equal( g, ConformalSymplecticGroup( stored ) ) or > BaseDomain( stored ) <> F ) and -> is_equal( g, ConformalSymplecticGroup( d, F ) ) and +> is_equal( g, ConformalSymplecticGroup( d, q ) ) and > is_equal( g, ConformalSymplecticGroup( form ) ) and > is_equal( g, ConformalSymplecticGroup( d, q, g ) ) and > is_equal( g, ConformalSymplecticGroup( d, q, stored ) ) and @@ -37,21 +46,22 @@ gap> for q in [ 2, 3, 4, 5, 7, 8, 9, 11, 13, 16, 17, 19, 23, 25 ] do > Error( "problem with CSp(", d, ",", q, ")" ); > fi; > -> if Size( g ) < 10^7 and filt = IsPlistRep then -> #TODO: Make this work for `IsPlistMatrixRep` +> if Size( g ) < 10^7 then > pg:= ConformalSymplecticGroup( IsPermGroup, d, q ); > if Size( g ) <> Size( pg ) then > Error( "problem with CSp(IsPermGroup, ", d, ",", q, ")" ); > fi; > fi; > -> #TODO: Once `SymplecticGroup` supports matrix objects, -> # the following will work. -> # sp:= SymplecticGroup( d, q, permmat ); -> # g:= ConformalSymplecticGroup( sp ); -> # if not IsSubset( g, sp ) then -> # Error( "problem with CSp(", d, ",", q, ")" ); -> # fi; +> sp:= SymplecticGroup( d, q, permmat ); +> if filt <> IsPlistRep and not filt( One( sp ) ) then +> Error( "wrong repres. of matrices" ); +> fi; +> g:= ConformalSymplecticGroup( sp ); +> # if not IsSubset( g, sp ) then # make this fast in the GAP library +> if ForAny( GeneratorsOfGroup( sp ), x -> not x in g ) then +> Error( "problem with CSp(", d, ",", q, ")" ); +> fi; > > PushOptions( rec( ConstructingFilter:= fail ) ); > od; @@ -59,5 +69,5 @@ gap> for q in [ 2, 3, 4, 5, 7, 8, 9, 11, 13, 16, 17, 19, 23, 25 ] do > od; ## -gap> STOP_TEST( "conformal.tst" ); +gap> STOP_TEST( "Forms: conformal.tst" ); diff --git a/tst/testall.g b/tst/testall.g index 8667153..1b6bd66 100644 --- a/tst/testall.g +++ b/tst/testall.g @@ -6,6 +6,11 @@ if not IsBound(DescribesInvariantQuadraticForm) then Add( exclude, "adv/classic.tst" ); fi; +if not IsBound(ConformalSymplecticGroup) then + # conformal.tst cannot run + Add( exclude, "adv/conformal.tst" ); +fi; + TestDirectory(DirectoriesPackageLibrary("forms", "tst"), rec( exitGAP := true, From 1595d1395e6d697e78fb98742290d704999626be Mon Sep 17 00:00:00 2001 From: ThomasBreuer Date: Tue, 3 Feb 2026 18:17:08 +0100 Subject: [PATCH 3/4] fix side-effects of rebasing and of changes in GAP --- lib/classic.gi | 23 +++++++++++------------ lib/conformal.gi | 4 ++-- tst/adv/conformal.tst | 3 ++- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/lib/classic.gi b/lib/classic.gi index c1cee90..31e3951 100644 --- a/lib/classic.gi +++ b/lib/classic.gi @@ -784,17 +784,17 @@ InstallMethod( SpecialUnitaryGroupCons, g:= SpecialUnitaryGroupCons( filt, d, q ); stored:= InvariantSesquilinearForm( g ).matrix; - # If the prescribed form fits then just return. - if stored = form!.matrix then - return g; - fi; - # Check that 'form' lives over the intended field. F:= GF(q^2); if not IsSubset( F, form!.basefield ) then Error( "the defining field of does not fit to " ); fi; + # If the prescribed form fits then just return. + if stored = form!.matrix then + return g; + fi; + # Compute a base change matrix. # (Check that the canonical forms are equal.) wanted:= HermitianFormByMatrix( stored, F ); @@ -928,6 +928,11 @@ InstallMethod( SymplecticGroupCons, g:= SymplecticGroupCons( filt, d, F ); stored:= InvariantBilinearForm( g ).matrix; + # Check that 'form' lives over the intended field. + if not IsSubset( F, form!.basefield ) then + Error( "the defining field of does not fit to " ); + fi; + # If the prescribed form fits then just return. form_matrix:= Matrix( form!.matrix, stored ); #T This 'Matrix' call should become unnecessary. @@ -936,12 +941,6 @@ InstallMethod( SymplecticGroupCons, return g; fi; - # Check that 'form' lives over the intended field. - F:= GF(q); - if not IsSubset( F, form!.basefield ) then - Error( "the defining field of does not fit to " ); - fi; - # Compute a base change matrix. # (Check that the canonical forms are equal.) wanted:= BilinearFormByMatrix( stored, F ); @@ -964,7 +963,7 @@ InstallMethod( SymplecticGroupCons, SetName( gg, Name( g ) ); fi; - SetInvariantBilinearForm( gg, rec( matrix:= form!.matrix, + SetInvariantBilinearForm( gg, rec( matrix:= form_matrix, baseDomain:= F ) ); if HasIsFullSubgroupGLorSLRespectingBilinearForm( g ) then SetIsFullSubgroupGLorSLRespectingBilinearForm( gg, diff --git a/lib/conformal.gi b/lib/conformal.gi index 3502fdd..ce265c4 100644 --- a/lib/conformal.gi +++ b/lib/conformal.gi @@ -14,7 +14,7 @@ InstallMethod( ConformalSymplecticGroupCons, { filt, G } -> ConformalSymplecticGroupCons( filt, BilinearFormByMatrix( InvariantBilinearForm( G ).matrix, - Forms_FieldOfDefinition( G, InvariantBilinearForm( G ) ) ) ) ); + Forms_FieldOfDefinition( InvariantBilinearForm( G ), G ) ) ) ); InstallMethod( ConformalSymplecticGroupCons, "matrix group for group with form", @@ -23,7 +23,7 @@ InstallMethod( ConformalSymplecticGroupCons, { filt, G } -> ConformalSymplecticGroupCons( filt, BilinearFormByMatrix( InvariantBilinearFormUpToScalars( G ).matrix, - Forms_FieldOfDefinition( G, InvariantBilinearFormUpToScalars( G ) ) ) ) ); + Forms_FieldOfDefinition( InvariantBilinearFormUpToScalars( G ), G ) ) ) ); InstallMethod( ConformalSymplecticGroupCons, "matrix group for form", diff --git a/tst/adv/conformal.tst b/tst/adv/conformal.tst index 4f7f33e..859c2d2 100644 --- a/tst/adv/conformal.tst +++ b/tst/adv/conformal.tst @@ -46,7 +46,8 @@ gap> for q in [ 2, 3, 4, 5, 7, 8, 9, 11, 13, 16, 17, 19, 23, 25 ] do > Error( "problem with CSp(", d, ",", q, ")" ); > fi; > -> if Size( g ) < 10^7 then +> if Size( g ) < 10^7 and filt = IsPlistRep then +> # Change this as soon as also `IsPlistMatrixRep` works! > pg:= ConformalSymplecticGroup( IsPermGroup, d, q ); > if Size( g ) <> Size( pg ) then > Error( "problem with CSp(IsPermGroup, ", d, ",", q, ")" ); From 8e881220efc4029a651625110b102b0ee540a175 Mon Sep 17 00:00:00 2001 From: ThomasBreuer Date: Wed, 4 Feb 2026 12:40:53 +0100 Subject: [PATCH 4/4] restrict the new tests to a new enough GAP --- tst/adv/classic.tst | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/tst/adv/classic.tst b/tst/adv/classic.tst index c8be837..f2b7faa 100644 --- a/tst/adv/classic.tst +++ b/tst/adv/classic.tst @@ -1,4 +1,5 @@ -#@local is_equal, q, F, d, es, e, g, filt, stored, pi, permmat, form, gg, F2, mat +#@local is_equal, q, F, d, es, e, g, filters, filt, stored, pi, permmat +#@local form, gg, F2, mat gap> START_TEST( "Forms: classic.tst" ); @@ -164,10 +165,19 @@ gap> for q in [ 2, 3, 4, 5, 7, 8, 9, 11, 13, 16, 25 ] do > od; # Test the creation of symplectic groups. +gap> if IsBound( ConformalSymplecticGroup ) then +> # Support for matrix objects was added together with this function, +> # https://github.com/gap-system/gap/pull/6213. +> # Once we know a GAP version that decides the availability, +> # the version number can be used for the distinction. +> filters:= [ IsPlistRep, IsPlistMatrixRep ];; +> else +> filters:= [ IsPlistRep ];; +> fi; gap> for q in [ 2, 3, 4, 5, 7, 8, 9, 11, 13, 16, 17, 19, 23, 25 ] do > F:= GF(q); > for d in [ 2, 4 .. 8 ] do -> for filt in [ IsPlistRep, IsPlistMatrixRep ] do +> for filt in filters do > PushOptions( rec( ConstructingFilter:= filt ) ); > > g:= SymplecticGroup( d, q );