|
#############################################################################
##
## This file is part of GAP, a system for computational discrete algebra.
## This file's authors include Alexander Hulpke, Soley Jonsdottir.
##
## Copyright of GAP belongs to its developers, whose names are too numerous
## to list here. Please refer to the COPYRIGHT file for details.
##
## SPDX-License-Identifier: GPL-2.0-or-later
##
## This file contains an implementation of the Cannon/Holt automorphism
## group algorithm:
## Automorphism group computation and isomorphism testing in finite groups.
## J. Symb. Comput. 35, No. 3, 241-267 (2003)
# call as big,perm,aut (the latter two can be groups or generator lists,
# optional permiso.
# returns two groups with corresponding generators
BindGlobal( "AGSRReducedGens", function(arg)
local big,bp,A,permgens,s,auts,sel,i,sub;
big:=arg[1];
bp:=arg[2];
A:=arg[3];
if IsGroup(bp) then
permgens:=GeneratorsOfGroup(bp);
else
permgens:=bp;
bp:=SubgroupNC(Parent(big),permgens);
fi;
SetSize(bp,Size(big));
if IsGroup(A) then
auts:=GeneratorsOfGroup(A);
else
auts:=A;
A:=fail;
fi;
if Length(permgens)<>Length(auts) then Error("correspondence!");fi;
s:=SmallGeneratingSet(big);
if Length(s)+2>=Length(permgens) then
return fail;
fi;
# first try to reduce by dropping generators
sel:=[1..Length(permgens)];
for i in [1..Length(permgens)] do
sub:=SubgroupNC(Parent(big),permgens{Difference(sel,[i])});
if Size(sub)=Size(big) then
RemoveSet(sel,i);
bp:=sub;
fi;
od;
# good enough?
if Length(s)+2<Length(sel) then
if Length(arg)>3 then
i:=InverseGeneralMapping(arg[4]);
else
i:=GroupHomomorphismByImagesNC(big,Group(auts),permgens,auts);
fi;
auts:=List(s,x->ImagesRepresentative(i,x));
bp:=SubgroupNC(Parent(big),s);
SetSize(bp,Size(big));
auts:=Group(auts);
else
auts:=Group(auts{sel});
fi;
SetIsGroupOfAutomorphismsFiniteGroup(auts,true);
SetIsFinite(auts,true);
SetSize(auts,Size(bp));
if A<>fail then
if HasInnerAutomorphismsAutomorphismGroup(A) then
SetInnerAutomorphismsAutomorphismGroup(auts,
InnerAutomorphismsAutomorphismGroup(A));
fi;
if HasNiceMonomorphism(A) then
SetNiceMonomorphism(auts,NiceMonomorphism(A));
SetNiceObject(auts,NiceObject(A));
fi;
fi;
return [bp,auts];
end );
# If M<=Frat(C_G(M)), try to find relators for C/M that in G evaluate to
# generators of M and for which exponent sums are multiples of p. In this
# case the values of the relators on pre-images in G do not depend on choice
# of representatives and can be used to deduce the module automorphism
# belonging to a factor group automorphism.
BindGlobal("AGSRFindRels",function(nat,newgens)
local C,M,p,all,gens,sub,q,hom,fp,rels,new,pre,i,free,cnt;
M:=KernelOfMultiplicativeGeneralMapping(nat);
C:=Centralizer(Source(nat),M);
if not IsSubset(FrattiniSubgroup(C),M) then
return fail;
fi;
p:=SmallestPrimeDivisor(Size(M));
all:=[];
if newgens=true then
# so generators new
sub:=TrivialSubgroup(Image(nat));
while Size(sub)<Size(Image(nat)) do
sub:=ClosureGroup(sub,ImagesRepresentative(nat,Random(C)));
od;
else
sub:=Image(nat,C);
fi;
gens:=SmallGeneratingSet(sub);
free:=FreeGroup(Length(gens));
sub:=TrivialSubgroup(M);
cnt:=0;
while Size(sub)<Size(M) do
q:=Group(gens);
SetSize(q,Size(Image(nat,C)));
# use `ByGenerators` to force more random relators
hom:=IsomorphismFpGroupByGenerators(q,gens);
fp:=Range(hom);
rels:=Filtered(RelatorsOfFpGroup(fp),x->ForAll(ExponentSums(x),x->x mod p=0));
rels:=List(rels,x->ElementOfFpGroup(FamilyObj(One(fp)),x));
new:=RestrictedMapping(nat,C)*hom;
pre:=List(rels,x->PreImagesRepresentative(new,x));
for i in [1..Length(rels)] do
if not pre[i] in sub then
Add(all,MappedWord(rels[i],
GeneratorsOfGroup(fp),GeneratorsOfGroup(free)));
sub:=ClosureGroup(sub,pre[i]);
fi;
od;
cnt:=cnt+1;
if cnt>5 then return fail;fi;
od;
return rec(gens:=gens,free:=free,rels:=all);
end);
BindGlobal("AGSRPrepareAutomLift",function(G,pcgs,nat)
local ocr,fphom,fpg,free,len,dim,tmp,L0,R,rels,mat,r,RS,i,g,v,cnt;
ocr:=rec(group:=G,modulePcgs:=pcgs);
fphom:=IsomorphismFpGroup(G);
ocr.identity := One(ocr.modulePcgs[1]);
fpg:=FreeGeneratorsOfFpGroup(Range(fphom));
ocr.factorpres:=[fpg,RelatorsOfFpGroup(Range(fphom))];
ocr.generators:=List(GeneratorsOfGroup(Range(fphom)),
i->PreImagesRepresentative(fphom,i));
OCAddMatrices(ocr,ocr.generators);
OCAddRelations(ocr,ocr.generators);
OCAddSumMatrices(ocr,ocr.generators);
OCAddToFunctions(ocr);
ocr.module:=GModuleByMats(
LinearActionLayer(G,ocr.generators,ocr.modulePcgs),ocr.field);
ocr.moduleauts:=MTX.ModuleAutomorphisms(ocr.module);
if Size(ocr.moduleauts)>
# Finding the relations comes at a cost that needs to be plausible
# with searching multiple times through the automorphism group. This
# order bound is a heuristic that seems to be OK by magnitude.
100
then
cnt:=0;
repeat
ocr.trickrels:=AGSRFindRels(nat,cnt>3);
cnt:=cnt+1;
until ocr.trickrels<>fail or 2^cnt>100*Size(ocr.moduleauts);
if ocr.trickrels=fail then Info(InfoMorph,1,"trickrels fails");fi;
else
ocr.trickrels:=fail;
fi;
ocr.factorgens:=List(ocr.generators,i->Image(nat,i));
free:=FreeGroup(Length(ocr.generators),"f");
ocr.free:=free;
ocr.decomp:=GroupGeneralMappingByImages(Image(nat,G),free,
ocr.factorgens,GeneratorsOfGroup(free));
# Initialize system.
len:=Length(ocr.generators);
dim:=Length(pcgs);
tmp := ocr.moduleMap( ocr.identity );
L0 := Concatenation( List( [ 1 .. len ], x -> tmp ) );
ConvertToVectorRep(L0,ocr.field);
R := ListWithIdenticalEntries( len * dim,Zero( ocr.field ) );
ConvertToVectorRep(R,ocr.field);
rels:=ocr.relators;
mat:=List([1..len*dim],x->[]);
for i in mat do
ConvertToVectorRep(i,ocr.field);
od;
for i in [1..Length(rels)] do
Info(InfoCoh,2," relation ", i, " (",Length(rels),")");
r:=1;
for g in [1..len] do
RS:=OCEquationMatrix(ocr,rels[i],g);
for v in RS do
Append(mat[r],v);
r:=r+1;
od;
od;
od;
ocr.matrix:=ImmutableMatrix(ocr.field,mat);
ocr.semiech:=ShallowCopy(SemiEchelonMatTransformation(ocr.matrix));
ocr.semiech.numrows:=NrRows(ocr.matrix);
return ocr;
end);
# solve using the stored LR decomposition
BindGlobal("AGSRSolMat",function(sem,vec)
local i,vno,x,z,sol;
z := ZeroOfBaseDomain(sem.vectors);
sol := ListWithIdenticalEntries(sem.numrows,z);
ConvertToVectorRepNC(sol);
for i in [1..Length(vec)] do
vno := sem.heads[i];
if vno <> 0 then
x := vec[i];
if x <> z then
AddRowVector(vec, sem.vectors[vno], -x);
AddRowVector(sol, sem.coeffs[vno], x);
fi;
fi;
od;
if IsZero(vec) then
return sol;
else
return fail;
fi;
end);
#############################################################################
##
#F OCEquationVectorAutom(<ocr>,<r>,<genimages>)
##
BindGlobal("OCEquationVectorAutom",function(ocr,r,genimages)
local n,i;
# If <r> has an entry 'conjugated' the records is no relator for a
# presentation,but belongs to relation
# (g_i n_i) ^ s_j =<r>
# which is used to determinate normal complements. [i,j] is bound to
# <conjugated>.
if IsBound(r.conjugated) then
Error("not yet implemented");
fi;
n:=ocr.identity;
for i in [1 .. Length(r.generators)] do
n:=n*genimages[r.generators[i]]^r.powers[i];
od;
Assert(1,n in GroupByGenerators(NumeratorOfModuloPcgs(ocr.modulePcgs)));
return ShallowCopy(ocr.moduleMap(n));
end);
BindGlobal("AGSRAutomLift",function(ocr,nat,fhom,miso)
local v, rels, genimages, v1, psim, w, s, t, l, hom, i, e, j,ep,phom,enum;
v:=[];
rels:=ocr.relators;
genimages:=List(ocr.factorgens,i->MappedWord(
ImagesRepresentative(ocr.decomp,Image(fhom,i)),
GeneratorsOfGroup(ocr.free),
ocr.generators));
for i in [1..Length(rels)] do
v1:=OCEquationVectorAutom(ocr,rels[i],genimages);
Add(v,v1);
od;
#for ep in Enumerator(ocr.moduleauts) do
if ocr.trickrels<>fail then
# special case for M<=Frat(C_G(M)). Use special relators for factor that
# allow to deduce corresponding module aut.
t:=ocr.trickrels;
phom:=IdentityMapping(ocr.moduleauts);
s:=List(t.gens,x->PreImagesRepresentative(nat,x));
l:=List(t.gens,x->PreImagesRepresentative(nat,ImagesRepresentative(fhom,x)));
s:=List(t.rels,x->MappedWord(x,GeneratorsOfGroup(t.free),s));
l:=List(t.rels,x->MappedWord(x,GeneratorsOfGroup(t.free),l));
s:=List(s,x->ExponentsOfPcElement(ocr.modulePcgs,x))*One(ocr.field);
l:=List(l,x->ExponentsOfPcElement(ocr.modulePcgs,x))*One(ocr.field);
Info(InfoMorph,5,"Deduced corresponding module automorphism");
if RankMat(l)<Length(l) then
return fail;
fi;
enum:=[s^-1*l];
else
phom:=IsomorphismPermGroup(ocr.moduleauts);
enum:=Enumerator(Image(phom,ocr.moduleauts));
Info(InfoMorph,5,"Search through module automorphisms of size ",
Size(Image(phom)));
fi;
for ep in enum do
e:=PreImagesRepresentative(phom,ep);
psim:=e*miso;
psim:=psim^-1;
w:=-List(v,i->i*psim);
#s:=SolutionMat(ocr.matrix,Concatenation(w));
s:=AGSRSolMat(ocr.semiech,Concatenation(w));
if s<>fail then
psim:=psim^-1;
t:=[];
ConvertToVectorRep(t,ocr.field);
l:=Length(ocr.modulePcgs);
for i in [1..Length(genimages)] do
v1:=s{[(i-1)*l+1..(i*l)]}*psim;
for j in [1..Length(v1)] do
t[(i-1)*l+j]:=v1[j];
od;
od;
s:=ocr.cocycleToList(t);
for i in [1..Length(genimages)] do
genimages[i]:=genimages[i]*s[i];
od;
# later use NC version
hom:=GroupHomomorphismByImagesNC(ocr.group,ocr.group,
ocr.generators,genimages);
Assert(2,IsBijective(hom));
return hom;
fi;
od;
return fail;
end);
# Find a larger subgroup that satisfies a condition, when testing
# the condition can become expensive.
# First try `SubgroupProperty`, but when it stalls attempt to find
# minimal supergroups and prove that none of them satisfies.
BindGlobal("SubgroupConditionAboveAux",function(G,cond,S1,avoid)
local S,c,hom,q,a,b,i,t,have,ups,new,u,good,abort,clim,worked,pp,
cnt,locond,tstcnt,setupc,havetest;
setupc:=function()
hom:=NaturalHomomorphismByNormalSubgroupNC(G,u);
q:=Image(hom,G);
ups:=Image(hom,avoid);
# aim for zuppos that cannot intersect avoid
c:=ConjugacyClasses(q);
c:=Filtered(c,x->IsPrimePowerInt(Order(Representative(x))) and
not Representative(x) in ups);
# elements that do not have prime-order power in the subgroup avoid
c:=Filtered(c,x->not Representative(x)^
(Order(Representative(x))/SmallestPrimeDivisor(Order(Representative(x))))
in ups);
# this also implies prime powers after the respective primes
SortBy(c,Size);
Info(InfoMorph,3,Length(c)," classes with ",Sum(c,Size)," subgroups");
end;
S:=S1;
Info(InfoMorph,2,"SubgroupAbove ",IndexNC(G,S));
# Strategy: First try `SubgroupProperty` with a bailout limit, just in
# case it finds (enough) elements. The bailout limit is set smaller if the
# factor is solvable, as it will be cheaper to find complementing zuppos
# in this case.
u:=Intersection(S,avoid);
if IsNormal(G,u) and IsNormal(G,avoid)
and HasSolvableFactorGroup(G,u) then
setupc();
clim:=Minimum(Maximum(QuoInt(Sum(c,Size),4),10),1000);
else
hom:=fail;
# if less than 1/100 percent of elements succeed, assume close
# to the subgroup has been found, and rather aim to prove there
# will not be more.
c:=fail;
clim:=Maximum(QuoInt(IndexNC(G,S),10000),1000);
fi;
# first try, how far `SubgroupProperty` goes
b:=0;
tstcnt:=0;
abort:=false;
havetest:=[];
if IsPermGroup(G) then
worked:=SubgroupProperty(G,
function(elm)
if abort then
return true; # are we bailing out since it behaves too badly?
fi;
# would it contribute to avoid outside S?
if elm in avoid or
ForAny(Set(Factors(Order(elm))),e->elm^e in avoid and not elm^e in S)
then
# cannot be good
return false;
fi;
tstcnt:=tstcnt+1;
AddSet(havetest,elm);
if cond(elm) then
S:=ClosureGroup(S,elm); # remember
Info(InfoMorph,3,"New element ",IndexNC(G,S));
b:=0;
return true;
else
b:=b+1;
if b>clim then
abort:=true;
fi;
return false;
fi;
end,S);
else
worked:=S;
abort:=true;
fi;
if abort=false then
# we actually found the subgroup
Info(InfoMorph,2,"SubgroupProperty finds index ",IndexNC(G,worked));
return worked;
fi;
Info(InfoMorph,2,"SubgroupProperty did ",tstcnt," tests and improved by ",
IndexNC(S,S1));
if not (IsNormal(G,u) and IsNormal(G,avoid)) then
Error("may only call if normalizing");
fi;
if c=fail then setupc();fi;
havetest:=Set(List(havetest,x->ImagesRepresentative(hom,x)));
locond:=function(elm)
tstcnt:=tstcnt+1;
return cond(elm);
end;
clim:=
# avoid writing down a permutation representation on more than
150000;
#cosets, as it gets too memory expensive.
worked:=[]; # indicate whether class worked
# now run over all Zuppos (Conjugates of class representatives) in factor that do
# not intersect (the image of) avoid. These, that saisfy, will span the correct subgroup.
i:=1;
while worked<>fail and i<=Length(c) do
#should we abort?
if Sum(c{[i..Length(c)]},Size)>IndexNC(G,S) and IndexNC(G,S)<=clim then
a:=Normalizer(G,S);
# if the normalizer index is large, there will be many subgroups above
if IndexNC(a,S)<200 then
worked:=fail; # abort and go to other method
fi;
fi;
# if prime powers, the primes must have worked
a:=Representative(c[i]);
# no coprime power in earlier class?
have:=worked<>fail and
ForAll(Filtered([2..Order(a)-1],o->Gcd(o,Order(a))=1),
o->PositionProperty(c{[1..i-1]},x->a^o in x)=fail);
pp:=false;
if have and not IsPrimeInt(Order(a)) then
pp:=SmallestPrimeDivisor(Order(a));
a:=a^(Order(a)/pp);
have:=worked[PositionProperty(c,x->a in x)];
fi;
if have then
have:=false;
a:=Representative(c[i]);
t:=Orbit(q,a);
t:=Filtered(t,x->not x in havetest);
cnt:=0;
for b in t do
new:=PreImagesRepresentative(hom,b);
if (pp=false or new^pp in S) and locond(new) then
S:=ClosureGroup(S,new);
have:=true;
cnt:=cnt+1;
fi;
od;
Info(InfoMorph,4,"Did class ",i," order =",Order(Representative(c[i])),
", len=",Size(c[i])," newtest=", Length(t),
" found ",cnt,": ",Size(S));
fi;
if worked<>fail then Add(worked,have);fi;
i:=i+1;
od;
if worked=fail then
# still need to test
Info(InfoMorph,2,"Go to blocks test");
if Index(G,S)>clim then Error("clim"); fi;
a:=SmallGeneratingSet(G);
if Length(GeneratorsOfGroup(G))>Length(a) then
b:=Size(G);
G:=Group(a);
SetSize(G,b);
fi;
repeat
good:=false;
u:=Filtered(GeneratorsOfGroup(G),x->cond(x) and not x in S);
for i in u do
S:=ClosureGroup(S,i);
od;
# try to prove no supergroup works
t:=RightTransversal(G,S:noascendingchain); # don't try to be clever in
# decomposing transversal, as this could be hard
a:=Action(G,t,OnRight); # coset action, don't need homomorphism
b:=RepresentativesMinimalBlocks(a,MovedPoints(a));
Info(InfoMorph,3,"Above are ",Length(b)," blocks");
if IsPermGroup(G) and Length(b)*10>IndexNC(G,S) then
# there are too many blocks. Direct test is cheaper!
S:=SubgroupProperty(G,locond,S);
else
for i in [1..Length(b)] do
CompletionBar(InfoMorph,3,"SubgroupsAboveBlocks ",i/Length(b));
c:=First(b[i],x->x>1);
if cond(t[c]) then
S:=ClosureGroup(S,t[c]);
good:=true;
fi;
od;
CompletionBar(InfoMorph,3,"SubgroupsAboveBlocks ",false);
fi;
until good=false;
fi;
Info(InfoMorph,3,"Did ",tstcnt," tests, grow by ",Index(S,S1));
return S;
end);
# Same syntax as `SubgroupProperty`, but assumption that the tests are
# expensive. Thus minimize number of tests by doing more group calculations
InstallGlobalFunction(SubgroupConditionAbove,function(G,cond,Sorig)
local cs,nr,u,no,un,S,rad,res,ise,uno;
# first, try to find a few elements that work (and catch the case that the
# subgroup has small index. Ensure we test at least once for each possible
# step.
nr:=2^LogInt(Size(G),1000)+Length(Factors(Size(G)));
u:=[];
if IsPermGroup(G) then
S:=SubgroupProperty(G,
function(elm)
if nr<0 then return true;fi;
nr:=nr-1;
if cond(elm) then
Add(u,elm);
return true;
else
return false;
fi;
end,Sorig);
else
u:=Filtered(GeneratorsOfGroup(G),cond);
nr:=-1;
fi;
if nr>=0 then # succeeded (small index case)
return S;
elif Length(u)>0 then
S:=ClosureGroup(Sorig,u);
else
S:=Sorig;
fi;
# now build along composition series
rad:=SolvableRadical(G);
# composition series through perfect residuum seems to work better
# Possible reason: Simple composition factors arise as inner
# automorphisms. Moving the perfect residuum (if smaller) in the series,
# thus increases the chance that much of the subgroup is found early (and
# does not need to be found through search through complements)
res:=PerfectResiduum(G);
nr:=[Core(G,S),NormalClosure(G,S)];
if Size(nr[1])=Size(nr[2]) then nr:=nr{[1]};fi;
# refine with perfect residuum
no:=Intersection(nr[1],res);
if not no in nr then nr:=Concatenation([no],nr);fi;
if Length(nr)>1 then
no:=ClosureGroup(Last(nr),res);
if not no in nr then Add(nr,no);fi;
fi;
cs:=CompositionSeriesThrough(G,nr);
nr:=First([1..Length(cs)],x->IsSubset(S,cs[x]));
uno:=false;
while nr>1 do
nr:=nr-1;
if IsSubset(cs[nr+1],S) then
u:=S;
ise:=u;
if uno=false then
# if the group is huge use the radical-based normalizer
if Size(rad)>10^13 and IndexNC(G,ise)>10^5 then
Info(InfoMorph,4,"Radical-based Normalizer:",IndexNC(G,u));
uno:=NormalizerViaRadical(G,ise);
else
Info(InfoMorph,4,"Ordinary Normalizer:",IndexNC(G,u));
uno:=Normalizer(G,ise);
fi;
fi;
no:=Intersection(uno,cs[nr]);
else
u:=Intersection(cs[nr],S);
ise:=Intersection(cs[nr+1],u);
# if the group is huge use the radical-based normalizer
if Size(Intersection(rad,cs[nr]))>10^13 and IndexNC(cs[nr],ise)>10^5 then
Info(InfoMorph,4,"Radical-based Normalizer:",IndexNC(cs[nr],ise),
" of ",Index(cs[nr],cs[nr+1]));
no:=NormalizerViaRadical(cs[nr],ise);
else
Info(InfoMorph,4,"Ordinary Normalizer:",IndexNC(cs[nr],ise),
" of ",Index(cs[nr],cs[nr+1]));
no:=Normalizer(cs[nr],ise);
fi;
fi;
un:=Size(no);
no:=Group(SmallGeneratingSet(no));
SetSize(no,un);
un:=SubgroupConditionAboveAux(no,cond,u,Intersection(no,cs[nr+1]));
Info(InfoMorph,2,
"Step ",nr,": ",IndexNC(cs[nr],cs[nr+1])," to ",IndexNC(un,u));
if not IsSubset(S,un) then
S:=ClosureGroup(S,un);
uno:=false;
fi;
od;
return S;
end);
# find classes of normal subgroups
BindGlobal("AGSRNormalSubgroupClasses",function(G)
local fp,n,pat,pools,i,sel;
# fingerprint
fp:=function(x)
local l;
if ID_AVAILABLE(Size(x)) <> fail
and ValueOption(NO_PRECOMPUTED_DATA_OPTION)<>true then
return IdGroup(x);
fi;
l:=[Size(x)];
Add(l,Collected(List(ConjugacyClasses(x),
y->[Order(Representative(y)),Size(y)])));
Add(l,AbelianInvariants(x));
return l;
end;
n:=ValueOption("directs");
if n<>fail then
# avoid large number of normals in direct product
n:=Concatenation(List(n,
x->Filtered(NormalSubgroups(x),y->Size(y)>1 and Size(y)<Size(x))));
else
n:=NormalSubgroups(G);
fi;
pat:=List(n,fp);
pools:=[];
for i in Set(pat) do
sel:=Filtered([1..Length(n)],x->pat[x]=i);
Add(pools,n{sel});
od;
return pools;
end);
# form a characterististic series through radical with elab factors
BindGlobal("AGSRCharacteristicSeries",function(G,r)
local somechar,d,i,j,u,v;
d:=Filtered(StructuralSeriesOfGroup(G),x->IsSubset(r,x));
# refine
d:=RefinedSubnormalSeries(d,Centre(G));
d:=RefinedSubnormalSeries(d,Centre(r));
somechar:=ValueOption("someCharacteristics");
if somechar<>fail then
if IsRecord(somechar) then
somechar:=somechar.subgroups;
fi;
for i in somechar do
d:=RefinedSubnormalSeries(d,i);
od;
fi;
if not ForAll([1..Length(d)-1],
x->HasElementaryAbelianFactorGroup(d[x],d[x+1])) then
for i in PrimeDivisors(Size(r)) do
u:=PCore(r,i);
if Size(u)>1 then
d:=RefinedSubnormalSeries(d,u);
j:=1;
repeat
v:=Agemo(u,i,j);
if Size(v)>1 then
d:=RefinedSubnormalSeries(d,v);
fi;
j:=j+1;
until Size(v)=1;
j:=1;
repeat
if Size(u)>=2^24 then
v:=u; # bail out as method for `Omega` will do so.
else
v:=Omega(u,i,j);
if Size(v)<Size(u) then
d:=RefinedSubnormalSeries(d,v);
fi;
j:=j+1;
fi;
until Size(v)=Size(u);
fi;
od;
fi;
Assert(1,ForAll([1..Length(d)-1],x->Size(d[x])<>Size(d[x+1])));
return d;
end);
# main automorphism method -- currently still using factor groups, but
# nevertheless faster..
# option somechar may be a list of characteristic subgroups, or a record with
# component subgroups, orbits
BindGlobal("AutomGrpSR",function(G)
local ff,r,d,ser,u,v,i,j,k,p,bd,e,gens,lhom,M,N,hom,Q,Mim,q,ocr,split,MPcgs,
b,fratsim,AQ,OQ,Zm,D,innC,oneC,imgs,C,maut,innB,tmpAut,imM,a,A,B,
cond,sub,AQI,AQP,AQiso,rf,res,resperm,proj,Aperm,Apa,precond,ac,
comiso,extra,mo,rada,makeaqiso,ind,lastperm,actbase,somechar,stablim,
scharorb,asAutom,jorb,jorpo,substb,isBadPermrep,ma,nosucl,nosuf,rlgf;
# criterion for when to force degree reduction
isBadPermrep:=function(g)
return NrMovedPoints(g)^3>Size(g)*Index(g,DerivedSubgroup(g));
end;
asAutom:=function(sub,hom) return Image(hom,sub);end;
actbase:=ValueOption("autactbase");
nosucl:=fail;
makeaqiso:=function()
local a,b;
Info(InfoMorph,3,"enter makeaqiso");
if HasIsomorphismPermGroup(AQ) then
AQiso:=IsomorphismPermGroup(AQ);
elif HasNiceMonomorphism(AQ) and IsPermGroup(Range(NiceMonomorphism(AQ))) then
AQiso:=NiceMonomorphism(AQ:autactbase:=fail);
else
a:=Filtered(GeneratorsOfGroup(AQ),HasConjugatorOfConjugatorIsomorphism);
if Length(a)>2 then
a:=List(a,ConjugatorOfConjugatorIsomorphism);
b:=SmallGeneratingSet(Group(a));
if Length(b)<Length(a) then
a:=List(b,x->ConjugatorAutomorphism(Source(AQ.1),x));
b:=Filtered(GeneratorsOfGroup(AQ),x->not HasConjugatorOfConjugatorIsomorphism(x));
a:=Concatenation(a,b);
b:=InnerAutomorphismsAutomorphismGroup(AQ);
AQ:=Group(a,One(AQ));
SetInnerAutomorphismsAutomorphismGroup(AQ,b);
SetIsGroupOfAutomorphismsFiniteGroup(AQ,true);
fi;
fi;
if actbase<>fail then
AQiso:=IsomorphismPermGroup(AQ:autactbase:=List(actbase,x->Image(hom,x)));
else
AQiso:=IsomorphismPermGroup(AQ);
fi;
fi;
AQP:=Image(AQiso,AQ);
Info(InfoMorph,3,"Permrep of AQ ",Size(AQ),", deg:",NrMovedPoints(AQP));
if Length(GeneratorsOfGroup(AQP))=Length(GeneratorsOfGroup(AQ)) then
a:=AGSRReducedGens(AQP,AQP,AQ,AQiso);
if a<>fail then
AQP:=a[1];
AQ:=a[2];
fi;
fi;
# force degree down
if isBadPermrep(AQP) then
a:=SmallerDegreePermutationRepresentation(AQP:cheap);
if NrMovedPoints(Image(a))<NrMovedPoints(AQP) then
Info(InfoMorph,3,"Permdegree reduced ",
NrMovedPoints(AQP),"->",NrMovedPoints(Image(a)));
AQiso:=AQiso*a;
b:=Image(a,AQP);
if Length(GeneratorsOfGroup(b))>Length(GeneratorsOfGroup(AQP)) then
b:=Group(List(GeneratorsOfGroup(AQP),x->ImagesRepresentative(a,x)));
SetSize(b,Size(AQP));
fi;
AQP:=b;
fi;
fi;
end;
stablim:=function(gp,cond,lim)
local no,same,sz,ac,i,sub;
same:=true;
repeat
sz:=Size(Aperm);
if Size(gp)/Size(Aperm)>lim then
no:=Normalizer(gp,Aperm);
if Size(no)>Size(Aperm) and Size(no)<Size(gp) then
stablim(no,cond,lim);
fi;
else
no:=Aperm;
fi;
if Size(gp)/Size(Aperm)>lim then
ac:=AscendingChain(gp,Aperm);
List(Union(List(ac,GeneratorsOfGroup)),cond); # try generators...
if Size(Aperm)>sz then
ac:=Unique(List(ac,x->ClosureGroup(Aperm,x)));
fi;
i:=First([Length(ac),Length(ac)-1..1],x->Size(ac[x])/sz<=lim);
sub:=ac[i];
else
sub:=gp;
fi;
if Size(sub)>Size(Aperm) and not IsSubset(no,sub) then
SubgroupProperty(sub,cond,Aperm);
fi;
same:=Size(Aperm)=sz;
if not same then
Info(InfoMorph,3,"stablim improves by ",Size(Aperm)/sz,
" remaining ",Size(gp)/Size(Aperm));
fi;
until same;
return sub=gp;
end;
ff:=FittingFreeLiftSetup(G);
r:=ff.radical;
rlgf:=LGFirst(SpecialPcgs(r));
# find series through r
somechar:=ValueOption("someCharacteristics");
# derived and then primes and then elementary abelian
d:=ValueOption("series");
if d=fail then
d:=AGSRCharacteristicSeries(G,r:someCharacteristics:=somechar);
d:=Reversed(d);
else
d:=ShallowCopy(d);
SortBy(d,Size); # in case reversed order....
fi;
scharorb:=fail;
if somechar<>fail then
if IsRecord(somechar) then
if IsBound(somechar.orbits) then
scharorb:=somechar.orbits;
fi;
somechar:=somechar.subgroups;
fi;
fi;
# now go up in series if elementary abelian to avoid too many tiny steps
u:=1; # last group in the series to be used
i:=2;
while i<Length(d) do
p:=IndexNC(d[i+1],d[u]);
# should we skip group i?
if p<100 and IsPrimePowerInt(p) and
HasElementaryAbelianFactorGroup(d[i+1],d[u]) then
d:=Concatenation(d{[1..i-1]},d{[i+1..Length(d)]});
# i stays the same, as it now is the next subgroup
else
u:=i;
i:=i+1;
fi;
od;
# avoid small central subgroups, as the factor will be hard to represent
u:=Centre(G);
if Size(u)>1 then
p:=SmallestPrimeDivisor(Size(u));
u:=SylowSubgroup(u,p);
u:=Omega(u,p,1);
i:=Position(d,u);
if i<>fail and i>2 then
d:=d{Union([1],[i..Length(d)])};
fi;
fi;
ser:=[TrivialSubgroup(G)];
for i in d{[2..Length(d)]} do
u:=Last(ser);
for p in PrimeDivisors(Size(i)/Size(u)) do
bd:=PValuation(Size(i)/Size(u),p); # max p-exponent
u:=ClosureSubgroup(u,SylowSubgroup(i,p));
v:=Last(ser);
while not HasElementaryAbelianFactorGroup(u,v) do
gens:=Filtered(GeneratorsOfGroup(u),x->not x in v);
e:=List(gens,x->First([1..bd],a->x^(p^a) in v));
e:=p^(Maximum(e)-1);
for j in gens do
v:=ClosureSubgroup(v,j^e);
od;
Add(ser,v);
od;
Add(ser,u);
od;
od;
rada:=fail;
ser:=Reversed(ser);
hom:=ff.factorhom;
Q:=Image(hom,G);
if IsPermGroup(Q) and NrMovedPoints(Q)/Size(Q)*Size(Socle(Q))
>SufficientlySmallDegreeSimpleGroupOrder(Size(Q)) then
# just in case the radical factor hom is inherited.
Q:=SmallerDegreePermutationRepresentation(Q:cheap);
Info(InfoMorph,3,"Radical factor degree reduced ",NrMovedPoints(Range(hom)),
" -> ",NrMovedPoints(Range(Q)));
hom:=hom*Q;
Q:=Image(hom,G);
fi;
ma:=MaximalSubgroupClassesSol(G);
AQ:=AutomorphismGroupFittingFree(Q:someCharacteristics:=fail);
AQI:=InnerAutomorphismsAutomorphismGroup(AQ);
lastperm:=fail;
# preseed natural homs in ascending form (as the largest one might help
# for smaller ones)
for i in [Length(ser),Length(ser)-1..2] do
lhom:=NaturalHomomorphismByNormalSubgroup(G,ser[i]);
od;
i:=1;
while i<Length(ser) do
Assert(2,ForAll(GeneratorsOfGroup(AQ),x->Size(Source(x))=Size(Q)));
# ensure that the step is OK
lhom:=hom;
OQ:=Q;
repeat
Info(InfoMorph,4,List(ser,Size)," ",i);
Info(InfoMorph,1,"Step ",i," ",Size(ser[i]),"->",Size(ser[i+1]));
M:=ser[i];
N:=ser[i+1];
hom:=NaturalHomomorphismByNormalSubgroup(G,N);
Q:=Image(hom,G);
# degree reduction called for?
if IsPermGroup(Q) and Size(N)>1
and (isBadPermrep(Q) or NrMovedPoints(Q)>1000) then
q:=SmallerDegreePermutationRepresentation(Q:cheap);
if NrMovedPoints(Q)>1000
and NrMovedPoints(Q)=NrMovedPoints(Image(q)) then
q:=SmallerDegreePermutationRepresentation(Q);
fi;
if NrMovedPoints(Range(q))<NrMovedPoints(Q) then
Info(InfoMorph,3,"reduced permrep Q ",NrMovedPoints(Q)," -> ",
NrMovedPoints(Range(q)));
hom:=hom*q;
Q:=Image(hom,G);
fi;
fi;
# inherit radical factor map
q:=GroupHomomorphismByImagesNC(Q,Range(ff.factorhom),
List(GeneratorsOfGroup(G),x->ImagesRepresentative(hom,x)),
List(GeneratorsOfGroup(G),x->ImagesRepresentative(ff.factorhom,x)));
b:=Image(hom,ff.radical);
SetSolvableRadical(Q,b);
AddNaturalHomomorphismsPool(Q,b,q);
# Use known maximals for Frattini
for j in ma do
D:=Image(hom,j);
if not IsSubset(D,b) then
b:=Core(Q,NormalIntersection(b,D));
fi;
od;
SetIsNilpotentGroup(b,true);
SetFrattiniSubgroup(Q,b);
# M-factor
Mim:=Image(hom,M);
MPcgs:=Pcgs(Mim);
q:=GroupHomomorphismByImagesNC(Q,OQ,
List(GeneratorsOfGroup(G),x->ImagesRepresentative(hom,x)),
List(GeneratorsOfGroup(G),x->ImagesRepresentative(lhom,x)));
AddNaturalHomomorphismsPool(Q,Mim,q);
mo:=GModuleByMats(LinearActionLayer(GeneratorsOfGroup(Q),MPcgs),GF(RelativeOrders(MPcgs)[1]));
# is the extension split?
ocr:=OneCocycles(Q,Mim);
split:=ocr.isSplitExtension;
if not split then
# test: Semisimple and Frattini
b:=MTX.BasisRadical(mo);
fratsim:=Length(b)=0;
if not fratsim then
b:=List(b,x->PreImagesRepresentative(hom,PcElementByExponents(MPcgs,x)));
for j in b do
N:=ClosureSubgroup(N,b);
od;
# insert in series
for j in [Length(ser),Length(ser)-1..i+1] do
ser[j+1]:=ser[j];
od;
ser[i+1]:=N;
Info(InfoMorph,2,"insert1");
else
# Frattini?
fratsim:=IsSubset(FrattiniSubgroup(Q),Mim);
if not fratsim then
N:=Intersection(FrattiniSubgroup(Q),Mim);
# insert
for j in [Length(ser),Length(ser)-1..i+1] do
ser[j+1]:=ser[j];
od;
ser[i+1]:=PreImage(hom,N);
Info(InfoMorph,2,"insert2");
fi;
N:=ser[i+1]; # the added normal
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;
# Use cocycles
b:=BasisVectors(Basis(ocr.oneCocycles));
# find D
Zm:=PreImage(q,Centre(OQ));
D:=Centralizer(Zm,Mim);
innC:=List(GeneratorsOfGroup(D),d->InnerAutomorphism(Q,d));
D:=List(innC,inn->List(ocr.generators,o->Image(inn,o)));
D:=List(D,d->List([1..Length(ocr.generators)],i->ocr.generators[i]^-1*d[i]));
D:=List(D,d->ocr.listToCocycle(d));
TriangulizeMat(D);
D:=Filtered(D,x->x<>0*x);
b:=BaseSteinitzVectors(b,D).factorspace;
C:=[];
if Size(Group(ocr.generators))<Size(Q) then
extra:=MPcgs;
else
extra:=[];
fi;
for j in b do
oneC := ocr.cocycleToList( j );
imgs:=List([1..Length(ocr.generators)],i->ocr.generators[i]*oneC[i]);
oneC:=GroupHomomorphismByImagesNC(Q,Q,Concatenation(ocr.generators,extra),Concatenation(imgs,extra));
Assert(2,IsBijective(oneC));
Add(C,oneC);
od;
B:=[];
if lastperm<>fail then
AQiso:=lastperm;
AQP:=Group(List(GeneratorsOfGroup(AQ),x->ImagesRepresentative(AQiso,x)));
else
makeaqiso();
fi;
if split then
maut:=MTX.ModuleAutomorphisms(mo);
# find noninner of B
innB:=List(SmallGeneratingSet(Zm),z->InnerAutomorphism(Q,z));
innB:=Group(One(DefaultFieldOfMatrixGroup(maut))*
List(innB,inn->List(MPcgs,m->ExponentsOfPcElement(MPcgs,Image(inn,m)))));
tmpAut:=SubgroupNC(maut,Filtered(GeneratorsOfGroup(maut),aut->not aut in innB));
gens:=GeneratorsOfGroup(ocr.complement);
for a in GeneratorsOfGroup(tmpAut) do
imM:=List(a,i->PcElementByExponents(MPcgs,i));
imM:=GroupHomomorphismByImagesNC(Q,Q,Concatenation(MPcgs,gens),Concatenation(imM,gens));
Assert(2,IsBijective(imM));
Add(B,imM);
od;
# test condition for lifting, also add corresponding automorphism
comiso:=GroupHomomorphismByImagesNC(ocr.complement,OQ,gens,List(gens,x->ImagesRepresentative(q,x)));
precond:=fail;
mo:=GModuleByMats(LinearActionLayer(gens,MPcgs),mo.field);
cond:=function(perm)
local aut,newgens,mo2,iso,a;
if perm in Aperm then
return true;
fi;
aut:=PreImagesRepresentative(AQiso,perm);
newgens:=List(gens,x->PreImagesRepresentative(comiso,
ImagesRepresentative(aut,ImagesRepresentative(comiso,x))));
mo2:=GModuleByMats(LinearActionLayer(newgens,MPcgs),mo.field);
iso:=MTX.IsomorphismModules(mo,mo2);
if iso=fail then
return false;
else
# build associated auto
a:=GroupHomomorphismByImagesNC(Q,Q,Concatenation(gens,MPcgs),
Concatenation(newgens,
List(MPcgs,x->PcElementByExponents(MPcgs,
(ExponentsOfPcElement(MPcgs,x)*One(mo.field))*iso ))));
Assert(2,IsBijective(a));
Add(A,a);
Add(Apa,perm);
Aperm:=ClosureGroup(Aperm,perm);
return true;
fi;
end;
else
# there is no B in the nonsplit case
B:=[];
ocr:=AGSRPrepareAutomLift( Q, MPcgs, q );
precond:=function(perm)
local aut,newgens,mo2;
if perm in Aperm then
return true;
fi;
aut:=PreImagesRepresentative(AQiso,perm);
newgens:=List(GeneratorsOfGroup(Q),
x->PreImagesRepresentative(q,Image(aut,ImagesRepresentative(q,x))));
mo2:=GModuleByMats(LinearActionLayer(newgens,MPcgs),mo.field);
return MTX.IsomorphismModules(mo,mo2)<>fail;
end;
cond:=function(perm)
local aut,newgens,mo2,iso,a;
if perm in Aperm then
return true;
fi;
aut:=PreImagesRepresentative(AQiso,perm);
newgens:=List(GeneratorsOfGroup(Q),
x->PreImagesRepresentative(q,Image(aut,ImagesRepresentative(q,x))));
mo2:=GModuleByMats(LinearActionLayer(newgens,MPcgs),mo.field);
iso:=MTX.IsomorphismModules(mo,mo2);
if iso=fail then
return false;
else
# build associated auto
a:=AGSRAutomLift(ocr,q,aut,iso);
if a=fail then
#Print("test failed\n");
return false;
else
Add(A,a);
Add(Apa,perm);
Aperm:=ClosureGroup(Aperm,perm);
#Print("test succeeded\n");
return true;
fi;
fi;
end;
fi;
# find A using the set condition
A:=[];
Apa:=[];
# note: we do not include AQI here, so might need to add later
Aperm:=SubgroupNC(AQP,List(GeneratorsOfGroup(AQI),
x->ImagesRepresentative(AQiso,x)));
# try to find some further generators
if Size(AQP)/Size(Aperm)>100 then
for j in SpecialPcgs(SolvableRadical(AQP)) do
cond(j);
od;
for j in GeneratorsOfGroup(AQP) do
cond(j);
od;
fi;
sub:=AQP;
if precond<>fail and not ForAll(GeneratorsOfGroup(sub),precond) then
# compatible pairs condition
sub:=SubgroupProperty(sub,precond,Aperm);
fi;
if IndexNC(sub,Aperm)>10^6 then
# try to find characteristic subgroups
Info(InfoMorph,2,"Use normal subgroup classes");
if nosucl=fail then nosucl:=AGSRNormalSubgroupClasses(G);fi;
nosuf:=List(nosucl,x->Set(List(x,y->Image(lhom,y))));
nosuf:=Filtered(nosuf,x->Size(x[1])>1 and Size(x[1])<Size(OQ));
SortBy(nosuf,Length);
for j in nosuf do
# stabilize class
k:=SmallGeneratingSet(sub);
ac:=OrbitStabilizerAlgorithm(sub,false,false,
k,List(k,x->PreImagesRepresentative(AQiso,x)),
rec(pnt:=j,
act:=
function(set,phi)
#local phi;
#phi:=PreImagesRepresentative(AQiso,perm);
return Set(List(set,x->Image(phi,x)));
end,
onlystab:=true));
Info(InfoMorph,3,"Improved index ",IndexNC(sub,ac.stabilizer));
if Size(ac.stabilizer)<Size(sub) then
sub:=ac.stabilizer;
fi;
od;
fi;
j:=Size(sub);
Info(InfoMorph,2,"start search ",IndexNC(sub,Aperm));
sub:=SubgroupConditionAbove(sub,cond,Aperm);
Info(InfoMorph,2,"end search ",j/Size(sub));
# Note: Aperm is larger than what is generated by Apa
j:=1;
while Size(Aperm)<Size(sub) do
ac:=InnerAutomorphism(OQ,Image(q,GeneratorsOfGroup(Q)[j]));
k:=ImagesRepresentative(AQiso,ac);
if not k in Aperm then
Add(Apa,k);
Aperm:=ClosureGroup(Aperm,k);
Add(A,InnerAutomorphism(Q,GeneratorsOfGroup(Q)[j]));
fi;
j:=j+1;
od;
# remove redundant generators. Note that Aperm could be to big, thus
# make group from Apa
ac:=AGSRReducedGens(SubgroupNC(Parent(sub),Apa),Apa,A);
if ac<>fail then
Apa:=GeneratorsOfGroup(ac[1]);
A:=GeneratorsOfGroup(ac[2]);
fi;
Info(InfoMorph,2,"Lift Index ",Size(AQP)/Size(sub));
# now make the new automorphism group
innB:=List(SmallGeneratingSet(Q),x->InnerAutomorphism(Q,x));
gens:=ShallowCopy(innB);
Info(InfoMorph,2,"|gens|=",Length(gens),"+",Length(C),
"+",Length(B),"+",Length(A));
Append(gens,C);
Append(gens,B);
Append(gens,A);
Assert(2,ForAll(gens,IsBijective));
for j in gens do
SetIsBijective(j,true);
od;
A:=Group(gens);
SetIsAutomorphismGroup(A,true);
SetIsGroupOfAutomorphismsFiniteGroup(A,true);
SetIsFinite(A,true);
AQI:=SubgroupNC(A,innB);
SetInnerAutomorphismsAutomorphismGroup(A,AQI);
AQ:=A;
if Size(KernelOfMultiplicativeGeneralMapping(hom))>1
or ValueOption("delaypermrep")<>true then
makeaqiso();
if not IsIdenticalObj(A,AQ) then
A:=AQ;
fi;
else
A!.makeaqiso:=makeaqiso;
fi;
# do we use induced radical automorphisms to help next step?
if Size(KernelOfMultiplicativeGeneralMapping(hom))>1 and
Size(A)>10^8
#(
## potentially large GL
#Size(GL(Length(MPcgs),RelativeOrders(MPcgs)[1]))>10^10 and
## automorphism size really grew from B/C-bit
##Size(A)/Size(AQP)*Index(AQP,sub)>10^10) )
## do so for all factors
and ForAll([2..Length(rlgf)],x->rlgf[x]-rlgf[x-1]<10)
and ValueOption("noradicalaut")<>true then
if rada=fail then
if IsElementaryAbelian(r) and Size(r)>1 then
B:=Pcgs(r);
rf:=GF(RelativeOrders(B)[1]);
ind:=Filtered(ser,x->IsSubset(r,x) and Size(x)>1 and Size(x)<Size(r));
ind:=List(ind,x->List(GeneratorsOfGroup(x),y->ExponentsOfPcElement(B,y)));
ind:=List(ind,x->x*One(rf));
ind:=SpaceAndOrbitStabilizer(Length(B),rf,ind,[]);
rada:=List(GeneratorsOfGroup(ind),x->
GroupHomomorphismByImagesNC(r,r,B,List(x,y->PcElementByExponents(B,List(y,Int)))));
rada:=Group(rada);
SetIsGroupOfAutomorphismsFiniteGroup(rada,true);
NiceMonomorphism(rada:autactbase:=fail,someCharacteristics:=fail);
else
ind:=IsomorphismPcGroup(r);
rada:=AutomorphismGroup(Image(ind,r):someCharacteristics:=fail,autactbase:=fail);
# we only consider those homomorphism that stabilize the series we use
for k in List(ser,x->Image(ind,x)) do
if ForAny(GeneratorsOfGroup(rada),x->Image(x,k)<>k) then
Info(InfoMorph,3,"radical automorphism stabilizer");
NiceMonomorphism(rada:autactbase:=fail,someCharacteristics:=fail);
SetIsGroupOfAutomorphismsFiniteGroup(rada,true);
rada:=Stabilizer(rada,k,asAutom);
fi;
od;
# move back to bad degree
rada:=Group(List(GeneratorsOfGroup(rada),
x-> InducedAutomorphism(InverseGeneralMapping(ind),x)));
fi;
fi;
rf:=Image(hom,r);
Info(InfoMorph,2,"Use radical automorphisms for reduction");
makeaqiso();
B:=MappingGeneratorsImages(AQiso);
res:=List(B[1],x->
GroupHomomorphismByImagesNC(rf,rf,GeneratorsOfGroup(rf),
List(GeneratorsOfGroup(rf),y->ImagesRepresentative(x,y))));
ind:=[];
for j in GeneratorsOfGroup(rada) do
k:=GroupHomomorphismByImagesNC(rf,rf,
GeneratorsOfGroup(rf),
List(GeneratorsOfGroup(rf),
y->ImagesRepresentative(hom,ImagesRepresentative(j,
PreImagesRepresentative(hom,y)))));
Assert(2,IsBijective(k));
Add(ind,k);
od;
C:=Group(Unique(Concatenation(res,ind))); # to guarantee common parent
SetIsFinite(C,true);
SetIsGroupOfAutomorphismsFiniteGroup(C,true);
Size(C:autactbase:=fail,someCharacteristics:=fail); # disable autactbase transfer
res:=SubgroupNC(C,res);
ind:=SubgroupNC(C,ind);
# this should now go via the niceo of C
Size(ind:autactbase:=fail,someCharacteristics:=fail);
Size(res:autactbase:=fail,someCharacteristics:=fail);
ind:=Intersection(res,ind); # only those we care about
if Size(ind)<Size(res) then
# reduce to subgroup that induces valid automorphisms
Info(InfoMorph,1,"Radical autos reduce by factor ",Size(res)/Size(ind));
resperm:=IsomorphismPermGroup(C);
proj:=GroupHomomorphismByImagesNC(AQP,Image(resperm),
B[2],List(GeneratorsOfGroup(res),x->ImagesRepresentative(resperm,x)));
C:=PreImage(proj,Image(resperm,ind));
C:=List(SmallGeneratingSet(C),x->PreImagesRepresentative(AQiso,x));
AQ:=Group(C);
SetIsFinite(AQ,true);
SetIsGroupOfAutomorphismsFiniteGroup(AQ,true);
makeaqiso();
fi;
# # hook for using existing characteristics to reduce for next step
if somechar<>fail then
u:=Filtered(Unique(List(somechar,x->Image(hom,x))),x->Size(x)>1);
u:=Filtered(u,s->ForAny(GeneratorsOfGroup(AQ),h->Image(h,s)<>s));
SortBy(u,Size);
Info(InfoMorph,1,"Forced characteristics ",List(u,Size));
if scharorb<>fail then
# these are subgroups for which certain orbits must be stabilized.
C:=List(Reversed(scharorb),x->List(x,y->Image(hom,y)));
C:=Filtered(C,x->Size(x[1])>1 and Size(x[1])<Size(Q));
Info(InfoMorph,1,"Forced orbits ",List(C,x->Size(x[1])));
Append(u,C);
fi;
if Length(u)>0 then
C:=MappingGeneratorsImages(AQiso);
if C[2]<>GeneratorsOfGroup(AQP) then
C:=[List(GeneratorsOfGroup(AQP),
x->PreImagesRepresentative(AQiso,x)),
GeneratorsOfGroup(AQP)];
fi;
for j in u do
if IsList(j) then
# stabilizer set of subgroups
jorb:=ShallowCopy(Orbit(AQP,j[1],C[2],C[1],asAutom));
jorpo:=[Position(jorb,j[1]),Position(jorb,j[2])];
if jorpo[2]=fail then
Append(jorb,Orbit(AQP,j[1],C[2],C[1],asAutom));
jorpo[2]:=Position(jorb,j[2]);
fi;
if Length(jorb)>Length(j) then
B:=ActionHomomorphism(AQP,jorb,C[2],C[1],asAutom);
substb:=Group(List(C[2],x->ImagesRepresentative(B,x)),());
substb:=Stabilizer(substb,Set(jorpo),OnSets);
substb:=PreImage(B,substb);
Info(InfoMorph,2,"Stabilize characteristic orbit ",Size(j[1]),
" :",Size(AQP)/Size(substb) );
else
substb:=AQP;
fi;
else
substb:=Stabilizer(AQP,j,C[2],C[1],asAutom);
Info(InfoMorph,2,"Stabilize characteristic subgroup ",Size(j),
" :",Size(AQP)/Size(substb) );
fi;
if Size(substb)<Size(AQP) then
B:=Size(substb);
substb:=SmallGeneratingSet(substb);
AQP:=Group(substb);
SetSize(AQP,B);
C:=[List(substb,x->PreImagesRepresentative(AQiso,x)),substb];
fi;
od;
AQ:=Group(C[1]);
SetIsFinite(AQ,true);
SetIsGroupOfAutomorphismsFiniteGroup(AQ,true);
SetSize(AQ,Size(AQP));
#AQP:=Group(C[2]); # ensure small gen set
#SetSize(AQP,Size(AQ));
makeaqiso();
fi;
fi;
lastperm:=AQiso;
else
lastperm:=fail;
fi;
i:=i+1;
od;
return AQ;
end);
# find characteristic subgroups by splitting into elementary abelian,
# homogeneous layers
BindGlobal("AGSRModuleLayerSeries",function(g)
local s,l,r,i,j,sy,hom,p,pcgs;
s:=ShallowCopy(DerivedSeriesOfGroup(g));
r:=SolvableRadical(Last(s));
if Size(r)>1 then # cannot be last, as solvable
Append(s,DerivedSeriesOfGroup(r));
fi;
i:=2;
while i<=Length(s) do
if HasAbelianFactorGroup(s[i-1],s[i]) then
p:=Factors(IndexNC(s[i-1],s[i]))[1];
# is it a single prime?
if not IsPrimePowerInt(IndexNC(s[i-1],s[i])) then
hom:=NaturalHomomorphismByNormalSubgroupNC(s[i-1],s[i]);
sy:=SylowSystem(Image(hom));
l:=[sy[1]];
for j in [2..Length(sy)-1] do
Add(l,ClosureGroup(Last(l),sy[j]));
od;
l:=Reversed(l);
l:=List(l,x->PreImage(hom,x));
Info(InfoMorph,6,"insert prime @",i);
s:=Concatenation(s{[1..i-1]},l,s{[i..Length(s)]});
elif not HasElementaryAbelianFactorGroup(s[i-1],s[i]) then
# not elementary abelian -- pth powers suffice as abelian
l:=s[i];
for j in GeneratorsOfGroup(s[i-1]) do
l:=ClosureGroup(l,j^p);
od;
Info(InfoMorph,6,"insert ppower @",i);
s:=Concatenation(s{[1..i-1]},[l],s{[i..Length(s)]});
else
# make module
pcgs:=ModuloPcgs(s[i-1],s[i]);
l:=LinearActionLayer(g,pcgs);
l:=GModuleByMats(l,GF(p));
# check for characteristic submodules
r:=MTX.BasisRadical(l);
if Length(r)=0 then
r:=MTX.BasisSocle(l);
if Length(r)=l.dimension then
# semisimple -- use homogeneous
sy:=List(MTX.CollectedFactors(l),x->x[1]);
if Length(sy)>1 then
r:=Minimum(List(sy,x->x.dimension));
sy:=Filtered(sy,x->x.dimension=r);
r:=[];
for j in sy do
Append(r,MTX.Homomorphisms(j,l));
od;
r:=Concatenation(r);
r:=Filtered(TriangulizedMat(r),x->not IsZero(x));
else
r:=fail;
fi;
fi;
fi;
if r=fail then
i:=i+1;
else
l:=s[i];
for j in r do;
l:=ClosureGroup(l,PcElementByExponents(pcgs,j));
od;
if Size(l)<Size(s[i-1]) and Size(l)>Size(s[i]) then
Info(InfoMorph,6,"insert module @",i);
s:=Concatenation(s{[1..i-1]},[l],s{[i..Length(s)]});
else
i:=i+1;
fi;
fi;
fi;
else
i:=i+1;
fi;
od;
return s;
end);
# find corresponding characteristic subgroups
BindGlobal("AGSRMatchedCharacteristics",function(g,h)
local a,props,cg,ch,clg,clh,ng,nh,coug,couh,pg,ph,i,j,stop,coinc;
props:=function(a)
local p,b;
if ID_AVAILABLE(Size(a))<>fail then
p:=ShallowCopy(-IdGroup(a)); # negative avoids clash with others
else
p:=[Size(a)];
b:=ShallowCopy(AbelianInvariants(a));
Sort(b);
Add(p,b);
Add(p,List(DerivedSeriesOfGroup(a),Size));
fi;
# # intersections
# for i in [1..Length(chars)] do
# for j in AGSRModuleLayerSeries(chars[i]) do
# der(Intersection(j,a));
# der(ClosureGroup(j,a));
# od;
# od;
return p;
end;
# Process that G-subgroup i matches H-subgroup j. Return true if there is
# a problem.
coinc:=function(i,j)
local a,b,sa,sb,sel,p,q;
if pg[i]<>ph[j] then Error("matching bug");fi;
a:=cg[i];
b:=ch[j];
Add(ng,a);
Add(nh,b);
sel:=Difference([1..Length(cg)],[i]);
cg:=cg{sel};
pg:=pg{sel};
sel:=Difference([1..Length(ch)],[j]);
ch:=ch{sel};
ph:=ph{sel};
# find associated modules and further match
sa:=AGSRModuleLayerSeries(a);
sb:=AGSRModuleLayerSeries(b);
if List(sa,Size)<>List(sb,Size) then return true;fi;
for i in [1..Length(sa)] do
p:=Position(cg,sa[i]);
q:=Position(ch,sb[i]);
if p=fail then
if q<>fail then return true;fi;
elif q=fail then return true;
else
if pg[p]<>ph[q] # but not same properties
or coinc(p,q) then return true;fi;
fi;
od;
return false;
end;
ng:=[];
nh:=[];
# get the characteristic subgroups
cg:=ShallowCopy(CharacteristicSubgroups(g));
ch:=ShallowCopy(CharacteristicSubgroups(h));
SortBy(cg,x->-Size(x));
SortBy(ch,x->-Size(x));
# find list of properties, tryign to match them up
pg:=List(cg,props);
ph:=List(ch,props);
stop:=false;
while Length(cg)>0 and not stop do
# test in loop as list changes
if Collected(pg)<>Collected(ph) then return fail;fi;
i:=First([1..Length(pg)],x->Number(pg,y->y=pg[x])=1);
if i<>fail then
# found a unique one -- process
# because properties agree this must match
j:=Position(ph,pg[i]);
if coinc(i,j) then return fail;fi;
else
stop:=true; # give up, for the moment
# try clusters
j:=Set(pg);
clg:=List(j,x->cg{Filtered([1..Length(cg)],y->pg[y]=x)});
clh:=List(j,x->ch{Filtered([1..Length(ch)],y->ph[y]=x)});
# also use classes of size 1 for compare
Append(clg,List(ng,x->[x]));
Append(clh,List(nh,x->[x]));
# sort larger first
SortParallel(clg,clh,function(a,b) return Size(a[1])>Size(b[1]);end);
i:=1;
while i<=Length(clg) do
if Length(clg[i])>1 then
j:=i+1;
while j<=Length(clg) do
coug:=List(clg[i],x->Number(clg[j],y->IsSubset(x,y)));
couh:=List(clh[i],x->Number(clh[j],y->IsSubset(x,y)));
if Collected(coug)<>Collected(couh) then return fail;fi;
a:=First(Collected(coug),x->x[2]=1);
if a<>fail then
# unique number -- split
a:=a[1];
if coinc(Position(cg,clg[i][Position(coug,a)]),
Position(ch,clh[i][Position(couh,a)])) then return fail;fi;
i:=Length(clg); j:=Length(clg); # break out of loops
stop:=false;
fi;
j:=j+1;
od;
fi;
i:=i+1;
od;
fi;
od;
j:=Set(pg);
return rec(
ng:=ng,nh:=nh,
cg:=List(j,x->cg{Filtered([1..Length(cg)],y->pg[y]=x)}),
ch:=List(j,x->ch{Filtered([1..Length(ch)],y->ph[y]=x)})
);
end);
BindGlobal("AGBoundedOrbrep",function(G,from,to,act,bound)
local orb,rep,S,i,g,img,p;
orb:=[from];
rep:=[One(G)];
S:=[];
i:=1;
while i<=Length(orb) do
for g in GeneratorsOfGroup(G) do
img:=act(orb[i],g);
p:=Position(orb,img);
if p=fail then
if Length(orb)>=bound then return fail;fi; # length bailout
Add(orb,img);
Add(rep,rep[i]*g);
else
img:=rep[i]*g/rep[p];
if not (img in S or img^-1 in S) then
Add(S,img);
fi;
fi;
od;
i:=i+1;
od;
p:=Position(orb,to);
if p=fail then return false;fi; # not in orbit
return rec(rep:=rep[p],orblen:=Length(orb),stabgens:=S);
end);
# pathetic isomorphism test, based on the automorphism group of GxH. This is
# only of use as long as we don't yet have a Cannon/Holt version of
# isomorphism available and there are many generators
InstallGlobalFunction(PatheticIsomorphism,function(G,H)
local d,a,map,cG,nG,nH,i,j,u,v,asAutomorphism,K,L,conj,e1,e2,
iso,api,gens,pre,aab,as,somechar;
asAutomorphism:=function(sub,hom)
return Image(hom,sub);
end;
# TODO: use matgrp package
if not (IsPermGroup(G) or IsPcGroup(G)) then
i:=IsomorphismPermGroup(G);
iso:=PatheticIsomorphism(Image(i,G),H);
if iso=fail then
return iso;
else
return i*iso;
fi;
fi;
# TODO: use matgrp package
if not (IsPermGroup(H) or IsPcGroup(H)) then
i:=IsomorphismPermGroup(H);
iso:=PatheticIsomorphism(G,Image(i,H));
if iso=fail then
return iso;
else
return iso*InverseGeneralMapping(i);
fi;
fi;
# go through factors of characteristic series to keep orbits short.
AutomorphismGroup(G:someCharacteristics:=fail);
AutomorphismGroup(H:someCharacteristics:=fail);
d:=AGSRMatchedCharacteristics(G,H);
if d=fail then return fail;fi; # characteristics do not match
nG:=d.ng;
nH:=d.nh;
for i in [1..Length(d.cg)] do
u:=TrivialSubgroup(G);
for j in d.cg[i] do
u:=ClosureGroup(u,j);
od;
if not u in nG then
Add(nG,u);
u:=TrivialSubgroup(H);
for j in d.ch[i] do
u:=ClosureGroup(u,j);
od;
Add(nH,u);
fi;
od;
d:=DirectProduct(G,H);
e1:=Embedding(d,1);
e2:=Embedding(d,2);
# combine images of characteristic factors, reverse order
cG:=[];
nG:=Reversed(nG);
nH:=Reversed(nH);
for i in [1..Length(nG)] do
Add(cG,ClosureGroup(Image(e1,nG[i]),Image(e2,nH[i])));
od;
nG:=Concatenation([TrivialSubgroup(G)],nG);
nH:=Concatenation([TrivialSubgroup(H)],nH);
SortParallel(nG,nH,function(a,b) return Size(a)<Size(b);end);
if List(nG,Size)<>List(nH,Size) then return fail;fi;
for i in [2..Length(nG)] do
K:=Filtered([1..Length(nG)],x->Size(nG[x])*2=Size(nG[i])
and IsSubset(nG[i],nG[x]));
if Length(K)>0 then
K:=K[1];
# We are seeking an isomorphism, not the full automorphism group of
# GxG. It is thus sufficient, if we find the subgroup Aut(G)\wr 2.
# We now found that G and H have two characteristic subgroups A<B with
# [B:A]=2. An isomorphism swapping G and H will need to map B to B and
# A to A. Furthermore, in the factor modulo A_G xA_H, a generator of
# B_G must be swappes with a generator of B_H.
# This implies that A_G\times A_H, together with the diagonal of B is
# characteristic in Aut(A)\wr 2. We thus may add this subgroup as
# ``characteristic'' to improve the series.
Add(cG,ClosureGroup(
ClosureGroup(Image(e1,nG[K]),Image(e2,nH[K])),
Image(e1,First(GeneratorsOfGroup(nG[i]),x->not x in nG[K]))
*Image(e2,First(GeneratorsOfGroup(nH[i]),x->not x in nH[K]))));
fi;
od;
aab:=[Image(e1,G),Image(e2,H)];
# we also fix the *pairs* of the characteristic subgroups as orbits. Again
# this must happen in Aut(G)\wr 2, and reduces the size of the group.
somechar:=rec(subgroups:=cG,
orbits:=List([1..Length(nG)],x->[Image(e1,nG[x]),Image(e2,nH[x])]));
a:=AutomorphismGroup(d:autactbase:=aab,someCharacteristics:=somechar,
directs:=aab,
delaypermrep:=true );
for i in cG do
if not ForAll(GeneratorsOfGroup(a),x->Image(x,i)=i) then
a:=Stabilizer(a,i,asAutomorphism);
fi;
od;
iso:=fail;
#if NrMovedPoints(api)>5000 then
# K:=SmallerDegreePermutationRepresentation(api);
# Info(InfoMorph,2,"Permdegree reduced ",
# NrMovedPoints(api),"->",NrMovedPoints(Image(K)));
# iso:=iso*K;
# api:=Image(iso);
# fi;
# now work in reverse through the characteristic factors
conj:=One(a);
K:=Image(e1,G);
L:=Image(e2,H);
map:=AGBoundedOrbrep(a,K,L,asAutomorphism,20);
if map=false then
Info(InfoMorph,1,"Shortorb test noniso");
return fail;
elif map<>fail then
conj:=map.rep;
Info(InfoMorph,1,"Shortorb test iso found");
else
as:=a;
Add(cG,TrivialSubgroup(d));
SortBy(cG,x->-Size(x));
for i in cG do
u:=ClosureGroup(i,K);
v:=ClosureGroup(i,L);
if u<>v then
# try cheap orbit stabilizer first
if iso<>fail then
map:=fail;
else
map:=AGBoundedOrbrep(as,u,v,asAutomorphism,100);
fi;
if map=false then
Info(InfoMorph,1,"Shortorb factor noniso");
return fail;
elif map<>fail then
Info(InfoMorph,1,"Shortorb factor reduce ",map.orblen);
as:=SubgroupNC(Parent(as),map.stabgens);
map:=map.rep;
conj:=conj*map;
K:=Image(map,K);
as:=as^map;
else
if iso=fail then
Info(InfoMorph,1,"Shortorb failed, get delayed permrep");
if IsBound(a!.makeaqiso) then a!.makeaqiso();fi;
iso:=IsomorphismPermGroup(a:autactbase:=aab);
api:=Image(iso,as);
fi;
if IsSolvableGroup(api) then
gens:=Pcgs(api);
else
gens:=SmallGeneratingSet(api);
fi;
pre:=List(gens,x->PreImagesRepresentative(iso,x));
map:=RepresentativeAction(SubgroupNC(a,pre),u,v,asAutomorphism);
if map=fail then
return fail;
fi;
conj:=conj*map;
K:=Image(map,K);
if Size(i)>1 then
u:=Stabilizer(api,v,gens,pre,asAutomorphism);
Info(InfoMorph,1,"Factor ",Size(d)/Size(i),": ",
"reduce by ",Size(api)/Size(u));
api:=u;
fi;
fi;
fi;
od;
fi;
return GroupHomomorphismByImagesNC(G,H,GeneratorsOfGroup(G),
List(GeneratorsOfGroup(G),x->PreImagesRepresentative(e2,
Image(conj,Image(e1,x)))));
end);
[ Dauer der Verarbeitung: 0.24 Sekunden
(vorverarbeitet)
]
|