|
#############################################################################
##
## This file is part of GAP, a system for computational discrete algebra.
## This file's authors include Frank Celler, Bettina Eick.
##
## Copyright of GAP belongs to its developers, whose names are too numerous
## to list here. Please refer to the COPYRIGHT file for details.
##
## SPDX-License-Identifier: GPL-2.0-or-later
##
## This file contains the methods for groups with a polycyclic collector.
##
#############################################################################
##
#M CanonicalPcgsWrtFamilyPcgs( <grp> )
##
InstallMethod( CanonicalPcgsWrtFamilyPcgs,
true,
[ IsGroup and HasFamilyPcgs ],
0,
function( grp )
local cgs;
cgs := CanonicalPcgs( InducedPcgsWrtFamilyPcgs(grp) );
if cgs = FamilyPcgs(grp) then
SetIsWholeFamily( grp, true );
fi;
return cgs;
end );
#############################################################################
##
#M CanonicalPcgsWrtHomePcgs( <grp> )
##
InstallMethod( CanonicalPcgsWrtHomePcgs,
true,
[ IsGroup and HasHomePcgs ],
0,
function( grp )
return CanonicalPcgs( InducedPcgsWrtHomePcgs(grp) );
end );
#############################################################################
##
#M InducedPcgsWrtFamilyPcgs( <grp> )
##
InstallMethod( InducedPcgsWrtFamilyPcgs,
true,
[ IsGroup and HasFamilyPcgs ],
0,
function( grp )
local pa, igs;
pa := FamilyPcgs(grp);
if HasPcgs(grp) and IsInducedPcgs(Pcgs(grp)) then
if pa = ParentPcgs(Pcgs(grp)) then
return Pcgs(grp);
fi;
fi;
igs := InducedPcgsByGenerators( pa, GeneratorsOfGroup(grp) );
if igs = pa then
SetIsWholeFamily( grp, true );
fi;
SetGroupOfPcgs (igs, grp);
return igs;
end );
InstallMethod( InducedPcgsWrtFamilyPcgs,"whole family", true,
[ IsPcGroup and IsWholeFamily], 0,
FamilyPcgs);
#############################################################################
##
#M InducedPcgsWrtHomePcgs( <G> )
##
InstallMethod( InducedPcgsWrtHomePcgs,"from generators", true, [ IsGroup ], 0,
function( G )
local home, ind;
home := HomePcgs( G );
if HasPcgs(G) and IsInducedPcgs(Pcgs(G)) then
if IsIdenticalObj(home,ParentPcgs(Pcgs(G))) then
return Pcgs(G);
fi;
fi;
ind := InducedPcgsByGenerators( home, GeneratorsOfGroup( G ) );
SetGroupOfPcgs (ind, G);
return ind;
end );
InstallMethod( InducedPcgsWrtHomePcgs,"pc group: home=family", true,
[ IsPcGroup ], 0,
InducedPcgsWrtFamilyPcgs);
#############################################################################
##
#M InducedPcgs( <pcgs>,<G> )
##
InstallGlobalFunction( InducedPcgs, function(pcgs, G)
local cache, i, igs;
if not IsPcgs(pcgs) then
Error("InducedPcgs: <pcgs> must be a pcgs");
fi;
if not IsGroup(G) then
Error("InducedPcgs: <G> must be a group");
fi;
pcgs := ParentPcgs (pcgs);
cache := ComputedInducedPcgses(G);
i := 1;
while i <= Length (cache) do
if cache[i]= pcgs then
return cache[i+1];
fi;
i := i + 2;
od;
igs := InducedPcgsOp( pcgs, G );
SetGroupOfPcgs (igs, G);
Append (cache, [pcgs, igs]);
if not HasPcgs(G) then
SetPcgs (G, igs);
fi;
# set home pcgs stuff
if not HasHomePcgs(G) then
SetHomePcgs (G, pcgs);
fi;
if IsIdenticalObj (HomePcgs(G), pcgs) then
SetInducedPcgsWrtHomePcgs (G, igs);
fi;
return igs;
end );
#############################################################################
##
#M InducedPcgsOp
##
InstallMethod (InducedPcgsOp, "generic method",
IsIdenticalObj, [IsPcgs, IsGroup],
function (pcgs, G)
return InducedPcgsByGenerators(
ParentPcgs(pcgs), GeneratorsOfGroup( G ) );
end);
#############################################################################
##
#M InducedPcgsOp
##
InstallMethod (InducedPcgsOp, "sift existing pcgs",
IsIdenticalObj, [IsPcgs, IsGroup and HasPcgs],
function (pcgs, G)
local seq, # pc sequence wrt pcgs (and its parent)
depths, # depths of this sequence
len, # length of the sequence
pos, # index
x, # a group element
d; # depth of x
pcgs := ParentPcgs (pcgs);
seq := [];
depths := [];
len := 0;
for x in Reversed (Pcgs (G)) do
# sift x through seq
d := DepthOfPcElement (pcgs, x);
pos := PositionSorted (depths, d);
while pos <= len and depths[pos] = d do
x := ReducedPcElement (pcgs, x, seq[pos]);
d := DepthOfPcElement (pcgs, x);
pos := PositionSorted (depths, d);
od;
if d> Length(pcgs) then
Error ("Panic: Pcgs (G) does not seem to be a pcgs");
else
seq{[pos+1..len+1]} := seq{[pos..len]};
depths{[pos+1..len+1]} := depths{[pos..len]};
seq[pos] := x;
depths[pos] := d;
len := len + 1;
fi;
od;
return InducedPcgsByPcSequenceNC (pcgs, seq, depths);
end);
#############################################################################
##
#M ComputedInducedPcgses
##
InstallMethod (ComputedInducedPcgses, "default method", [IsGroup],
G -> []);
#############################################################################
##
#F SetInducedPcgs( <home>,<G>,<pcgs> )
##
InstallGlobalFunction(SetInducedPcgs,function(home,G,pcgs)
home := ParentPcgs(home);
if not HasHomePcgs(G) then
SetHomePcgs(G,home);
fi;
if IsIdenticalObj(ParentPcgs(pcgs),home) then
Append (ComputedInducedPcgses(G), [home, pcgs]);
if IsIdenticalObj(HomePcgs(G),home) then
SetInducedPcgsWrtHomePcgs(G,pcgs);
fi;
fi;
SetGroupOfPcgs (pcgs, G);
end);
#############################################################################
##
#M Pcgs( <G> )
##
InstallMethod( Pcgs, "fail if not solvable", true,
[ IsGroup and HasIsSolvableGroup ],
SUM_FLAGS, # for groups for which we know that they are not solvable
# this is the best we can do.
function( G )
if not IsSolvableGroup( G ) then return fail;
else TryNextMethod(); fi;
end );
#############################################################################
##
#M Pcgs( <pcgrp> )
##
InstallMethod( Pcgs,
"for a group with known family pcgs",
true,
[ IsGroup and HasFamilyPcgs ],
0,
InducedPcgsWrtFamilyPcgs );
InstallMethod( Pcgs,
"for a group with known home pcgs",
true,
[ IsGroup and HasHomePcgs ],
1,
InducedPcgsWrtHomePcgs );
InstallMethod( Pcgs, "take induced pcgs", true,
[ IsGroup and HasInducedPcgsWrtHomePcgs ], SUM_FLAGS,
InducedPcgsWrtHomePcgs );
#############################################################################
##
#M Pcgs( <whole-family-grp> )
##
InstallMethod( Pcgs,
"for a group containing the whole family and with known family pcgs",
true,
[ IsGroup and HasFamilyPcgs and IsWholeFamily ],
0,
FamilyPcgs );
#############################################################################
##
#M GeneralizedPcgs( <G> )
##
# This used to be an immediate method. It was replaced by an ordinary
# method as the attribute is set when creating pc groups.
InstallMethod( GeneralizedPcgs,true,[ IsGroup and HasPcgs], 0, Pcgs );
#############################################################################
##
#M HomePcgs( <G> )
##
## BH: changed Pcgs to G -> ParentPcgs (Pcgs(G))
##
InstallMethod( HomePcgs, true, [ IsGroup ], 0, G -> ParentPcgs( Pcgs( G ) ) );
#############################################################################
##
#M PcgsChiefSeries( <pcgs> )
##
InstallMethod( PcgsChiefSeries,"compute chief series and pcgs",true,
[IsGroup],0,
function(G)
local p,cs,csi,l,i,pcs,ins,j,u;
p:=Pcgs(G);
if p=fail then
Error("<G> must be solvable");
fi;
if not HasParent(G) then
SetParentAttr(G,Parent(G));
fi;
cs:=ChiefSeries(G);
csi:=List(cs,i->InducedPcgs(p,i));
l:=Length(cs);
pcs:=[];
ins:=[0];
for i in [l-1,l-2..1] do
# extend the pc sequence. We have a vector space factor, so we can
# simply add *some* further generators.
u:=AsSubgroup(Parent(G),cs[i+1]);
for j in Reversed(Filtered(csi[i],k->not k in cs[i+1])) do
if not j in u then
Add(pcs,j);
#NC is safe
u:=ClosureSubgroupNC(u,j);
fi;
od;
if Length(pcs)<>Length(csi[i]) then
Error("pcgs length!");
fi;
Add(ins,Length(pcs));
od;
l:=Length(pcs)+1;
pcs:=PcgsByPcSequenceNC(FamilyObj(OneOfPcgs(p)),Reversed(pcs));
SetGroupOfPcgs (pcs, G);
# store the indices
SetIndicesChiefNormalSteps(pcs,Reversed(List(ins,i->l-i)));
return pcs;
end);
#############################################################################
##
#M GroupWithGenerators( <gens> ) . . . . . . . . . . . . group by generators
#M GroupWithGenerators( <gens>, <id> )
##
## These methods override the generic code. They are installed for
## `IsMultiplicativeElementWithInverseByPolycyclicCollectorCollection' and
## automatically set family pcgs and home pcgs.
##
InstallMethod( GroupWithGenerators,
"method for pc elements collection",
true, [ IsCollection and
IsMultiplicativeElementWithInverseByPolycyclicCollectorCollection and
IsFinite] ,
# override methods for `IsList' or `IsEmpty'.
10,
function( gens )
local G,fam,id,pcgs;
fam:=FamilyObj(gens);
pcgs:=DefiningPcgs(ElementsFamily(fam));
id:=One(gens[1]);
# pc groups are always finite and gens is finite.
G:=MakeGroupyObj(fam, IsSolvableGroup and IsFinite,
AsList(gens),id,
FamilyPcgs,pcgs,HomePcgs,pcgs,GeneralizedPcgs,pcgs);
return G;
end );
InstallOtherMethod( GroupWithGenerators,
"method for pc collection and identity element",
IsCollsElms, [ IsCollection and
IsMultiplicativeElementWithInverseByPolycyclicCollectorCollection
and IsFinite,
IsMultiplicativeElementWithInverseByPolycyclicCollector] ,
0,
function( gens, id )
local G,fam,pcgs;
fam:=FamilyObj(gens);
pcgs:=DefiningPcgs(ElementsFamily(fam));
# pc groups are always finite and gens is finite.
G:=MakeGroupyObj(fam, IsSolvableGroup and IsFinite,
AsList(gens),id,
FamilyPcgs,pcgs,HomePcgs,pcgs,GeneralizedPcgs,pcgs);
return G;
end );
InstallOtherMethod( GroupWithGenerators,
"method for empty pc collection and identity element",
true, [ IsList and IsEmpty,
IsMultiplicativeElementWithInverseByPolycyclicCollector] ,
# override methods for `IsList' or `IsEmpty'.
10,
function( empty, id )
local G,fam,pcgs;
fam:= CollectionsFamily( FamilyObj( id ) );
pcgs:=DefiningPcgs(ElementsFamily(fam));
# pc groups are always finite and gens is finite.
G:=MakeGroupyObj(fam, IsSolvableGroup and IsFinite,
empty,id,
FamilyPcgs,pcgs,HomePcgs,pcgs,GeneralizedPcgs,pcgs);
return G;
end );
#############################################################################
##
#M <elm> in <pcgrp>
##
InstallMethod( \in,
"for pc group",
IsElmsColls,
[ IsMultiplicativeElementWithInverse,
IsGroup and HasFamilyPcgs and CanEasilyComputePcgs
],
2, # rank this method higher than the following one
function( elm, grp )
return SiftedPcElement(InducedPcgsWrtFamilyPcgs(grp),elm) = One(grp);
end );
#############################################################################
##
#M <elm> in <pcgrp>
##
InstallMethod( \in,
"for pcgs computable groups with home pcgs",
IsElmsColls,
[ IsMultiplicativeElementWithInverse,
IsGroup and HasInducedPcgsWrtHomePcgs and CanEasilyComputePcgs
],
1, # rank this method higher than the following one
function( elm, grp )
local pcgs, ppcgs;
pcgs := InducedPcgsWrtHomePcgs (grp);
ppcgs := ParentPcgs (pcgs);
if Length (pcgs) = Length (ppcgs) or not CanEasilyTestMembership (GroupOfPcgs(ppcgs)) then
TryNextMethod();
fi;
if elm in GroupOfPcgs (ppcgs) then
return SiftedPcElement(InducedPcgsWrtHomePcgs(grp),elm) = One(grp);
else
return false;
fi;
end );
#############################################################################
##
#M <elm> in <pcgrp>
##
InstallMethod( \in,
"for pcgs computable groups with induced pcgs",
IsElmsColls,
[ IsMultiplicativeElementWithInverse,
IsGroup and HasComputedInducedPcgses and CanEasilyComputePcgs
],
0,
function( elm, grp )
local pcgs, ppcgs;
for pcgs in ComputedInducedPcgses(grp) do
ppcgs := ParentPcgs (pcgs);
if Length (pcgs) < Length (ppcgs) and CanEasilyTestMembership (GroupOfPcgs(ppcgs)) then
if elm in GroupOfPcgs (ppcgs) then
return SiftedPcElement(pcgs, elm) = One(grp);
else
return false;
fi;
fi;
od;
TryNextMethod();
end );
#############################################################################
##
#M <pcgrp1> = <pcgrp2>
##
InstallMethod( \=,
"pcgs computable groups using home pcgs",
IsIdenticalObj,
[ IsGroup and HasHomePcgs and HasCanonicalPcgsWrtHomePcgs,
IsGroup and HasHomePcgs and HasCanonicalPcgsWrtHomePcgs ],
0,
function( left, right )
if HomePcgs(left) <> HomePcgs(right) then
TryNextMethod();
fi;
return CanonicalPcgsWrtHomePcgs(left) = CanonicalPcgsWrtHomePcgs(right);
end );
#############################################################################
##
#M <pcgrp1> = <pcgrp2>
##
InstallMethod( \=,
"pcgs computable groups using family pcgs",
IsIdenticalObj,
[ IsGroup and HasFamilyPcgs and HasCanonicalPcgsWrtFamilyPcgs,
IsGroup and HasFamilyPcgs and HasCanonicalPcgsWrtFamilyPcgs ],
0,
function( left, right )
if FamilyPcgs(left) <> FamilyPcgs(right) then
TryNextMethod();
fi;
return CanonicalPcgsWrtFamilyPcgs(left)
= CanonicalPcgsWrtFamilyPcgs(right);
end );
#############################################################################
##
#M IsSubset( <pcgrp>, <pcsub> )
##
## This method is better than calling `\in' for all generators,
## since one has to fetch the pcgs only once.
##
InstallMethod( IsSubset,
"pcgs computable groups",
IsIdenticalObj,
[ IsGroup and HasFamilyPcgs and CanEasilyComputePcgs,
IsGroup ],
0,
function( grp, sub )
local pcgs, id, g;
pcgs := InducedPcgsWrtFamilyPcgs(grp);
id := One(grp);
for g in GeneratorsOfGroup(sub) do
if SiftedPcElement( pcgs, g ) <> id then
return false;
fi;
od;
return true;
end );
#############################################################################
##
#M SubgroupByPcgs( <G>, <pcgs> )
##
InstallMethod( SubgroupByPcgs, "subgroup with pcgs",
true, [IsGroup, IsPcgs], 0,
function( G, pcgs )
local U;
U := SubgroupNC( G, AsList( pcgs ) );
SetSize(U,Product(RelativeOrders(pcgs)));
SetPcgs( U, pcgs );
SetGroupOfPcgs (pcgs, U);
# home pcgs will be inherited
if HasHomePcgs(U) and IsIdenticalObj(HomePcgs(U),ParentPcgs(pcgs)) then
SetInducedPcgsWrtHomePcgs(U,pcgs);
fi;
if HasIsInducedPcgsWrtSpecialPcgs( pcgs ) and
IsInducedPcgsWrtSpecialPcgs( pcgs ) and
HasSpecialPcgs( G ) then
SetInducedPcgsWrtSpecialPcgs( U, pcgs );
fi;
return U;
end);
#############################################################################
##
#F VectorSpaceByPcgsOfElementaryAbelianGroup( <pcgs>, <f> )
##
InstallGlobalFunction( VectorSpaceByPcgsOfElementaryAbelianGroup,
function( arg )
local pcgs, dim, field;
pcgs := arg[1];
dim := Length( pcgs );
if IsBound( arg[2] ) then
field := arg[2];
elif dim > 0 then
field := GF( RelativeOrderOfPcElement( pcgs, pcgs[1] ) );
else
Error("trivial vectorspace, need field \n");
fi;
return VectorSpace( field, Immutable( IdentityMat( dim, field ) ) );
end );
#############################################################################
##
#F LinearActionLayer( <G>, <gens>, <pcgs> )
##
InstallGlobalFunction( LinearActionLayer, function( arg )
local gens, pcgs, field, m,mat,i,j;
# catch arguments
if Length( arg ) = 2 then
if IsGroup( arg[1] ) then
gens := GeneratorsOfGroup( arg[1] );
elif IsPcgs( arg[1] ) then
gens := AsList( arg[1] );
else
gens := arg[1];
fi;
pcgs := arg[2];
elif Length( arg ) = 3 then
gens := arg[2];
pcgs := arg[3];
fi;
# in case the layer is trivial
if Length( pcgs ) = 0 then
Error("pcgs is trivial - no field defined ");
fi;
# construct matrix rep
field := GF( RelativeOrderOfPcElement( pcgs, pcgs[1] ) );
# the following code takes too much time, as it has to create obvious pc
# elements again from vectors with 1 nonzero entry.
# V := Immutable( IdentityMat(Length(pcgs),field) );
# linear := function( x, g )
# return ExponentsOfPcElement( pcgs,
# PcElementByExponentsNC( pcgs, x )^g ) * One(field);
# end;
# return LinearAction( gens, V, linear );
#this is done much quicker by the following direct code:
m:=[];
for i in gens do
mat:=[];
for j in pcgs do
Add(mat,ExponentsConjugateLayer(pcgs,j,i)*One(field));
od;
mat:=ImmutableMatrix(field,mat,true);
Add(m,mat);
od;
return m;
end );
#############################################################################
##
#F AffineActionLayer( <G>, <pcgs>, <transl> )
##
InstallGlobalFunction( AffineActionLayer, function( arg )
local gens, pcgs, transl, V, field, linear;
# catch arguments
if Length( arg ) = 3 then
if IsPcgs( arg[1] ) then
gens := AsList( arg[1] );
elif IsGroup( arg[1] ) then
gens := GeneratorsOfGroup( arg[1] );
else
gens := arg[1];
fi;
pcgs := arg[2];
transl := arg[3];
elif Length( arg ) = 4 then
gens := arg[2];
pcgs := arg[3];
transl := arg[4];
fi;
# in the trivial case we cannot do anything
if Length( pcgs ) = 0 then
Error("layer is trivial . . . field is not defined \n");
fi;
# construct matrix rep
field := GF( RelativeOrderOfPcElement( pcgs, pcgs[1] ) );
V:= Immutable( IdentityMat(Length(pcgs),field) );
linear := function( x, g )
return ExponentsConjugateLayer(pcgs,
PcElementByExponentsNC( pcgs, x ),g ) * One(field);
end;
return AffineAction( gens, V, linear, transl );
end );
#############################################################################
##
#M AffineAction( <gens>, <V>, <linear>, <transl> )
##
InstallMethod( AffineAction,"generators",
true,
[ IsList,
IsMatrix,
IsFunction,
IsFunction ],
0,
function( Ggens, V, linear, transl )
local mats, gens, zero,one, g, mat, i, vec;
mats := [];
gens:=V;
zero:=Zero(V[1][1]);
one:=One(zero);
for g in Ggens do
mat := List( gens, x -> linear( x, g ) );
vec := ShallowCopy( transl(g) );
for i in [ 1 .. Length(mat) ] do
mat[i] := ShallowCopy( mat[i] );
Add( mat[i], zero );
od;
Add( vec, one );
Add( mat, vec );
mat:=ImmutableMatrix(Characteristic(one),mat,true);
Add( mats, mat );
od;
return mats;
end );
InstallOtherMethod( AffineAction,"group",
true,
[ IsGroup,
IsMatrix,
IsFunction,
IsFunction ],
0,
function( G, V, linear, transl )
return AffineAction( GeneratorsOfGroup(G), V, linear, transl );
end );
InstallOtherMethod( AffineAction,"group2",
true,
[ IsGroup,
IsList,
IsMatrix,
IsFunction,
IsFunction ],
0,
function( G, gens, V, linear, transl )
return AffineAction( gens, V, linear, transl );
end );
InstallOtherMethod( AffineAction,"pcgs",
true,
[ IsPcgs,
IsMatrix,
IsFunction,
IsFunction ],
0,
function( pcgsG, V, linear, transl )
return AffineAction( AsList( pcgsG ), V, linear, transl );
end );
#############################################################################
##
#M ClosureGroup( <U>, <H> )
##
## use home pcgs
##
InstallMethod( ClosureGroup,
"groups with home pcgs",
IsIdenticalObj,
[ IsGroup and HasHomePcgs,
IsGroup and HasHomePcgs ],
0,
function( U, H )
local home, pcgsU, pcgsH, new, N;
home := HomePcgs( U );
if home <> HomePcgs( H ) then
TryNextMethod();
fi;
pcgsU := InducedPcgs(home,U);
pcgsH := InducedPcgs(home,H);
if Length( pcgsU ) < Length( pcgsH ) then
new := InducedPcgsByPcSequenceAndGenerators( home, pcgsH,
GeneratorsOfGroup( U ) );
else
new := InducedPcgsByPcSequenceAndGenerators( home, pcgsU,
GeneratorsOfGroup( H ) );
fi;
N := SubgroupByPcgs( GroupOfPcgs( home ), new );
# SetHomePcgs( N, home );
# SetInducedPcgsWrtHomePcgs( N, new );
return N;
end );
#############################################################################
##
#M ClosureGroup( <U>, <g> )
##
## use home pcgs
##
InstallMethod( ClosureGroup,
"groups with home pcgs",
IsCollsElms,
[ IsGroup and HasHomePcgs,
IsMultiplicativeElementWithInverse ],
0,
function( U, g )
local home, pcgsU, new, N;
home := HomePcgs( U );
pcgsU := InducedPcgsWrtHomePcgs( U );
if not g in GroupOfPcgs( home ) then
TryNextMethod();
fi;
if g in U then
return U;
else
new := InducedPcgsByPcSequenceAndGenerators( home, pcgsU, [g] );
N := SubgroupByPcgs( GroupOfPcgs(home), new );
# SetHomePcgs( N, home );
# SetInducedPcgsWrtHomePcgs( N, new );
return N;
fi;
end );
#############################################################################
##
#M CommutatorSubgroup( <U>, <V> )
##
InstallMethod( CommutatorSubgroup,
"groups with home pcgs",
true,
[ IsGroup and HasHomePcgs,
IsGroup and HasHomePcgs ],
0,
function( U, V )
local pcgsU, pcgsV, home, C, u, v;
# check
home := HomePcgs(U);
if home <> HomePcgs( V ) then
TryNextMethod();
fi;
pcgsU := InducedPcgsWrtHomePcgs(U);
pcgsV := InducedPcgsWrtHomePcgs(V);
# catch trivial cases
if Length(pcgsU) = 0 or Length(pcgsV) = 0 then
return TrivialSubgroup( GroupOfPcgs(home) );
fi;
if U = V then
return DerivedSubgroup(U);
fi;
# compute commutators
C := [];
for u in pcgsU do
for v in pcgsV do
AddSet( C, Comm( v, u ) );
od;
od;
C := Subgroup( GroupOfPcgs( home ), C );
C := NormalClosure( ClosureGroup(U,V), C );
# that's it
return C;
end );
#############################################################################
##
#M ConjugateGroup( <U>, <g> )
##
InstallMethod( ConjugateGroup,
"groups with home pcgs",
IsCollsElms,
[ IsGroup and HasHomePcgs,
IsMultiplicativeElementWithInverse ],
0,
function( U, g )
local home, pcgs, id, pag, h, d, N;
# <g> must lie in the home
home := HomePcgs(U);
if not g in GroupOfPcgs(home) then
TryNextMethod();
fi;
# shift <g> through <U>
pcgs := InducedPcgsWrtHomePcgs( U );
id := Identity( U );
g := SiftedPcElement( pcgs, g );
# catch trivial case
if IsEmpty(pcgs) or g = id then
return U;
fi;
# conjugate generators
pag := [];
for h in Reversed( pcgs ) do
h := h ^ g;
d := DepthOfPcElement( home, h );
while h <> id and IsBound( pag[d] ) do
h := ReducedPcElement( home, h, pag[d] );
d := DepthOfPcElement( home, h );
od;
if h <> id then
pag[d] := h;
fi;
od;
# <pag> is an induced system
pag := Compacted( pag );
N := Subgroup( GroupOfPcgs(home), pag );
SetHomePcgs( N, home );
pag := InducedPcgsByPcSequenceNC( home, pag );
SetGroupOfPcgs (pag, N);
SetInducedPcgsWrtHomePcgs( N, pag );
# maintain useful information
UseIsomorphismRelation( U, N );
return N;
end );
#############################################################################
##
#M ConjugateSubgroups( <G>, <U> )
##
InstallMethod( ConjugateSubgroups,
"groups with home pcgs",
IsIdenticalObj,
[ IsGroup and HasHomePcgs,
IsGroup and HasHomePcgs ],
0,
function( G, U )
local pcgs, home, f, orb, i, L, res, H,ip;
# check the home pcgs are compatible
home := HomePcgs(U);
if home <> HomePcgs(G) then
TryNextMethod();
fi;
H := GroupOfPcgs( home );
# get a canonical pcgs for <U>
pcgs := CanonicalPcgsWrtHomePcgs(U);
# <G> acts on this <pcgs> via conjugation
f := function( c, g )
#was: CanonicalPcgs( HomomorphicInducedPcgs( home, c, g ) );
return CorrespondingGeneratorsByModuloPcgs(home,List(c,i->i^g));
end;
# compute the orbit of <G> on <pcgs>
orb := Orbit( G, pcgs, f );
res := List( orb, ReturnFalse );
for i in [1..Length(orb)] do
L := Subgroup( H, orb[i] );
SetHomePcgs( L, home );
if not(IsPcgs(orb[i])) then
ip:=InducedPcgsByPcSequenceNC(home,orb[i]);
else
ip:=orb[i];
fi;
SetInducedPcgsWrtHomePcgs( L, ip );
SetGroupOfPcgs (ip, L);
res[i] := L;
od;
return res;
end );
#############################################################################
##
#M Core( <U>, <V> )
##
InstallMethod( CoreOp,
"pcgs computable groups",
true,
[ IsGroup and CanEasilyComputePcgs,
IsGroup ],
0,
function( V, U )
local pcgsV, C, v, N;
# catch trivial cases
pcgsV := Pcgs(V);
if IsSubset( U, V ) or IsTrivial(U) or IsTrivial(V) then
return U;
fi;
# start with <U>.
C := U;
# now compute intersection with all conjugate subgroups, conjugate with
# all generators of V and its powers
for v in Reversed(pcgsV) do
repeat
N := ConjugateGroup( C, v );
if C <> N then
C := Intersection( C, N );
fi;
until C = N;
if IsTrivial(C) then
return C;
fi;
od;
return C;
end );
#############################################################################
##
#M EulerianFunction( <G>, <n> )
##
InstallMethod( EulerianFunction,
"pcgs computable groups using special pcgs",
true,
[ IsGroup and CanEasilyComputePcgs,
IsPosInt ],
0,
function( G, n )
local spec, first, weights, m, i, phi, start,
next, p, d, j, pcgsS, pcgsN, pcgsL, mats,
modu, max, series, comps, sub, new, index, order;
spec := SpecialPcgs( G );
if Length( spec ) = 0 then return 1; fi;
first := LGFirst( spec );
weights := LGWeights( spec );
m := Length( spec );
# the first head
i := 1;
phi := 1;
while i <= Length(first)-1 and
weights[first[i]][1] = 1 and weights[first[i]][2] = 1 do
start := first[i];
next := first[i+1];
p := weights[start][3];
d := next - start;
for j in [0..d-1] do
phi := phi * (p^n - p^j);
od;
if phi = 0 then return 0; fi;
i := i + 1;
od;
# the rest
while i <= Length( first ) - 1 do
start := first[i];
next := first[i+1];
p := weights[start][3];
d := next - start;
if weights[start][2] = 1 then
pcgsS := InducedPcgsByPcSequenceNC( spec, spec{[start..m]} );
pcgsN := InducedPcgsByPcSequenceNC( spec, spec{[next..m]} );
pcgsL := pcgsS mod pcgsN;
mats := LinearActionLayer( spec, pcgsL );
modu := GModuleByMats( mats, GF(p) );
max := MTX.BasesMaximalSubmodules( modu );
# compute series
series := [ Immutable( IdentityMat(d, GF(p)) ) ];
comps := [];
sub := series[1];
while Length( max ) > 0 do
sub := SumIntersectionMat( sub, max[1] )[2];
if Length( sub ) = 0 then
new := max;
else
new := Filtered( max, x ->
RankMat( Concatenation( x, sub ) ) < d );
fi;
Add( comps, Sum( List( new, x -> p^(d - Length(x)) ) ) );
Add( series, sub );
max := Difference( max, new );
od;
# run down series
for j in [1..Length( series )-1] do
index := Length( series[j] ) - Length( series[j+1] );
order := p^index;
phi := phi * ( order^n - comps[j] );
if phi = 0 then return phi; fi;
od;
# only the radical is missing now
index := Length( Last(series) );
order := p^index;
phi := phi * (order^n);
if phi = 0 then return 0; fi;
else
order := p^d;
phi := phi * ( order^n );
if phi = 0 then return 0; fi;
fi;
i := i + 1;
od;
return phi;
end );
RedispatchOnCondition(EulerianFunction,true,[IsGroup,IsPosInt],
[IsSolvableGroup,IsPosInt],
1 # make the priority higher than the default method computing
# the table of marks
);
#############################################################################
##
#M LinearAction( <gens>, <basisvectors>, <linear> )
##
InstallMethod( LinearAction,
true,
[ IsList,
IsMatrix,
IsFunction ],
0,
function( gens, base, linear )
local i,mats;
# catch trivial cases
if Length( gens ) = 0 then
return [];
fi;
# compute matrices
if Length(base)>0 then
mats := List( gens, x -> ImmutableMatrix(Characteristic(base),
List( base, y -> linear( y, x ) ),true ));
else
mats:=List(gens,i->[]);
fi;
MakeImmutable(mats);
return mats;
end );
InstallOtherMethod( LinearAction,
true,
[ IsGroup,
IsMatrix,
IsFunction ],
0,
function( G, base, linear )
return LinearAction( GeneratorsOfGroup( G ), base, linear );
end );
InstallOtherMethod( LinearAction,
true,
[ IsPcgs,
IsMatrix,
IsFunction ],
0,
function( pcgs, base, linear )
return LinearAction( pcgs, base, linear );
end );
InstallOtherMethod( LinearAction,
true,
[ IsGroup,
IsList,
IsMatrix,
IsFunction ],
0,
function( G, gens, base, linear )
return LinearAction( gens, base, linear );
end );
#############################################################################
##
#M NormalClosure( <G>, <U> )
##
InstallMethod( NormalClosureOp,
"groups with home pcgs",
true,
[ IsGroup and HasHomePcgs,
IsGroup and HasHomePcgs ],
0,
function( G, U )
local pcgs, home, gens, subg, id, K, M, g, u, tmp;
# catch trivial case
pcgs := InducedPcgsWrtHomePcgs(U);
if Length(pcgs) = 0 then
return U;
fi;
home := HomePcgs(U);
if home <> HomePcgs(G) then
TryNextMethod();
fi;
# get operating elements
gens := GeneratorsOfGroup( G );
gens := Set( gens, x -> SiftedPcElement( pcgs, x ) );
subg := GeneratorsOfGroup( U );
id := Identity( G );
K := ShallowCopy( pcgs );
repeat
M := [];
for g in gens do
for u in subg do
tmp := Comm( g, u );
if tmp <> id then
AddSet( M, tmp );
fi;
od;
od;
tmp := InducedPcgsByPcSequenceAndGenerators( home, K, M );
tmp := CanonicalPcgs( tmp );
subg := Filtered( tmp, x -> not x in K );
K := tmp;
until 0 = Length(subg);
K := SubgroupByPcgs( GroupOfPcgs(home), tmp );
# SetHomePcgs( K, home );
# SetInducedPcgsWrtHomePcgs( K, tmp );
return K;
end );
#############################################################################
##
#M Random( <pcgrp> )
##
InstallMethodWithRandomSource( Random,
"for a random source and a pcgs computable groups",
[ IsRandomSource, IsGroup and CanEasilyComputePcgs and IsFinite ],
function(rs, grp)
local p;
p := Pcgs(grp);
if Length( p ) = 0 then
return One( grp );
else
return Product( p, x -> x^Random(rs, 1, RelativeOrderOfPcElement(p,x)) );
fi;
end );
BindGlobal( "CentralizerSolvableGroup", function(H,U,elm)
local G, home, # the supergroup (of <H> and <U>), the home pcgs
Hp, # a pcgs for <H>
inequal, # G<>H flag
eas, # elementary abelian series in <G> through <U>
step, # counter looping over <eas>
L, # members of <eas>
Kp,Lp, # induced and modulo pcgs's
KcapH,LcapH, # pcgs's of intersections with <H>
N, cent, # elementary abelian factor, for affine action
cls, # classes in range/source of homomorphism
opr, # (elm^opr)=cls.representative
nexpo,indstep,Ldep,allcent;
# Treat the case of a trivial group.
if IsTrivial( U ) then
return H;
fi;
if IsSubgroup(H,U) then
G:=H;
inequal:=false;
else
G:=ClosureGroup( H, U );
inequal:=true;
fi;
home:=HomePcgs(G);
if not HasIndicesEANormalSteps(home) then
home:=PcgsElementaryAbelianSeries(G);
fi;
# Calculate a (central) elementary abelian series with all pcgs induced
# w.r.t. <home>.
if IsPGroup( G ) then
home:=PcgsCentralSeries(G);
eas:=CentralNormalSeriesByPcgs(home);
cent:=ReturnTrue;
else
home:=PcgsElementaryAbelianSeries(G);
eas:=EANormalSeriesByPcgs(home);
cent:=PcClassFactorCentralityTest;
fi;
indstep:=IndicesEANormalSteps(home);
Hp:=InducedPcgs(home,H);
# Initialize the algorithm for the trivial group.
step:=1;
while IsSubset( eas[ step + 1 ], U ) do
step:=step + 1;
od;
L :=eas[ step ];
Ldep:=indstep[step];
Lp:=InducedPcgs(home,L);
if inequal then
LcapH:=NormalIntersectionPcgs( home, Hp, Lp );
fi;
cls:=[rec( representative:=elm,centralizer:=H,
centralizerpcgs:=InducedPcgs(home,H) )];
opr:=One( U );
# Now go back through the factors by all groups in the elementary abelian
# series.
for step in [ step + 1 .. Length( eas ) ] do
# We apply the homomorphism principle to the homomorphism G/L -> G/K.
# The actual computations are all done in <G>, factors are
# represented by modulo pcgs.
Kp:=Lp;
L :=eas[ step ];
Ldep:=indstep[step];
Lp:=InducedPcgs(home,L );
N :=Kp mod Lp; # modulo pcgs representing the kernel
allcent:=cent(home,home,N,Ldep);
if allcent=false then
nexpo:=LinearActionLayer(home{[1..indstep[step-1]-1]},N);
fi;
# #T What is this? Obviously it is needed somewhere, but it is
# #T certainly not good programming style. AH
# SetFilterObj( N, IsPcgs );
if inequal then
KcapH :=LcapH;
LcapH :=NormalIntersectionPcgs( home, Hp, Lp );
N!.capH:=KcapH mod LcapH;
#T See above
# SetFilterObj( N!.capH, IsPcgs );
else
N!.capH:=N;
fi;
cls[ 1 ].candidates:=cls[ 1 ].representative;
if allcent
or cent(home, cls[ 1 ].centralizerpcgs, N, Ldep ) then
cls:=CentralStepClEANS( home,H, U, N, cls[ 1 ],false );
else
cls:=GeneralStepClEANS( home,H, U, N, nexpo,cls[ 1 ],false );
fi;
opr:=opr * cls[ 1 ].operator;
if IsModuloPcgs(cls[1].cengen) then
cls[1].centralizerpcgs:=cls[1].cengen;
else
cls[1].centralizerpcgs:=InducedPcgsByPcSequenceNC(home,cls[1].cengen);
fi;
od;
if not IsBound(cls[1].centralizer) then
cls[1].centralizer:=SubgroupByPcgs(G,cls[1].centralizerpcgs);
fi;
cls:=ConjugateSubgroup( cls[ 1 ].centralizer, opr ^ -1 );
return cls;
end );
#############################################################################
##
#M Centralizer( <G>, <g> ) . . . . . . . . . . . . . . using affine methods
##
InstallMethod( CentralizerOp,
"pcgs computable group and element",
IsCollsElms,
[ IsGroup and CanEasilyComputePcgs and IsFinite,
IsMultiplicativeElementWithInverse ],
0, # in solvable permutation groups, backtrack seems preferable
function( G, g )
return CentralizerSolvableGroup( G, GroupByGenerators( [ g ] ), g );
end );
InstallMethod( CentralizerOp,
"pcgs computable groups",
IsIdenticalObj,
[ IsGroup and CanEasilyComputePcgs and IsFinite,
IsGroup and CanEasilyComputePcgs and IsFinite ],
0, # in solvable permutation groups, backtrack seems preferable
function( G, H )
local h,P;
P:=Parent(G);
for h in MinimalGeneratingSet( H ) do
G := CentralizerSolvableGroup( G,H, h );
od;
G:=AsSubgroup(P,G);
Assert(2,ForAll(GeneratorsOfGroup(G),i->ForAll(GeneratorsOfGroup(H),
j->Comm(i,j)=One(G))));
return G;
end );
#############################################################################
##
#M RepresentativeAction( <G>, <d>, <e>, OnPoints ) using affine methods
##
InstallOtherMethod( RepresentativeActionOp,
"element conjugacy in pcgs computable groups", IsCollsElmsElmsX,
[ IsGroup and CanEasilyComputePcgs and IsFinite,
IsMultiplicativeElementWithInverse,
IsMultiplicativeElementWithInverse,
IsFunction ],
0,
function( G, d, e, opr )
if opr <> OnPoints or not (IsPcGroup(G) or (d in G and e in G)) or
not (d in G and e in G) then
TryNextMethod();
fi;
return ClassesSolvableGroup( G, 4,rec(candidates:= [ d, e ] ));
end );
#############################################################################
##
#M CentralizerModulo(<H>,<N>,<elm>) full preimage of C_(H/N)(elm.N)
##
InstallMethod(CentralizerModulo,"pcgs computable groups, for elm",
IsCollsCollsElms,[IsGroup and CanEasilyComputePcgs, IsGroup and
CanEasilyComputePcgs, IsMultiplicativeElementWithInverse],0,
function(H,NT,elm)
local G, # common parent
home,Hp, # the home pcgs, induced pcgs
eas, step, # elementary abelian series in <G> through <U>
ea2, # used for factor series
L, # members of <eas>
Kp,Lp, # induced and modulo pcgs's
KcapH,LcapH, # pcgs's of intersections with <H>
N, cent, # elementary abelian factor, for affine action
tra, # transversal for candidates
nexpo,indstep,Ldep,allcent,
cl, i; # loop variables
# Treat trivial cases.
if Index(H,NT)=1 or (HasAbelianFactorGroup(H,NT) and elm in H)
or elm in NT then
return H;
fi;
if elm in H then
G:=H;
else
G:=ClosureGroup(H,elm);
# is the subgroup still normal
if not IsNormal(G,NT) then
Error("subgroup not normal!");
fi;
fi;
home := HomePcgs( G );
if not HasIndicesEANormalSteps(home) then
home:=PcgsElementaryAbelianSeries(G);
fi;
# Calculate a (central) elementary abelian series.
eas:=fail;
if IsPGroup( G ) then
home:=PcgsPCentralSeriesPGroup(G);
eas:=PCentralNormalSeriesByPcgsPGroup(home);
if NT in eas then
cent := ReturnTrue;
else
eas:=fail; # useless
fi;
fi;
if eas=fail then
home:=PcgsElementaryAbelianSeries([G,NT]);
eas:=EANormalSeriesByPcgs(home);
cent:=PcClassFactorCentralityTest;
fi;
indstep:=IndicesEANormalSteps(home);
# series to NT
ea2:=List(eas,i->ClosureGroup(NT,i));
eas:=[];
for i in ea2 do
if not i in eas then
Add(eas,i);
fi;
od;
for i in eas do
if not HasHomePcgs(i) then
SetHomePcgs(i,ParentPcgs(home));
fi;
od;
Hp:=InducedPcgs(home,H);
# Initialize the algorithm for the trivial group.
step := 1;
while IsSubset( eas[ step + 1 ], H ) do
step := step + 1;
od;
L := eas[ step ];
Lp := InducedPcgs(home, L );
if not IsIdenticalObj( G, H ) then
LcapH := NormalIntersectionPcgs( home, Hp, Lp );
fi;
cl := rec( representative := elm,
centralizer := H,
centralizerpcgs := InducedPcgs(home,H ));
tra := One( H );
# cls := List( candidates, c -> cl );
# tra := List( candidates, c -> One( H ) );
tra:=One(H);
# Now go back through the factors by all groups in the elementary abelian
# series.
for step in [ step + 1 .. Length( eas ) ] do
Kp := Lp;
L := eas[ step ];
Ldep:=indstep[step];
Lp := InducedPcgs(home, L );
N := Kp mod Lp;
#SetFilterObj( N, IsPcgs );
allcent:=cent(home,home,N,Ldep);
if allcent=false then
nexpo:=LinearActionLayer(home{[1..indstep[step-1]-1]},N);
fi;
if not IsIdenticalObj( G, H ) then
KcapH := LcapH;
LcapH := NormalIntersectionPcgs( home, Hp, Lp );
N!.capH := KcapH mod LcapH;
else
N!.capH := N;
fi;
cl.candidates := cl.representative;
if allcent
or cent(home,cl.centralizerpcgs, N, Ldep) then
cl := CentralStepClEANS( home,G, H, N, cl,true )[1];
else
cl := GeneralStepClEANS( home,G, H, N,nexpo, cl,true )[1];
fi;
tra := tra * cl.operator;
if IsModuloPcgs(cl.cengen) then
cl.centralizerpcgs:=cl.cengen;
else
cl.centralizerpcgs:=InducedPcgsByPcSequenceNC(home,cl.cengen);
fi;
od;
if not IsBound(cl.centralizer) then
cl.centralizer:=SubgroupByPcgs(G,cl.centralizerpcgs);
fi;
cl:=ConjugateSubgroup( cl.centralizer, tra ^ -1 );
Assert(2,ForAll(GeneratorsOfGroup(cl),i->Comm(elm,i) in NT));
Assert(2,IsSubset(G,cl));
return cl;
end);
InstallMethod(CentralizerModulo,"group centralizer via generators",
IsFamFamFam,[IsGroup and CanEasilyComputePcgs, IsGroup and
CanEasilyComputePcgs, IsGroup],0,
function(G,NT,U)
local i,P;
P:=Parent(G);
for i in GeneratorsOfGroup(U) do
G:=CentralizerModulo(G,NT,i);
od;
G:=AsSubgroup(P,G);
return G;
end);
# enforce solvability check.
RedispatchOnCondition(CentralizerModulo,true,[IsGroup,IsGroup,IsObject],
[IsGroup and IsSolvableGroup,IsGroup and IsSolvableGroup,IsObject],0);
#############################################################################
##
#F ElementaryAbelianSeries( <list> )
##
InstallOtherMethod( ElementaryAbelianSeries,"list of pcgs computable groups",
true,[IsList and IsFinite],
1, # there is a generic groups function with value 0
function( S )
local home,i, N, O, I, E, L;
if Length(S)=0 or not CanEasilyComputePcgs(S[1]) then
TryNextMethod();
fi;
# typecheck arguments
if 1 < Size(Last(S)) then
S := ShallowCopy( S );
Add( S, TrivialSubgroup(S[1]) );
fi;
# start with the elementary series of the first group of <S>
L := ElementaryAbelianSeries( S[ 1 ] );
# enforce the same parent for 'HomePcgs' purposes.
home:=HomePcgs(S[1]);
N := [ S[ 1 ] ];
for i in [ 2 .. Length( S ) - 1 ] do
O := L;
L := [ S[ i ] ];
for E in O do
I := IntersectionSumPcgs(home, InducedPcgs(home,E),
InducedPcgs(home,S[ i ]) );
I.sum:=SubgroupByPcgs(S[1],I.sum);
I.intersection:=SubgroupByPcgs(S[1],I.intersection);
if not I.sum in N then
Add( N, I.sum );
fi;
if not I.intersection in L then
Add( L, I.intersection );
fi;
od;
od;
for E in L do
if not E in N then
Add( N, E );
fi;
od;
return N;
end);
#############################################################################
##
#M \<(G,H) . . . . . . . . . . . . . . . . . comparison of pc groups by CGS
##
InstallMethod(\<,"cgs comparison",IsIdenticalObj,[IsPcGroup,IsPcGroup],0,
function( G, H )
return Reversed( CanonicalPcgsWrtFamilyPcgs(G) )
< Reversed( CanonicalPcgsWrtFamilyPcgs(H) );
end);
#############################################################################
##
#F GapInputPcGroup( <U>, <name> ) . . . . . . . . . . . . gap input string
##
## Compute the pc-presentation for a finite polycyclic group as gap input.
## Return this input as string. The group will be named <name>,the
## generators "g<i>".
##
InstallGlobalFunction( GapInputPcGroup, function(U,name)
local gens,
wordString,
newLines,
lines,
ne,
i,j;
# <lines> will hold the various lines of the input for gap,they are
# concatenated later.
lines:=[];
# Get the generators for the group <U>.
gens:=InducedPcgsWrtHomePcgs(U);
# Initialize the group and the generators.
Add(lines,name);
Add(lines,":=function()\nlocal ");
for i in [1 .. Length(gens)] do
Add(lines,"g");
Add(lines,String(i));
Add(lines,",");
od;
Add(lines,"r,f,g,rws,x;\n");
Add(lines,"f:=FreeGroup(IsSyllableWordsFamily,");
Add(lines,String(Length(gens)));
Add(lines,");\ng:=GeneratorsOfGroup(f);\n");
for i in [1 .. Length(gens)] do
Add(lines,"g" );
Add(lines,String(i) );
Add(lines,":=g[");
Add(lines,String(i) );
Add(lines,"];\n" );
od;
Add(lines,"rws:=SingleCollector(f,");
Add(lines,String(List(gens,i->RelativeOrderOfPcElement(gens,i))));
Add(lines,");\n");
Add(lines,"r:=[\n");
# A function will yield the string for a word.
wordString:=function(a)
local k,l,list,str,count;
list:=ExponentsOfPcElement(gens,a);
k:=1;
while k <= Length(list) and list[k] = 0 do k:=k + 1; od;
if k > Length(list) then return "IdWord"; fi;
if list[k] <> 1 then
str:=Concatenation("g",String(k),"^",
String(list[k]));
else
str:=Concatenation("g",String(k));
fi;
count:=Length(str) + 15;
for l in [k + 1 .. Length(list)] do
if count > 60 then
str :=Concatenation(str,"\n ");
count:=4;
fi;
count:=count - Length(str);
if list[l] > 1 then
str:=Concatenation(str,"*g",String(l),"^",
String(list[l]));
elif list[l] = 1 then
str:=Concatenation(str,"*g",String(l));
fi;
count:=count + Length(str);
od;
return str;
end;
# Add the power presentation part.
for i in [1 .. Length(gens)] do
ne:=gens[i]^RelativeOrderOfPcElement(gens,gens[i]);
if ne<>One(U) then
Add(lines,Concatenation("[",String(i),",",
wordString(ne),"]"));
if i<Length(gens) then
Add(lines,",\n");
else
Add(lines,"\n");
fi;
fi;
od;
Add(lines,"];\nfor x in r do SetPower(rws,x[1],x[2]);od;\n");
Add(lines,"r:=[\n");
# Add the commutator presentation part.
for i in [1 .. Length(gens) - 1] do
for j in [i + 1 .. Length(gens)] do
ne:=Comm(gens[j],gens[i]);
if ne<>One(U) then
if i <> Length(gens) - 1 or j <> i + 1 then
Add(lines,Concatenation("[",String(j),",",String(i),",",
wordString(ne),"],\n"));
else
Add(lines,Concatenation("[",String(j),",",String(i),",",
wordString(ne),"]\n"));
fi;
fi;
od;
od;
Add(lines,"];\nfor x in r do SetCommutator(rws,x[1],x[2],x[3]);od;\n");
Add(lines,"return GroupByRwsNC(rws);\n");
Add(lines,"end;\n");
Add(lines,name);
Add(lines,":=");
Add(lines,name);
Add(lines,"();\n");
Add(lines,"Print(\"#I A group of order \",Size(");
Add(lines,name);
Add(lines,"),\" has been defined.\\n\");\n");
Add(lines,"Print(\"#I It is called ");
Add(lines,name);
Add(lines,"\\n\");\n");
# Concatenate all lines and return.
while Length(lines) > 1 do
if Length(lines) mod 2 = 1 then
Add(lines,"");
fi;
newLines:=[];
for i in [1 .. Length(lines) / 2] do
newLines[i]:=Concatenation(lines[2*i-1],lines[2*i]);
od;
lines:=newLines;
od;
IsString(lines[1]);
return lines[1];
end );
#############################################################################
##
#F PrintPcPresentation( <grp>, <commBool> )
##
## Display the pc relations of a pc group.
## If <commBool> is true, then the commutator presentation is printed,
## otherwise the power presentation.
## Trivial commutators / powers are not printed.
## The generators are named "g<i>".
## The returned boolean indicates if there are commuting generators.
##
InstallGlobalFunction( PrintPcPresentation, function(G, commBool)
local pcgs, n, F, gens, i, pis, exp, t, h, commPower, j, trivialCommutators;
pcgs:=Pcgs(G);
n:=Length(pcgs);
F := FreeGroup( n, "g" );
gens := GeneratorsOfGroup( F );
pis := RelativeOrders( pcgs );
# compute the orders of the pc-generators
for i in [1..n] do
exp := ExponentsOfRelativePower( pcgs, i ){[i+1..n]};
t := One( F );
for h in [i+1..n] do
t := t * gens[h]^exp[h-i];
od;
if IsOne( t ) then
t := "id";
fi;
Print(gens[i], "^", pis[i], " = ", t, "\n");
od;
# compute the commutators / conjugation
# of all pairs of pc-generators
trivialCommutators := false;
for i in [1..n] do
for j in [i+1..n] do
if pcgs[j] * pcgs[i] = pcgs[i] * pcgs[j] then
trivialCommutators := true;
continue;
fi;
if commBool then
commPower := Comm( pcgs[j], pcgs[i] );
else
commPower := pcgs[j]^pcgs[i];
fi;
exp := ExponentsOfPcElement( pcgs, commPower ){[i+1..n]};
t := One( F );
for h in [i+1..n] do
t := t * gens[h]^exp[h-i];
od;
if commBool then
Print("[", gens[j], ",", gens[i] , "]");
else
Print(gens[j], "^", gens[i]);
fi;
Print(" = ", t, "\n");
od;
od;
return trivialCommutators;
end );
#############################################################################
##
#M Display( <grp> )
##
InstallMethod( Display,
"for a pc group",
[ IsPcGroup ],
function( G )
local n, trivialCommutators;
if IsTrivial(G) then
Print("trivial pc-group\n");
return;
fi;
n := Size(Pcgs(G));
if IsOne(n) then
Print("cyclic pc-group with 1 pc-generator and the relation:\n");
else
Print("pc-group with ", Size(Pcgs(G)), " pc-generators and relations:\n");
fi;
trivialCommutators := PrintPcPresentation( G, false );
if IsAbelian(G) then
Print("all generators commute, the groups is abelian\n");
elif trivialCommutators then
Print("all other pairs of generators commute\n");
fi;
end );
#############################################################################
##
#M Enumerator( <G> ) . . . . . . . . . . . . . . . . . . enumerator by pcgs
##
InstallMethod( Enumerator,"finite pc computable groups",true,
[ IsGroup and CanEasilyComputePcgs and IsFinite ], 0,
G -> EnumeratorByPcgs( Pcgs( G ) ) );
#############################################################################
##
#M KnowsHowToDecompose( <G>, <gens> )
##
InstallMethod( KnowsHowToDecompose,
"pc group and generators: always true",
IsIdenticalObj,
[ IsPcGroup, IsList ], 0,
ReturnTrue);
#############################################################################
##
#F CanonicalSubgroupRepresentativePcGroup( <G>, <U> )
##
InstallGlobalFunction( CanonicalSubgroupRepresentativePcGroup,
function(G,U)
local e, # EAS
pcgs, # himself
iso, # isomorphism to EAS group
start, # index of largest abelian quotient
i, # loop
m, # e[i+1]
pcgsm, # pcgs(m)
mpcgs, # pcgs mod pcgsm
V, # canon. rep
fv, # <V,m>
fvgens, # gens(fv)
no, # its normalizer
orb, # orbit
o, # orb index
nno, # growing normalizer
min,
minrep, # minimum indicator
# p, # orbit pos.
one, # 1
abc, # abelian case indicator
nopcgs, #pcgs(no)
te, # transversal exponents
opfun, # operation function
ce; # conj. elm
if not IsSubgroup(G,U) then
Error("#W CSR Closure\n");
G:=Subgroup(Parent(G),Concatenation(GeneratorsOfGroup(G),
GeneratorsOfGroup(U)));
fi;
# compute a pcgs fitting the EAS
pcgs:=PcgsChiefSeries(G);
e:=ChiefNormalSeriesByPcgs(pcgs);
if not IsBound(G!.chiefSeriesPcgsIsFamilyInduced) then
# test whether pcgs is family induced
m:=List(pcgs,i->ExponentsOfPcElement(FamilyPcgs(G),i));
G!.chiefSeriesPcgsIsFamilyInduced:=
ForAll(m,i->Number(i,j->j<>0)=1) and ForAll(m,i->Number(i,j->j=1)=1)
and m=Reversed(Set(m));
if not G!.chiefSeriesPcgsIsFamilyInduced then
# compute isom. &c.
V:=PcGroupWithPcgs(pcgs);
iso:=GroupHomomorphismByImagesNC(G,V,pcgs,FamilyPcgs(V));
G!.isomorphismChiefSeries:=iso;
G!.isomorphismChiefSeriesPcgs:=FamilyPcgs(Image(iso));
G!.isomorphismChiefSeriesPcgsSeries:=List(e,i->Image(iso,i));
fi;
fi;
if not G!.chiefSeriesPcgsIsFamilyInduced then
iso:=G!.isomorphismChiefSeries;
pcgs:=G!.isomorphismChiefSeriesPcgs;
e:=G!.isomorphismChiefSeriesPcgsSeries;
U:=Image(iso,U);
G:=Image(iso);
else
iso:=false;
fi;
#pcgs:=Concatenation(List([1..Length(e)-1],i->
# InducedPcgs(home,e[i]) mod InducedPcgs(home,e[i+1])));
#pcgs:=PcgsByPcSequence(ElementsFamily(FamilyObj(G)),pcgs);
##AH evtl. noch neue Gruppe
# find the largest abelian quotient
start:=2;
while start<Length(e) and HasAbelianFactorGroup(G,e[start+1]) do
start:=start+1;
od;
#initialize
V:=U;
one:=One(G);
ce:=One(G);
no:=G;
for i in [start..Length(e)-1] do
# lift from G/e[i] to G/e[i+1]
m:=e[i+1];
pcgsm:=InducedPcgs(pcgs,m);
mpcgs:=pcgs mod pcgsm;
# map v,no
#fv:=ClosureGroup(m,V);
#img:=CanonicalPcgs(InducedPcgsByGenerators(pcgs,GeneratorsOfGroup(fv)));
# if true then
nopcgs:=InducedPcgs(pcgs,no);
fvgens:=GeneratorsOfGroup(V);
if true then
min:=CorrespondingGeneratorsByModuloPcgs(mpcgs,fvgens);
#UU:=ShallowCopy(min);
# NORMALIZE_IGS(mpcgs,min);
#if UU<>min then
# Error("hier1");
#fi;
# trim m-part
min:=List(min,i->CanonicalPcElement(pcgsm,i));
# operation function: operate on the cgs modulo m
opfun:=function(u,e)
u:=CorrespondingGeneratorsByModuloPcgs(mpcgs,List(u,j->j^e));
#UU:=ShallowCopy(u);
# NORMALIZE_IGS(mpcgs,u);
#if UU<>u then
# Error("hier2");
#fi;
# trim m-part
u:=List(u,i->CanonicalPcElement(pcgsm,i));
return u;
end;
else
min:=fv;
opfun:=OnPoints;
fi;
# this function computes the orbit in a well-defined order that permits
# to find a transversal cheaply
orb:=Pcgs_OrbitStabilizer(nopcgs,false,min,nopcgs,opfun);
nno:=orb.stabpcgs;
abc:=orb.lengths;
orb:=orb.orbit;
#if Length(orb)<>Index(no,Normalizer(no,fv)) then
# Error("len!");
#fi;
# determine minimal conjugate
minrep:=one;
for o in [2..Length(orb)] do
if orb[o]<min then
min:=orb[o];
minrep:=o;
fi;
od;
# compute representative
if IsInt(minrep) then
te:=ListWithIdenticalEntries(Length(nopcgs),0);
o:=2;
while minrep<>1 do
while abc[o]>=minrep do
o:=o+1;
od;
te[o-1]:=-QuoInt(minrep-1,abc[o]);
minrep:=(minrep-1) mod abc[o]+1;
od;
te:=LinearCombinationPcgs(nopcgs,te)^-1;
if opfun(orb[1],te)<>min then
Error("wrong repres!");
fi;
minrep:=te;
fi;
#
#
# nno:=Normalizer(no,fv);
#
# rep:=RightTransversal(no,nno);
# #orb:=List(rep,i->CanonicalPcgs(InducedPcgs(pcgs,fv^i)));
#
# # try to cope with action on vector space (long orbit)
## abc:=false;
## if Index(fv,m)>1 and HasElementaryAbelianFactorGroup(fv,m) then
## nocl:=NormalClosure(no,fv);
## if HasElementaryAbelianFactorGroup(nocl,m) then
### abc:=true; # try el. ab. case
## fi;;
## fi;
#
# if abc then
# nocl:=InducedPcgs(pcgs,nocl) mod pcgsm;
# nopcgs:=InducedPcgs(pcgs,no) mod pcgsm;
# lop:=LinearActionLayer(Group(nopcgs),nocl); #matrices for action
# fvgens:=List(fvgens,i->ShallowCopy(
# ExponentsOfPcElement(nocl,i)*Z(RelativeOrders(nocl)[1])^0));
# TriangulizeMat(fvgens); # canonize
# min:=fvgens;
# minrep:=one;
# for o in rep do
# if o<>one then
# # matrix image of rep
# orb:=ExponentsOfPcElement(nopcgs,o);
# orb:=Product([1..Length(orb)],i->lop[i]^orb[i]);
# orb:=List(fvgens*orb,ShallowCopy);
# TriangulizeMat(orb);
# if orb<min then
# min:=orb;
# minrep:=o;
# fi;
# fi;
# od;
#
# else
# min:=CorrespondingGeneratorsByModuloPcgs(mpcgs,fvgens);
# NORMALIZE_IGS(mpcgs,min);
# minrep:=one;
# for o in rep do
# if o<>one then
# if Length(fvgens)=1 then
# orb:=fvgens[1]^o;
# orb:=orb^(1/LeadingExponentOfPcElement(mpcgs,orb)
# mod RelativeOrderOfPcElement(mpcgs,orb));
# orb:=[orb];
# else
# orb:=CorrespondingGeneratorsByModuloPcgs(mpcgs,List(fvgens,j->j^o));
# NORMALIZE_IGS(mpcgs,orb);
# fi;
# if orb<min then
# min:=orb;
# minrep:=o;
# fi;
# fi;
# od;
# fi;
# conjugate normalizer to new minimal one
no:=ClosureGroup(m,List(nno,i->i^minrep));
ce:=ce*minrep;
V:=V^minrep;
od;
if iso<>false then
V:=PreImage(iso,V);
no:=PreImage(iso,no);
ce:=PreImagesRepresentative(iso,ce);
fi;
return [V,no,ce];
end );
#############################################################################
##
#M ConjugacyClassSubgroups(<G>,<g>) . . . . . . . constructor for pc groups
## This method installs 'CanonicalSubgroupRepresentativePcGroup' as
## CanonicalRepresentativeDeterminator
##
InstallMethod(ConjugacyClassSubgroups,IsIdenticalObj,[IsPcGroup,IsPcGroup],0,
function(G,U)
local cl;
cl:=Objectify(NewType(CollectionsFamily(FamilyObj(G)),
IsConjugacyClassSubgroupsByStabilizerRep),rec());
SetActingDomain(cl,G);
SetRepresentative(cl,U);
SetFunctionAction(cl,OnPoints);
SetCanonicalRepresentativeDeterminatorOfExternalSet(cl,
CanonicalSubgroupRepresentativePcGroup);
return cl;
end);
InstallOtherMethod(RepresentativeActionOp,"pc group on subgroups",true,
[IsPcGroup,IsPcGroup,IsPcGroup,IsFunction],0,
function(G,U,V,f)
local c1,c2;
if f<>OnPoints or not (IsSubset(G,U) and IsSubset(G,V)) then
TryNextMethod();
fi;
if Size(U)<>Size(V) then
return fail;
fi;
c1:=CanonicalSubgroupRepresentativePcGroup(G,U);
c2:=CanonicalSubgroupRepresentativePcGroup(G,V);
if c1[1]<>c2[1] then
return fail;
fi;
return c1[3]/c2[3];
end);
#############################################################################
##
#F ChiefSeriesUnderAction( <U>, <G> )
##
InstallMethod( ChiefSeriesUnderAction,
"method for a pcgs computable group",
IsIdenticalObj,
[ IsGroup, IsGroup and CanEasilyComputePcgs ], 0,
function( U, G )
local home,e,ser,i,j,k,pcgs,mpcgs,op,m,cs,n;
home:=HomePcgs(G);
e:=ElementaryAbelianSeriesLargeSteps(G);
# make the series U-invariant
ser:=ShallowCopy(e);
e:=[G];
n:=G;
for i in [2..Length(ser)] do
# check whether we actually stepped down (or did the intersection
# already do it?
if Size(ser[i])<Size(n) then
if not IsNormal(U,ser[i]) then
# assuming the last was normal we intersect the conjugates and get a
# new normal with still ea. factor
ser[i]:=Core(U,ser[i]);
# intersect the rest of the series.
for j in [i+1..Length(ser)-1] do
ser[j]:=Intersection(ser[i],ser[j]);
od;
fi;
Add(e,ser[i]);
n:=ser[i];
fi;
od;
ser:=[G];
for i in [2..Length(e)] do
Info(InfoPcGroup,1,"Step ",i,": ",Index(e[i-1],e[i]));
if IsPrimeInt(Index(e[i-1],e[i])) then
Add(ser,e[i]);
else
pcgs:=InducedPcgs(home,e[i-1]);
mpcgs:=pcgs mod InducedPcgs(home,e[i]);
op:=LinearActionLayer(U,GeneratorsOfGroup(U),mpcgs);
if ForAll(op,IsOne) then
m:=op[1]; # identity
cs:=List([0..Length(m)],x->m{[Length(m)+1-x..Length(m)]});
else
m:=GModuleByMats(op,GF(RelativeOrderOfPcElement(mpcgs,mpcgs[1])));
cs:=MTX.BasesCompositionSeries(m);
fi;
Sort(cs,function(a,b) return Length(a)>Length(b);end);
cs:=cs{[2..Length(cs)]};
Info(InfoPcGroup,2,Length(cs)-1," compositionFactors");
for j in cs do
n:=e[i];
for k in j do
n:=ClosureGroup(n,PcElementByExponentsNC(mpcgs,List(k,IntFFE)));
od;
Add(ser,n);
od;
fi;
od;
return ser;
end);
InstallMethod(IsSimpleGroup,"for solvable groups",true,
[IsSolvableGroup],
# this is also better for permutation groups, so we increase the value to
# be above the value for `IsPermGroup'.
{} -> Maximum(RankFilter(IsSolvableGroup),
RankFilter(IsPermGroup)+1)
-RankFilter(IsSolvableGroup),
function(G)
return IsInt(Size(G)) and IsPrimeInt(Size(G));
end);
#############################################################################
##
#M ViewObj(<G>)
##
InstallMethod(ViewObj,"pc group",true,[IsPcGroup],0,
function(G)
local nrgens;
nrgens := Length(GeneratorsOfGroup(G));
if (not HasParent(G)) or
nrgens*Length(GeneratorsOfGroup(Parent(G)))
/ GAPInfo.ViewLength > 50 then
Print("<pc group");
if HasSize(G) then
Print(" of size ",Size(G));
fi;
Print(" with ", Pluralize(nrgens, "generator"), ">");
else
Print("Group(");
ViewObj(GeneratorsOfGroup(G));
Print(")");
fi;
end);
#############################################################################
##
#M CanEasilyComputePcgs( <pcgrp> ) . . . . . . . . . . . . . . . . pc group
##
InstallTrueMethod( CanEasilyComputePcgs, IsPcGroup );
# InstallTrueMethod( CanEasilyComputePcgs, HasPcgs );
# we cannot guarantee that computations with any pcgs is efficient
InstallTrueMethod( CanEasilyComputePcgs, IsGroup and HasFamilyPcgs );
#############################################################################
##
#M CanEasilyTestMembership
##
# InstallTrueMethod(CanEasilyTestMembership,CanEasilyComputePcgs);
# we cannot test membership using a pcgs
# InstallTrueMethod(CanComputeSize, CanEasilyComputePcgs); #unnecessary
#############################################################################
##
#M IsSolvableGroup
##
InstallTrueMethod(IsSolvableGroup, CanEasilyComputePcgs);
#############################################################################
##
#M CanComputeSizeAnySubgroup
##
InstallTrueMethod( CanComputeSizeAnySubgroup, CanEasilyComputePcgs );
#############################################################################
##
#M CanEasilyComputePcgs( <grp> ) . . . . . . . . . subset or factor relation
##
## Since factor groups might be in a different representation,
## they should *not* inherit `CanEasilyComputePcgs' automatically.
##
#InstallSubsetMaintenance( CanEasilyComputePcgs,
# IsGroup and CanEasilyComputePcgs, IsGroup );
#############################################################################
##
#M IsConjugatorIsomorphism( <hom> )
##
InstallMethod( IsConjugatorIsomorphism,
"for a pc group general mapping",
true,
[ IsGroupGeneralMapping ], 1,
# There is no filter to test whether source and range of a homomorphism
# are pc groups.
# So we have to test explicitly and make this method
# higher ranking than the default one in `ghom.gi'.
function( hom )
local s, r, G, genss, rep;
s:= Source( hom );
if not IsPcGroup( s ) then
TryNextMethod();
elif not ( IsGroupHomomorphism( hom ) and IsBijective( hom ) ) then
return false;
elif IsEndoGeneralMapping( hom ) and IsInnerAutomorphism( hom ) then
return true;
fi;
r:= Range( hom );
# Check whether source and range are in the same family.
if FamilyObj( s ) <> FamilyObj( r ) then
--> --------------------
--> maximum size reached
--> --------------------
[ 0.39Quellennavigators
Projekt
]
|