|
#############################################################################
##
## This file is part of GAP, a system for computational discrete algebra.
## This file's authors include Werner Nickel.
##
## 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
##
CHECK := false;
BindGlobal( "NumberOfCommutators", function( ranks )
local class, hclass, coranks, ngens, cl, nofc;
class := Length(ranks);
hclass := Int( (class)/2 );
## corank[cl] is the number of generators g of weight at least cl+1 such
## that wt(g) + cl <= class.
coranks := [];
ngens := Sum( ranks );
for cl in [1..hclass] do
ngens := ngens - ranks[cl] - ranks[ class - cl + 1];
coranks[ cl ] := ngens;
od;
nofc := 0;
for cl in [1..hclass] do
nofc := nofc + ranks[cl] * coranks[cl]
+ (ranks[cl] * (ranks[cl]-1))/2;
od;
return nofc;
end );
#############################################################################
##
#F PQStatistics . . . . . . . . . . . . . . . . . . . p-quotient statistics
##
PQStatistics := rec(
TailCountBNA := 0,
TailCountBAN := 0,
TailCountCBA := 0,
ConsCountANA := 0,
ConsCountBNA := 0,
ConsCountBAN := 0,
ConsCountCBA := 0 );
MakeThreadLocal( "PQStatistics" );
BindGlobal( "IncreaseCounter", function( string )
PQStatistics.(string) := PQStatistics.(string) + 1;
end );
BindGlobal( "PrintCounters", function()
Print( "Number of consistency checks:\n" );
Print( "a^p a : ", PQStatistics.ConsCountANA, "\n" );
Print( "b a^p : ", PQStatistics.ConsCountBAN, "\n" );
Print( "b^p a : ", PQStatistics.ConsCountBNA, "\n" );
Print( "c (b a) : ", PQStatistics.ConsCountCBA, "\n" );
Print( "Number of tail computations:\n" );
Print( "b a^p : ", PQStatistics.TailCountBAN, "\n" );
Print( "b^p a : ", PQStatistics.TailCountBNA, "\n" );
Print( "c (b a) : ", PQStatistics.TailCountCBA, "\n" );
end );
BindGlobal( "ClearPQuotientStatistics", function()
PQStatistics.TailCountBNA := 0;
PQStatistics.TailCountBAN := 0;
PQStatistics.TailCountCBA := 0;
PQStatistics.ConsCountANA := 0;
PQStatistics.ConsCountBNA := 0;
PQStatistics.ConsCountBAN := 0;
PQStatistics.ConsCountCBA := 0;
end );
#############################################################################
##
## The following functions work with a lower trianguar matrix (LTM). A
## LTM may have the following shape where . (*) denotes a (non-) zero entry:
##
## . . . . . . . . . . . . . .
## . . . . . . . . . . . . . .
## . . . . . . . . . . . . . .
## . . . . . . . . . . . . . .
## . . . . . . . . . . . . . .
## * * * * * * . . . . . . . .
## . . . . . . . . . . . . . .
## * * * * * * * * . . . . . .
## * * * * * * * * * . . . . .
## . . . . . . . . . . . . . .
## * * * * * * * * * * * . . .
## . . . . . . . . . . . . . .
## . . . . . . . . . . . . . .
## . . . . . . . . . . . . . .
##
## Rows of zeroes are not stored in a LTM. A LTM has a list of indices at
## which non-zero rows are stored. Other information stored in a LTM is the
## zero and one of its coefficient domain, the number of columns and a zero
## vector of the correct length.
##
#############################################################################
##
#F TrailingEntriesLTM . . . . . . . . . . return list of trailing of a LTM
##
##
BindGlobal( "TrailingEntriesLTM", function( LTM )
return LTM.bound;
end );
#############################################################################
##
#F ReducedVectorLTM . . . . . . . . . . . . . a vector reduced modulo a LTM
##
BindGlobal( "ReducedVectorLTM", function( LTM, v )
local zero, M, i;
if Length(v) <> LTM.dimension then
Error( "vector has incompatible length" );
fi;
zero := LTM.zero;
M := LTM.matrix;
for i in LTM.bound do
if v[i] <> zero then
v := v - v[i] * M[i];
fi;
od;
return v;
end );
#############################################################################
##
#F AddVectorLTM . . . . . . . . . . . . . . . . . . . add a vector to a LTM
##
BindGlobal( "AddVectorLTM", function( LTM, v )
local M, zero, i, trailingEntry;
if Length(v) <> LTM.dimension then
Error( "vector has incompatible length" );
fi;
M := LTM.matrix;
zero := LTM.zero;
v := v + zero;
if v = LTM.nullvector then return; fi;
## Reduce vector modulo the matrix
for i in LTM.bound do
if v[i] <> zero then
v := v - v[i] * M[i];
fi;
od;
## Check if the vector is zero and, if not, normalize it, add it to the
## lower triangular matrix.
if v <> LTM.nullvector then
for trailingEntry in Reversed([1..LTM.dimension]) do
if v[ trailingEntry ] <> zero then
break;
fi;
od;
if v[ trailingEntry ] <> LTM.one then
v := v / v[ trailingEntry ];
fi;
LTM.matrix[ trailingEntry ] := v;
i := PositionSorted( LTM.bound, trailingEntry,
function( a,b ) return a > b; end );
Add( LTM.bound, trailingEntry, i );
fi;
end );
#############################################################################
##
#F RowEchelonFormLTM . . . . row echelon form of a lower triangular matrix
##
BindGlobal( "RowEchelonFormLTM", function( LTM )
local i, j;
for i in LTM.bound do
for j in [i+1..LTM.dimension] do
if IsBound(LTM.matrix[j]) then
LTM.matrix[j] := LTM.matrix[j]
- LTM.matrix[j][i] * LTM.matrix[i];
fi;
od;
od;
end );
#############################################################################
##
#F LowerTriangularMatrix . . . . . . . initialize a lower triangular matrix
##
BindGlobal( "LowerTriangularMatrix", function( dim, field )
local LTM;
LTM := rec( dimension := dim,
zero := Zero( field ),
one := One( field ),
matrix := [],
bound := [],
nullvector := [1..dim] * Zero(field)
);
return LTM;
end );
#############################################################################
##
#F QuotSysDefinitionByIndex . . . . . . . . . . convert index to definition
##
InstallGlobalFunction( QuotSysDefinitionByIndex,
function( qs, index )
local r, j, i;
r := RanksOfDescendingSeries( qs )[1];
if index <= r*(r-1)/2 then
j := 0; while j*(j-1)/2 < index do j := j+1; od;
i := index - (j-1)*(j-2)/2;
return [j,i];
else
j := index - r*(r-1)/2;
if j mod r = 0 then
return [ j / r + r, r ];
else
return [ Int( j / r ) + 1 + r, j mod r ];
fi;
fi;
end );
#############################################################################
##
#F QuotSysIndexByDefinition . . . . . . . . . . convert definition to index
##
InstallGlobalFunction( QuotSysIndexByDefinition,
function( qs, def )
local r;
r := RanksOfDescendingSeries( qs )[1];
if def[1] <= r then
return (def[1]-2) * (def[1]-1) / 2 + def[2];
else
return (r-1)*r/2 + r * (def[1]-2 - (r-1)) + def[2];
fi;
end );
#############################################################################
##
#M GetDefinitionNC . . . . . . . . . . . . get the definition of a generator
##
InstallMethod( GetDefinitionNC, true, [IsPQuotientSystem, IsPosInt], 0,
function( qs , g )
return qs!.definitions[ g ];
end );
#############################################################################
##
#M SetDefinitionNC . . . . . . . . . . . . set the definition of a generator
##
InstallMethod( SetDefinitionNC, true,
[ IsPQuotientSystem, IsPosInt, IsObject ], 0,
function( qs, g, def )
qs!.definitions[g] := def;
if IsPosInt( def ) then
qs!.isDefiningPower[ def ] := true;
elif IsInt( def ) then
# set image
return;
else
qs!.isDefiningConjugate[ QuotSysIndexByDefinition( qs, def ) ] := true;
fi;
end );
#############################################################################
##
#M ClearDefinitionNC . . . . . . . . . . clear the definition of a generator
##
InstallMethod( ClearDefinitionNC, true, [ IsPQuotientSystem, IsPosInt ], 0,
function( qs, g )
local def;
def := GetDefinitionNC( qs, g );
Unbind( qs!.definitions[g] );
if IsPosInt( def ) then
qs!.isDefiningPower[ def ] := false;
elif IsInt( def ) then
return;
else
qs!.isDefiningConjugate[ QuotSysIndexByDefinition( qs, def ) ] := false;
fi;
end );
#############################################################################
##
#M NumberOfNewGenerators . . . . . . number of generators in the next layer
##
BindGlobal( "NumberOfNewGenerators", function( qs )
local nong, d, cl, i;
nong := 0;
## One new generator for each non-defining image, ...
nong := nong + Length(qs!.images);
qs!.numberOfEpimGenerators := Length( qs!.nonDefiningImages );
## ... one new generator for each commutator of the form [c,1] ...
d := qs!.RanksOfDescendingSeries[1];
for cl in Reversed( [1..LengthOfDescendingSeries(qs)] ) do
if cl = 1 then
nong := nong + d * (d-1) / 2;
else
nong := nong + d * qs!.RanksOfDescendingSeries[ cl ];
fi;
## ... and one new generator for each p-th power whose generator is
## itself a p-th power or an image ...
for i in GeneratorsOfLayer( qs, cl ) do
if IsInt( GetDefinitionNC( qs, i ) ) then
nong := nong + 1;
fi;
od;
## -- Note the number of p-cover generators --
if cl = LengthOfDescendingSeries(qs) then
qs!.numberOfNucleusGenerators :=
nong - Length(qs!.images);
fi;
od;
## ... which is not a definition. Since each generator has a
## definition, we have to subtract the number of generators.
nong := nong - qs!.numberOfGenerators;
qs!.numberOfPseudoGenerators :=
nong - qs!.numberOfEpimGenerators - qs!.numberOfNucleusGenerators;
return nong;
end );
#############################################################################
##
#M InitaliseCentralRelations . . . . . . . . . initialise central relations
##
BindGlobal( "InitialiseCentralRelations", function( qs )
## We call the relations obtained by the consistency check and the
## evaluation of relations `central relations'. They are stored as a
## (lower triangular) matrix over GF(p).
qs!.centralRelations :=
LowerTriangularMatrix( NumberOfNewGenerators( qs ), GF(qs!.prime) );
end );
#############################################################################
##
#M ClearCentralRelations . . . . . . . . . . . . . delete central relations
##
BindGlobal( "ClearCentralRelations", function( qs )
Unbind( qs!.centralRelations );
end );
#############################################################################
##
#M CentralRelations . . . . . . . . . . . . . . . . return central relations
##
BindGlobal( "CentralRelations", function( qs )
if not IsBound( qs!.centralRelations ) then
InitialiseCentralRelations( qs );
fi;
return qs!.centralRelations;
end );
#############################################################################
##
#M IncorporateCentralRelations . . . . . . . . . . . relations into pc pres
##
InstallMethod( IncorporateCentralRelations,
"p-quotient system",
true,
[ IsPQuotientSystem ], 0,
function( qs )
local M, coll, one, type, i, pair, g, wt, wth,
h, w;
M := CentralRelations( qs );
coll := qs!.collector;
one := One( qs!.field );
type := coll![SCP_DEFAULT_TYPE];
## At first we run through the images.
for i in qs!.nonDefiningImages do
pair := SplitWordTail( qs, qs!.images[i] );
pair[2] := ExtRepByTailVector( qs,
ReducedVectorLTM( M, pair[2] * one ));
qs!.images[i] :=
AssocWord( type, Concatenation( pair[1], pair[2] ) );
od;
## Run through the inverses.
for g in [1..GeneratorNumberOfQuotient(qs)] do
w := qs!.collector![SCP_INVERSES][g];
pair := SplitWordTail( qs, w );
pair[2] := ExtRepByTailVector( qs,
ReducedVectorLTM( M, pair[2] * one ) );
qs!.collector![SCP_INVERSES][g] :=
AssocWord( type, Concatenation( pair[1], pair[2] ) );
od;
## Run through the power relations.
for g in [1..GeneratorNumberOfQuotient(qs)] do
pair := SplitWordTail( qs, GetPowerNC( qs!.collector, g ) );
pair[2] := ExtRepByTailVector( qs,
ReducedVectorLTM( M, pair[2] * one ) );
SetPowerANC( qs!.collector, g,
AssocWord( type, Concatenation( pair[1], pair[2] ) ) );
od;
## Run through the commutator relations.
for wt in Reversed([2..LengthOfDescendingSeries(qs)+1]) do
wth := wt-1;
while 2*wth >= wt do
for h in GeneratorsOfLayer( qs, wth ) do
for g in GeneratorsOfLayer( qs, wt - wth ) do
if g >= h then break; fi;
pair := SplitWordTail( qs,
GetConjugateNC( qs!.collector, h, g ) );
pair[2] := ExtRepByTailVector( qs,
ReducedVectorLTM( M, pair[2] * one ) );
SetConjugateANC( qs!.collector, h, g,
AssocWord( type,
Concatenation( pair[1], pair[2] ) ) );
od;
od;
wth := wth - 1;
od;
od;
## Update the definitions: The relations and images defining generators
## which have been eliminated are no longer definitions.
for g in TrailingEntriesLTM( CentralRelations( qs ) ) do
ClearDefinitionNC( qs, GeneratorNumberOfQuotient(qs) + g );
od;
## Keep the eliminated generators.
qs!.eliminatedGens := Union( qs!.eliminatedGens,
TrailingEntriesLTM( CentralRelations( qs ) ) );
## Throw away the central relations.
ClearCentralRelations( qs );
end );
#############################################################################
##
#F UpdateWeightInfo . . . . . . . . . . . . . update the weight information
##
BindGlobal( "UpdateWeightInfo", function( qs )
local n, nhwg, ranks, class, last_in_cl, avector, cl, wt,
avc2, g, h;
n := GeneratorNumberOfQuotient(qs);
nhwg := qs!.numberOfHighestWeightGenerators;
ranks := RanksOfDescendingSeries(qs);
class := LengthOfDescendingSeries(qs);
## Update the a-vector.
last_in_cl := n;
avector := [];
for cl in [1..Int( (LengthOfDescendingSeries(qs)+1)/2 )] do
Append( avector, [1..ranks[cl]] * 0 + last_in_cl );
last_in_cl := last_in_cl - ranks[ class - cl + 1 ];
od;
Append( avector, [Length(avector)+1..n+nhwg] );
qs!.collector![SCP_AVECTOR] := avector;
## Update the weight informataion
class := class + 1;
qs!.collector![SCP_CLASS] := class;
wt := qs!.collector![SCP_WEIGHTS];
wt{n+[1..nhwg]} := [1..nhwg] * 0 + class;
avc2 := [1..n]+0;
for g in [1..n] do
if 3*wt[g] > class then
break;
fi;
h := avector[g];
while g < h and 2*wt[h] + wt[g] > class do h := h-1; od;
avc2[g] := h;
od;
qs!.collector![SCP_AVECTOR2] := avc2;
SetFilterObj( qs!.collector, IsUpToDatePolycyclicCollector );
end );
#############################################################################
##
#M DefineNewGenerators . . . . . . . . . . . . generators of the next layer
##
InstallMethod( DefineNewGenerators,
"p-quotient system",
true,
[ IsPQuotientSystem ], 0,
function( qs )
local gens, n, g, cl, h;
gens := GeneratorsOfRws( qs!.collector );
n := GeneratorNumberOfQuotient(qs);
if n + NumberOfNewGenerators( qs ) >
qs!.collector![SCP_NUMBER_RWS_GENERATORS] then
return fail;
fi;
## Add new generators to the p-quotient.
for cl in Reversed([1..LengthOfDescendingSeries(qs)]) do
## Each commutator relation of the form [c,1] which is not a
## definition will get a new generator.
for h in GeneratorsOfLayer( qs, cl ) do
for g in GeneratorsOfLayer( qs, 1 ) do
if g >= h then break; fi;
if not [h,g] in qs!.definitions then
n := n+1;
SetConjugateANC( qs!.collector, h, g,
GetConjugateNC( qs!.collector, h, g )
* gens[n] );
SetDefinitionNC( qs, n, [h,g] );
Info( InfoQuotientSystem, 4, " Defining ",
TraceDefinition( qs, n ), " = ", n );
fi;
od;
od;
## A p-th power of a generator defines a new generator if the
## generator was itself defined by a p-th power.
for g in GeneratorsOfLayer( qs, cl ) do
if not g in qs!.definitions then
if IsInt( GetDefinitionNC( qs , g ) ) then
n := n+1;
SetPowerANC( qs!.collector, g,
GetPowerNC( qs!.collector, g ) * gens[n] );
SetDefinitionNC( qs, n, g );
Info( InfoQuotientSystem, 4, " Defining ",
TraceDefinition( qs, n ), " = ", n );
else
qs!.isDefiningPower[ g ] := false;
fi;
fi;
od;
od;
## Define a new generator for each non-defining image.
for g in qs!.nonDefiningImages do
n := n+1;
qs!.images[g] := qs!.images[g] * gens[n];
SetDefinitionNC( qs, n, -g );
od;
qs!.numberOfHighestWeightGenerators := n - GeneratorNumberOfQuotient(qs);
Info( InfoQuotientSystem, 2, " Defined ",
qs!.numberOfHighestWeightGenerators, " new generators" );
UpdateWeightInfo( qs );
return true;
end );
#############################################################################
##
#M SplitWordTail . . . . . . . . . . . . . . split a word in prefix and tail
##
InstallMethod( SplitWordTail,
"p-quotient system, word",
true,
[ IsPQuotientSystem, IsAssocWord ], 0,
function( qs, w )
local n, c, one, zero, i, h, t;
n := GeneratorNumberOfQuotient(qs);
c := qs!.numberOfHighestWeightGenerators;
one := One( qs!.field );
zero := 0 * one;
w := ExtRepOfObj( w );
## Find the beginning of the tail.
i := 1; while i <= Length(w) and w[i] <= n do i := i+2; od;
## Chop off the head.
h := w{[1..i-1]};
## Convert the tail into an exponent vector.
t := [1..c] * zero;
while i <= Length(w) do
t[ w[i] - n ] := w[i+1] * one;
i := i+2;
od;
return [ h, t ];
end );
#############################################################################
##
#M ExtRepByTailVector . . . . . ext repr from an exponent vector of a tail
##
InstallMethod( ExtRepByTailVector,
"p-quotient system, vector",
true,
[ IsPQuotientSystem, IsVector ], 0,
function( qs, v )
local extrep, i, zero;
extrep := [];
if IsInt( v[1] ) then
for i in [1..Length(v)] do
if v[i] <> 0 then
Add( extrep, GeneratorNumberOfQuotient(qs) + i );
Add( extrep, v[i] mod qs!.prime );
fi;
od;
else
zero := Zero( qs!.field );
for i in [1..Length(v)] do
if v[i] <> zero then
Add( extrep, GeneratorNumberOfQuotient(qs) + i );
Add( extrep, IntFFE(v[i]) );
fi;
od;
fi;
return extrep;
end );
#############################################################################
##
#M TailsInverses . . compute the tails of the inverses in a single collector
##
InstallMethod( TailsInverses,
"p-quotient system",
true,
[ IsPQuotientSystem ], 0,
function( qs )
local n, h, M, type, inverses, v, zeroes, g,
t;
n := GeneratorNumberOfQuotient(qs);
h := n + qs!.numberOfHighestWeightGenerators;
M := CentralRelations( qs );
type := qs!.collector![SCP_DEFAULT_TYPE];
inverses := qs!.collector![SCP_INVERSES];
v := ListWithIdenticalEntries( h, 0 );
zeroes := v{[n+1..h]};
for g in [1..n] do
repeat
v[g] := 1;
until CollectWordOrFail( qs!.collector, v, inverses[g] ) = true;
t := ExtRepByTailVector( qs, ReducedVectorLTM( M, -v{[n+1..h]} ) );
inverses[g] :=
AssocWord( type, Concatenation( ExtRepOfObj( inverses[g] ), t ) );
v{[n+1..h]} := zeroes;
od;
end );
#############################################################################
##
#M ComputeTails . . . . . . . . . . . . compute the tails of a presentation
##
InstallMethod( ComputeTails,
"p-quotient system",
true,
[ IsPQuotientSystem ], 0,
function( qs )
local S, p, type, n, m, l, r, zeroes, c, g,
def, b, a, t, u, y, z, x;
S := qs!.collector;
p := qs!.prime;
type := S![ SCP_DEFAULT_TYPE ];
n := GeneratorNumberOfQuotient(qs);
m := n + qs!.numberOfHighestWeightGenerators;
l := ListWithIdenticalEntries( m, 0 );
r := ListWithIdenticalEntries( m, 0 );
zeroes := ListWithIdenticalEntries( m, 0 );
for c in Reversed( [1..LengthOfDescendingSeries(qs)] ) do
## Compute tails for the power relations.
for g in GeneratorsOfLayer( qs, c ) do
## Does the p-th power of g define a generator?
if not g in qs!.definitions then
## No it does not, therefore we compute the tail for g^p.
## The definition must be commutator.
def := GetDefinitionNC( qs , g );
b := def[1]; a := def[2];
EvaluateOverlapBNA( S, l, r, b, p, a );
IncreaseCounter( "TailCountBNA" );
t := ExtRepByTailVector( qs, l{[n+1..m]} - r{[n+1..m]} );
SetPowerANC( S, g, GetPowerNC( S,g ) * AssocWord( type,t ) );
l{[1..m]} := zeroes; r{[1..m]} := zeroes;
fi;
od;
## The conjugate relations.
## a is the weight of the first generator, b the weight of the
## second generator in a commutator. Their sum is c.
a := c+1-2; b := 2;
while a >= b do
for u in GeneratorsOfLayer( qs, b ) do
## How is u defined?
def := GetDefinitionNC( qs, u );
## Compute the tail for [ z, u ]
if IsInt( def ) then
y := def;
for z in GeneratorsOfLayer( qs, a ) do
if z > u then
IncreaseCounter( "TailCountBAN" );
EvaluateOverlapBAN( S, l, r, z, y, p );
t := ExtRepByTailVector( qs,
l{[n+1..m]} - r{[n+1..m]} );
SetConjugateANC( S, z, u,
GetConjugateNC( S,z,u )
* AssocWord( type,t ) );
l{[1..m]} := zeroes; r{[1..m]} := zeroes;
fi;
od;
else
y := def[1]; x := def[2];
for z in GeneratorsOfLayer( qs, a ) do
if z > u then
IncreaseCounter( "TailCountCBA" );
EvaluateOverlapCBA( S, l, r, z, y, x );
t := ExtRepByTailVector( qs,
l{[n+1..m]} - r{[n+1..m]} );
SetConjugateANC( S, z, u,
GetConjugateNC( S, z, u )
* AssocWord( type,t ) );
l{[1..m]} := zeroes; r{[1..m]} := zeroes;
fi;
od;
fi;
od;
a := a - 1; b := b + 1;
od;
od;
end );
#############################################################################
##
#M EvaluateConsistency . . . . . . . . . . . . . . run the consistency tests
##
InstallMethod( EvaluateConsistency,
"p-quotient system",
true,
[ IsPQuotientSystem ], 0,
function( qs )
local S, M, n, m, p, l, r, wt, a, wta, wtb, bs,
b, c, zeroes, which, pos;
S := qs!.collector;
n := GeneratorNumberOfQuotient(qs);
m := n + qs!.numberOfHighestWeightGenerators;
p := qs!.prime;
l := ListWithIdenticalEntries( m, 0 );
r := ListWithIdenticalEntries( m, 0 );
M := CentralRelations( qs );
zeroes := ListWithIdenticalEntries( m, 0 );
##
## a^p a = a a^p
## The weight condition on the first type of consistency checks is
## 2 wt(a) < class.
##
which := "ConsCountANA";
wt := 1;
while 2*wt < LengthOfDescendingSeries(qs)+1 do
for a in GeneratorsOfLayer( qs, wt ) do
EvaluateOverlapANA( S, l, r, a, p );
if CHECK and l{[1..n]} - r{[1..n]} <> zeroes{[1..n]} then
Error( "result not a tail" );
fi;
AddVectorLTM( M, l{[n+1..m]} - r{[n+1..m]} );
IncreaseCounter( which );
l{[1..m]} := zeroes; r{[1..m]} := zeroes;
od;
wt := wt + 1;
od;
##
## Check all overlaps b a^p for b > a and wt(b)+wt(a) < class.
##
which := "ConsCountBAN";
for wt in [2..LengthOfDescendingSeries(qs)] do
## wt = wt(a) + wt(b)
wta := 1; wtb := wt - 1;
while wtb >= wta do
for a in GeneratorsOfLayer( qs, wta ) do
bs := GeneratorsOfLayer( qs, wtb );
if qs!.isDefiningPower[a] then
c := GeneratorSyllable( GetPowerNC( S, a ), 1 );
## For c < b, [b,c] is a commutator for which we have
## computed its tail by EvaluateBAN( ... b, a, p )
## Therefore, we only want to invoke EvaluateBAN() for
## those b with b <= c.
if bs[ 1 ] > c then
bs := [];
elif c <= Last(bs) then
bs := [bs[1]..c];
fi;
fi;
for b in bs do
if a < b then
EvaluateOverlapBAN( S, l, r, b, a, p );
IncreaseCounter( which );
if CHECK and
l{[1..n]} - r{[1..n]} <> zeroes{[1..n]} then
Error( "result not a tail" );
fi;
AddVectorLTM( M, l{[n+1..m]} - r{[n+1..m]} );
l{[1..m]} := zeroes; r{[1..m]} := zeroes;
fi;
od;
od;
wta := wta+1; wtb := wtb-1;
od;
od;
##
## Check all overlaps b^p a for b > a, wt(a) = 1 and
## wt(a) + wt(b) < class. Hence wt(b) < class - 1.
##
which := "ConsCountBNA";
wtb := 1;
while wtb < LengthOfDescendingSeries(qs)+1 - 1 do
for b in GeneratorsOfLayer( qs, wtb ) do
for a in GeneratorsOfLayer( qs, 1 ) do
if a >= b then break; fi;
pos := Position( qs!.definitions, [b,a] );
if pos = fail or pos > qs!.numberOfGenerators then
EvaluateOverlapBNA( S, l, r, b, p, a );
IncreaseCounter( which );
if CHECK and l{[1..n]} - r{[1..n]} <> zeroes{[1..n]} then
Error( "result not a tail" );
fi;
AddVectorLTM( M, l{[n+1..m]} - r{[n+1..m]} );
l{[1..m]} := zeroes; r{[1..m]} := zeroes;
fi;
od;
od;
wtb := wtb + 1;
od;
##
## Check overlaps c b a with c > b > a and wt(a) = 1 and
## wt(a) + wt(b) + wt(c) <= class.
##
## Since wt(a) = 1 and wt(b) <= wt(c) we can reformulate the above
## condition to
##
## wt(b) <= wt(c) = wt - wt(b) - 1
## where wt runs from 1 to class.
## So we get 2 * wt(b) <= wt - 1.
which := "ConsCountCBA";
wt := 2;
while wt <= LengthOfDescendingSeries(qs)+1 do
wtb := 1;
while 2 * wtb <= wt - 1 do
for c in GeneratorsOfLayer( qs, wt - wtb - 1 ) do
for b in GeneratorsOfLayer( qs, wtb ) do
if b >= c then break; fi;
for a in GeneratorsOfLayer( qs, 1 ) do
if a >= b then break; fi;
pos := Position( qs!.definitions, [b,a] );
if pos = fail or pos > qs!.numberOfGenerators
or pos >= c then
EvaluateOverlapCBA( S, l, r, c, b, a );
if CHECK and l{[1..n]} - r{[1..n]} <> zeroes{[1..n]} then
Error( "result not a tail" );
fi;
AddVectorLTM( M, l{[n+1..m]} - r{[n+1..m]} );
IncreaseCounter( which );
l{[1..m]} := zeroes; r{[1..m]} := zeroes;
fi;
od;
od;
od;
wtb := wtb + 1;
od;
wt := wt + 1;
od;
end );
#############################################################################
##
#M RenumberHighestWeightGenerators . . . . . . . . . . . renumber generators
##
InstallMethod( RenumberHighestWeightGenerators,
"p-quotient system",
true,
[ IsPQuotientSystem ], 0,
function( qs )
local n, c, gens, surgens, newgens, g,
i, w, h, wt, wth, renumber;
## Those generators which have been eliminated from the quotient system
## don't occur anymore. Now we replace the surviving generators by a
## consecutive list of generators.
n := GeneratorNumberOfQuotient(qs);
c := qs!.numberOfHighestWeightGenerators;
gens := GeneratorsOfRws( qs!.collector );
surgens := n + DifferenceLists( [1..c], qs!.eliminatedGens );
newgens := gens{[1..n+Length(surgens)]};
renumber := [];
renumber{Concatenation([1..n], surgens)} := [1..n+Length(surgens)];
## Update the definitions of the surviving generators.
for g in [1..Length(surgens)] do
SetDefinitionNC( qs, n+g, GetDefinitionNC( qs, surgens[g] ) );
od;
qs!.definitions := qs!.definitions{[1..Length(newgens)]};
## Run through all non-defining images.
for i in qs!.nonDefiningImages do
qs!.images[i] := RenumberedWord( qs!.images[i], renumber );
od;
## Run through all inverses
for g in [1..GeneratorNumberOfQuotient(qs)] do
w := qs!.collector![SCP_INVERSES][g];
w := RenumberedWord( w, renumber );
qs!.collector![SCP_INVERSES][g] := w;
od;
## Run through all power relations
for g in [1..GeneratorNumberOfQuotient(qs)] do
w := GetPowerNC( qs!.collector, g );
w := RenumberedWord( w, renumber );
SetPowerANC( qs!.collector, g, w );
od;
## Run through all conjugate relations
for wt in Reversed([2..LengthOfDescendingSeries(qs)+1]) do
wth := wt-1;
while 2*wth >= wt do
for h in GeneratorsOfLayer( qs, wth ) do
for g in GeneratorsOfLayer( qs, wt - wth ) do
if g >= h then break; fi;
w := GetConjugateNC( qs!.collector, h, g );
w := RenumberedWord( w, renumber );
SetConjugateANC( qs!.collector, h, g, w );
od;
od;
wth := wth - 1;
od;
od;
Add( qs!.RanksOfDescendingSeries,
qs!.numberOfHighestWeightGenerators - Length( qs!.eliminatedGens ) );
qs!.numberOfGenerators := qs!.numberOfGenerators +
qs!.numberOfHighestWeightGenerators -
Length( qs!.eliminatedGens );
qs!.numberOfHighestWeightGenerators := 0;
qs!.eliminatedGens := [];
end );
#############################################################################
##
#M EvaluateRelators . . . . . . . evaluate relations of a p-quotient system
##
BindGlobal( "EvaluateRelation", function( sc, w, gens )
local v, i, g, e, j;
v := ListWithIdenticalEntries( Length(gens), 0 );
for i in [1..NumberSyllables(w)] do
g := GeneratorSyllable( w, i );
e := ExponentSyllable( w, i );
if e > 0 then
for j in [1..e] do
if CollectWordOrFail( sc, v, gens[g] ) = fail then
return fail;
fi;
od;
else
for j in [1..-e] do
if CollectWordOrFail( sc, v, sc![SCP_INVERSES][g] ) = fail then
return fail;
fi;
od;
fi;
od;
return v;
end );
InstallMethod( EvaluateRelators,
"p-quotient system",
true,
[ IsPQuotientSystem ], 0,
function( qs )
local G, S, n, c, F, Fgens, LTM, v, zeroes, r, rr;
G := qs!.preimage;
S := qs!.collector;
n := GeneratorNumberOfQuotient(qs);
c := qs!.numberOfHighestWeightGenerators;
F := FreeGroupOfFpGroup( G );
Fgens := GeneratorsOfGroup( F );
LTM := CentralRelations( qs );
v := ListWithIdenticalEntries( n+c, 0 );
zeroes := v{[n+1..n+c]};
for r in RelatorsOfFpGroup( G ) do
rr := MappedWord( r, Fgens, qs!.images );
# v := EvaluateRelation( S, rr, gens );
# while v = fail do
# v := EvaluateRelation( S, rr, gens );
# od;
while CollectWordOrFail( S, v, rr ) = fail do
Info( InfoQuotientSystem, 3,
"Warning: Collector failed in evaluating relator",
" and was restarted" );
od;
AddVectorLTM( LTM, v{[n+1..n+c]} );
v{[n+1..n+c]} := zeroes;
od;
end );
#############################################################################
##
#M LiftEpimorphism . . . . . . . . lift the epimorphism onto the p-quotient
##
InstallMethod( LiftEpimorphism,
"p-quotient system",
true,
[ IsPQuotientSystem ], 0,
function( qs )
# Compute tails for inverses of generators.
TailsInverses( qs );
# Evaluate relations.
EvaluateRelators( qs );
end );
#############################################################################
##
#M QuotientSystem . . . . . . . . . . . . . initialize a p-quotient system
##
InstallMethod( QuotientSystem,
"pquotient",
true,
[ IsGroup, IsPosInt, IsPosInt, IsString ],
0,
function( G, p, n, collector )
local qs, fam, type;
if not IsPrime(p) then
return Error( "prime expected" );
fi;
qs := rec();
## The finitely presented group.
qs.preimage := G;
## The p-quotient.
qs.prime := p;
qs.field := GF(p);
## The p-rank of each factor in the p-central series. The p-class is
## the length of this list.
qs.LengthOfDescendingSeries := 0;
qs.RanksOfDescendingSeries := [];
## images of the generators in G.
qs.images := [];
## definitions of the generators in P.
qs.isDefiningPower := [];
qs.isDefiningConjugate := [];
qs.definitions := [];
qs.nonDefiningImages := [];
## Create the collector.
if collector = "combinatorial" then
qs.collector := CombinatorialCollector(
FreeGroup(IsSyllableWordsFamily, n, "a" ), p );
else
qs.collector := SingleCollector(
FreeGroup(IsSyllableWordsFamily, n, "a" ), p );
fi;
qs.collector![SCP_INVERSES] :=
List( qs!.collector![SCP_RWS_GENERATORS], g->g^(p-1) );
qs.collector![SCP_CLASS] := 0;
qs.collector![SCP_WEIGHTS] := [];
## Number of used generators in the collector not counting the highest
## weight generators.
qs.numberOfGenerators := 0;
## Number of highest weight generators.
qs.numberOfHighestWeightGenerators := 0;
## Eliminated generators: contains those generators which have been
## eliminated by applying central relations to the quotient system.
## These are used when generators are renumbered.
qs.eliminatedGens := [];
## Now turn this into a new object.
fam := NewFamily( "QuotientSystem", IsQuotientSystem );
type := NewType( fam, IsPQuotientSystem and IsMutable and IsComponentObjectRep );
Objectify( type, qs );
return qs;
end );
#############################################################################
##
#M GeneratorNumberOfQuotient . . . . . . . . generator number of p-quotient
##
InstallMethod( GeneratorNumberOfQuotient,
"p-quotient system",
true,
[IsPQuotientSystem], 0,
function( qs )
return qs!.numberOfGenerators;
end );
#############################################################################
##
#M GeneratorsOfLayer . . . . generators of a layer in the descending series
##
InstallMethod( GeneratorsOfLayer,
"p-quotient system",
true,
[IsPQuotientSystem, IsPosInt], 0,
function( qs, cl )
local ranks, s;
ranks := RanksOfDescendingSeries( qs );
s := Sum( ranks{[1..cl-1]} );
return s + [1..ranks[cl]];
end );
#############################################################################
##
#M LengthOfDescendingSeries . . . . . . . . length of the descending series
##
InstallMethod( LengthOfDescendingSeries,
"p-quotient system",
true,
[IsPQuotientSystem], 0,
function( qs )
return Length(RanksOfDescendingSeries(qs));
end );
#############################################################################
##
#M RanksOfDescendingSeries . . ranks of the factors in the descending series
##
InstallMethod( RanksOfDescendingSeries,
"p-quotient system",
true,
[IsPQuotientSystem], 0,
function( qs )
return qs!.RanksOfDescendingSeries;
end );
#############################################################################
##
#M CheckConsistencyOfDefinitions . . . . . check consistency of definitions
##
InstallMethod( CheckConsistencyOfDefinitions,
"p-quotient system",
true,
[IsPQuotientSystem], 0,
function( qs )
local g, h, def;
## Is each generator marked as defining power contained in .definitions?
for g in [1..GeneratorNumberOfQuotient(qs)
-RanksOfDescendingSeries(qs)[LengthOfDescendingSeries(qs)]] do
if qs!.isDefiningPower[g] and not g in qs!.definitions then
Print( "#W Generator number ", g );
Print( " is marked as a defining power.\n" );
Print( "#W There is no corresponding definition.\n" );
return fail;
fi;
od;
## Is each pair of generators marked as defining conjugate contained in
## .definitions?
for h in [1..GeneratorNumberOfQuotient(qs)
-RanksOfDescendingSeries(qs)[LengthOfDescendingSeries(qs)]] do
for g in [1..Minimum( RanksOfDescendingSeries(qs)[1], h-1 )] do
if qs!.isDefiningConjugate[
QuotSysIndexByDefinition( qs, [h,g] ) ] and
not [h,g] in qs!.definitions then
Print( "#W Generator pair ", [h,g] );
Print( " is marked as a defining conjugate.\n" );
Print( "#W There is no corresponding definition.\n" );
return fail;
fi;
od;
od;
## Is each definition marked in .definingPower or .definingConjugate?
for def in qs!.definitions do
if IsPosInt( def ) then
if not qs!.isDefiningPower[ def ] then
Print( "#W The power of generator number ", def );
Print( " defines a generator.\n" );
Print( "#W The generator is not marked as defining.\n" );
return fail;
fi;
elif IsInt( def ) then
## check defining images
else
if not qs!.isDefiningConjugate[
QuotSysIndexByDefinition( qs, def ) ] then
Print( "#W The conjugate pair ", def );
Print( " defines a generator.\n" );
Print( "#W The pair is not marked as defining.\n" );
return fail;
fi;
fi;
od;
end );
#############################################################################
##
#F AbelianPQuotient . . . . . . . . . . . initialize an abelian p-quotient
##
InstallGlobalFunction( AbelianPQuotient,
function( qs )
local G, n, gens, LTM, trailers, d,
generators, i, r, l;
# Setup some variables.
G := qs!.preimage;
n := Length( GeneratorsOfGroup( G ) );
gens := GeneratorsOfRws( qs!.collector );
LTM := LowerTriangularMatrix( n, qs!.field );
for r in RelatorsOfFpGroup( G ) do
AddVectorLTM( LTM, ExponentSums( r ) );
od;
RowEchelonFormLTM( LTM );
trailers := TrailingEntriesLTM( LTM );
## Each row in LTM corresponds to a generator that can be expressed in
## terms of earlier generators. The column of the trailing entry is the
## generator number.
qs!.nonDefiningImages := trailers;
## The p-rank.
d := n - Length( trailers );
## The generator numbers of the p-quotient
generators := DifferenceLists( [1..n], trailers );
## Their images are the first d generators.
qs!.images{ generators } := gens{[1..d]};
## Fix their definitions.
qs!.definitions{[1..d]} := -generators;
## Now we have to compute the images of the non defining generators.
l := 0;
for i in Reversed([1..Length(LTM.matrix)]) do
if IsBound( LTM.matrix[i] ) then
l := l+1;
r := ShallowCopy(-LTM.matrix[i]);
r[ trailers[l] ] := 0;
qs!.images[ trailers[l] ] :=
ObjByExponents( qs!.collector, List( r{generators}, Int ) );
fi;
od;
qs!.numberOfGenerators := Length( qs!.definitions );
qs!.RanksOfDescendingSeries[1] := Length( qs!.definitions );
## Update the weight information
qs!.collector![SCP_CLASS] := 1;
qs!.collector![SCP_WEIGHTS]{[1..qs!.numberOfGenerators]} :=
[1..qs!.numberOfGenerators] * 0 + 1;
end );
#############################################################################
##
#F PQuotient . . . . . . . . . . . p-quotient of a finitely presented group
##
InstallGlobalFunction( PQuotient,
function( arg )
local G, p, cl, ngens, collector, qs, t,noninteractive;
## First we parse the arguments to this function
if Length( arg ) < 2 or Length( arg ) >= 6 then
Error( "PQuotient( <G>, <p>, <cl>",
" [, <ngens>] [, \"single\" | \"combinatorial\" ] )" );
fi;
G := arg[1];
if not IsFpGroup( G ) then
Error( "The first argument must be a finitely presented group" );
fi;
p := arg[2];
if not (IsInt(p) and p > 0 and IsPrime( p )) then
Error( "The second argument must be a positive prime" );
fi;
## defaults for the optional parameters
cl := 666;
ngens := 256;
collector := "combinatorial";
if Length( arg ) >= 3 then
cl := arg[3];
if not (IsInt(3) and cl > 0) then
Error( "The third argument (if present) is the p-class and",
" must be a positive integer" );
fi;
fi;
if Length( arg ) >= 4 then
if IsInt(arg[4]) then
ngens := arg[4];
if not ngens > 0 then
Error( "If the fourth argument is present and an integer,",
" then it is the initial number of generators in the",
" collector and must be a positive integer" );
fi;
elif IsString( arg[4] ) then
collector := arg[4];
if not (collector = "single" or collector = "combinatorial") then
Error( "If the fourth argument is present and a string",
" then it is the collector that is used during the",
" p-quotient algorithm and must be either ",
" \"single\" or \"combinatorial\"" );
fi;
fi;
fi;
if Length( arg ) >= 5 then
collector := arg[5];
if not (IsString( collector ) and
(collector = "single" or collector = "combinatorial")) then
Error( "If the fifth argument is present and a string",
" then it is the collector that is used during the",
" p-quotient algorithm and must be either ",
" \"single\" or \"combinatorial\"" );
fi;
fi;
# do we call the routine within code (and want a `fail' returned if not
# enough generators can be created, instead of an error message.
noninteractive:=ValueOption("noninteractive")=true;
ClearPQuotientStatistics();
qs := QuotientSystem( G, p, ngens, collector );
##
## First do the abelian p-quotient. This might later on become a special
## case of the general step.
##
Info( InfoQuotientSystem, 1, "Class ",
LengthOfDescendingSeries(qs)+1, " quotient" );
t := Runtime();
AbelianPQuotient( qs );
Info( InfoQuotientSystem, 1, " rank of this layer: ",
RanksOfDescendingSeries(qs)[LengthOfDescendingSeries(qs)],
" (runtime: ", Runtime()-t, " msec)" );
while LengthOfDescendingSeries(qs) < cl do
t := Runtime();
Info( InfoQuotientSystem, 1,
"Class ", LengthOfDescendingSeries(qs)+1, " quotient" );
Info( InfoQuotientSystem, 2, " Define new generators." );
if DefineNewGenerators( qs ) = fail then
if noninteractive then
return fail;
else
Error( "Collector not large enough ",
"to define generators for the next class.\n",
"To return the current quotient (of class ",
LengthOfDescendingSeries(qs), ") type `return;' ",
"and `quit;' otherwise.\n" );
return qs;
fi;
fi;
Info( InfoQuotientSystem, 2, " Compute tails." );
ComputeTails( qs );
Info( InfoQuotientSystem, 2, " Enforce consistency." );
EvaluateConsistency( qs );
Info( InfoQuotientSystem, 2, " Lift epimorphism." );
LiftEpimorphism( qs );
Info( InfoQuotientSystem, 2, " Incorporate relations." );
IncorporateCentralRelations( qs );
if qs!.numberOfHighestWeightGenerators
> Length(qs!.eliminatedGens) then
RenumberHighestWeightGenerators( qs );
else
qs!.numberOfHighestWeightGenerators := 0;
qs!.eliminatedGens := [];
return qs;
fi;
Info( InfoQuotientSystem, 1, " rank of this layer: ",
RanksOfDescendingSeries(qs)[LengthOfDescendingSeries(qs)],
" (runtime: ", Runtime()-t, " msec)" );
od;
return qs;
end );
#############################################################################
##
#M EpimorphismPGroup . . . . . . . . . . . . . . epimorphism onto a p-group
##
InstallMethod( EpimorphismPGroup,
"for finitely presented groups",
true,
[IsSubgroupFpGroup and IsWholeFamily, IsPosInt],
0,
function( G, p )
return EpimorphismPGroup( G, p, 1000 );
end );
InstallMethod( EpimorphismPGroup,
"for finitely presented groups, class bound", true,
[IsSubgroupFpGroup and IsWholeFamily, IsPosInt, IsPosInt], 0,
function( G, p, c )
local ngens,pq;
ngens:=32;
repeat
ngens:=ngens*8;
pq:=PQuotient(G,p,c,ngens:noninteractive);
until pq<>fail;
return EpimorphismQuotientSystem(pq);
end );
InstallMethod( EpimorphismPGroup,
"for subgroups of finitely presented groups",
true,
[IsSubgroupFpGroup, IsPosInt ],
0,
function( U, p )
return EpimorphismPGroup( U, p, 1000 );
end );
InstallMethod( EpimorphismPGroup,
"for subgroups of finitely presented groups, class bound",true,
[IsSubgroupFpGroup, IsPosInt, IsPosInt ],0,
function( U, p, c )
local phi, ngens, qs, psi, images, eps;
phi:=IsomorphismFpGroup( U );
ngens:=32;
repeat
ngens:=ngens*8;
qs:=PQuotient(Image(phi),p,c,ngens:noninteractive );
until qs<>fail;
psi:=EpimorphismQuotientSystem( qs );
# images := GeneratorsOfGroup( U );
# images := List( images , g->Image( phi, g ) );
images:=MappingGeneratorsImages(phi)[2];
images:=List( images , g->Image( psi, g ) );
eps:=CompositionMapping2(psi,phi);
SetIsSurjective( eps, true );
return eps;
end);
InstallMethod( EpimorphismPGroup,"finite groups",true,
[IsFinite and IsGroup, IsPosInt ],0,
function( U, p )
return EpimorphismPGroup( U, p, LogInt(Size(U),p) );
end );
InstallMethod( EpimorphismPGroup,"finite group, class bound",true,
[IsFinite and IsGroup, IsPosInt, IsPosInt ],0,
function( U, p, c )
local ser;
if IsSubgroupFpGroup(U) or IsFreeGroup(U) then
TryNextMethod(); # fp groups *use* the PQ for the central series.
fi;
ser:=PCentralSeries(U,p);
c:=Minimum(c+1,Length(ser));
return NaturalHomomorphismByNormalSubgroupNC(U,ser[c]);
end);
#############################################################################
##
#M GroupByQuotientSystem . . . . . . . . . . . group from a quotient system
##
InstallMethod( GroupByQuotientSystem,
"p-group from a p-quotient system",
true,
[ IsPQuotientSystem ], 0,
function( qs )
local n, coll, i;
n := qs!.numberOfGenerators + qs!.numberOfHighestWeightGenerators;
coll := ShallowCopy(qs!.collector);
coll![ SCP_NUMBER_RWS_GENERATORS ] := n;
for i in
[ SCP_RWS_GENERATORS,
SCP_POWERS,
SCP_INVERSES,
SCP_CONJUGATES,
SCP_AVECTOR,
SCP_AVECTOR2,
SCP_RELATIVE_ORDERS,
SCP_WEIGHTS ] do
if not IsBound( coll![ i ] ) then
continue;
fi;
if Length( coll![ i ] ) > n then
# truncate the collector to the correct number of generators
coll![ i ] := coll![ i ]{[1..n]};
else
# make a shallow copy, so that modifications made to coll do not
# modify qs!.collector (e.g. in HPC-GAP, GroupByRwsNC makes all
# members of the given collector readonly; if it shares e.g. its
# SCP_WEIGHTS with qs!.collector, then this would cause errors
# later on when extending the quotient system.
coll![ i ] := ShallowCopy( coll![ i ] );
fi;
od;
return GroupByRwsNC( coll );
end );
#############################################################################
##
#F PCover . . . . . . . . . . . . . . . . . . p-cover of a quotient system
##
BindGlobal( "PCover", function( qs )
local G, range, defByEpim, g;
if DefineNewGenerators( qs ) = fail then
Error( "Collector not large enough ",
"to define generators for the next class.\n" );
return fail;
fi;
ComputeTails( qs );
EvaluateConsistency( qs );
IncorporateCentralRelations( qs );
G := GroupByQuotientSystem( qs );
range := [1..qs!.numberOfHighestWeightGenerators];
## Construct the subgroup generated by generators which have not been
## eliminated.
range := DifferenceLists( range, qs!.eliminatedGens );
range := range + GeneratorNumberOfQuotient(qs);
## We do not want to include highestweight generators that are defined
## as images of the epimorphism.
defByEpim := [];
for g in range do
if IsNegRat( GetDefinitionNC(qs, g) ) then Add( defByEpim, g ); fi;
od;
range := DifferenceLists( range, defByEpim );
##
## Add all the other generators
##
range := Concatenation( [1..GeneratorNumberOfQuotient(qs)], range );
return Group( GeneratorsOfGroup(G){range}, One(G) );
end );
#############################################################################
##
#F PMultiplicator . . . . . . . . . . . . . . p-multiplicator of a p-cover
##
BindGlobal( "PMultiplicator", function( qs, G )
local n, gens;
n := GeneratorNumberOfQuotient( qs );
gens := GeneratorsOfGroup( G );
return Subgroup( G, gens{[n+1..Length(gens)]} );
end );
#############################################################################
##
#F Nucleus . . . . . . . . . . . . . . . . . . . . the nucleus of a p-cover
##
InstallMethod(Nucleus, "for a p-quotient system and a group",
[IsPQuotientSystem,IsGroup],
function( qs, G )
local n, gens, m;
n := GeneratorNumberOfQuotient( qs );
gens := GeneratorsOfGroup( G );
## The first highest weight generators generate the nucleus. Find those
## which have not been eliminated.
m := Length( DifferenceLists( [1..qs!.numberOfNucleusGenerators],
qs!.eliminatedGens ) );
return Subgroup( G, gens{[n+1..n+m]} );
end);
#############################################################################
##
#F AllowableSubgroup . . . . return the subgroup generated by the relations
##
BindGlobal( "AllowableSubgroup", function( qs, G )
local LTM, gens, fam, n, i, extrep, j;
LiftEpimorphism( qs );
## convert central relations to words in G and return the result.
LTM := CentralRelations( qs );
gens := [];
fam := ElementsFamily( FamilyObj(G) );
n := GeneratorNumberOfQuotient( qs );
for i in LTM.bound do
## skip central relations that express generators defined by the
## epimorphism in terms of earlier generators. Those generators are
## the last generators among the highest weight generators.
if i <= qs!.numberOfHighestWeightGenerators
- qs!.numberOfEpimGenerators then
## convert the vector into an external representation
extrep := [];
for j in [1..LTM.dimension] do
if LTM.matrix[i][j] <> LTM.zero then
Add( extrep, n+j );
Add( extrep, Int( LTM.matrix[i][j] ) );
fi;
od;
Add( gens, ObjByExtRep( fam, extrep ) );
fi;
od;
return Subgroup( G, gens );
end );
#############################################################################
##
#M ViewObj . . . . . . . . . . . . . . . . . . . . view a p-quotient system
##
InstallMethod( ViewObj,
"p-quotient system",
true,
[ IsPQuotientSystem ], 0,
function( qs )
Print( "<",
qs!.prime,
"-quotient system of ",
qs!.prime,
"-class ",
LengthOfDescendingSeries( qs ),
" with ",
GeneratorNumberOfQuotient( qs ),
" generators" );
if qs!.numberOfHighestWeightGenerators > 0 then
Print( " and ",
qs!.numberOfHighestWeightGenerators,
" highest weight generators" );
fi;
Print( ">" );
end );
#############################################################################
##
#M EpimorphismQuotientSystem
##
InstallMethod(EpimorphismQuotientSystem,
"for p-quotient systems",
true,
[IsPQuotientSystem],
0, function(qs)
local H, l, hom;
H := GroupByQuotientSystem( qs );
SetIsPGroup( H, true );
SetPrimePGroup( H, qs!.prime );
# now we write the images of the generators of G in H from qs:
l := List(qs!.images,x->ObjByExtRep(FamilyObj(One(H)),ExtRepOfObj(x)));
hom:=GroupHomomorphismByImagesNC(qs!.preimage,H,
GeneratorsOfGroup(qs!.preimage),l);
# The homomorphism is surjective.
SetIsSurjective(hom,true);
return hom;
end );
#############################################################################
##
#M EpimorphismNilpotentQuotient
##
## This function does not belong here
##
InstallGlobalFunction("EpimorphismNilpotentQuotient",function(arg)
local g,n;
g:=arg[1];
if Length(arg)>1 then
n:=arg[2];
else
n:=fail;
fi;
return EpimorphismNilpotentQuotientOp(g,n);
end);
InstallMethod( EpimorphismNilpotentQuotientOp,"subgroup fp group",
true, [ IsSubgroupFpGroup,IsObject],0,
function(G,n)
local iso;
iso:=IsomorphismFpGroup(G);
return iso*EpimorphismNilpotentQuotient(Image(iso),n);
end);
InstallMethod( EpimorphismNilpotentQuotientOp,"full fp group",
true, [ IsSubgroupFpGroup and IsWholeFamily,IsObject],0,
function(g,n)
local a,h,i,q,d,img,geni,gen,hom,lcs,c,sqa,cnqs,genum;
a:=Set( Flat( List( AbelianInvariants(g), Factors ) ) );
if 0 in a then
Error("infinite quotients currently impossible");
fi;
if Length(a) = 0 then
hom:= NaturalHomomorphismByNormalSubgroup(g,g);
if IsSubgroupFpGroup( Range( hom ) ) then
# Do not return an identity mapping,
# in order to avoid infinite recursions.
hom:= GroupHomomorphismByImages( g, SymmetricGroup( 1 ), [], [] );
fi;
return hom;
fi;
h:=[];
for i in a do
if n <> fail then
# caveat: The PQ gives p-class. We might have to go to a higher
# p-class to get the corresponding nilpotency class
c:=n;
cnqs:=1;
#T the way we run the pq iteratively is a bit stupid. Once the
#T interface is documented it would be better to run it iteratively
repeat
sqa:=cnqs;
c:=c+1;
genum:=Minimum(8192,(c*Length(GeneratorsOfGroup(g)))^2);
q := PQuotient(g,i,c,genum);
# try to increase the number of generators in time
if q!.numberOfGenerators*8>genum then
genum:=genum*16;
fi;
q := EpimorphismQuotientSystem( q );
lcs:=LowerCentralSeriesOfGroup(Image(q));
# size of the class-n quotient bit so far
cnqs:=Index(lcs[1],lcs[Minimum(Length(lcs),n+1)]);
until cnqs=sqa; # the class-n-quotient did not grow
if Length(lcs)>n+1 then
# take only the top bit
q:=q*NaturalHomomorphismByNormalSubgroupNC(lcs[1],lcs[n+1]);
fi;
else
q := PQuotient(g,i,1000,
Maximum(256,(40*Length(GeneratorsOfGroup(g)))^2));
q := EpimorphismQuotientSystem( q );
fi;
Add(h,q);
od;
d := DirectProduct( List( h, ImagesSource ) );
geni:=[];
for gen in GeneratorsOfGroup(g) do
img := One(d);
for i in [1..Length(a)] do
img:=img * Image( Embedding(d,i), Image(h[i],gen) );
od;
Add( geni, img );
od;
hom:=GroupHomomorphismByImagesNC(g,d,GeneratorsOfGroup(g),geni);
# The homomorphism is surjective.
SetIsSurjective(hom,true);
return hom;
end);
#############################################################################
##
#M TraceDefinition . . . . . . trace a generator back to defining generators
##
InstallMethod( TraceDefinition,
"p-quotient system",
true,
[ IsPQuotientSystem, IsPosInt ], 0,
function( qs, g )
local def, trace;
def := GetDefinitionNC( qs, g );
trace := [];
while IsList( def ) or IsPosInt( def ) do
if IsList( def ) then
g := def[1];
Add( trace, def[2] );
else
g := def;
Add( trace, '^' );
fi;
def := GetDefinitionNC( qs, g );
od;
Add( trace, g );
return Reversed( trace );
end );
#############################################################################
##
#E Emacs . . . . . . . . . . . . . . . . . . . . . . . . . . emacs variables
##
## Local Variables:
## mode: outline
## tab-width: 4
## outline-regexp: "#[ACEFHMOPRWY]"
## fill-column: 77
## fill-prefix: "## "
## eval: (hide-body)
## End:
[ 0.67Quellennavigators
Projekt
]
|