Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 80 additions & 39 deletions gap/perm/giant.gi
Original file line number Diff line number Diff line change
Expand Up @@ -23,51 +23,88 @@ SetInfoLevel( InfoGiants, 1 );

#######################################################################
##
#F NiceGeneratorsSn(<n>,<grp>,<N>) ...... find n-cycle, transposition
##
##

RECOG.NiceGeneratorsSn := function ( mp, grp, N )

local t, cyclen, g, y, h, i, x, supp, n;
#F NiceGeneratorsSn(<grp>,<N>) ...... find n-cycle, transposition
##
## For a permutation group grp on n points, this function returns either fail
## or a list [ fullCycle, transp ] such that the following hold:
## - n > 2.
## - fullCycle is an n-cycle.
## - transp is a transposition, i.e., a 2-cycle.
## - transp interchanges two successive points of fullCycle. In other words,
## fullCycle = (1,...,n) and transp = (1,2) up to renaming the n points.
## The conditions on [ fullCycle, transp ] imply that grp is the full symmetric
## group on n points. Thus the function always returns fail if grp is not the
## full symmetric group, and also if n <= 2.
## fullCycle, transp are found by a random search, drawing at most N random
## elements from grp.
## Thus if grp is the full symmetric group on n points, then the function does not
## return fail with a probability that grows as N grows.

RECOG.NiceGeneratorsSn := function ( grp, N )

local cyclen, fullCycle, transp, rand, orders, i, supp, suppNew, n, mp,
numTries;

mp := MovedPoints(grp);
n := Length(mp);
while N > 0 do
N := N - 1;
t := PseudoRandom( grp );
# was: cyclen := Collected( CycleLengths( t, [1..n] ) );
cyclen := CycleStructurePerm(t);

# was: if IsBound(g) = false and [n,1] in cyclen then
if IsBound(g) = false and IsBound(cyclen[n-1]) then
# we found an $n$-cycle
g := t;
fi;
if n<=2 then
return fail;
fi;

if IsBound(y) = false and IsBound(cyclen[1]) and cyclen[1] = 1 and
ForAll([1..QuoInt(n,2)-1],i->not IsBound(cyclen[2*i+1]))
# was: Filtered( cyclen, x -> x[1] mod 2 = 0 ) = [ [ 2,1 ] ]
then
# we can get a transposition
y := t^(Lcm(Filtered([2..n],x->IsBound(cyclen[x-1])))/2);
# Random search for an n-cycle and a transposition in grp
fullCycle := fail;
transp := fail;
while (fullCycle = fail or transp = fail) and N > 0 do
N := N - 1;
rand := PseudoRandom( grp );
# If cyclen[i] is bound, it is the number of cycles of length i+1 in rand.
# Otherwise rand has no cycle of length i+1.
cyclen := CycleStructurePerm(rand);
# Is rand an n-cycle?
# Check that at least one n-cycle appears in the cycle decomposition of rand.
# In this case, rand must be equal to this n-cycle.
if fullCycle = fail and IsBound(cyclen[n-1]) then
fullCycle := rand;
fi;

if IsBound(g) and IsBound(y) then
i := 10*n;
supp := MovedPoints( y );
while i > 0 do # take up to i conjugates,
# check if they match the $n$-cycle
i := i-1;
x := PseudoRandom( grp );
if (supp[1]^x)^g = supp[2]^x or (supp[2]^x)^g = supp[1]^x
then h := y^x;
return [ g, h ];
# Can we construct a transposition from rand?
# Check that the cycle decomposition of rand contains exactly one 2-cycle
# and no cycle of length k where k is even and 4<=k<n.
if transp = fail and IsBound(cyclen[1]) and cyclen[1] = 1 then
# orders is a the list of all k>2 such that rand contains a k-cycle.
# Since rand contains a 2-cycle, we only have to check up to n-2.
orders := Filtered([3..n-2],x->IsBound(cyclen[x-1]));
if ForAll(orders, IsOddInt) then
if IsEmpty(orders) then # rand is a 2-cycle
transp := rand;
else
# Raising to the power of Lcm(orders) kills all cycles of
# odd length. The single 2-cycle in rand remains
transp := rand^(Lcm(orders));
fi;
od;
return fail;
fi;
fi;
od;

