From 4c91be2320cc0786264da9aec7b70910cd81bd22 Mon Sep 17 00:00:00 2001 From: ThomasBreuer Date: Thu, 12 Feb 2026 12:10:32 +0100 Subject: [PATCH 01/15] fix a problem with a new feature for matrix groups - add a description of the cache `FULLGLNICOCACHE` in the code; it is used only by `NicomorphismFFMatGroupOnFullSpace` - change the keys of `FULLGLNICOCACHE`: take the field of the matrix entries into account (not only its size), take the `ConstructingFilter` of the matrices into account - in `NicomorphismFFMatGroupOnFullSpace`, deal with special cases of matrices in `IsBlockMatrixRep` (which have no `ConstructingFilter`) and in `Is8BitMatrixRep` in the situation that the group itself lives over `GF(2)` - in order to admit the action of `IsMatrixObj` matrices on vectors supported by GAP's `Enumerator`s of vector spaces, add a `\^` method that `Unpack`s the matrix object (Eventually we want to avoid this overhead, but then we need `Enumerator`s consisting of vector objects corresponding to the matrix objects.) - add `Matrix` calls in the function that computes preimages under an action homomorphism with `Source` a matrix group, in order to support matrix group elements of prescribed kinds of `IsMatrixObj`. - add tests for the new supported situations (most of the changes were actually forced by already available tests) --- lib/grpffmat.gi | 71 +++++++++++++++++++++++++++++--------- lib/matobj.gi | 9 +++++ lib/oprt.gi | 11 +++--- tst/testinstall/grpmat.tst | 18 +++++++++- 4 files changed, 86 insertions(+), 23 deletions(-) diff --git a/lib/grpffmat.gi b/lib/grpffmat.gi index 147563c60b..6a80a75ce4 100644 --- a/lib/grpffmat.gi +++ b/lib/grpffmat.gi @@ -124,31 +124,66 @@ end ); ############################################################################# ## -#M NiceMonomorphism( ) +#V FULLGLNICOCACHE +## +## 'NicomorphismFFMatGroupOnFullSpace' uses a cache of length up to 5, +## as follows. +## +## - If the argument is a matrix group that fits to an entry of this +## cache, in the sense that dimension, field of definition, +## and 'ConstructingFilter' of the matrices in the group are the same as +## for the cached value, the stored mapping is returned. +## - If a new mapping has to be constructed, the first cached entry is +## dropped and the new mapping gets added to the cache. ## MakeThreadLocal("FULLGLNICOCACHE"); # avoid recreating same homom. repeatedly -FULLGLNICOCACHE:=[]; -InstallGlobalFunction( NicomorphismFFMatGroupOnFullSpace, function( grp ) - local field, dim, V, xset, nice; +BindGlobal( "FULLGLNICOCACHE", [] ); - field := FieldOfMatrixGroup( grp ); - dim := DimensionOfMatrixGroup( grp ); + +############################################################################# +## +#M NiceMonomorphism( ) +## +InstallGlobalFunction( NicomorphismFFMatGroupOnFullSpace, function( grp ) + local rep, filt, dim, field, V, xset, nice; + + rep:= Representative( grp ); + field:= FieldOfMatrixGroup( grp ); + if IsBlockMatrixRep( rep ) then + # There is no support for these matrices acting on vectors. + filt:= IsPlistRep; + elif Size( field ) = 2 and Is8BitMatrixRep( rep ) then + # We cannot keep both 'field' and 'Is8BitMatrixRep', + # the latter does not admit matrices over GF(2). + filt:= IsGF2MatrixRep; +#TODO: How can we get rid of these hacks? + else + filt:= ConstructingFilter( rep ); + fi; + dim:= DimensionOfMatrixGroup( grp ); #check cache - V:=Size(field); - nice:=First(FULLGLNICOCACHE,x->x[1]=V and x[2]=dim); - if nice<>fail then return nice[3];fi; + nice:= First( FULLGLNICOCACHE, + x -> x[1] = field and x[2] = dim and x[3] = filt ); + + if nice<>fail then return nice[4];fi; if not (HasIsNaturalGL(grp) and IsNaturalGL(grp)) then - grp:=GL(dim,field); # enforce map on full GL + # enforce map on full GL + grp:= GL( dim, field : ConstructingFilter:= filt ); fi; V := field ^ dim; xset := ExternalSet( grp, V ); - # STILL: reverse the base to get point sorting compatible with lexicographic # vector arrangement - SetBaseOfGroup( xset, One( grp )); + if IsList( One( grp ) ) then + SetBaseOfGroup( xset, One( grp )); + else + SetBaseOfGroup( xset, List( One( grp ), List ) ); +#TODO: this is an ugly hack, +# probably we will have to support vector spaces consisting of 'VectorObj's + fi; nice := ActionHomomorphism( xset,"surjective" ); SetIsInjective( nice, true ); if not HasNiceMonomorphism(grp) then @@ -158,12 +193,12 @@ InstallGlobalFunction( NicomorphismFFMatGroupOnFullSpace, function( grp ) SetIsCanonicalNiceMonomorphism(nice,true); if Size(V)>10^5 then # store only one big one and have it get thrown out quickly - FULLGLNICOCACHE[1]:=[Size(field),dim,nice]; + FULLGLNICOCACHE[1]:= [ field, dim, filt, nice ]; else if Length(FULLGLNICOCACHE)>4 then - FULLGLNICOCACHE:=FULLGLNICOCACHE{[2..5]}; + Remove( FULLGLNICOCACHE, 1 ); fi; - Add(FULLGLNICOCACHE,[Size(field),dim,nice]); + Add( FULLGLNICOCACHE, [ field, dim, filt, nice ] ); fi; return nice; @@ -190,8 +225,7 @@ local tt; # if the permutation image would be too large, compute the orbit. TryNextMethod(); fi; - return NicomorphismFFMatGroupOnFullSpace( GL( DimensionOfMatrixGroup( grp ), - Size( FieldOfMatrixGroup( Parent(grp) ) ) ) ); + return NicomorphismFFMatGroupOnFullSpace( grp ); end ); ############################################################################# @@ -249,6 +283,8 @@ end); InstallMethod( \in, "general linear group", IsElmsColls, [ IsMatrix, IsFFEMatrixGroup and IsFinite and IsNaturalGL ], 0, +#T OrMatrixObj +#T too low rank? (below nice monom. method?) function( mat, G ) return Length( mat ) = Length( mat[ 1 ] ) and Length( mat ) = DimensionOfMatrixGroup( G ) @@ -258,6 +294,7 @@ end ); InstallMethod( \in, "special linear group", IsElmsColls, [ IsMatrix, IsFFEMatrixGroup and IsFinite and IsNaturalSL ], 0, +#T OrMatrixObj function( mat, G ) return Length( mat ) = Length( mat[ 1 ] ) and Length( mat ) = DimensionOfMatrixGroup( G ) diff --git a/lib/matobj.gi b/lib/matobj.gi index f80b705319..19eee2d643 100644 --- a/lib/matobj.gi +++ b/lib/matobj.gi @@ -1222,6 +1222,15 @@ InstallOtherMethod( \^, [ IsVectorObj, IsMatrixObj ], \* ); +#TODO: This is an ugly hack. +# Action homomorphisms for matrix groups use vector spaces, +# which currently consist of lists not vector objects. +# If we want to act on the vectors with matrix groups consisting of +# matrix objects, we have to support the following. +InstallOtherMethod( \^, + [ IsRowVector, IsMatrixObj ], + { v, M } -> v ^ Unpack( M ) ); + ############################################################################ ## diff --git a/lib/oprt.gi b/lib/oprt.gi index fe1acea79d..b086562c42 100644 --- a/lib/oprt.gi +++ b/lib/oprt.gi @@ -3380,7 +3380,7 @@ end ); InstallMethod( PreImagesRepresentative,"IsLinearActionHomomorphism", FamRangeEqFamElm, [ IsLinearActionHomomorphism, IsPerm ], 0, function( hom, elm ) - local V, xset,lab,f; + local V, G, Grep, xset,lab,f; # is this method applicable? Test whether the domain contains a vector # space basis (respectively just get this basis). @@ -3393,20 +3393,21 @@ function( hom, elm ) #if not elm in Image( hom ) then return fail; fi; xset:=UnderlyingExternalSet(hom); V := HomeEnumerator(xset); - f:=DefaultFieldOfMatrixGroup(Source(hom)); + G:= Source( hom ); + Grep:= Representative( G ); + f:=DefaultFieldOfMatrixGroup(G); if not IsBound(hom!.linActBasisPositions) then hom!.linActBasisPositions:=List(lab,i->PositionCanonical(V,i)); fi; if not IsBound(hom!.linActInverse) then - lab:=ImmutableMatrix(f,lab); + lab:=ImmutableMatrix(f, Matrix( lab, Grep )); hom!.linActInverse:=Inverse(lab); fi; elm:=OnTuples(hom!.linActBasisPositions,elm); # image points elm:=V{elm}; # the corresponding vectors - f:=DefaultFieldOfMatrixGroup(Source(hom)); - elm:=ImmutableMatrix(f,elm); + elm:=ImmutableMatrix(f, Matrix( elm, Grep )); return hom!.linActInverse*elm; end ); diff --git a/tst/testinstall/grpmat.tst b/tst/testinstall/grpmat.tst index 31b192e7ba..4c23741723 100644 --- a/tst/testinstall/grpmat.tst +++ b/tst/testinstall/grpmat.tst @@ -1,4 +1,4 @@ -#@local cl,g,gd,gens,hom,i,img,iso,pcgs,u,G,F,o,a,m,nice,H +#@local cl,g,gd,gens,hom,i,img,iso,pcgs,u,G,F,o,a,m,nice,H,G2,nice2 gap> START_TEST("grpmat.tst"); gap> i := E(4);; G := Group([[i,0],[0,-i]],[[0,1],[-1,0]]);; gap> gens := GeneratorsOfGroup( G );; IsSSortedList( gens ); @@ -89,5 +89,21 @@ gap> TrivialSubgroup( GL(2, 2) ); gap> Length( LowIndexSubgroups( GL(2,5), 50 ) ) = 31; true +# 'NiceMonomorphism' behaves well w.r.t. 'ConstructingFilter' +gap> G:= SP( 4, 2 );; +gap> nice:= NiceMonomorphism( G );; +gap> G2:= SP( 4, 2 : ConstructingFilter:= IsPlistMatrixRep );; +gap> nice2:= NiceMonomorphism( G2 );; +gap> IsPlistMatrixRep( One( G ) ); +false +gap> IsPlistMatrixRep( One( G2 ) ); +true +gap> IsIdenticalObj( nice, nice2 ); +false +gap> IsSubset( Source( nice ), G ); +true +gap> IsSubset( Source( nice2 ), G2 ); +true + # gap> STOP_TEST( "grpmat.tst" ); From cf2c08b8087867b30f446110befda23aef94a2fb Mon Sep 17 00:00:00 2001 From: ThomasBreuer Date: Thu, 12 Feb 2026 13:36:20 +0100 Subject: [PATCH 02/15] add `\in` methods for matrix object and (`GL` or `SL`) --- lib/grpffmat.gi | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/lib/grpffmat.gi b/lib/grpffmat.gi index 6a80a75ce4..1d65bec0f1 100644 --- a/lib/grpffmat.gi +++ b/lib/grpffmat.gi @@ -283,8 +283,6 @@ end); InstallMethod( \in, "general linear group", IsElmsColls, [ IsMatrix, IsFFEMatrixGroup and IsFinite and IsNaturalGL ], 0, -#T OrMatrixObj -#T too low rank? (below nice monom. method?) function( mat, G ) return Length( mat ) = Length( mat[ 1 ] ) and Length( mat ) = DimensionOfMatrixGroup( G ) @@ -292,9 +290,21 @@ InstallMethod( \in, "general linear group", IsElmsColls, and Length( mat ) = RankMat( mat ); end ); +InstallMethod( \in, "general linear group", IsElmsColls, + [ IsMatrixObj, IsFFEMatrixGroup and IsFinite and IsNaturalGL ], 0, + function( mat, G ) + local n, F; + n:= NumberRows( mat ); + F:= FieldOfMatrixGroup( G ); + return n = NumberColumns( mat ) + and n = DimensionOfMatrixGroup( G ) + and n = RankMat( mat ) + and ( IsSubset( F, BaseDomain( mat ) ) or + ForAll( Unpack( mat ), row -> IsSubset( F, row ) ) ); +end ); + InstallMethod( \in, "special linear group", IsElmsColls, [ IsMatrix, IsFFEMatrixGroup and IsFinite and IsNaturalSL ], 0, -#T OrMatrixObj function( mat, G ) return Length( mat ) = Length( mat[ 1 ] ) and Length( mat ) = DimensionOfMatrixGroup( G ) @@ -303,6 +313,19 @@ InstallMethod( \in, "special linear group", IsElmsColls, and DeterminantMat(mat)=One(FieldOfMatrixGroup( G )); end ); +InstallMethod( \in, "special linear group", IsElmsColls, + [ IsMatrixObj, IsFFEMatrixGroup and IsFinite and IsNaturalSL ], 0, + function( mat, G ) + local n, F; + n:= NumberRows( mat ); + F:= FieldOfMatrixGroup( G ); + return n = NumberColumns( mat ) + and n = DimensionOfMatrixGroup( G ) + and ( IsSubset( F, BaseDomain( mat ) ) or + ForAll( Unpack( mat ), row -> IsSubset( F, row ) ) ) + and DeterminantMat( mat ) = One( F ); +end ); + ############################################################################# ## From eceea283c5b71f6060277e30d7bc39637b31080a Mon Sep 17 00:00:00 2001 From: ThomasBreuer Date: Thu, 19 Feb 2026 17:55:51 +0100 Subject: [PATCH 03/15] avoid some `Unpack` hacks - introduce `NormedRowVectors_internal( F, base )`, and `AsListOfFreeLeftModule_internal( F, base, zero )`, in order to admit `IsVectorObj`s in `base` without supporting the whole vector space machinery for these objects - introduce `ExternalSet( G )` for matrix groups `G`, meaning the natural `G`-set (an undocumented method for permutation groups `G` was already available) - change `NicomorphismFFMatGroupOnFullSpace` such that we can act with the `IsMatrixObj` matrices on their `IsVectorObj` vectors, without unpacking the matrices - support `IsMatrixObj` matrices in the `Random` methods for GL and SL --- lib/grpffmat.gi | 76 ++++++++++++++++++++------ lib/modfree.gi | 23 +++++--- lib/oprt.gi | 32 +++++++---- lib/vspcrow.gi | 60 ++++++++++----------- tst/testinstall/MatrixObj/acthom.tst | 80 ++++++++++++++++++++++++++++ 5 files changed, 204 insertions(+), 67 deletions(-) create mode 100644 tst/testinstall/MatrixObj/acthom.tst diff --git a/lib/grpffmat.gi b/lib/grpffmat.gi index 1d65bec0f1..e1d6d71a19 100644 --- a/lib/grpffmat.gi +++ b/lib/grpffmat.gi @@ -122,6 +122,22 @@ local gen, d, f; end ); +############################################################################# +## +## the natural G-set of a matrix group consists of the vectors of the +## natural module +## +InstallOtherMethod( ExternalSet, + [ IsFFEMatrixGroup and IsFinite ], + function( G ) + local basis, zero; + + basis:= RowsOfMatrix( One( G ) ); + zero:= Zero( basis[1] ); + return ExternalSet( G, AsListOfFreeLeftModule_internal( + FieldOfMatrixGroup( G ), basis, zero ) ); +end ); + ############################################################################# ## #V FULLGLNICOCACHE @@ -145,14 +161,15 @@ BindGlobal( "FULLGLNICOCACHE", [] ); #M NiceMonomorphism( ) ## InstallGlobalFunction( NicomorphismFFMatGroupOnFullSpace, function( grp ) - local rep, filt, dim, field, V, xset, nice; + local rep, filt, q, dim, field, xset, nice; rep:= Representative( grp ); field:= FieldOfMatrixGroup( grp ); + q:= Size( field ); if IsBlockMatrixRep( rep ) then # There is no support for these matrices acting on vectors. filt:= IsPlistRep; - elif Size( field ) = 2 and Is8BitMatrixRep( rep ) then + elif q = 2 and Is8BitMatrixRep( rep ) then # We cannot keep both 'field' and 'Is8BitMatrixRep', # the latter does not admit matrices over GF(2). filt:= IsGF2MatrixRep; @@ -172,17 +189,14 @@ InstallGlobalFunction( NicomorphismFFMatGroupOnFullSpace, function( grp ) # enforce map on full GL grp:= GL( dim, field : ConstructingFilter:= filt ); fi; - V := field ^ dim; - xset := ExternalSet( grp, V ); + xset := ExternalSet( grp ); # STILL: reverse the base to get point sorting compatible with lexicographic # vector arrangement if IsList( One( grp ) ) then SetBaseOfGroup( xset, One( grp )); else - SetBaseOfGroup( xset, List( One( grp ), List ) ); -#TODO: this is an ugly hack, -# probably we will have to support vector spaces consisting of 'VectorObj's + SetBaseOfGroup( xset, RowsOfMatrix( One( grp ) ) ); fi; nice := ActionHomomorphism( xset,"surjective" ); SetIsInjective( nice, true ); @@ -191,7 +205,7 @@ InstallGlobalFunction( NicomorphismFFMatGroupOnFullSpace, function( grp ) fi; # because we act on the full space we are canonical. SetIsCanonicalNiceMonomorphism(nice,true); - if Size(V)>10^5 then + if q^dim > 10^5 then # store only one big one and have it get thrown out quickly FULLGLNICOCACHE[1]:= [ field, dim, filt, nice ]; else @@ -568,10 +582,23 @@ InstallMethodWithRandomSource( Random, "for a random source and natural GL", [ IsRandomSource, IsFFEMatrixGroup and IsFinite and IsNaturalGL ], function(rs, G) - local m; - m := RandomInvertibleMat( rs, DimensionOfMatrixGroup( G ), - FieldOfMatrixGroup( G ) ); - return ImmutableMatrix(FieldOfMatrixGroup(G), m, true); + local d, F, m; + + d:= DimensionOfMatrixGroup( G ); + F:= FieldOfMatrixGroup( G ); + m:= Representative( G ); + if IsMatrix( m ) then + m:= RandomInvertibleMat( rs, d, F ); + m:= ImmutableMatrix(F, m, true); + else + m:= ZeroMatrix( d, d, m ); + repeat + Randomize( rs, m ); + until RankMat( m ) = d; + MakeImmutable( m ); + fi; + + return m; end); @@ -587,11 +614,26 @@ InstallMethodWithRandomSource( Random, "for a random source and natural SL", [ IsRandomSource, IsFFEMatrixGroup and IsFinite and IsNaturalSL ], function(rs, G) - local m; - m:= RandomInvertibleMat( rs, DimensionOfMatrixGroup( G ), - FieldOfMatrixGroup( G ) ); - MultVector(m[1], DeterminantMat(m)^-1); - return ImmutableMatrix(FieldOfMatrixGroup(G), m, true); + local d, m, F, det; + + d:= DimensionOfMatrixGroup( G ); + m:= Representative( G ); + if IsMatrix( m ) then + F:= FieldOfMatrixGroup( G ); + m:= RandomInvertibleMat( rs, d, F ); + MultVector( m[1], DeterminantMat( m )^-1 ); + m:= ImmutableMatrix( F, m, true ); + else + m:= ZeroMatrix( d, d, m ); + repeat + Randomize( rs, m ); + det:= DeterminantMat( m ); + until not IsZero( det ); + MultMatrixRow( m, 1, det^-1 ); + MakeImmutable( m ); + fi; + + return m; end); ############################################################################# diff --git a/lib/modfree.gi b/lib/modfree.gi index c88e380f42..2082cf8611 100644 --- a/lib/modfree.gi +++ b/lib/modfree.gi @@ -175,23 +175,26 @@ InstallMethod( Size, ## Either this basis has been entered when the space was constructed, or a ## basis is computed together with the elements list. ## -BindGlobal( "AsListOfFreeLeftModule", function( V ) + +## The vectors in 'base' are assumed to be linearly independent. +BindGlobal( "AsListOfFreeLeftModule_internal", function( F, base, zero ) local elms, # elements list, result B, # $F$-basis of $V$ new, # intermediate elements list v, # one generator of $V$ i; # loop variable - if not IsFinite( V ) then - Error( "cannot compute elements list of infinite domain " ); + elms:= [ zero ]; + + if IsEmpty( base ) then + return elms; + elif not IsFinite( F ) then + Error( "cannot compute elements list over infinite domain " ); fi; - B := Basis( V ); - elms := [ Zero( V ) ]; -#T check whether we have the elements now ? - for v in BasisVectors( B ) do + for v in base do new:= []; - for i in AsList( LeftActingDomain( V ) ) do + for i in AsList( F ) do Append( new, List( elms, x -> x + i * v ) ); od; elms:= new; @@ -202,6 +205,10 @@ BindGlobal( "AsListOfFreeLeftModule", function( V ) return elms; end ); +BindGlobal( "AsListOfFreeLeftModule", + V -> AsListOfFreeLeftModule_internal( LeftActingDomain( V ), + BasisVectors( Basis( V ) ), Zero( V ) ) ); + InstallMethod( AsList, "for a free left module", [ IsFreeLeftModule ], diff --git a/lib/oprt.gi b/lib/oprt.gi index b086562c42..20c0a590e8 100644 --- a/lib/oprt.gi +++ b/lib/oprt.gi @@ -655,7 +655,7 @@ local xset,surj,G, D, act, fam, filter, hom, i,blockacttest; elif IsExternalSetByActorsRep( xset ) then filter := filter and IsActionHomomorphismByActors; elif IsMatrixGroup( G ) - and IsScalarList( D[ 1 ] ) then + and ( IsScalarList( D[ 1 ] ) or IsVectorObj( D[ 1 ] ) ) then if act in [ OnPoints, OnRight ] then # we act linearly. This might be used to compute preimages by linear # algebra @@ -663,7 +663,8 @@ local xset,surj,G, D, act, fam, filter, hom, i,blockacttest; # vector space base. This will be done the first time, # `LinearActionBasis' is called (i.e. in the preimages routine). filter := filter and IsLinearActionHomomorphism; - elif act=OnLines then + elif act=OnLines and IsScalarList( D[1] ) then +#TODO: Fix IsProjectiveActionHomomorphism before IsVectorObj gets supported. filter := filter and IsProjectiveActionHomomorphism; fi; @@ -3468,18 +3469,24 @@ end); InstallMethod(LinearActionBasis,"find basis in domain",true, [IsLinearActionHomomorphism],0, function(hom) -local xset,D,b,t,i,r,pos; +local xset, base, M, D, b, t, i, r, pos, v; xset:=UnderlyingExternalSet(hom); if Size(xset)=0 then return fail; fi; - pos:=[]; # if there is a base, check whether it's full rank, if yes, take it - if HasBaseOfGroup(xset) - and RankMat(BaseOfGroup(xset))=Length(BaseOfGroup(xset)[1]) then - # this implies injectivity - SetIsInjective(hom,true); - return BaseOfGroup(xset); + if HasBaseOfGroup(xset) then + base:= BaseOfGroup(xset); + if IsMatrix( base ) then + M:= base; + elif ForAll( base, IsVectorObj ) then + M:= Matrix( BaseOfGroup( xset ), One( ActingDomain( xset ) ) ); + fi; + if RankMat( M ) = NrCols( M ) then + # this implies injectivity + SetIsInjective(hom,true); + return base; + fi; fi; # otherwise we've to find a basis from the domain. D:=HomeEnumerator(xset); @@ -3487,12 +3494,15 @@ local xset,D,b,t,i,r,pos; t:=[]; r:=Length(D[1]); i:=1; + pos:=[]; while Length(b)Length(t) then + v:= Unpack( D[i] ); +#TODO: try to get rid of 'Unpack' + if RankMat(Concatenation(t,[v]))>Length(t) then # new indep. vector Add(b,D[i]); Add(pos,i); - Add(t,ShallowCopy(D[i])); + Add(t, v); TriangulizeMat(t); # for faster rank tests fi; i:=i+1; diff --git a/lib/vspcrow.gi b/lib/vspcrow.gi index 812c16179f..05df43d515 100644 --- a/lib/vspcrow.gi +++ b/lib/vspcrow.gi @@ -1033,44 +1033,26 @@ InstallMethod( Intersection2, ############################################################################# ## -#M NormedRowVectors( ) +#M NormedRowVectors_internal( , ) ## -InstallMethod( NormedRowVectors, - "for Gaussian row space", - [ IsGaussianRowSpace ], - function( V ) - local base, # basis vectors - elms, # element list, result - elms2, # intermediate element list - F, # `LeftActingDomain( V )' - q, # `Size( F )' - fieldelms, # elements of `F' (in other succession) - j, # loop over `base' - new, # intermediate element list - pos, # position in `new' to store the next element - len, # actual length of `elms2' - i, # loop over field elements - toadd, # vector to add to known vectors - k, # loop over `elms2' - v; # one normed row vector - - if not IsFinite( V ) then - Error( "sorry, cannot compute normed vectors of infinite domain " ); - fi; +## Assume that is a list of row vectors or vector objects over +## that is in echelon form. +## +BindGlobal( "NormedRowVectors_internal", function( F, base ) + local elms, elms2, fieldelms, j, new, pos, len, i, toadd, k; - base:= Reversed( BasisVectors( CanonicalBasis( V ) ) ); if Length( base ) = 0 then return []; + elif not IsFinite( F ) then + Error( "sorry, cannot compute normed vectors over infinite domain " ); fi; + base := Reversed( base ); elms := [ base[1] ]; elms2 := [ base[1] ]; - F := LeftActingDomain( V ); - q := Size( F ); fieldelms := List( AsSSortedList( F ), x -> x - 1 ); for j in [ 1 .. Length( base ) - 1 ] do - # Here `elms2' has the form # $b_i + M = b_i + \langle b_{i+1}, \ldots, b_n \rangle$. # Compute $b_{i-1} + \bigcup_{\lambda\in F} \lambda b_i + ( b_i + M )$. @@ -1080,9 +1062,7 @@ InstallMethod( NormedRowVectors, for i in fieldelms do toadd:= base[j+1] + i * base[j]; for k in [ 1 .. len ] do - v:= elms2[k] + toadd; - v:= ImmutableVector( q, v ); - new[ pos + k ]:= v; + new[ pos + k ]:= ImmutableVector( F, elms2[k] + toadd ); od; pos:= pos + len; od; @@ -1090,9 +1070,27 @@ InstallMethod( NormedRowVectors, # `elms2' is a set here. Append( elms, elms2 ); - od; + # Return the result. + return elms; + end ); + + +############################################################################# +## +#M NormedRowVectors( ) +## +InstallMethod( NormedRowVectors, + "for Gaussian row space", + [ IsGaussianRowSpace ], + function( V ) + local base, # basis vectors + elms; # element list, result + + base:= BasisVectors( CanonicalBasis( V ) ); + elms:= NormedRowVectors_internal( LeftActingDomain( V ), base ); + # The list is strictly sorted, so we store this. MakeImmutable( elms ); Assert( 1, IsSSortedList( elms ) ); diff --git a/tst/testinstall/MatrixObj/acthom.tst b/tst/testinstall/MatrixObj/acthom.tst new file mode 100644 index 0000000000..6f7e73ed3e --- /dev/null +++ b/tst/testinstall/MatrixObj/acthom.tst @@ -0,0 +1,80 @@ +#@local right_representation, q, F, d, filt, groups, G, basis, xset, vectors +#@local orbs, len, D1, v, D2, D3, D4, actions, D, hom, i, g, img, pre +gap> START_TEST( "acthom.tst" ); + +# +gap> right_representation:= function( filt, g ) +> return ( filt = IsPlistRep and IsList( g ) ) or +> ( filt <> IsPlistRep and filt( g ) ); +> end;; +gap> for q in [ 2, 3, 4 ] do +> F:= GF(q); +> for d in [ 2 .. 4 ] do +> for filt in [ IsPlistRep, IsPlistMatrixRep ] do +> PushOptions( rec( ConstructingFilter:= filt ) ); +> # Test groups with special methods and a general group. +> groups:= [ GL( d, F ), SL( d, F ) ]; +> # Add( groups, SylowSubgroup( groups[1], 2 ) ); +> # Add( groups, Subgroup( groups[1], +> # [ GeneratorsOfGroup( groups[1] )[1] ] ) ); +> for G in groups do +> # Consider the G-action +> # - given by the nice monomorphism +> # - on an orbit (linear & projective) +> # - on the whole natural G-set (linear & projective) +> basis:= RowsOfMatrix( One( G ) ); +> xset:= ExternalSet( G ); +> vectors:= HomeEnumerator( xset ); +> orbs:= Orbits( G, vectors ); +> len:= Maximum( List( orbs, Length ) ); +> D1:= [ First( orbs, x -> Length( x ) = len ), OnRight ]; +> v:= OnLines( D1[1][1], One( G ) ); +> D2:= [ Orbit( G, v, OnLines ), OnLines ]; +> D3:= [ vectors, OnRight ]; +> D4:= [ NormedRowVectors_internal( F, basis ), OnLines ]; +> +> actions:= [ NiceMonomorphism( G ) ]; +> for D in [ D1, D2, D3, D4 ] do +> if not CompatibleVectorFilter( One( G ) )( D[1][1] ) then +> Error( "wrong repres. of vector for ", [ q, d, filt ] ); +> fi; +> hom:= ActionHomomorphism( G, D[1], D[2] ); +> if D[2] <> OnLines and +> not IsLinearActionHomomorphism( hom ) then +> Error( "unexpected type of action hom. for ", +> [ q, d, filt ] ); +> fi; +> Add( actions, hom ); +> od; +> +> for hom in actions do +> for i in [ 1 .. 5 ] do +> g:= Random( G ); +> if not right_representation( filt, g ) then +> Error( "wrong repres. of random matrix for ", +> [ q, d, filt ] ); +> fi; +> img:= g^hom; +> pre:= PreImagesRepresentative( hom, img ); +> if not right_representation( filt, pre ) then +> Error( "wrong repres. of preimage matrix for ", +> [ q, d, filt ] ); +> fi; +> if FunctionAction( UnderlyingExternalSet( hom ) ) = OnLines then +> if not ( pre / g in Centre( G ) ) then +> Error( "problem with projective action for ", +> [ q, d, filt ] ); +> fi; +> elif pre <> g then +> Error( "problem with linear action for ", +> [ q, d, filt ] ); +> fi; +> od; +> od; +> od; +> od; +> od; +> od; + +# +gap> STOP_TEST( "acthom.tst" ); From 8d3c3cfc2a90330e408e081eb47ec23d02eccdcb Mon Sep 17 00:00:00 2001 From: ThomasBreuer Date: Thu, 19 Feb 2026 18:09:48 +0100 Subject: [PATCH 04/15] make gaplint happy --- lib/modfree.gi | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/modfree.gi b/lib/modfree.gi index 2082cf8611..7483942761 100644 --- a/lib/modfree.gi +++ b/lib/modfree.gi @@ -179,7 +179,6 @@ InstallMethod( Size, ## The vectors in 'base' are assumed to be linearly independent. BindGlobal( "AsListOfFreeLeftModule_internal", function( F, base, zero ) local elms, # elements list, result - B, # $F$-basis of $V$ new, # intermediate elements list v, # one generator of $V$ i; # loop variable From a77ffb28a19f18a86f423a3816590d26a74021e5 Mon Sep 17 00:00:00 2001 From: ThomasBreuer Date: Thu, 19 Feb 2026 21:02:59 +0100 Subject: [PATCH 05/15] adjust an error message in a test --- tst/testbugfix/2007-07-02-t00180.tst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tst/testbugfix/2007-07-02-t00180.tst b/tst/testbugfix/2007-07-02-t00180.tst index be77f789b5..a7f8e14c7c 100644 --- a/tst/testbugfix/2007-07-02-t00180.tst +++ b/tst/testbugfix/2007-07-02-t00180.tst @@ -1,6 +1,6 @@ # 2007/07/02 (SK) gap> GeneratorsOfRing(Rationals); -Error, cannot compute elements list of infinite domain +Error, cannot compute elements list over infinite domain gap> GeneratorsOfRingWithOne(Rationals); Error, no method found! For debugging hints type ?Recovery from NoMethodFound Error, no 2nd choice method found for `GeneratorsOfRingWithOne' on 1 arguments From eb7be53540eb36853f1146d6a752380f247574f9 Mon Sep 17 00:00:00 2001 From: ThomasBreuer Date: Tue, 3 Mar 2026 12:15:50 +0100 Subject: [PATCH 06/15] add some documentation --- lib/oprt.gi | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/lib/oprt.gi b/lib/oprt.gi index 20c0a590e8..f81d97aa90 100644 --- a/lib/oprt.gi +++ b/lib/oprt.gi @@ -3417,6 +3417,25 @@ end ); ## #M PreImagesRepresentative( , ) . . . . . . . . . . build matrix ## +## The idea is as follows. +## We have an $F$-basis $(v_1, \ldots, v_n)$. +## The matrix $M \in GL(n, F)$ acts first by right multiplication, +## let $w_i = v_i M$. +## Due to the projective action, we know only the normed vectors $c_i w_i$, +## where $c_i \in F \setminus \{ 0 \}$. +## The data computed by 'LinearActionBasis' (if its result is not 'fail') +## provide a vector $v = \sum_i a_i v_i$ such that all $a_i \not= 0$, +## and we know the normed vector $c w$ where $w = v M$ and $c \in F$. +## +## We can compute the decomposition $c w = \sum_i b_i (c_i w_i)$, +## and get $\sum_i b_i (c_i w_i) = c v M = c \sum_i a_i w_i$, +## which means $c_i = c a_i/b_i$ for all $i$. +## Thus we can reconstruct the matrix $M$ up to the scalar factor $c$. +## +## In the cases that are supported, either $c$ is irrelevant because the +## matrix group contains all scalar matrices, or we know that the preimage +## has determinant $1$ and taking the root in question is unique. +## InstallMethod( PreImagesRepresentative,"IsProjectiveActionHomomorphism", FamRangeEqFamElm, [ IsProjectiveActionHomomorphism, IsPerm ], 0, function( hom, elm ) From b97fa1dc17fddcd8bc664238e7f797daa4b4fbed Mon Sep 17 00:00:00 2001 From: ThomasBreuer Date: Tue, 3 Mar 2026 17:13:06 +0100 Subject: [PATCH 07/15] adjust also `IsProjectiveActionHomomorphism` methods --- lib/matobj.gi | 9 --------- lib/oprt.gi | 41 +++++++++++++++++++++++++---------------- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/lib/matobj.gi b/lib/matobj.gi index 19eee2d643..f80b705319 100644 --- a/lib/matobj.gi +++ b/lib/matobj.gi @@ -1222,15 +1222,6 @@ InstallOtherMethod( \^, [ IsVectorObj, IsMatrixObj ], \* ); -#TODO: This is an ugly hack. -# Action homomorphisms for matrix groups use vector spaces, -# which currently consist of lists not vector objects. -# If we want to act on the vectors with matrix groups consisting of -# matrix objects, we have to support the following. -InstallOtherMethod( \^, - [ IsRowVector, IsMatrixObj ], - { v, M } -> v ^ Unpack( M ) ); - ############################################################################ ## diff --git a/lib/oprt.gi b/lib/oprt.gi index f81d97aa90..824b500224 100644 --- a/lib/oprt.gi +++ b/lib/oprt.gi @@ -663,8 +663,7 @@ local xset,surj,G, D, act, fam, filter, hom, i,blockacttest; # vector space base. This will be done the first time, # `LinearActionBasis' is called (i.e. in the preimages routine). filter := filter and IsLinearActionHomomorphism; - elif act=OnLines and IsScalarList( D[1] ) then -#TODO: Fix IsProjectiveActionHomomorphism before IsVectorObj gets supported. + elif act=OnLines then filter := filter and IsProjectiveActionHomomorphism; fi; @@ -3439,7 +3438,7 @@ end ); InstallMethod( PreImagesRepresentative,"IsProjectiveActionHomomorphism", FamRangeEqFamElm, [ IsProjectiveActionHomomorphism, IsPerm ], 0, function( hom, elm ) - local V, mat, xset,lab,f,dim,sol,i; + local V, G, Grep, mat, xset,lab,f,dim,sol,i; # is this method applicable? Test whether field # finite, that the domain contains a vector @@ -3456,18 +3455,20 @@ function( hom, elm ) #if not elm in Image( hom ) then return fail; fi; xset:=UnderlyingExternalSet(hom); V := HomeEnumerator(xset); - f:=DefaultFieldOfMatrixGroup(Source(hom)); - dim:=DimensionOfMatrixGroup(Source(hom)); + G:= Source( hom ); + Grep:= Representative( G ); + f:=DefaultFieldOfMatrixGroup(G); + dim:=DimensionOfMatrixGroup(G); elm:=OnTuples(hom!.projActBasisPositions,elm); # image points elm:=V{elm}; # the corresponding vectors - mat:=elm{[1..dim]}; + mat:= Matrix( List( elm{ [ 1 .. dim ] }, ShallowCopy ), Grep ); sol:=SolutionMat(mat,elm[dim+1]); for i in [1..dim] do - mat[i]:=sol[i]*mat[i]; + MultMatrixRow( mat, i, sol[i] ); od; - mat:=hom!.projActInverse*ImmutableMatrix(f,mat); + mat:= hom!.projActInverse * mat; # correct scalar using determinant if needed if hom!.correctionFactors[1]<>fail then @@ -3478,7 +3479,7 @@ function( hom, elm ) fi; fi; - return mat; + return MakeImmutable( mat ); end); ############################################################################# @@ -3562,7 +3563,7 @@ end); InstallOtherMethod(LinearActionBasis,"projective with extra vector",true, [IsProjectiveActionHomomorphism],0, function(hom) -local xset,G,D,b,t,i,r,binv,pos,dets,roots,dim,f; +local xset,G,Grep,D,b,t,i,r,binv,pos,dets,roots,dim,f,v; xset:=UnderlyingExternalSet(hom); if Size(xset)=0 then return fail; @@ -3570,6 +3571,7 @@ local xset,G,D,b,t,i,r,binv,pos,dets,roots,dim,f; # will the determinants suffice to get suitable scalars? G:= Source(hom); + Grep:= Representative( G ); dim:=DimensionOfMatrixGroup(G); f:=DefaultFieldOfMatrixGroup(G); @@ -3605,11 +3607,13 @@ local xset,G,D,b,t,i,r,binv,pos,dets,roots,dim,f; i:=1; pos:=[]; while Length(b)Length(t) then + v:= Unpack( D[i] ); +#TODO: try to get rid of 'Unpack' + if RankMat(Concatenation(t,[v]))>Length(t) then # new indep. vector Add(b,D[i]); Add(pos,i); - Add(t,ShallowCopy(D[i])); + Add(t,v); TriangulizeMat(t); # for faster rank tests fi; i:=i+1; @@ -3619,15 +3623,20 @@ local xset,G,D,b,t,i,r,binv,pos,dets,roots,dim,f; fi; # try to find a vector that has nonzero coefficients for all b - binv:=Inverse(ImmutableMatrix(f,b)); + binv:= Inverse( ImmutableMatrix( f, Matrix( b, Grep ) ) ); while i<=Length(D) do - if ForAll(D[i]*binv,x->not IsZero(x)) then + if ForAll( Unpack( D[i] * binv ), x -> not IsZero(x) ) then +#TODO: get rid of 'Unpack'? Add(b,D[i]); Add(pos,i); hom!.projActBasisPositions:=pos; - hom!.projActInverse:=ImmutableMatrix(f,binv*Inverse(DiagonalMat(D[i]*binv))); + hom!.projActInverse:= ImmutableMatrix( f, + binv * Inverse( DiagonalMatrix( D[i] * binv, binv ) ) ); hom!.correctionFactors:=[dets,roots]; - return ImmutableMatrix(f,b); + if IsScalarList( D[1] ) then + b:= ImmutableMatrix(f,b); + fi; + return b; fi; i:=i+1; od; From a69aba81ee8c96804ad266b7834cb501b65e921f Mon Sep 17 00:00:00 2001 From: ThomasBreuer Date: Tue, 3 Mar 2026 17:21:52 +0100 Subject: [PATCH 08/15] remove a now unused local variable --- lib/oprt.gi | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/oprt.gi b/lib/oprt.gi index 824b500224..d9d6b34a81 100644 --- a/lib/oprt.gi +++ b/lib/oprt.gi @@ -3438,7 +3438,7 @@ end ); InstallMethod( PreImagesRepresentative,"IsProjectiveActionHomomorphism", FamRangeEqFamElm, [ IsProjectiveActionHomomorphism, IsPerm ], 0, function( hom, elm ) - local V, G, Grep, mat, xset,lab,f,dim,sol,i; + local V, G, Grep, mat, xset,lab,dim,sol,i; # is this method applicable? Test whether field # finite, that the domain contains a vector @@ -3457,7 +3457,6 @@ function( hom, elm ) V := HomeEnumerator(xset); G:= Source( hom ); Grep:= Representative( G ); - f:=DefaultFieldOfMatrixGroup(G); dim:=DimensionOfMatrixGroup(G); elm:=OnTuples(hom!.projActBasisPositions,elm); # image points From f128e987b06ad0111fd04b9eafae76e5847c1662 Mon Sep 17 00:00:00 2001 From: ThomasBreuer Date: Wed, 25 Mar 2026 00:08:18 +0100 Subject: [PATCH 09/15] add `RandomMatrix`, `RandomInvertibleMatrix` and fix the `RankMat` method for `IsPlistMatrixRep` --- doc/ref/matrix.xml | 2 + lib/matobjplist.gi | 8 +- lib/matrix.gd | 144 ++++++++++++++++++ lib/matrix.gi | 95 ++++++++++++ lib/methsel.g | 24 +++ .../MatrixObj/DeterminantMatrix.tst | 29 ++++ .../MatrixObj/RandomInvertibleMatrix.tst | 52 +++++++ tst/testinstall/MatrixObj/RandomMatrix.tst | 72 +++++++++ tst/testinstall/MatrixObj/RankMatrix.tst | 23 +++ 9 files changed, 448 insertions(+), 1 deletion(-) create mode 100644 tst/testinstall/MatrixObj/DeterminantMatrix.tst create mode 100644 tst/testinstall/MatrixObj/RandomInvertibleMatrix.tst create mode 100644 tst/testinstall/MatrixObj/RandomMatrix.tst create mode 100644 tst/testinstall/MatrixObj/RankMatrix.tst diff --git a/doc/ref/matrix.xml b/doc/ref/matrix.xml index 59f006dbfd..e80f6b2419 100644 --- a/doc/ref/matrix.xml +++ b/doc/ref/matrix.xml @@ -341,7 +341,9 @@ see . Random Matrices <#Include Label="RandomMat"> +<#Include Label="RandomMatrix"> <#Include Label="RandomInvertibleMat"> +<#Include Label="RandomInvertibleMatrix"> <#Include Label="RandomUnimodularMat"> diff --git a/lib/matobjplist.gi b/lib/matobjplist.gi index 7a1b99f4d4..ee52f8d303 100644 --- a/lib/matobjplist.gi +++ b/lib/matobjplist.gi @@ -1222,7 +1222,13 @@ InstallMethod( InverseSameMutability, InstallMethod( RankMat, [ "IsPlistMatrixRep" ], - M -> RankMat( List( M![ROWSPOS], x -> x![ELSPOS] ) ) ); + function( M ) + M:= M![ROWSPOS]; + if Length( M ) = 0 then + return 0; + fi; + return RankMat( List( M, x -> x![ELSPOS] ) ); + end ); InstallMethodWithRandomSource( Randomize, "for a random source and a mutable plist matrix", diff --git a/lib/matrix.gd b/lib/matrix.gd index 8056c80165..2c09629047 100644 --- a/lib/matrix.gd +++ b/lib/matrix.gd @@ -1887,6 +1887,78 @@ DeclareGlobalFunction( "DiagonalMat" ); DeclareGlobalFunction( "ReflectionMat" ); +############################################################################# +## +#O RandomInvertibleMatrix( [, ][, ], ) +#O RandomInvertibleMatrix( [, ], ) +## +## <#GAPDoc Label="RandomInvertibleMatrix"> +## +## +## +## +## +## an invertible matrix or matrix object with m columns, +## with base domain R or equal to BaseDomain( M ). +## +## +## If a semiring R is given then it will be the base domain +## (see ) +## of the returned matrix. +## In this case, a filter filt can be specified that defines the +## internal representation of the result +## (see ). +## The default value for filt is determined from R. +##

