diff --git a/lib/autsr.gi b/lib/autsr.gi index 217f9f9fff..b745507640 100644 --- a/lib/autsr.gi +++ b/lib/autsr.gi @@ -1015,7 +1015,7 @@ local ff,r,d,ser,u,v,i,j,k,p,bd,e,gens,lhom,M,N,hom,Q,Mim,q,ocr,split,MPcgs, for j in b do N:=ClosureSubgroup(N,b); od; - # insert + # insert in series for j in [Length(ser),Length(ser)-1..i+1] do ser[j+1]:=ser[j]; od; @@ -1034,13 +1034,13 @@ local ff,r,d,ser,u,v,i,j,k,p,bd,e,gens,lhom,M,N,hom,Q,Mim,q,ocr,split,MPcgs, Info(InfoMorph,2,"insert2"); fi; N:=ser[i+1]; # the added normal - if rada<>fail - and ForAny(GeneratorsOfGroup(rada),x->N<>Image(x,N)) then - Info(InfoMorph,3,"radical automorphism stabilizer"); - SetIsGroupOfAutomorphismsFiniteGroup(rada,true); - NiceMonomorphism(rada:autactbase:=fail,someCharacteristics:=fail); - rada:=Stabilizer(rada,N,asAutom); - fi; + fi; + if rada<>fail + and ForAny(GeneratorsOfGroup(rada),x->N<>Image(x,N)) then + Info(InfoMorph,3,"radical automorphism stabilizer"); + SetIsGroupOfAutomorphismsFiniteGroup(rada,true); + NiceMonomorphism(rada:autactbase:=fail,someCharacteristics:=fail); + rada:=Stabilizer(rada,N,asAutom); fi; fi; until split or fratsim; @@ -1332,7 +1332,6 @@ local ff,r,d,ser,u,v,i,j,k,p,bd,e,gens,lhom,M,N,hom,Q,Mim,q,ocr,split,MPcgs, # move back to bad degree rada:=Group(List(GeneratorsOfGroup(rada), x-> InducedAutomorphism(InverseGeneralMapping(ind),x))); - fi; fi; diff --git a/lib/ctblfuns.gd b/lib/ctblfuns.gd index ae879e38b1..9249ca63f1 100644 --- a/lib/ctblfuns.gd +++ b/lib/ctblfuns.gd @@ -1013,7 +1013,7 @@ DeclareAttribute( "TrivialCharacter", IsGroup ); ## gap> NaturalCharacter( Group( [ [ 0, -1 ], [ 1, -1 ] ] ) ); ## Character( CharacterTable( Group([ [ [ 0, -1 ], [ 1, -1 ] ] ]) ), ## [ 2, -1, -1 ] ) -## gap> d8:= DihedralGroup( 8 );; hom:= IsomorphismPermGroup( d8 );; +## gap> d8:= DihedralGroup( 8 );; hom:= RegularActionHomomorphism( d8 );; ## gap> NaturalCharacter( hom ); ## Character( CharacterTable( ), ## [ 8, 0, 0, 0, 0 ] ) diff --git a/lib/ctblgrp.gi b/lib/ctblgrp.gi index e033265dc4..015f75dc8e 100644 --- a/lib/ctblgrp.gi +++ b/lib/ctblgrp.gi @@ -82,6 +82,7 @@ local D,C,cl,pl; D.classes:= ConjugacyClasses( C ); cl:=ShallowCopy(D.classes); D.classreps:=List(cl,Representative); + D.centfachom:=[]; D.klanz:=Length(cl); D.classrange:=[1..D.klanz]; Info(InfoCharacterTable,1,D.klanz," classes"); @@ -1727,7 +1728,23 @@ DoubleCentralizerOrbit := function(D,c1,c2) else #Info(InfoCharacterTable,3,"using DoubleCosets;"); cent:=Centralizer(D.classes[inv]); - l:=DoubleCosetRepsAndSizes(D.group,cent,Centralizer(D.classes[c2])); + if IndexNC(D.group,cent)<=10^5 then + if not IsBound(D.centfachom[inv]) then + e:=ActionHomomorphism(D.group,RightTransversal(D.group,cent),OnRight, + "surjective"); + Image(e); + # do not use action later on + e:=AsGroupGeneralMappingByImages(e); + D.centfachom[inv]:=e; + else + e:=D.centfachom[inv]; + fi; + s:=Orbits(Image(e,Centralizer(D.classes[c2])),[1..IndexNC(D.group,cent)]); + l:=List(s,x->[PreImagesRepresentative(e, + RepresentativeAction(Image(e),1,x[1])),Size(cent)*Length(x)]); + else + l:=DoubleCosetRepsAndSizes(D.group,cent,Centralizer(D.classes[c2])); + fi; s1:=Size(cent); e:=[]; s:=[]; diff --git a/lib/factgrp.gd b/lib/factgrp.gd index 9be74a06cc..2567571be0 100644 --- a/lib/factgrp.gd +++ b/lib/factgrp.gd @@ -232,11 +232,12 @@ DeclareSynonym( "ImproveOperationDegreeByBlocks", ## called internally might try a degree reduction.) ##

