From 618b05823c50742e0fbfd736597adb0506b4bf32 Mon Sep 17 00:00:00 2001 From: Alexander Hulpke Date: Thu, 29 Sep 2022 15:57:50 -0600 Subject: [PATCH] ENHANCE: MinimalGeneratingSet method for arbitrary finite groups --- lib/grp.gd | 10 +-- lib/grp.gi | 90 +++++++++++++++++++ lib/grplatt.gd | 6 ++ lib/grplatt.gi | 33 +++++++ .../2022-09-09-MinimalGeneratingSet.tst | 3 +- tst/teststandard/permgrp.tst | 5 ++ 6 files changed, 139 insertions(+), 8 deletions(-) diff --git a/lib/grp.gd b/lib/grp.gd index bdbfb7336b..860ae8862b 100644 --- a/lib/grp.gd +++ b/lib/grp.gd @@ -2181,12 +2181,10 @@ DeclareAttribute( "LargestElementGroup", IsGroup ); ## ## returns a generating set of G of minimal possible length. ##

-## Note that –apart from special cases– currently there are only -## efficient methods known to compute minimal generating sets of finite -## solvable groups and of finitely generated nilpotent groups. -## Hence so far these are the only cases for which methods are available. -## The former case is covered by a method implemented in the ⪆ library, -## while the second case requires the package Polycyclic. +## Note that only methods for finite groups, solvable groups, or finitely +## generated nilpotent groups are available (the latter through the +## Polycyclic package) and that +## calculations for nonsolvable finite groups of higher rank can be expensive. ##

