diff --git a/doc/ref/grpfp.xml b/doc/ref/grpfp.xml index a339a38745..c7f54a3164 100644 --- a/doc/ref/grpfp.xml +++ b/doc/ref/grpfp.xml @@ -282,7 +282,7 @@ gap> f := FreeGroup( "a", "b" ); gap> g := f / [ f.1^2, f.2^3, (f.1*f.2)^5 ]; gap> h := IsomorphismPermGroup( g ); -[ a, b ] -> [ (1,2)(4,5), (2,3,4) ] +[ a, b ] -> [ (2,4)(5,6), (1,2,3)(4,5,6) ] gap> u:=Subgroup(g,[g.1*g.2]);;rt:=RightTransversal(g,u); RightTransversal(,Group([ a*b ])) diff --git a/lib/grp.gd b/lib/grp.gd index 401edcd7d6..7bb7737243 100644 --- a/lib/grp.gd +++ b/lib/grp.gd @@ -4358,14 +4358,10 @@ DeclareAttribute( "IsomorphismFpGroup", IsGroup ); ## SetInfoLevel( InfoFpGroup, 1 ); ## gap> iso := IsomorphismFpGroupByGenerators( g, [ (1,2), (1,2,3,4,5) ] ); -## #I the image group has 2 gens and 5 rels of total length 52 +## #I the image group has 2 gens and 5 rels of total length 39 ## [ (1,2), (1,2,3,4,5) ] -> [ F1, F2 ] ## gap> fp := Image( iso ); ## -## gap> RelatorsOfFpGroup( fp ); -## [ F1^2, (F1*F2^-1)^4, (F2^-2*F1*F2^-3)^2, -## F2^-1*(F2^-1*F1)^2*F2^2*(F1*F2^-1)^2*F2^-1*F1*F2*F1, -## (F1*F2^-2)^2*F2^-1*F1*F2^3*F1*F2^-3 ] ## ]]> ##

## The main task of the function @@ -4392,9 +4388,9 @@ DeclareAttribute( "IsomorphismFpGroup", IsGroup ); ## (1,12)(2,11)(3,6)(4,8)(5,9)(7,10) ]) ## gap> gens := GeneratorsOfGroup( M12 );; ## gap> iso := IsomorphismFpGroupByGenerators( M12, gens );; -## #I the image group has 3 gens and 21 rels of total length 559 +## #I the image group has 3 gens and 24 rels of total length 669 ## gap> iso := IsomorphismFpGroupByGenerators( M12, gens );; -## #I the image group has 3 gens and 21 rels of total length 548 +## #I the image group has 3 gens and 20 rels of total length 414 ## ]]> ##

## Also in the case of a permutation group G, the function @@ -4443,7 +4439,7 @@ DeclareAttribute( "IsomorphismFpGroup", IsGroup ); ## #I the image group has 3 gens and 11 rels of total length 92 ## gap> iso := IsomorphismFpGroupByGenerators( M12, gens : ## > method := "fast" );; -## #I the image group has 3 gens and 176 rels of total length 3821 +## #I the image group has 3 gens and 136 rels of total length 3170 ## ]]> ##

