|
#############################################################################
##
## This file is part of GAP, a system for computational discrete algebra.
## This file's authors include Heiko Theißen.
##
## 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
##
#############################################################################
##
#M CanEasilyTestMembership( <permgroup> )
##
InstallTrueMethod(CanEasilyTestMembership,IsPermGroup);
#############################################################################
##
#M CanEasilyComputePcgs( <permgroup> )
##
## solvable permgroups can compute a pcgs. (The group may have been told it
## is solvable without actually doing a solvability test, so we need the
## implication.)
InstallTrueMethod(CanEasilyComputePcgs,IsPermGroup and IsSolvableGroup);
#M CanComputeSizeAnySubgroup
InstallTrueMethod(CanComputeSizeAnySubgroup,IsPermGroup);
#############################################################################
##
#M AsSubgroup( <G>, <U> ) . . . . . . . . . . . . with stab chain transfer
##
InstallMethod( AsSubgroup,"perm groups",
IsIdenticalObj, [ IsPermGroup, IsPermGroup ], 0,
function( G, U )
local S;
# test if the parent is already alright
if HasParent(U) and IsIdenticalObj(Parent(U),G) then
return U;
fi;
if not IsSubset( G, U ) then
return fail;
fi;
S:= SubgroupNC( G, GeneratorsOfGroup( U ) );
UseIsomorphismRelation( U, S );
UseSubsetRelation( U, S );
if HasStabChainMutable( U ) then
SetStabChainMutable( S, StabChainMutable( U ) );
fi;
return S;
end );
#############################################################################
##
#M CanEasilyComputeWithIndependentGensAbelianGroup( <permgroup> )
##
InstallTrueMethod(CanEasilyComputeWithIndependentGensAbelianGroup,
IsPermGroup and IsAbelian);
#############################################################################
##
#F IndependentGeneratorsAbelianPPermGroup( <P>, <p> ) . . . nice generators
##
InstallGlobalFunction( IndependentGeneratorsAbelianPPermGroup,
function ( P, p )
local inds, # independent generators, result
pows, # their powers
base, # the base of the vectorspace
size, # the size of the group generated by <inds>
orbs, # orbits
trns, # transversal
gens, # remaining generators
one, # identity of `P'
gens2, # remaining generators for next round
exp, # exponent of <P>
g, # one generator from <gens>
h, # its power
b, # basepoint
c, # other point in orbit
i, j, k; # loop variables
# initialize the list of independent generators
inds := [];
pows := [];
base := [];
size := 1;
orbs := [];
trns := [];
# gens are the generators for the remaining group
gens := GeneratorsOfGroup( P );
one:= One( P );
# loop over the exponents
exp := Maximum( List( gens, g -> LogInt( Order( g ), p ) ) );
for i in [exp,exp-1..1] do
# loop over the remaining generators
gens2 := [];
for j in [1..Length(gens)] do
g := gens[j];
h := g ^ (p^(i-1));
# reduce <g> and <h>
while not IsOne(h)
and IsBound(trns[SmallestMovedPoint(h)^h])
do
g := g / pows[ trns[SmallestMovedPoint(h)^h] ];
h := h / base[ trns[SmallestMovedPoint(h)^h] ];
od;
# if this is linear independent, add it to the generators
if not IsOne(h) then
Add( inds, g );
Add( pows, g );
Add( base, h );
size := size * p^i;
b := SmallestMovedPoint(h);
if not IsBound( orbs[b] ) then
orbs[b] := [ b ];
trns[b] := [ one ];
fi;
for c in ShallowCopy(orbs[b]) do
for k in [1..p-1] do
Add( orbs[b], c ^ (h^k) );
trns[c^(h^k)] := Length(base);
od;
od;
# otherwise reduce and add to gens2
else
Add( gens2, g );
fi;
od;
# prepare for the next round
gens := gens2;
pows := List( pows, i->i^p );
od;
# return the independent generators
return inds;
end );
#############################################################################
##
#M IndependentGeneratorsOfAbelianGroup( <G> ) . . . . . . . nice generators
##
InstallMethod( IndependentGeneratorsOfAbelianGroup, "for perm group",
[ IsPermGroup and IsAbelian ],
function ( G )
local inds, # independent generators, result
one, # identity of `G'
p, # prime factor of group size
gens, # generators of <p>-Sylowsubgroup
g, # one generator
o; # its order
# loop over all primes
inds := [];
one:= One( G );
for p in Union( List( GeneratorsOfGroup( G ),
g -> Factors(Order(g)) ) ) do
if p <> 1 then
# compute the generators for the Sylow <p> subgroup
gens := [];
for g in GeneratorsOfGroup( G ) do
o := Order(g);
while o mod p = 0 do o := o / p; od;
g := g^o;
if not IsOne(g) then Add( gens, g ); fi;
od;
# append the independent generators for the Sylow <p> subgroup
Append( inds,
IndependentGeneratorsAbelianPPermGroup(
GroupByGenerators( gens, one ), p ) );
fi;
od;
# Sort the independent generators by increasing order
SortBy( inds, Order );
# return the independent generators
return inds;
end );
#############################################################################
##
#F OrbitPerms( <gens>, <d> ) . . . . . . . . . . . . . orbit of permutations
##
InstallGlobalFunction( OrbitPerms, function( gens, d )
local max, orb, new, pnt, img, gen;
# get the largest point <max> moved by the group <G>
max := LargestMovedPoint( gens );
# handle fixpoints
if not d in [1..max] then
return [ d ];
fi;
# start with the singleton orbit
orb := [ d ];
new := BlistList( [1..max], [1..max] );
new[d] := false;
# loop over all points found
for pnt in orb do
# apply all generators <gen>
for gen in gens do
img := pnt ^ gen;
# add the image <img> to the orbit if it is new
if new[img] then
Add( orb, img );
new[img] := false;
fi;
od;
od;
return orb;
end );
#############################################################################
##
#F OrbitsPerms( <gens>, <D> ) . . . . . . . . . . . orbits of permutations
##
InstallGlobalFunction( OrbitsPerms, function( gens, D )
local max, dom, new, orbs, fst, orb, pnt, gen, img;
# get the largest point <max> moved by the group <G>
max := LargestMovedPoint( gens );
dom := BlistList( [1..max], D );
new := BlistList( [1..max], [1..max] );
orbs := [];
# repeat until the domain is exhausted
fst := Position( dom, true );
while fst <> fail do
# start with the singleton orbit
orb := [ fst ];
new[fst] := false;
dom[fst] := false;
# loop over all points found
for pnt in orb do
# apply all generators <gen>
for gen in gens do
img := pnt ^ gen;
# add the image <img> to the orbit if it is new
if new[img] then
Add( orb, img );
new[img] := false;
dom[img] := false;
fi;
od;
od;
# add the orbit to the list of orbits and take next point
Add( orbs, orb );
fst := Position( dom, true, fst );
od;
# add the remaining points of <D>, they are fixed
for pnt in D do
if pnt>max then
Add( orbs, [ pnt ] );
fi;
od;
return orbs;
end );
#############################################################################
##
#M SmallestMovedPoint( <C> ) . . . . . . . for a collection of permutations
##
BindGlobal( "SmallestMovedPointPerms", function( C )
local min, m, gen;
min := infinity;
for gen in C do
if not IsOne( gen ) then
m := SmallestMovedPointPerm( gen );
if m < min then
min := m;
fi;
fi;
od;
return min;
end );
InstallMethod( SmallestMovedPoint,
"for a collection of permutations",
true,
[ IsPermCollection ], 0,
SmallestMovedPointPerms );
InstallMethod( SmallestMovedPoint,
"for an empty list",
true,
[ IsList and IsEmpty ], 0,
SmallestMovedPointPerms );
#############################################################################
##
#M LargestMovedPoint( <C> ) . . . . . . . for a collection of permutations
##
BindGlobal( "LargestMovedPointPerms", function( C )
local max, m, gen;
max := 0;
for gen in C do
if not IsOne( gen ) then
m := LargestMovedPointPerm( gen );
if max < m then
max := m;
fi;
fi;
od;
return max;
end );
InstallMethod( LargestMovedPoint,
"for a collection of permutations",
true,
[ IsPermCollection ], 0,
LargestMovedPointPerms );
InstallMethod( LargestMovedPoint,
"for an empty list",
true,
[ IsList and IsEmpty ], 0,
LargestMovedPointPerms );
#############################################################################
##
#F MovedPoints( <C> ) . . . . . . . . . . for a collection of permutations
##
InstallGlobalFunction(MovedPointsPerms,function( C )
local mov, gen, pnt;
mov := [ ];
for gen in C do
if gen <> One( gen ) then
for pnt in [ SmallestMovedPoint( gen ) ..
LargestMovedPoint( gen ) ] do
if pnt ^ gen <> pnt then
mov[ pnt ] := pnt;
fi;
od;
fi;
od;
return Set( mov );
end);
InstallMethod( MovedPoints, "for a collection of permutations", true,
[ IsPermCollection ], 0,
MovedPointsPerms );
InstallMethod( MovedPoints, "for an empty list", true,
[ IsList and IsEmpty ], 0,
MovedPointsPerms );
InstallMethod( MovedPoints, "for a permutation", true, [ IsPerm ], 0,
function(p)
return MovedPointsPerms([p]);
end);
#############################################################################
##
#M NrMovedPoints( <C> ) . . . . . . . . . for a collection of permutations
##
BindGlobal( "NrMovedPointsPerms", function( C )
local mov, sma, pnt, gen;
mov := 0;
sma := SmallestMovedPoint( C );
if sma = infinity then
return 0;
fi;
for pnt in [ sma .. LargestMovedPoint( C ) ] do
for gen in C do
if pnt ^ gen <> pnt then
mov:= mov + 1;
break;
fi;
od;
od;
return mov;
end );
InstallMethod( NrMovedPoints,
"for a collection of permutations",
[ IsPermCollection ],
NrMovedPointsPerms );
InstallMethod( NrMovedPoints,
"for an empty list",
[ IsList and IsEmpty ],
NrMovedPointsPerms );
#############################################################################
##
#M LargestMovedPoint( <G> ) . . . . . . . . . . . . for a permutation group
##
InstallMethod( LargestMovedPoint,
"for a permutation group",
true,
[ IsPermGroup ], 0,
G -> LargestMovedPoint( GeneratorsOfGroup( G ) ) );
#############################################################################
##
#M SmallestMovedPoint( <G> ) . . . . . . . . . . . . for a permutation group
##
InstallMethod( SmallestMovedPoint,
"for a permutation group",
true,
[ IsPermGroup ], 0,
G -> SmallestMovedPoint( GeneratorsOfGroup( G ) ) );
#############################################################################
##
#M MovedPoints( <G> ) . . . . . . . . . . . . . . . for a permutation group
##
InstallMethod( MovedPoints,
"for a permutation group",
true,
[ IsPermGroup ], 0,
G -> MovedPoints( GeneratorsOfGroup( G ) ) );
#############################################################################
##
#M NrMovedPoints( <G> ) . . . . . . . . . . . . . . for a permutation group
##
InstallMethod( NrMovedPoints,
"for a permutation group",
true,
[ IsPermGroup ], 0,
G -> NrMovedPoints( GeneratorsOfGroup( G ) ) );
#############################################################################
##
#M OrbitsMovedPoints( <G> )
##
InstallMethod( OrbitsMovedPoints, "for a permutation group", [IsPermGroup],
function(G)
local o,i;
o:=Orbits(G,MovedPoints(G));
o:=List(o,Set);
List(o,IsRange); # save memory if long orbits
Sort(o);
MakeImmutable(o);
for i in o do
SetIsSSortedList(i,true);
od;
SetIsSSortedList(o,true);
return o;
end);
#############################################################################
##
#M \=( <G>, <H> ) . . . . . . . . . . . test if two perm groups are equal
##
InstallMethod( \=, "perm groups", IsIdenticalObj, [ IsPermGroup, IsPermGroup ],
function ( G, H )
# avoid new stabilizer chains if they are not computed yet
if HasSize(G) and HasSize(H) and Size(G)<>Size(H) then return false;fi;
if not (HasGeneratorsOfGroup(G) and HasGeneratorsOfGroup(H)) then
TryNextMethod();
fi;
if LargestMovedPoint(G)<>LargestMovedPoint(H)
or OrbitsMovedPoints(G)<>OrbitsMovedPoints(H) then
return false;
fi;
if IsEqualSet( GeneratorsOfGroup( G ), GeneratorsOfGroup( H ) ) then
return true;
fi;
# try to use one existing stab chain
if HasStabChainMutable(H) or HasStabChainImmutable(H) then
return Size(G)=Size(H) and ForAll(GeneratorsOfGroup(G),gen->gen in H);
fi;
return Size(G)=Size(H) and ForAll(GeneratorsOfGroup(H),gen->gen in G);
end);
#############################################################################
##
#M BaseOfGroup( <G> )
##
InstallMethod( BaseOfGroup,
"for a permutation group",
true,
[ IsPermGroup ], 0,
G -> BaseStabChain( StabChainMutable( G ) ) );
#############################################################################
##
#M Size( <G> ) . . . . . . . . . . . . . . . . . . size of permutation group
##
InstallMethod( Size,
"for a permutation group",
true,
[ IsPermGroup ], 0,
G -> SizeStabChain( StabChainMutable( G ) ) );
#############################################################################
##
#M Enumerator( <G> ) . . . . . . . . . . . . enumerator of permutation group
##
BindGlobal( "ElementNumber_PermGroup",
function( enum, pos )
local elm, S, len, n;
S := enum!.stabChain;
elm := S.identity;
n:= pos;
pos := pos - 1;
while Length( S.genlabels ) <> 0 do
len := Length( S.orbit );
elm := LeftQuotient( InverseRepresentative
( S, S.orbit[ pos mod len + 1 ] ), elm );
pos := QuoInt( pos, len );
S := S.stabilizer;
od;
if pos <> 0 then
Error( "<enum>[", n, "] must have an assigned value" );
fi;
return elm;
end );
BindGlobal( "NumberElement_PermGroup",
function( enum, elm )
local pos, val, S, img;
if not IsPerm( elm ) then
return fail;
fi;
pos := 1;
val := 1;
S := enum!.stabChain;
while Length( S.genlabels ) <> 0 do
img := S.orbit[ 1 ] ^ elm;
#pos := pos + val * ( Position( S.orbit, img ) - 1 );
if IsBound(S.orbitpos[img]) then
pos := pos + val * S.orbitpos[img];
#val := val * Length( S.orbit );
val := val * S.ol;
elm := elm * InverseRepresentative( S, img );
S := S.stabilizer;
else
return fail;
fi;
od;
if elm <> S.identity then
return fail;
fi;
return pos;
end );
InstallMethod( Enumerator,
"for a permutation group",
[ IsPermGroup ],
function(G)
local e,S,i,p;
# get a good base
S:=G;
p:=[];
while Size(S)>1 do
e:=Orbits(S,MovedPoints(S));
i:=List(e,Length);
i:=Position(i,Maximum(i));
Add(p,e[i][1]);
S:=Stabilizer(S,e[i][1]);
od;
Info(InfoGroup,1,"choose base ",p);
S:=StabChainOp(G,rec(base:=p));
e:=EnumeratorByFunctions( G, rec(
ElementNumber := ElementNumber_PermGroup,
NumberElement := NumberElement_PermGroup,
Length := enum -> SizeStabChain( enum!.stabChain ),
PrintObj := function( G )
Print( "<enumerator of perm group>" );
end,
stabChain := S ) );
while Length(S.genlabels)<>0 do
S.ol:=Length(S.orbit);
S.orbitpos:=[];
for i in [1..Maximum(S.orbit)] do
p:=Position(S.orbit,i);
if p<>fail then
S.orbitpos[i]:=p-1;
fi;
od;
S:=S.stabilizer;
od;
return e;
end);
#############################################################################
##
#M Iterator( <G> ) . . . . . . . . . . . . . . iterator of permutation group
##
InstallMethod( Iterator,
"for a trivial permutation group",
[ IsPermGroup and IsTrivial ],
function(G)
return IteratorList([()]);
end);
InstallMethod( Iterator,
"for a permutation group",
[ IsPermGroup ],
function(G)
return IteratorStabChain(StabChain(G));
end);
#############################################################################
##
#M Random( <G> ) . . . . . . . . . . . . . . . . . . . . . . random element
##
InstallMethodWithRandomSource( Random,
"for a random source and a permutation group",
[ IsRandomSource, IsPermGroup ], 10,
function( rs, G )
local S, rnd;
# go down the stabchain and multiply random representatives
S := StabChainMutable( G );
rnd := S.identity;
while Length( S.genlabels ) <> 0 do
rnd := LeftQuotient( InverseRepresentative( S,
Random( rs, S.orbit ) ), rnd );
S := S.stabilizer;
od;
# return the random element
return rnd;
end );
#############################################################################
##
#M <g> in <G> . . . . . . . . . . . . . . . . . . . . . . . membership test
##
InstallMethod( \in,
"for a permutation, and a permutation group",
true,
[ IsPerm, IsPermGroup and HasGeneratorsOfGroup], 0,
function( g, G )
if g = One( G ) or g in GeneratorsOfGroup( G ) then
return true;
else
G := StabChainMutable( G );
return SiftedPermutation( G, g ) = G.identity;
fi;
end );
#############################################################################
##
#M ClosureGroup( <G>, <gens>, <options> ) . . . . . . closure with options
##
## The following function implements the method and may be called even with
## incomplete (temp) stabilizer chains.
BindGlobal("DoClosurePrmGp",function( G, gens, options )
local C, # closure of < <G>, <obj> >, result
P, inpar, # parent of the closure
g, # an element of gens
o, # order
newgens, # new generator list
chain; # the stabilizer chain created
options:=ShallowCopy(options); # options will be overwritten
# if all generators are in <G>, <G> is the closure
gens := Filtered( gens, gen -> not gen in G );
if IsEmpty( gens ) then
return G;
fi;
# is the closure normalizing?
if Length(gens)=1 and
ForAll(gens,i->ForAll(GeneratorsOfGroup(G),j->j^i in G)) then
g:=gens[1];
if Size(G)>1 then
o:=First(Difference(DivisorsInt(Order(g)),[1]),j->g^j in G);
options.limit:=Size(G)*o;
else
o:=First(Difference(DivisorsInt(Order(g)),[1]),j->IsOne(g^j));
options.limit:=o;
fi;
options.size:=options.limit;
#Print(options.limit,"<<\n");
fi;
# otherwise decide between random and deterministic methods
P := Parent( G );
inpar := IsSubset( P, gens );
while not inpar and not IsIdenticalObj( P, Parent( P ) ) do
P := Parent( P );
inpar := IsSubset( P, gens );
od;
if inpar then
CopyOptionsDefaults( P, options );
elif not IsBound( options.random ) then
options.random := DefaultStabChainOptions.random;
fi;
# perhaps <G> is normal in <C> with solvable factor group
#AH 5-feb-96: Disabled (see gap-dev discussion).
# if DefaultStabChainOptions.tryPcgs
# and ForAll( gens, gen -> ForAll( GeneratorsOfGroup( G ),
# g -> g ^ gen in G ) ) then
# if inpar then
# C := SubgroupNC( P,
# Concatenation( GeneratorsOfGroup( G ), gens ) );
# else
# C := GroupByGenerators
# ( Concatenation( GeneratorsOfGroup( G ), gens ) );
# fi;
# pcgs:=TryPcgsPermGroup( [ C, G ], false, false, false );
# if IsPcgs( pcgs ) then
# chain:=pcgs!.stabChain;
# if inpar then C := GroupStabChain( P, chain, true );
# else C := GroupStabChain( chain ); fi;
# SetStabChainOptions( C, rec( random := options.random ) );
#
# UseSubsetRelation( C, G );
# return C;
# fi;
# fi;
# make the base of G compatible with options.base
chain := StructuralCopy( StabChainMutable( G ) );
if IsBound( options.base ) then
ChangeStabChain( chain, options.base,
IsBound( options.reduced ) and options.reduced );
fi;
if LargestMovedPoint( Concatenation( GeneratorsOfGroup( G ),
gens ) ) <= 100 then
options := ShallowCopy( options );
options.base := BaseStabChain( chain );
for g in gens do
if SiftedPermutation( chain, g ) <> chain.identity then
StabChainStrong( chain, [ g ], options );
fi;
od;
else
o:=ValueOption("knownClosureSize");
if IsInt(o) then options.limit:=o;fi;
chain := ClosureRandomPermGroup( chain, gens, options );
fi;
newgens:=Concatenation(GeneratorsOfGroup(G),gens);
if Length(chain.generators)<=Length(newgens) then
if inpar then C := GroupStabChain( P, chain, true );
else C := GroupStabChain( chain ); fi;
else
if inpar then C := SubgroupNC(P,newgens);
else C := Group( newgens,One(G) ); fi;
SetStabChainMutable(C,chain);
fi;
SetStabChainOptions( C, rec( random := options.random ) );
UseSubsetRelation( C, G );
return C;
end );
InstallOtherMethod( ClosureGroup,"permgroup, elements, options",
true,
[ IsPermGroup, IsList and IsPermCollection, IsRecord ], 0,
DoClosurePrmGp);
InstallOtherMethod( ClosureGroup, "empty list",true,
[ IsPermGroup, IsList and IsEmpty ], 0,
ReturnFirst);
InstallMethod( ClosureGroup, "permgroup, element",true,
[ IsPermGroup, IsPerm ], 0,
function( G, g )
return DoClosurePrmGp( G, [ g ], rec() );
end );
InstallMethod( ClosureGroup, "permgroup, elements",true,
[ IsPermGroup, IsList and IsPermCollection ], 0,
function( G, gens )
return DoClosurePrmGp( G, gens, rec() );
end );
InstallOtherMethod( ClosureGroup, "permgroup, element, options",true,
[ IsPermGroup, IsPerm, IsRecord ], 0,
function( G, g, options )
return DoClosurePrmGp( G, [ g ], options );
end );
InstallOtherMethod( ClosureGroup, "permgroup, permgroup, options", true,
[ IsPermGroup, IsPermGroup, IsRecord ], 0,
function( G, H, options )
return DoClosurePrmGp( G, GeneratorsOfGroup( H ), options );
end );
InstallOtherMethod( ClosureGroup, "empty list and options",true,
[ IsPermGroup, IsList and IsEmpty, IsRecord ], 0,
ReturnFirst);
#############################################################################
##
#M NormalClosureOp( <G>, <U> ) . . . . . . . . . . . . . . . . in perm group
##
BindGlobal("DoNormalClosurePermGroup",function ( G, U )
local N, # normal closure of <U> in <G>, result
chain, # stabilizer chain for the result
rchain, # restored version of <chain>, for `VerifySGS'
options, # options record for stabilizer chain construction
gensG, # generators of the group <G>
genG, # one generator of the group <G>
gensN, # generators of the group <N>
genN, # one generator of the group <N>
cnj, # conjugated of a generator of <U>
random, k, # values measuring randomness of <chain>
param, missing, correct, result, i, one;
# get a set of monoid generators of <G>
gensG := GeneratorsOfGroup( G );
one:= One( G );
# make a copy of the group to be closed
N := SubgroupNC( G, GeneratorsOfGroup(U) );
UseIsomorphismRelation(U,N);
UseSubsetRelation(U,N);
SetStabChainMutable( N, StabChainMutable( U ) );
options := ShallowCopy( StabChainOptions( U ) );
if IsBound( options.random ) then random := options.random;
else random := 1000; fi;
options.temp := true;
# make list of conjugates to be added to N
repeat
gensN := [ ];
for i in [ 1 .. 10 ] do
genG := SCRRandomSubproduct( gensG, One( G ) );
cnj := SCRRandomSubproduct( Concatenation(
GeneratorsOfGroup( N ), gensN ), One( G ) ) ^ genG;
if not cnj in N then
Add( gensN, cnj );
fi;
od;
if not IsEmpty( gensN ) then
N := ClosureGroup( N, gensN, options );
fi;
until IsEmpty( gensN );
# Guarantee that all conjugates are in the normal closure: Loop over all
# generators of N
gensN := ShallowCopy( GeneratorsOfGroup( N ) );
for genN in gensN do
# loop over the generators of G
for genG in gensG do
# make sure that the conjugated element is in the closure
cnj := genN ^ genG;
if not cnj in N then
N := ClosureGroup( N, [ cnj ], options );
Add( gensN, cnj );
fi;
od;
od;
# Verify the stabilizer chain.
chain := StabChainMutable( N );
if not IsBound( chain.orbits ) then
if IsBound( chain.aux ) then
chain := SCRRestoredRecord( chain );
fi;
result := chain.identity;
elif random = 1000 then
missing := chain.missing;
correct := chain.correct;
rchain := SCRRestoredRecord( chain );
result := VerifySGS( rchain, missing, correct );
if not IsPerm(result) then
repeat
result := SCRStrongGenTest2(chain,[0,0,1,10/chain.diam,0,0]);
until result <> one;
fi;
chain := rchain;
else
k := First([0..14],x->(3/5)^x <= 1-random/1000);
if IsBound(options.knownBase) then
param := [k,4,0,0,0,0];
else
param := [QuoInt(k,2),4,QuoInt(k+1,2),4,50,5];
fi;
if options.random <= 200 then
param[2] := 2;
param[4] := 2;
fi;
result := SCRStrongGenTest( chain, param, chain.orbits,
chain.basesize, chain.base,
chain.correct, chain.missing );
if result = chain.identity and not chain.correct then
result := SCRStrongGenTest2( chain, param );
fi;
chain := SCRRestoredRecord( chain );
fi;
SetStabChainMutable( N, chain );
if result <> chain.identity then
N := ClosureGroup( N, [ result ] );
fi;
# give N the proper randomness
StabChainOptions(N).random:=random;
# return the normal closure
UseSubsetRelation( N, U );
return N;
end );
InstallMethod( NormalClosureOp, "subgroup of perm group",
IsIdenticalObj, [ IsPermGroup, IsPermGroup ], 0,
function(G,U)
# test applicability
if not IsSubset(G,U) then
TryNextMethod();
fi;
return DoNormalClosurePermGroup(G,U);
end);
#############################################################################
##
#M ConjugateGroup( <G>, <g> ) . . . . . . . . . . . . of permutation group
##
InstallMethod( ConjugateGroup, "<P>, <g>", true, [ IsPermGroup, IsPerm ], 0,
function( G, g )
local H, S;
H := GroupByGenerators( OnTuples( GeneratorsOfGroup( G ), g ), One( G ) );
if HasStabChainMutable(G) then
S := EmptyStabChain( [ ], One( H ) );
ConjugateStabChain( StabChainMutable( G ), S, g, g );
SetStabChainMutable( H, S );
elif HasSize(G) then
SetSize(H,Size(G));
fi;
UseIsomorphismRelation( G, H );
return H;
end );
#############################################################################
##
#M CommutatorSubgroup( <U>, <V> ) . . . . . . . . . . . . . for perm groups
##
InstallMethod( CommutatorSubgroup, "permgroups", IsIdenticalObj,
[ IsPermGroup, IsPermGroup ], 0,
function( U, V )
local C, # the commutator subgroup
CUV, # closure of U,V
doneCUV, # boolean; true if CUV is computed
u, # random subproduct of U.generators
v, # random subproduct of V.generators
comm, # commutator of u,v
list, # list of commutators
i; # loop variable
# [ <U>, <V> ] = normal closure of < [ <u>, <v> ] >.
C := TrivialSubgroup( U );
doneCUV := false;
# if there are lot of generators, use random subproducts
if Length( GeneratorsOfGroup( U ) ) *
Length( GeneratorsOfGroup( V ) ) > 10 then
repeat
list := [];
for i in [1..10] do
u := SCRRandomSubproduct( GeneratorsOfGroup( U ), One( U ) );
v := SCRRandomSubproduct( GeneratorsOfGroup( V ), One( V ) );
comm := Comm( u, v );
if not comm in C then
Add( list, comm ) ;
fi;
od;
if Length(list) > 0 then
C := ClosureGroup( C, list, rec( random := 0,
temp := true ) );
if not doneCUV then
CUV := ClosureGroup( U, V );
doneCUV := true;
fi;
# force closure test
StabChainOptions(C).random:=DefaultStabChainOptions.random;
C := DoNormalClosurePermGroup( CUV, C );
fi;
until IsEmpty( list );
fi;
# do the deterministic method; it will also check correctness
list := [];
for u in GeneratorsOfGroup( U ) do
for v in GeneratorsOfGroup( V ) do
comm := Comm( u, v );
if not comm in C then
Add( list, comm );
fi;
od;
od;
if not IsEmpty( list ) then
C := ClosureGroup( C, list, rec( random := 0,
temp := true ) );
if not doneCUV then
CUV := ClosureGroup( U, V );
doneCUV := true;
fi;
# force closure test
StabChainOptions(C).random:=DefaultStabChainOptions.random;
C := DoNormalClosurePermGroup( CUV, C );
fi;
return C;
end );
#############################################################################
##
#M DerivedSubgroup( <G> ) . . . . . . . . . . . . . . of permutation group
##
InstallMethod( DerivedSubgroup,"permgrps",true, [ IsPermGroup ], 0,
function( G )
local D, # derived subgroup of <G>, result
g, h, # random subproducts of generators
comm, # their commutator
list, # list of commutators
i,j; # loop variables
# find the subgroup generated by the commutators of the generators
D := TrivialSubgroup( G );
# if there are >4 generators, use random subproducts
if Length( GeneratorsOfGroup( G ) ) > 4 then
repeat
list := [];
for i in [1..10] do
g := SCRRandomSubproduct( GeneratorsOfGroup( G ), One( G ) );
h := SCRRandomSubproduct( GeneratorsOfGroup( G ), One( G ) );
comm := Comm( g, h );
if not comm in D then
Add( list, comm );
fi;
od;
if Length(list) > 0 then
D := ClosureGroup(D,list,rec( random := 0,
temp := true ) );
# force closure test
if IsBound(StabChainOptions(G).random) then
StabChainOptions(D).random:=StabChainOptions(G).random;
else
StabChainOptions(D).random:=DefaultStabChainOptions.random;
fi;
D := DoNormalClosurePermGroup( G, D );
fi;
until list = [];
fi;
# do the deterministic method; it will also check random result
list := [];
for i in [ 2 .. Length( GeneratorsOfGroup( G ) ) ] do
for j in [ 1 .. i - 1 ] do
comm := Comm( GeneratorsOfGroup( G )[i],
GeneratorsOfGroup( G )[j] );
if not comm in D then
Add( list, comm );
fi;
od;
od;
if not IsEmpty( list ) then
D := ClosureGroup(D,list,rec( random := 0,
temp := true ) );
# give D a proper randomness
if IsBound(StabChainOptions(G).random) then
StabChainOptions(D).random:=StabChainOptions(G).random;
else
StabChainOptions(D).random:=DefaultStabChainOptions.random;
fi;
D := DoNormalClosurePermGroup( G, D );
fi;
if HasIsNaturalSymmetricGroup(G) and Size(D)<Size(G) then IsNaturalAlternatingGroup(D);fi;
return D;
end );
#############################################################################
##
#M IsSimpleGroup( <G> ) . . . . . . . test if a permutation group is simple
##
## This is a most interesting function. It tests whether a permutation
## group is simple by testing whether the group is perfect and then only
## looking at the size of the group and the degree of a primitive operation.
## Basically it uses the O'Nan--Scott theorem, which gives a pretty clear
## description of perfect primitive groups. This algorithm is described in
## William M. Kantor,
## Finding Composition Factors of Permutation Groups of Degree $n\leq 10^6$,
## J. Symbolic Computation, 12:517--526, 1991.
##
InstallMethod( IsSimpleGroup,"for permgrp", true, [ IsPermGroup ], 0,
function ( G )
local D, # operation domain of <G>
hom, # transitive constituent or blocks homomorphism
d, # degree of <G>
n, m, # $d = n^m$
simple, # list of orders of simple groups
transperf, # list of orders of transitive perfect groups
s, t; # loop variables
# if <G> is the trivial group, it is not simple
if IsTrivial( G ) then
return false;
fi;
# first find a transitive representation for <G>
D := Orbit( G, SmallestMovedPoint( G ) );
if not IsEqualSet( MovedPoints( G ), D ) then
hom := ActionHomomorphism( G, D,"surjective" );
if Size( G ) <> Size( Image( hom ) ) then
return false;
fi;
G := Image( hom );
fi;
# next find a primitive representation for <G>
D := Blocks( G, MovedPoints( G ) );
while Length( D ) <> 1 do
hom := ActionHomomorphism( G, D, OnSets,"surjective" );
if Size( G ) <> Size( Image( hom ) ) then
return false;
fi;
G := Image( hom );
D := Blocks( G, MovedPoints( G ) );
od;
# compute the degree $d$ and express it as $d = n^m$
D := MovedPoints( G );
d := Length( D );
n := SmallestRootInt( d );
m := LogInt( d, n );
if 10^6 < d then
Error("cannot decide whether <G> is simple or not");
fi;
# if $G = C_p$, it is simple
if IsPrimeInt( Size( G ) ) then
return true;
# if $G = A_d$, it is simple (unless $d < 5$)
elif IsNaturalAlternatingGroup(G) then
return 5 <= d;
# if $G = S_d$, it is not simple (except $S_2$)
elif IsNaturalSymmetricGroup(G) then
return 2 = d;
# if $G$ is not perfect, it is not simple (unless $G = C_p$, see above)
elif Size( DerivedSubgroup( G ) ) < Size( G ) then
return false;
# if $\|G\| = d^2$, it is not simple (Kantor's Lemma 4)
elif Size( G ) = d ^ 2 then
return false;
# if $d$ is a prime, <G> is simple
elif IsPrimeInt( d ) then
return true;
# if $G = U(4,2)$, it is simple (operation on 27 points)
elif d = 27 and Size( G ) = 25920 then
return true;
# if $G = PSL(n,q)$, it is simple (operations on prime power points)
elif ( (d = 8 and Size(G) = (7^3-7)/2 ) # PSL(2,7)
or (d = 9 and Size(G) = (8^3-8) ) # PSL(2,8)
or (d = 32 and Size(G) = (31^3-31)/2 ) # PSL(2,31)
or (d = 121 and Size(G) = 237783237120) # PSL(5,3)
or (d = 128 and Size(G) = (127^3-127)/2 ) # PSL(2,127)
or (d = 8192 and Size(G) = (8191^3-8191)/2 ) # PSL(2,8191)
or (d = 131072 and Size(G) = (131071^3-131071)/2) # PSL(2,131071)
or (d = 524288 and Size(G) = (524287^3-524287)/2)) # PSL(2,524287)
and IsTransitive( Stabilizer( G, D[1] ), Difference( D, [ D[1] ] ) )
then
return true;
# if $d$ is a prime power, <G> is not simple (except the cases above)
elif IsPrimePowerInt( d ) then
return false;
# if we don't have at least an $A_5$ acting on the top, <G> is simple
elif m < 5 then
return true;
# otherwise we must check for some special cases
else
# orders of simple subgroups of $S_n$ with primitive normalizer
simple := [ ,,,,,
[60,360],,,, # 5: A(5), A(6)
[60,360,1814400],, # 10: A(5), A(6), A(10)
[660,7920,95040,239500800],, # 12: PSL(2,11), M(11), M(12), A(12)
[1092,43589145600], # 14: PSL(2,13), A(14)
[360,2520,20160,653837184000] # 15: A(6), A(7), A(8), A(15)
];
# orders of transitive perfect subgroups of $S_m$
transperf := [ ,,,,
[60], # 5: A(5)
[60,360], # 6: A(5), A(6)
[168,2520], # 7: PSL(3,2), A(7)
[168,8168,20160] # 8: PSL(3,2), AGL(3,2), A(8)
];
# test the special cases (Kantor's Lemma 3)
for s in simple[n] do
for t in transperf[m] do
if Size( G ) mod (t * s^m) = 0
and (((t * (2*s)^m) mod Size( G ) = 0 and s <> 360)
or ((t * (4*s)^m) mod Size( G ) = 0 and s = 360))
then
return false;
fi;
od;
od;
# otherwise <G> is simple
return true;
fi;
end );
#############################################################################
##
#M IsSolvableGroup( <G> ) . . . . . . . . . . . . . . . . solvability test
##
# fast test for finding iteratively nontrivial commutators in a permutation group.
# if this goes on too long the group may not be solvable. This can be much faster
# for large degree groups than to use the full pcgs machinery (which uses the same
# idea but builds a pcgs on the way).
# The function returns `true` if the group has been proved to be *non*solvable.
BindGlobal("QuickUnsolvabilityTestPerm",function(G)
local som,elvth,fct,gens,new,l,i,j,a,b,bound;
# a few moved points
som:=MovedPoints(G);
if Length(som) = 0 then SetIsTrivial(G,true); return fail; fi;
bound:=Int(LogInt(Length(som)^5,3)/2); #Dixon Bound
if Length(som)>100 then
som:=som{List([1..100],x->Random(1, Length(som)))};
fi;
elvth:=One(G);
fct:=function(G)
elvth:=elvth*Product([1..Random(1,5)],x->Random(GeneratorsOfGroup(G))^Random(-3,3));
return elvth;
end;
gens:=GeneratorsOfGroup(G);
# find one nontrivial commutator
new:=fail;
i:=1;
while i<=Length(gens) and new=fail do
j:=i+1;
while j<=Length(gens) and new=fail do
if ForAny(som,x->(x^gens[i])^gens[j]<>(x^gens[j])^gens[i]) then
new:=Comm(gens[i],gens[j]);
fi;
j:=j+1;
od;
i:=i+1;
od;
if new=fail then return fail;fi;
# now take commutators with conjugates to get down
i:=1;
repeat
gens:=new;
j:=1;
new:=fail;
l:=[gens];
while j<=10 and new=fail do
a:=Random(l)^fct(G);
b:=First(l,b->ForAny(som,x->(x^a)^b<>(x^b)^a));
if b<>fail then
new:=Comm(a,b);
else
Add(l,a);
fi;
j:=j+1;
od;
if new=fail then
return fail;
fi;
i:=i+1;
until i>bound;
return true;
end);
InstallMethod( IsSolvableGroup,"for permgrp", true, [ IsPermGroup ], 0,
function(G)
local pcgs;
if QuickUnsolvabilityTestPerm(G)=true then return false;fi;
pcgs:=TryPcgsPermGroup( G, false, false, true );
if IsPcgs(pcgs) then
SetIndicesEANormalSteps(pcgs,pcgs!.permpcgsNormalSteps);
SetIsPcgsElementaryAbelianSeries(pcgs,true);
if not HasPcgs(G) then
SetPcgs(G,pcgs);
fi;
if not HasPcgsElementaryAbelianSeries(G) then
SetPcgsElementaryAbelianSeries(G,pcgs);
fi;
return true;
else
return false;
fi;
end);
#############################################################################
##
#M IsNilpotentGroup( <G> ) . . . . . . . . . . . . . . . . . nilpotency test
##
InstallMethod( IsNilpotentGroup,"for permgrp", true, [ IsPermGroup ], 0,
G -> IsPcgs(PcgsCentralSeries(G) ) );
#############################################################################
##
#M PcgsCentralSeries( <G> )
##
InstallOtherMethod( PcgsCentralSeries,"for permgrp", true, [ IsPermGroup ], 0,
function(G)
local pcgs;
pcgs:=TryPcgsPermGroup( G, true, false, false );
if IsPcgs(pcgs) then
SetIndicesCentralNormalSteps(pcgs,pcgs!.permpcgsNormalSteps);
SetIsPcgsCentralSeries(pcgs,true);
if not HasPcgs(G) then
SetPcgs(G,pcgs);
fi;
fi;
return pcgs;
end);
#############################################################################
##
#M PcgsPCentralSeriesPGroup( <G> )
##
InstallOtherMethod( PcgsPCentralSeriesPGroup,"for permgrp", true, [ IsPermGroup ], 0,
function(G)
local pcgs;
pcgs:=TryPcgsPermGroup( G, true, true, Factors(Size(G))[1] );
if IsPcgs(pcgs) then
SetIndicesPCentralNormalStepsPGroup(pcgs,pcgs!.permpcgsNormalSteps);
SetIsPcgsPCentralSeriesPGroup(pcgs,true);
fi;
if IsPcgs(pcgs) and not HasPcgs(G) then
SetPcgs(G,pcgs);
fi;
return pcgs;
end);
#T AH, 3/26/07: This method triggers a bug with indices. It is not clear
#T form the description why TryPcgs... should also give a drived series. Thus
#T disabled.
# #############################################################################
# ##
# #M DerivedSeriesOfGroup( <G> ) . . . . . . . . . . . . . . . derived series
# ##
# InstallMethod( DerivedSeriesOfGroup,"for permgrp", true, [ IsPermGroup ], 0,
# function( G )
# local pcgs, series;
#
# if (not DefaultStabChainOptions.tryPcgs
# or ( HasIsSolvableGroup( G ) and not IsSolvableGroup( G ) ))
# and not (HasIsSolvableGroup(G) and IsSolvableGroup(G)) then
# TryNextMethod();
# fi;
#
# pcgs := TryPcgsPermGroup( G, false, true, false );
# if not IsPcgs( pcgs ) then
# TryNextMethod();
# fi;
# SetIndicesEANormalSteps(pcgs,pcgs!.permpcgsNormalSteps);
# series := EANormalSeriesByPcgs( pcgs );
# if not HasDerivedSubgroup( G ) then
# if Length( series ) > 1 then SetDerivedSubgroup( G, series[ 2 ] );
# else SetDerivedSubgroup( G, G ); fi;
# fi;
# return series;
# end );
#############################################################################
##
#M LowerCentralSeriesOfGroup( <G> ) . . . . . . . . . lower central series
##
InstallMethod( LowerCentralSeriesOfGroup,"for permgrp", true, [ IsPermGroup ], 0,
function( G )
local pcgs, series;
if IsTrivial(G) then
return [G];
fi;
if not DefaultStabChainOptions.tryPcgs
or HasIsNilpotentGroup( G ) and not IsNilpotentGroup( G )
and not (HasIsNilpotentGroup(G) and IsNilpotentGroup(G)) then
TryNextMethod();
fi;
pcgs := TryPcgsPermGroup( G, true, true, false );
if not IsPcgs( pcgs ) then
TryNextMethod();
fi;
SetIndicesCentralNormalSteps(pcgs,pcgs!.permpcgsNormalSteps);
series := CentralNormalSeriesByPcgs( pcgs );
if not HasDerivedSubgroup( G ) then
if Length( series ) > 1 then SetDerivedSubgroup( G, series[ 2 ] );
else SetDerivedSubgroup( G, G ); fi;
fi;
return series;
end );
InstallOtherMethod( ElementaryAbelianSeries, "perm group", true,
[ IsPermGroup and IsFinite],
# we want this to come *before* the method for Pcgs computable groups
RankFilter(IsPermGroup and CanEasilyComputePcgs and IsFinite)
-RankFilter(IsPermGroup and IsFinite),
function(G)
local pcgs;
pcgs:=PcgsElementaryAbelianSeries(G);
if not IsPcgs(pcgs) then
TryNextMethod();
fi;
return EANormalSeriesByPcgs(pcgs);
end);
#InstallOtherMethod( ElementaryAbelianSeries, fam -> IsIdenticalObj
# ( fam, CollectionsFamily( CollectionsFamily( PermutationsFamily ) ) ),
# [ IsList ], 0,
# function( list )
# local pcgs;
#
# if IsEmpty( list )
# or not DefaultStabChainOptions.tryPcgs
# or ( HasIsSolvableGroup( list[ 1 ] )
# and not IsSolvableGroup( list[ 1 ] ) ) then
# TryNextMethod();
## fi;
#
## pcgs := TryPcgsPermGroup( list, false, false, true );
# if not IsPcgs( pcgs ) then return fail;
# else return ElementaryAbelianSeries( pcgs ); fi;
#end );
#############################################################################
##
#M PCentralSeries( <G>, <p> ) . . . . . . . . . . . . . . p-central series
##
InstallMethod( PCentralSeriesOp,"for permgrp", true, [ IsPermGroup, IsPosInt ], 0,
function( G, p )
local pcgs;
if Length(Factors(Size(G)))>1 then
TryNextMethod();
fi;
pcgs:=PcgsPCentralSeriesPGroup(G);
if not IsPcgs(pcgs) then
TryNextMethod();
fi;
return PCentralNormalSeriesByPcgsPGroup(pcgs);
end);
#############################################################################
##
#M SylowSubgroup( <G>, <p> ) . . . . . . . . . . . . . . Sylow $p$-subgroup
##
InstallMethod( SylowSubgroupOp,"permutation groups", true,
[ IsPermGroup, IsPosInt ], 0,
function( G, p )
local S;
if not HasIsSolvableGroup(G) and IsSolvableGroup(G) then
# enforce solvable methods if we detected anew that the group is
# solvable.
return SylowSubgroupOp(G,p);
fi;
S := SylowSubgroupPermGroup( G, p );
S := GroupStabChain( G, StabChainMutable( S ) );
SetIsNilpotentGroup( S, true );
if Size(S) > 1 then
SetIsPGroup( S, true );
SetPrimePGroup( S, p );
SetHallSubgroup(G, [p], S);
fi;
return S;
end );
InstallGlobalFunction( SylowSubgroupPermGroup, function( G, p )
local S, # <p>-Sylow subgroup of <G>, result
q, # largest power of <p> dividing the size of <G>
D, # domain of operation of <G>
O, # one orbit of <G> in this domain
B, # blocks of the operation of <G> on <D>
f, # action homomorphism of <G> on <O> or <B>
T, # <p>-Sylow subgroup in the image of <f>
g, g2, # one <p> element of <G>
C, C2; # centralizer of <g> in <G>
# get the size of the <p>-Sylow subgroup
q := 1; while Size( G ) mod (q * p) = 0 do q := q * p; od;
# handle trivial subgroup
if q = 1 then
return TrivialSubgroup( G );
fi;
# go down in stabilizers as long as possible
S := StabChainMutable( G );
while Length( S.orbit ) mod p <> 0 do
S := S.stabilizer;
od;
G := GroupStabChain( G, S, true );
# handle full group
if q = Size( G ) then
return G;
fi;
# handle <p>-Sylow subgroups of size <p>
if q = p then
repeat g := Random( G ); until Order( g ) mod p = 0;
g := g ^ (Order( g ) / p);
return SubgroupNC( G, [ g ] );
fi;
# if the group is not transitive work with the transitive constituents
D := MovedPoints( G );
if not IsTransitive( G, D ) then
Info(InfoGroup,1,"PermSylow: transitive");
S := G;
D := ShallowCopy( D );
while q < Size( S ) do
O := Orbit( S, D[1] );
f := ActionHomomorphism( S, O,"surjective" );
T := SylowSubgroupPermGroup( Range( f ), p );
S := PreImagesSet( f, T );
SubtractSet( D, O );
od;
return S;
fi;
# if the group is not primitive work in the image first
B := Blocks( G, D );
if Length( B ) <> 1 then
Info(InfoGroup,1,"PermSylow: blocks");
f := ActionHomomorphism( G, B, OnSets,"surjective" );
T := SylowSubgroupPermGroup( Range( f ), p );
if Size( T ) < Size( Range( f ) ) then
T := PreImagesSet( f, T );
S := SylowSubgroupPermGroup( T , p) ;
return S;
fi;
fi;
# find a <p> element whose centralizer contains a full <p>-Sylow subgroup
Info(InfoGroup,1,"PermSylow: element");
repeat g := Random( G ); until Order( g ) mod p = 0;
g := g ^ (Order( g ) / p);
C := Centralizer( G, g );
while GcdInt( q, Size( C ) ) < q do
repeat g2 := Random( C ); until Order( g2 ) mod p = 0;
g2 := g2 ^ (Order( g2 ) / p);
C2 := Centralizer( G, g2 );
if GcdInt( q, Size( C ) ) < GcdInt( q, Size( C2 ) ) then
C := C2; g := g2;
fi;
od;
# the centralizer acts on the cycles of the <p> element
B := List( Cycles( g, D ), Set );
Info(InfoGroup,1,"PermSylow: cycleaction");
f := ActionHomomorphism( C, B, OnSets,"surjective" );
T := SylowSubgroupPermGroup( Range( f ), p );
S := PreImagesSet( f, T );
return S;
end );
#############################################################################
##
#M Socle( <G> ) . . . . . . . . . . . socle of primitive permutation group
##
InstallMethod( Socle,"test primitive", true, [ IsPermGroup ], 0,
function( G )
local Omega, deg, coll, ds, L, z, ord,
p, i;
Omega := MovedPoints( G );
if not IsPrimitive( G, Omega ) then
TryNextMethod();
fi;
# Affine groups first.
L := Earns( G, Omega );
if L <> [] then
return L[1];
fi;
deg := Length( Omega );
if deg >= 6103515625 then
TryNextMethod();
# the normal closure actually seems to be faster than derived series, so
# this is not really a shortcut
#elif deg < 12960000 then
# shortcut := true;
# if deg >= 3125 then
# coll := Collected( Factors( deg ) );
# d := Gcd( List( coll, c -> c[ 2 ] ) );
# if d mod 5 = 0 then
# m := 1;
# for c in coll do
# m := m * c[ 1 ] ^ ( c[ 2 ] / d );
# od;
# if m >= 5 then
# shortcut := false;
# fi;
# fi;
# fi;
# if shortcut then
# ds := DerivedSeriesOfGroup( G );
# return Last(ds);
# fi;
fi;
coll := Collected( Factors(Integers, Size( G ) ) );
if deg < 78125 then
p := Last(coll)[ 1 ];
else
i := Length( coll );
while coll[ i ][ 2 ] = 1 do
i := i - 1;
od;
p := coll[ i ][ 1 ];
fi;
repeat
repeat
z := Random( G );
ord := Order( z );
until ord mod p = 0;
z := z ^ ( ord / p );
until Index( G, Centralizer( G, z ) ) mod p <> 0;
# immediately add conjugate as this will be needed anyhow and seems to
# speed up the closure process
L := NormalClosure( G, SubgroupNC( G, [ z,z^Random(G) ] ) );
if deg >= 78125 then
ds := DerivedSeriesOfGroup( L );
L := Last(ds);
fi;
if IsSemiRegular( L, Omega ) then
L := ClosureSubgroup( L, Centralizer( G, L ) );
fi;
return L;
end );
#############################################################################
##
#M FrattiniSubgroup( <G> ) . . . . . . . . . . . . for permutation p-groups
##
InstallMethod( FrattiniSubgroup,"for permgrp", true, [ IsPermGroup ], 0,
function( G )
local p, l, k, i, j;
if not IsPGroup( G ) then
TryNextMethod();
elif IsTrivial( G ) then
return G;
fi;
p := PrimePGroup( G );
l := GeneratorsOfGroup( G );
k := [ l[1]^p ];
for i in [ 2 .. Length(l) ] do
for j in [ 1 .. i-1 ] do
Add(k, Comm(l[i], l[j]));
od;
Add(k, l[i]^p);
od;
return SolvableNormalClosurePermGroup( G, k );
end );
#############################################################################
##
#M ApproximateSuborbitsStabilizerPermGroup(<G>,<pnt>) . . . approximation of
## the orbits of Stab_G(pnt) on all points of the orbit pnt^G. (As not
## all schreier generators are used, the results may be the orbits of a
## subgroup.)
##
InstallGlobalFunction( ApproximateSuborbitsStabilizerPermGroup,
function(G,punkt)
local one, orbit, trans, gen, pnt, img, i, changed,
processStabGen, currPt,currGen, gens,Ggens;
one:= One( G );
if HasStabChainMutable( G ) and punkt in StabChainMutable(G).orbit then
# if we already have a stab chain and bother computing the proper
# stabilizer, a trivial orbit algorithm seems best.
return Concatenation([[punkt]],
OrbitsDomain(Stabilizer(G,punkt),
Difference(MovedPoints(G),[punkt])));
# orbit:=Difference(StabChainMutable(G).orbit,[punkt]);
# stab:=Stabilizer(G,punkt));
# # catch trivial case
# if Length(stab)=0 then
# stab:=[ one ];
# fi;
# stgp:=1;
#
# processStabGen:=function()
# stgp:=stgp+1;
# if stgp>Length(stab) then
# StabGenAvailable:=false;
# fi;
# return stab[stgp-1];
# end;
else
# compute orbit and transversal
Ggens:=GeneratorsOfGroup(G);
orbit := [ punkt ];
trans := [];
trans[ punkt ] := one;
for pnt in orbit do
for gen in Ggens do
img:=pnt^gen;
if not IsBound( trans[ img ] ) then
Add( orbit, img );
trans[img ] := gen;
fi;
od;
od;
fi;
currPt:=1;
currGen:=0;
processStabGen:=function()
local punkt,img,a,b,gen;
if Random(1,2)=1 then
# next one
currGen:=currGen+1;
if currGen>Length(Ggens) then
currGen:=1;
currPt:=currPt+1;
if currPt>Length(orbit) then
return ();
fi;
fi;
a:=currPt;
b:=currGen;
else
a:=Random(1, Length(orbit));
b:=Random(1, Length(Ggens));
fi;
# map a with b
img:=orbit[a]^Ggens[b];
if img=orbit[a] then
return ();
else
punkt:=orbit[a];
gen:=Ggens[b];
while punkt<>orbit[1] do
gen:=trans[punkt]*gen;
punkt:=punkt/trans[punkt];
od;
if orbit[1]^gen<>img then Error("EHEH"); fi;
punkt:=img;
while punkt<>orbit[1] do
gen:=gen/trans[punkt];
punkt:=punkt/trans[punkt];
od;
if orbit[1]^gen<>orbit[1] then Error("UHEH"); fi;
fi;
return gen;
end;
# as the first approximation take a subgroup generated by four nontrivial
# Schreier generators
changed:=0;
gens:=[];
while changed<=3 and currPt<=Length(orbit) do
gen:=();
while IsOne(gen) and currPt<=Length(orbit) do
gen:=processStabGen();
od;
Add(gens,gen);
changed:=changed+1;
od;
if Length(gens)=0 then
orbit:=List(orbit,i->[i]);
else
orbit:=OrbitsPerms(gens,orbit);
fi;
# todo: further fuse
return orbit;
end );
#############################################################################
##
#M AllBlocks . . . Representatives of all block systems
##
InstallMethod( AllBlocks, "generic", [ IsPermGroup ],
function(g)
local gens, one, dom,DoBlocks,pool,subo;
DoBlocks:=function(b)
local bl,bld,i,t,n;
# find representatives of all potential suborbits
bld:=List(Filtered(subo,i->not ForAny(b,j->j in i)),i->i[1]);
bl:=[];
if not IsPrime(Length(dom)/Length(b)) then
for i in bld do
t:=Union(b,[i]);
n:=Blocks(g,dom,t);
if Length(n)>1 and #ok durch pool:ForAll(Difference(n[1],t),j->j>i) and
not n[1] in pool then
t:=n[1];
Add(pool,t);
bl:=Concatenation(bl,[t],DoBlocks(t));
fi;
od;
fi;
return bl;
end;
one:= One( g );
gens:= Filtered( GeneratorsOfGroup(g), i -> i <> one );
if Length(gens)=0 then return []; fi;
subo:=ApproximateSuborbitsStabilizerPermGroup(g,
Minimum(List(gens,SmallestMovedPoint)));
dom:=Union(subo);
if not IsTransitive(g,dom) then
Error("AllBlocks, must be transitive");
fi;
pool:=[];
return DoBlocks(subo[1]);
end);
#############################################################################
##
#F SignPermGroup
##
InstallGlobalFunction( SignPermGroup, function(g)
if ForAll(GeneratorsOfGroup(g),i->SignPerm(i)=1) then
return 1;
else
return -1;
fi;
end );
BindGlobal( "CreateAllCycleStructures", function(n)
local i,j,l,m;
l:=[];
for i in Partitions(n) do
m:=[];
for j in i do
if j>1 then
if IsBound(m[j-1]) then
m[j-1]:=m[j-1]+1;
else
m[j-1]:=1;
fi;
fi;
od;
Add(l,m);
od;
return l;
end );
#############################################################################
##
#F CycleStructuresGroup(G) list of all cyclestructures occ. in a perm group
##
InstallGlobalFunction( CycleStructuresGroup, function(g)
local l,m,i, one;
l:=CreateAllCycleStructures(Length(MovedPoints(g)));
m:=List([1..Length(l)-1],i->0);
one:= One( g );
for i in ConjugacyClasses(g) do
if Representative(i) <> one then
m[Position(l,CycleStructurePerm(Representative(i)))-1]:=1;
fi;
od;
return m;
end );
# return e is a^e=b or false otherwise
InstallGlobalFunction("LogPerm",
function(a,b)
local oa,ob,q,aa,m,e,tst,s,c,p,g;
oa:=Order(a);
ob:=Order(b);
if oa mod ob<>0 then Info(InfoGroup,20,"1"); return false;fi;
q:=oa/ob;
aa:=a^q;
m:=1; # order modulo which power is OK so far
e:=1;
while m<ob do
tst:=aa^e/b;
s:=SmallestMovedPoint(tst); # a point on which there might be a difference
if s=infinity then # found it
return q*e mod oa;
fi;
c:=Cycle(aa,s);
g:=Gcd(m,Length(c));
p:=Position(c,s^b);
if p=fail or (e mod g<>(p-1) mod g) then return false;fi;
e:=ChineseRem([m,Length(c)],[e,p-1]);
m:=Lcm(m,Length(c));
od;
if aa^e<>b then return false;fi;
return q*e mod oa;
end);
#############################################################################
##
#M SmallGeneratingSet(<G>)
##
InstallMethod(SmallGeneratingSet,"random and generators subset, randsims",true,
[IsPermGroup],0,
function (G)
local i, j, U, gens,a,mintry,orb,orp,isok;
gens := ShallowCopy(Set(GeneratorsOfGroup(G)));
# remove obvious redundancies...
# sort elements by descending order; trivial permutations
# at the end
SortBy(gens, g -> -Order(g));
i := 1;
while i < Length(gens) do
j := i + 1;
while j <= Length(gens) do
a:=LogPerm(gens[i], gens[j]);
if a = false then
j := j + 1;
else
Remove(gens, j);
fi;
od;
i := i + 1;
od;
if Length(gens)<=Length(AbelianInvariants(G))+2 then
return gens;
fi;
# try pc methods. The solvability test should not exceed cost, nor
# the number of points.
if #Length(MovedPoints(G))<50000 and
#((HasIsSolvableGroup(G) and IsSolvableGroup(G)) or IsAbelian(G))
IsSolvableGroup(G)
and Length(gens)>3 then
return MinimalGeneratingSet(G);
fi;
# store orbit data
orb:=Set(Orbits(G,MovedPoints(G)),Set);
# longer primitive orbits
orp:=Filtered([1..Length(orb)],x->Length(orb[x])>100 and IsPrimitive(Action(G,orb[x])));
isok:=function(U)
if Length(MovedPoints(G))>Length(MovedPoints(U)) or
Length(orb)>Length(Orbits(U,MovedPoints(U))) or
ForAny(orp,x->not IsPrimitive(U,orb[x])) then
return false;
fi;
StabChainOptions(U).random:=100; # randomized size
return Size(U)=Size(G);
end;
mintry:=2;
if Length(gens)>2 then
# lower bound on what to accept -- use index of G' instead of full
# structure of G/G', as the latter is more expensive.
mintry:=Maximum(List(Collected(Factors(Size(G)/Size(DerivedSubgroup(G)))),x->x[2]));
mintry:=Maximum(mintry,2);
if mintry=Length(gens) then return gens;fi;
i:=Maximum(2,mintry);
while i<=mintry+1 and i<Length(gens) do
# try to find a small generating system by random search
j:=1;
while j<=5 and i<Length(gens) do
U:=Subgroup(G,List([1..i],j->Random(G)));
if isok(U) then
gens:=Set(GeneratorsOfGroup(U));
fi;
j:=j+1;
od;
i:=i+1;
od;
fi;
# try to delete generators
i := 1;
while i <= Length(gens) and Length(gens)>mintry do
# random did not improve much, try removing generators one by one
U:=Subgroup(G,gens{Difference([1..Length(gens)],[i])});
if isok(U) then
gens:=Set(GeneratorsOfGroup(U));
else
i:=i+1;
fi;
od;
return gens;
end);
#############################################################################
##
#M GeneratorsSmallest(<G>) . . . . . . . . . . . . . for permutation groups
##
DeclareGlobalFunction( "GeneratorsSmallestStab" );
InstallGlobalFunction( GeneratorsSmallestStab, function ( S )
local gens, # smallest generating system of <S>, result
gen, # one generator in <gens>
orb, # basic orbit of <S>
pnt, # one point in <orb>
T, # stabilizer in <S>
o2,img,oo,og; # private orbit algorithm
# handle the anchor case
if Length(S.generators) = 0 then
return [];
fi;
# now get the smallest generating system of the stabilizer
gens := GeneratorsSmallestStab( S.stabilizer );
# get the sorted orbit (the basepoint will be the first point)
orb := Set( S.orbit );
SubtractSet( orb, [S.orbit[1]] );
# handle the other points in the orbit
while Length(orb) <> 0 do
# take the smallest point (coset) and one representative
pnt := orb[1];
gen := S.identity;
while S.orbit[1] ^ gen <> pnt do
gen := LeftQuotient( S.transversal[ pnt / gen ], gen );
od;
# the next generator is the smallest element in this coset
T := S.stabilizer;
while Length(T.generators) <> 0 do
pnt := Minimum( OnTuples( T.orbit, gen ) );
while T.orbit[1] ^ gen <> pnt do
gen := LeftQuotient( T.transversal[ pnt / gen ], gen );
od;
T := T.stabilizer;
od;
# add this generator to the generators list and reduce orbit
Add( gens, gen );
#SubtractSet( orb,
# Orbit( GroupByGenerators( gens, S.identity ), S.orbit[1] ) );
# here we want to be really fast, so use a private orbit algorithm
o2:=[S.orbit[1]];
RemoveSet(orb,S.orbit[1]);
for oo in o2 do
for og in gens do
img:=oo^og;
if img in orb then
Add(o2,img);
RemoveSet(orb,img);
fi;
od;
od;
od;
# return the smallest generating system
return gens;
end );
InstallMethod(GeneratorsSmallest,"perm group via minimal stab chain",
[IsPermGroup],
function(G)
# call the recursive function to do the work
--> --------------------
--> maximum size reached
--> --------------------
[ Normaldarstellung0.15Diashow
(vorverarbeitet)
]
|