## iso:=RegularActionHomomorphism(SymmetricGroup(4));; ## gap> image:= Image( iso );; NrMovedPoints( image ); ## 24 ## gap> small:= SmallerDegreePermutationRepresentation( image );; ## gap> Image( small ); -## Group([ (2,3), (1,2,3), (1,3)(2,4), (1,2)(3,4) ]) +## Group([ (2,5,4,3), (1,4)(2,6)(3,5) ]) ## gap> g:=Image(IsomorphismPermGroup(GL(4,5)));; ## gap> sm:=SmallerDegreePermutationRepresentation(g:cheap);; ## gap> NrMovedPoints(Range(sm)); diff --git a/lib/factgrp.gi b/lib/factgrp.gi index 7a167d6040..b17831904b 100644 --- a/lib/factgrp.gi +++ b/lib/factgrp.gi @@ -266,6 +266,10 @@ local pool,p,h,ise,emb,i,j; fi; od; ise:=List(ise,i->GetNaturalHomomorphismsPool(G,pool.ker[i])); + if not (ForAll(ise,IsPcGroup) or ForAll(ise,IsPermGroup)) then + ise:=List(ise,x->x*IsomorphismPermGroup(Image(x))); + fi; + h:=CallFuncList(DirectProduct,List(ise,Image)); emb:=List([1..Length(ise)],i->Embedding(h,i)); emb:=List(GeneratorsOfGroup(G), diff --git a/lib/fitfree.gd b/lib/fitfree.gd index ed3d53ae3e..3f08944b8e 100644 --- a/lib/fitfree.gd +++ b/lib/fitfree.gd @@ -141,6 +141,7 @@ InstallTrueMethod(CanComputeFittingFree,HasFittingFreeLiftSetup); ## ## <#/GAPDoc> DeclareGlobalFunction("FittingFreeSubgroupSetup"); +DeclareOperation("DoFFSS",[IsGroup,IsGroup]); # This attribute is used for groups treated by constructive recognition and # a composition tree. It is declared in the library such that the function diff --git a/lib/fitfree.gi b/lib/fitfree.gi index 1be29c8779..b04218e19b 100644 --- a/lib/fitfree.gi +++ b/lib/fitfree.gi @@ -41,21 +41,11 @@ local R; fi; end); -InstallGlobalFunction(FittingFreeSubgroupSetup,function(G,U) -local cache,ffs,pcisom,rest,it,kpc,k,x,ker,r,pool,i,xx,inv,pregens; - ffs:=FittingFreeLiftSetup(G); +InstallMethod(DoFFSS,"generic",IsIdenticalObj,[IsGroup and IsFinite,IsGroup],0, +function(G,U) +local ffs,pcisom,rest,it,kpc,k,x,ker,r,pool,i,xx,inv,pregens,iso; - # result cached? - if not IsBound(U!.cachedFFS) then - cache:=[]; - U!.cachedFFS:=cache; - else - cache:=U!.cachedFFS; - fi; - r:=First(cache,x->IsIdenticalObj(x[1],ffs)); - if r<>fail then - return r[2]; - fi; + ffs:=FittingFreeLiftSetup(G); pcisom:=ffs.pcisom; @@ -90,25 +80,51 @@ local cache,ffs,pcisom,rest,it,kpc,k,x,ker,r,pool,i,xx,inv,pregens; k:=ffs.pcgs; else - inv:=RestrictedInverseGeneralMapping(rest); - pregens:=List(SmallGeneratingSet(Image(rest)), - x->ImagesRepresentative(inv,x)); - it:=CoKernelGensIterator(inv); - kpc:=TrivialSubgroup(Image(pcisom)); - while not IsDoneIterator(it) do - x:=NextIterator(it); - pool:=[x]; - for x in pool do - xx:=ImagesRepresentative(pcisom,x); - if not xx in kpc then - kpc:=ClosureGroup(kpc,xx); - for i in pregens do - Add(pool,x^i); - od; + iso:=IsomorphismFpGroup(Image(rest,U)); + pregens:=List(GeneratorsOfGroup(Range(iso)),x-> + PreImagesRepresentative(rest,PreImagesRepresentative(iso,x))); + # evaluate relators + pool:=List(RelatorsOfFpGroup(Range(iso)), + x->MappedWord(x,FreeGeneratorsOfFpGroup(Range(iso)),pregens)); + # divide off original generators + Append(pool,List(GeneratorsOfGroup(U),x->x/ + MappedWord(UnderlyingElement(ImagesRepresentative(iso,ImagesRepresentative(ffs.factorhom,x))),FreeGeneratorsOfFpGroup(Range(iso)),pregens))); + + + pool:=List(pool,x->ImagesRepresentative(pcisom,x)); + kpc:=SubgroupNC(Image(pcisom),pool); + pool:=List(SmallGeneratingSet(kpc),x->PreImage(pcisom,x)); + # normal closure + for x in pool do + for i in GeneratorsOfGroup(U) do + xx:=x^i; + k:=ImagesRepresentative(pcisom,xx); + if not k in kpc then + kpc:=ClosureSubgroupNC(kpc,k); + Add(pool,xx); fi; od; - #Print("|pool|=",Length(pool),"\n"); od; + +# inv:=RestrictedInverseGeneralMapping(rest); +# pregens:=List(SmallGeneratingSet(Image(rest)), +# x->ImagesRepresentative(inv,x)); +# it:=CoKernelGensIterator(inv); +# kpc:=TrivialSubgroup(Image(pcisom)); +# while not IsDoneIterator(it) do +# x:=NextIterator(it); +# pool:=[x]; +# for x in pool do +# xx:=ImagesRepresentative(pcisom,x); +# if not xx in kpc then +# kpc:=ClosureGroup(kpc,xx); +# for i in pregens do +# Add(pool,x^i); +# od; +# fi; +# od; +# #Print("|pool|=",Length(pool),"\n"); +# od; SetSize(U,Size(Image(rest))*Size(kpc)); k:=InducedPcgs(FamilyPcgs(Image(pcisom)),kpc); k:=List(k,x->PreImagesRepresentative(pcisom,x)); @@ -131,6 +147,44 @@ local cache,ffs,pcisom,rest,it,kpc,k,x,ker,r,pool,i,xx,inv,pregens; pcgs:=k, serdepths:=List(ffs.depths,y->First([1..Length(r)],x->r[x]>=y)) ); + + return r; + +end); + +InstallGlobalFunction(FittingFreeSubgroupSetup,function(G,U) +local ffs,cache,rest,ker,k,r; + ffs:=FittingFreeLiftSetup(G); + + # result cached? + if not IsBound(U!.cachedFFS) then + cache:=[]; + U!.cachedFFS:=cache; + else + cache:=U!.cachedFFS; + fi; + r:=First(cache,x->IsIdenticalObj(x[1],ffs)); + if r<>fail then + return r[2]; + fi; + + if IsIdenticalObj(G,U) or GeneratorsOfGroup(G)=GeneratorsOfGroup(U) then + rest:=ffs.factorhom; + ker:=ffs.radical; + k:=ffs.pcgs; + r:=[1..Length(k)]; + r:=rec(parentffs:=ffs, + rest:=rest, + ker:=ker, + pcgs:=k, + serdepths:=List(ffs.depths,y->First([1..Length(r)],x->r[x]>=y)) + ); + else + r:=DoFFSS(G,U); + fi; + + k:=r.pcgs; + rest:=r.rest; Add(cache,[ffs,r]); # keep if Length(k)=0 then SetSize(U,Size(Image(rest))); diff --git a/lib/ghom.gd b/lib/ghom.gd index 12f8e76c90..d658a5392e 100644 --- a/lib/ghom.gd +++ b/lib/ghom.gd @@ -673,3 +673,5 @@ DeclareAttribute( "ImagesSmallestGenerators", ## <#/GAPDoc> ## DeclareAttribute( "RegularActionHomomorphism", IsGroup ); + +DeclareGlobalName("IsomorphismAbelianGroupViaIndependentGenerators"); diff --git a/lib/ghom.gi b/lib/ghom.gi index 37c1bf98e6..bde77886d3 100644 --- a/lib/ghom.gi +++ b/lib/ghom.gi @@ -1397,6 +1397,10 @@ function ( G ) if not HasIsAbelian( G ) and IsAbelian( G ) then # Redispatch to give the special methods for abelian groups a chance. return IsomorphismPermGroup( G ); + elif (not HasIsSolvableGroup(G)) and IsSolvableGroup(G) then + # Redispatch to give the special methods for solvable groups a chance. + return IsomorphismPermGroup( G ); + # MH: Disabled the following code for now, as computing IsNilpotentGroup # can be very expensive, depending on the group type. We could # re-enable it for e.g. pc groups, but I am not sure whether it is diff --git a/lib/grp.gd b/lib/grp.gd index 684b829ae8..c5c4f7edab 100644 --- a/lib/grp.gd +++ b/lib/grp.gd @@ -4279,10 +4279,9 @@ DeclareAttribute( "IsomorphismSpecialPcGroup", IsGroup ); ## gap> g:=SmallGroup(24,12); ## ## gap> iso:=IsomorphismPermGroup(g); -## +## [ f1, f2, f3, f4 ] -> [ (2,3), (2,3,4), (1,2)(3,4), (1,3)(2,4) ] ## gap> Image(iso,g.3*g.4); -## (1,12)(2,16)(3,19)(4,5)(6,22)(7,8)(9,23)(10,11)(13,24)(14,15)(17, -## 18)(20,21) +## (1,4)(2,3) ## ]]> ##

