Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Symplectic C6 #74

Merged
merged 6 commits into from
Nov 10, 2021
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions gap/ClassicalMaximals.gd
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@ DeclareGlobalFunction("MaximalSubgroupClassRepsSpecialUnitaryGroup");

DeclareGlobalFunction("GLMinusSL");
DeclareGlobalFunction("GUMinusSU");
DeclareGlobalFunction("NormSpMinusSp");
59 changes: 57 additions & 2 deletions gap/ClassicalMaximals.gi
Original file line number Diff line number Diff line change
Expand Up @@ -616,7 +616,7 @@ function(n, q)
if IsOddInt(r) then
if 2 * e = OrderMod(p, r) then
extraspecialNormalizerSubgroup := ExtraspecialNormalizerInSU(r, m, q);
# Cf. Tables 3.5.A and 3.5.G in [KL90]
# Cf. Tables 3.5.B and 3.5.G in [KL90]
numberOfConjugates := Gcd(n, q + 1);
if n = 3 and ((q - 2) mod 9 = 0 or (q - 5) mod 9 = 0) then
numberOfConjugates := 1;
Expand All @@ -630,7 +630,7 @@ function(n, q)
# n = 2 ^ m >= 4
if e = 1 and 2 * e = OrderMod(p, 4) then
extraspecialNormalizerSubgroup := ExtraspecialNormalizerInSU(2, m, q);
# Cf. Tables 3.5.A and 3.5.G in [KL90]
# Cf. Tables 3.5.B and 3.5.G in [KL90]
numberOfConjugates := Gcd(n, q + 1);
if n = 4 and (q - 3) mod 8 = 0 then
numberOfConjugates := 2;
Expand Down Expand Up @@ -827,3 +827,58 @@ function(n, q, classes...)

return maximalSubgroups;
end);

# Return an element of the normalizer of Sp(n, q) in GL(n, q) that is not
# already contained in Sp(n, q), i.e. which preserves the symplectic form
# modulo a scalar
InstallGlobalFunction("NormSpMinusSp",
function(n, q)
local F, zeta, result, halfOfn;

if IsOddInt(n) then
ErrorNoReturn("<n> must be even but <n> = ", n);
fi;

F := GF(q);
zeta := PrimitiveElement(F);
halfOfn := QuoInt(n, 2);
result := DiagonalMat(Concatenation(List([1..halfOfn], i -> zeta),
List([1..halfOfn], i -> zeta ^ 0)));
return ImmutableMatrix(F, result);
end);

BindGlobal("C6SubgroupsSymplecticGroupGeneric",
function(n, q)
local factorisationOfq, p, e, factorisationOfn, r, m, result,
generatorNormSpMinusSp, numberOfConjugates, extraspecialNormalizerSubgroup;

result := [];
if not IsPrimePowerInt(n) then
return result;
fi;

factorisationOfq := PrimePowersInt(q);
p := factorisationOfq[1];
e := factorisationOfq[2];
factorisationOfn := PrimePowersInt(n);
r := factorisationOfn[1];
m := factorisationOfn[2];
generatorNormSpMinusSp := NormSpMinusSp(n, q);

# Cf. Table 4.6.B and the corresponding definition in [KL90]
if r = 2 and e = 1 then
extraspecialNormalizerSubgroup := ExtraspecialNormalizerInSp(m, q);
# Cf. Tables 3.5.C and 3.5.G in [KL90]
if (q - 1) = 0 mod 8 or (q - 7) = 0 mod 8 then
maxhauck marked this conversation as resolved.
Show resolved Hide resolved
numberOfConjugates := 2;
else
numberOfConjugates := 1;
fi;
result := ConjugatesInGeneralGroup(extraspecialNormalizerSubgroup,
generatorNormSpMinusSp,
numberOfConjugates);
fi;

return result;
end);

179 changes: 145 additions & 34 deletions gap/ExtraspecialNormalizerMatrixGroups.gi
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ function(r, m, q)
# It is a straightforward calculation to show that these matrices preserve
# the unitary form given by the matrix I_d if one uses the fact that r | q + 1
# in the unitary case.
#
# In the symplectic case, we have r = 2 and therefore omega = -1 and it is
# also straightforward to check that these matrices preserve the symplectic
# form given by the block-diagonal matrix consisting of blocks [[0, 1], [-1, 0]]
listOfXi := List([1..m], i ->
KroneckerProduct(KroneckerProduct(IdentityMat(r ^ (m - i), F), X),
IdentityMat(r ^ (i - 1), F)));
Expand Down Expand Up @@ -56,6 +60,10 @@ function(r, m, q, type...)