od; # loop over random elements
# Conjugate transp by random elements until we obtain a transposition that
# interchanges successive elements of fullCycle.
if fullCycle <> fail and transp <> fail then
numTries := 10*n; # TODO: Why do we choose this constant?
supp := MovedPoints( transp ); # Support of transp
while numTries > 0 do
numTries := numTries-1;
rand := PseudoRandom( grp );
suppNew := List(supp, x->x^rand); # Support of transp^rand
# Check that transp^rand interchanges successive points of fullCycle,
# or equivalently, that fullCycle moves one point of transp^rand
# to the other one.
if suppNew[1]^fullCycle = suppNew[2]
or suppNew[2]^fullCycle = suppNew[1]
then
return [ fullCycle, transp^rand ];
fi;
od;
fi;

return fail;

Expand Down Expand Up @@ -125,9 +162,11 @@ RECOG.RecogniseSn := function( mp, grp, eps )
le := le + 1;
od;

# TODO: Document these magic constants. They are probably from some paper.
# Also, Max Horn suggested that it may be better to use a smaller N.
N := Int(24 * (4/3)^3 * le * 6 * n);

gens := RECOG.NiceGeneratorsSn( mp, grp, N );
gens := RECOG.NiceGeneratorsSn( grp, N );
if gens = fail then
Info(InfoGiants,1,"couldn't find nice generators for Sn");
return fail;
Expand Down Expand Up @@ -441,6 +480,7 @@ end;
##
##
## This is the main function.
## TODO: Document the precise meaning of the threshold eps. Does it come from a specific paper?
##

RECOG.RecogniseGiant := function( mp, grp, eps )
Expand Down Expand Up @@ -694,7 +734,8 @@ RECOG.SLPforAn := function( n, pi )

end;


# TODO: Document this magic constant properly.
# (It is apparently the default for eps in FindHomMethodsPerm.)
RECOG.GiantEpsilon := 1/1024;

#! @BeginChunk Giant
Expand Down
50 changes: 50 additions & 0 deletions tst/working/quick/PermNiceGens.tst
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#
gap> START_TEST("PermNiceGens.tst");
gap> testNiceGeneratorsSn := function(grp)
> local Sn, m, niceGens, fullCycle, transp, cyclen1, x, y, i;
> m := Size(MovedPoints(grp));
> niceGens := RECOG.NiceGeneratorsSn(grp, 10000);
> if m<=2 then
> if niceGens = fail then
> return; # everything ok
> else
> Display("NiceGeneratorsSn should return fail for n<=2");
> fi;
> elif niceGens <> fail then
> fullCycle := niceGens[1];
> transp := niceGens[2];
> if Size(MovedPoints(transp)) <> 2 then
> Display("Second generator is not a transposition");
> fi;
> cyclen1 := CycleStructurePerm(niceGens[1]);
> for i in [1..m-2] do
> if IsBound(cyclen1[i]) then
> Display("First generator is not a full cycle");
> fi;
> od;
> x := MovedPoints(transp)[1];
> y := MovedPoints(transp)[2];
> if not (x^fullCycle = y or y^fullCycle = x) then
> Display("Transposition does not interchange successive points of full cycle");
> fi;
> fi;
> end;;

# Test that NiceGeneratorsSn finds correct generators
gap> for n in [1..50] do
> Sn := SymmetricGroup(n);
> for j in [1..3] do # Test a few times
> testNiceGeneratorsSn(Sn);
> testNiceGeneratorsSn(Group(Random(Sn), Random(Sn)));
> od;
> od;

# Test that NiceGeneratorsSn returns fail for some groups that
# are not a full symmetric group
gap> grps := [Group((1,2,3)), Group((1,2), (3,4)), Group((1,10),(2,15,30))];;
gap> for G in grps do if RECOG.NiceGeneratorsSn(G, 10000) <> fail then
> Print("NiceGeneratorsSn should not succeed for ", G, "\n");
> fi; od;

#
gap> STOP_TEST("PermNiceGens.tst");