## In many cases the permutation representation constructed by diff --git a/lib/grppc.gi b/lib/grppc.gi index 286c7fd9cf..85a73c19ef 100644 --- a/lib/grppc.gi +++ b/lib/grppc.gi @@ -2876,3 +2876,134 @@ local pcgs; factorhom:=NaturalHomomorphismByNormalSubgroupNC(G,G)); end ); + +############################################################################# +## +#M IsomorphismPermGroup( ) . . . . . . . . . for solvable group +## +InstallMethod( IsomorphismPermGroup, + "solvable groups, e.g. Hall action", + [ IsGroup and IsFinite and IsSolvableGroup and CanEasilyComputePcgs ], +function ( G ) +local d,i,j,a,iso,ims,gens,s,p,abovent; + if IsAbelian(G) then + # force redispatch on abelian group + return IsomorphismAbelianGroupViaIndependentGenerators(IsPermGroup,G); + elif Size(G)<=100 and Size(G)<>64 then + # for these orders the optimal degree is fast enough + iso:=MinimalFaithfulPermutationRepresentation(G); + Info(InfoPcGroup,1,"Use optimal degree ", + Size(G),":",NrMovedPoints(Range(iso))); + return iso; + fi; + + # can be subdirect? + d:=MinimalNormalSubgroups(G); + if Length(d)>1 then + # it can -- find a nice decomposition in two + + # take two largest + d:=ShallowCopy(d); + SortBy(d,x->-Size(x)); + abovent:=function(a,b) + local hom,n; + hom:=NaturalHomomorphismByNormalSubgroupNC(G,a); + b:=Image(hom,b); + n:=NormalSubgroups(Image(hom,a)); + n:=Filtered(n,x->not IsSubset(x,b)); + SortBy(n,x->-Size(x)); # descending order + return List(n,x->PreImage(hom,x)); + end; + + d:=[abovent(d[1],d[2]),abovent(d[2],d[1])]; + if Size(d[2][1])>Size(d[1][1]) then d:=Reversed(d);fi; + d:=[d[1][1],First(d[2],x->Size(Intersection(x,d[1][1]))=1)]; + Info(InfoPcGroup,1,"Subdirect ",List(d,x->Size(G)/Size(x))); + iso:=List(d,x->NaturalHomomorphismByNormalSubgroupNC(G,x)); + d:=List(iso,x->IsomorphismPermGroup(Image(x,G))); + ims:=List([1,2],x->List(GeneratorsOfGroup(G), + y->ImagesRepresentative(d[x],ImagesRepresentative(iso[x],y)))); + ims:=SubdirectDiagonalPerms(ims[1],ims[2]); + d:=Group(ims); + UseIsomorphismRelation(G,d); + s:=SmallerDegreePermutationRepresentation(d:cheap); + if NrMovedPoints(Range(s))ImagesRepresentative(s,x)); + d:=Image(s); + UseIsomorphismRelation(G,d); + fi; + iso:=GroupHomomorphismByImagesNC(G,d,GeneratorsOfGroup(G),ims); + Assert(1,IsBijective(iso)); + SetIsBijective(iso,true); + return iso; + fi; + + p:=Collected(Factors(Size(G))); + if Length(p)>1 then + d:=Combinations(p); + SortBy(d,x->-Product(x,y->y[1]^y[2])); + for i in d do + s:=HallSubgroup(G,List(i,x->x[1])); + if Size(Core(G,s))=1 then + Info(InfoPcGroup,1,"Hall ",List(i,x->x[1])); + + # try normalizer quotient + d:=Normalizer(G,s); + if Size(Core(G,d))=1 then + s:=d; + else + d:=ShallowCopy(IntermediateSubgroups(d,s).subgroups); + SortBy(d,x->-Size(x)); + a:=First(d,x->Size(Core(G,x))=1); + if a<>fail then s:=a;fi; + fi; + + iso:=FactorCosetAction(G,s); + Assert(1,IsBijective(iso)); + SetIsBijective(iso,true); + d:=Image(iso); + UseIsomorphismRelation(G,d); + return iso; + fi; + od; + + else + # p-group, one minimal: try to go from factor permrep + d:=MinimalNormalSubgroups(G)[1]; + ims:=NaturalHomomorphismByNormalSubgroupNC(G,d); + iso:=IsomorphismPermGroup(Image(ims,G)); + ims:=ims*iso; + a:=Image(ims,G); + Info(InfoPcGroup,1,"Factor ",List(Orbits(a,MovedPoints(a)),Length)); + s:=List(Orbits(a,MovedPoints(a)),x->Stabilizer(a,x[1])); + s:=List(s,x->PreImage(ims,x)); + SortBy(s,x->-Size(x)); + for j in [1..Length(Factors(Size(s[1])))] do + for i in [1..Length(s)] do + p:=LowLayerSubgroups(s[i],j); + p:=Filtered(p,x->not IsSubset(x,d)); + if Length(p)>0 then + d:=Maximum(List(p,Size)); + iso:=FactorCosetAction(G,First(p,x->Size(x)=d)); + ims:=[List(GeneratorsOfGroup(G),x->ImagesRepresentative(iso,x)), + List(GeneratorsOfGroup(G),x->ImagesRepresentative(ims,x))]; + ims:=SubdirectDiagonalPerms(ims[1],ims[2]); + d:=Group(ims); + UseIsomorphismRelation(G,d); + s:=SmallerDegreePermutationRepresentation(d:cheap); + if NrMovedPoints(Range(s))ImagesRepresentative(s,x)); + d:=Image(s); + UseIsomorphismRelation(G,d); + fi; + iso:=GroupHomomorphismByImagesNC(G,d,GeneratorsOfGroup(G),ims); + Assert(1,IsBijective(iso)); + SetIsBijective(iso,true); + return iso; + fi; + od; + od; + + Error("should never happen"); + fi; +end); diff --git a/tst/testbugfix/2022-12-24-IsomorphismGroups.tst b/tst/testbugfix/2022-12-24-IsomorphismGroups.tst new file mode 100644 index 0000000000..b45688bcef --- /dev/null +++ b/tst/testbugfix/2022-12-24-IsomorphismGroups.tst @@ -0,0 +1,15 @@ +# Reported by B.Sambale on 12/24/22 in the forum +gap> G:=Group([(1,2,3,4,5)(6,8,7), +> (9,10,12,17,11,15)(13,19,28,38,50,45)(14,21,26,24,23, +> 16)(18,27,35,46,42,36)(20,30,25,34,44,51)(22,32,41,29,39,47)(31,37,49,55, +> 54,48)(33,43,53,40,52,56), (9,11,16,24)(10,13,14,20)(12,18,21,31)(15,22, +> 23,33)(17,25,26,28)(19,29,30,40)(27,36,37,48)(32,42,43,54)(34,45,38, +> 51)(35,47,49,56)(39,41,52,53)(44,46,50,55), +> (10,14)(13,20)(15,23)(17,26)(22,33)(25,28)(27,37)(36,48)(39,52)(41,53)(44, +> 50)(46,55) ]);; +gap> SetAssertionLevel(0); +gap> IsomorphismGroups(G,G)<>fail; +true +gap> g5:=DirectProduct(SmallGroup(1800,328),CyclicGroup(IsPermGroup,4));; +gap> Size(AutomorphismGroup(g5)); +3840