diff --git a/lib/grp.gd b/lib/grp.gd index 401edcd7d6..67c9e8ed31 100644 --- a/lib/grp.gd +++ b/lib/grp.gd @@ -2216,6 +2216,11 @@ DeclareAttribute( "LargestElementGroup", IsGroup ); ## with getting a reasonably small set of generators, you better use ## . ##

+## Another way to find the minimal generating set is +## MinimalGeneratingSetUsingChiefSeries +## which executes in time polynominally bounded by the size of group, +## but is slower than MinimalGeneratingSet for practical purposes. +##

## Information about the minimal generating sets of the finite simple ## groups of order less than 10^6 can be found in . ## See also the package AtlasRep. diff --git a/lib/grp.gi b/lib/grp.gi index 76c8861eb5..56b1560228 100644 --- a/lib/grp.gi +++ b/lib/grp.gi @@ -11,6 +11,106 @@ ## This file contains generic methods for groups. ## +############################################################################# +## +#M MinimalGeneratingSetUsingChiefSeries( ) +## +# This algorithm is described in the paper +# "The Minimum Generating Set Problem" by Dhara Thakar and Andrea Lucchini. +# link : https://arxiv.org/abs/2306.07633 +BindGlobal("MinimalGeneratingSetUsingChiefSeries",function(G) + local + cs, # The chief series of G + check, # A function to check if GbyGk is generated by cosets with given representative + GbyGk, # The quotient group of G with the k+1 st group in its chief series (1st is G) + Gkm1byGk, # Quotient of the kth and k-1 th groups in chief series of G. + Gkm1byGk_elem_reps, # The coset representatives of Gkm1byGk + Gkm1byGk_gen, # A (small) generating set of Gkm1byGk + Gkm1byGk_gen_reps, # The coset representatives (CR) of elements Gkm1byGk_gen + mingenset_k_reps, # The CR of minimum generating set (MGS) of GbyGkm1 + phi_GbyG1, # Homomorphism for quotient group GbyG1 + GbyG1, # Quotient of G and 2nd group in its chief series + phi_GbyGk, # Homomorphism for quotient group GbyG1 + phi_Gkm1byGk, # Homomorphism for quotient group Gkm1byGk + temp,i,j,l,L,x,xl,prev,gmod,g,g0,g1,s,r,stop,k; + if IsTrivial(G) then return []; fi; + cs := ChiefSeries(G); + phi_GbyG1 := NaturalHomomorphismByNormalSubgroupNC(G,cs[2]); + GbyG1 := Image(phi_GbyG1); + mingenset_k_reps := List(MinimalGeneratingSet(GbyG1), x -> PreImagesRepresentative(phi_GbyG1, x)); + # GbyG1 is a simple group, so it has a 2 size generating set which can be found easily. + # I'll rely on MinimalGeneratingSet to do this. + check := gx -> GbyGk = GroupWithGenerators(ImagesSet(phi_GbyGk,gx)); + for k in [3..Length(cs)] do # Lifting + # We wish to compute the CR of MGS of GbyGk, given the CR of MGS of GbyGkm1 . + phi_GbyGk := NaturalHomomorphismByNormalSubgroupNC(G,cs[k]); + GbyGk := Image(phi_GbyGk); + if check(mingenset_k_reps) then continue; fi; + phi_Gkm1byGk := NaturalHomomorphismByNormalSubgroupNC(cs[k-1],cs[k]); + Gkm1byGk := Image(phi_Gkm1byGk); + Gkm1byGk_gen := SmallGeneratingSet(Gkm1byGk); + Gkm1byGk_gen_reps := List(Gkm1byGk_gen,x -> PreImagesRepresentative(phi_Gkm1byGk,x)); + g := ShallowCopy(mingenset_k_reps); + stop := false; + if IsAbelian(Gkm1byGk) then + for i in [1..Length(g)] do + if stop then break; fi; + for j in [1..Length(Gkm1byGk_gen_reps)] do + temp := g[i]; + g[i] := temp * Gkm1byGk_gen_reps[j]; + if check(g) then + mingenset_k_reps := g; + stop := true; + break; + fi; + g[i] := temp; + od; + od; + if not stop then + Add(g,Gkm1byGk_gen_reps[1]); + Assert(1,check(g),"The algorithm is failing"); + mingenset_k_reps := g; + fi; + else + Gkm1byGk_elem_reps := List(Enumerator(Gkm1byGk),x -> PreImagesRepresentative(phi_Gkm1byGk,x)); + g0 := ShallowCopy(mingenset_k_reps); + g1 := ShallowCopy(mingenset_k_reps); + Add(g1,Gkm1byGk_elem_reps[1]); + for g in [g0,g1] do + if stop then break;fi; + l := Length(g); + L := Length(Gkm1byGk_elem_reps); + s := L^l; + prev := []; + for i in [l,l-1..1] do prev[i] := 1; od; + gmod := ShallowCopy(g); + for x in [0..s-1] do + xl := []; + for i in [1..l] do xl[i] := 0; od; + i := 1; + while x > 0 do + r := RemInt(x,L); + x := QuoInt(x,L); + xl[i]:=r; + i:= i+1; + od; + for i in [1..l] do + if xl[i] <> prev[i] then + gmod[i] := g[i] * Gkm1byGk_elem_reps[xl[i]+1]; + fi; + od; + if check(gmod) then + mingenset_k_reps := gmod; + stop := true; + break; + fi; + prev := xl; + od; + od; + fi; + od; + return mingenset_k_reps; +end); ############################################################################# ## diff --git a/tst/testinstall/opers/MinimalGeneratingSetUsingChiefSeries.tst b/tst/testinstall/opers/MinimalGeneratingSetUsingChiefSeries.tst new file mode 100644 index 0000000000..4c6063e48b --- /dev/null +++ b/tst/testinstall/opers/MinimalGeneratingSetUsingChiefSeries.tst @@ -0,0 +1,45 @@ +gap> START_TEST("MinimalGeneratingSetUsingChiefSeries.tst"); +gap> CrossVerifyMinimalGeneratingSetUsingChiefSeries := function(startsize,endsize) +> local G,size,gens1,gens2,G1,G2,i; +> for size in [startsize..endsize] do +> i := 0; +> for G in AllSmallGroups(size) do +> i := i + 1; +> gens1:= MinimalGeneratingSet(G); +> gens2:= MinimalGeneratingSetUsingChiefSeries(G); +> if Length(gens1) = 0 then +> if IsTrivial(G) then +> if Length(gens2) > 0 then +> return Concatenation("FAILED on AllSmallGroups(",String(size),")[",String(i),"]"); +> else +> continue; +> fi; +> else +> return Concatenation("MinimalGeneratingSet is failing on AllSmallGroups(",String(size),")[",String(i),"]"); +> fi; +> fi; +> G1 := GroupByGenerators(gens1); +> if not G = G1 then +> return Concatenation("MinimalGeneratingSet is failing on AllSmallGroups(",String(size),")[",String(i),"]"); +> fi; +> G2 := GroupByGenerators(gens2); +> if (not G = G2) or Length(gens1) < Length(gens2) then +> return Concatenation("FAILED on AllSmallGroups(",String(size),")[",String(i),"]"); +> fi; +> od; +> od; +> return "PASSED"; +> end; +function( startsize, endsize ) ... end +gap> CrossVerifyMinimalGeneratingSetUsingChiefSeries(1,60); +"PASSED" +gap> CrossVerifyMinimalGeneratingSetUsingChiefSeries(115,125); +"PASSED" +gap> G := AlternatingGroup(5); +Alt( [ 1 .. 5 ] ) +gap> G := DirectProduct(G,G);; +gap> mu := MinimalGeneratingSetUsingChiefSeries(G);; +gap> G = GroupByGenerators(mu); +true +gap> Length(mu); +2