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/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 );
diff --git a/lib/grpffmat.gi b/lib/grpffmat.gi
index 147563c60b..8acaffc207 100644
--- a/lib/grpffmat.gi
+++ b/lib/grpffmat.gi
@@ -124,31 +124,80 @@ end );
#############################################################################
##
-#M NiceMonomorphism( )
+## 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
+##
+## '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, 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 q = 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 );
-
+ xset := ExternalSet( grp );
# 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, RowsOfMatrix( One( grp ) ) );
+ fi;
nice := ActionHomomorphism( xset,"surjective" );
SetIsInjective( nice, true );
if not HasNiceMonomorphism(grp) then
@@ -156,14 +205,14 @@ 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]:=[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 +239,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 );
#############################################################################
@@ -256,6 +304,19 @@ 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,
function( mat, G )
@@ -266,6 +327,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 );
+
#############################################################################
##
@@ -508,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);
@@ -527,11 +614,18 @@ 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;
+
+ # 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 );
+ MakeImmutable( m );
+
+ return m;
end);
#############################################################################
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..4fe2262981 100644
--- a/lib/matrix.gd
+++ b/lib/matrix.gd
@@ -1887,6 +1887,82 @@ DeclareGlobalFunction( "DiagonalMat" );
DeclareGlobalFunction( "ReflectionMat" );
+#############################################################################
+##
+#O RandomInvertibleMatrix( [, ][, ], )
+#O RandomInvertibleMatrix( [, ], )
+##
+## <#GAPDoc Label="RandomInvertibleMatrix">
+##
+##
+##
+##
+##
+## 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
+## (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
+## gap> m:= RandomInvertibleMatrix( Integers, 2 );;
+## gap> IsUnit( Integers, DeterminantMatrix( m ) );
+## 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
@@ -1900,13 +1976,19 @@ DeclareGlobalFunction( "ReflectionMat" );
## 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);
-## [ [ -4, 1, 0, -1 ], [ -1, -1, 1, -1 ], [ 1, -2, -1, -2 ],
-## [ 0, -1, 2, -2 ] ]
+## gap> m := RandomInvertibleMat( 4, Integers );
+## [ [ 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 ] ]
## ]]>
##
##
@@ -1939,6 +2021,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
@@ -1955,13 +2109,13 @@ DeclareGlobalFunction( "RandomMat" );
## from Integers
## m := RandomUnimodularMat(3);
-## [ [ -5, 1, 0 ], [ 12, -2, -1 ], [ -14, 3, 0 ] ]
+## [ [ 1, -5, 26 ], [ 0, 1, -6 ], [ 0, 0, 1 ] ]
## gap> m^-1;
-## [ [ -3, 0, 1 ], [ -14, 0, 5 ], [ -8, -1, 2 ] ]
+## [ [ 1, 5, 4 ], [ 0, 1, 6 ], [ 0, 0, 1 ] ]
## gap> RandomUnimodularMat(3:domain:=[-1000..1000]);
-## [ [ 312330173, 15560030349, -125721926670 ],
-## [ -307290, -15309014, 123693281 ],
-## [ -684293792, -34090949551, 275448039848 ] ]
+## [ [ 6001314, 239514853714734, -44705745791413 ],
+## [ -1448123744, -57795200635226587, 10787546189680040 ],
+## [ -671, -26780118200, 4998542420 ] ]
## ]]>
##
##
diff --git a/lib/matrix.gi b/lib/matrix.gi
index b98e8d9e1d..9853a7c978 100644
--- a/lib/matrix.gi
+++ b/lib/matrix.gi
@@ -3952,6 +3952,71 @@ InstallGlobalFunction( ReflectionMat, function( arg )
end );
+#########################################################################
+##
+#M RandomInvertibleMatrix( [, ][, ], )
+#M RandomInvertibleMatrix( [, ], )
+##
+InstallTagBasedMethod( RandomInvertibleMatrix,
+ function( filt, rs, R, m )
+ local mat, i, j;
+
+ 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 );
+
+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 +4066,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/lib/modfree.gi b/lib/modfree.gi
index c88e380f42..7483942761 100644
--- a/lib/modfree.gi
+++ b/lib/modfree.gi
@@ -175,23 +175,25 @@ 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 +204,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 fe1acea79d..d9d6b34a81 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
@@ -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 );
@@ -3415,10 +3416,29 @@ 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 )
- local V, 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
@@ -3435,18 +3455,19 @@ 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 );
+ 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
@@ -3457,7 +3478,7 @@ function( hom, elm )
fi;
fi;
- return mat;
+ return MakeImmutable( mat );
end);
#############################################################################
@@ -3467,18 +3488,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);
@@ -3486,12 +3513,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;
@@ -3532,7 +3562,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;
@@ -3540,6 +3570,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);
@@ -3575,11 +3606,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;
@@ -3589,15 +3622,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;
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/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
diff --git a/tst/testinstall/MatrixObj/DeterminantMatrix.tst b/tst/testinstall/MatrixObj/DeterminantMatrix.tst
new file mode 100644
index 0000000000..8d9f63eeca
--- /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, DeterminantMat: must be a nonempty square matrix
+
+# 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" );
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" );
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" );