+## If a matrix object M is given then the returned matrix will have +## the same internal representation and the same base domain as M. +##

+## If a random source rs is given (see ) +## then the entries of the result are computed using rs, +## the default random source is . +##

+## If the value +## of the result implies then the result is +## fully mutable. +##

+## m:= RandomInvertibleMatrix( GF(9), 2 );; +## gap> RankMatrix( m ); +## 2 +## gap> DimensionsMat( m ); +## [ 2, 2 ] +## gap> BaseDomain( m ); +## GF(3^2) +## gap> Is8BitMatrixRep( m ); +## true +## gap> m1:= RandomInvertibleMatrix( IsPlistMatrixRep, GF(9), 2 );; +## gap> IsPlistMatrixRep( m1 ); +## true +## gap> m2:= RandomInvertibleMatrix( 2, m1 );; +## gap> IsPlistMatrixRep( m2 ); +## true +## ]]> +## +## +## <#/GAPDoc> +## +DeclareTagBasedOperation( "RandomInvertibleMatrix", + [ IsOperation, IsRandomSource, IsSemiring, IsInt ] ); +DeclareOperation( "RandomInvertibleMatrix", + [ IsRandomSource, IsSemiring, IsInt ] ); +DeclareOperation( "RandomInvertibleMatrix", + [ IsOperation, IsSemiring, IsInt ] ); +DeclareOperation( "RandomInvertibleMatrix", + [ IsSemiring, IsInt ] ); + +DeclareOperation( "RandomInvertibleMatrix", + [ IsRandomSource, IsInt, IsMatrixOrMatrixObj ] ); +DeclareOperation( "RandomInvertibleMatrix", + [ IsInt, IsMatrixOrMatrixObj ] ); + + ############################################################################# ## #F RandomInvertibleMat( [rs ,] [, ] ) . . . random invertible matrix @@ -1939,6 +2011,78 @@ DeclareGlobalFunction( "RandomInvertibleMat" ); DeclareGlobalFunction( "RandomMat" ); +############################################################################# +## +#O RandomMatrix( [, ][, ], , ) +#O RandomMatrix( [, ], , ) +## +## <#GAPDoc Label="RandomMatrix"> +## +## +## +## +## +## a matrix or matrix object with m rows and n columns, +## with base domain R or equal to BaseDomain( M ). +## +## +## If a semiring R is given then it will be the base domain +## (see ) +## of the returned matrix. +## In this case, a filter filt can be specified that defines the +## internal representation of the result +## (see ). +## The default value for filt is determined from R. +##

