Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve construction of permutation actions and by this e.g. make FactorCosetAction faster by several magnitudes in certain examples #5698

Merged
merged 8 commits into from
May 6, 2024
Merged
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
3 changes: 3 additions & 0 deletions lib/csetgrp.gd
Original file line number Diff line number Diff line change
Expand Up @@ -441,3 +441,6 @@ DeclareGlobalFunction("IntermediateGroup");
# forward declaration for recursive call.
DeclareGlobalFunction("DoConjugateInto");

# action-based ascending series for perm group
DeclareGlobalFunction("ActionRefinedSeries");

8 changes: 6 additions & 2 deletions lib/csetgrp.gi
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,10 @@ local o,b,img,G1,c,m,hardlimit,gens,t,k,intersize;
fi;
fi;

if ValueOption("cheap")=true then
return fail; # do not do hard work
fi;

if Index(G,U)>hardlimit then
Info(InfoWarning,1,
"will have to use permutation action of degree bigger than ", hardlimit);
Expand Down Expand Up @@ -988,9 +992,9 @@ local c, flip, maxidx, cano, tryfct, p, r, t,
actlimit, uplimit, badlimit,avoidlimit,start,includestab,quot;

actlimit:=300000; # maximal degree on which we try blocks
uplimit:=10000; # maximal index for up step
uplimit:=500000; # maximal index for up step
avoidlimit:=200000; # beyond this index we want to get smaller
badlimit:=1000000; # beyond this index things might break down
badlimit:=5000000; # beyond this index things might break down

mayflip:=true; # are we allowed to flip?

Expand Down
247 changes: 231 additions & 16 deletions lib/csetperm.gi
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,54 @@ end );
##
MAX_SIZE_TRANSVERSAL := 100000;

# so far only orbits and perm groups -- TODO: Other deduced actions
InstallGlobalFunction(ActionRefinedSeries,function(G,U)
local o,A,ser,act,i;
o:=List(Orbits(U,MovedPoints(G)),Set);
SortBy(o,Length);
A:=G;
ser:=[A];
act:=[0]; # dummy entry
i:=1;
while i<=Length(o) and Size(A)>Size(U) do
A:=Stabilizer(A,o[i],OnSets);
if Size(A)<Size(ser[Length(ser)]) then
Add(ser,A);
Add(act,[o[i],OnSets]);
fi;
i:=i+1;
od;
if Size(A)>Size(U) then
Add(ser,U);
Add(act,fail);
fi;
# refine large step?
for i in [1..Length(ser)-1] do
if IndexNC(ser[i],ser[i+1])>MAX_SIZE_TRANSVERSAL then
A:=IntermediateGroup(ser[i],ser[i+1]:cheap);
if A<>fail then
# refine with action
o:=ActionRefinedSeries(ser[i],A);
ser:=Concatenation(ser{[1..i]},o[1]{[1..Length(o[1])-1]},
ser{[i+1..Length(ser)]});
act:=Concatenation(act{[1..i]},o[2]{[1..Length(o[2])-1]},
act{[i+1..Length(act)]});
else
# no refinement, next step
i:=i+1;
fi;
else
# no refinement needed, next step
i:=i+1;
fi;
od;
# make ascending like AscendingSeries
return [Reversed(ser),Reversed(act)];
end);

