############################################################################# ## ## This file is part of GAP, a system for computational discrete algebra. ## This file's authors include Ákos Seress. ## ## 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 functions for a random Schreier-Sims algorithm ## with verification. ##
############################################################################# ## #F StabChainRandomPermGroup( <gens>, <id>, <opts> ) . random Schreier-Sims ## ## The method consists of 2 phases: a heuristic construction and ## either a deterministic or two random checking phases. ## In the random checking phases, we take random elements of ## created set, multiply by random subproduct of generators, and sift down ## by dividing with the appropriate coset representatives. The stabchain is ## correct when all siftees are (). In the first checking phase, all ## computations are carried out with words in strong generators and siftees ## are checked by plugging in random points in words; in the second checking ## phase, permutations are multiplied. ## ## During the construction, we create new records for stabilizers: ## S.aux = set of strong generators for S, temporarily created during the ## construction. On the other hand, S.generators contains a strong ## generating set for S which would have been created by the ## deterministic method ## S.treegen = elements of S.aux used in S.transversal ## S.treegeninv = inverses of S.treegen; also used in S.transversal, ## and they are also elements of S.aux ## S.treedepth = depth of Schreier tree of S ## S.diam = sum of treedepths in stabilizer chain of S ## ## After the stabilizer chain is ready, the extra records are deleted. ## Transversals are rebuilt using the generators in the .generators records. ##
InstallGlobalFunction( StabChainRandomPermGroup, function( gens, id, options)
local S, # stabilizer chain
degree, # degree of S
givenbase,# list of points from which first base points should come
correct, # boolean; true if a correct base is given
size, # size of <G> as constructed
order, # size of G if given in input
limit, # upper bound on Size(G) given in input
orbits, # list of orbits of G
orbits2, # list of orbits of G
k, # number of pairs of generators checked
param, # list of parameters guiding number of repetitions # in random constructions
where, # list indicating which orbit contains points in domain
basesize, # list; i^th entry = number of base points in orbits[i]
i,j,
ready, # boolean; true if stabilizer chain ready
warning, # used at warning if given and computed size differ
new, # list of permutations to be added to stab. chain
result, # output of checking phase; nontrivial if stabilizer # chain is incorrect
base, # ordering of domain from which base points are taken
missing, # if a correct base was provided by input, missing # contains those points of it which are not in # constructed base
T; # a stabilizer chain containing the usual records
S:= rec( generators := ShallowCopy( gens ), identity := id ); if options.random = 1000then #case of deterministic computation with known size
k := 1; else
k:=First([1..14],x->(3/5)^x<1-options.random/1000); fi;
degree := LargestMovedPoint( S.generators );
if IsBound( options.knownBase) and
Length(options.knownBase)<4+LogInt(degree,10) then
param:=[k,4,0,0,0,0]; else
param:=[QuoInt(k,2),4,QuoInt(k+1,2),4,50,5];
options:=ShallowCopy(options);
Unbind(options.knownBase); fi; if options.random <= 200then
param[2] := 2;
param[4] := 2; fi;
#param[1] = number of pairs of random subproducts from generators in # first checking phase #param[2] = (number of random elements from created set)/S.diam # in first checking phase #param[3] = number of pairs of random subproducts from generators in # second checking phase #param[4] = (number of random elements from created set)/S.diam # in second checking phase #param[5] = maximum size of orbits in which we evaluate words on all # points of orbit #param[6] = minimum number of random points from orbit to plug in to check # whether given word is identity on orbit
# prepare input of construction if IsBound(options.base) then
givenbase := options.base; else
givenbase := []; fi;
if IsBound(options.size) then
order := options.size;
warning := 0;
limit := 0; else
order := 0; if IsBound(options.limit) then
limit := options.limit; else
limit := 0; fi; fi;
if IsBound( options.knownBase ) then
correct := true; else
correct := false; fi;
if correct then # if correct base was given as input, no need for orbit information
base:=Concatenation(givenbase,Difference(options.knownBase,givenbase));
missing := Set(options.knownBase);
basesize := [];
where := [];
orbits := []; else # create ordering of domain used in choosing base points and # compute orbit information
base:=Concatenation(givenbase,Difference([1..degree],givenbase));
missing:=[];
orbits2:=OrbitsPerms(S.generators,[1..degree]); #throw away one-element orbits
orbits:=[];
j:=0; for i in [1..Length(orbits2)] do if Length(orbits2[i]) >1then
j:=j+1; orbits[j]:= orbits2[i]; fi;
od;
basesize:=[];
where:=[]; for i in [1..Length(orbits)] do
basesize[i]:=0; for j in [1..Length(orbits[i])] do
where[orbits[i][j]]:=i;
od;
od; # temporary solution to speed up of handling of lots of small orbits # until compiler if Length(orbits) > degree/40then
param[1] := 0;
param[3] := k; fi; fi;
ready:=false;
new:=S.generators;
while not ready do
SCRMakeStabStrong
(S,new,param,orbits,where,basesize,base,correct,missing,true); # last parameter of input is true if function called for original G # in recursive calls on stabilizers, it is false # reason: on top level, # we do not want to add anything to generating set
# start checking
size := 1; T := S; while Length( T.generators ) <> 0do
size := size * Length( T.orbit );
T := T.stabilizer;
od; if size = order or size = limit then
ready := true; elif size < order then # we have an incorrect stabilizer chain # repeat checking until a new element is discovered
result := id; if options.random = 1000then if correct then
result := SCRStrongGenTest(S,[1,10/S.diam,0,0,0,0],orbits,
basesize,base,correct,missing); else
result := SCRStrongGenTest2(S,[0,0,1,10/S.diam,0,0]); fi; if result = id then
T := SCRRestoredRecord(S);
result := VerifySGS(T,missing,correct); fi; if result = id then
Print("Warning, computed and given size differ","\n");
ready := true;
S := T; else if not IsPerm(result) then
repeat
result := SCRStrongGenTest2(S,[0,0,1,10/S.diam,0,0]);
until result <> id; fi;
new := [result]; fi; else
warning := 0; if correct then # if correct base was provided, it is enough to check # images of base points to check whether a word is trivial # no need for second checking phase while result = id do
warning := warning + 1; if warning > 5then
Print("Warning, computed and given size differ","\n"); fi;
result := SCRStrongGenTest(S,param,orbits,
basesize,base,correct,missing);
od; else while result = id do
warning := warning + 1; if warning > 5then
Print("Warning, computed and given size differ","\n"); fi;
result:=SCRStrongGenTest(S,param,orbits,
basesize,base,correct,missing); if result = id then # Print("entering SGT2","\n");
result:=SCRStrongGenTest2(S,param); fi;
od; fi; # correct or not
new:=[result]; fi; # end of random checking or not, when size is known else #no information or only upper bound about Size(S) if options.random = 1000then if correct then
result := SCRStrongGenTest(S,[1,10/S.diam,0,0,0,0],orbits,
basesize,base,correct,missing); else
result := SCRStrongGenTest2(S,[0,0,1,10/S.diam,0,0]); fi; if result = id then
T := SCRRestoredRecord(S);
result := VerifySGS(T,missing,correct); fi; if result = id then
S := T;
ready := true; else if not IsPerm(result) then
repeat
result := SCRStrongGenTest2(S,[0,0,1,10/S.diam,0,0]);
until result <> id; fi;
new := [result]; fi; else # Print("entering SGT", "\n");
result:=SCRStrongGenTest(S,param,orbits,basesize,
base,correct,missing); if result <> id then
new:=[result]; elif correct then # no need for second checking phase
ready:=true; else # Print("entering SGT2","\n");
result:=SCRStrongGenTest2(S,param); if result = id then
ready:=true; else
new:=[result]; fi; fi; fi; # random checking or not, when size is not known fi; # size known
od;
#restore usual record elements if not IsBound(S.labels) then
S := SCRRestoredRecord(S); fi;
return S; end );
############################################################################# ## #F SCRMakeStabStrong( ... ) . . . . . . . . . . . . . . . . . . . . . local ## ## heuristic stabilizer chain construction, with one random subproduct on ## each level, and one or two (defined by mlimit) random cosets to make ## Schreier generators ##
InstallGlobalFunction( SCRMakeStabStrong, function ( S, new, param, orbits, where, basesize, base,
correct, missing, top )
local x,m,j,l, # loop variables
ran1, # random permutation
string, # random 0-1 string
w, # random subproduct of generators
len, # number of generators of S
mlimit, # number of random elements to be tested
coset, # word representing coset of S
residue, # first component: remainder of Schreier generator # after factorization; second component > 0 # if factorization unsuccessful
jlimit, # number of random points to plug into residue[1]
ran, # index of random point in an orbit of S
g, # permutation to be added to S.stabilizer
gen, # permutations used in S.transversal
inv, # their inverses
firstmove; # first point of base moved by an element of new
if new <> [] then
firstmove := First( base, x->ForAny( new, gen->x^gen<>x ) ); # if necessary add a new stabilizer to the stabchain if not IsBound(S.stabilizer) then
S.orbit := [firstmove];
S.transversal := [];
S.transversal[S.orbit[1]] := S.identity;
S.generators := [];
S.treegen := [];
S.treegeninv := [];
S.stabilizer := rec();
S.stabilizer.identity := S.identity;
S.stabilizer.aux := [];
S.stabilizer.generators := [];
S.stabilizer.diam := 0; if not correct then
basesize[where[S.orbit[1]]]
:= basesize[where[S.orbit[1]]] + 1; fi;
missing := Difference( missing, [ firstmove ] ); else if Position(base,firstmove) < Position(base,S.orbit[1]) then
S.stabilizer := ShallowCopy(S);
S.orbit := [firstmove];
S.transversal := [];
S.transversal[S.orbit[1]] := S.identity;
S.generators := ShallowCopy(S.stabilizer.generators);
S.treegen := [];
S.treegeninv := []; if not correct then
basesize[where[S.orbit[1]]]
:= basesize[where[S.orbit[1]]] + 1; fi;
missing := Difference( missing, [ firstmove ] ); fi; fi;
# on top level, we want to keep the original generators if not top or Length(S.generators) = 0then for j in new do
StretchImportantSLPElement(j);
od;
Append(S.generators,new); fi; #construct orbit of basepoint
SCRSchTree(S,new);
#check whether new elements are really new in the system while new <> [] do
g := SCRSift( S, Remove(new) ); if g <> S.identity then
SCRMakeStabStrong(S.stabilizer,[g],param,orbits,
where,basesize,base,correct,missing,false);
S.diam:=S.treedepth+S.stabilizer.diam;
S.aux:=Concatenation(S.treegen,
S.treegeninv,S.stabilizer.aux); fi;
od; fi;
#check random Schreier generators
gen := Concatenation(S.treegen,S.treegeninv,[S.identity]);
inv := Concatenation(S.treegeninv,S.treegen,[S.identity]);
len := Length(S.aux); #in case of more than one generator for S, form a random subproduct #otherwise, use the generator if len > 1then
ran1 := SCRRandomPerm(len);
string := SCRRandomString(len);
w := S.identity; for x in [1..len] do
w := w*(S.aux[x^ran1]^string[x]);
od; else
w:=S.aux[1]; fi;
# take random coset(s)
mlimit:=1;
m:=0; while m < mlimit do
m := m+1;
ran := Random(1, Length(S.orbit));
coset := CosetRepAsWord(S.orbit[1],S.orbit[ran],S.transversal);
coset := InverseAsWord(coset,gen,inv); if w <> S.identity then # form Schreier generator and factorize
Add(coset,w);
residue := SiftAsWord(S,coset); # check whether factorization is successful if residue[2] > 0then # factorization is unsuccessful; use remainder for # construction in stabilizer
g := Product(residue[1]);
SCRMakeStabStrong(S.stabilizer,[g],param,orbits,where,
basesize,base,correct,missing,false);
S.diam := S.treedepth+S.stabilizer.diam;
S.aux := Concatenation(S.treegen,S.treegeninv,
S.stabilizer.aux); # get out of current loop
m := 0; elif correct then # enough to check images of points in given base
l := 0; while l < Length(missing) do
l := l+1; if ImageInWord(missing[l],residue[1]) <> missing[l] then # factorization is unsuccessful; # use remainder for construction in stabilizer
g := Product(residue[1]);
SCRMakeStabStrong(S.stabilizer,[g],param,
orbits,where,basesize,
base,correct,missing,false);
S.diam := S.treedepth+S.stabilizer.diam;
S.aux := Concatenation(S.treegen,
S.treegeninv,S.stabilizer.aux); # get out of current loop
m := 0;
l := Length(missing); fi;
od; else
l:=0; while l < Length(orbits) do
l:=l+1; if Length(orbits[l]) > param[5] then # in large orbits, plug in random points
j:=0;
jlimit:=Maximum(param[6],basesize[l]); while j < jlimit do
j:=j+1;
ran:=Random(1, Length(orbits[l])); if ImageInWord(orbits[l][ran],residue[1])
<> orbits[l][ran] then # factorization is unsuccessful; # use remainder for construction in stabilizer
g := Product(residue[1]);
SCRMakeStabStrong(S.stabilizer,[g],param,
orbits,where,basesize,
base,correct,missing,false);
S.diam := S.treedepth+S.stabilizer.diam;
S.aux := Concatenation(S.treegen,S.treegeninv,
S.stabilizer.aux); # get out of current loop
m := 0;
j := jlimit;
l := Length(orbits); fi;
od; #j loop else # in small orbits, check images of all points
j := 0; while j < Length(orbits[l]) do
j := j+1; if ImageInWord(orbits[l][j],residue[1])
<> orbits[l][j] then # factorization is unsuccessful; # use remainder for construction in stabilizer
g := Product(residue[1]);
SCRMakeStabStrong(S.stabilizer,[g],param,
orbits,where,basesize,
base,correct,missing,false);
S.diam := S.treedepth+S.stabilizer.diam;
S.aux := Concatenation(S.treegen,S.treegeninv,
S.stabilizer.aux); # get out of current loop
m := 0;
j := Length(orbits[l]);
l := Length(orbits); fi;
od; #j loop fi;
od; #l loop fi; fi;
od; #m loop
end );
############################################################################# ## #F SCRStrongGenTest( ... ) . . . . . . . . . . . . . . . . . . . . . . local ## ## tests whether product of a random element of S and a random subproduct of ## the strong generators of S is in S. Computations are carried out with ## words in generators representing group elements; random points are ## plugged in to test whether a word represents the identity. If SGS for S ## not complete, returns a permutation not in S. ##
InstallGlobalFunction( SCRStrongGenTest, function ( S, param, orbits, basesize,
base, correct, missing)
local x,i,k,m,j,l, # loop variables
ran1,ran2, # random permutations
string, # random 0-1 string
w, # list containing random subproducts of generators
len, # number of generators of S
len2, # length of short random subproduct
mlimit, # number of random elements to be tested
ranword, # random element of S as a word in generators
residue, # first component: remainder of ranword # after factorization; second component > 0 # if factorization unsuccessful
jlimit, # number of random points to plug into residue[1]
ran, # index of random point in an orbit of S
g; # product of residue[1]
k := 0; while k < param[1] do
k := k+1;
len := Length(S.aux); #in case of large S.aux, form random subproducts #otherwise, try all of them if len > 2*param[3] then
ran1 := SCRRandomPerm(len);
ran2 := SCRRandomPerm(len);
len2 := Random(1, QuoInt(len,2));
string := SCRRandomString(len+len2);
# we form two random subproducts: # w[1] in a random ordering of all generators # w[2] in a random ordering of a random subset of them
w:=[];
w[1] := S.identity; for x in [1 .. len] do
w[1] := w[1]*(S.aux[x^ran1]^string[x]);
od;
w[2] := S.identity; for x in [1 .. len2] do
w[2] := w[2]*(S.aux[x^ran2]^string[x+len]);
od;
else # take next two generators of S (unless only one is left)
w := [];
w[1] := S.aux[2*k-1]; if len > 2*k-1then
w[2] := S.aux[2*k]; else
w[2] := S.identity; fi; fi;
# take random elements of S as words
m := 0;
mlimit := param[2]*S.diam; while m < mlimit do
m:=m+1;
ranword := RandomElmAsWord(S);
i := 0; while i < 2do
i := i+1; if w[i] <> S.identity then
Append(ranword,[w[i]]);
residue := SiftAsWord(S,ranword); if residue[2]>0then # factorization is unsuccessful; # remainder is witness that SGS for S is not complete
g := Product(residue[1]); # Print("k=",k," i=",i," m=",m," mlimit=",mlimit,"\n");
return g; elif correct then # enough to check whether base points are fixed
l:=0; while l < Length(missing) do
l:=l+1; if ImageInWord(missing[l],residue[1])
<> missing[l] then # remainder is not in S
g := Product(residue[1]);
return g; fi;
od; else # plug in points from each orbit to check whether # action on orbit is trivial
l:=0; while l < Length(orbits) do
l:=l+1; if Length(orbits[l]) > param[5] then # on large orbits, plug in random points
j := 0;
jlimit := Maximum(param[6],basesize[l]); while j < jlimit do
j := j+1;
ran := Random(1, Length(orbits[l])); if ImageInWord(orbits[l][ran],residue[1])
<> orbits[l][ran] then #remainder is not in S
g := Product(residue[1]);
return g; fi;
od; #j loop else # on small orbits, plug in all points
j := 0; while j < Length(orbits[l]) do
j := j+1; if ImageInWord( orbits[l][j],residue[1] )
<> orbits[l][j] then # remainder is not in S
g:=Product(residue[1]);
return g; fi;
od; #j loop fi;
od; #l loop fi; fi;
od; #i loop
od; #m loop if len <= 2*k then #finished making Schr. generators with all in S.aux
k := param[1]; fi;
od; #k loop
return S.identity; end );
############################################################################# ## #F SCRSift( <S>, <g> ) . . . . . . . . . . . . . . . . . . . . . . . . local ## ## tries to factor g as product of cosetreps in S; returns remainder ##
InstallGlobalFunction( SCRSift, function(S,g)
local result;
if not IsInternalRep( g ) then
return SiftedPermutation(S, g); fi;
result := SCR_SIFT_HELPER(S, g, Maximum(LargestMovedPoint(g),LargestMovedPoint(S!.generators)));
############################################################################# ## #F SCRStrongGenTest2( <S>, <param> ) . . . . . . . . . . . . . . . . . local ## ## tests whether product of a random element of S and a random subproduct of ## the strong generators of S is in S. Computations are carried out with ## complete permutations. ##
InstallGlobalFunction( SCRStrongGenTest2, function ( S, param )
local x,i,k,m, # loop variables
ran1,ran2, # random permutations
string, # random 0-1 string
w, # list containing random subproducts of generators
len, # number of generators of S
len2, # length of short random subproduct
mlimit, # number of random elements to be tested
ranelement, # random element of S
T, p,
residue; # remainder of ranelement after factorization
k := 0; while k < param[3] do
k := k+1;
len := Length(S.aux); #in case of large S.aux, form random subproducts #otherwise, try all of them if len > 2*param[3] then
ran1 := SCRRandomPerm(len);
ran2 := SCRRandomPerm(len);
len2 := Random(1, QuoInt(len,2));
string := SCRRandomString(len+len2);
# we form two random subproducts: # w[1] in a random ordering of all generators # w[2] in a random ordering of a random subset of them
w:=[];
w[1] := S.identity; for x in [1 .. len] do
w[1] := w[1]*(S.aux[x^ran1]^string[x]);
od;
w[2] := S.identity; for x in [1 .. len2] do
w[2] := w[2]*(S.aux[x^ran2]^string[x+len]);
od;
else # take next two generators of S (unless only one is left)
w := [];
w[1] := S.aux[2*k-1]; if len > 2*k-1then
w[2] := S.aux[2*k]; else
w[2] := S.identity; fi; fi;
# take random elements of S
m := 0;
mlimit := param[4]*S.diam; while m < mlimit do
m:=m+1;
ranelement := S.identity;
T := S; while Length( T.generators ) <> 0do
p := Random( T.orbit ); while p <> T.orbit[ 1 ] do
ranelement := LeftQuotient( T.transversal[ p ],
ranelement );
p := p ^ T.transversal[ p ];
od;
T := T.stabilizer;
od;
i := 0; while i < 2do
i := i+1; if w[i] <> S.identity then # test whether product of ranelement and w[i] in S
ranelement := ranelement*w[i];
residue := SCRSift(S,ranelement); if residue <> S.identity then
return residue; fi; fi;
od; #i loop
od; #m loop if len <= 2*k then #finished checking all in S.aux
k := param[3]; fi;
od; #k loop
return S.identity; end );
############################################################################# ## #F SCRNotice( <orb>, <transversal>, <genlist> ) . . . . . . . . . . . local ## ## checks whether orbit is closed for the action of permutations in ## genlist. If not, returns orbit point and generator witnessing. ##
InstallGlobalFunction( SCRNotice, function ( orb, transversal, genlist )
local flag, #first component of output; true if orb is closed for #action of genlist
i, #second component of output, index of point in orb moving out
j ; #third component, index of permutation in genlist moving orb[i]
i := 0;
flag := true; while i < Length(orb) and flag do
i := i+1;
j := 0; while j < Length(genlist) and flag do
j := j+1; if not IsBound(transversal[orb[i]^genlist[j]]) then
flag := false; fi;
od;
od;
return [flag,i,j]; end );
############################################################################# ## #F SCRExtend( <list> ) . . . . . . . . . . . . . . . . . . . . . . . . local ## ## given a partial Schreier tree of depth d, ## SCRExtends the partial Schreier tree to depth d+1 ## input, output coded in list of length 5 ##
InstallGlobalFunction( SCRExtend, function ( list )
local orb, #partial orbit
transversal, #partial transversal
treegen, #list of generators
treegeninv, #inverses of elements of treegen #both treegen, treegeninv are used in transversal
i, j, #loop variables
previous, #index, showing end of level d-1 in orb
len; #length of orb at entering routine
# for each point on level d, check whether one of the generators or # inverses moves it out of orb. If yes, add image to orb for i in [previous+1..len] do for j in [1..Length(treegen)] do if not IsBound(transversal[orb[i]^treegen[j]]) then
transversal[orb[i]^treegen[j]] := treegeninv[j];
Add(orb, orb[i]^treegen[j]); fi; if not IsBound(transversal[orb[i]^treegeninv[j]]) then
transversal[orb[i]^treegeninv[j]] := treegen[j];
Add(orb, orb[i]^treegeninv[j]); fi;
od;
od;
# return Schreier tree of depth one larger
return [orb,transversal,treegen,treegeninv,len];
end );
############################################################################# ## #F SCRSchTree( <S>, <new> ) . . . . . . . . . . . . . . . . . . . . . local ## ## creates Schreier tree for the group generated by S.generators \cup new ##
InstallGlobalFunction( SCRSchTree, function ( S, new )
local l, #output of notice
flag, #first component of output
i, #second component of output
j, #third component of output
word, #list of permutations coding a coset representative
g, #the coset representative coded by word
witness, #a permutation moving a point out of S.orbit
list; #list coding input and output of 'extend'
l := SCRNotice(S.orbit,S.transversal,new);
flag := l[1]; if flag then #do nothing; the orbit did not change
return; else
i := l[2];
j := l[3];
witness := new[j]; fi;
while not flag do
word := CosetRepAsWord(S.orbit[1],S.orbit[i],S.transversal);
g := Product(word); #add a new generator to treegen which moves S.orbit[1] out of S.orbit
Add(S.treegen, g^(-1)*witness);
Add(S.treegeninv, witness^(-1)*g);
#recompute Schreier tree to new depth
S.orbit := [S.orbit[1]];
S.transversal := [];
S.transversal[S.orbit[1]] := S.identity;
S.treedepth := 0;
list := [S.orbit,S.transversal,S.treegen,S.treegeninv,0];
flag := false;
#with k generators, we build only a tree of depth 2k while not flag and S.treedepth < 2*Length(S.treegen) do
list := SCRExtend(list);
S.orbit := list[1];
S.transversal := list[2]; if Length(S.orbit) = list[5] then #the tree did not extend; orbit is closed for treegen
flag := true; else
S.treedepth := S.treedepth + 1; fi;
od; #increased S.orbit may not be closed for all generators of S
l := SCRNotice(S.orbit,S.transversal,S.generators);
flag := l[1]; if not flag then
i := l[2];
j := l[3];
witness := S.generators[j]; fi;
od;
############################################################################# ## #F SCRRandomPerm( <d> ) . . . . . . . . . . . . . . . . . . . . . . . local ## ## constructs random permutation in Sym(d) ## without creating group record of Sym(d) ##
InstallGlobalFunction( SCRRandomPerm, function ( d )
local rnd, # random permutation, result
tmp, # temporary variable for swapping
i, k; # loop variables
# use Floyd\'s algorithm
rnd := [ 1 .. d ]; for i in [ 1 .. d-1 ] do
k := Random( 1, d+1-i );
tmp := rnd[d+1-i];
rnd[d+1-i] := rnd[k];
rnd[k] := tmp;
od;
# return the permutation
return PermList( rnd ); end );
############################################################################# ## #F SCRRandomString( <n> ) . . . . . . . . . . . . . . . . . . . . . . local ## ## constructs random 0-1 string of length n ## same steps as Random, but uses created random number for 28 bits ##
InstallGlobalFunction( SCRRandomString, function ( n )
local i, j, # loop variables
k, # number of 28 long substrings
rnd, # the random number which would be created by Random
string, # the random string constructed
range; # Upper value of range used for getting ints
range := 2^28-1;
string:=[];
k:=QuoInt(n-1,28); for i in [0..k-1] do
rnd := Random(0,range); # use each bit of rnd for j in [1 .. 28] do
string[28*i+j] := rnd mod 2;
rnd := QuoInt(rnd,2);
od;
od;
# construct last <= 28 bits of string
rnd := Random(0, range); for j in [28*k+1 .. n] do
string[j] := rnd mod 2;
rnd := QuoInt(rnd,2);
od;
return string; end );
############################################################################# ## #F SCRRandomSubproduct( <list>, <id> ) . random subproduct of perms in list ##
InstallGlobalFunction( SCRRandomSubproduct, function( list, id )
local string, # 0-1 string containing the exponents of elements of list
random, # the random subproduct
i; # loop variable
string := SCRRandomString(Length(list));
random := id; for i in [1 .. Length(list)] do if string[i] = 1then
random := random*list[i]; fi;
od;
return random; end );
############################################################################# ## #F SCRExtendRecord( <G> ) . . . . . . . . . . . . . . . . . . . . . . local ## ## defines record elements used at random stabilizer chain construction ##
InstallGlobalFunction( SCRExtendRecord, function(G)
local list, # list of stabilizer subgroups
len, # length of list
i; # loop variable
list := ListStabChain(G);
len := Length(list);
list[len].diam := 0;
list[len].aux := []; for i in [1 .. len - 1] do # list[len - i].real := list[len - i].generators;
if Length(list[len - i].orbit) = 1then # in this case, SCRSchTree will not do anything; # we have to define records treedepth, aux, and diameter
list[len - i].treedepth := 0;
list[len - i].aux := list[len - i + 1].aux;
list[len - i].diam := list[len - i + 1].diam; fi;
############################################################################# ## #F SCRRestoredRecord( <G> ) . . . . . . . . . . . . . . . . . . . . . local ## ## restores usual group records after random stabilizer chain construction ##
InstallGlobalFunction( SCRRestoredRecord, function( G )
local sgs, T, S, l, pnt,o,ind;
S := G;
sgs := [ S.identity ]; while IsBound( S.stabilizer ) do
UniteSet( sgs, S.treegen );
UniteSet( sgs, S.treegeninv );
S := S.stabilizer;
od;
T := EmptyStabChain( sgs, G.identity );
sgs := [ 2 .. Length( sgs ) ];
S := T; while IsBound( G.stabilizer ) do
InsertTrivialStabilizer( S, G.orbit[ 1 ] );
S.genlabels := sgs;
S.generators := G.generators;
S.orbit := G.orbit;
S.transversal := G.transversal;
o:=ShallowCopy(S.orbit); # check identity of transversal elements first: Most are # identical and thus element comparisons are relatively # infrequent while Length(o)>0do
ind:=S.transversal[o[1]];
ind:=Filtered([1..Length(o)],
i->IsIdenticalObj(S.transversal[o[i]],ind)); for l in sgs do if S.transversal[o[1]]=S.labels[l] then for pnt in o{ind} do# all these transv. elements are same
S.translabels[ pnt ] := l;
od; fi;
od;
o:=o{Difference([1..Length(o)],ind)}; # the rest
od;
#was: # (The element comparisons could be expensive) #for pnt in S.orbit do # if S.transversal[ pnt ] = lab then # S.translabels[ pnt ] := l; # fi; #od;
sgs := Filtered( sgs, l ->
S.orbit[ 1 ] ^ S.labels[ l ] = S.orbit[ 1 ] );
S := S.stabilizer;
G := G.stabilizer;
od;
return T; end );
############################################################################# ## #F VerifyStabilizer( <S>, <z>, <missing>, <correct> ) . . . . verification ##
InstallGlobalFunction( VerifyStabilizer, function(S,z,missing,correct) #z is an involution, moving first base point # correct is boolean telling whether a base is known # if yes, missing contains the base points which do not occur in the base of S
local pt1, # the first base point
zinv, # inverse of z
pt2, # pt1^zinv
flag, # Boolean; true if z exchanges pt1 and pt2
stab, # the stabilizer of pt1 in S
chain, # stab after base change, to bring pt2 first
stabpt2, # stabilizer of pt2 in chain
result, # output, witness perm if something is wrong
g, # generator of stabpt2
where1, # stores which orbit of stab pts of S.orbit belong to
orbit1count, # index running through orbits of stab
leaders, # list of orbit representatives of stab
i, j, l, # loop variables
residue, # result of sifting as word
where2, # boolean list to mark orbits of stabpt2 as processed
k, # point of an orbit of stab
gen, # a generator of stab
transversal, # transversal of stab, on all of its orbits
orb, # an orbit of stabpt2
img, # image of a point at computation of orb
pnt, # a point from orb
w1, w2, w3, w4, # words/permutations coding coset representatives
w1inv, # inverse of w1
schgen; # Schreier generator
pt1 := S.orbit[1];
zinv := z^(-1);
pt2 := pt1^zinv;
result := S.identity;
stab := S.stabilizer; if pt2^zinv = pt1 then
flag := true;
result := SCRSift(stab, z^2); else
flag := false; fi;
# store which orbit of stab the pts of S.orbit belong to # in each orbit, compute transversals from a representative
where1 := []; # orbits of stab
leaders := [pt2];
orbit1count := 1;
transversal := [];
transversal[pt2] := S.identity;
where1[pt2] := 1;
orb := [pt2];
j := 1; while j <= Length( orb ) do for gen in stab.generators do
k := orb[j] / gen; if not IsBound( transversal[k] ) then
transversal[k] := gen;
Add( orb, k );
where1[k] := orbit1count; fi;
od;
j := j + 1;
od; for i in S.orbit do if not IsBound(where1[i]) then
orbit1count := orbit1count + 1;
Add(leaders, i);
orb := [i];
where1[i] := orbit1count;
transversal[i] := S.identity;
j := 1; while j <= Length( orb ) do for gen in stab.generators do
k := orb[j] / gen; if not IsBound( transversal[k] ) then
transversal[k] := gen;
Add( orb, k );
where1[k] := orbit1count; fi;
od;
j := j + 1;
od; fi;
od;
# check that conjugates of point stabilizers of stab are subgroups of stab
chain:=StructuralCopy(stab); for j in [1..Length(leaders)] do if result = S.identity then
i := leaders[Length(leaders)+1-j];
ChangeStabChain( chain, [i], false ); if i = pt2 then
w1 := z;
w1inv := zinv; else
w1 := CosetRepAsWord(pt1, i, S.transversal);
w1 := Product(w1);
w1inv := w1^(-1); fi; for g in chain.stabilizer.generators do if result = S.identity then if correct then
residue := SiftAsWord(stab, [w1inv,g,w1]); if residue[2] <> 0then
result := Product(residue[1]); else
l := 0; while ( l < Length(missing) ) and ( result = S.identity ) do
l:=l+1; if ImageInWord(missing[l],residue[1])
<> missing[l] then # remainder is not in S
result := Product(residue[1]); fi;
od; fi; else
result := SCRSift(stab, w1inv*g*w1); fi; fi;
od; fi;
od;
if result = S.identity then
stabpt2 := chain.stabilizer; # process orbits of stabpt2
where2:= BlistList( [1..Length(where1)],[] ) ; for i in S.orbit do if result = S.identity and (not where2[i]) then
orb := [i];
where2[i] := true; for pnt in orb do for gen in stabpt2.generators do
img := pnt^gen; if not where2[img] then
Add( orb, img );
where2[img] := true; fi;
od;
od;
# if we hit a new orbit of stabpt2 # and if z exchanges pt1 and pt2 then # mark the orbit if flag and not where2[i^z] then
orb := [i^z];
where2[i^z] := true; for pnt in orb do for gen in stabpt2.generators do
img := pnt^gen; if not where2[img] then
Add( orb, img );
where2[img] := true; fi;
od;
od; fi;
# compute Schreier generator either as a word or perm # if i is not a fixed point of z, # we have to compute a Schreier generator
w1 := CosetRepAsWord(pt1, leaders[where1[i]], S.transversal);
w2 := CosetRepAsWord(leaders[where1[i]], i, transversal);
w3 := CosetRepAsWord(leaders[where1[i^z]], i^z, transversal); if where1[i] <> where1[i^z] then
w4 := CosetRepAsWord(pt1,leaders[where1[i^z]], S.transversal); else
w4 := w1; fi;
schgen := (Product(w1))^(-1)*(Product(w2))^(-1); if correct then
schgen := Concatenation([schgen,z],w3,w4); else
schgen := schgen*z*Product(w3)*Product(w4); fi;
# sift Schreier generator either as a word or perm if correct then
residue := SiftAsWord(stab, schgen); if residue[2] <> 0then
result := Product(residue[1]); else
l := 0; while ( l < Length(missing) ) and ( result = S.identity ) do
l:=l+1; if ImageInWord(missing[l],residue[1])
<> missing[l] then # remainder is not in S
result := Product(residue[1]); fi;
od; fi; else
result := SCRSift(stab, schgen); fi; fi; # result = S.identity and not where2[i]
od; fi;
return result;
end );
############################################################################# ## #F VerifySGS( <S>, <missing>, <correct> ) . . . . . . . . . . verification ##
InstallGlobalFunction( VerifySGS, function(S,missing,correct) # correct is boolean telling whether a base is known # if yes, missing contains the base points which do not occur in the base of S
local n, # degree of S
list, # list of subgroups in stabchain
result, # result of the test
len, # length of list
i,l, # loop variables
residue, # result of sifting as word
temp, # subgroup of S we currently work with
temp2, # temp, with possible extension on blocks
gen, # the generator whose addition we verify
longer, # generator list of temp, after gen is added
gencount, # counts the generators to be verified
set, # set of orbit of temp
orbit, # orbit of temp after adding gen
blks, # images of block when gen is added
extension, # list of length 2; first coord. is temp2, extended by # the action on blks, second coord. is newgen
newgen, # the extension of gen
leader, # first point in orbit of temp2
block, # block containing leader
point, # another point from block
pos; # position of set in blks
n := LargestMovedPoint(S.generators);
list := ListStabChain(S);
len := Length(list);
result := S.identity;
# verify the subgroup chain from bottom up
i := 1; while i < len and result = S.identity do
temp := ShallowCopy(list[len - i + 1]);
InsertTrivialStabilizer(temp, list[len -i].orbit[1]);
gencount := 0;
# add one generator a time while (gencount < Length(list[len - i].generators)) and (result= S.identity ) do
gencount := gencount + 1;
gen := list[len - i].generators[gencount];
set := Set( temp.orbit );
# if adding gen did not change the fundamental orbit, just sift gen if set = OnSets(set,gen) then if correct then
residue := SiftAsWord(temp, [gen]); if residue[2] <> 0then
result := Product(residue[1]); else
l := 0; while ( l < Length(missing) ) and ( result = S.identity ) do
l:=l+1; if ImageInWord(missing[l],residue[1])
<> missing[l] then # remainder is not in S
result := Product(residue[1]); fi;
od; fi; else
result := SCRSift(temp, gen); fi; # if the fundamental orbit increased, compute block system # obtained when adding gen else if Length(set) =1then
temp2 := temp;
newgen := gen; # otherwise, compute the action on blocks else
longer := Concatenation(temp.generators, [gen]);
orbit := OrbitPerms( longer, temp.orbit[ 1 ] );
blks := Blocks( GroupByGenerators( longer ), orbit, set ); if Length(blks) * Length(set) <> Length(orbit) then
result := "false0"; else
pos := Position( blks, set );
extension := ExtensionOnBlocks( temp, n, blks, [gen] );
temp2 := extension[1];
newgen := extension[2][1];
InsertTrivialStabilizer(temp2, n + pos); fi; fi;
# first generator in first group can be verified easily if i=1 and Length(set) =1then
result := newgen^(CycleLengthOp(newgen,temp2.orbit[1]));
AddGeneratorsExtendSchreierTree(temp2, [newgen]); elif result = S.identity then
AddGeneratorsExtendSchreierTree( temp2, [ newgen ] );
blks := Blocks( GroupByGenerators(temp2.generators),
temp2.orbit); if Length(blks) > 1then
leader := temp2.orbit[1];
block := First(blks, x -> leader in x);
point := First(block, x -> x <> leader);
newgen := Product(CosetRepAsWord(leader ,point,
temp2.transversal));
temp2 := temp2.stabilizer;
InsertTrivialStabilizer(temp2, leader);
AddGeneratorsExtendSchreierTree(temp2, [newgen]);
result := VerifyStabilizer(temp2,newgen,missing,correct); if leader > n then
AddGeneratorsExtendSchreierTree(temp,[RestrictedPermNC
(newgen, [1..n])]); else
temp := temp2; fi;
gencount := gencount - 1; else
result := VerifyStabilizer(temp2,newgen,missing,correct); if temp2.orbit[1] > n then
AddGeneratorsExtendSchreierTree(temp,[gen]); fi; fi; if result <> S.identity and temp2.orbit[1] > n then
result := RestrictedPermNC(result, [1..n]); fi;
# construct extensions of permutations in elms
newelms := List( elms, hom );
return [ T, newelms ]; end );
############################################################################# ## #F ClosureRandomPermGroup( <G>, <genlist>, <options> ) make closure randomly ##
InstallGlobalFunction( ClosureRandomPermGroup, function( G, genlist, options )
local k, # number of pairs of subproducts of generators in # testing result
givenbase, # ordering from which initial base points should # be chosen
gens, # generators in genlist that are not in <G>
g, # element of gens
degree, # degree of closure
orbits, # list of orbits of closure
orbits2, # list of orbits of closure
i,j, # loop variables
param, # list of parameters guiding number of repetitions # in random constructions
where, # list indicating which orbit contains points in domain
basesize, # list; i^th entry = number of base points in orbits[i]
ready, # boolean; true if stabilizer chain ready
new, # list of permutations to be added to stab. chain
result, # output of checking phase; nontrivial if stabilizer # chain is incorrect
base, # ordering of domain from which base points are taken
missing, # if a correct base was provided by input, missing # contains those points of it which are not in # constructed base
cnt, # iteration counter
correct; # boolean; true if a correct base is given
# warning: options.base should be compatible with BaseOfGroup(G)
gens := Filtered( genlist, gen -> not(IsOne(SCRSift(G,gen))) ); if Length(gens) > 0then
G.identity := One(gens[1]) ; if options.random = 1000then #case of deterministic computation with known size
k := 1; else
k:=First([1..14],x->(3/5)^x<1-options.random/1000); fi; 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 <= 200then
param[2] := 2;
param[4] := 2; fi;
#param[1] = number of pairs of random subproducts from generators in # first checking phase #param[2] = (number of random elements from created set)/S.diam # in first checking phase #param[3] = number of pairs of random subproducts from generators in # second checking phase #param[4] = (number of random elements from created set)/S.diam # in second checking phase #param[5] = maximum size of orbits in which we evaluate words on all # points of orbit #param[6] = minimum number of random points from orbit to plug in to check # whether given word is identity on orbit
# prepare input of construction if IsBound(options.base) then
givenbase := options.base; else
givenbase := []; fi;
if IsBound(options.knownBase) then
correct := true; else
correct := false; fi;
if correct then # if correct base was given as input, # no need for orbit information
base := Set( givenbase ); for i in BaseStabChain(G) do if not i in base then
Add( givenbase, i ); fi;
od;
base := Concatenation(givenbase,Difference(options.knownBase,
givenbase));
missing := Difference(options.knownBase,BaseStabChain(G));
basesize := [];
where := [];
orbits := []; else # create ordering of domain used in choosing base points and # compute orbit information
base := Set( givenbase ); for i in BaseStabChain(G) do if not i in base then
Add( givenbase, i ); fi;
od;
base := Concatenation(givenbase,Difference([1..degree],givenbase));
missing := [];
orbits2 := OrbitsPerms( Union( G.generators, gens ), [1..degree] ); #throw away one-element orbits
orbits:=[];
j:=0; for i in [1..Length(orbits2)] do if Length(orbits2[i]) >1then
j:=j+1; orbits[j]:= orbits2[i]; fi;
od;
basesize:=[];
where:=[]; for i in [1..Length(orbits)] do
basesize[i]:=0; for j in [1..Length(orbits[i])] do
where[orbits[i][j]]:=i;
od;
od; # temporary solution to speed up of handling # of lots of small orbits until compiler if Length(orbits) > degree/40then
param[1] := 0;
param[3] := k; fi; fi;
if not IsBound(G.aux) then
SCRExtendRecord(G); fi;
new := gens;
#the first call of SCRMakeStabStrong has top:=false #in order to add gens to the generating set of G; #further calls have top:=true, in order not to add #output of SCRStrongGenTest to generating set. #remark: adding gens to the generating set of G before #calling SCRMakeStabStrong gives a nasty error if first base #point changes for g in gens do if not(IsOne(SCRSift(G,g))) then
SCRMakeStabStrong (G,[g],param,orbits,
where,basesize,base,correct,missing,false); fi;
od;
cnt:=0;
ready := false; while not ready do if IsBound(options.limit)
and SizeStabChain(G)=options.limit then
ready := true; else # we do a little random testing, to ensure heuristically a # correct result if correct then
result := SCRStrongGenTest(G,[1,10/G.diam,0,0,0,0],orbits,
basesize,base,correct,missing); else
result := SCRStrongGenTest2(G,[0,0,1,10/G.diam,0,0]); fi; if not(IsPerm(result) and IsOne(result)) then
new := [result];
ready := false; elif options.random = 1000then
G.restored := SCRRestoredRecord(G);
result := VerifySGS( G.restored, missing, correct );
cnt:=cnt+1; if cnt>99then # in rare cases this loop iterates for a very long time. # In this case, rather create a new chain, than try to # fix the problematic one #Error("infinite loop?");
return StabChainRandomPermGroup(G.generators,G.identity,
options); fi; elif options.random > 0then
result := SCRStrongGenTest
(G,param,orbits,basesize,base,correct,missing); fi; if not(IsPerm(result) and IsOne(result)) then if not IsPerm(result) then
repeat
result := SCRStrongGenTest2(G,[0,0,1,10/G.diam,0,0]);
until not(IsPerm(result) and IsOne(result)); fi;
new := [result];
ready := false; elif correct or options.random = 0 or options.random = 1000then
ready := true; else
result := SCRStrongGenTest2(G,param); if IsPerm(result) and IsOne(result) then
ready := true; else
new := [result];
ready := false; fi; fi; if not ready then
Unbind(G.restored);
SCRMakeStabStrong (G,new,param,orbits,
where,basesize,base,correct,missing,true); #Print("D ",SizeStabChain(G),"\n"); fi; fi;
od;
if not IsBound(options.temp) or options.temp = false then if IsBound( G.restored ) then
G := G.restored; else
G := SCRRestoredRecord(G); fi; else
G.basesize := basesize;
G.correct := correct;
G.orbits := orbits;
G.missing := missing;
G.base := base; fi;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.