Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
| Download
GAP 4.8.9 installation with standard packages -- copy to your CoCalc project to get it
Project: cocalc-sagemath-dev-slelievre
Views: 418346############################################################################# ## #W claspcgs.gi GAP library Heiko Theißen ## ## #Y Copyright (C) 1997, Lehrstuhl D für Mathematik, RWTH Aachen, Germany #Y (C) 1998 School Math and Comp. Sci., University of St Andrews, Scotland #Y Copyright (C) 2002 The GAP Group ## ## This file contains functions that deal with conjugacy topics in solvable ## groups using affine methods. These topics includes calculating the ## (rational) conjugacy classes and centralizers in solvable groups. The ## functions rely only on the existence of pcgs, not on the particular ## representation of the groups. ## ############################################################################# ## #F SubspaceVectorSpaceGroup( <N>, <p>, <gens>, <howmuch> ) ## ## This function creates a record containing information about a complement ## in <N> to the span of <gens>. ## InstallGlobalFunction( SubspaceVectorSpaceGroup, function( N, p, gens,howmuch ) local zero, one, r, ran, n, nan, cg, pos, Q, i, j, v; one:=One( GF( p ) ); zero:=0 * one; r:=Length( N ); ran:=[ 1 .. r ]; n:=Length( gens ); nan:=[ 1 .. n ]; Q:=[ ]; if n <> 0 and IsMultiplicativeElementWithInverse( gens[ 1 ] ) then Q:=List( gens, gen -> ExponentsOfPcElement( N, gen ) ) * one; else Q:=ShallowCopy( gens ); fi; cg:=rec( matrix :=[ ], one :=one, baseComplement:=ShallowCopy( ran ), commutator :=0, centralizer :=0, dimensionN :=r, dimensionC :=n ); if n = 0 or r = 0 then cg.inverse:=NullMapMatrix; cg.projection :=IdentityMat( r, one ); cg.needed :=[]; return cg; fi; for i in nan do cg.matrix[ i ]:=Concatenation( Q[ i ], zero * nan ); cg.matrix[ i ][ r + i ]:=one; od; TriangulizeMat( cg.matrix ); pos:=1; for v in cg.matrix do while v[ pos ] = zero do pos:=pos + 1; od; RemoveSet( cg.baseComplement, pos ); if pos <= r then cg.commutator :=cg.commutator + 1; else cg.centralizer:=cg.centralizer + 1; fi; od; if howmuch=1 then return Immutable(cg); fi; cg.needed :=[ ]; cg.projection :=IdentityMat( r, one ); # Find a right pseudo inverse for <Q>. Append( Q, cg.projection ); Q:=MutableTransposedMat( Q ); TriangulizeMat( Q ); Q:=TransposedMat( Q ); i:=1; j:=1; while i <= Length( N ) do while j <= Length( gens ) and Q[ j ][ i ] = zero do j:=j + 1; od; if j <= Length( gens ) and Q[ j ][ i ] <> zero then cg.needed[ i ]:=j; else # If <Q> does not have full rank, terminate when the bottom row # is reached. i:=Length( N ); fi; i:=i + 1; od; if IsEmpty( cg.needed ) then cg.inverse:=NullMapMatrix; else cg.inverse:=Q{ Length( gens ) + ran } { [ 1 .. Length( cg.needed ) ] }; cg.inverse:=ImmutableMatrix(p,cg.inverse,true); fi; if IsEmpty( cg.baseComplement ) then cg.projection:=NullMapMatrix; else # Find a base change matrix for the projection onto the complement. for i in [ 1 .. cg.commutator ] do cg.projection[ i ][ i ]:=zero; od; Q:=[ ]; for i in [ 1 .. cg.commutator ] do Q[ i ]:=cg.matrix[ i ]{ ran }; od; for i in [ cg.commutator + 1 .. r ] do Q[ i ]:=ListWithIdenticalEntries( r, zero ); Q[ i ][ cg.baseComplement[ i-r+Length(cg.baseComplement) ] ] :=one; od; cg.projection:=cg.projection ^ Q; cg.projection:=cg.projection{ ran }{ cg.baseComplement }; cg.projection:=ImmutableMatrix(p,cg.projection,true); fi; return Immutable(cg); end ); ############################################################################# ## #F KernelHcommaC( <N>, <h>, <C>, <howmuch> ) ## ## Given a homomorphism C -> N, c |-> [h,c], this function determines (a) a ## vector space decomposition N = [h,C] + K with projection onto K and (b) ## the ``kernel'' S < C which plays the role of C_G(h) in lemma 3.1 of ## [Mecky, Neub\"user, Bull. Aust. Math. Soc. 40]. ## InstallGlobalFunction( KernelHcommaC, function( N, h, C, howmuch ) local i, tmp, v,x; x:=List( C, c -> Comm( h, c ) ); N!.subspace:=SubspaceVectorSpaceGroup(N,RelativeOrders(N)[1],x,howmuch); tmp:=[ ]; for i in [ N!.subspace.commutator + 1 .. N!.subspace.commutator + N!.subspace.centralizer ] do v:=N!.subspace.matrix[ i ]; tmp[ i - N!.subspace.commutator ]:=PcElementByExponentsNC( C, v{ [ N!.subspace.dimensionN + 1 .. N!.subspace.dimensionN + N!.subspace.dimensionC ] } ); od; return tmp; end ); ############################################################################# ## #F CentralStepClEANS( <homepcgs>,<H>, <U>, <N>, <cl>,<off> ) ## # if <off> is true the normal subgroup is not necessarily in the series and # we cannot call `ExtendedPcgs' but must form a new pcgs. InstallGlobalFunction( CentralStepClEANS, function( home,H, U, N, cl,off ) local classes, # classes to be constructed, the result field, # field over which <N> is a vector space h, # preimage `cl.representative' under <hom> gens, # preimage `Centralizer( cl )' under <hom> cemodk, cengen, exp, w, # coefficient vectors for projection along $[h,N]$ kern,img, c,nc; # loop variable field:=GF( RelativeOrders( N )[ 1 ] ); h:=cl.representative; if IsBound(cl.centralizerpcgs) then if IsSubset(cl.centralizerpcgs,DenominatorOfModuloPcgs(N!.capH)) then cemodk:=Filtered(cl.centralizerpcgs,i->not i in DenominatorOfModuloPcgs(N!.capH)); else cemodk:=cl.centralizerpcgs mod DenominatorOfModuloPcgs( N!.capH ); fi; else cemodk:=InducedPcgs(home, cl.centralizer ) mod DenominatorOfModuloPcgs( N!.capH ); fi; kern:=DenominatorOfModuloPcgs( N!.capH ); if IsBound(cl.candidates) then img:=KernelHcommaC( N, h, cemodk,2 ); else img:=KernelHcommaC( N, h, cemodk,1 ); fi; if off then cengen:=InducedPcgsByPcSequenceAndGenerators(ParentPcgs( kern ), kern, img ); else #cengen:=ExtendedPcgs(kern,img); cengen:=Concatenation(img,kern); fi; #C:=SubgroupByPcgs( H, cengen ); classes:=[ ]; if IsBound( cl.candidates ) then gens:=cemodk{ N!.subspace.needed }; if IsIdenticalObj( FamilyObj( U ), FamilyObj( cl.candidates ) ) then for c in cl.candidates do exp:=ExponentsOfPcElement( N, LeftQuotient( h, c ) ); MultRowVector( exp, One( field ) ); w:=exp * N!.subspace.projection; exp{ N!.subspace.baseComplement }:= exp{ N!.subspace.baseComplement }-w; nc:=rec( representative:=h * PcElementByExponentsNC ( N, N!.subspace.baseComplement, w ), #centralizer:=C, #centralizerpcgs:=cengen, cengen:=cengen, operator:=LinearCombinationPcgs( gens, exp * N!.subspace.inverse, One( cl.candidates[1] ))^(-1)); # check that action is really OK Assert(1,c^nc.operator/nc.representative in Group(DenominatorOfModuloPcgs(N),One(U))); Add( classes, nc ); od; else c:=rec( representative:=cl.candidates, #centralizer:=C, #centralizerpcgs:=cengen, cengen:=cengen, operator:=One( H ) ); Add( classes, c ); fi; else gens:=N!.subspace.baseComplement; for w in field ^ Length( gens ) do c:=rec( representative:=h * PcElementByExponentsNC( N,gens,w ), #centralizer:=C ) #centralizerpcgs:=cengen ) cengen:=cengen ); Add( classes, c ); od; fi; return classes; end ); ############################################################################# ## #F CorrectConjugacyClass(<home>,<h>,<n>,<stabpcgs>,<N>,<depth>,<cNh>,<off> ) ## cf. MN89 ## InstallGlobalFunction( CorrectConjugacyClass, function( home, h, n, stab, N,depthlev, cNh,off ) local cl, comm, s, ostab; ostab:=stab; #AH: take only those elements module N - the part in N is cNh stab:=Filtered(stab,i->DepthOfPcElement(home,i)<depthlev); if Length(N!.subspace.inverse)>0 and Length(stab)>0 then comm:=[]; for s in [ 1 .. Length( stab ) ] do comm[ s ]:=ExponentsOfPcElement( N, Comm( n, stab[ s ] )*Comm( h, stab[ s ] )); od; comm:=comm * N!.subspace.inverse; for s in [ 1 .. Length( comm ) ] do stab[ s ]:=stab[ s ] / PcElementByExponentsNC ( N!.capH, N!.subspace.needed, comm[ s ] ); od; fi; if off then stab:=InducedPcgsByPcSequenceAndGenerators(ParentPcgs( cNh ), cNh, stab ); elif IsList(cNh) and IsList(cNh[1]) then #stab:=ExtendedPcgs(cNh[1],Concatenation(stab,cNh[2])); stab:=Concatenation(stab,cNh[2],cNh[1]); else #stab:=ExtendedPcgs(cNh,stab); stab:=Concatenation(stab,cNh); fi; cl:=rec( representative:=h * n, cengen:=stab ); return cl; end ); ############################################################################# ## #F GeneralStepClEANS( <homepcgs>, <H>, <U>, <N>,<nexpo>, <cl>,<off> ) ## # if <off> is true the normal subgroup is not necessarily in the series and # we cannot call `ExtendedPcgs' but must form a new pcgs. InstallGlobalFunction(GeneralStepClEANS,function(home, H, U, N,nexpo, cl, off) local classes, # classes to be constructed, the result field, # field over which <N> is a vector space h, # preimage `cl.representative' under <hom> cNh, # centralizer of <h> in <N> gens, # preimage `Centralizer( cl )' under <hom> r, # dimension of <N> ran, # constant range `[ 1 .. r ]' aff, # <N> as affine space xset, # affine operation of <C> on <aff> imgs, M, # generating matrices for affine operation orb, # orbit of affine operation Rep, # representative function to use for <orb> n, k, # cf. Mecky--Neub\"user paper cls,rep,pos,# set of classes with canonical representatives j, c, ca, i, # loop variables S, # orbit-stabilizer ceve,#xponent vector p, # positions Cgens, # generators of C in N next,blist, # orbit stabilizer algo depthlev, # depth at which N starts one,zero, vec, kern,img; depthlev:=DepthOfPcElement(home,N[1]); Cgens:=cl.centralizerpcgs; field:=GF( RelativeOrders( N )[ 1 ] ); h:=cl.representative; # Determine the subspace $[h,N]$ and calculate the centralizer of <h>. kern:=DenominatorOfModuloPcgs( N!.capH ); img:=KernelHcommaC( N, h, N!.capH,2 ); r:=Length( N!.subspace.baseComplement ); #AH: Take only those which are not in N gens:=Cgens mod NumeratorOfModuloPcgs(N!.capH); if not (off or IsBound(cl.candidates)) and r=0 then # special treatment: The commutators span the whole space # this is noncentral_case4 in GAP3 c:=CorrectConjugacyClass( home, h, One(gens[1]), gens, N, depthlev,[kern,img],off ); return [c]; fi; ran:=[ 1 .. r ]; if off then cNh:=InducedPcgsByPcSequenceAndGenerators(ParentPcgs( kern ), kern, img ); else #cNh:=ExtendedPcgs(kern,img); cNh:=[kern,img]; # we only need cNh to extend it fi; # Construct matrices for the affine operation on $N/[h,N]$. aff:=ExtendedVectors( field ^ r ); one:=One(field); zero:=Zero(field); imgs:=[ ]; for c in gens do ceve:=ExponentsOfPcElement(home,c,[1..depthlev-1]); M:=[ ]; for i in [ 1 .. r ] do p:=N!.subspace.baseComplement[i]; # construct the vector image vec:=p; for j in [1..Length(ceve)] do for k in [1..ceve[j]] do if IsInt(vec) then vec:=nexpo[j][vec]; else vec:=vec*nexpo[j]; fi; od; od; M[ i ]:=Concatenation( vec * N!.subspace.projection, [ zero ] ); od; i:=Comm( h, c ); M[ r + 1 ]:=Concatenation( ExponentsOfPcElement ( N, i ) * N!.subspace.projection, [ one ] ); M:=ImmutableMatrix(field,M,true); Add( imgs, M ); od; classes:=[ ]; if IsBound( cl.candidates ) then # not yet improved: we use an external set and thus have to give a # full list of generators of C: imgs:=Concatenation(imgs,List([1..Length(Cgens)-Length(gens)],i->IdentityMat( r + 1, field ))); gens:=Cgens; xset:=ExternalSet(SubgroupByPcgs(H,Cgens),aff,gens,imgs,OnPoints); if IsIdenticalObj( FamilyObj( U ), FamilyObj( cl.candidates ) ) then Rep:=CanonicalRepresentativeOfExternalSet; else cl.candidates:=[ cl.candidates ]; Rep:=Representative; fi; cls:=[ ]; for ca in cl.candidates do n:=ExponentsOfPcElement( N, LeftQuotient( h, ca ) ) * One( field ); ConvertToVectorRep(n, field); k:=n * N!.subspace.projection; orb:=Concatenation( k, [ One( field ) ]); ConvertToVectorRep(orb, field); orb:=ExternalOrbit( xset, Immutable(orb) ); rep:=PcElementByExponentsNC( N, N!.subspace.baseComplement, Rep( orb ){ ran } ); pos:=Position( cls, rep ); if pos = fail then Add( cls, rep ); c:=StabilizerOfExternalSet( orb ); if IsIdenticalObj( Rep, CanonicalRepresentativeOfExternalSet ) then c:=ConjugateSubgroup( c, ActorOfExternalSet( orb ) ); fi; c:=CorrectConjugacyClass( home, h, rep, InducedPcgs(home,c), N, depthlev,cNh,off ); else c:=rec( representative:=h * rep, #centralizer:=classes[ pos ].centralizer ) #centralizerpcgs:=classes[ pos ].centralizerpcgs ) cengen:=classes[ pos ].cengen ); fi; n:=ShallowCopy( -n ); n{ N!.subspace.baseComplement }:= k + n{ N!.subspace.baseComplement }; c.operator:=PcElementByExponentsNC( N, N!.subspace.needed, n * N!.subspace.inverse ); # Now (h.n)^c.operator = h.k if IsIdenticalObj(Rep,CanonicalRepresentativeOfExternalSet) then c.operator:=c.operator * ActorOfExternalSet( orb ); # Now (h.n)^c.operator = h.rep mod [h,N] k:=PcElementByExponentsNC( N, N!.subspace.needed, ExponentsOfPcElement( N, LeftQuotient ( c.representative, ca ^ c.operator ) ) * N!.subspace.inverse ); c.operator:=c.operator / k; # Now (h.n)^c.operator = h.rep fi; Add( classes, c ); od; else #xset:=ExternalSet( C, aff, gens, imgs ); #k:=ExternalOrbitsStabilizers( xset ); # do the orbits stuff ourselves blist:=BlistList([1..Length(aff)],[]); next:=1; k:=[]; while next<>fail do S:=Pcs_OrbitStabilizer(gens,aff,aff[next],imgs,OnRight); # tick off if IsPositionDictionary(S.dictionary) then UniteBlist(blist,S.dictionary!.blist); else for i in S.orbit do blist[PositionCanonical(aff,i)]:=true; od; fi; Unbind(S.dictionary); Add(k,S); next:=Position(blist,false,next); od; for orb in k do rep:=PcElementByExponentsNC( N, N!.subspace.baseComplement, orb.orbit[1]{ ran } ); c:=CorrectConjugacyClass( home, h, rep, #orb.stabilizer, N, depthlev,cNh,off ) orb.stabpcs, N, depthlev,cNh,off ); Add( classes, c ); od; fi; return classes; end ); ############################################################################# ## #F ClassesSolvableGroup(<G>, <mode> [,<opt>]) . . . . . ## ## In this function classes are described by records with components ## `representative', `centralizer', `galoisGroup' (for rational classes). If ## <candidates> are given, their classes will have a canonical ## `representative' ## InstallGlobalFunction(ClassesSolvableGroup, function(arg) local G, home, # the group and the home pcgs H,Hp, # acting group mustlift, liftkerns, QH,QG, fhome,ofhome, first, mode, # LSB: ratCl | power | test :MSB candidates, # candidates to be replaced by their canonical reps. eas, # elementary abelian series in <G> step, # counter looping over <eas> K, L, # members of <eas> indstep, # indice normal steps Ldep, # depth of L in pcgs Kp,mK,Lp,mL, # induced and modulo pcgs's LcapH,KcapH, # intersections N, cent, # elementary abelian factor, for affine action cls, newcls, # classes in range/source of homomorphism cli, # index news, # new classes obtained in step cl, # class looping over <cls> opr, exp, # (candidates[i]^opr[i])^exp[i]=cls[i].representative team, # team of candidates with same image modulo <K> blist,pos,q, # these control grouping of <cls> into <team>s p, # prime dividing $|G|$ i,c, # loop variables opt, # options consider, # consider function divi, inflev, # InfoLevel flag nexpo, # N-Exponents of the elements of N conjugated allcent; # DivisorsInt(Size(G)) (used for Info) inflev:=InfoLevel(InfoClasses)>1; mode:=arg[2]; # explained below whenever it appears if mode mod 2=1 then Error("this function does not cater for rational classes any longer"); fi; G:=arg[1]; if Length(arg)=3 then opt:=ShallowCopy(arg[3]); # convert series to pcgs if IsBound(opt.series) and not IsBound(opt.pcgs) then fi; else opt:=rec(); fi; # <candidates> is a list of elements whose classes will be output (but # with canonical representatives), see comment above. Or <candidates> is # just one element, from whose output class the centralizer will be read # off. H:=G; if IsBound(opt.candidates) then candidates:=opt.candidates; if not ForAll(candidates,i->i in G) then G:=ClosureGroup(H,candidates); fi; else candidates:=false; fi; if IsBound(opt.consider) then consider:=opt.consider; else consider:=true; fi; # Treat the case of a trivial group. if IsTrivial(H) then if mode=4 then # test conjugacy of two elements return One(G); else cl:=rec(representative:=One(G), centralizer:=H); fi; if candidates<>false then cls:=List(candidates, c -> cl); else cls:=[cl]; fi; return cls; fi; # Calculate a (central) elementary abelian series with all pcgs induced # w.r.t. <homepcgs>. if IsBound(opt.pcgs) then # we prescribed a series home:=opt.pcgs; eas:=EANormalSeriesByPcgs(home); cent:=false; elif IsPrimePowerInt(Size(G)) then p:=FactorsInt(Size(G))[1]; home:=PcgsPCentralSeriesPGroup(G); eas:=PCentralNormalSeriesByPcgsPGroup(home); cent:=ReturnTrue; else home:=PcgsElementaryAbelianSeries(G); eas:=EANormalSeriesByPcgs(home); cent:=function(cl, N, L) return ForAll(N, k -> ForAll #(InducedPcgs(home,cl.centralizer), (cl.centralizerpcgs, #T was: Only those elements form the induced PCGS. The subset seemed to #T enforce taking only the elements up, but the ordering of the series used #T may be different then the ordering in the PCGS. So this will fail. AH #T one might pick the right ones, but this would be almost the same work. #T { [1 .. Length(InducedPcgsWrtHomePcgs(cl.centralizer)) #T - Length(InducedPcgsWrtHomePcgs(L))] }, c -> Comm(k, c) in L)); end; cent:=false; fi; if cent=false then # AH, 26-4-99: Test centrality not via `in' but via exponents cent:=function(pcgs,grpg,Npcgs,dep) local i,j; for i in grpg do for j in Npcgs do if DepthOfPcElement(pcgs,Comm(j,i))<dep then return false; fi; od; od; return true; end; fi; indstep:=IndicesEANormalSteps(home); # check to which factors we want to lift mustlift:=List(eas,i->false); liftkerns:=[]; if candidates=false then # we only want to go in factor groups if no candidates are given # (otherwise we'd have to take care not to forget tails when mapping in # the factor groups) step:=2; # the first step we'd have for i in [2..Length(eas)-1] do if Index(G,eas[i])>1000 or Index(G,eas[i+1])>10000 then # only form a factor if the factor is large enough or the next step # would be large # form a factor by i and go to this factor at the first time (index # step) no factor representation was given mustlift[step]:=true; liftkerns[step]:=eas[i]; step:=i+1; fi; od; if step>2 then # we created a factor, so we have to lift at the end mustlift[step]:=true; liftkerns[step]:=eas[Length(eas)]; fi; fi; Info(InfoClasses,1,"Series of sizes ",List(eas,Size)); if mode<3 and inflev then divi:=DivisorsInt(Size(G)); Info(InfoClasses,2,"centsiz: ",divi); fi; # Initialize the algorithm for the trivial group. step:=1; L:=eas[step]; Lp:=InducedPcgs(home,L); if not IsIdenticalObj( G, H ) then Hp:=InducedPcgs(home, H ); LcapH:=NormalIntersectionPcgs( home, Hp, Lp ); fi; if candidates<>false then mL:=ModuloPcgsByPcSequenceNC(home, home, Lp); fi; cl:=rec(representative:=One(G), centralizer:=H, centralizerpcgs:=InducedPcgs(home,H), cengen:=InducedPcgs(home,H)); if candidates<>false then cls:=List(candidates, c -> cl); opr:=List(candidates, c -> One(G)); exp:=ListWithIdenticalEntries(Length(candidates), 1); else cls:=[cl]; fi; # Now go back through the factors by all groups in the elementary abelian # series. first:=true; fhome:=home; # just to avoid unboundness the first time QG:=G; QH:=H; for step in [step + 1 .. Length(eas)] do Info(InfoClasses,1,"Step ",step,", ",Length(cls)," classes to lift"); # We apply the homomorphism principle to the homomorphism G/L -> G/K. if mustlift[step] then ofhome:=fhome; # get the new quotient and Q's if Size(eas[step])=1 then QH:=H; fhome:=home; QG:=G; else # the new factor group in which we calculate QH:=home mod InducedPcgs(home,liftkerns[step]); QH:=GROUP_BY_PCGS_FINITE_ORDERS(QH); fhome:=FamilyPcgs(QH); QG:=SubgroupByPcgs(QG, ProjectedInducedPcgs(home,fhome,InducedPcgs(home,G))); fi; fi; # The actual computations are all done in <G>, factors are # represented by modulo pcgs. Ldep:=indstep[step]; if IsIdenticalObj(fhome,home) then K:=eas[step-1]; Kp:=InducedPcgs(fhome,K); L:=eas[step]; Lp:=InducedPcgs(fhome,L); elif mustlift[step] then Kp:=ProjectedInducedPcgs(home,fhome,InducedPcgs(home,eas[step-1])); K:=SubgroupByPcgs(QG,Kp); Lp:=ProjectedInducedPcgs(home,fhome,InducedPcgs(home,eas[step])); L:=SubgroupByPcgs(QG,Lp); # not needed any longer else # we did not lift K:=L; Kp:=Lp; Lp:=ProjectedInducedPcgs(home,fhome,InducedPcgs(home,eas[step])); L:=SubgroupByPcgs(QG,Lp); # not needed any longer fi; N:=Kp mod Lp; # modulo pcgs representing the kernel if mustlift[step] then for i in cls do if not IsBound(i.yet) then if first then # if it is the first time, we must actually map in the factor i.representative:=ProjectedPcElement(home,fhome,i.representative); i.centralizerpcgs:=ProjectedInducedPcgs(home,fhome,i.cengen); i.cengen:=i.centralizerpcgs!.pcSequence; else i.representative:=LiftedPcElement(fhome,ofhome,i.representative); i.centralizerpcgs:=LiftedInducedPcgs(fhome,ofhome,i.cengen,N); i.cengen:=i.centralizerpcgs!.pcSequence; fi; i.yet:=true; # several cl records may be equal. We must map only # once fi; od; else for i in cls do if IsBound(i.cengen) and not IsBound(i.centralizerpcgs) then i.centralizerpcgs:=InducedPcgsByPcSequence(fhome,i.cengen); i.cengen:=i.centralizerpcgs!.pcSequence; fi; od; fi; first:=false; # allcent:=ForAll(N,i->ForAll(GeneratorsOfGroup(G),j->Comm(i,j) in L)) allcent:=cent(fhome,fhome,N,Ldep); if allcent=false then nexpo:=LinearOperationLayer(fhome{[1..indstep[step-1]-1]},N); fi; #T What is this? Obviously it is needed somewhere, but it is #T certainly not good programming style. AH #SetFilterObj(N, IsPcgs); if not IsIdenticalObj(G,H) then Error("This case disabled -- code not yet corrected"); KcapH:=LcapH; LcapH:=NormalIntersectionPcgs(fhome,Hp,Lp); N!.capH:=KcapH mod LcapH; SetFilterObj( N!.capH, IsPcgs ); else N!.capH:=N; fi; # Identification of classes. # Rational classes or identification of classes. if candidates<>false then mK:=mL; mL:=ModuloPcgsByPcSequenceNC(fhome, fhome, Lp); if mode=4 # test conjugacy of two elements and not cls[1].representative / cls[2].representative in K then return fail; fi; blist:=BlistList([1 .. Length(cls)], []); pos:=Position(blist, false); while pos<>fail do # Find a team of candidates with same image under <modK>. cl:=cls[pos]; cl.representative:=PcElementByExponentsNC(mK, ExponentsOfPcElement(mK, cl.representative)); cl.candidates:=[]; team:=[]; q:=pos; while q<>fail do if cls[q].representative / cl.representative in K then c:=candidates[q] ^ opr[q]; i:=PositionSorted(cl.candidates, c); if i > Length(cl.candidates) or cl.candidates[i]<>c then Add(cl.candidates, c,i); Add(team, [q], i); else Add(team[i], q); fi; blist[q]:=true; fi; q:=Position(blist, false, q); od; # Now <cl> is a class modulo <K> (possibly with # `<cl>.candidates' a list of elements mapping into this # class modulo <K>). Let <newcls> be a list of all classes # modulo <L> that map to <cl> modulo <K> (resp. a list of # classes to which the list `<cl>.candidates' maps modulo # <K>, together with `operator's and `exponent's as in # (c^o^e=r)). if allcent then # generic central Info(InfoClasses,5,"central case 1"); newcls:=CentralStepClEANS(fhome,QH, QG, N, cl,false); elif cent(fhome,cl.centralizerpcgs, N, Ldep) then # central in this case Info(InfoClasses,5,"central case 2"); newcls:=CentralStepClEANS(fhome,QH, QG, N, cl,false); else Info(InfoClasses,5,"general case"); newcls:=GeneralStepClEANS(fhome, QH, QG, N, nexpo, cl,false); fi; # Update <cls>, <opr> and <exp>. for i in [1 .. Length(team)] do for q in team[i] do cls[q]:=newcls[i]; opr[q]:=opr[q] * newcls[i].operator; od; od; pos:=Position(blist, false, pos); od; else newcls:=[]; for cli in [1..Length(cls)] do cl:=cls[cli]; if consider=true or consider(fhome,cl.representative,cl.centralizerpcgs,K,L) then if allcent or cent(fhome,cl.centralizerpcgs, N, Ldep) then news:=CentralStepClEANS(fhome,QG, QG, N, cl,false); else news:=GeneralStepClEANS(fhome, QG, QG, N,nexpo, cl,false); fi; Assert(1,# only do the test if no factors were formed FamilyObj(news[1].cengen)<>FamilyObj(eas[step]) or ForAll(news, i->ForAll(i.cengen, j->Comm(i.representative,j) in eas[step]))); Append(newcls,news); fi; Unbind(cls[cli]); od; cls:=newcls; fi; if inflev then c:=Collected(List(cls,i->Size(SubgroupByPcgs(QH, InducedPcgsByPcSequence(fhome,i.cengen))))); if not IsBound( divi ) then divi:=DivisorsInt(Size(G)); fi; c:=Concatenation(c,List(divi,i->[i,0])); # to cope with `First' Info(InfoClasses,2,List(divi,i->First(c,j->j[1]=i)[2])); fi; od; if mode=4 then # test conjugacy of two elements if cls[1].representative<>cls[2].representative then return fail; else return opr[1] / opr[2]; fi; fi; for i in cls do if not IsBound(i.centralizer) then if not IsBound(i.centralizerpcgs) then i.centralizerpcgs:=InducedPcgsByPcSequence(home,i.cengen); i.cengen:=i.centralizerpcgs; fi; i.centralizer:=SubgroupByPcgs(G,i.centralizerpcgs); fi; od; if candidates<>false then # add operators (and exponents) for i in [1 .. Length(cls)] do cls[i].operator:=opr[i]; od; fi; return cls; end); InstallGlobalFunction(CentralizerSizeLimitConsiderFunction,function(sz) return function(fhome,rep,cenp,K,L) return Product(RelativeOrders(cenp))/Size(K)<=sz; end; end); ############################################################################# ## #M ActorOfExternalSet( <cl> ) . . . . . . . . . conj. cl. of solv. groups ## InstallMethod( ActorOfExternalSet, true, [ IsConjugacyClassGroupRep ], 0, function( cl ) local G, rep; G:=ActingDomain( cl ); if not CanEasilyComputePcgs( G ) then TryNextMethod(); fi; rep:=ClassesSolvableGroup( G, 0,rec(candidates:=[ Representative(cl)]) ) [ 1 ]; if not HasStabilizerOfExternalSet( cl ) then SetStabilizerOfExternalSet( cl, ConjugateSubgroup( rep.centralizer, rep.operator ^ -1 ) ); fi; SetCanonicalRepresentativeOfExternalSet( cl, rep.representative ); return rep.operator; end ); ############################################################################# ############################################################################# # everything which follows is only used for rational classes in p groups. # This is not of that much importance any longer as the permutation groups # class algorithm is different, but it is still worth having for rational # classes of p-elements. AH, 14-apr-99 ############################################################################# ## #F RationalClassesSolvableGroup(<G>, <mode> [,<opt>]) . . . . . ## ## This is the old version. It is now only used for rational classes and ## does not incorporate any of the improvements to the ordinary code. ## (However therefore the ordinary code does not need to worry with the ## rational classes case) ## In this function classes are described by records with components ## `representative', `centralizer', `galoisGroup' (for rational classes). If ## <candidates> are given, their classes will have a canonical ## `representative' ## and additional components `operator' and `exponent' (for ## rational classes) such that ## (candidate ^ operator) ^ exponent=representative. (c^o^e=r) ## InstallGlobalFunction(RationalClassesSolvableGroup, function(arg) local G, home, # the group and the home pcgs H,Hp, # acting group mode, # LSB: ratCl | power | test :MSB candidates, # candidates to be replaced by their canonical reps. eas, # elementary abelian series in <G> step, # counter looping over <eas> K, L, # members of <eas> Kp,mK,Lp,mL, # induced and modulo pcgs's LcapH,KcapH, # intersections N, cent, # elementary abelian factor, for affine action cls, newcls, # classes in range/source of homomorphism news, # new classes obtained in step cl, # class looping over <cls> opr, exp, # (candidates[i]^opr[i])^exp[i]=cls[i].representative team, # team of candidates with same image modulo <K> blist,pos,q, # these control grouping of <cls> into <team>s p, # prime dividing $|G|$ ord, # order of a rational class modulo <L> new, power, # auxiliary variables for determination of power tree c, i, # loop variables opt, # options divi; # DivisorsInt(Size(G)) (used for Info) G:=arg[1]; mode :=arg[2]; # explained below whenever it appears if Length(arg)=3 then opt:=ShallowCopy(arg[3]); # convert series to pcgs if IsBound(opt.series) and not IsBound(opt.pcgs) then Error("convert series to pcgs!"); fi; else opt:=rec(); fi; # <candidates> is a list of elements whose classes will be output (but # with canonical representatives), see comment above. Or <candidates> is # just one element, from whose output class the centralizer will be read # off. H:=G; if IsBound(opt.candidates) then candidates:=opt.candidates; if not ForAll(candidates,i->i in G) then G:=ClosureGroup(H,candidates); fi; else candidates:=false; fi; #if IsBound(opt.consider) then # consider:=opt.consider; #else # consider:=true; #fi; # Treat the case of a trivial group. if IsTrivial(H) then if mode=4 then # test conjugacy of two elements return One(G); elif mode mod 2=1 then # rational classes cl:=rec(representative:=One(G), centralizer:=G, galoisGroup:=GroupByPrimeResidues([], 1)); cl.galoisGroup!.type:=3; cl.galoisGroup!.operators:=[]; cl.isCentral:=true; if mode mod 4=3 then # construct the power tree cl.power :=rec(representative:=One(G)); cl.power.operator:=One(G); cl.power.exponent:=1; fi; else cl:=rec(representative:=One(G), centralizer:=H); fi; if candidates<>false then cls:=List(candidates, c -> cl); else cls:=[cl]; fi; return cls; fi; # Calculate a (central) elementary abelian series with all pcgs induced # w.r.t. <homepcgs>. if IsBound(opt.pcgs) then # we prescribed a series home:=opt.pcgs; eas:=EANormalSeriesByPcgs(home); cent:=function(cl, N, L) return ForAll(N, k -> ForAll (InducedPcgs(home,cl.centralizer), c -> Comm(k, c) in L)); end; elif IsPrimePowerInt(Size(G)) then p:=FactorsInt(Size(G))[1]; home:=PcgsPCentralSeriesPGroup(G); eas:=PCentralNormalSeriesByPcgsPGroup(home); cent:=ReturnTrue; elif mode mod 2=1 then # rational classes Error("<G> must be a p-group"); else home:=PcgsElementaryAbelianSeries(G); eas:=EANormalSeriesByPcgs(home); cent:=function(cl, N, L) return ForAll(N, k -> ForAll (InducedPcgs(home,cl.centralizer), #T was: Only those elements form the induced PCGS. The subset seemed to #T enforce taking only the elements up, but the ordering of the series used #T may be different then the ordering in the PCGS. So this will fail. AH #T one might pick the right ones, but this would be almost the same work. #T { [1 .. Length(InducedPcgsWrtHomePcgs(cl.centralizer)) #T - Length(InducedPcgsWrtHomePcgs(L))] }, c -> Comm(k, c) in L)); end; fi; Info(InfoClasses,1,"Series of sizes ",List(eas,Size)); if mode<3 and InfoLevel(InfoClasses)>1 then divi:=DivisorsInt(Size(G)); Info(InfoClasses,2,"centsiz: ",divi); fi; # Initialize the algorithm for the trivial group. step:=1; L :=eas[step]; Lp:=InducedPcgs(home,L); if not IsIdenticalObj( G, H ) then Hp := InducedPcgs(home, H ); LcapH := NormalIntersectionPcgs( home, Hp, Lp ); fi; if mode mod 2=1 # rational classes or candidates<>false then mL:=ModuloPcgsByPcSequenceNC(home, home, Lp); fi; if mode mod 2=1 then # rational classes cl:=rec(representative:=One(G), centralizer:=H, galoisGroup:=GroupByPrimeResidues([], 1)); cl.galoisGroup!.type:=3; cl.galoisGroup!.operators:=[]; if mode mod 4=3 then # construct the power tree cl.power :=rec(representative:=One(G)); cl.power.operator:=One(G); cl.power.exponent:=1; cl.power.kernel :=false; fi; else cl:=rec(representative:=One(G), centralizer:=H); fi; if candidates<>false then cls:=List(candidates, c -> cl); opr:=List(candidates, c -> One(G)); exp:=ListWithIdenticalEntries(Length(candidates), 1); else cls:=[cl]; fi; # Now go back through the factors by all groups in the elementary abelian # series. for step in [step + 1 .. Length(eas)] do Info(InfoClasses,1,"Step ",step,", ",Length(cls)," classes to lift"); # We apply the homomorphism principle to the homomorphism G/L -> G/K. # The actual computations are all done in <G>, factors are # represented by modulo pcgs. K :=L; Kp:=Lp; L :=eas[step]; Lp:=InducedPcgs(home,L); N :=Kp mod Lp; # modulo pcgs representing the kernel #T What is this? Obviously it is needed somewhere, but it is #T certainly not good programming style. AH SetFilterObj(N, IsPcgs); if not IsIdenticalObj(G,H) then KcapH := LcapH; LcapH := NormalIntersectionPcgs(home,Hp,Lp); N!.capH:=KcapH mod LcapH; SetFilterObj( N!.capH, IsPcgs ); else N!.capH:=N; fi; # Rational classes or identification of classes. if mode mod 2=1 or candidates<>false then mK:=mL; mL:=ModuloPcgsByPcSequenceNC(home, home, Lp); fi; # Identification of classes. if candidates<>false then if mode=4 # test conjugacy of two elements and not cls[1].representative / cls[2].representative in K then return fail; fi; blist:=BlistList([1 .. Length(cls)], []); pos:=Position(blist, false); while pos<>fail do # Find a team of candidates with same image under <modK>. cl:=cls[pos]; cl.representative:=PcElementByExponentsNC(mK, ExponentsOfPcElement(mK, cl.representative)); cl.candidates:=[]; team:=[]; q:=pos; while q<>fail do if cls[q].representative / cl.representative in K then c:=candidates[q] ^ opr[q]; if mode mod 2=1 then # rational classes c:=c ^ exp[q]; fi; i:=PositionSorted(cl.candidates, c); if i > Length(cl.candidates) or cl.candidates[i]<>c then Add( cl.candidates,c,i); Add(team, [q], i); else Add(team[i], q); fi; blist[q]:=true; fi; q:=Position(blist, false, q); od; # Now <cl> is a class modulo <K> (possibly with # `<cl>.candidates' a list of elements mapping into this # class modulo <K>). Let <newcls> be a list of all classes # modulo <L> that map to <cl> modulo <K> (resp. a list of # classes to which the list `<cl>.candidates' maps modulo # <K>, together with `operator's and `exponent's as in # (c^o^e=r)). if mode mod 2=1 then # rational classes newcls:=CentralStepRatClPGroup(home, H, N, mK, mL, cl); elif cent(cl, N, L) then newcls:=CentralStepClEANS(home,H, G, N, cl); else newcls:=GeneralStepClEANS(home, H, G, N, cl); fi; # Update <cls>, <opr> and <exp>. for i in [1 .. Length(team)] do for q in team[i] do cls[q]:=newcls[i]; opr[q]:=opr[q] * newcls[i].operator; if mode mod 2=1 then # rational classes ord:=OrderModK(cls[q].representative, mL); if ord<>1 then # For historical reasons, the `exponent's # returns by `CentralStepRatClPGroup' are the # inverses of what we need. exp[q]:=exp[q] / newcls[i].exponent mod ord; fi; fi; od; od; pos:=Position(blist, false, pos); od; elif mode mod 2=1 then # rational classes newcls:=[]; for cl in cls do if IsBound(cl.power) then # construct the power tree cl.representative:=PcElementByExponentsNC(mK, ExponentsOfPcElement(mK, cl.representative)); cl.power.representative:=PcElementByExponentsNC(mK, ExponentsOfPcElement(mK, cl.power.representative)); fi; new:=CentralStepRatClPGroup(home, G, N, mK, mL, cl); ord:=OrderModK(new[1].representative, mL); # if ord <= limit.order # and ( limit.size=0 # or limit.size mod Size(new[1])=0) then if IsBound(cl.power) then # construct the power tree if ord=1 then power:=cl.power; else cl.power.candidates:=[(new[1].representative ^ cl.power.operator) ^ (p*cl.power.exponent)]; power:=CentralStepRatClPGroup(home, G, N, mK, mL, cl.power)[1]; power.operator:=cl.power.operator * power.operator; power.exponent:=cl.power.exponent / power.exponent mod ord; fi; for c in new do c.power:=power; od; fi; Append(newcls, new); # fi od; cls:=newcls; else newcls:=[]; for cl in cls do #if consider=true or consider(fhome,cl.representative,cl.centralizerpcgs,K,L) #then if cent(cl, N, L) then news:=CentralStepClEANS(home,G, G, N, cl); else news:=GeneralStepClEANS(home, G, G, N, cl); fi; Assert(1,ForAll(news, i->ForAll(GeneratorsOfGroup(i.centralizer), j->Comm(i.representative,j) in eas[step]))); Append(newcls,news); #fi; od; cls:=newcls; fi; if InfoLevel(InfoClasses)>1 then c:=Collected(List(cls,i->Size(i.centralizer))); if not IsBound( divi ) then divi:=DivisorsInt(Size(G)); fi; c:=Concatenation(c,List(divi,i->[i,0])); # to cope with `First' Info(InfoClasses,2,List(divi,i->First(c,j->j[1]=i)[2])); fi; od; if mode=4 then # test conjugacy of two elements if cls[1].representative<>cls[2].representative then return fail; else return opr[1] / opr[2]; fi; fi; if candidates<>false then # add operators (and exponents) for i in [1 .. Length(cls)] do cls[i].operator:=opr[i]; if mode mod 2=1 then # rational classes cls[i].exponent:=exp[i]; fi; od; fi; return cls; end); ############################################################################# ## #F OrderModK( <h>, <mK> ) . . . . . . . . . . order modulo normal subgroup ## InstallGlobalFunction( OrderModK, function( h, mK ) local ord, d, o; ord:=1; d:=DepthOfPcElement( mK, h ); while d <= Length( mK ) do o:=RelativeOrders( mK )[ d ]; h:=h ^ o; ord:=ord * o; d:=DepthOfPcElement( mK, h, d + 1 ); od; return ord; end ); ############################################################################# ## #F OldSubspaceVectorSpaceGroup( <N>, <p>, <gens>, <howmuch> ) . complement and projection ## ## This function creates a record containing information about a complement ## in <N> to the span of <gens>. ## BindGlobal("OldSubspaceVectorSpaceGroup", function( N, p, gens ) local zero, one, r, ran, n, nan, cg, pos, Q, i, j, v; one:=One( GF( p ) ); zero:=0 * one; r:=Length( N ); ran:=[ 1 .. r ]; n:=Length( gens ); nan:=[ 1 .. n ]; Q:=[ ]; if n <> 0 and IsMultiplicativeElementWithInverse( gens[ 1 ] ) then Q:=List( gens, gen -> ExponentsOfPcElement( N, gen ) ) * one; else Q:=ShallowCopy( gens ); fi; cg:=rec( matrix :=[ ], needed := [], one :=one, baseComplement:=ShallowCopy( ran ), projection := IdentityMat( r, one ), commutator :=0, centralizer :=0, dimensionN :=r, dimensionC :=n ); if n = 0 or r = 0 then cg.inverse:=NullMapMatrix; return cg; fi; for i in nan do cg.matrix[ i ]:=Concatenation( Q[ i ], zero * nan ); cg.matrix[ i ][ r + i ]:=one; od; TriangulizeMat( cg.matrix ); pos:=1; for v in cg.matrix do while v[ pos ] = zero do pos:=pos + 1; od; RemoveSet( cg.baseComplement, pos ); if pos <= r then cg.commutator :=cg.commutator + 1; else cg.centralizer:=cg.centralizer + 1; fi; od; cg.needed :=[ ]; cg.projection :=IdentityMat( r, one ); # Find a right pseudo inverse for <Q>. Append( Q, cg.projection ); Q:=MutableTransposedMat( Q ); TriangulizeMat( Q ); Q:=TransposedMat( Q ); i:=1; j:=1; while i <= Length( N ) do while j <= Length( gens ) and Q[ j ][ i ] = zero do j:=j + 1; od; if j <= Length( gens ) and Q[ j ][ i ] <> zero then cg.needed[ i ]:=j; else # If <Q> does not have full rank, terminate when the bottom row # is reached. i:=Length( N ); fi; i:=i + 1; od; if IsEmpty( cg.needed ) then cg.inverse:=NullMapMatrix; else cg.inverse:=Q{ Length( gens ) + ran } { [ 1 .. Length( cg.needed ) ] }; cg.inverse:=ImmutableMatrix(p,cg.inverse,true); fi; if IsEmpty( cg.baseComplement ) then cg.projection:=NullMapMatrix; else # Find a base change matrix for the projection onto the complement. for i in [ 1 .. cg.commutator ] do cg.projection[ i ][ i ]:=zero; od; Q:=[ ]; for i in [ 1 .. cg.commutator ] do Q[ i ]:=cg.matrix[ i ]{ ran }; od; for i in [ cg.commutator + 1 .. r ] do Q[ i ]:=ListWithIdenticalEntries( r, zero ); Q[ i ][ cg.baseComplement[ i-r+Length(cg.baseComplement) ] ] :=one; od; cg.projection:=cg.projection ^ Q; cg.projection:=cg.projection{ ran }{ cg.baseComplement }; cg.projection:=ImmutableMatrix(p,cg.projection,true); fi; return Immutable(cg); end ); ############################################################################# ## #F OldKernelHcommaC( <N>, <h>, <C> ) ## ## Given a homomorphism C -> N, c |-> [h,c], this function determines (a) a ## vector space decomposition N = [h,C] + K with projection onto K and (b) ## the ``kernel'' S < C which plays the role of C_G(h) in lemma 3.1 of ## [Mecky, Neub\"user, Bull. Aust. Math. Soc. 40]. ## BindGlobal("OldKernelHcommaC", function( N, h, C ) local i, tmp, v; N!.subspace := OldSubspaceVectorSpaceGroup( N, RelativeOrders( N )[ 1 ], List( C, c -> Comm( h, c ) ) ); tmp := [ ]; for i in [ N!.subspace.commutator + 1 .. N!.subspace.commutator + N!.subspace.centralizer ] do v := N!.subspace.matrix[ i ]; tmp[ i - N!.subspace.commutator ] := PcElementByExponentsNC( C, v{ [ N!.subspace.dimensionN + 1 .. N!.subspace.dimensionN + N!.subspace.dimensionC ] } ); od; return tmp; end ); ############################################################################# ## #F CentralStepConjugatingElement( ... ) . . . . . . . . . . . . . . . local ## ## This function returns an element of <G> conjugating <hk1> to <hk2>^<l>. ## InstallGlobalFunction( CentralStepConjugatingElement, function( N, h, k1, k2, l, cN ) local v, conj; v:=ExponentsOfPcElement( N, h ^ -l * h ^ cN * k1 * k2 ^ -l ); conj:=LinearCombinationPcgs( N!.CmodK{ N!.subspace.needed }, v * N!.subspace.inverse,OneOfPcgs( N ) ); conj:=LeftQuotient( conj, cN ); return conj; end ); ############################################################################# ## #F CentralStepRatClPGroup(<homepcgs>, <G>, <N>, <mK>, <mL>, <cl> ) ## InstallGlobalFunction( CentralStepRatClPGroup, function( home, G, N, mK, mL, cl ) local h, # preimage of `cl.representative' under <hom> candexps, # list of exponent vectors for <h> mod <candidates> classes, # the resulting list of classes ohN, oh, # order of <h> in `Range(<hom>)' resp. `Source(<hom>)' p, # exponent of <N> K, # a complement to $[h,C]$ in <N> Gal, gal, # Galois group for element in `Source(<hom>)' preimage, # preimage of $Gal(hN)$ in $Z_oh^*$ operator, # generator of <preimage> acting by conjugation reps, conj, #\ representatives, conjugating elements, exps, #/ exponents and orbit lengths in orbit algorithm Q, v, r, # subspace to be projected onto, projection vectors k, # orbit representative in <N> gens, oprs, # generators and operators for new Galois group type, # the type of the Galois group as subgroup of Z_2^r^* i, j, l, c, # loop variables C, cyc, xset, opr, orb,kern,img; p :=RelativeOrders( N )[ 1 ]; h :=cl.representative; ohN:=OrderModK( h, mK ); oh :=OrderModK( h, mL ); classes:=[ ]; if oh = 1 then # Special case: <h> is trivial. Gal:=Units( Integers mod 1 ); gal:=GroupByPrimeResidues( [ ], p ); gal!.type:=3; gal!.operators:=[ ]; if IsBound( cl.candidates ) then for c in cl.candidates do l:=LeadingExponentOfPcElement( N, c ); if l = fail then l:=1; c:=rec( representative:=c, galoisGroup:=TrivialSubgroup( Gal ) ); c.galoisGroup!.type:=3; c.galoisGroup!.operators:=[ ]; else c:=rec( representative:=c ^ ( 1 / l mod p ), galoisGroup:=gal ); fi; c.centralizer:=G; c.operator :=OneOfPcgs( N ); c.exponent :=l; Add( classes, c ); od; else c:=rec( representative:=One( G ), centralizer:=G, galoisGroup:=TrivialSubgroup( Gal ) ); c.galoisGroup!.type:=3; c.galoisGroup!.operators:=[ ]; Add( classes, c ); for v in EnumeratorOfNormedRowVectors( GF( p ) ^ Length( N ) ) do c:=rec( representative:=PcElementByExponentsNC( N, v ), centralizer:=G, galoisGroup:=gal ); Add( classes, c ); od; fi; else Gal:=Units( Integers mod oh ); if IsBound( cl.kernel ) then N:=cl.kernel; else N!.CmodK:=InducedPcgs(home, cl.centralizer ) mod DenominatorOfModuloPcgs( N ); kern:=DenominatorOfModuloPcgs( N ); img:=OldKernelHcommaC( N, h, N!.CmodK ) ; #N!.CmodL:=ExtendedPcgs(kern,img); N!.CmodL:=InducedPcgsByPcSequenceAndGenerators(ParentPcgs( kern ), kern, img ); fi; if IsBound( cl.candidates ) then cl.candidates:=List( cl.candidates, c -> LeftQuotient( h, c ) ); candexps:=List( cl.candidates, c -> ExponentsOfPcElement( N, c ) ) * N!.subspace.projection; fi; # If <p> = 2, use a projection operation. if p = 2 then # Construct the preimage of $Gal(hN)$ in $Z_oh^*$. if ohN <= 2 then preimage:=GroupByPrimeResidues( [ -1, 5 ], oh ); preimage!.type:=1; preimage!.operators:=List( GeneratorsOfGroup( preimage ), i -> One( G ) ); else if cl.galoisGroup!.type = 1 then preimage:=[ -1, 5^(ohN/(2*Size(cl.galoisGroup))) ]; elif cl.galoisGroup!.type = 2 then preimage:=[ -( 5^(ohN/(4*Size(cl.galoisGroup)))) ]; else preimage:=[ 5^(ohN/(4*Size(cl.galoisGroup))) ]; fi; preimage:=GroupByPrimeResidues( preimage, oh ); preimage!.type:=cl.galoisGroup!.type; if Length( GeneratorsOfGroup( preimage ) ) = Length( GeneratorsOfGroup( cl.galoisGroup ) ) then preimage!.operators:=cl.galoisGroup!.operators; else preimage!.operators:=Concatenation ( cl.galoisGroup!.operators, [ One( G ) ] ); fi; fi; # Construct the image of the homomorphism <preimage> -> <K>. Q:=[ ]; for i in [ 1 .. Length( GeneratorsOfGroup( preimage ) ) ] do #Assert(2,LeftQuotient(h^Int(GeneratorsOfGroup(preimage)[i]), # h^preimage!.operators[i]) in # Group(NumeratorOfModuloPcgs(N))); Add( Q, ExponentsOfPcElement( N, LeftQuotient( h ^ Int( GeneratorsOfGroup( preimage )[ i ] ), h ^ preimage!.operators[ i ] ) ) ); od; Q:=Q * N!.subspace.projection; K:=InducedPcgsByPcSequenceNC( N, N{ N!.subspace.baseComplement } ); K!.subspace:=OldSubspaceVectorSpaceGroup( K, p, Q ); # Project the factors in <N> onto a complement to <Q>. if IsBound( cl.candidates ) then v:=List( candexps, ShallowCopy ); r:=v * K!.subspace.projection; reps:=[ ]; exps:=[ ]; conj:=[ ]; if not IsEmpty( K!.subspace.baseComplement ) then v{[1..Length(v)]}{K!.subspace.baseComplement}:= v{[1..Length(v)]}{K!.subspace.baseComplement} + r; fi; v:=v * K!.subspace.inverse; for i in [ 1 .. Length( r ) ] do reps[ i ]:=PcElementByExponentsNC ( K, K!.subspace.baseComplement, r[ i ] ); exps[ i ]:=LinearCombinationPcgs( GeneratorsOfGroup(preimage){K!.subspace.needed}, v[ i ],One(preimage)); conj[ i ]:=LinearCombinationPcgs( preimage!.operators { K!.subspace.needed }, v[ i ], One(G)); od; # In the construction case, the complement to <Q> is a set of # representatives. else reps:=EnumeratorByPcgs( K, K!.subspace.baseComplement ); fi; # The kernel of the homomorphism into <K> is the Galois group of # <h>. if IsTrivial( preimage ) then # pre = < 1 > gens:=GeneratorsOfGroup( preimage ); oprs:=preimage!.operators; type:=preimage!.type; else if Q[ 1 ] = Zero( Q[ 1 ] ) then i:=1; else i:=2; fi; if Length( GeneratorsOfGroup( preimage ) ) = 1 then gens:=[ GeneratorsOfGroup( preimage )[ 1 ] ^ i ]; oprs:=[ preimage!.operators [ 1 ] ^ i ]; if preimage!.type = 1 then type:=2 * i - 1; # <-1> elif preimage!.type = 2 then type:=i + 1; else type:=3; fi; else if Q[ 2 ] = Zero( Q[ 2 ] ) then j:=1; else j:=2; fi; if i = 1 then gens:=[ GeneratorsOfGroup( preimage )[ 1 ], GeneratorsOfGroup( preimage )[ 2 ] ^ j ]; oprs:=[ preimage!.operators [ 1 ], preimage!.operators [ 2 ] ^ j ]; type:=1; elif j = 2 and Q[ 1 ] = Q[ 2 ] then gens:=[ GeneratorsOfGroup( preimage )[ 1 ] * GeneratorsOfGroup( preimage )[ 2 ] ]; oprs:=[ preimage!.operators [ 1 ] * preimage!.operators [ 2 ] ]; type:=2; else gens:=[ GeneratorsOfGroup( preimage )[ 2 ] ^ j ]; oprs:=[ preimage!.operators [ 2 ] ^ j ]; type:=3; fi; fi; fi; # If <p> <> 2, use an affine operation of a cyclic group generated by # <preimage>. else K:=EnumeratorByPcgs( N, N!.subspace.baseComplement ); cyc:=GroupByPrimeResidues( [ PowerModInt ( PrimitiveRootMod( oh ), IndexInParent( cl.galoisGroup ), oh ) ], oh ); SetSize( cyc, Phi( oh ) / IndexInParent( cl.galoisGroup ) ); if IsTrivial( cyc ) then preimage:=One( cyc ); else SetIndependentGeneratorsOfAbelianGroup( cyc, GeneratorsOfGroup( cyc ) ); preimage:=Pcgs( cyc )[ 1 ]; fi; if IsTrivial( cl.galoisGroup ) then operator:=One( G ); else operator:=cl.galoisGroup!.operators[ 1 ]; fi; v:=PcElementByExponentsNC( N, N!.subspace.baseComplement, ExponentsOfPcElement( N, LeftQuotient( h ^ Int( preimage ), h ^ operator ) ) * N!.subspace.projection ); opr:=function( k, l ) return #AH, jun3 2001: without the pcgs filtereing we might get # extra kernel elements. I have no idea how this was # originally avoided. This is rather a workaround than a fix # -- the whole code should be rewritten cleanly. PcElementByExponentsNC(N,ExponentsOfPcElement(N, ( v * k ) ^ ( 1 / Int( l ) mod p ) )); end; xset:=ExternalSet( cyc, K, opr ); reps:=[ ]; exps:=[ ]; if IsBound( cl.candidates ) then conj:=[ ]; for c in candexps do orb:=ExternalOrbit( xset, PcElementByExponentsNC( N, N!.subspace.baseComplement, c ) ); Add( reps, CanonicalRepresentativeOfExternalSet( orb ) ); i:=Size( cyc ) / Order( ActorOfExternalSet( orb ) ); Add( exps, preimage ^ i ); Add( conj, operator ^ i ); od; else for orb in ExternalOrbits( xset ) do Add( reps, CanonicalRepresentativeOfExternalSet( orb ) ); Add( exps, preimage ^ Size( orb ) ); od; fi; fi; # If <reps> is a set of representatives of the orbits then <h><reps> # is a set of representatives of the rational classes in <hN>. for l in [ 1 .. Length( reps ) ] do k:=reps[ l ]; # Construct the Galois group and find conjugating elements # corresponding to its generator(s). if p <> 2 then gens:=[ exps[ l ] ]; oprs:=[ operator ^ Int( exps[ l ] ) ]; fi; gal:=SubgroupNC( Gal, gens ); if p = 2 then gal!.type:=type; fi; gal!.operators:=[ ]; for i in [ 1 .. Length( GeneratorsOfGroup( gal ) ) ] do Add( gal!.operators, CentralStepConjugatingElement ( N, h, k, k, Int( GeneratorsOfGroup( gal )[ i ] ), oprs[ i ] ) ); od; C:=SubgroupNC( G, N!.CmodL ); c:=rec( representative:=h * k, centralizer:=C, galoisGroup:=gal ); if IsBound( cl.candidates ) then # cl.candidates[l] ^ c.operator = # c.representative ^ c.exponent (DIFFERS from (c^o^e=r)!) c.exponent:=Int( exps[ l ] ); c.operator:=CentralStepConjugatingElement ( N, h, cl.candidates[ l ], k, c.exponent, conj[ l ] ); if IsBound( cl.kernel ) then c.kernel:=N; fi; fi; Add( classes, c ); od; fi; return classes; end ); ############################################################################# ## #E claspcgs.gi . . . . . . . . . . . . . . . . . . . . . . . . . . ends here