BindGlobal( "RightTransversalPermGroupConstructor", function( filter, G, U )
local GC, UC, noyet, orbs, domain, GCC, UCC, ac, nc, bpt, enum, i;
local GC, UC, noyet, orbs, domain, GCC, UCC, ac, nc, bpt, enum, i,
actions,nct;

GC := CopyStabChain( StabChainImmutable( G ) );
UC := CopyStabChain( StabChainImmutable( U ) );
Expand All @@ -62,20 +108,27 @@ BindGlobal( "RightTransversalPermGroupConstructor", function( filter, G, U )
#Print(SizeStabChain(GCC),"/",SizeStabChain(UCC),":",
# SizeStabChain(GCC)/SizeStabChain(UCC),"\n");
if noyet and (
(SizeStabChain(GCC)/SizeStabChain(UCC)*10 >MAX_SIZE_TRANSVERSAL) or
(SizeStabChain(GCC)/SizeStabChain(UCC) >MAX_SIZE_TRANSVERSAL) or
(Length(UCC.genlabels)=0 and
SizeStabChain(GCC)>MAX_SIZE_TRANSVERSAL)
) then
# we potentially go through many steps, making it expensive
ac:=AscendingChain(G,U:cheap);

# first get a factorization through actions
ac:=ActionRefinedSeries(G,U);
actions:=ac[2];
ac:=ac[1];

# go in biggish steps through the chain
nc:=[ac[1]];
nct:=[actions[1]];
for i in [3..Length(ac)] do
if Size(ac[i])/Size(nc[Length(nc)])>MAX_SIZE_TRANSVERSAL then
Add(nc,ac[i-1]);
Add(nct,actions[i-1]);
fi;
od;
Add(nc,ac[Length(ac)]);
Add(nct,actions[Length(actions)]);
if Length(nc)>2 then
ac:=[];
for i in [Length(nc),Length(nc)-1..2] do
Expand Down Expand Up @@ -158,7 +211,8 @@ end );
InstallGlobalFunction( AddCosetInfoStabChain, function( G, U, maxmoved )
local orb, pimg, img, vert, s, t, index,
block, B, blist, pos, sliced, lenflock, i, j,
ss, tt,t1,t1lim,found,tl,vimg;
ss, tt,t1,t1lim,found,tl,vimg,
sel,shortsel,gorpo,pisel,prepi,transinv;

# iterated image
vimg:=function(point,list)
Expand Down Expand Up @@ -251,13 +305,85 @@ InstallGlobalFunction( AddCosetInfoStabChain, function( G, U, maxmoved )
t1lim:=50;
fi;

sel:=Filtered([1..Maximum(G.orbit)],x->IsBound(U.translabels[x]));
shortsel:=Length(sel)<t1lim;

if shortsel then
# inverse transversal elements
t1:=ShallowCopy(G.generators);
Add(t1,G.identity);
vert:=List(t1,x->x^-1);
# inverses of transversal, stored compact
# TODO: Instead of `Position`, use translabels entry
#transinv:=List(G.transversal,x->vert[Position(t1,x)]);
transinv:=[];
for i in [1..Length(G.transversal)] do
if IsBound(G.transversal[i]) then
t:=Position(t1,G.transversal[i]);
if t=fail then
Add(t1,G.transversal[i]);
Add(vert,G.transversal[i]^-1);
t:=Length(t1);
fi;
transinv[i]:=vert[t];
fi;
od;
# Position in orbit
gorpo:=[];
for i in [1..Length(G.orbit)] do
gorpo[G.orbit[i]]:=i;
od;
fi;

orb := G.orbit{ [ 1 .. U.lenblock ] };
pimg := [ ];
while index < U.index do

pimg{ orb } := CosetNumber( G.stabilizer, U.stabilizer, s,
orb );
t := 2;
while t <= U.lenblock and index < U.index do

if shortsel then

# test for the few wrong values, mapping backwards

pisel:=Filtered([1..Length(pimg)],
x->IsBound(pimg[x]) and pimg[x] in sel);

while t <= U.lenblock and index < U.index do

# For this point in the block, find the images of the
# earlier points under the representative.
vert := G.orbit{ [ 1 .. t-1 ] };
img := G.orbit[ t ];
tl:=[];
while img <> G.orbit[ 1 ] do
Add(tl,transinv[img]);
img := img ^ G.transversal[ img ];
od;
prepi:=pisel;
for t1 in [Length(tl),Length(tl)-1..1] do
prepi:=OnTuples(prepi,tl[t1]);
od;

# If $Ust = Us't'$ then $1t'/t/s in 1U$. Also if $1t'/t/s
# in 1U$ then $st/t' = u.g_1$ with $u in U, g_1 in G_1$
# and $g_1 = u_1.s'$ with $u_1 in U_1, s' in S_1$, so
# $Ust = Us't'$.
#if ForAll( [ 1 .. t-1 ], i -> not
# vert[ i ] in prepi ) then
if not ForAny(gorpo{prepi},x->x>=1 and x<=t-1) then
U.repsStab[ t ][ s ] := true;
index := index + lenflock;

fi;

t := t + 1;
od;

else

while t <= U.lenblock and index < U.index do

# do not test all points first if not necessary
# (test only at most t1lim points, if the test succeeds,
Expand All @@ -269,25 +395,25 @@ InstallGlobalFunction( AddCosetInfoStabChain, function( G, U, maxmoved )
vert := G.orbit{ [ 1 .. t1 ] };
img := G.orbit[ t ];
while img <> G.orbit[ 1 ] do
vert := OnTuples( vert, G.transversal[ img ] );
img := img ^ G.transversal[ img ];
vert := OnTuples( vert, G.transversal[ img ] );
img := img ^ G.transversal[ img ];
od;

# If $Ust = Us't'$ then $1t'/t/s in 1U$. Also if $1t'/t/s
# in 1U$ then $st/t' = u.g_1$ with $u in U, g_1 in G_1$
# and $g_1 = u_1.s'$ with $u_1 in U_1, s' in S_1$, so
# $Ust = Us't'$.
if ForAll( [ 1 .. t1 ], i -> not IsBound
( U.translabels[ pimg[ vert[ i ] ] ] ) ) then
( U.translabels[ pimg[ vert[ i ] ] ] ) ) then

# do all points
if t1<t-1 then
img := G.orbit[ t ];
if t<=10*t1lim then
vert := G.orbit{ [ 1 .. t - 1 ] };
while img <> G.orbit[ 1 ] do
vert := OnTuples( vert, G.transversal[ img ] );
img := img ^ G.transversal[ img ];
vert := OnTuples( vert, G.transversal[ img ] );
img := img ^ G.transversal[ img ];
od;
found:=ForAll( [ t1+1 .. t - 1 ], i -> not IsBound
( U.translabels[ pimg[ vert[ i ] ] ] ) );
Expand All @@ -298,16 +424,16 @@ InstallGlobalFunction( AddCosetInfoStabChain, function( G, U, maxmoved )
# long a list, failure will happen quickly.
tl:=[];
while img <> G.orbit[ 1 ] do
Add(tl,G.transversal[img]);
img := img ^ G.transversal[ img ];
Add(tl,G.transversal[img]);
img := img ^ G.transversal[ img ];
od;
found:=ForAll( [ t1+1 .. t - 1 ], i -> not IsBound
( U.translabels[ pimg[ vimg(G.orbit[ i ],tl) ] ] ) );
fi;

if found then
U.repsStab[ t ][ s ] := true;
index := index + lenflock;
U.repsStab[ t ][ s ] := true;
index := index + lenflock;
fi;

else
Expand All @@ -317,7 +443,9 @@ InstallGlobalFunction( AddCosetInfoStabChain, function( G, U, maxmoved )
fi;

t := t + 1;
od;
od;
fi;

s := s + 1;
od;

Expand Down Expand Up @@ -735,3 +863,90 @@ function(cos1,cos2)
od;
return [];
end);


#############################################################################
##
#F FactorCosetAction( <G>, <U>, [<N>] ) operation on the right cosets Ug
## with possibility to indicate kernel
##
BindGlobal("DoFactorCosetActionPerm",function(arg)
local G,u,op,h,N,rt,ac,actions,hom,i,q;
G:=arg[1];
u:=arg[2];
if Length(arg)>2 then
N:=arg[3];
else
N:=false;
fi;
if IsList(u) and Length(u)=0 then
u:=G;
Error("only trivial operation ? I Set u:=G;");
fi;
if N=false then
N:=Core(G,u);
fi;

ac:=ActionRefinedSeries(G,u);
actions:=ac[2];
ac:=ac[1];
hom:=false;
for i in [2..Length(ac)] do
if actions[i-1]<>fail
# allow 2GB memory use for writing down orbit
and SIZE_OBJ(actions[i-1][1])*IndexNC(ac[i],ac[i-1])<2*10^9 then

op:=rec();
h:=Orbit(ac[i],actions[i-1][1],actions[i-1][2]:permutations:=op);
if IsBound(op.permutations) then
rt:=List(op.permutations,PermList);
q:=Group(rt);
SetSize(q,IndexNC(G,N));
h:=GroupHomomorphismByImagesNC(ac[i],Group(rt),
op.generators,rt);
else
h:=ActionHomomorphism(ac[i],h,actions[i-1][2],"surjective");
fi;
else
rt:=RightTransversal(ac[i],ac[i-1]);
if not IsRightTransversalRep(rt) then
# the right transversal has no special `PositionCanonical' method.
rt:=List(rt,i->RightCoset(ac[i-1],i));
fi;
h:=ActionHomomorphism(ac[i],rt,OnRight,"surjective");

fi;
Unbind(op);
Unbind(rt);
if i=2 then
hom:=h;
else
hom:=KuKGenerators(ac[i],h,hom);;
q:=Group(hom);
StabChainOptions(q).limit:=Size(ac[i]);
hom:=GroupHomomorphismByImagesNC(ac[i],q,GeneratorsOfGroup(ac[i]),hom);;
fi;
od;

op:=Image(hom,G);
SetSize(op,IndexNC(G,N));

# and note our knowledge
SetKernelOfMultiplicativeGeneralMapping(hom,N);
AddNaturalHomomorphismsPool(G,N,hom);
return hom;
end);

InstallMethod(FactorCosetAction,"by right transversal operation",
IsIdenticalObj,[IsPermGroup,IsPermGroup],0,
function(G,U)
return DoFactorCosetActionPerm(G,U);
end);

InstallOtherMethod(FactorCosetAction,
"by right transversal operation, given kernel",IsFamFamFam,
[IsPermGroup,IsPermGroup,IsPermGroup],0,
function(G,U,N)
return DoFactorCosetActionPerm(G,U,N);
end);

6 changes: 5 additions & 1 deletion lib/factgrp.gd
Original file line number Diff line number Diff line change
Expand Up @@ -150,13 +150,17 @@ DeclareAttribute("NaturalHomomorphismsPool",IsGroup,
## gap> g:=Group((1,2,3,4,5),(1,2));;u:=SylowSubgroup(g,2);;Index(g,u);
## 15
## gap> FactorCosetAction(g,u);
## <action epimorphism>
## [ (1,2,3,4,5), (1,2) ] -> [ (1,4,7,10,13)(2,5,8,11,14)(3,6,9,12,15),\
## (1,4)(2,6)(3,5)(7,8)(10,12)(13,14) ]
## gap> StructureDescription(Range(last));
## "S5"
## gap> FactorCosetAction(g,[u,SylowSubgroup(g,3)]);;
## gap> Size(Image(last));
## 120
## ]]></Example>
## The correspondence of points with cosets will, for performance reasons,
## depend on the method used. It is not guaranteed that it will be the same
## as used by <C>RightTransversal</C> or <C>RightCosets</C>.
## </Description>
## </ManSection>
## <#/GAPDoc>
Expand Down
Loading
Loading