## Though the option method := "regular" is only checked in the case @@ -4465,7 +4461,7 @@ DeclareAttribute( "IsomorphismFpGroup", IsGroup ); ## [ [ 0, 1, 0, 0, 0 ], [ 0, 0, 1, 0, 0 ], [ 0, 0, 0, 1, 0 ], ## [ 1, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 1 ] ] ] ## gap> iso := IsomorphismFpGroupByGenerators( G, gens );; -## #I the image group has 2 gens and 8 rels of total length 88 +## #I the image group has 2 gens and 11 rels of total length 120 ## gap> iso := IsomorphismFpGroupByGenerators( G, gens : ## > method := "regular");; ## #I the image group has 2 gens and 6 rels of total length 56 diff --git a/lib/grp.gi b/lib/grp.gi index 76c8861eb5..b6c336917d 100644 --- a/lib/grp.gi +++ b/lib/grp.gi @@ -229,7 +229,9 @@ local r,i,j,u,f,q,n,lim,sel,nat,ok,mi; 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 + if Length(q)>0 then return List(GeneratorsOfGroup(f), + x->ImagesRepresentative(q[1],x)) ;fi; # found + fi; r:=r+1; until false; diff --git a/lib/grpfp.gi b/lib/grpfp.gi index b84ded14ab..2a47de07de 100644 --- a/lib/grpfp.gi +++ b/lib/grpfp.gi @@ -1,3 +1,4 @@ +# gaplint: disable = analyse-lvars ############################################################################# ## ## This file is part of GAP, a system for computational discrete algebra. @@ -3892,8 +3893,7 @@ end); ## #M Size( ) . . . . . . . . . . . . . size of a finitely presented group ## -InstallMethod(Size, "for finitely presented groups", true, - [ IsSubgroupFpGroup and IsGroupOfFamily ], 0, +BindGlobal("SIZE_FP_FROM_CYCLIC_INDEX", function( G ) local fgens, # generators of the free group rels, # relators of @@ -3942,6 +3942,8 @@ local fgens, # generators of the free group end ); +InstallMethod(Size, "for finitely presented groups", true, + [ IsSubgroupFpGroup and IsGroupOfFamily ], 0, SIZE_FP_FROM_CYCLIC_INDEX); ############################################################################# ## @@ -3978,7 +3980,7 @@ end); ## InstallGlobalFunction(IsomorphismPermGroupOrFailFpGroup, function(arg) -local mappow, G, max, p, gens, rels, comb, i, l, m, H, t, gen, sz, +local mappow, G, max, p, gens, rels, comb, i, l, m, H, HH, t, gen, sz, t1, bad, trial, b, bs, r, nl, o, u, rp, eo, rpo, e, e2, sc, j, z, timerFunc,amax,iso,useind; @@ -4042,57 +4044,70 @@ local mappow, G, max, p, gens, rels, comb, i, l, m, H, t, gen, sz, H:=[]; # indicate pseudo-size 0 if not HasSize(G) then - Info(InfoFpGroup,1,"First compute size via cyclic subgroup"); - t:=FinIndexCyclicSubgroupGenerator(G,max); - if t<>fail then - gen:=t[1]; - Unbind(t); - t := NEWTC_CosetEnumerator( FreeGeneratorsOfFpGroup(G), - RelatorsOfFpGroup(G),[gen],true,false: - cyclic:=true,limit:=1+max,quiet:=true ); - fi; - - if t=fail then - # we cannot get the size within the permitted limits -- give up - return fail; - fi; - e:=NEWTC_CyclicSubgroupOrder(t); - if e=0 then - SetSize(G,infinity); - return fail; - fi; - sz:=e*t.index; + sz:=SIZE_FP_FROM_CYCLIC_INDEX(G); SetSize(G,sz); - Info(InfoFpGroup,1,"found size ",sz); - if sz>200*t.index then - # try the corresponding perm rep - p:=t.ct{t.offset+[1..Length(FreeGeneratorsOfFpGroup(G))]}; - Unbind(t); - - for j in [1..Length(p)] do - p[j]:=PermList(p[j]); - od; - H:= GroupByGenerators( p ); - # compute stabilizer chain with size info. - StabChain(H,rec(limit:=sz)); - if Size(H)i<>[gen]); - fi; - else - # for memory reasons it might be better to try other perm rep first - Unbind(t); - fi; - - elif Size(G)=infinity then + fi; + if Size(G)=infinity then return fail; fi; sz:=Size(G); + if sz*10>max then max:=sz*10; fi; + # do we have a cyclic subgroup by which we can enumerate? + if HasCyclicSubgroupFpGroup(G) then + trial:=GeneratorsOfGroup(CyclicSubgroupFpGroup(G)); + trial:=List(trial,UnderlyingElement); + Info(InfoFpGroup,1,"Try subgroup ",trial," with ",max); + t:=CosetTableFromGensAndRels(gens,rels,trial:silent:=true,max:=max ); + if t<>fail and IndexCosetTab(t)>1 then + p:=t{[1,3..Length(t)-1]}; + Unbind(t); + for j in [1..Length(p)] do + p[j]:=PermList(p[j]); + od; + H:= GroupByGenerators( p ); + + StabChain(H,rec(limit:=sz)); + + Info(InfoFpGroup,1,"found index ",NrMovedPoints(H)," size ",Size(H)); + + if Size(H)ElementOfFpGroup(FamilyObj(One(G)),x)) + ):silent:=true,max:=2*max); + if iso<>fail then + + HH:=Range(iso); + SetSize(HH,sz/NrMovedPoints(H)); + +# p:=GroupHomomorphismByImagesNC(G,H,GeneratorsOfGroup(G), +# GeneratorsOfGroup(H)); +# ker:=SubgroupNC(HH,List(GeneratorsOfGroup(Kernel(p)),x-> +# ImagesRepresentative(iso,x))); +# +# # try to find a subgroup not containing `ker` +# we know that the subgroup is cyclic, thus abelian. so no test needed +# if not IsSubset(DerivedSubgroup(HH),ker) then + t:=MaximalAbelianQuotient(HH); + t:=t*MinimalFaithfulPermutationRepresentation(Range(t)); + t:=InducedRepFpGroup(iso*t,Source(iso)); + H:=Group(MappingGeneratorsImages(t)[2]); + StabChain(H,rec(limit:=sz)); + fi; +# fi; + + fi; + + + fi; + + fi; + # Do not die on large coset table amax:=max; if max>10^4*sz then @@ -4108,17 +4123,19 @@ local mappow, G, max, p, gens, rels, comb, i, l, m, H, t, gen, sz, i:=1; while Size(H)IsSubset(i,trial)) then + if not ForAny(bad,i->IsSubset(trial,i)) then Info(InfoFpGroup,1,"Try subgroup ",trial," with ",max); t:=CosetTableFromGensAndRels(gens,rels,trial:silent:=true,max:=max ); - if t<>fail then + if t<>fail and IndexCosetTab(t)>1 then Info(InfoFpGroup,1,"has index ",IndexCosetTab(t)); + p:=t{[1,3..Length(t)-1]}; Unbind(t); for j in [1..Length(p)] do p[j]:=PermList(p[j]); od; H:= GroupByGenerators( p ); + # compute stabilizer chain with size info. if Length(trial)=0 then # regular is faithful @@ -4127,14 +4144,16 @@ local mappow, G, max, p, gens, rels, comb, i, l, m, H, t, gen, sz, StabChain(H,rec(limit:=sz)); fi; - # try to use induced rep - if Size(H)1 then + if Size(H)ElementOfFpGroup(FamilyObj(One(G)),x)) ):silent:=true,max:=2*max); - H:=Range(iso); - t:=IsomorphismPermGroupOrFailFpGroup(H,max); + HH:=Range(iso); + SetSize(HH,sz/NrMovedPoints(H)); + + t:=IsomorphismPermGroupOrFailFpGroup(HH,max); if t<>fail then t:=iso*t; iso:=InducedRepFpGroup(t,Source(iso)); diff --git a/lib/sgpres.gi b/lib/sgpres.gi index ecf4fd224d..26f60e83df 100644 --- a/lib/sgpres.gi +++ b/lib/sgpres.gi @@ -3514,7 +3514,7 @@ end ); # -1: No relators InstallGlobalFunction(NEWTC_PresentationMTC,function(arg) local DATA,rels,i,j,w,f,r,s,fam,ri,a,offset,rset,re,stack,pres, - subnum,bad,warn,parameter,str; + subnum,parameter,str,wordefs; DATA:=arg[1]; if Length(arg)=1 then @@ -3605,9 +3605,11 @@ local DATA,rels,i,j,w,f,r,s,fam,ri,a,offset,rset,re,stack,pres, fi; # add definitions of secondary generators + wordefs:=[]; for i in [subnum+1..DATA.secount] do r:=WordProductLetterRep(DATA.secondary[i],[-i]); Add(rels,r); + wordefs[i]:=r; od; if ForAll(str,IsString) and DATA.secount >=Length(str) then @@ -3630,49 +3632,49 @@ local DATA,rels,i,j,w,f,r,s,fam,ri,a,offset,rset,re,stack,pres, TzSearch(pres); TzOptions(pres).lengthLimit:=pres!.tietze[TZ_TOTAL]+1; fi; - #TzGoGo(pres); TzOptions(pres).eliminationsLimit:=5; - TzGoElim(pres,subnum); + TzGoElim(pres,subnum,wordefs); if IsEvenInt(parameter) and Length(GeneratorsOfPresentation(pres))>subnum then - warn:=true; - # Help Tietze with elimination - bad:=Reversed(List(GeneratorsOfPresentation(pres) - {[subnum+1..Length(GeneratorsOfPresentation(pres))]}, - x->LetterRepAssocWord(x)[1])); - for i in bad do - r:=DATA.secondary[i]; - re:=true; - while re do - s:=[]; - re:=false; - for j in r do - if AbsInt(j)>subnum then - re:=true; - if j>0 then - Append(s,DATA.secondary[j]); - else - Append(s,-Reversed(DATA.secondary[-j])); - fi; - else - Add(s,j); - fi; - od; - Info(InfoFpGroup,2,"Length =",Length(s)); - r:=s; - if warn and Length(s)>100*Sum(rels,Length) then - warn:=false; - Error( - "Trying to eliminate all auxiliary generators might cause the\n", - "size of the presentation to explode. Proceed at risk!"); - fi; - od; - r:=AssocWordByLetterRep(fam,Concatenation(r,[-i])); - AddRelator(pres,r); - #TzSearch(pres); Do *not* search, as this might kill the relator we - #just added. - TzEliminate(pres,i); - od; - Assert(0,Length(GeneratorsOfPresentation(pres))=subnum); + Error("did not eliminate properly"); +# warn:=true; +# # Help Tietze with elimination +# bad:=Reversed(List(GeneratorsOfPresentation(pres) +# {[subnum+1..Length(GeneratorsOfPresentation(pres))]}, +# x->LetterRepAssocWord(x)[1])); +# for i in bad do +# r:=DATA.secondary[i]; +# re:=true; +# while re do +# s:=[]; +# re:=false; +# for j in r do +# if AbsInt(j)>subnum then +# re:=true; +# if j>0 then +# Append(s,DATA.secondary[j]); +# else +# Append(s,-Reversed(DATA.secondary[-j])); +# fi; +# else +# Add(s,j); +# fi; +# od; +# Info(InfoFpGroup,2,"Length =",Length(s)); +# r:=s; +# if warn and Length(s)>100*Sum(rels,Length) then +# warn:=false; +# Error( +# "Trying to eliminate all auxiliary generators might cause the\n", +# "size of the presentation to explode. Proceed at risk!"); +# fi; +# od; +# r:=AssocWordByLetterRep(fam,Concatenation(r,[-i])); +# AddRelator(pres,r); +# #TzSearch(pres); Do *not* search, as this might kill the relator we +# #just added. +# TzEliminate(pres,i); +# od; +# Assert(0,Length(GeneratorsOfPresentation(pres))=subnum); fi; r:=List(GeneratorsOfPresentation(pres){[1..subnum]}, diff --git a/lib/tietze.gi b/lib/tietze.gi index a2fe81a79e..bab75a9851 100644 --- a/lib/tietze.gi +++ b/lib/tietze.gi @@ -2324,25 +2324,79 @@ InstallGlobalFunction( TzGo, function ( arg ) end ); # reduce presentation in generators (for MTC) -InstallGlobalFunction(TzGoElim,function(T,downto) -local tietze,len,olen; +InstallGlobalFunction(TzGoElim,function(T,downto,wordefs) +local tietze,oll,num,gens,gensn,rev,w,i,r,a; + gens:=ShallowCopy(T!.generators); + gensn:=List(gens,x->LetterRepAssocWord(x)[1]); + num:=Length(gens); + rev:=ListWithIdenticalEntries(num,fail); + for i in [1..Length(gens)] do + rev[gensn[i]]:=gens[i]; + od; TzTestInitialSetup(T); # run `1Or2Relators' if not yet done tietze := T!.tietze; - len:=tietze[TZ_TOTAL]; - olen:=len+1; - while tietze[TZ_NUMGENS]-tietze[TZ_NUMREDUNDS]>downto and olen<>len do - TzSearch(T); - TzEliminateGens(T); + while num>downto do + if gens[num] in T!.generators then + TzSearch(T); + # the search might have killed the generator + if gens[num] in T!.generators then + # does replacement make longer by 25% or more? If so try harder + if TzOccurrences(T!.tietze,Position(T!.generators,gens[num]))[1][1] + *(Length(wordefs[gensn[num]])-1)*3> T!.tietze[TZ_TOTAL] then + + TzOptions(T).protected:=num; + oll:=TzOptions(T).loopLimit; + TzOptions(T).loopLimit:=5; + TzGo(T); # cleanup + TzOptions(T).loopLimit:=oll; + TzOptions(T).protected:=downto; + TzPrintStatus(T,true); + + fi; + + TzEliminate(T,gens[num]); + fi; + if gens[num] in T!.generators then + Info(InfoFpGroup,1,"increase length limit"); + TzOptions(T).lengthLimit:=TzOptions(T)!.lengthLimit*100; + + # and re-add the defining relator + w:=ShallowCopy(wordefs[gensn[num]]); + i:=1; + while i<=Length(w) do + a:=AbsInt(w[i]); + if not rev[a] in T!.generators then + r:=wordefs[a]; + # remove letter itself, regardless how written + r:=Filtered(r,x->AbsInt(x)0 then TzPrintStatus(T,true); fi; + if TzOptions(T).printLevel>0 and (num mod 100=0 or num<20) then + + TzOptions(T).protected:=num; + oll:=TzOptions(T).loopLimit; + TzOptions(T).loopLimit:=5; + TzGo(T); # cleanup + TzOptions(T).loopLimit:=oll; + TzOptions(T).protected:=downto; + TzPrintStatus(T,true); + + fi; od; - olen:=TzOptions(T).loopLimit; - TzOptions(T).loopLimit:=5; - TzGo(T); # cleanup - TzOptions(T).loopLimit:=olen; end);