## If you do not really need a minimal generating set, but are satisfied ## with getting a reasonably small set of generators, you better use diff --git a/lib/grp.gi b/lib/grp.gi index b8ec336d5b..20ecee1c1a 100644 --- a/lib/grp.gi +++ b/lib/grp.gi @@ -142,6 +142,96 @@ function(G) fi; end); +InstallOtherMethod(MinimalGeneratingSet,"finite groups",true, + [IsGroup and IsFinite],0, +function(g) +local r,i,j,u,f,q,n,lim,sel,nat,ok,mi; + if not HasIsSolvableGroup(g) and IsSolvableGroup(g) and + CanEasilyComputePcgs(g) then + return MinimalGeneratingSet(g); + fi; + # start at rank 2/abelian rank + n:=AbelianInvariants(g); + if Length(n)>0 then + r:=Maximum(List(Set(List(n,SmallestPrimeDivisor)), + x->Number(n,y->y mod x=0))); + else r:=0; fi; + r:=Maximum(r,2); + n:=false; + repeat + if Length(GeneratorsOfGroup(g))=r then + return GeneratorsOfGroup(g); + fi; + for i in [1..10^r] do + u:=SubgroupNC(g,List([1..r],x->Random(g))); + if Size(u)=Size(g) then return GeneratorsOfGroup(u);fi; # found + od; + f:=FreeGroup(r); + ok:=false; + if IsPerfectGroup(g) then + if n=false then + n:=ShallowCopy(NormalSubgroups(g)); + # all perfect groups of order <15360 *are* 2-generated + lim:=15360; + n:=Filtered(n,x->IndexNC(g,x)>=lim and Size(x)>1); + SortBy(n,x->-Size(x)); + mi:=MinimalInclusionsGroups(n); + fi; + i:=1; + while i<=Length(n) do + ok:=false; + # is factor randomly r-generated? + q:=2^r; + while ok=false and q>0 do + u:=n[i]; + for j in [1..r] do + u:=ClosureGroup(u,Random(g)); + od; + ok:=Size(u)=Size(g); + q:=q-1; + od; + + if not ok then + # is factor a nonsplit extension with minimal normal -- if so rank + # stays the same, no new test + + # minimal overnormals + sel:=List(Filtered(mi,x->x[1]=i),x->x[2]); + if Length(sel)>0 then + nat:=NaturalHomomorphismByNormalSubgroupNC(g,n[i]); + for j in sel do + if not ok then + # nonsplit extension (so pre-images will still generate)? + ok:=0=Length( + ComplementClassesRepresentatives(Image(nat),Image(nat,n[j]))); + fi; + od; + fi; + fi; + + if not ok then + q:=GQuotients(f,g/n[i]:findall:=false); + if Length(q)=0 then + # fail in quotient + i:=Length(n)+10; + Info(InfoGroup,2,"Rank ",r," fails in quotient\n"); + fi; + fi; + + i:=i+1; + od; + + fi; + if n=false or i<=Length(n)+1 then + # still try group + q:=GQuotients(f,g:findall:=false); + if Length(q)>0 then return r;fi; # found + fi; + r:=r+1; + until false; +end); + + ############################################################################# ## #M IsElementaryAbelian() . . . . . test if a group is elementary abelian diff --git a/lib/grplatt.gd b/lib/grplatt.gd index 3daf75738b..0ab6d2f7a7 100644 --- a/lib/grplatt.gd +++ b/lib/grplatt.gd @@ -577,3 +577,9 @@ DeclareOperation("MinimalFaithfulPermutationRepresentation", DeclareGlobalFunction("DescSubgroupIterator"); DeclareGlobalFunction("SubgroupConditionAbove"); + +# Utility function +# MinimalInclusionsGroups(l) +# returns a list of all inclusion indices [a,b] where l[a] is maximal subgroup +# of l[b]. +DeclareGlobalFunction("MinimalInclusionsGroups"); diff --git a/lib/grplatt.gi b/lib/grplatt.gi index 4cad162a28..a1cf55e534 100644 --- a/lib/grplatt.gi +++ b/lib/grplatt.gi @@ -3737,3 +3737,36 @@ local divs,limit,mode,l,process,done,bound,maxer,prime; end)); end); +# Utility function +# MinimalInclusionsGroups(l) +# returns a list of all inclusion indices [a,b] where l[a] is maximal subgroup +# of l[b]. +InstallGlobalFunction(MinimalInclusionsGroups,function(l) +local s,p,incl,cont,i,j,done; + # sort increasing size + s:=List(l,Size); + p:=Sortex(s); + l:=Permuted(l,p); + s:=List(l,Size); + incl:=[]; + cont:=[]; + for i in [Length(l),Length(l)-1..1] do + # those we know it will be in + done:=[i]; + for j in [i+1..Length(l)] do + if not j in done and s[j]>s[i] and s[j] mod s[i]=0 then + if IsSubset(l[j],l[i]) then + Add(incl,[i,j]); + done:=Union(done,cont[j]); + fi; + fi; + od; + cont[i]:=done; + od; + p:=p^-1; + incl:=List(incl,x->OnTuples(x,p)); + Sort(incl); + return incl; +end); + + diff --git a/tst/testbugfix/2022-09-09-MinimalGeneratingSet.tst b/tst/testbugfix/2022-09-09-MinimalGeneratingSet.tst index 98d0d13389..efc68115cf 100644 --- a/tst/testbugfix/2022-09-09-MinimalGeneratingSet.tst +++ b/tst/testbugfix/2022-09-09-MinimalGeneratingSet.tst @@ -1,5 +1,4 @@ gap> G:=Group((1,2),(2,3),(3,4));; gap> H:=Image(IsomorphismFpGroup(G));; gap> MinimalGeneratingSet(H); -Error, no method found! For debugging hints type ?Recovery from NoMethodFound -Error, no 4th choice method found for `MinimalGeneratingSet' on 1 arguments +[ F1^-1*F2*F1^-1*F3^-1, F1^-1*F2^-1*F3^-1 ] diff --git a/tst/teststandard/permgrp.tst b/tst/teststandard/permgrp.tst index 1031360c1e..2db111f47a 100644 --- a/tst/teststandard/permgrp.tst +++ b/tst/teststandard/permgrp.tst @@ -140,5 +140,10 @@ true gap> Length(ConjugacyClasses(PSL(2,64))); 65 +# MinimalGeneratingSet +gap> AllTransitiveGroups(NrMovedPoints,12, +> x->Length(MinimalGeneratingSet(x)),4); +[ [3^4:2^3]E(4) ] + # gap> STOP_TEST( "permgrp.tst", 1);