# Similarly to above, it is a straightforward calculation to show that the
# matrices Ui preserve the unitary form given by the identity matrix
#
# Again, in the symplectic case, one can easily check that the Ui preserve
# the symplectic form given by the block-diagonal matrix consisting of
# blocks [[0, 1], [-1, 0]] (recall that r = 2 in this case!)
listOfUi := List([1..m], i ->
KroneckerProduct(KroneckerProduct(IdentityMat(r ^ (m - i), F), U),
IdentityMat(r ^ (i - 1), F)));
Expand All @@ -64,6 +72,10 @@ function(r, m, q, type...)
# straightforward to see Vi * I_d * HermitianConjugate(Vi) = r * I_d. This
# "problem" will be taken care of eventually when we normalize determinants
# in the function ExtraspecialNormalizerInSL.
#
# In the same way, the matrices Vi scale the symplectic form given by the
# block-diagonal matrix consisting of blocks [[0, 1], [-1, 0]] by a factor
# of r = 2. This will be corrected later once we fix determinants.
listOfVi := List([1..m], i ->
KroneckerProduct(KroneckerProduct(IdentityMat(r ^ (m - i), F), V),
IdentityMat(r ^ (i - 1), F)));
Expand All @@ -82,6 +94,10 @@ function(r, m, q, type...)
W := Z(q) ^ 0 * PermutationMat(w, r ^ 2);
# These matrices preserve the unitary form given by the identity matrix
# as W is a permutation matrix.
#
# Similarly, one can check that they also preserve the symplectic form
# given by the block-diagonal matrix consisting of blocks [[0, 1], [-1, 0]]
# (remember r = 2 in this case!)
listOfWi := List([1..m - 1],
i -> KroneckerProduct(KroneckerProduct(IdentityMat(r ^ (m - 1 - i),
F),
Expand All @@ -97,7 +113,10 @@ function(r, m, q, type...)
# so this is actually q
rootOfq := RootInt(q);
generatingScalar := (zeta ^ (rootOfq - 1)) * IdentityMat(r ^ m, F);
elif type = "S" then
generatingScalar := -IdentityMat(r ^ m, F);
fi;

result := OddExtraspecialGroup(r, m, q);
result.generatingScalar := generatingScalar;
result.listOfUi := listOfUi;
Expand Down Expand Up @@ -153,15 +172,21 @@ end);

# Construction as in Lemma 9.4 of [HR05]
BindGlobal("Extraspecial2MinusTypeNormalizerInGL",
function(m, q)
function(m, q, type...)
local F, solutionQuadraticCongruence, a, b, kroneckerFactorX1, kroneckerFactorY1,
kroneckerFactorU1, kroneckerFactorV1, kroneckerFactorW1, result, p;

if (q - 1) mod 2 <> 0 then
ErrorNoReturn("<q> must be odd but <q> = ", q);
fi;

result := OddExtraspecialNormalizerInGL(2, m, q);
if Length(type) = 0 then
type := "L";
else
type := type[1];
fi;

result := OddExtraspecialNormalizerInGL(2, m, q, type);

F := GF(q);
p := PrimeDivisors(q)[1];
Expand All @@ -170,23 +195,34 @@ function(m, q)
b := solutionQuadraticCongruence.b;

# This has determinant 1 by construction of a and b
#
# It preserves the symplectic form given by the block-diagonal matrix
# consisting of blocks [[0, -1], [1, 0]]
kroneckerFactorX1 := Z(q) ^ 0 * [[a, b], [b, -a]];
# Determinant 1
#
# Preserves the aforementioned symplectic form
kroneckerFactorY1 := Z(q) ^ 0 * [[0, -1], [1, 0]];
# Determinant 2
#
# Scales the aforementioned symplectic form by 2; this will be corrected
# once we fix determinants later on
kroneckerFactorU1 := Z(q) ^ 0 * [[1, 1], [-1, 1]];
# Determinant 4
#
# Scales the aforementioned symplectic form by 4; this will be corrected
# once we fix determinants later on
kroneckerFactorV1 := Z(q) ^ 0 * [[1 + a + b, 1 - a + b],
[-1 - a + b, 1 - a - b]];
if m <> 1 then
# Determinant 4
#
# Scales the aforementioned symplectic form by 2; this will be
# corrected once we fix determinants later on
kroneckerFactorW1 := Z(q) ^ 0 * [[1, 0, 1, 0], [0, 1, 0, 1],
[0, 1, 0, -1], [-1, 0, 1, 0]];
fi;

# TODO
# It seems we don't need the Ui here, but just U1?
# --> Check this with the Magma code!
result.listOfUi := [];
# Determinant 1
result.listOfXi[1] := KroneckerProduct(IdentityMat(2 ^ (m - 1), F),
Expand Down Expand Up @@ -500,61 +536,108 @@ function(m, q, type...)
end);

# Construction as in Proposition 9.5 of [HR05]
# Only for d = 2
# Only built for m = 1 if type = "L"
BindGlobal("Extraspecial2MinusTypeNormalizerInSL",
function(q)
function(m, q, type...)
local F, generatorsOfNormalizerInGL, generatingScalar, p, e, V1, U1,
factorization, generators, size, scalarMultiplierV1, scalarMultiplierU1,
zeta;
factorization, generators, size, scalarMultiplier, zeta, listOfVi, listOfWi;

if Length(type) = 0 then
type := "L";
else
type := type[1];
fi;

if type = "L" and m > 1 then
ErrorNoReturn("If <type> = 'L', we must have <m> = 1 but <m> = ", m);
fi;

F := GF(q);
# q = p ^ e with p prime
factorization := PrimePowersInt(q);
p := factorization[1];
e := factorization[2];
zeta := PrimitiveElement(F);

generatorsOfNormalizerInGL := Extraspecial2MinusTypeNormalizerInGL(1, q);
# Note that we only have the matrices X1, Y1, U1, V1
generatorsOfNormalizerInGL := Extraspecial2MinusTypeNormalizerInGL(m, q, type);
U1 := generatorsOfNormalizerInGL.listOfUi[1];
V1 := generatorsOfNormalizerInGL.listOfVi[1];
listOfVi := generatorsOfNormalizerInGL.listOfVi;
listOfWi := generatorsOfNormalizerInGL.listOfWi;

# We always need a generating element of Z(SL(d, q))
generatingScalar := zeta ^ (QuoInt(q - 1, Gcd(q - 1, 2))) *
IdentityMat(2, F);
# We always need a generating scalar
if type = "L" then
generatingScalar := zeta ^ (QuoInt(q - 1, Gcd(q - 1, 2))) *
IdentityMat(2 ^ m, F);
elif type = "S" then
generatingScalar := -IdentityMat(2 ^ m, F);
fi;

# Note that det(X1) = det(Y1) = 1, so we do not need to rescale these to
# determinant 1. Furthermore, det(V1) = 4 and this is always a square, so
# we can always rescale V1 to determinant 1.
scalarMultiplierV1 := ScalarToNormalizeDeterminant(V1, 2, F);
V1 := scalarMultiplierV1 * V1;
# Note that det(Xi) = det(Yi) = 1, so we do not need to rescale these to
# determinant 1. Similarly, det(Wi) = 1 for i >= 2.
#
# Furthermore, det(V1) = 2 ^ (2 ^ m) so we just have to rescale this by a
# factor of 1 / 2; this also leads to V1 preserving the symplectic form
# given by the block-diagonal matrix consisting of blocks [[0, 1], [-1, 0]]
listOfVi[1] := 1 / 2 * listOfVi[1];

# If type = "L", we have m = 1 and the only generator not considered yet is
# U1. Then det(U1) = 2 and we need to find a square root of 2 in GF(q).
#
# If type = "S", we still have to consider U1, the Vi for i >= 2 and W1.
# All these scale the symplectic form given by the block-diagonal matrix
# consisting of blocks [[0, 1], [-1, 0]] by a factor of 2 so we need to
# find a square root of 2 in GF(q) to fix this; this will also take care of
# the determinants.
if IsEvenInt(e) or (p - 1) mod 8 = 0 or (p - 7) mod 8 = 0 then
# These are the cases where we can find a square root of det(U1) = 2 in
# GF(q) to rescale U1 to determinant 1.
scalarMultiplierU1 := ScalarToNormalizeDeterminant(U1, 2, F);
U1 := scalarMultiplierU1 * U1;
# These are the cases where we can find a square root of 2 (by quadratic
# reciprocity).
scalarMultiplier := 1 / RootFFE(F, 2 * zeta ^ 0, 2);
U1 := scalarMultiplier * U1;
listOfVi{[2..m]} := List(listOfVi{[2..m]}, Vi -> scalarMultiplier * Vi);
if m >= 2 then
listOfWi[1] := scalarMultiplier * listOfWi[1];
fi;

generators := Concatenation([generatingScalar],
generatorsOfNormalizerInGL.listOfXi,
generatorsOfNormalizerInGL.listOfYi,
[U1, V1]);
[U1],
listOfVi,
listOfWi);

else
# According to the Magma code, there is no need to take another
# generator instead of U1 if we cannot rescale it to determinant 1 - we
# simply discard U1 as a generator.
# Comparing with the Magma code, we can simply take
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you mean here? Are you saying: "The Magma code does this, so we use this as justification for doing the same"? (that doesn't seem like a great argument, by the way ;-).

Or are you saying: "compared to the Magma code, we are better/different/whatever, and hence it suffices to do XYZ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The former :D ... well, sadly the relevant paper does not really say much (or rather anything) concerning this particular case so I trusted the Magma code on this one - however, doing this did pass some sanity checks: (1) the group sizes do come out the right way and (2) this case is exactly where an exception occurs in the structure of these C6 subgroups according to the tables in [BHR13] (namely, they are a bit smaller), so it makes sense that this should suffice.

One could of course check "theoretically" that these generators are enough, but I am not sure if this maybe transcends our current mathematical horizon a bit ;)

# U1 ^ (-1) * V2, U1 ^ (-1) * V3, ..., U1 ^ (-1) * Vm, U1 ^ (-1) * W1
# in this case.
#
# Observe that these all indeed have determinant 1 and
# that they all preserve the symplectic form given by the
# block-diagonal matrix consisting of blocks [[0, 1], [-1, 0]].
listOfVi{[2..m]} := List(listOfVi{[2..m]}, Vi -> U1 ^ (-1) * Vi);
if m >= 2 then
listOfWi[1] := U1 ^ (-1) * listOfWi[1];
fi;

generators := Concatenation([generatingScalar],
generatorsOfNormalizerInGL.listOfXi,
generatorsOfNormalizerInGL.listOfYi,
[V1]);
listOfVi,
listOfWi);
fi;

# Size according to Table 2.9 of [BHR13]
if (q - 1) mod 8 = 0 or (q - 7) mod 8 = 0 then
size := 2 * Factorial(4);
else
size := Factorial(4);
if type = "L" then
if (q - 1) mod 8 = 0 or (q - 7) mod 8 = 0 then
size := 2 * Factorial(4);
else
size := Factorial(4);
fi;
elif type = "S" then
if (q - 1) mod 8 = 0 or (q - 7) mod 8 = 0 then
size := 2 ^ (1 + 2 * m) * SizeSO(-1, 2 * m, 2);
else
size := 2 ^ (2 * m) * SizeSO(-1, 2 * m, 2);
fi;
fi;

return MatrixGroupWithSize(F, generators, size);
Expand All @@ -569,7 +652,7 @@ function(r, m, q)
return SymplecticTypeNormalizerInSL(m, q);
else
# r = 2 and m = 1
return Extraspecial2MinusTypeNormalizerInSL(q);
return Extraspecial2MinusTypeNormalizerInSL(m, q);
fi;
end);

Expand Down Expand Up @@ -601,3 +684,31 @@ function(r, m, q)

return result;
end);

# Construction as in Proposition 9.5 of [HR05]
BindGlobal("ExtraspecialNormalizerInSp",
function(m, q)
local F, result, gramMatrix;
if not 2 ^ m > 2 then
ErrorNoReturn("2 ^ <m> must be at least 4 in the symplectic case, but",
" <m> = ", m);
fi;

F := GF(q);
result := Extraspecial2MinusTypeNormalizerInSL(m, q, "S");

# This group now preserves the symplectic form given by the block-diagonal
# matrix consisting of blocks [[0, 1], [-1, 0]] (see the constructor
# functions above for more info).
# We conjugate the group so that it preserves the standard GAP form
# Antidiag(1, ..., 1, -1, ..., -1).
gramMatrix := MatrixByEntries(F, 2 ^ m, 2 ^ m,
Concatenation(List([1..2 ^ (m - 1)],
i -> [2 * i - 1, 2 * i, 1]),
List([1..2 ^ (m - 1)],
i -> [2 * i, 2 * i - 1, -1])));
SetInvariantBilinearForm(result, rec(matrix := gramMatrix));
result := ConjugateToStandardForm(result, "S");

return result;
end);
Loading