+## If a matrix object M is given then the returned matrix will have +## the same internal representation and the same base domain as M. +##

+## If a random source rs is given (see ) +## then the entries of the result are computed using rs, +## the default random source is . +##

+## If the value +## of the result implies then the result is +## fully mutable. +##

+## m:= RandomMatrix( GF(9), 2, 3 );; +## gap> DimensionsMat( m ); +## [ 2, 3 ] +## gap> BaseDomain( m ); +## GF(3^2) +## gap> Is8BitMatrixRep( m ); +## true +## gap> m1:= RandomMatrix( IsPlistMatrixRep, GF(9), 2, 3 );; +## gap> IsPlistMatrixRep( m1 ); +## true +## gap> m2:= RandomMatrix( 3, 4, m1 );; +## gap> DimensionsMat( m2 ); +## [ 3, 4 ] +## gap> IsPlistMatrixRep( m2 ); +## true +## ]]> +## +## +## <#/GAPDoc> +## +DeclareTagBasedOperation( "RandomMatrix", + [ IsOperation, IsRandomSource, IsSemiring, IsInt, IsInt ] ); +DeclareOperation( "RandomMatrix", + [ IsRandomSource, IsSemiring, IsInt, IsInt ] ); +DeclareOperation( "RandomMatrix", + [ IsOperation, IsSemiring, IsInt, IsInt ] ); +DeclareOperation( "RandomMatrix", + [ IsSemiring, IsInt, IsInt ] ); + +DeclareOperation( "RandomMatrix", + [ IsRandomSource, IsInt, IsInt, IsMatrixOrMatrixObj ] ); +DeclareOperation( "RandomMatrix", + [ IsInt, IsInt, IsMatrixOrMatrixObj ] ); + + ############################################################################# ## #F RandomUnimodularMat( [rs ,] ) . . . . . . . . random unimodular matrix diff --git a/lib/matrix.gi b/lib/matrix.gi index b98e8d9e1d..5d53cea83d 100644 --- a/lib/matrix.gi +++ b/lib/matrix.gi @@ -3952,6 +3952,55 @@ InstallGlobalFunction( ReflectionMat, function( arg ) end ); +######################################################################### +## +#M RandomInvertibleMatrix( [, ][, ], ) +#M RandomInvertibleMatrix( [, ], ) +## +InstallTagBasedMethod( RandomInvertibleMatrix, + function( filt, rs, R, m ) + local mat, det; + + mat:= ZeroMatrix( filt, R, m, m ); + repeat + Randomize( rs, mat ); + det:= DeterminantMat( mat ); + until not IsZero( det ); + + return mat; + end ); + +InstallMethod( RandomInvertibleMatrix, + [ IsRandomSource, IsSemiring, IsInt ], + function( rs, R, m ) + return RandomInvertibleMatrix( DefaultMatrixRepForBaseDomain( R ), rs, R, m ); + end ); + +InstallMethod( RandomInvertibleMatrix, + [ IsOperation, IsSemiring, IsInt ], + function( filt, R, m ) + return RandomInvertibleMatrix( filt, GlobalMersenneTwister, R, m ); + end ); + +InstallMethod( RandomInvertibleMatrix, + [ IsSemiring, IsInt ], + function( R, m ) + return RandomInvertibleMatrix( DefaultMatrixRepForBaseDomain( R ), GlobalMersenneTwister, R, m ); + end ); + +InstallMethod( RandomInvertibleMatrix, + [ IsRandomSource, IsInt, IsMatrixOrMatrixObj ], + function( rs, m, M ) + return RandomInvertibleMatrix( ConstructingFilter( M ), rs, BaseDomain( M ), m ); + end ); + +InstallMethod( RandomInvertibleMatrix, + [ IsInt, IsMatrixOrMatrixObj ], + function( m, M ) + return RandomInvertibleMatrix( ConstructingFilter( M ), GlobalMersenneTwister, BaseDomain( M ), m ); + end ); + + ######################################################################### ## #F RandomInvertibleMat( [rs ,] [, ] ) . . . make a random invertible matrix @@ -4001,6 +4050,52 @@ InstallGlobalFunction( RandomInvertibleMat, function ( arg ) end ); +######################################################################### +## +#M RandomMatrix( [, ][, ], , ) +#M RandomMatrix( [, ], , ) +## +InstallTagBasedMethod( RandomMatrix, + function( filt, rs, R, m, n ) + local mat; + + mat:= ZeroMatrix( filt, R, m, n ); + Randomize( rs, mat ); + + return mat; + end ); + +InstallMethod( RandomMatrix, + [ IsRandomSource, IsSemiring, IsInt, IsInt ], + function( rs, R, m, n ) + return RandomMatrix( DefaultMatrixRepForBaseDomain( R ), rs, R, m, n ); + end ); + +InstallMethod( RandomMatrix, + [ IsOperation, IsSemiring, IsInt, IsInt ], + function( filt, R, m, n ) + return RandomMatrix( filt, GlobalMersenneTwister, R, m, n ); + end ); + +InstallMethod( RandomMatrix, + [ IsSemiring, IsInt, IsInt ], + function( R, m, n ) + return RandomMatrix( DefaultMatrixRepForBaseDomain( R ), GlobalMersenneTwister, R, m, n ); + end ); + +InstallMethod( RandomMatrix, + [ IsRandomSource, IsInt, IsInt, IsMatrixOrMatrixObj ], + function( rs, m, n, M ) + return RandomMatrix( ConstructingFilter( M ), rs, BaseDomain( M ), m, n ); + end ); + +InstallMethod( RandomMatrix, + [ IsInt, IsInt, IsMatrixOrMatrixObj ], + function( m, n, M ) + return RandomMatrix( ConstructingFilter( M ), GlobalMersenneTwister, BaseDomain( M ), m, n ); + end ); + + ############################################################################# ## #F RandomMat( [rs ,] , [, ] ) . . . . . . . . make a random matrix diff --git a/lib/methsel.g b/lib/methsel.g index 54e1e02fd9..1f8634f7c9 100644 --- a/lib/methsel.g +++ b/lib/methsel.g @@ -253,6 +253,30 @@ BIND_GLOBAL( "NewTagBasedOperation", return method( req1, req2, req3, req4 ); end ); + elif LENGTH( requirements ) = 5 then + InstallEarlyMethod( oper, + function( req1, req2, req3, req4, req5 ) + local method; + + if not ( requirements[1]( req1 ) and + requirements[2]( req2 ) and + requirements[3]( req3 ) and + requirements[4]( req4 ) and + requirements[5]( req5 ) ) then + TryNextMethod(); + fi; + + method:= FIND_OBJ_MAP( methods, req1, fail ); + if method = fail then + # Take the default method if there is one. + method:= FIND_OBJ_MAP( methods, IS_OBJECT, fail ); + fi; + if method = fail then + Error( "no default installed for tag based operation " ); + fi; + + return method( req1, req2, req3, req4, req5 ); + end ); else Error( "tag based operations for ", LENGTH( requirements ), " arguments are currently not supported" ); diff --git a/tst/testinstall/MatrixObj/DeterminantMatrix.tst b/tst/testinstall/MatrixObj/DeterminantMatrix.tst new file mode 100644 index 0000000000..0a4d88fb18 --- /dev/null +++ b/tst/testinstall/MatrixObj/DeterminantMatrix.tst @@ -0,0 +1,29 @@ +#@local M, mat +gap> START_TEST( "DeterminantMatrix.tst" ); + +# nonsquare matrix +gap> mat:= [[1,2,1,2],[1,0,1,0],[2,2,2,2]];; +gap> M:= NewMatrix( IsPlistMatrixRep, GF(5), 4, mat*Z(5)^0 );; +gap> DeterminantMatrix( M ); +Error, must be a square matrix at least 1x1 + +# 0x0 matrix +gap> M:= ZeroMatrix( IsPlistRep, GF(9), 0, 0 ); +[ ] +gap> DeterminantMatrix( M ); +Error, no method found! For debugging hints type ?Recovery from NoMethodFound +Error, no 1st choice method found for `DeterminantMatrix' on 1 arguments +gap> M:= ZeroMatrix( IsPlistMatrixRep, GF(9), 0, 0 ); +<0x0-matrix over GF(3^2)> +gap> DeterminantMatrix( M ); +Error, no method found! For debugging hints type ?Recovery from NoMethodFound +Error, no 1st choice method found for `DeterminantMatrix' on 1 arguments + +# square nonempty matrix +gap> mat:= [[1,2,1],[1,0,1],[2,2,2]];; +gap> M:= NewMatrix( IsPlistMatrixRep, GF(5), 3, mat*Z(5)^0 );; +gap> DeterminantMatrix( M ); +0*Z(5) + +# +gap> STOP_TEST( "DeterminantMatrix.tst" ); diff --git a/tst/testinstall/MatrixObj/RandomInvertibleMatrix.tst b/tst/testinstall/MatrixObj/RandomInvertibleMatrix.tst new file mode 100644 index 0000000000..258d6c2934 --- /dev/null +++ b/tst/testinstall/MatrixObj/RandomInvertibleMatrix.tst @@ -0,0 +1,52 @@ +#@local rs, M +gap> START_TEST( "RandomInvertibleMatrix.tst" ); + +# with base domain +gap> Is8BitMatrixRep( RandomInvertibleMatrix( GF(9), 10 ) ); +true +gap> IsPlistMatrixRep( RandomInvertibleMatrix( Integers, 10 ) ); +true + +# with constructing filter and base domain +gap> Is8BitMatrixRep( RandomInvertibleMatrix( Is8BitMatrixRep, GF(9), 10 ) ); +true +gap> IsPlistRep( RandomInvertibleMatrix( IsPlistRep, Integers, 10 ) ); +true + +# with random source and base domain +gap> rs:= RandomSource(IsMersenneTwister);; +gap> Is8BitMatrixRep( RandomInvertibleMatrix( Is8BitMatrixRep, GF(9), 10 ) ); +true +gap> IsPlistRep( RandomInvertibleMatrix( IsPlistRep, Integers, 10 ) ); +true + +# with constructing filter, random source, and base domain +gap> Is8BitMatrixRep( RandomInvertibleMatrix( Is8BitMatrixRep, rs, GF(9), 10 ) ); +true +gap> IsPlistRep( RandomInvertibleMatrix( IsPlistRep, rs, Integers, 10 ) ); +true + +# with example matrix +gap> M:= Matrix( IsPlistMatrixRep, Integers, [ 1 ], 1 );; +gap> RandomInvertibleMatrix( 2, M ); +<2x2-matrix over Integers> +gap> M:= [ [ 1 ] ];; +gap> IsPlistRep( RandomInvertibleMatrix( 2, M ) ); +true +gap> M:= Matrix( IsPlistMatrixRep, GF(3), [ Z(3) ], 1 );; +gap> RandomInvertibleMatrix( 2, M ); +<2x2-matrix over GF(3)> + +# with random source and example matrix +gap> M:= Matrix( IsPlistMatrixRep, Integers, [ 1 ], 1 );; +gap> RandomInvertibleMatrix( rs, 2, M ); +<2x2-matrix over Integers> +gap> M:= [ [ 1 ] ];; +gap> IsPlistRep( RandomInvertibleMatrix( rs, 2, M ) ); +true +gap> M:= Matrix( IsPlistMatrixRep, GF(3), [ Z(3) ], 1 );; +gap> RandomInvertibleMatrix( rs, 2, M ); +<2x2-matrix over GF(3)> + +# +gap> STOP_TEST( "RandomInvertibleMatrix.tst" ); diff --git a/tst/testinstall/MatrixObj/RandomMatrix.tst b/tst/testinstall/MatrixObj/RandomMatrix.tst new file mode 100644 index 0000000000..d4460cf9c0 --- /dev/null +++ b/tst/testinstall/MatrixObj/RandomMatrix.tst @@ -0,0 +1,72 @@ +#@local rs, M +gap> START_TEST( "RandomMatrix.tst" ); + +# with base domain +gap> Is8BitMatrixRep( RandomMatrix( GF(9), 10, 5 ) ); +true +gap> RandomMatrix( GF(9), 0, 1 ); +Error, Is8BitMatrixRep with zero rows not yet supported +gap> IsPlistMatrixRep( RandomMatrix( Integers, 10, 5 ) ); +true +gap> IsPlistMatrixRep( RandomMatrix( Integers, 0, 0 ) ); +true + +# with constructing filter and base domain +gap> Is8BitMatrixRep( RandomMatrix( Is8BitMatrixRep, GF(9), 10, 5 ) ); +true +gap> IsPlistMatrixRep( RandomMatrix( IsPlistMatrixRep, GF(9), 0, 0 ) ); +true +gap> IsPlistRep( RandomMatrix( IsPlistRep, Integers, 10, 5 ) ); +true + +# with random source and base domain +gap> rs:= RandomSource(IsMersenneTwister);; +gap> Is8BitMatrixRep( RandomMatrix( Is8BitMatrixRep, GF(9), 10, 5 ) ); +true +gap> IsPlistMatrixRep( RandomMatrix( IsPlistMatrixRep, GF(9), 0, 0 ) ); +true +gap> IsPlistRep( RandomMatrix( IsPlistRep, Integers, 10, 5 ) ); +true + +# with constructing filter, random source, and base domain +gap> Is8BitMatrixRep( RandomMatrix( Is8BitMatrixRep, rs, GF(9), 10, 5 ) ); +true +gap> IsPlistMatrixRep( RandomMatrix( IsPlistMatrixRep, rs, GF(9), 0, 0 ) ); +true +gap> IsPlistRep( RandomMatrix( IsPlistRep, rs, Integers, 10, 5 ) ); +true + +# with example matrix +gap> M:= Matrix( IsPlistMatrixRep, Integers, [ 1 ], 1 );; +gap> RandomMatrix( 2, 2, M ); +<2x2-matrix over Integers> +gap> RandomMatrix( 0, 0, M ); +<0x0-matrix over Integers> +gap> M:= [ [ 1 ] ];; +gap> BaseDomain( M ); +Rationals +gap> IsPlistRep( RandomMatrix( 2, 2, M ) ); +true +gap> ForAll( Flat( RandomMatrix( 1, 2, M ) ), IsRat ); +true +gap> M:= Matrix( IsPlistMatrixRep, GF(3), [ Z(3) ], 1 );; +gap> RandomMatrix( 2, 2, M ); +<2x2-matrix over GF(3)> + +# with random source and example matrix +gap> M:= Matrix( IsPlistMatrixRep, Integers, [ 1 ], 1 );; +gap> RandomMatrix( rs, 2, 2, M ); +<2x2-matrix over Integers> +gap> RandomMatrix( rs, 0, 0, M ); +<0x0-matrix over Integers> +gap> M:= [ [ 1 ] ];; +gap> IsPlistRep( RandomMatrix( rs, 2, 2, M ) ); +true +gap> ForAll( Flat( RandomMatrix( rs, 1, 2, M ) ), IsRat ); +true +gap> M:= Matrix( IsPlistMatrixRep, GF(3), [ Z(3) ], 1 );; +gap> RandomMatrix( rs, 2, 2, M ); +<2x2-matrix over GF(3)> + +# +gap> STOP_TEST( "RandomMatrix.tst" ); diff --git a/tst/testinstall/MatrixObj/RankMatrix.tst b/tst/testinstall/MatrixObj/RankMatrix.tst new file mode 100644 index 0000000000..d8561b2806 --- /dev/null +++ b/tst/testinstall/MatrixObj/RankMatrix.tst @@ -0,0 +1,23 @@ +#@local M, mat +gap> START_TEST( "RankMatrix.tst" ); + +# nonsquare matrix +gap> mat:= [[1,2,1,2],[1,0,1,0],[2,2,2,2]];; +gap> M:= NewMatrix( IsPlistMatrixRep, GF(5), 4, mat*Z(5)^0 ); +<3x4-matrix over GF(5)> +gap> RankMatrix( M ); +2 + +# 0x0 matrix +gap> M:= ZeroMatrix( IsPlistRep, GF(9), 0, 0 ); +[ ] +gap> RankMatrix( M ); +Error, no method found! For debugging hints type ?Recovery from NoMethodFound +Error, no 1st choice method found for `RankMatrix' on 1 arguments +gap> M:= ZeroMatrix( IsPlistMatrixRep, GF(9), 0, 0 ); +<0x0-matrix over GF(3^2)> +gap> RankMatrix( M ); +0 + +# +gap> STOP_TEST( "RankMatrix.tst" ); From e145c01b5d1b7a80ae2c0673cbf1c234118fff1a Mon Sep 17 00:00:00 2001 From: ThomasBreuer Date: Wed, 25 Mar 2026 00:52:33 +0100 Subject: [PATCH 10/15] address a comment: use the new `RandomInvertibleMatrix` --- lib/grpffmat.gi | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/lib/grpffmat.gi b/lib/grpffmat.gi index e1d6d71a19..3057217b5a 100644 --- a/lib/grpffmat.gi +++ b/lib/grpffmat.gi @@ -614,24 +614,18 @@ InstallMethodWithRandomSource( Random, "for a random source and natural SL", [ IsRandomSource, IsFFEMatrixGroup and IsFinite and IsNaturalSL ], function(rs, G) - local d, m, F, det; - + local d, m; + +#TODO: This code assumes that all elements in the group have the same +# 'BaseDomain' value (hence we may take any 'Representative'). +# If this assumption is not valid then we have to call the +# four argument variant of 'RandomInvertibleMatrix'. +# Moreover, the code assumes that 'ImmutableMatrix' is not needed +# in the situation that the result is in 'IsMatrix'. d:= DimensionOfMatrixGroup( G ); - m:= Representative( G ); - if IsMatrix( m ) then - F:= FieldOfMatrixGroup( G ); - m:= RandomInvertibleMat( rs, d, F ); - MultVector( m[1], DeterminantMat( m )^-1 ); - m:= ImmutableMatrix( F, m, true ); - else - m:= ZeroMatrix( d, d, m ); - repeat - Randomize( rs, m ); - det:= DeterminantMat( m ); - until not IsZero( det ); - MultMatrixRow( m, 1, det^-1 ); - MakeImmutable( m ); - fi; + m:= RandomInvertibleMatrix( rs, d, Representative( G ) ); + MultMatrixRow( m, 1, DeterminantMatrix( m )^-1 ); + MakeImmutable( m ); return m; end); From 24920cadaed1ebb3e4dc0683eeb5d2633a462b34 Mon Sep 17 00:00:00 2001 From: ThomasBreuer Date: Wed, 25 Mar 2026 10:10:20 +0100 Subject: [PATCH 11/15] adjust manual examples Due to new examples in the chapter about matrices, the number of `Random` calls has increased, thus it is not surprising that some outputs involving such calls have changed. --- lib/matrix.gd | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/matrix.gd b/lib/matrix.gd index 2c09629047..f5b00b8f09 100644 --- a/lib/matrix.gd +++ b/lib/matrix.gd @@ -1974,11 +1974,11 @@ DeclareOperation( "RandomInvertibleMatrix", ## Optionally, a random source rs can be supplied. ## m := RandomInvertibleMat(4); -## [ [ -4, 1, 0, -1 ], [ -1, -1, 1, -1 ], [ 1, -2, -1, -2 ], -## [ 0, -1, 2, -2 ] ] +## [ [ 1, -4, 0, 1 ], [ 0, -2, 3, 1 ], [ 0, -2, -1, -1 ], +## [ 0, -2, 2, 4 ] ] ## gap> m^-1; -## [ [ -1/8, -11/24, 1/24, 1/4 ], [ 1/4, -13/12, -1/12, 1/2 ], -## [ -1/8, 5/24, -7/24, 1/4 ], [ -1/4, 3/4, -1/4, -1/2 ] ] +## [ [ 1, -1/14, -19/14, -4/7 ], [ 0, -1/14, -5/14, -1/14 ], +## [ 0, 5/14, -3/14, -1/7 ], [ 0, -3/14, -1/14, 2/7 ] ] ## ]]> ## ## @@ -2099,13 +2099,13 @@ DeclareOperation( "RandomMatrix", ## from Integers ## m := RandomUnimodularMat(3); -## [ [ -5, 1, 0 ], [ 12, -2, -1 ], [ -14, 3, 0 ] ] +## [ [ 11, 16, -11 ], [ -2, -3, 2 ], [ -1, 3, 0 ] ] ## gap> m^-1; -## [ [ -3, 0, 1 ], [ -14, 0, 5 ], [ -8, -1, 2 ] ] +## [ [ -6, -33, -1 ], [ -2, -11, 0 ], [ -9, -49, -1 ] ] ## gap> RandomUnimodularMat(3:domain:=[-1000..1000]); -## [ [ 312330173, 15560030349, -125721926670 ], -## [ -307290, -15309014, 123693281 ], -## [ -684293792, -34090949551, 275448039848 ] ] +## [ [ -315847801, 117009136366294, -5631029155564 ], +## [ 726663524, -269200137227249, 12955174856287 ], +## [ -9171690, 3397748911844, -163515634591 ] ] ## ]]> ## ## From 53e33ff5abdf3d5542b68089cf4ff19304d17f3c Mon Sep 17 00:00:00 2001 From: ThomasBreuer Date: Mon, 13 Apr 2026 13:30:08 +0200 Subject: [PATCH 12/15] fix the use of the `"ConstructingFilter"` option --- grp/classic.gd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grp/classic.gd b/grp/classic.gd index 0bfd9b3b4b..ae891da7be 100644 --- a/grp/classic.gd +++ b/grp/classic.gd @@ -836,7 +836,7 @@ BindGlobal( "SymplecticGroup", function ( arg ) rep:= filt; filt:= IsMatrixGroup; else - rep:= fail; + rep:= ValueOption( "ConstructingFilter" ); fi; if DescribesInvariantBilinearForm( Last( arg ) ) then form:= Remove( arg ); From f68512424db2f7ab331d01c034844835df63e755 Mon Sep 17 00:00:00 2001 From: ThomasBreuer Date: Mon, 20 Apr 2026 12:05:22 +0200 Subject: [PATCH 13/15] adjust to #6328 --- tst/testinstall/MatrixObj/DeterminantMatrix.tst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tst/testinstall/MatrixObj/DeterminantMatrix.tst b/tst/testinstall/MatrixObj/DeterminantMatrix.tst index 0a4d88fb18..8d9f63eeca 100644 --- a/tst/testinstall/MatrixObj/DeterminantMatrix.tst +++ b/tst/testinstall/MatrixObj/DeterminantMatrix.tst @@ -5,7 +5,7 @@ gap> START_TEST( "DeterminantMatrix.tst" ); gap> mat:= [[1,2,1,2],[1,0,1,0],[2,2,2,2]];; gap> M:= NewMatrix( IsPlistMatrixRep, GF(5), 4, mat*Z(5)^0 );; gap> DeterminantMatrix( M ); -Error, must be a square matrix at least 1x1 +Error, DeterminantMat: must be a nonempty square matrix # 0x0 matrix gap> M:= ZeroMatrix( IsPlistRep, GF(9), 0, 0 ); From ee41838380cfab125e8ff56bbe2f46c90ff51e35 Mon Sep 17 00:00:00 2001 From: ThomasBreuer Date: Wed, 22 Apr 2026 09:45:36 +0200 Subject: [PATCH 14/15] update a comment in the code Since #6299, it is documented that all matrix objects in a matrix group must have the same `BaseDomain` value. --- lib/grpffmat.gi | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/lib/grpffmat.gi b/lib/grpffmat.gi index 3057217b5a..8acaffc207 100644 --- a/lib/grpffmat.gi +++ b/lib/grpffmat.gi @@ -616,12 +616,10 @@ InstallMethodWithRandomSource( Random, function(rs, G) local d, m; -#TODO: This code assumes that all elements in the group have the same -# 'BaseDomain' value (hence we may take any 'Representative'). -# If this assumption is not valid then we have to call the -# four argument variant of 'RandomInvertibleMatrix'. -# Moreover, the code assumes that 'ImmutableMatrix' is not needed -# in the situation that the result is in 'IsMatrix'. + # We assume that all elements in the group have the same + # 'BaseDomain' value, hence we may take any 'Representative'. + # see the section "Groups Consisting of Matrix Objects" + # in the Reference Manual. d:= DimensionOfMatrixGroup( G ); m:= RandomInvertibleMatrix( rs, d, Representative( G ) ); MultMatrixRow( m, 1, DeterminantMatrix( m )^-1 ); From c5b44fb9abcdf34fa47fa147f47cfaaa60f02acc Mon Sep 17 00:00:00 2001 From: ThomasBreuer Date: Wed, 22 Apr 2026 11:16:07 +0200 Subject: [PATCH 15/15] change the definition of `RandomInvertibleMatrix` The result is guaranteed to be invertible over the prescribed `R`. --- lib/matrix.gd | 26 ++++++++++++++++++-------- lib/matrix.gi | 28 ++++++++++++++++++++++------ 2 files changed, 40 insertions(+), 14 deletions(-) diff --git a/lib/matrix.gd b/lib/matrix.gd index f5b00b8f09..4fe2262981 100644 --- a/lib/matrix.gd +++ b/lib/matrix.gd @@ -1900,8 +1900,9 @@ DeclareGlobalFunction( "ReflectionMat" ); ## Label="with example matrix"/> ## ## -## an invertible matrix or matrix object with m columns, -## with base domain R or equal to BaseDomain( M ). +## a matrix or matrix object I with m columns, +## with base domain R or equal to BaseDomain( M ), +## such that I is invertible over its base domain. ## ## ## If a semiring R is given then it will be the base domain @@ -1939,6 +1940,9 @@ DeclareGlobalFunction( "ReflectionMat" ); ## gap> m2:= RandomInvertibleMatrix( 2, m1 );; ## gap> IsPlistMatrixRep( m2 ); ## true +## gap> m:= RandomInvertibleMatrix( Integers, 2 );; +## gap> IsUnit( Integers, DeterminantMatrix( m ) ); +## true ## ]]> ## ## @@ -1972,8 +1976,14 @@ DeclareOperation( "RandomInvertibleMatrix", ## matrix with m rows and columns with elements taken from the ring ## R, which defaults to . ## Optionally, a random source rs can be supplied. +##

