
| Current Path : /usr/share/gap/lib/ |
Linux ift1.ift-informatik.de 5.4.0-216-generic #236-Ubuntu SMP Fri Apr 11 19:53:21 UTC 2025 x86_64 |
| Current File : //usr/share/gap/lib/meataxe.gi |
#############################################################################
##
#W meataxe.gi GAP Library Derek Holt
#W Sarah Rees
#W Alexander Hulpke
##
##
#Y Copyright 1994 -- School of Mathematical Sciences, ANU
#Y (C) 1998-2001 School Math. Sci., University of St Andrews, Scotland
#Y Copyright (C) 1996, Lehrstuhl D für Mathematik, RWTH Aachen, Germany
##
## This file contains the 'Smash'-MeatAxe modified for GAP4 and using the
## standard MeatAxe interface. It defines the MeatAxe SMTX.
##
InstallGlobalFunction(GModuleByMats,function(arg)
local l,f,dim,m;
l:=arg[1];
if Length(arg)=1 then
Error("Usage: GModuleByMats(<mats>,[<id>,]<field>)");
fi;
f:=arg[Length(arg)];
if Length(l)>0 and Characteristic(l[1])<>Characteristic(f) then
Error("matrices and field do not fit together");
fi;
l:=List(l,i->ImmutableMatrix(f,i));
if ForAny(l,i->Length(i)<>Length(i[1])) or
Length(Set(List(l,Length)))>1 then
Error("<l> must be a list of square matrices of the same dimension");
fi;
m:=rec(field:=f,
isMTXModule:=true);
if Length(l)>0 then
dim:=Length(l[1][1]);
elif Length(arg)=2 then
Error("if no generators are given the dimension must be given explicitly");
else
dim:=arg[2];
l:=[ ImmutableMatrix(f, IdentityMat(dim,f) ) ];
m.smashMeataxe:=rec(isZeroGens:=true);
fi;
m.dimension:=dim;
m.generators:=MakeImmutable(l);
m.IsOverFiniteField:= Size(f)<>infinity and IsFFECollCollColl(l);
return m;
end);
#############################################################################
##
#F TrivialGModule( g, F ) . . . trivial G-module
##
## g is a finite group, F a field, trivial smash G-module computed.
InstallGlobalFunction(TrivialGModule,function(g, F)
local mats;
mats:=List(GeneratorsOfGroup(g),i->[[One(F)]]);
return GModuleByMats(mats,F);
end);
#############################################################################
##
#F InducedGModule( g, h, m ) . . . calculate an induced G-module
##
## h should be a subgroup of a finite group g, and m a smash
## GModule for h.
## The induced module for g is calculated.
InstallGlobalFunction(InducedGModule,function(g, h, m)
local gensh, mats, ghom, gdim, hdim, F, index, gen, genim,
gensim, r, i, j, k, l, elt, im;
if IsGroup(g) = false then
return Error("First argument is not a group.");
fi;
if SMTX.IsMTXModule(m) = false then
return Error("Second argument is not a meataxe module.");
fi;
gensh:=GeneratorsOfGroup(h);
mats:=SMTX.Generators(m);
if Length(gensh) <> Length(mats) then
Error("m does not have same number of generators as h = G1");
fi;
hdim:=SMTX.Dimension(m);
F:=SMTX.Field(m);
if Characteristic(F)=0 then
ghom:=GroupHomomorphismByImagesNC(h,Group(mats),gensh,mats);
else
ghom:=GroupHomomorphismByImages(h,GL(hdim,F),gensh,mats);
fi;
# set up transveral
r:=RightTransversal(g, h);
index:=Length(r);
gdim:=index*hdim;
# Now calculate images of generators.
gensim:=[];
for gen in GeneratorsOfGroup(g) do
genim:=NullMat(gdim, gdim, F);
for i in [1..index] do
j:=PositionCanonical(r, r[i]*gen);
elt:=r[i]*gen/r[j];
im:=Image(ghom, elt);
# Now insert hdim x hdim matrix im in the correct place in the genim.
for k in [1..hdim] do
for l in [1..hdim] do
genim[ (i-1)*hdim+k][ (j-1)*hdim+l]:=im[k][l];
od;
od;
od;
Add(gensim, genim);
od;
return GModuleByMats(gensim, F);
end);
#############################################################################
##
#F PermutationGModule( g, F) . permutation module
##
## g is a permutation group, F a field.
## The corresponding permutation module is output.
InstallGlobalFunction(PermutationGModule,function(g, F)
local gens, deg;
gens:=GeneratorsOfGroup(g);
deg:=LargestMovedPoint(gens);
return GModuleByMats(List(gens,g->PermutationMat(g,deg,F)),F);
end);
###############################################################################
##
#F TensorProductGModule( m1, m2 ) . . tensor product of two G-modules
##
## TensorProductGModule calculates the tensor product of smash
## modules m1 and m2.
## They are assumed to be modules over the same algebra so, in particular,
## they should have the same number of generators.
##
InstallGlobalFunction(TensorProductGModule,function( m1, m2)
local mat1, mat2, F1, F2, gens, i, l;
mat1:=SMTX.Generators(m1); mat2:=SMTX.Generators(m2);
F1:=SMTX.Field(m1); F2:=SMTX.Field(m2);
if F1 <> F2 then
Error("GModules are defined over different fields.\n");
fi;
l:=Length(mat1);
if l <> Length(mat2) then
Error("GModules have different numbers of generators.");
fi;
gens:=[];
for i in [1..l] do
gens[i]:=KroneckerProduct(mat1[i], mat2[i]);
od;
return GModuleByMats(gens, F1);
end);
###############################################################################
##
#F WedgeGModule( module ) . . . . . wedge product of a G-module
##
## WedgeGModule calculates the wedge product of a G-module.
## That is the action on antisymmetrix tensors.
##
InstallGlobalFunction(WedgeGModule,function( module)
local mats, mat, newmat, row, F, gens, dim, nmats, i, j, k, m, n, x;
mats:=SMTX.Generators(module);
F:=SMTX.Field(module);
nmats:=Length(mats);
dim:=SMTX.Dimension(module);
gens:=[];
for i in [1..nmats] do
mat:=mats[i];
newmat:=[];
for j in [1..dim] do
for k in [1..j - 1] do
row:=[];
for m in [1..dim] do
for n in [1..m - 1] do
x:=mat[j][m] * mat[k][n] - mat[j][n] * mat[k][m];
Add(row, x);
od;
od;
Add(newmat, row);
od;
od;
Add(gens, newmat);
od;
return GModuleByMats(gens, F);
end);
SMTX.Setter:=function(string)
MakeImmutable(string);
return function(module,obj)
if not IsBound(module.smashMeataxe) then
module.smashMeataxe:=rec();
fi;
module.smashMeataxe.(string):=obj;
end;
end;
SMTX.IsMTXModule:=function(module)
return IsBound(module.isMTXModule) and
IsBound(module.field) and
IsBound(module.generators) and
IsBound(module.dimension);
end;
SMTX.IsZeroGens:=function(module)
return IsBound(module.smashMeataxe)
and IsBound(module.smashMeataxe.isZeroGens)
and module.smashMeataxe.isZeroGens=true;
end;
SMTX.Dimension:=function(module)
return module.dimension;
end;
SMTX.Field:=function(module)
return module.field;
end;
SMTX.Generators:=function(module)
if SMTX.IsZeroGens(module) then
return [];
else
return module.generators;
fi;
end;
SMTX.SetIsIrreducible:=function(module,b)
module.IsIrreducible:=b;
end;
SMTX.HasIsIrreducible:=function(module)
return IsBound(module.IsIrreducible);
end;
SMTX.IsAbsolutelyIrreducible:=function(module)
if not IsBound(module.IsAbsolutelyIrreducible) then
if not SMTX.IsIrreducible(module) then
return false;
fi;
module.IsAbsolutelyIrreducible:=SMTX.AbsoluteIrreducibilityTest(module);
fi;
return module.IsAbsolutelyIrreducible;
end;
SMTX.SetIsAbsolutelyIrreducible:=function(module,b)
module.IsAbsolutelyIrreducible:=b;
end;
SMTX.HasIsAbsolutelyIrreducible:=function(module)
return IsBound(module.IsAbsolutelyIrreducible);
end;
SMTX.SetSmashRecord:=SMTX.Setter("dummy");
SMTX.Subbasis:=SMTX.Getter("subbasis");
SMTX.SetSubbasis:=SMTX.Setter("subbasis");
SMTX.AlgEl:=SMTX.Getter("algebraElement");
SMTX.SetAlgEl:=SMTX.Setter("algebraElement");
SMTX.AlgElMat:=SMTX.Getter("algebraElementMatrix");
SMTX.SetAlgElMat:=SMTX.Setter("algebraElementMatrix");
SMTX.AlgElCharPol:=SMTX.Getter("characteristicPolynomial");
SMTX.SetAlgElCharPol:=SMTX.Setter("characteristicPolynomial");
SMTX.AlgElCharPolFac:=SMTX.Getter("charpolFactors");
SMTX.SetAlgElCharPolFac:=SMTX.Setter("charpolFactors");
SMTX.AlgElNullspaceVec:=SMTX.Getter("nullspaceVector");
SMTX.SetAlgElNullspaceVec:=SMTX.Setter("nullspaceVector");
SMTX.AlgElNullspaceDimension:=SMTX.Getter("ndimFlag");
SMTX.SetAlgElNullspaceDimension:=SMTX.Setter("ndimFlag");
SMTX.CentMat:=SMTX.Getter("centMat");
SMTX.SetCentMat:=SMTX.Setter("centMat");
SMTX.CentMatMinPoly:=SMTX.Getter("centMatMinPoly");
SMTX.SetCentMatMinPoly:=SMTX.Setter("centMatMinPoly");
SMTX.FGCentMat:=SMTX.Getter("fieldGenCentMat");
SMTX.SetFGCentMat:=SMTX.Setter("fieldGenCentMat");
SMTX.FGCentMatMinPoly:=SMTX.Getter("fieldGenCentMatMinPoly");
SMTX.SetFGCentMatMinPoly:=SMTX.Setter("fieldGenCentMatMinPoly");
SMTX.SetDegreeFieldExt:=SMTX.Setter("degreeFieldExt");
#############################################################################
##
#F SMTX.OrthogonalVector( subbasis ) single vector othogonal to a submodule,
## N.B. subbasis is assumed to consist of normed vectors,
## submodule is assumed proper.
##
SMTX_OrthogonalVector:=function( subbasis )
local zero, one, v, i, j, k, x, dim, len;
subbasis:=ShallowCopy(subbasis);
Sort(subbasis);
subbasis:=Reversed(subbasis);
# Now subbasis is in order so that the vector whose leading coefficient
# comes furthest to the left comes first.
len:=Length(subbasis);
dim:=Length(subbasis[1]);
i:= 1;
v:=[];
one:=One(subbasis[1][1]);
zero:=Zero(one);
for i in [1..dim] do
v[i]:=zero;
od;
i:=1;
while i <= len and subbasis[i][i] = one do
i:= i + 1;
od;
v[i]:=one;
for j in Reversed([1..i-1]) do
x:=zero;
for k in [j + 1..i] do
x:=x + v[k] * subbasis[j][k];
od;
v[j]:=-x;
od;
return v;
end;
SMTX.OrthogonalVector:=SMTX_OrthogonalVector;
SubGModLeadPos:=function(sub,dim,subdim,zero)
local leadpos,i,j,k;
## As in SpinnedBasis, leadpos[i] gives the position of the first nonzero
## entry (which will always be 1) of sub[i].
leadpos:=[];
for i in [1..subdim] do
j:=1;
while j <= dim and sub[i][j]=zero do j:=j + 1; od;
leadpos[i]:=j;
for k in [1..i - 1] do
if leadpos[k] = j then
Error("Subbasis isn't normed.");
fi;
od;
od;
return leadpos;
end;
#############################################################################
##
#F SpinnedBasis( v, matrices, F, [ngens] ) . . . .
##
## The first argument v can either be a vector over the module on
## which matrices act or a subspace.
##
## SpinnedBasis computes a basis for the submodule defined by the action of the
## matrix group generated by the list matrices on v.
## F is the field over which we act.
## It is returned as a list of normed vectors.
## If the optional third argument is present, then only the first ngens
## matrices in the list are used.
SMTX_SpinnedBasis:=function( arg )
local v, matrices, ngens, zero,
ans, dim, subdim, leadpos, w, i, j, k, l, m,F;
if Length(arg) < 3 or Length(arg) > 4 then
Error("Usage: SpinnedBasis( v, matrices, F, [ngens] )");
fi;
v:=arg[1];
matrices:=arg[2];
F:=arg[3];
if Length(arg) = 4 then
ngens:=arg[4];
if ngens <= 0 or ngens > Length(matrices) then
ngens:=Length(matrices);
fi;
else
ngens:=Length(matrices);
fi;
ans:=[];
if Length(v)=0 then
return [];
fi;
if not IsList(v[1]) then
v:=[v];
fi;
zero:=Zero(matrices[1][1][1]);
ans:=ShallowCopy(Basis(VectorSpace(F,v)));
if Length(ans)=0 then
return ans;
fi;
ans:=List(ans, v -> ImmutableVector(F,v));
dim:=Length(ans[1]);
subdim:=Length(ans);
leadpos:=SubGModLeadPos(ans,dim,subdim,zero);
i:=1;
while i <= subdim do
for l in [1..ngens] do
m:=matrices[l];
# apply generator m to submodule generator i
w:=ShallowCopy(ans[i] * m);
# try to express w in terms of existing submodule generators
j:=1;
for j in [1..subdim] do
k:=w[leadpos[j]];
if k <> zero then
#w:=w - k * ans[j];
AddRowVector(w,ans[j],-k);
fi;
od;
j:=1;
while j <= dim and w[j] = zero do j:=j + 1; od;
if j <= dim then
# we have found a new generator of the submodule
subdim:=subdim + 1;
leadpos[subdim]:=j;
#w:=(w[j]^-1) * w;
MultRowVector(w,w[j]^-1);
Add( ans, w );
if subdim = dim then
ans:=ImmutableMatrix(F,ans);
return ans;
fi;
fi;
od;
i:=i + 1;
od;
Sort(ans);
ans:=Reversed(ans); # To bring it into semi-echelonised form.
ans:=ImmutableMatrix(F,ans);
return ans;
end;
SMTX.SpinnedBasis:=SMTX_SpinnedBasis;
SMTX_SubGModule:=function(module, subspace)
## The submodule of module generated by <subspace>.
return SMTX.SpinnedBasis(subspace, SMTX.Generators(module),
SMTX.Field(module));
end;
SMTX.SubGModule:=SMTX_SubGModule;
SMTX.SubmoduleGModule:=SMTX_SubGModule;
#############################################################################
##
#F SMTX.SubQuotActionsModule(matrices,sub,dim,subdim,field,typ) . . .
## generators of sub- and quotient-module and original module wrt new basis
##
## IT IS ASSUMED THAT THE GENERATORS OF SUB ARE NORMED.
##
## this function is used to compute all submodule/quotient stuff, as
## indicated by typ: 1=Sub, 2=Quotient, 4=Common
## The function returns a record with components 'smatrices', 'qmatrices',
## 'nmatrices' and 'nbasis' if applicable.
##
## See the description for 'SMTX.InducedAction' for
## description of the matrices
##
SMTX_SubQuotActions:=function(matrices,sub,dim,subdim,F,typ)
local s, c, q, leadpos, zero, zerov, smatrices, newg, im, newim, k, subi,
qmats, smats, nmats, sr, qr, g, h, erg, i, j;
s:=(typ mod 2)=1; # subspace indicator
typ:=QuoInt(typ,2);
q:=(typ mod 2)=1; # quotient indicator
c:=typ>1; # common indicator
zero:=Zero(F);
leadpos:=SubGModLeadPos(sub,dim,subdim,zero);
if subdim*2<dim and not (q or c) then
# the subspace dimension is small and we only want the subspace action:
# performing a base change is too expensive
zerov:=ListWithIdenticalEntries(subdim,zero);
ConvertToVectorRep(zerov,F);
smatrices:=[];
for g in matrices do
newg:=[];
for i in [1..subdim] do
im:=ShallowCopy(sub[i] * g);
newim:=ShallowCopy(zerov);
for j in [1..subdim] do
k:=im[leadpos[j]];
if k<> zero then
newim[j]:=k;
AddRowVector(im,sub[j],-k);
fi;
od;
# Check that the vector is now zero - if not, then sub was
# not the basis of a submodule
if im <> Zero(im) then return fail; fi;
Add(newg, newim);
od;
Add(smatrices,ImmutableMatrix(F,newg));
od;
return rec(smatrices:=smatrices);
else
# we want the quotient or all or the subspace dimension is big enough to
# merit a basechange
# first extend the basis
sub:=ShallowCopy(sub);
Append(sub,One(matrices[1]){Difference([1..dim],leadpos)});
sub:=ImmutableMatrix(F,sub);
subi:=sub^-1;
qmats:=[];
smats:=[];
nmats:=[];
sr:=[1..subdim];qr:=[subdim+1..dim];
for g in matrices do
g:=sub*g*subi;
if s then
h:=g{sr}{sr};
h:=ImmutableMatrix(F,h);
Add(smats,h);
fi;
if q then
h:=g{qr}{qr};
h:=ImmutableMatrix(F,h);
Add(qmats,h);
fi;
if c then Add(nmats,g);fi;
od;
erg:=rec();
if s then
erg.smatrices:=smats;
fi;
if q then
erg.qmatrices:=qmats;
fi;
if c then
erg.nmatrices:=nmats;
fi;
if q or c then
erg.nbasis:=sub;
fi;
return erg;
fi;
end;
SMTX.SubQuotActions:=SMTX_SubQuotActions;
#############################################################################
##
## SMTX.NormedBasisAndBaseChange(sub)
##
## returns a list [bas,change] where bas is a normed basis for <sub> and
## change is the base change from bas to sub (the basis vectors of bas
## expressed in coefficients for sub)
SMTX.NormedBasisAndBaseChange:=function(sub)
local l,m,d;
l:=Length(sub);
d:=Length(sub[1]);
m:= IdentityMat(d,One(sub[1][1]));
sub:=List([1..l],i->Concatenation(ShallowCopy(sub[i]),m[i]));
TriangulizeMat(sub);
m:=d+l;
return [sub{[1..l]}{[1..d]},sub{[1..l]}{[d+1..m]}];
end;
#############################################################################
##
#F SMTX.InducedActionSubmoduleNB( module, sub ) . . . . construct submodule
##
## module is a module record, and sub is a list of generators of a submodule.
## IT IS ASSUMED THAT THE GENERATORS OF SUB ARE NORMED.
## (i.e. each has leading coefficient 1 in a unique place).
## SMTX.InducedActionSubmoduleNB( module, sub ) computes the submodule of
## module for which sub is the basis.
## If sub does not generate a submodule then fail is returned.
SMTX.InducedActionSubmoduleNB:=function( module, sub )
local ans, dim, subdim, smodule,F;
subdim:=Length(sub);
if subdim = 0 then
return List(module.generators,i->[[]]);
fi;
dim:=SMTX.Dimension(module);
F:=SMTX.Field(module);
ans:=SMTX.SubQuotActions(module.generators,sub,dim,subdim,F,1);
if ans=fail then
return fail;
fi;
if SMTX.IsZeroGens(module) then
smodule:=GModuleByMats([],Length(ans.smatrices[1]),F);
else
smodule:=GModuleByMats(ans.smatrices,F);
fi;
return smodule;
end;
# Ditto, but allowing also unnormed modules
SMTX.InducedActionSubmodule:=function(module,sub)
local nb,ans,dim,subdim,smodule,F;
nb:=SMTX.NormedBasisAndBaseChange(sub);
sub:=nb[1];
nb:=nb[2];
subdim:=Length(sub);
if subdim = 0 then
return List(module.generators,i->[[]]);
fi;
dim:=SMTX.Dimension(module);
F:=SMTX.Field(module);
ans:=SMTX.SubQuotActions(module.generators,
sub,dim,subdim,F,1);
if ans=fail then
return fail;
fi;
# conjugate the matrices to correspond to given sub
if SMTX.IsZeroGens(module) then
smodule:=GModuleByMats([],Length(ans.smatrices[1]),F);
else
smodule:=GModuleByMats(List(ans.smatrices,i->i^nb),F);
fi;
return smodule;
end;
SMTX.ProperSubmoduleBasis:=function(module)
if SMTX.IsIrreducible(module) then
return fail;
fi;
return SMTX.Subbasis(module);
end;
#############################################################################
##
#F SMTX.InducedActionFactorModule( module, sub [,compl] )
##
## module is a module record, and sub is a list of generators of a submodule.
## (i.e. each has leading coefficient 1 in a unique place).
## Qmodule is returned, where qmodule
## is the quotient module.
##
SMTX.InducedActionFactorModule:=function(arg)
local module,sub, ans, dim, subdim, F,qmodule;
module:=arg[1];
sub:=arg[2];
sub:=TriangulizedMat(sub);
subdim:=Length(sub);
dim:=SMTX.Dimension(module);
if subdim = dim then
return List(module.generators,i->[[]]);
fi;
F:=SMTX.Field(module);
ans:=SMTX.SubQuotActions(module.generators,
sub,dim,subdim,F,2);
if ans=fail then
return fail;
fi;
if Length(arg)=3 then
# compute basechange
sub:=Concatenation(sub,arg[3]);
sub:=sub*Inverse(ans.nbasis);
ans.qmatrices:=List(ans.qmatrices,i->i^sub);
fi;
if SMTX.IsZeroGens(module) then
qmodule:=GModuleByMats([],Length(ans.qmatrices[1]),F);
else
qmodule:=GModuleByMats(ans.qmatrices, F);
fi;
return qmodule;
end;
#############################################################################
##
#F SMTX.InducedActionFactorModuleWithBasis( module, sub )
##
# FIXME: this function is never used and documented. Keep it or remove it?
SMTX.InducedActionFactorModuleWithBasis:=function(module,sub)
local ans, dim, subdim, F,qmodule;
sub:=TriangulizedMat(sub);
subdim:=Length(sub);
dim:=SMTX.Dimension(module);
if subdim = dim then
return List(module.generators,i->[[]]);
fi;
F:=SMTX.Field(module);
ans:=SMTX.SubQuotActions(module.generators,
sub,dim,subdim,F,2);
if ans=fail then
return fail;
fi;
# fetch new basis
sub:=ans.nbasis{[Length(sub)+1..module.dimension]};
if SMTX.IsZeroGens(module) then
qmodule:=GModuleByMats([],Length(ans.qmatrices[1]),F);
else
qmodule:=GModuleByMats(ans.qmatrices, F);
fi;
return [qmodule,sub];
end;
#############################################################################
##
#F SMTX.InducedAction( module, sub, typ )
## generators of sub- and quotient-module and original module wrt new basis
## and new basis
##
## module is a module record, and sub is a list of generators of a submodule.
## IT IS ASSUMED THAT THE GENERATORS OF SUB ARE NORMED.
## (i.e. each has leading coefficient 1 in a unique place).
## SMTX.InducedAction computes the submodule and quotient
## and the original module with its matrices written wrt to the basis used
## to compute smodule and qmodule.
## [smodule, qmodule, nmodule] is returned,
## where smodule is the submodule and qmodule the quotient module.
## The matrices of nmodule have the form A 0 where A and B are the
## C B
## corresponding matrices of smodule and qmodule resepctively.
## If sub is not the basis of a submodule then fail is returned.
SMTX.InducedAction:=function( arg )
local module,sub,typ,ans,dim,subdim,F,one,erg;
module:=arg[1];
sub:=arg[2];
if Length(arg)>2 then
typ:=arg[3];
else
typ:=7;
fi;
subdim:=Length(sub);
dim:=SMTX.Dimension(module);
F:=SMTX.Field(module); one:=One(F);
erg:=SMTX.SubQuotActions(module.generators,
sub,dim,subdim,F,typ);
if erg=fail then
return fail;
fi;
ans:=[];
if IsBound(erg.smatrices) then
if SMTX.IsZeroGens(module) then
Add(ans,GModuleByMats([],Length(erg.smatrices[1]), F));
else
Add(ans,GModuleByMats(erg.smatrices, F));
fi;
fi;
if IsBound(erg.qmatrices) then
if SMTX.IsZeroGens(module) then
Add(ans,GModuleByMats([],Length(erg.qmatrices[1]), F));
else
Add(ans,GModuleByMats(erg.qmatrices, F));
fi;
fi;
if IsBound(erg.nmatrices) then
if SMTX.IsZeroGens(module) then
Add(ans,GModuleByMats([],Length(erg.nmatrices[1]), F));
else
Add(ans,GModuleByMats(erg.nmatrices, F));
fi;
fi;
if IsBound(erg.nbasis) then
Add(ans,erg.nbasis);
fi;
return ans;
end;
#############################################################################
##
#F SMTX.InducedActionSubMatrixNB( mat, sub ) . . . . construct submodule
##
## as InducedActionSubmoduleNB but for a matrix.
# FIXME: this function is never used and documented. Keep it or remove it?
SMTX.InducedActionSubMatrixNB:=function( mat, sub )
local subdim, dim, F, ans;
subdim:=Length(sub);
if subdim = 0 then
return [];
fi;
dim:=Length(mat);
F:=DefaultFieldOfMatrix(mat);
ans:=SMTX.SubQuotActions([mat],sub,dim,subdim,F,1);
if ans=fail then
return fail;
else
return ans.smatrices[1];
fi;
end;
# Ditto, but allowing also unnormed modules
# FIXME: this function is never used and documented. Keep it or remove it?
SMTX.InducedActionSubMatrix:=function(mat,sub)
local nb, subdim, dim, F, ans;
nb:=SMTX.NormedBasisAndBaseChange(sub);
sub:=nb[1];
nb:=nb[2];
subdim:=Length(sub);
if subdim = 0 then
return [];
fi;
dim:=Length(mat);
F:=DefaultFieldOfMatrix(mat);
ans:=SMTX.SubQuotActions([mat],sub,dim,subdim,F,1);
if ans=fail then
return fail;
else
# conjugate the matrices to correspond to given sub
return ans.smatrices[1]^nb;
fi;
end;
#############################################################################
##
#F SMTX.InducedActionFactorMatrix( mat, sub [,compl] )
##
## as InducedActionFactor, but for a matrix.
##
SMTX.InducedActionFactorMatrix:=function(arg)
local mat, sub, subdim, dim, F, ans;
mat:=arg[1];
sub:=arg[2];
sub:=TriangulizedMat(sub);
subdim:=Length(sub);
dim:=Length(mat);
if subdim = dim then
return [];
fi;
F:=DefaultFieldOfMatrix(mat);
ans:=SMTX.SubQuotActions([mat],sub,dim,subdim,F,2);
if ans=fail then
return fail;
fi;
if Length(arg)=3 then
# compute basechange
sub:=Concatenation(sub,arg[3]);
sub:=sub*Inverse(ans.nbasis);
ans.qmatrices:=List(ans.qmatrices,i->i^sub);
fi;
return ans.qmatrices[1];
end;
SMTX_SMCoRaEl:=function(matrices,ngens,newgenlist,dim,F)
local g1,g2,coefflist,M,pol;
g1:=Random([1..ngens]);
g2:=g1;
while g2=g1 and ngens>1 do
g2:=Random([1..ngens]);
od;
ngens:=ngens + 1;
matrices[ngens]:=matrices[g1] * matrices[g2];
Add(newgenlist, [g1, g2]);
# Take a random linear sum of the existing generators as new generator.
# Record the sum in coefflist
coefflist:=[Random(F)];
M:=coefflist[1]*matrices[1];
for g1 in [2..ngens] do
g2:=Random(F);
if IsOne(g2) then
M:=M + matrices[g1];
elif not IsZero(g2) then
M:=M + g2 * matrices[g1];
fi;
Add(coefflist, g2);
od;
Info(InfoMeatAxe,2,"Evaluated random element in algebra.");
pol:=CharacteristicPolynomialMatrixNC(F,M,1);
return [M,coefflist,pol];
end;
SMTX.SMCoRaEl:=SMTX_SMCoRaEl;
# how many random elements should we try before (temporarily ) giving up?
# This number is set relatively high to minimize the chance of an unlucky
# random run in functions such as composition series computation.
SMTX.RAND_ELM_LIMIT:=5000;
#############################################################################
##
#F SMTX.IrreduciblityTest( module ) try to reduce a module over a finite
## field
##
## 27/12/2000.
## New version incorporating Ivanyos/Lux method of handling one difficult case
## for proving reducibility.
## (See G.Ivanyos and K. Lux, `Treating the exceptional cases of the meataxe',
## Experimental Mathematics 9, 2000, 373-381.
##
## module is a module record
## IsIrreducible( ) attempts to decide whether module is irreducible.
## When it succeeds it returns true or false.
## We choose at random elements of the group algebra of the group.
## If el is such an element, we define M, p, fac, N, e and v as follows:-
## M is the matrix corresponding to el, p is its characteristic polynomial,
## fac an irreducible factor of p, N the nullspace of the matrix fac(M),
## ndim the dimension of N, and v a vector in N.
## If we can find the above such that ndim = deg(fac) then we can test
## conclusively for irreducibility. Then, in the case where irreducibility is
## proved, we store the information as fields for the module, since it may be
## useful later (e.g. to test for absolute irreducibility, equivalence with
## another module).
## These fields are accessed by the functions
## AlgEl()(el), AlgElMat(M), AlgElCharPol(p),
## AlgElCharPolFac(fac), AlgElNullspaceDimension(ndim), and
## AlgElNullspaceVec(v).
##
## If we cannot find such a set with ndim = deg(fac) we may nonetheless prove
## reducibility by finding a submodule. However we can never prove
## irreducibility without such a set (and hence the algorithm could run
## forever, but hopefully this will never happen!)
## Where reducibility is proved, we set the field .subbasis
## (a basis for the submodule, normed in the sense that the first non-zero
## component of each basis vector is 1, and is in a different position from
## the first non-zero component of every other basis vector).
## The test for irreducibility is based on the meataxe method (but in the
## meataxe, ndim is always very small, usually 1. The modification here is put
## in to enable the method to work over modules with large centralizing fields).
## We simply spin v. If we do not get the whole space, we have a submodule,
## on the other hand, if we do get the whole space, we calculate the
## nullspace NT of the transpose of fac(M), spin that under the group
## generated by the transposes of the generating matrices, and thus either
## find the transpose of a submodule or conclusively prove irreducibility.
##
## This function can also be used to get a random submodule. Therefore it
## is not an end-user function but only called internally
SMTX_IrreducibilityTest:=function( module )
local matrices, tmatrices, ngens, ans, M, mat, g1, g2, maxdeg,
newgenlist, coefflist, orig_ngens, zero,
N, NT, v, subbasis, fac, sfac, pol, orig_pol, q, dim, ndim, i,
l, trying, deg, facno, bestfacno, F, count, R, rt0,idmat,
pfac1, pfac2, quotRem, pfr, idemp, M2, mat2, mat3;
rt0:=Runtime();
Info(InfoMeatAxe,1,"Calling MeatAxe. All times will be in milliseconds");
if not SMTX.IsMTXModule(module) then
return Error("Argument of IsIrreducible is not a module.");
fi;
if not module.IsOverFiniteField then
return Error("Argument of IsIrreducible is not over a finite field.");
fi;
matrices:=ShallowCopy(module.generators);
dim:=SMTX.Dimension(module);
ngens:=Length(matrices);
orig_ngens:=ngens;
F:=SMTX.Field(module);
zero:=Zero(F);
R:=PolynomialRing(F);
# Now compute random elements M of the group algebra, calculate their
# characteristic polynomials, factorize, and apply the irreducible factors
# to M to get matrices with nontrivial nullspaces.
# tmatrices will be a list of the transposed generators if required.
tmatrices:=[];
trying:=true;
# trying will become false when we have an answer
maxdeg:=1;
newgenlist:=[];
# Do a small amount of preprocessing to increase the generator set.
for i in [1..1] do
g1:=Random([1..ngens]);
g2:=g1;
while g2=g1 and Length(matrices) > 1 do
g2:=Random([1..ngens]);
od;
ngens:=ngens + 1;
matrices[ngens]:=matrices[g1] * matrices[g2];
Add(newgenlist, [g1, g2]);
od;
Info(InfoMeatAxe,1,"Done preprocessing. Time = ",Runtime()-rt0,".");
count:=0;
# Main loop starts - choose a random element of group algebra on each pass
while trying do
count:=count + 1;
if count mod SMTX.RAND_ELM_LIMIT = 0 then
Error("Have generated ",SMTX.RAND_ELM_LIMIT,
"random elements and failed to prove\n",
"or disprove irreducibility. Type return to keep trying.");
fi;
maxdeg:=Minimum(maxdeg * 2,dim);
# On this pass, we only consider irreducible factors up to degree maxdeg.
# Using higher degree factors is very time consuming, so we prefer to try
# another element.
# To choose random element, first add on a new generator as a product of
# two randomly chosen unequal existing generators
# Record the product in newgenlist.
Info(InfoMeatAxe,1,"Choosing random element number ",count);
M:=SMTX.SMCoRaEl(matrices,ngens,newgenlist,dim,F);
ngens:=Length(matrices);
coefflist:=M[2];
pol:=M[3];
M:=ImmutableMatrix(F,M[1]);
idmat:=M^0;
orig_pol:=pol;
Info(InfoMeatAxe,2,"Evaluated characteristic polynomial. Time = ",
Runtime()-rt0,".");
# Now we extract the irreducible factors of pol starting with those
# of low degree
deg:=0;
fac:=[];
# The next loop is through the degrees of irreducible factors
while DegreeOfLaurentPolynomial(pol) > 0 and deg < maxdeg and trying do
repeat
deg:=deg + 1;
if deg > Int(DegreeOfLaurentPolynomial(pol) / 2) then
fac:=[pol];
else
fac:=Factors(R, pol: factoroptions:=rec(onlydegs:=[deg]));
fac:=Filtered(fac,i->DegreeOfLaurentPolynomial(i)=deg);
Info(InfoMeatAxe,2,Length(fac)," factors of degree ",deg,
", Time = ",Runtime()-rt0,".");
fi;
until fac <> [] or deg = maxdeg;
if fac <> [] then
if DegreeOfLaurentPolynomial(fac[1]) = dim then
# In this case the char poly is irreducible, so the
# module is irreducible.
ans:=true;
trying:=false;
bestfacno:=1;
v:=ListWithIdenticalEntries(dim,zero);
v[1]:=One(F);
ndim:=dim;
fi;
# Otherwise, first see if there is a non-repeating factor.
# If so it will be decisive, so delete the rest of the list
l:=Length(fac);
facno:=1;
while facno <= l and trying do
if facno = l or fac[facno] <> fac[facno + 1] then
fac:=[fac[facno]]; l:=1;
else
while facno < l and fac[facno] = fac[facno + 1] do
facno:=facno + 1;
od;
fi;
facno:=facno + 1;
od;
# Now we can delete repetitions from the list fac
sfac:=Set(fac);
if DegreeOfLaurentPolynomial(fac[1]) <> dim then
# Now go through the factors and attempt to find a submodule
facno:=1; l:=Length(sfac);
while facno <= l and trying do
mat:=Value(sfac[facno], M,idmat);
Info(InfoMeatAxe,2,"Evaluated matrix on factor. Time = ",
Runtime()-rt0,".");
N:=NullspaceMat(mat);
v:=N[1];
ndim:=Length(N);
Info(InfoMeatAxe,2,"Evaluated nullspace. Dimension = ",
ndim,". Time = ",Runtime()-rt0,".");
subbasis:=SMTX.SpinnedBasis(v, matrices, F,orig_ngens);
Info(InfoMeatAxe,2,"Spun up vector. Dimension = ",
Length(subbasis),". Time = ",Runtime()-rt0,".");
if Length(subbasis) < dim then
# Proper submodule found
trying:=false;
ans:=false;
SMTX.SetSubbasis(module, subbasis);
elif ndim = deg then
trying:=false;
# if we transpose and find no proper submodule, then the
# module is definitely irreducible.
mat:=TransposedMat(mat);
if Length(tmatrices)=0 then
for i in [1..orig_ngens] do
Add(tmatrices, TransposedMat(matrices[i]));
od;
fi;
Info(InfoMeatAxe,2,"Transposed matrices. Time = ",
Runtime()-rt0,".");
NT:=NullspaceMat(mat);
Info(InfoMeatAxe,2,"Evaluated nullspace. Dimension = ",
Length(NT),". Time = ",Runtime()-rt0, ".");
subbasis:=SMTX.SpinnedBasis(NT[1],tmatrices,F,orig_ngens);
Info(InfoMeatAxe,2,"Spun up vector. Dimension = ",
Length(subbasis),". Time = ",Runtime()-rt0, ".");
if Length(subbasis) < dim then
# subbasis is a basis for a submodule of the transposed
# module, and the orthogonal complement of this is a
# submodule of the original module. So we find a vector
# v in that, and then spin it. Of course we won't
# necessarily get the full orthogonal complement
# that way, but we'll certainly get a proper submodule.
v:=SMTX.OrthogonalVector(subbasis);
SMTX.SetSubbasis(module,
SMTX.SpinnedBasis(v,matrices,F,orig_ngens));
ans:=false;
else
ans:=true;
bestfacno:=facno;
fi;
fi;
if trying and deg>1 and count>2 then
Info(InfoMeatAxe,1,"Trying Ivanyos/Lux Method");
# first find the appropriate idempotent
pfac1:=sfac[facno];
pfac2:=orig_pol;
while true do
quotRem := QuotRemLaurpols(pfac2, sfac[facno], 3);
if quotRem[2] <> Zero(R) then
break;
fi;
pfac2 := quotRem[1];
pfac1:=pfac1*sfac[facno];
od;
pfr:=GcdRepresentation(pfac1, pfac2);
idemp:=QuotRemLaurpols(pfr[2]*pfac2, orig_pol, 2);
# Now another random element in the group algebra.
# and a random vector in the module
g2:=Random(F);
if IsOne(g2) then
M2:=matrices[1];
else
M2:=g2 * matrices[1];
fi;
for g1 in [2..ngens] do
g2:=Random(F);
if IsOne(g2) then
M2:=M2 + matrices[g1];
elif not IsZero(g2) then
M2:=M2 + g2 * matrices[g1];
fi;
od;
Info(InfoMeatAxe,2,
"Evaluated second random element in algebra.");
v:=Random(FullRowSpace(F,dim));
mat2:=Value(idemp, M,idmat);
mat3:=mat2*M2*mat2;
v:=v*(M*mat3 - mat3*M);
# This vector might lie in a proper subspace!
subbasis:=SMTX.SpinnedBasis(v, matrices, F,orig_ngens);
Info(InfoMeatAxe,2,"Spun up vector. Dimension = ",
Length(subbasis),". Time = ",Runtime()-rt0,".");
if Length(subbasis) < dim and Length(subbasis) <> 0 then
# Proper submodule found
trying:=false;
ans:=false;
SMTX.SetSubbasis(module, subbasis);
fi;
fi;
facno:=facno + 1;
od; # going through irreducible factors of fixed degree.
# If trying is false at this stage, then we don't have
# an answer yet, so we have to go onto factors of the next degree.
# Now divide p by the factors used if necessary
if trying and deg < maxdeg then
for q in fac do
pol:=Quotient(R, pol, q);
od;
fi;
fi; #DegreeOfLaurentPolynomial(fac[1]) <> dim
fi; #fac <> []
od; # loop through degrees of irreducible factors
# if we have not found a submodule and trying is false, then the module
# must be irreducible.
if trying = false and ans = true then
SMTX.SetAlgEl(module, [newgenlist, coefflist]);
SMTX.SetAlgElMat(module, M);
SMTX.SetAlgElCharPol(module, orig_pol);
SMTX.SetAlgElCharPolFac(module, sfac[bestfacno]);
SMTX.SetAlgElNullspaceVec(module, v);
SMTX.SetAlgElNullspaceDimension(module, ndim);
fi;
od; # main loop
Info(InfoMeatAxe,1,"Total time = ",Runtime()-rt0," milliseconds.");
return ans;
end;
SMTX.IrreducibilityTest:=SMTX_IrreducibilityTest;
SMTX.IsIrreducible:=function(module)
if not IsBound(module.IsIrreducible) then
module.IsIrreducible:=SMTX.IrreducibilityTest(module);
fi;
return module.IsIrreducible;
end;
#############################################################################
##
#F SMTX.RandomIrreducibleSubGModule( module ) . . .
## find a basis for a random irreducible
## submodule of module, and return that basis and the submodule, with all
## the irreducibility flags set.
## Returns false if module is irreducible.
SMTX_RandomIrreducibleSubGModule:=function( module )
local ranSub, subbasis, submodule, subbasis2, submodule2,
F, dim, el, M, fac, N, i, matrices, ngens, genpair;
if not SMTX.IsMTXModule(module) then
return Error("Argument of RandomIrreducibleSubGModule is not a module.");
elif SMTX.HasIsIrreducible(module) and SMTX.IsIrreducible(module) then
return false;
fi;
# now call an irreducibility test that will compute a new subbasis
i:=SMTX.IrreducibilityTest(module);
if i then
# we just found out it is irreducible
SMTX.SetIsIrreducible(module,true);
return false;
elif not SMTX.HasIsIrreducible(module) then
# or store reducibility
SMTX.SetIsIrreducible(module,false);
fi;
subbasis:=SMTX.Subbasis(module);
submodule:=SMTX.InducedActionSubmoduleNB(module, subbasis);
ranSub:=SMTX.RandomIrreducibleSubGModule(submodule);
if ranSub = false then
# submodule has been proved irreducible in a call to this function,
# so the flags have been set.
return [ subbasis, submodule];
else
# ranSub[1] is given in terms of the basis for the submodule,
# but we want it in terms of the basis of the original module.
# So we multiply it by subbasis.
# Then we need our basis to be normed.
# this is done by triangulization
F:=SMTX.Field(module);
subbasis2:=ranSub[1] * subbasis;
subbasis2:=TriangulizedMat(subbasis2);
# But now since we've normed the basis subbasis2,
# the matrices of the submodule ranSub[2] are given with respect to
# the wrong basis. So we have to recompute the submodule.
submodule2:=SMTX.InducedActionSubmoduleNB(module, subbasis2);
# Unfortunately, although it's clear that this submodule is
# irreducible, we'll have to reset the flags that IsIrreducible sets.
# AH Why can't we keep irreducibility?
# Some will be the same # as in ranSub[2], but some are affected by
# the base change, or at least part of it, since the flags gets
# screwed up by the base change.
# We need to set the following flags:
el:=SMTX.AlgEl(ranSub[2]);
SMTX.SetAlgEl(submodule2,el);
SMTX.SetAlgElCharPol(submodule2,SMTX.AlgElCharPol(ranSub[2]));
fac:=SMTX.AlgElCharPolFac(ranSub[2]);
SMTX.SetAlgElCharPolFac(submodule2,fac);
SMTX.SetAlgElNullspaceDimension(submodule2,
SMTX.AlgElNullspaceDimension(ranSub[2]));
# Only the actual algebra element and its nullspace have to be recomputed
# This code is essentially from IsomorphismGModule
dim:=SMTX.Dimension(submodule2);
matrices:=ShallowCopy(submodule2.generators);
ngens:=Length(matrices);
for genpair in el[1] do
ngens:=ngens + 1;
matrices[ngens]:=matrices[genpair[1]] * matrices[genpair[2]];
od;
M:=ImmutableMatrix(F,Sum([1..ngens], i-> el[2][i] * matrices[i]));
SMTX.SetAlgElMat(submodule2,M);
N:=NullspaceMat(Value(fac,M,M^0));
SMTX.SetAlgElNullspaceVec(submodule2,N[1]);
return [subbasis2, submodule2];
fi;
end;
SMTX.RandomIrreducibleSubGModule:=SMTX_RandomIrreducibleSubGModule;
#############################################################################
##
#F SMTX.GoodElementGModule( module ) . . find good group algebra element
## in an irreducible module
##
## module is a module that is already known to be irreducible.
## GoodElementGModule finds a group algebra element with nullspace of
## minimal possible dimension. This dimension is 1 if the module is absolutely
## irreducible, and the degree of the relevant field extension otherwise.
## This is needed for testing for equivalence of modules.
SMTX_GoodElementGModule:=function( module )
local matrices, ngens, M, mat, N, newgenlist, coefflist, orig_ngens,
fac, sfac, pol, oldpol, q, deg, i, l,
trying, dim, mindim, F, R, count, rt0, idmat;
rt0:=Runtime();
if not SMTX.IsMTXModule(module) or not SMTX.IsIrreducible(module) then
ErrorNoReturn("Argument is not an irreducible module.");
fi;
if not SMTX.HasIsAbsolutelyIrreducible(module) then
SMTX.IsAbsolutelyIrreducible(module);
fi;
if SMTX.IsAbsolutelyIrreducible(module) then
mindim:=1;
else
mindim:=SMTX.DegreeFieldExt(module);
fi;
if SMTX.AlgElNullspaceDimension(module) = mindim then return; fi;
# This is the condition that we want. If it holds already, then there is
# nothing else to do.
dim:=SMTX.Dimension(module);
matrices:=ShallowCopy(module.generators);
ngens:=Length(matrices);
orig_ngens:=ngens;
F:=SMTX.Field(module);
R:=PolynomialRing(F);
# Now compute random elements el of the group algebra, calculate their
# characteristic polynomials, factorize, and apply the irreducible factors
# to el to get matrices with nontrivial nullspaces.
trying:=true;
count:=0;
newgenlist:=[];
while trying do
count:=count + 1;
if count mod SMTX.RAND_ELM_LIMIT = 0 then
Error("Have generated ",SMTX.RAND_ELM_LIMIT,
" random elements and failed ",
"to find a good one. Type return to keep trying.");
fi;
Info(InfoMeatAxe,2,"Choosing random element number ",count,".");
M:=SMTX.SMCoRaEl(matrices,ngens,newgenlist,dim,F);
ngens:=Length(matrices);
coefflist:=M[2];
pol:=M[3];
M:=M[1];
idmat:=M^0;
Info(InfoMeatAxe,2,"Evaluated characteristic polynomial. Time = ",
Runtime()-rt0,".");
# That is necessary in case p is defined over a smaller field that F.
oldpol:=pol;
# Now we extract the irreducible factors of pol starting with those
# of low degree
deg:=0;
fac:=[];
while deg <= mindim and trying do
repeat
deg:=deg + 1;
if deg > mindim then
fac:=[pol];
else
fac:=Factors(R, pol: factoroptions:=rec(onlydegs:=[deg]));
fac:=Filtered(fac,i->DegreeOfLaurentPolynomial(i)<=deg);
Info(InfoMeatAxe,2,Length(fac)," factors of degree ",deg,
", Time = ",Runtime()-rt0,".");
sfac:=Set(fac);
fi;
until fac <> [];
l:=Length(fac);
if trying and deg <= mindim then
i:=1;
while i <= l and trying do
mat:=Value(fac[i], M,idmat);
Info(InfoMeatAxe,2,"Evaluated matrix on factor. Time = ",
Runtime()-rt0,".");
N:=NullspaceMat(mat);
Info(InfoMeatAxe,2,"Evaluated nullspace. Dimension = ",
Length(N),". Time = ",Runtime()-rt0,".");
if Length(N) = mindim then
trying:=false;
SMTX.SetAlgEl(module, [newgenlist, coefflist]);
SMTX.SetAlgElMat(module, M);
SMTX.SetAlgElCharPol(module, oldpol);
SMTX.SetAlgElCharPolFac(module, fac[i]);
SMTX.SetAlgElNullspaceVec(module, N[1]);
SMTX.SetAlgElNullspaceDimension(module, Length(N));
fi;
i:=i + 1;
od;
fi;
if trying then
for q in fac do
pol:=Quotient(R, pol, q);
od;
fi;
od;
od;
Info(InfoMeatAxe,1,"Total time = ",Runtime()-rt0," milliseconds.");
end;
SMTX.GoodElementGModule:=SMTX_GoodElementGModule;
#############################################################################
##
#F EnlargedIrreducibleGModule(module, mat) . .add a generator to a module that
#
# 2bdef!
#############################################################################
##
#F SMTX.FrobeniusAction(A, v [, basis]) . . action of matrix A on
## . . Frobenius block of vector v
##
## FrobeniusAction(A, v) computes the Frobenius block of the dxd matrix A
## generated by the length - d vector v, and returns it.
## It is based on code of MinPolCoeffsMat.
## The optional third argument is for returning the basis for this block.
##
SMTX_FrobeniusAction:=function( arg )
local L, d, p, M, one, zero, R, h, v, w, i, j, nd, ans,
A, basis;
if Length(arg) = 2 then
A:=arg[1];
v:=arg[2];
basis:=0;
elif Length(arg) = 3 then
A:=arg[1];
v:=arg[2];
basis:=arg[3];
else
return Error("usage: SMTX.FrobeniusAction( <A>, <v>, [, <basis>] )");
fi;
one :=One(A[1][1]);
zero:=Zero(one);
d:=Length( A );
M:=ListWithIdenticalEntries(Length(A[1]) + 1,zero);
ConvertToVectorRep(M,DefaultField(v));
# L[i] (length d) will contain a vector with head entry 1 at position i,
# which is in the current block.
# R[i] (length d + 1 but (d + 1) - entry always 0) is vector expressing
# L[i] in terms of the basis of the block.
L:=[];
R:=[];
# <j> - 1 gives the power of <A> we are looking at
j:=1;
# spin vector around and construct polynomial
repeat
# compute the head of <v>
h:=1;
while v[h] = zero do
h:=h + 1;
od;
# start with appropriate polynomial x^(<j> - 1)
p:=ShallowCopy( M );
p[j]:=one;
# divide by known left sides
w:=v;
while h <= d and IsBound( L[h] ) do
p:=p - w[h] * R[h];
w:=w - w[h] * L[h];
while h <= d and w[h] = zero do
h:=h + 1;
od;
od;
# if <v> is not the zero vector try next power
if h <= d then
#AH replaced Copy by ShallowCopy as only vector is used
if basis <> 0 then basis[j]:=ShallowCopy(v); fi;
R[h]:=p * w[h]^-1;
L[h]:=w * w[h]^-1;
j:=j + 1;
v:=v * A;
fi;
until h > d;
nd:=Length(p);
while 0 < nd and p[nd] = zero do
nd:=nd - 1;
od;
nd:=nd - 1;
ans:=[];
for i in [1..nd - 1] do
ans[i]:=[];
for j in [1..nd] do ans[i][j]:=zero; od;
ans[i][i + 1]:=one;
od;
ans[nd]:=[];
for j in [1..nd] do
ans[nd][j]:= - p[j];
od;
return ans;
end;
SMTX.FrobeniusAction:=SMTX_FrobeniusAction;
#############################################################################
##
#F SMTX.CompleteBasis(matrices,basis) . complete a basis under a group action
##
## CompleteBasis( matrices, basis ) takes the partial basis 'basis' of the
## underlying space of the (irreducible) module defined by matrices, and
## attempts to extend it to a complete basis which is a direct sum of
## translates of the original subspace under group elements. It returns
## true or false according to whether it succeeds.
## It is called by IsAbsolutelyIrreducible()
##
SMTX_CompleteBasis:=function( matrices, basis )
local L, d, subd, subd0, zero, h, v, w, i, bno, gno, vno, newb, ngens;
subd:=Length(basis);
subd0:=subd;
d:=Length( basis[1] );
if d = subd then
return true;
fi;
# L is list of normalized generators of the subspace spanned by basis.
L:=[];
zero:=Zero(basis[1][1]);
ngens:=Length(matrices);
# First find normalized generators for subspace itself.
for i in [1..subd] do
v:=basis[i];
h:=1;
while v[h] = zero do
h:=h + 1;
od;
w:=v;
while h <= d and IsBound( L[h] ) do
w:=w - w[h] * L[h];
while h <= d and w[h] = zero do
h:=h + 1;
od;
od;
if h <= d then
L[h]:=w * w[h]^-1;
else
return Error("Initial vectors are not linearly independent.");
fi;
od;
# Now start translating
bno:=1; gno:=1; vno:=1;
while subd < d do
# translate vector vno of block bno by generator gno
v:= basis[ (bno - 1) * subd0 + vno] * matrices[gno];
h:=1;
while h<=d and v[h] = zero do
h:=h + 1;
od;
w:=v;
while h <= d and IsBound( L[h] ) do
w:=w - w[h] * L[h];
while h <= d and w[h] = zero do
h:=h + 1;
od;
od;
if h <= d then
# new generator (and block)
if vno = 1 then
newb:=true;
elif newb = false then
return false;
fi;
L[h]:=w * w[h]^-1;
subd:=subd + 1;
basis[subd]:=v;
else
# in existing subspace
if vno = 1 then
newb:=false;
elif newb = true then
return false;
fi;
fi;
vno:=vno + 1;
if vno > subd0 then
vno:=1;
gno:=gno + 1;
if gno > ngens then
gno:=1;
bno:=bno + 1;
fi;
fi;
od;
return true;
end;
SMTX.CompleteBasis:=SMTX_CompleteBasis;
#############################################################################
##
#F SMTX.AbsoluteIrreducibilityTest( module ) . . decide if an irreducible
## module over a finite field is absolutely irreducible
##
## this function does the work for an absolute irreducibility test but does
## not actually set the flags.
## The function calculates the centralizer of the module.
## The centralizer should be isomorphic to the multiplicative
## group of the field GF(q^e) for some e, or rather to the group of
## dim/e x dim/e scalar matrices over GF(q^e), or equivalently,
## dim x dim matrices composed of identical e x e blocks along the diagonal.
## e = 1 <=> the module is absolutely irreducible.
## The .fieldExtDeg component is set to e during the function call.
## The function shouldn't be called if the module has not already been
## shown to be irreducible, using IsIrreducible.
##
SMTX_AbsoluteIrreducibilityTest:=function( module )
local dim, ndim, gcd, div, e, ct, F, q, ok,
M, v, M0, v0, C, C0, centmat, one, zero,
pow, matrices, newmatrices, looking,
basisN, basisB, basisBN, P, Pinv, i, j, k, nblocks;
if not SMTX.IsMTXModule(module) then
Error("Argument of IsAbsoluteIrreducible is not a module");
fi;
if not SMTX.IsIrreducible(module) then
Error("Argument of iIsAbsoluteIrreducible s not an irreducible module");
fi;
if not module.IsOverFiniteField then
return Error("Argument of IsAbsoluteIrreducible is not over a finite field.");
fi;
dim:=SMTX.Dimension(module);
F:=SMTX.Field(module);
q:=Size(F);
matrices:=module.generators;
# M acts irreducibly on N, which is canonically defined with respect to M
# as the nullspace of fac(M), where fac is a factor of the char poly of M.
# ndim is the dimension of N, and v is a vector of N. All these come from
# the irreducibility test for the module.
# An element of the centralizer must centralize every element, and
# therefore M, and so must preserve N, since N is canonically defined
# wrt M. Our plan is therefore first to find an element which centralizes
# the restriction of M to N, and then extend it to the whole space.
M:=SMTX.AlgElMat(module);
ndim:=SMTX.AlgElNullspaceDimension(module);
v:=SMTX.AlgElNullspaceVec(module);
# e will have to divide both dim and ndim, and hence their gcd.
gcd:=GcdInt(dim, ndim);
Info(InfoMeatAxe,2,"GCD of module and nullspace dimensions = ", gcd, ".");
if gcd = 1 then
SMTX.SetDegreeFieldExt(module,1);
return true;
fi;
div:=DivisorsInt(gcd);
# It's easy to find elements in the centralizer of an element in Frobenius
# (=rational canonical) form (centralizing elements are defined by their
# action on the first basis element).
# M0 is the Frobenius form for the action of M on N.
# basisN is set by the function SMTX.FrobeniusAction to be the
# basis v, vM, vM^2, .. for N
basisN:=[];
Info(InfoMeatAxe,2,
"Calc. Frobenius action of element from group algebra on nullspace.");
M0:=SMTX.FrobeniusAction(M,v,basisN);
zero:=Zero(F);
one:= One(F);
v0:=ListWithIdenticalEntries(Length(M0[1]),zero);
v0[1]:=one;
ConvertToVectorRep(v0, F);
# v0 is just the vector (1, 0, 0....0) of length ndim. It has nothing
# in particular to do with M0[1], but multiplying a vector that happens to be
# around by 0 is a good way to get a zero vector of the right length.
# we try all possible divisors of gcd (biggest first) as possibilities for e
# We're looking for a centralizing element with order dividing q^e - 1, and
# blocks size e on N.
for ct in Reversed([2..Length(div)]) do
e:=div[ct];
Info(InfoMeatAxe,2,"Trying dimension ",e," for centralising field.");
# if ndim = e, M0 will do.
if ndim > e then
C:=M0;
# Take the smallest power of C guaranteed to have order dividing
# q^e - 1, and try that.
pow:=(q^ndim - 1) / (q^e - 1);
Info(InfoMeatAxe,2,"Looking for a suitable centralising element.");
repeat
# The first time through the loop C is M0, otherwise we choose C
# at random from the centralizer of M0. Since M0 is in Frobenius
# form any centralising element is determined by its top row
# (which may be anything but the zero vector).
if Length(C)=0 then
C[1]:=[];
repeat
ok:=0;
for i in [1..ndim] do
C[1][i]:=Random(F);
if C[1][i] <> zero then ok:=1; fi;
od;
until ok=1;
for i in [2..ndim] do C[i]:=C[i - 1] * M0; od;
C:=ImmutableMatrix(F,C);
fi;
# C0 is the Frobenius form for the action of this power on one
# of its blocks, B (all blocks have the same size). basisBN will
# be set to be a basis for B, in terms of the elements of basisN.
# A matrix product gives us the basis for B in terms of the
# original basis for the module.
basisBN:=[];
C0:=SMTX.FrobeniusAction(C^pow,v0,basisBN);
C:=[];
until Length(C0) = e;
Info(InfoMeatAxe,2,"Found one.");
basisB:=ShallowCopy(basisBN * basisN);
else
C0:=M0;
basisB:=ShallowCopy(basisN);
fi;
# Now try to extend basisB to a basis for the whole module, by
# translating it by the generating matrices.
Info(InfoMeatAxe,2,"Trying to extend basis to whole module.");
if SMTX.CompleteBasis(matrices,basisB) then
# We succeeded in extending the basis (might not have done).
# So now we have a full basis, which we think of now as a base
# change matrix.
Info(InfoMeatAxe,2,"Succeeded. Calculating centralising matrix.");
newmatrices:=[];
P:=ImmutableMatrix(F,basisB);
Pinv:=P^-1;
for i in [1..Length(matrices)] do
newmatrices[i]:=P * matrices[i] * Pinv;
od;
# Make the sum of copies of C0 as centmat
centmat:=NullMat(dim, dim, F);
nblocks:=dim/e;
for i in [1..nblocks] do
for j in [1..e] do
for k in [1..e] do
centmat[ (i - 1) * e + j][ (i - 1) * e + k]:=C0[j][k];
od;
od;
od;
centmat := ImmutableMatrix(F, centmat);
Info(InfoMeatAxe,2,"Checking that it centralises the generators.");
# Check centralizing.
looking:=true;
i:=1;
while looking and i <= Length(newmatrices) do
if newmatrices[i] * centmat <> centmat * newmatrices[i] then
looking:=false;
fi;
i:=i + 1;
od;
if looking then
Info(InfoMeatAxe,2,"It did!");
SMTX.SetDegreeFieldExt(module, e);
SMTX.SetCentMat(module, Pinv * centmat * P); # get the base right
# We will also record the minimal polynomial of C0 (and hence of
# centmat) in case we need it at some future date.
SMTX.SetCentMatMinPoly(module, MinimalPolynomialMatrixNC(F,C0,1));
return false;
fi;
Info(InfoMeatAxe,2,"But it didn't.");
else
Info(InfoMeatAxe,2,"Failed!");
fi;
od;
Info(InfoMeatAxe,2,
"Tried all divisors. Must be absolutely irreducible.");
SMTX.SetDegreeFieldExt(module, 1);
return true;
end;
SMTX.AbsoluteIrreducibilityTest:=SMTX_AbsoluteIrreducibilityTest;
SMTX.DegreeFieldExt:=function(module)
if not IsBound(module.smashMeataxe.degreeFieldExt) then
SMTX.AbsoluteIrreducibilityTest( module );
fi;
return module.smashMeataxe.degreeFieldExt;
end;
SMTX.DegreeSplittingField:=function(module)
return DegreeOverPrimeField(SMTX.Field(module))
*SMTX.DegreeFieldExt(module);
end;
#############################################################################
##
#F FieldGenCentMat( module ) . . find a centralizing matrix that generates
## the centralizing field of an irred. module
##
## FieldGenCentMat( ) should only be applied to modules that have already
## been proved irreducible using IsIrreducible. It then tests for absolute
## irreducibility (if not already known) and does nothing if module is
## absolutely irreducible. Otherwise, it returns a a matrix that generates
## (multiplicatively) the centralizing field (i.e. its multiplicative order
## is q^e - 1, where e is the degree of the centralizing field. This is not
## yet used, but maybe in future, if we wish to reduce the group to matrices
## over the larger field.
SMTX.FieldGenCentMat:=function( module )
local e, F, R, q, qe, minpol, pp,
M, v, M0, v0, C, C0, centmat, newcentmat, genpol, looking,
i, l, okd;
if SMTX.FGCentMat(module)=fail then
if SMTX.IsMTXModule(module) = false then
Error("Argument of IsIrreducible is not a module.");
fi;
if not SMTX.IsIrreducible(module) then
Error("GModule is not irreducible.");
fi;
# enforce absirred knowledge as well.
#if not SMTX.IsAbsolutelyIrreducible(module) then
# Error("GModule is not absolutely irreducible.");
#fi;
if SMTX.CentMat(module)=fail then
Error("No CentMat component!");
fi;
F:=SMTX.Field(module);
R:=PolynomialRing(F);
q:=Size(F);
e :=SMTX.DegreeFieldExt(module);
qe:=q^e - 1;
minpol:=SMTX.CentMatMinPoly(module);
# Factorise q^e - 1
pp:=PrimePowersInt(qe);
# We seek a generator of the field of order q^e - 1. In other words, a
# polynomial genpol of degree e, which has multiplicative order q^e - 1
# modulo minpol. We first try the polynomial x, which is the element we
# have already. If this does not work, then we try random nonconstant
# polynomials until we find one with the right order.
genpol:=Indeterminate(F);
looking:=true;
while looking do
if genpol <> minpol then
okd:=FFPOrderKnownDividend(R, genpol, minpol, pp);
if okd[1] * Order(One(F)*okd[2]) = qe then
looking:=false;
fi;
fi;
if looking then
repeat
genpol:=RandomPol(F, e,1);
until DegreeOfUnivariateLaurentPolynomial(genpol) > 0;
genpol:=StandardAssociate(R, genpol);
fi;
od;
# Finally recalculate centmat and its minimal polynomial.
centmat:=SMTX.CentMat(module);
newcentmat:=Value(genpol, centmat,centmat^0);
SMTX.SetFGCentMat(module, newcentmat);
SMTX.SetFGCentMatMinPoly(module,MinimalPolynomialMatrixNC(F,newcentmat,1));
# Ugh! That was very inefficient - should work out the min poly using
# polynomials, but will sort that out if its ever needed.
fi;
return SMTX.FGCentMat(module);
end;
###############################################################################
##
#F SMTX.CollectedFactors( module ) . . find composition factors of a module
##
## 01/01/01 Try to deal more efficiently with large numbers of repeated
## small factors by using SMTX.Homomorphisms
##
## SMTX.CollectedFactors calls IsIrreducible repeatedly to find the
## composition factors of the GModule `module'. It also calls
## IsomorphismGModule to determine which are isomorphic.
## It returns a list [f1, f2, ..fr], where each fi is a list [m, n],
## where m is an irreducible composition factor of module, and n is the
## number of times it occurs in module.
##
SMTX_CollectedFactors:= function( module )
local field,dim, factors, factorsout, queue, cmod, new,
d, i, j, l, lf, q, smod, ds, homs, mat;
if SMTX.IsMTXModule(module) = false then
return Error("Argument is not a module.");
fi;
dim:=SMTX.Dimension(module);
field:= SMTX.Field(module);
factors:=[];
for i in [1..dim] do
factors[i]:=[];
od;
# factors[i] will contain a list [f1, f2, ..., fr] of the composition factors
# of module of dimension i. Each fi will have the form [m, n], where m is
# the module, and n its multiplicity.
queue:=[module];
# queue is the list of modules awaiting processing.
while Length(queue) > 0 do
cmod:=Remove(queue);
d:=SMTX.Dimension(cmod);
Info(InfoMeatAxe,3,"Length of queue = ", Length(queue)+1, ", dim = ", d, ".");
if SMTX.IsIrreducible(cmod) then
Info(InfoMeatAxe,2,"Irreducible: ");
# module is irreducible. See if it is already on the list.
new:=true;
lf:=Length(factors[d]);
i:=1;
while new and i <= lf do
if SMTX.IsEquivalent(factors[d][i][1], cmod) then
new:=false;
factors[d][i][2]:=factors[d][i][2] + 1;
fi;
i:=i + 1;
od;
if new then
Info(InfoMeatAxe,2," new.");
factors[d][lf + 1]:=[cmod, 1];
else
Info(InfoMeatAxe,2," old.");
fi;
else
Info(InfoMeatAxe,2,"Reducible.");
# module is reducible. Add sub- and quotient-modules to queue.
q:=SMTX.InducedAction(cmod,
SMTX.Subbasis(cmod),3);
smod:=q[1];
ds:=SMTX.Dimension(smod);
if ds < d/10 and SMTX.IsIrreducible(smod) then
# Small dimensional submodule
# test for repeated occurrences.
homs:=SMTX.Homomorphisms( smod, cmod); # must have length >0
# build the submodule formed by their images
mat:=Concatenation(homs);
TriangulizeMat(mat);
mat:=Filtered(mat,i->not IsZero(i));
mat:=ImmutableMatrix(field,mat);
if Length(mat)<cmod.dimension then
# there is still some factor left
Add(queue, SMTX.InducedActionFactorModule(cmod, mat));
fi;
Info(InfoMeatAxe,2,
"Small irreducible submodule X ",Length(homs),
" subdim :",Length(mat)/smod.dimension,":");
# module is irreducible. See if it is already on the list.
new:=true;
lf:=Length(factors[ds]);
i:=1;
while new and i <= lf do
if SMTX.IsEquivalent(factors[ds][i][1], smod) then
Info(InfoMeatAxe,2," old.");
new:=false;
factors[ds][i][2]:=factors[ds][i][2] +
Length(mat)/smod.dimension;
fi;
i:=i + 1;
od;
if new then
Info(InfoMeatAxe,2," new.");
factors[ds][lf + 1]:=[smod, Length(mat)/smod.dimension];
fi;
else
Add(queue, smod);
Add(queue, q[2]);
fi;
fi;
od;
# Now repack the sequence for output.
l:=0;
factorsout:=[];
for i in [1..dim] do
for j in [1..Length(factors[i])] do
l:=l + 1;
factorsout[l]:=factors[i][j];
od;
od;
return factorsout;
end;
SMTX.CollectedFactors:=SMTX_CollectedFactors;
SMTX.CompositionFactors:=function( module )
if SMTX.IsIrreducible(module) then
return [module];
else
module:=SMTX.InducedAction(module,
SMTX.Subbasis(module),3);
return Concatenation(SMTX.CompositionFactors(module[1]),
SMTX.CompositionFactors(module[2]));
fi;
end;
###############################################################################
##
#F SMTX.Distinguish( cf, i ) distinguish a composition factor of a module
##
## cf is assumed to be the output of a call to SMTX.CollectedFactors,
## and i is the number of one of the cf.
## Distinguish tries to find a group-algebra element for factor[i]
## which gives nullity zero when applied to all other cf.
## Once this is done, it is easy to find submodules containing this
## composition factor.
##
SMTX_Distinguish:=function( cf, i )
local el, genpair, ngens, orig_ngens, mat, matsi, mats, M, idmat,
dimi, dim, F, fac, sfac, p, q, oldp, found, extdeg, j, k,
lcf, lf, x, y, wno, deg, trying, N, fact, R;
lcf:=Length(cf);
ngens:=Length(cf[1][1].generators);
orig_ngens:=ngens;
F:=SMTX.Field(cf[1][1]);
R:=PolynomialRing(F);
matsi:=ShallowCopy(cf[i][1].generators);
dimi:=SMTX.Dimension(cf[i][1]);
idmat:=matsi[1]^0;
# First check that the existing nullspace has dim. 1 over centralising field.
SMTX.GoodElementGModule(cf[i][1]);
# First see if the existing element is OK
# Apply the alg. el. of factor i to every other factor and see if the
# matrix is nonsingular.
found:=true;
el:=SMTX.AlgEl(cf[i][1]);
fact:=SMTX.AlgElCharPolFac(cf[i][1]);
for j in [1..lcf] do
if j <> i and found then
mats:=ShallowCopy(cf[j][1].generators);
dim:=SMTX.Dimension(cf[j][1]);
for genpair in el[1] do
ngens:=ngens + 1;
mats[ngens]:=mats[genpair[1]] * mats[genpair[2]];
od;
M:=ImmutableMatrix(F,Sum([1..ngens], k -> el[2][k] * mats[k]));
ngens:=orig_ngens;
mat:=Value(fact, M, M^0);
if RankMat(mat) < dim then
found:=false;
Info(InfoMeatAxe,2,"Current element failed on factor ", j);
fi;
fi;
od;
if found then
Info(InfoMeatAxe,2,"Current element worked.");
return;
fi;
# That didn't work, so we have to try new random elements.
wno:=0;
el:=[]; el[1]:=[];
extdeg:=SMTX.DegreeFieldExt(cf[i][1]);
while found = false do
Info(InfoMeatAxe,2,"Trying new one.");
wno:=wno + 1;
# Add a new generator if there are less than 8 or if wno mod 10=0.
if ngens<8 or wno mod 10 = 0 then
x:=Random([1..ngens]);
y:=x;
while y = x and ngens > 1 do y:=Random([1..ngens]); od;
Add(el[1], [x, y]);
ngens:=ngens + 1;
matsi[ngens]:=matsi[x] * matsi[y];
fi;
# Now take the new random element
el[2]:=[];
for j in [1..ngens] do el[2][j]:=Random(F); od;
# First evaluate on cf[i][1].
M:=ImmutableMatrix(F,Sum([1..ngens], k -> el[2][k] * matsi[k]));
p:=CharacteristicPolynomialMatrixNC(F,M,1);
# That is necessary in case p is defined over a smaller field that F.
oldp:=p;
# extract irreducible factors
deg:=0;
fac:=[];
trying:=true;
while deg <= extdeg and trying do
repeat
deg:=deg + 1;
if deg > extdeg then
fac:=[p];
else
fac:=Factors(R, p: factoroptions:=rec(onlydegs:=[deg]));
fac:=Filtered(fac,i->DegreeOfLaurentPolynomial(i)<=deg);
sfac:=Set(fac);
fi;
until fac <> [];
lf:=Length(fac);
if trying and deg <= extdeg then
j:=1;
while j <= lf and trying do
mat:=Value(fac[j], M,idmat);
N:=NullspaceMat(mat);
if Length(N) = extdeg then
trying:=false;
SMTX.SetAlgEl(cf[i][1], el);
SMTX.SetAlgElMat(cf[i][1], M);
SMTX.SetAlgElCharPol(cf[i][1], oldp);
SMTX.SetAlgElCharPolFac(cf[i][1], fac[j]);
SMTX.SetAlgElNullspaceVec(cf[i][1], N[1]);
fi;
j:=j + 1;
od;
fi;
if trying then
for q in fac do
p:=Quotient(R, p, q);
od;
fi;
od;
# Now see if it works against the other factors of cf
if trying = false then
Info(InfoMeatAxe,2,"Found one.");
found:=true;
fact:=SMTX.AlgElCharPolFac(cf[i][1]);
# Apply the alg. el. of factor i to every other factor and
# see if the matrix is nonsingular.
for j in [1..lcf] do
if j <> i and found then
mats:=ShallowCopy(cf[j][1].generators);
dim:=SMTX.Dimension(cf[j][1]);
ngens:=orig_ngens;
for genpair in el[1] do
ngens:=ngens + 1;
mats[ngens]:=mats[genpair[1]] * mats[genpair[2]];
od;
M:=ImmutableMatrix(F,Sum([1..ngens], k -> el[2][k] * mats[k]));
mat:=Value(fact, M, M^0);
if RankMat(mat) < dim then
found:=false;
Info(InfoMeatAxe,2,"Failed on factor ", j);
fi;
fi;
od;
fi;
if found then
Info(InfoMeatAxe,2,"It worked!");
fi;
od;
end;
SMTX.Distinguish:=SMTX_Distinguish;
###############################################################################
##
#F SMTX.MinimalSubGModule( module, cf, i ) . . find minimal submodule
## containing a given composition factor.
##
## cf is assumed to be the output of a call to SMTX.CollectedFactors,
## and i is the number of one of the cf.
## It is assumed that SMTX.Distinguish(cf, i) has already been called.
## A basis of a minimal submodule of module containing the composition factor
## cf[i][1] is calculated and returned - i.e. if cf[i][2] = 1.
##
SMTX_MinimalSubGModule:=function( module, cf, i )
local el, genpair, ngens, orig_ngens, mat, mats, M, dim, F,
k, N, fact;
if SMTX.IsMTXModule(module) = false then
return Error("First argument is not a module.");
fi;
ngens:=Length(module.generators);
orig_ngens:=ngens;
F:=SMTX.Field(module);
# Apply the alg. el. of factor i to module
el:=SMTX.AlgEl(cf[i][1]);
mats:=ShallowCopy(module.generators);
dim:=SMTX.Dimension(module);
for genpair in el[1] do
ngens:=ngens + 1;
mats[ngens]:=mats[genpair[1]] * mats[genpair[2]];
od;
M:=ImmutableMatrix(F,Sum([1..ngens], k -> el[2][k] * mats[k]));
# Now throw away extra generators of module
for k in [orig_ngens + 1..ngens] do
Unbind(mats[k]);
od;
ngens:=orig_ngens;
fact:=SMTX.AlgElCharPolFac(cf[i][1]);
mat:=Value(fact, M,M^0);
N:=NullspaceMat(mat);
return SMTX.SpinnedBasis(N[1], mats,F, ngens);
end;
SMTX.MinimalSubGModule:=SMTX_MinimalSubGModule;
#############################################################################
##
#F SMTX.Isomomorphism(module1, module2) . . . .
## decide whether two irreducible modules are isomorphic.
##
## If the 2 modules are not isomorphic, this function returns false;
## if they are isomorphic it returns the matrix B, whose rows form the
## basis of module2 which is the image of the standard basis for module1.
## Thus if X and Y are corresponding matrices in the generating sets
## for module1 and module2 respectively, Y = B^-1XB
## It is assumed that the same group acts on both modules.
## Otherwise who knows what will happen?
##
SMTX_IsomorphismComp:=function(module1, module2, action)
local matrices, matrices1, matrices2, F, R, dim, swapmodule, genpair,
swapped, orig_ngens, i, j, el, p, fac, ngens, M, mat, v1, v2, v,
N, basis, basis1, basis2;
if SMTX.IsMTXModule(module1) = false then
Error("Argument is not a module.");
elif SMTX.IsMTXModule(module2) = false then
Error("Argument is not a module.");
elif SMTX.Field(module1) <> SMTX.Field(module2) then
Error("GModules are defined over different fields.");
fi;
swapped:=false;
if not SMTX.HasIsIrreducible(module1) then
if not SMTX.HasIsIrreducible(module2) then
Error("Neither module is known to be irreducible.");
else
# The second module is known to be irreducible, so swap arguments.
swapmodule:=module2; module2:=module1; module1:=swapmodule;
swapped:=true;
Info(InfoMeatAxe,2,"Second module is irreducible. Swap them round.");
fi;
fi;
# At this stage, module1 is known to be irreducible
dim:=SMTX.Dimension(module1);
if dim <> SMTX.Dimension(module2) then
Info(InfoMeatAxe,2,"GModules have different dimensions.");
return fail;
fi;
F:=SMTX.Field(module1);
R:=PolynomialRing(F);
# First we must check that our nullspace is 1-dimensional over the
# centralizing field.
Info(InfoMeatAxe,2,
"Checking nullspace 1-dimensional over centralising field.");
SMTX.GoodElementGModule(module1);
matrices1:=module1.generators;
matrices2:=ShallowCopy(module2.generators);
ngens:=Length(matrices1);
orig_ngens:=ngens;
if ngens <> Length(matrices2) then
Error("GModules have different numbers of defining matrices.");
fi;
# Now we calculate the element in the group algebra of module2 that
# corresponds to that in module1. This is done using the AlgEl flag
# for module1. We first extend the generating set in the same way as
# we did for module1, and then calculate the group alg. element as
# a linear sum in the generators.
Info(InfoMeatAxe,2,"Extending generating set for second module.");
el:=SMTX.AlgEl(module1);
for genpair in el[1] do
ngens:=ngens + 1;
matrices2[ngens]:=matrices2[genpair[1]] * matrices2[genpair[2]];
od;
M:=ImmutableMatrix(F,Sum([1..ngens], i -> el[2][i] * matrices2[i]));
# Having done that, we no longer want the extra generators of module2,
# so we throw them away again.
for i in [orig_ngens + 1..ngens] do
Unbind(matrices2[i]);
od;
Info(InfoMeatAxe,2,
"Calculating characteristic polynomial for second module.");
p:=CharacteristicPolynomialMatrixNC(F,M,1);
if p <> SMTX.AlgElCharPol(module1) then
Info(InfoMeatAxe,2,"Characteristic polynomial different.");
return fail;
fi;
fac:=SMTX.AlgElCharPolFac(module1);
mat:=Value(fac, M,M^0);
Info(InfoMeatAxe,2,"Calculating nullspace for second module.");
N:=NullspaceMat(mat);
if Length(N) <> SMTX.AlgElNullspaceDimension(module1) then
Info(InfoMeatAxe,2,"Null space dimensions different.");
return fail;
fi;
# That concludes the easy tests for nonisomorphism. Now we must proceed
# to spin up. We first form the direct sum of the generating matrices.
Info(InfoMeatAxe,2,"Spinning up in direct sum.");
matrices:=SMTX.MatrixSum(matrices1, matrices2);
v1:=SMTX.AlgElNullspaceVec(module1);
v2:=N[1];
v:=Concatenation(v1, v2);
basis:=SMTX.SpinnedBasis(v, matrices, F);
if Length(basis) = dim then
if action<>true then
return true;
fi;
basis1:=[]; basis2:=[];
for i in [1..dim] do
basis1[i]:=[]; basis2[i]:=[];
for j in [1..dim] do
basis1[i][j]:=basis[i][j];
basis2[i][j]:=basis[i][j + dim];
od;
od;
if swapped then
return basis2^-1 * basis1;
else
return basis1^-1 * basis2;
fi;
else
return fail;
fi;
end;
SMTX.IsomorphismComp:=SMTX_IsomorphismComp;
SMTX.IsomorphismIrred:=function(module1,module2)
return SMTX.IsomorphismComp(module1,module2,true);
end;
SMTX.Isomorphism:=SMTX.IsomorphismIrred;
SMTX.IsEquivalent:=function(module1,module2)
return SMTX.IsomorphismComp(module1,module2,false)<>fail;
end;
#############################################################################
##
#F SMTX.MatrixSum(matrices1, matrices2) direct sum of two lists of matrices
##
SMTX_MatrixSum:=function(matrices1, matrices2)
local matrices, nmats, i;
matrices:=[];
nmats:=Length(matrices1);
for i in [1..nmats] do
matrices[i]:=DirectSumMat(matrices1[i],matrices2[i]);
od;
return matrices;
end;
SMTX.MatrixSum:=SMTX_MatrixSum;
#############################################################################
##
#F SMTX.Homomorphisms( m1, m2) . . . . homomorphisms from an irreducible
## . . . GModule to an arbitrary GModule
##
## It is assumed that m1 is a module that has been proved irreducible
## (using IsIrreducible), and m2 is an arbitrary module for the same group.
## A basis of the space of G-homomorphisms from m1 to m2 is returned.
## Each homomorphism is given as a list of base images.
##
SMTX_Homomorphisms:= function(m1, m2)
local F, ngens, orig_ngens, mats1, mats2, dim1, dim2, m1bas, imbases,
el, genpair, fac, mat, N, imlen, subdim, leadpos, vec, imvecs,
numrels, rels, leadposrels, newrels, bno, genno, colno, rowno,
zero, looking, ans, i, j, k;
if not SMTX.IsMTXModule(m1) then
return Error("First argument is not a module.");
elif not SMTX.IsIrreducible(m1) then
return Error("First module is not known to be irreducible.");
fi;
if not SMTX.IsMTXModule(m2) then
return Error("Second argument is not a module.");
fi;
mats1:=m1.generators;
mats2:=ShallowCopy(m2.generators);
ngens:=Length(mats1);
if ngens <> Length(mats2) then
return Error("GModules have different numbers of generators.");
fi;
F:=SMTX.Field(m1);
if F <> SMTX.Field(m2) then
return Error("GModules are defined over different fields.");
fi;
zero:=Zero(F);
dim1:=SMTX.Dimension(m1); dim2:=SMTX.Dimension(m2);
m1bas:=[];
m1bas[1]:= SMTX.AlgElNullspaceVec(m1);
# In any homomorphism from m1 to m2, the vector in the nullspace of the
# algebraic element that was used to prove irreducibility (which is now
# m1bas[1]) must map onto a vector in the nullspace of the same algebraic
# element evaluated in m2. We therefore calculate this nullspaces, and
# store a basis in imbases.
Info(InfoMeatAxe,2,"Extending generating set for second module.");
orig_ngens:=ngens;
el:=SMTX.AlgEl(m1);
for genpair in el[1] do
ngens:=ngens + 1;
mats2[ngens]:=mats2[genpair[1]] * mats2[genpair[2]];
od;
mat:=ImmutableMatrix(F,Sum([1..ngens], i -> el[2][i] * mats2[i]));
# Having done that, we no longer want the extra generators of m2,
# so we throw them away again.
for i in [orig_ngens + 1..ngens] do
Unbind(mats2[i]);
od;
ngens:=orig_ngens;
fac:=SMTX.AlgElCharPolFac(m1);
mat:=Value(fac, mat,mat^0);
Info(InfoMeatAxe,2,"Calculating nullspace for second module.");
N:=NullspaceMat(mat);
imlen:=Length(N);
Info(InfoMeatAxe,2,"Dimension = ", imlen, ".");
if imlen = 0 then
return [];
fi;
imbases:=List(N, vec -> [vec]);
# Now the main algorithm starts. We are going to spin the vectors in m1bas
# under the action of the module generators, norming as we go. Every
# operation that we perform on m1bas will also be performed on each of the
# vectors in imbas[1], ..., imbas[imlen].
# When we find a vector that norms to zero in m1bas, then the image of this
# under a homomorphism must be zero. This leads to a linear relation
# amongst some vectors in imbas. We store up such relations, echelonizing as
# we go. At the end, if we have numrels subch independent relations, then
# there will be imlen - numrels independent homomorphisms from m1 to m2,
# which we can then calculate.
subdim:=1; # the dimension of module spanned by m1bas
numrels:=0;
rels:=[];
# leadpos[j] will be the position of the first nonzero entry in m1bas[j]
leadpos:=[];
vec:=m1bas[1];
j:=1;
while j <= dim1 and vec[j] = zero do j:=j + 1; od;
leadpos[1]:=j;
k:=vec[j]^-1;
m1bas[1]:=k * vec;
for i in [1..imlen] do
imbases[i][1]:=k * imbases[i][1];
od;
leadposrels:=[];
# This will play the same role as leadpos but for the relation matrix.
Info(InfoMeatAxe,2,"Starting spinning.");
bno:=1;
while bno <= subdim do
for genno in [1..ngens] do
# apply generator no. genno to submodule generator bno
vec:=m1bas[bno] * mats1[genno];
# and do the same to the images
imvecs:=[];
for i in [1..imlen] do
imvecs[i]:=imbases[i][bno] * mats2[genno];
od;
# try to express w in terms of existing submodule generators
# make same changes to images
j:=1;
for j in [1..subdim] do
k:=vec[leadpos[j]];
if k <> zero then
vec:=vec - k * m1bas[j];
for i in [1..imlen] do
imvecs[i]:=imvecs[i] - k * imbases[i][j];
od;
fi;
od;
j:=1;
while j <= dim1 and vec[j] = zero do j:=j + 1; od;
if j <= dim1 then
# we have found a new generator of the submodule
subdim:=subdim + 1;
leadpos[subdim]:=j;
k:=vec[j]^-1;
m1bas[subdim]:=k * vec;
for i in [1..imlen] do
imbases[i][subdim]:=k * imvecs[i];
od;
else
# vec has reduced to zero. We get relations among the imvecs.
# (these are given by the transpose of imvec)
# reduce these against any existing relations.
newrels:=TransposedMat(imvecs);
for i in [1..Length(newrels)] do
vec:=newrels[i];
for j in [1..numrels] do
k:=vec[leadposrels[j]];
if k <> zero then
vec:=vec - k * rels[j];
fi;
od;
j:=1;
while j <= imlen and vec[j] = zero do j:=j + 1; od;
if j <= imlen then
# we have a new relation
numrels:=numrels + 1;
# if we have imlen relations, there can be no homomorphisms
# so we might as well give up immediately
if numrels = imlen then
return [];
fi;
k:=vec[j]^-1;
rels[numrels]:=k * vec;
leadposrels[numrels]:=j;
fi;
od;
fi;
od;
bno:=bno + 1;
od;
# That concludes the spinning. Now we do row operations on the im1bas to
# make it the identity, and do the same operations to the imvecs.
# Then the homomorphisms we output will be the basis images.
Info(InfoMeatAxe,2,"Done. Reducing spun up basis.");
for colno in [1..dim1] do
rowno:=colno;
looking:=true;
while rowno <= dim1 and looking do
if m1bas[rowno][colno] <> zero then
looking:=false;
if rowno <> colno then
# swap rows rowno and colno
vec:=m1bas[rowno]; m1bas[rowno]:=m1bas[colno];
m1bas[colno]:=vec;
# and of course the same in the images
for i in [1..imlen] do
vec:=imbases[i][rowno];
imbases[i][rowno]:=imbases[i][colno];
imbases[i][colno]:=vec;
od;
fi;
# and then clear remainder of column
for j in [1..dim1] do
if j <> colno and m1bas[j][colno] <> zero then
k:=m1bas[j][colno];
m1bas[j]:=m1bas[j] - k * m1bas[colno];
for i in [1..imlen] do
imbases[i][j]:=imbases[i][j] - k * imbases[i][colno];
od;
fi;
od;
fi;
rowno:=rowno + 1;
od;
od;
# Now we are ready to compute and output the linearly independent
# homomorphisms. The coefficients for the solution are given by
# the basis elements of the nullspace of the transpose of rels.
Info(InfoMeatAxe,2,"Done. Calculating homomorphisms.");
if rels = [] then
rels:=NullMat(imlen, 1, F);
else
rels:=TransposedMat(rels);
fi;
N:=NullspaceMat(rels);
ans:=[];
for vec in N do
mat:=ImmutableMatrix(F, Sum([1..imlen], i -> vec[i] * imbases[i]));
Add(ans, mat);
od;
return ans;
end;
SMTX.Homomorphisms:=SMTX_Homomorphisms;
#############################################################################
##
#F SMTX.SortHomGModule( m1, m2, homs) . . sort output of HomGModule
## according to their images
##
## It is assumed that m1 is a module that has been proved irreducible
## (using IsIrreducible), and m2 is an arbitrary module for the same group,
## and that homs is the output of a call HomGModule(m1, m2).
## Let e be the degree of the centralising field of m1.
## If e = 1 then SMTX.SortHomGModule does nothing. If e > 1, then it replaces
## the basis contained in homs by a new basis arranged in the form
## b11, b12, ..., b1e, b21, b22, ...b2e, ..., br1, br2, ...bre, where each
## block of e adjacent basis vectors are all equivalent under the
## centralising field of m1, and so they all have the same image in m2.
## A complete list of the distinct images can then be obtained with a call
## to DistinctIms(m1, m2, homs).
##
SMTX_SortHomGModule:=function(m1, m2, homs)
local e, F, ngens, mats1, mats2, dim1, dim2, centmat, fullimbas, oldhoms,
homno, dimhoms, newdim, subdim, leadpos, vec, nexthom,
i, j, k, zero;
if SMTX.IsAbsolutelyIrreducible(m1) then return; fi;
e:=SMTX.DegreeFieldExt(m1);
F:=SMTX.Field(m1);
zero:=Zero(F);
mats1:=m1.generators; mats2:=m2.generators;
dim1:=SMTX.Dimension(m1); dim2:=SMTX.Dimension(m2);
ngens:=Length(mats1);
centmat:=SMTX.CentMat(m1);
fullimbas:=[];
subdim:=0;
leadpos:=[];
# fullimbas will contain an echelonised basis for the submodule of m2
# generated by all images of the basis vectors of hom that we have found
# so far; subdim is its length.
# We go through the existing basis of homs.
# For each hom in the basis, we first check whether the first vector in
# the image of hom is in the space spanned by fullimbas.
# If so, we reject hom. If not, then hom is adjoined to the new
# basis of homs, as are the other e-1 linearly independent homomorphisms
# that are equivalent to hom by a multiplication by centmat. The
# resulting block of e homomorphisms all have the same image in m2.
# first make a copy of homs.
oldhoms:=ShallowCopy(homs);
dimhoms:=Length(homs);
homno:=0; newdim:=0;
while homno < dimhoms and newdim < dimhoms do
homno:=homno + 1;
nexthom:=oldhoms[homno];
vec:=nexthom[1];
# Now check whether vec is in existing submodule spanned by fullimbas
j:=1;
for j in [1..subdim] do
k:=vec[leadpos[j]];
if k <> zero then
vec:=vec - k * fullimbas[j];
fi;
od;
j:=1;
while j <= dim2 and vec[j] = zero do j:=j + 1; od;
if j <= dim2 then
# vec is not in the image, so we adjoin this homomorphism to the list;
# first adjoin vec and all other basis vectors in the image to fullimbas
subdim:=subdim + 1;
leadpos[subdim]:=j;
k:=vec[j]^-1;
fullimbas[subdim]:=k * vec;
for i in [2..dim1] do
vec:=nexthom[i];
j:=1;
for j in [1..subdim] do
k:=vec[leadpos[j]];
if k <> zero then
vec:=vec - k * fullimbas[j];
fi;
od;
j:=1;
while j <= dim2 and vec[j] = zero do j:=j + 1; od;
subdim:=subdim + 1;
leadpos[subdim]:=j;
k:=vec[j]^-1;
fullimbas[subdim]:=k * vec;
od;
newdim:=newdim + 1;
homs[newdim]:=nexthom;
# Now add on the other e - 1 homomorphisms equivalent to
# newhom by centmat.
for k in [1..e - 1] do
nexthom:=centmat * nexthom;
newdim:=newdim + 1;
homs[newdim]:=nexthom;
od;
fi;
od;
end;
SMTX.SortHomGModule:=SMTX_SortHomGModule;
#############################################################################
##
#F SMTX.Homomorphism(module1,module2,mat) . . . define a module homorphism
##
## module1 and module2 should be meataxe modules of dimensions m and n
## over the same algebra, and mat an mXn matrix over the field of
## the modules that defines a homomorphism module1 -> module2, where
## the i-th row of mat gives the image in module2 of the i-th basis
## vector of module1.
## It is checked whether mat really does define a homomorphism.
## If, so then the corresponding vector space homomorphism from the underlying
## row space of module1 to that of module2 is returned. This can be used
## for computing images, kernel, preimages, etc.
SMTX_Homomorphism:=function(module1, module2, mat)
local F, gens1, gens2, ng, dim1, dim2, i, j;
F:=SMTX.Field(module1);
if F <> SMTX.Field(module2) then
Error("Modules are over different fields");
fi;
gens1:=SMTX.Generators(module1); gens2:=SMTX.Generators(module2);
dim1:=SMTX.Dimension(module1); dim2:=SMTX.Dimension(module2);
ng:=Length(gens1);
if ng <> Length(gens2) then
Error("Modules are not over the same algebra");
fi;
if Length(mat) <> dim1 or Length(mat[1]) <> dim2 then
Error("matrix has wrong size for a homomorphism");
fi;
# Check if it is a homorphism
mat:=ImmutableMatrix(F,mat);
for i in [1..ng] do
for j in [1..dim1] do
if gens1[i][j] * mat <> mat[j] * gens2[i] then
Print(i,j,"\n");
Error("matrix does not define a homomorphism");
fi;
od;
od;
return LeftModuleHomomorphismByImages(FullRowSpace(F,dim1),
FullRowSpace(F,dim2),IdentityMat(dim1,F),mat);
end;
SMTX.Homomorphism:=SMTX_Homomorphism;
#############################################################################
##
#F SMTX.MinimalSubGModules(m1, m2, [max]) . .
## minimal submodules of m2 isomorphic to m1
##
## It is assumed that m1 is a module that has been proved irreducible
## (using IsIrreducible), and m2 is an arbitrary module for the same group.
## MinimalSubGModules computes and outputs a list of normed bases for all of the
## distinct minimal submodules of m2 that are isomorphic to m1.
## max is an optional maximal number - if the total number of submodules
## exceeds max, then the procedure aborts.
## First HomGModule is called and then SMTX.SortHomGModule to get a basis for
## the homomorphisms from m1 to m2 in the correct order.
## It is then easy to write down the list of distinct images.
##
SMTX_MinimalSubGModules:=function(arg)
local m1, m2, max, e, homs, coeff, dimhom, edimhom, F, elF, q,
submodules, sub, adno, more, count, sr, er, i, j, k;
if Length(arg) < 2 or Length(arg) > 3 then
Error("Number of arguments to MinimalSubGModules must be 2 or 3.");
fi;
m1:=arg[1]; m2:=arg[2];
if Length(arg) = 2 then max:=0; else max:=arg[3]; fi;
Info(InfoMeatAxe,2,"Calculating homomorphisms from m1 to m2.");
homs:=SMTX.Homomorphisms(m1, m2);
Info(InfoMeatAxe,2,"Sorting them.");
SMTX.SortHomGModule(m1, m2, homs);
F:=SMTX.Field(m1);
e:=SMTX.DegreeFieldExt(m1);
dimhom:=Length(homs);
edimhom:=dimhom / e;
submodules:=[];
count:=0;
elF:=AsList(F);
q:=Length(elF);
coeff:=ListWithIdenticalEntries(dimhom,1);
# coeff[i] will be an integer in the range [1..q] corresponding to the
# field element elF[coeff[i]].
# Each submodule will be calculated as the image of the homomorphism
# elF[coeff[1]] * homs[1] +...+ elF[coeff[dimhom]] * homs[dimhom]
# for appropriate field elements elF[coeff[i]].
# We get each distinct submodule
# exactly once by making the first nonzero elF[coeff[i]] to be 1,
# and all other elF[coeff[i]]'s in that block equal to zero.
Info(InfoMeatAxe,2,"Done. Calculating submodules.");
for i in Reversed([1..edimhom]) do
j:=e * (i - 1) + 1;
coeff[j]:=2; # giving field element 1.
for k in [j + 1..dimhom] do coeff[k]:=1; od; # field element 0.
sr:=j + e; er:=dimhom;
# coeff[i] for i in [sr..er] ranges over all field elements.
more:=true;
adno:=er;
while more do
count:=count + 1;
if max > 0 and count > max then
Info(InfoMeatAxe,2,"Number of submodules exceeds ", max,
". Aborting.");
return submodules;
fi;
# Calculate the next submodule
sub:=homs[j];
for k in [sr..er] do
sub:=sub + elF[coeff[k]] * homs[k];
od;
sub:=TriangulizedMat(sub);
Add(submodules, ImmutableMatrix(F,sub));
# Move on to next set of coefficients if any
while adno >= sr and coeff[adno]=q do
coeff[adno]:=1;
adno:=adno - 1;
od;
if adno < sr then
more:=false;
else
coeff[adno]:=coeff[adno] + 1;
adno:=er;
fi;
od;
od;
return submodules;
end;
SMTX.MinimalSubGModules:=SMTX_MinimalSubGModules;
SMTX_BasesCompositionSeries:=function(m)
local q,b,s,ser,queue,F,one,mats,mo;
mats:=m.generators;
SMTX.SetSmashRecord(m,0);
F:=SMTX.Field(m);
one:=One(F);
b:=IdentityMat(SMTX.Dimension(m),F);
b:=ImmutableMatrix(F,b);
# denombasis: basis of the kernel
m.smashMeataxe.denombasis:=[];
# fakbasis: Urbilder der Basis, bzgl. derer csbasis angegeben wird
# the first <dimension> vectors of <fakbasis> are the right ones.
m.smashMeataxe.fakbasis:=b;
ser:=[[]];
queue:=[m];
while Length(queue)>0 do
m:=Remove(queue);
if SMTX.IsIrreducible(m) then
mo:=m;
Info(InfoMeatAxe,3,SMTX.Dimension(m)," ",
Length(m.smashMeataxe.denombasis));
m:=Concatenation(
m.smashMeataxe.denombasis,
m.smashMeataxe.fakbasis{[1..SMTX.Dimension(m)]});
m:=List(m,ShallowCopy);
TriangulizeMat(m);
m:=ImmutableMatrix(F,m);
Add(ser,m);
else
b:=SMTX.Subbasis(m);
s:=SMTX.InducedAction(m,b,3);
q:=s[2];
b:=s[3];
s:=s[1];
SMTX.SetSmashRecord(s,0);
SMTX.SetSmashRecord(q,0);
Info(InfoMeatAxe,1,"chopped ",SMTX.Dimension(s),"\\", SMTX.Dimension(q));
s.smashMeataxe.denombasis:=m.smashMeataxe.denombasis;
s.smashMeataxe.fakbasis:=b*m.smashMeataxe.fakbasis;
q.smashMeataxe.denombasis:=Concatenation(
m.smashMeataxe.denombasis,
s.smashMeataxe.fakbasis{[1..s.dimension]});
q.smashMeataxe.fakbasis:=
s.smashMeataxe.fakbasis{[s.dimension+1..Length(b)]};
Add(queue,s);
Add(queue,q);
fi;
od;
SortBy(ser,Length);
return ser;
end;
SMTX.BasesCompositionSeries:=SMTX_BasesCompositionSeries;
# composition series with small steps
SMTX_BasesCSSmallDimUp:=function(m)
local cf,F,dim,b,den,sub,i,s,q,found,qb;
cf:=List(SMTX.CollectedFactors(m),x->[x[1],x[2]]); # so we can overwrite
SortBy(cf,x->x[1].dimension);
dim:=SMTX.Dimension(m);
F:=SMTX.Field(m);
b:=IdentityMat(dim,F);
b:=ImmutableMatrix(F,b);
den:=0;
sub:=[[]];
q:=m;
while den<dim do
i:=1;
found:=false;
while i<=Length(cf) and found=false do
if cf[i][2]>0 then # can we still get this module?
s:=MTX.Homomorphisms(cf[i][1],q);
if Length(s)>0 then
# found one
cf[i][2]:=cf[i][2]-1;
found:=true;
s:=s[1];
if Length(s)=q.dimension then
# on top
Add(sub,TriangulizedMat(b));
den:=dim;
else
s:=TriangulizedMat(s);
qb:=b{[den+1..Length(b)]}; # basis
qb:=List(s,x->x*qb);
qb:=Concatenation(b{[1..den]},qb);
qb:=TriangulizedMat(qb);
Add(sub,qb);
s:=SMTX.InducedAction(q,s,3);
b:=Concatenation(b{[1..den]},s[3]*b{[den+1..Length(b)]});
den:=den+Length(qb);
q:=s[2];
den:=Length(qb);
fi;
fi;
fi;
i:=i+1;
od;
od;
return sub;
end;
SMTX.BasesCSSmallDimUp:=SMTX_BasesCSSmallDimUp;
SMTX_BasesCSSmallDimDown:=function(m)
local d,sub;
d:=MTX.DualModule(m);
sub:=SMTX_BasesCSSmallDimUp(d);
return Concatenation([[]],
Reversed(List(sub{[2..Length(sub)-1]},x->SMTX.DualizedBasis(m,x))),
[IdentityMat(m.dimension,m.field)]);
end;
SMTX.BasesCSSmallDimDown:=SMTX_BasesCSSmallDimDown;
SMTX_BasesSubmodules:=function(m)
local cf,u,i,j,f,cl,min,neu,sq,sb,fb,k,nmin,F;
F:=SMTX.Field(m);
cf:=SMTX.CollectedFactors(m);
cl:=Sum(cf,i->i[2]); # composition length
cf:=List(cf,i->i[1]);
u:=[[]];
if cl>1 then
min:=Concatenation(List(cf,i->SMTX.MinimalSubGModules(i,m)));
u:=Concatenation(u,min);
fi;
for i in [2..cl-1] do
neu:=[];
for j in min do
f:=List(j,i->List(i,i->i));
sq:=SMTX.InducedAction(m,j,2);
Assert(2,j=f);
f:=sq[1];
sb:=j;
fb:=sq[2]{[Length(j)+1..Length(sq[2])]};
# actually we might want to count frequencies to speed up the process,
# so far I'm lazy
nmin:=Concatenation(List(cf,i->SMTX.MinimalSubGModules(i,f)));
Info(InfoMeatAxe,3,Length(nmin),"minimal submodules");
for k in nmin do
sq:=Concatenation(List(sb,ShallowCopy), # don't destroy old basis
k*fb);
TriangulizeMat(sq);
sq:=ImmutableMatrix(F,sq);
Assert(2,SMTX.InducedAction(m,sq)<>fail);
if not sq in neu then
Info(InfoMeatAxe,2,"submodule dimension ",Length(sq));
Add(neu,sq);
fi;
od;
od;
u:=Concatenation(u,neu);
min:=neu;
od;
Add(u,ImmutableMatrix(SMTX.Field(m),
IdentityMat(SMTX.Dimension(m),SMTX.Field(m))));
return u;
end;
SMTX.BasesSubmodules:=SMTX_BasesSubmodules;
SMTX_BasesMinimalSubmodules:=function(m)
local cf;
cf:=SMTX.CollectedFactors(m);
cf:=List(cf,i->i[1]);
return Concatenation(List(cf,i->SMTX.MinimalSubGModules(i,m)));
end;
SMTX.BasesMinimalSubmodules:=SMTX_BasesMinimalSubmodules;
SMTX.DualModule:=function(module)
if SMTX.IsZeroGens(module) then
return GModuleByMats([],module.dimension,SMTX.Field(module));
else
return GModuleByMats(List(SMTX.Generators(module),i->TransposedMat(i)^-1),
module.dimension,
SMTX.Field(module));
fi;
end;
###############################################################################
##
#F DualGModule( module ) . . . . . dual of a G-module
##
## DualGModule calculates the dual of a G-module.
## The matrices of the module are inverted and transposed.
##
InstallGlobalFunction(DualGModule,function( module)
return SMTX.DualModule(module);
end);
SMTX.DualizedBasis:=function(module,sub)
local F,M;
F:=DefaultFieldOfMatrix(sub);
M:=TransposedMatMutable(sub);
M:=TriangulizedNullspaceMatDestructive(M);
M:=ImmutableMatrix(F,M);
return M;
end;
SMTX_BasesMaximalSubmodules:=function(m)
local d,u;
d:=SMTX.DualModule(m);
u:=SMTX.BasesMinimalSubmodules(d);
return List(u,i->SMTX.DualizedBasis(d,i));
end;
SMTX.BasesMaximalSubmodules:=SMTX_BasesMaximalSubmodules;
SMTX_BasesMinimalSupermodules:=function(m,sub)
local a,u,i,nb;
a:=SMTX.InducedAction(m,sub,2);
u:=SMTX.BasesMinimalSubmodules(a[1]);
nb:=a[2];
nb:=nb{[Length(sub)+1..Length(nb)]}; # the new basis part
nb:=List(u,i->Concatenation( List( sub, ShallowCopy ),
i*nb));
u:=[];
for i in nb do
TriangulizeMat(i);
Add(u,Filtered(i,j->j<>Zero(j)));
od;
return u;
end;
SMTX.BasesMinimalSupermodules:=SMTX_BasesMinimalSupermodules;
SMTX_BasisRadical:=function(module)
local m,i,r;
m:=SMTX.BasesMaximalSubmodules(module);
r:=m[1];
for i in [2..Length(m)] do
r:=SumIntersectionMat(r,m[i])[2];
od;
return r;
end;
SMTX.BasisRadical:=SMTX_BasisRadical;
#############################################################################
##
#F SMTX.SpanOfMinimalSubGModules(m1, m2) . .
## span of the minimal submodules of m2 isomorphic to m1
##
## It is assumed that m1 is a module that has been proved irreducible
## (using IsIrreducible), and m2 is an arbitrary module for the same group.
## SpanOfMinimalSubGModules computes a normed bases for the span of
## the minimal submodules of m2 that are isomorphic to m1,
## First HomGModule is called.
##
SMTX_SpanOfMinimalSubGModules:=function(m1, m2)
local homs, e, mat, i;
Info(InfoMeatAxe,2,"Calculating homomorphisms from m1 to m2.");
homs:=SMTX.Homomorphisms(m1, m2);
if homs=[] then
return [];
fi;
Info(InfoMeatAxe,2,"Sorting them.");
SMTX.SortHomGModule(m1, m2, homs);
e:=SMTX.DegreeFieldExt(m1);
# homs are now grouped so that each block of e have the same image.
# We only want one from each block.
if e > 1 then
homs:=homs{Filtered([1..Length(homs)],i->(i mod e) = 1)};
fi;
if Length(homs) = 1 then
return homs[1];
fi;
# The span of the images of homs is what we want!
mat:=Concatenation(homs);
TriangulizeMat(mat);
mat:=ImmutableMatrix(m1.field,mat);
return mat;
end;
SMTX.SpanOfMinimalSubGModules:=SMTX_SpanOfMinimalSubGModules;
SMTX_BasisSocle:=function(module)
local cf, mat, i;
cf:=SMTX.CollectedFactors(module);
mat:=Concatenation(List(cf,i->SMTX_SpanOfMinimalSubGModules(i[1],module)));
if Length(cf) = 1 then
return mat;
fi;
TriangulizeMat(mat);
mat:=ImmutableMatrix(module.field,mat);
return mat;
end;
SMTX.BasisSocle:=SMTX_BasisSocle;
SMTX_BasisRadical:=function(module)
local d, bs;
d:=SMTX.DualModule(module);
bs:=SMTX.BasisSocle(d);
return SMTX.DualizedBasis(d,bs);
end;
SMTX.BasisRadical:=SMTX_BasisRadical;
# the following assignement is for profiling
SMTX.funcs:=[SMTX_OrthogonalVector,SMTX_SpinnedBasis,SMTX_SubQuotActions,
SMTX_SMCoRaEl,SMTX_IrreducibilityTest,SMTX_RandomIrreducibleSubGModule,
SMTX_GoodElementGModule,SMTX_FrobeniusAction,SMTX_CompleteBasis,
SMTX_AbsoluteIrreducibilityTest,SMTX_CollectedFactors,SMTX_Distinguish,
SMTX_MinimalSubGModule,SMTX_IsomorphismComp,SMTX_MatrixSum,
SMTX_Homomorphisms,SMTX_SortHomGModule,SMTX_MinimalSubGModules,
SMTX_BasesCompositionSeries,SMTX_BasesSubmodules,SMTX_BasesMinimalSubmodules,
SMTX_BasesMaximalSubmodules,SMTX_BasesMinimalSupermodules,SMTX_BasisSocle,
SMTX_BasisRadical];
# The following functions are for finding a basis of an irreducible module
# that is contained in an orbit of the G-action on vectors, and for
# looking for G-invariant bilinear and quadratic forms of the module.
# The special basis is used for finding invariant quadratic forms when
# the characteristic of the field is 2.
SMTX.SetBasisInOrbit:=function(module,b)
module.BasisInOrbit:=b;
end;
#############################################################################
##
#F BasisInOrbit( module ) . . . .
##
## Find a basis of the irrecucible GModule module that is contained in
## an orbit of the action of G.
## The code is similar to that of SpinnedBasis.
SMTX_BasisInOrbit:=function( module )
local v, matrices, ngens, zero, ans, normedans,
dim, subdim, leadpos, w, normedw, i, j, k, l, m, F;
if not SMTX.IsMTXModule(module) or not SMTX.IsIrreducible(module) then
Error("Argument of BasisInOrbit is not an irreducible module");
fi;
if IsBound(module.BasisInOrbit) then return module.BasisInOrbit; fi;
dim:=SMTX.Dimension(module);
F:=SMTX.Field(module);
matrices:=module.generators;
ngens:=Length(matrices);
zero:=Zero(F);
v:=IdentityMat(dim,F)[1];
ans:=[v];
normedans:=[v];
subdim:=1;
leadpos:=SubGModLeadPos(ans,dim,subdim,zero);
i:=1;
while i <= subdim and subdim < dim do
for l in [1..ngens] do
m:=matrices[l];
# apply generator m to submodule generator i
w:=ans[i] * m;
normedw:=w;
# try to express w in terms of existing submodule generators
j:=1;
for j in [1..subdim] do
k:=normedw[leadpos[j]];
if k <> zero then
normedw:=normedw - k * normedans[j];
fi;
od;
j:=1;
while j <= dim and normedw[j] = zero do
j:=j + 1;
od;
if j <= dim then
# we have found a new generator of the submodule
subdim:=subdim + 1;
leadpos[subdim]:=j;
normedw:=(normedw[j]^-1) * normedw;
Add( ans, w );
Add( normedans, normedw );
if subdim = dim then
break;
fi;
fi;
od;
i:=i + 1;
od;
Assert(0, subdim = dim);
ans:=ImmutableMatrix(F,ans);
SMTX.SetBasisInOrbit(module,ans);
return ans;
end;
SMTX.BasisInOrbit:=SMTX_BasisInOrbit;
SMTX.SetInvariantBilinearForm:=function(module,b)
module.InvariantBilinearForm:=b;
end;
#############################################################################
##
#F InvariantBilinearForm( module ) . . . .
##
## Look for an invariant bilinear form of the absolutely irreducible
## GModule module. Return fail, or the matrix of the form.
SMTX_InvariantBilinearForm:=function( module )
local DM, iso;
if not SMTX.IsMTXModule(module) or
not SMTX.IsAbsolutelyIrreducible(module) then
Error(
"Argument of InvariantBilinearForm is not an absolutely irreducible module");
fi;
if IsBound(module.InvariantBilinearForm) then
return module.InvariantBilinearForm;
fi;
DM:=SMTX.DualModule(module);
iso:=MTX.IsomorphismIrred(module,DM);
if iso = fail then
SMTX.SetInvariantBilinearForm(module, fail);
return fail;
fi;
iso:=ImmutableMatrix(module.field, iso);
SMTX.SetInvariantBilinearForm(module, iso);
return iso;
end;
SMTX.InvariantBilinearForm:=SMTX_InvariantBilinearForm;
SMTX.MatrixUnderFieldAuto:=function(matrix, r)
# raise every component of matrix to r-th power
local mat;
mat:=List( matrix, x -> List(x, y->y^r) );
mat:=ImmutableMatrix(GF(r^2), mat);
return mat;
end;
SMTX.TwistedDualModule:=function(module)
local q, r, mats;
q:=Size(module.field);
r:=RootInt(q,2);
if r^2 <> q then
Error("Size of field of module is not a square");
fi;
if SMTX.IsZeroGens(module) then
return GModuleByMats([],module.dimension,SMTX.Field(module));
else
mats:=List( SMTX.Generators(module),
i->SMTX.MatrixUnderFieldAuto(TransposedMat(i)^-1,r) );
return GModuleByMats( mats, module.dimension, SMTX.Field(module) );
fi;
end;
SMTX.SetInvariantSesquilinearForm:=function(module,b)
module.InvariantSesquilinearForm:=b;
end;
#############################################################################
##
#F InvariantSesquilinearForm( module ) . . . .
##
## Look for an invariant sesquililinear form of the absolutely irreducible
## GModule module. Return fail, or the matrix of the form.
SMTX_InvariantSesquilinearForm:=function( module )
local DM, q, r, iso, isot, l;
if not SMTX.IsMTXModule(module) or
not SMTX.IsAbsolutelyIrreducible(module) then
Error(
"Argument of InvariantSesquilinearForm is not an absolutely irreducible module"
);
fi;
if IsBound(module.InvariantSesquilinearForm) then
return module.InvariantSesquilinearForm;
fi;
DM:=SMTX.TwistedDualModule(module);
iso:=MTX.IsomorphismIrred(module,DM);
if iso = fail then
SMTX.SetInvariantSesquilinearForm(module, fail);
return fail;
fi;
# Replace iso by a scalar multiple to get iso twisted symmetric
q:=Size(module.field);
r:=RootInt(q,2);
isot:=List( TransposedMat(iso), x -> List(x, y->y^r) );
isot:=iso * isot^-1;
if not IsDiagonalMat(isot) then
Error("Form does not seem to be of the right kind (non-diagonal)!");
fi;
l:=LogFFE(isot[1][1],Z(q));
if l mod (r-1) <> 0 then
Error("Form does not seem to be of the right kind (not (q-1)st root)!");
fi;
iso:=Z(q)^(l/(r-1)) * iso;
iso:=ImmutableMatrix(GF(q), iso);
SMTX.SetInvariantSesquilinearForm(module, iso);
return iso;
end;
SMTX.InvariantSesquilinearForm:=SMTX_InvariantSesquilinearForm;
SMTX.SetInvariantQuadraticForm:=function(module,b)
module.InvariantQuadraticForm:=b;
end;
#############################################################################
##
#F InvariantQuadraticForm( module ) . . . .
##
## Look for an invariant quadratic form of the absolutely irreducible
## GModule module. Return fail, or the matrix of the form.
SMTX_InvariantQuadraticForm:=function( module )
local iso, bas, cgens, ciso, dim, f, z, x, i, j, qf, g, id, cqf, fix;
if not SMTX.IsMTXModule(module) or
not SMTX.IsAbsolutelyIrreducible(module) then
Error(
"Argument of InvariantQuadraticForm is not an absolutely irreducible module");
fi;
if IsBound(module.InvariantQuadraticForm) then
return module.InvariantQuadraticForm;
fi;
iso:=SMTX.InvariantBilinearForm(module);
if iso = fail then return fail; fi;
if Characteristic(module.field) <> 2 then return iso/2; fi;
# In characteristic two, we change to a basis in orbit.
# This makes the search for an invariant quadratic form quicker.
bas:=SMTX.BasisInOrbit(module);
cgens:=List(module.generators, x->bas*x*bas^-1 );
ciso:=List(bas * iso * TransposedMat(bas),ShallowCopy);
dim:=module.dimension;
f:=module.field;
z:=Zero(f);
# Matrix must be symplectic - perhaps it must be?
for i in [1..dim] do if ciso[i][i] <> z then
Print("Non-symplectic failure!\n");
return fail;
fi; od;
# If there is an invariant quadratic form, then it will be the lower
# left hand part of ciso plus a scalar.
for i in [1..dim-1] do for j in [i+1..dim] do ciso[i][j]:=z; od; od;
id:=IdentityMat(dim, f);
for x in f do
qf:=ciso + x*id;
fix:=true;
# Form is preserved if and only if diagonal is.
for g in cgens do
cqf:=g * qf * TransposedMat(g);
for j in [1..dim] do if cqf[j][j] <> x then
fix:=false;
break;
fi; od;
if not fix then break; fi;
od;
if fix then
qf:=bas^-1 * qf * TransposedMat(bas^-1);
# switch to lower triangular equivalent
for i in [1..dim-1] do for j in [i+1..dim] do
qf[j][i]:=qf[i][j] + qf[j][i];
qf[i][j]:=z;
od; od;
qf:=ImmutableMatrix(f,qf);
SMTX.SetInvariantQuadraticForm(module, qf);
return qf;
fi;
od;
SMTX.SetInvariantQuadraticForm(module, fail);
return fail;
end;
SMTX.InvariantQuadraticForm:=SMTX_InvariantQuadraticForm;
#############################################################################
##
#F OrthogonalSign( module ) . . . .
##
## When an absolutely irreducible G-module has an invariant quadratic
## form, this implies that it embeds in a General Orthogonal group. In
## even dimension there are two non-isomorphic General Orthogonal groups
## "plus" and "minus" type `GeneralOrthogonalGroup(+1,<n>,<q>)' and
## `GeneralOrthogobalGroup(-1,<n>,<q>)' in GAP terms. This function
## decides which one the module embeds into.
##
## It returns:
## fail if the module is not absolutely irreducible, or
## does not stabilize a quadratic form.
## 0 otherwise, if the dimension of the module is odd
## +1 or -1 otherwise, according to which GO the module embeds in
##
## This is an implementation of an algorithm by Jon Thackray
SMTX.SetOrthogonalSign:=function(module,s)
module.OrthogonalSign:=s;
end;
SMTX_OrthogonalSign:=function(gm)
local b, q, k, n, W, o, z, lo, lzo, lines, l, w, p,
x, y, r, i;
if IsBound(gm.OrthogonalSign) then
return gm.OrthogonalSign;
fi;
b:=MTX.InvariantBilinearForm(gm);
q:=MTX.InvariantQuadraticForm(gm);
if q = fail then
return fail;
fi;
n:=Length(b);
if n mod 2 = 1 then
return 0;
fi;
k:=MTX.Field(gm);
W:=IdentityMat(n,k);
#
# Assemble the points of projective 3-space
#
o:=One(k);
z:=Zero(k);
lo:=[o];
lzo:=[z,o];
lines:=List(AsSSortedList(FullRowSpace(k,2)),x -> Concatenation(lo,x));
Append(lines,List(AsSSortedList(k), x-> Concatenation(lzo,[x])));
Add(lines,[z,z,o]);
#
# Main loop of Thackray's algorithm, build up a totally isotropic
# subspace and restrict it's perp until the gap between is just 2 dimensional
#
while n > 2 do
#
# Find an isotropic vector
#
for l in lines do
w:=l*W;
if w*q*w = z then
break;
fi;
od;
Assert(1,w*b*w = z);
p:=PositionNonZero(l);
#
# delete it from W (add it to the subspace)
#
W{[p..n-1]}:=W{[p+1..n]};
Unbind(W[n]);
n:=n-1;
#
# find a vector with which it has non-zero inner product
#
x:=w*b;
p:=PositionProperty(W, row -> x*row <> z);
Assert(1, p <> fail);
#
# use it to find the perp of the enlarged subspace
#
y:=W[p];
r:=x*y;
for i in [p+1..n] do
AddRowVector(W[i], y, - x*W[i]/r);
W[i-1]:=W[i];
od;
Unbind(W[n]);
n:=n-1;
#
# Now n has gone down by 2 and W is still the "gap" between the
# subspace and its perp
#
od;
#
# Now we need to see if the span of W contains an isotropic vector
#
if W[2]*q*W[2] = z then
SMTX.SetOrthogonalSign(gm,1);
return 1;
else
for x in k do
w:=W[1]+x*W[2];
if w*q*w = z then
SMTX.SetOrthogonalSign(gm,1);
return 1;
fi;
od;
SMTX.SetOrthogonalSign(gm,-1);
return -1;
fi;
end;
SMTX.OrthogonalSign:=SMTX_OrthogonalSign;
if IsHPCGAP then
MakeReadOnlyObj(SMTX);
fi;