diff --git a/lib/csetgrp.gd b/lib/csetgrp.gd index c54b04931d..5ba8b12e84 100644 --- a/lib/csetgrp.gd +++ b/lib/csetgrp.gd @@ -441,3 +441,6 @@ DeclareGlobalFunction("IntermediateGroup"); # forward declaration for recursive call. DeclareGlobalFunction("DoConjugateInto"); +# action-based ascending series for perm group +DeclareGlobalFunction("ActionRefinedSeries"); + diff --git a/lib/csetgrp.gi b/lib/csetgrp.gi index 60e1bbc51a..7e0eefddaf 100644 --- a/lib/csetgrp.gi +++ b/lib/csetgrp.gi @@ -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); @@ -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? diff --git a/lib/csetperm.gi b/lib/csetperm.gi index 56aba83e81..7d7ca1d7bb 100644 --- a/lib/csetperm.gi +++ b/lib/csetperm.gi @@ -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(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 ) ); @@ -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 @@ -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) @@ -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)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, @@ -269,8 +395,8 @@ 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 @@ -278,7 +404,7 @@ InstallGlobalFunction( AddCosetInfoStabChain, function( G, U, maxmoved ) # 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 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 ] ] ] ) ); @@ -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 @@ -317,7 +443,9 @@ InstallGlobalFunction( AddCosetInfoStabChain, function( G, U, maxmoved ) fi; t := t + 1; - od; + od; + fi; + s := s + 1; od; @@ -735,3 +863,90 @@ function(cos1,cos2) od; return []; end); + + +############################################################################# +## +#F FactorCosetAction( , , [] ) 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); + diff --git a/lib/factgrp.gd b/lib/factgrp.gd index 2567571be0..b8feb8d310 100644 --- a/lib/factgrp.gd +++ b/lib/factgrp.gd @@ -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); -## +## [ (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 ## ]]> +## 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 RightTransversal or RightCosets. ## ## ## <#/GAPDoc> diff --git a/lib/oprt.gi b/lib/oprt.gi index c1def0b87c..5b13a6d7a0 100644 --- a/lib/oprt.gi +++ b/lib/oprt.gi @@ -985,11 +985,24 @@ end ); InstallMethod( OrbitOp, "with domain", true, OrbitishReq,0, function( G, D, pnt, gens, acts, act ) -local orb,d,gen,i,p; +local orb,d,gen,i,p,permrec,perms; + # is there an option indicating a wish to calculate permutations? + permrec:=ValueOption("permutations"); + if permrec<>fail then + if not IsRecord(permrec) then + Error("asks for permutations, but no record given"); + fi; + perms:=List(gens,x->[]); + permrec.generators:=gens; + permrec.permutations:=perms; + fi; + pnt:=Immutable(pnt); - d:=NewDictionary(pnt,false,D); orb := [ pnt ]; - AddDictionary(d,pnt); + if permrec=fail then + d:=NewDictionary(pnt,false,D); + AddDictionary(d,pnt); + fi; for p in orb do for gen in acts do i:=act(p,gen); @@ -1010,20 +1023,51 @@ InstallOtherMethod( OrbitOp, "standard orbit algorithm:list", true, IsList, IsFunction ], 0, function( G, pnt, gens, acts, act ) -local orb,d,gen,i,p,D; +local orb,d,gen,i,p,D,perms,permrec,gp,op,l; + # is there an option indicating a wish to calculate permutations? + permrec:=ValueOption("permutations"); + if permrec<>fail then + if not IsRecord(permrec) then + Error("asks for permutations, but no record given"); + fi; + perms:=List(gens,x->[]); + permrec.generators:=gens; + permrec.permutations:=perms; + fi; + # try to find a domain D:=DomainForAction(pnt,acts,act); pnt:=Immutable(pnt); - d:=NewDictionary(pnt,false,D); orb := [ pnt ]; - AddDictionary(d,pnt); + if permrec=fail then + d:=NewDictionary(pnt,false,D); + AddDictionary(d,pnt); + else + d:=NewDictionary(pnt,true,D); + AddDictionary(d,pnt,1); + fi; + op:=0; for p in orb do + op:=op+1; + gp:=0; for gen in acts do + gp:=gp+1; i:=act(p,gen); MakeImmutable(i); - if not KnowsDictionary(d,i) then - Add( orb, i ); - AddDictionary(d,i); + if permrec=fail then + if not KnowsDictionary(d,i) then + Add( orb, i ); + AddDictionary(d,i); + fi; + else + l:=LookupDictionary(d,i); + if l=fail then + Add( orb, i ); + AddDictionary(d,i,Length(orb)); + perms[gp][op]:=Length(orb); + else + perms[gp][op]:=l; + fi; fi; od; od; diff --git a/tst/teststandard/permgrp.tst b/tst/teststandard/permgrp.tst index 6d21bb3d92..2c989d0652 100644 --- a/tst/teststandard/permgrp.tst +++ b/tst/teststandard/permgrp.tst @@ -7,7 +7,7 @@ gap> START_TEST("permgrp.tst"); gap> Size(Normalizer(SymmetricGroup(100),PrimitiveGroup(100,1))); 1209600 gap> g:=Image(RegularActionHomomorphism(AbelianGroup([4,5,5])));; -gap> Size(Normalizer(SymmetricGroup(100),g)); +gap> Size(Normalizer(SymmetricGroup(100),g)); 96000 # the following tests used to choke GAP, because GAP failed to find @@ -136,7 +136,7 @@ false gap> IsConjugatorAutomorphism(hom); true -# classes over larger field extension +# classes over larger field extension gap> Length(ConjugacyClasses(PSL(2,64))); 65 @@ -149,5 +149,12 @@ gap> AllTransitiveGroups(NrMovedPoints,12, gap> MinimalFaithfulPermutationDegree(SmallGroup(5^6,33)); 55 +# FactorCosetAction +gap> g:=MathieuGroup(23);; +gap> u:=Normalizer(g,SylowSubgroup(g,3));; +gap> act:=FactorCosetAction(g,u);; +gap> NrMovedPoints(Range(act)); +70840 + # gap> STOP_TEST( "permgrp.tst", 1);