+## Note that the result need not be invertible over +## the prescribed ring R. +## Use +## for creating random matrices with this stronger property. +##

## m := RandomInvertibleMat(4); +## gap> m := RandomInvertibleMat( 4, Integers ); ## [ [ 1, -4, 0, 1 ], [ 0, -2, 3, 1 ], [ 0, -2, -1, -1 ], ## [ 0, -2, 2, 4 ] ] ## gap> m^-1; @@ -2099,13 +2109,13 @@ DeclareOperation( "RandomMatrix", ## from Integers ## m := RandomUnimodularMat(3); -## [ [ 11, 16, -11 ], [ -2, -3, 2 ], [ -1, 3, 0 ] ] +## [ [ 1, -5, 26 ], [ 0, 1, -6 ], [ 0, 0, 1 ] ] ## gap> m^-1; -## [ [ -6, -33, -1 ], [ -2, -11, 0 ], [ -9, -49, -1 ] ] +## [ [ 1, 5, 4 ], [ 0, 1, 6 ], [ 0, 0, 1 ] ] ## gap> RandomUnimodularMat(3:domain:=[-1000..1000]); -## [ [ -315847801, 117009136366294, -5631029155564 ], -## [ 726663524, -269200137227249, 12955174856287 ], -## [ -9171690, 3397748911844, -163515634591 ] ] +## [ [ 6001314, 239514853714734, -44705745791413 ], +## [ -1448123744, -57795200635226587, 10787546189680040 ], +## [ -671, -26780118200, 4998542420 ] ] ## ]]> ## ## diff --git a/lib/matrix.gi b/lib/matrix.gi index 5d53cea83d..9853a7c978 100644 --- a/lib/matrix.gi +++ b/lib/matrix.gi @@ -3959,13 +3959,29 @@ end ); ## InstallTagBasedMethod( RandomInvertibleMatrix, function( filt, rs, R, m ) - local mat, det; + local mat, i, j; - mat:= ZeroMatrix( filt, R, m, m ); - repeat - Randomize( rs, mat ); - det:= DeterminantMat( mat ); - until not IsZero( det ); + if IsIntegers( R ) then + # We have a dedicated method for this case. + mat:= RandomUnimodularMat( rs, m ); + if filt <> IsPlistRep then + mat:= Matrix( filt, R, mat ); + fi; + else + # The following works if 'R' is a field or a residue class ring. + # If other rings become important, + # we have to think about a better approach. + mat:= ZeroMatrix( filt, R, m, m ); + repeat + # 'Randomize' does not admit 'R' as an argument, + # and we want to cover also 'IsPlistRep'. + for i in [ 1 .. m ] do + for j in [ 1 .. m ] do + mat[i,j]:= Random( rs, R ); + od; + od; + until IsUnit( DeterminantMat( mat ) ); + fi; return mat; end );