Skip to content

Commit

Permalink
fix genus and allow parity variations
Browse files Browse the repository at this point in the history
  • Loading branch information
StevellM committed Dec 15, 2023
1 parent 97bd536 commit 6b47e22
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 33 deletions.
92 changes: 59 additions & 33 deletions src/QuadForm/Torsion.jl
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@ export discriminant_group, torsion_quadratic_module, normal_form, genus, is_genu

@doc raw"""
torsion_quadratic_module(M::ZZLat, N::ZZLat; gens::Union{Nothing, Vector{<:Vector}} = nothing,
snf::Bool = true,
modulus::QQFieldElem = QQFieldElem(0),
check::Bool = true) -> TorQuadModule
snf::Bool = true,
modulus::RationalUnion = QQFieldElem(0),
modulus_qf::RationalUnion = QQFieldElem(0),
check::Bool = true) -> TorQuadModule
Given a Z-lattice $M$ and a sublattice $N$ of $M$, return the torsion quadratic
module $M/N$.
Expand All @@ -29,12 +30,15 @@ generators of the abelian group $M/N$.
If `snf` is `true`, the underlying abelian group will be in Smith normal form.
Otherwise, the images of the basis of $M$ will be used as the generators.
One can decide on the modulus for the associated finite bilinear and quadratic
forms by setting `modulus` and `modulus_qf` respectively to the desired values.
"""
function torsion_quadratic_module(M::ZZLat, N::ZZLat; gens::Union{Nothing, Vector{<:Vector}} = nothing,
snf::Bool = true,
modulus::QQFieldElem = QQFieldElem(0),
modulus_qf::QQFieldElem = QQFieldElem(0),
check::Bool = true)
snf::Bool = true,
modulus::RationalUnion = QQFieldElem(0),
modulus_qf::RationalUnion = QQFieldElem(0),
check::Bool = true)
@req ambient_space(M) === ambient_space(N) """
Lattices must have same ambient space
"""
Expand All @@ -44,7 +48,7 @@ function torsion_quadratic_module(M::ZZLat, N::ZZLat; gens::Union{Nothing, Vecto
A = abelian_group(rels)
n = dim(ambient_space(M))
BM = basis_matrix(M)
if gens !== nothing && length(gens)>0
if gens !== nothing && length(gens) > 0
gens_in_A = elem_type(A)[]
for g in gens
@req length(g) == n "Generator not an element of the ambient space"
Expand All @@ -70,22 +74,24 @@ function torsion_quadratic_module(M::ZZLat, N::ZZLat; gens::Union{Nothing, Vecto
end
# mS : S -> A
# generators of S lifted along M -> M/N = A -> S
if gens !== nothing && length(gens)>0
if gens !== nothing && length(gens) > 0
gens_lift = gens
else
gens_lift = Vector{QQFieldElem}[reshape(collect(change_base_ring(FlintQQ, mS(s).coeff) * BM), :) for s in Hecke.gens(S)]
end

num = basis_matrix(M) * gram_matrix(ambient_space(M)) * transpose(basis_matrix(N))
if iszero(modulus)
modulus = reduce(gcd, num; init = zero(QQFieldElem))
_modulus = reduce(gcd, num; init = zero(QQFieldElem))
else
_modulus = QQ(modulus)
end
norm = reduce(gcd, diagonal(gram_matrix(N)); init = zero(QQFieldElem))

if iszero(modulus_qf)
modulus_qf = gcd(norm, 2 * modulus)
_modulus_qf = gcd(norm, 2 * _modulus)
else
modulus_qf = modulus_qf
_modulus_qf = QQ(modulus_qf)
end

T = TorQuadModule()
Expand All @@ -95,10 +101,10 @@ function torsion_quadratic_module(M::ZZLat, N::ZZLat; gens::Union{Nothing, Vecto
T.proj = inv(mS).map
T.gens_lift = gens_lift
T.gens_lift_mat = matrix(FlintQQ, length(gens_lift), degree(M), reduce(vcat, gens_lift; init = QQFieldElem[]))
T.modulus = modulus
T.modulus_qf = modulus_qf
T.value_module = QmodnZ(modulus)
T.value_module_qf = QmodnZ(modulus_qf)
T.modulus = _modulus
T.modulus_qf = _modulus_qf
T.value_module = QmodnZ(_modulus)
T.value_module_qf = QmodnZ(_modulus_qf)
T.is_normal = false
return T
end
Expand Down Expand Up @@ -1695,16 +1701,29 @@ function brown_invariant(T::TorQuadModule)
return brown
end

function _as_finite_bilinear_module(T::TorQuadModule)
n = modulus_bilinear_form(T)
return torsion_quadratic_module(cover(T), relations(T); modulus = n, modulus_qf = n)
end

@doc raw"""
genus(T::TorQuadModule, signature_pair::Tuple{Int, Int}) -> ZZGenus
genus(T::TorQuadModule, signature_pair::Tuple{Int, Int};
parity::RationalUnion = modulus_quadratic_form(T))
-> ZZGenus
Return the genus of an integer lattice whose discriminant group has the bilinear
form of `T`, the given `signature_pair` and the given `parity`.
Return the genus of an integer lattice with discriminant group `T` and the
given `signature_pair`. If no such genus exists, raise an error.
If no such genus exists, raise an error.
By default the `parity` is taken as the parity of the quadratic form on `T`.
# Reference
[Nik79](@cite) Corollary 1.9.4 and 1.16.3.
"""
function genus(T::TorQuadModule, signature_pair::Tuple{Int, Int})
function genus(T::TorQuadModule, signature_pair::Tuple{Int, Int}; parity::RationalUnion = modulus_quadratic_form(T))
@req modulus_bilinear_form(T) == 1 "Modulus for the bilinear form should be 1"
@req modulus_quadratic_form(T) == 1 || modulus_quadratic_form(T) == 2 "Modulus for the quadratic form should be 2"
s_plus = signature_pair[1]
s_minus = signature_pair[2]
rank = s_plus + s_minus
Expand All @@ -1726,8 +1745,9 @@ function genus(T::TorQuadModule, signature_pair::Tuple{Int, Int})
D, _ = primary_part(T, p)
if length(elementary_divisors(D)) != 0
G_p = inv(gram_matrix_quadratic(D))
dp = denominator(G_p)^2
# get rid of denominators without changing the local equivalence class
map_entries!(x -> x*denominator(G_p)^2, G_p, G_p)
map_entries!(x -> x*dp, G_p, G_p)
G_pZZ = change_base_ring(ZZ, G_p)
genus_p = genus(G_pZZ, p, valuation(elementary_divisors(D)[end], p))
else
Expand All @@ -1751,6 +1771,11 @@ function genus(T::TorQuadModule, signature_pair::Tuple{Int, Int})
end
# This genus has the right discriminant group
# but it may be empty
#
# in this case the blocks of scales 1, 2, 4 are under determined
# make sure the first 3 symbols are of scales 1, 2, 4
# i.e. their valuations are 0, 1, 2

sym2 = local_symbols[1]._symbol
if sym2[1][1] != 0
sym2 = pushfirst!(sym2, Int[0, 0, 1, 0, 0])
Expand All @@ -1761,13 +1786,10 @@ function genus(T::TorQuadModule, signature_pair::Tuple{Int, Int})
if length(sym2) <= 2 || sym2[3][1] != 2
sym2 = insert!(sym2, 3, Int[2, 0, 1, 0, 0])
end
if modulus_quadratic_form(T) == 1
# in this case the blocks of scales 1, 2, 4 are under determined
# make sure the first 3 symbols are of scales 1, 2, 4
# i.e. their valuations are 0, 1, 2

# the form is odd
block0 = [b for b in _blocks(sym2[1]) if b[4] == 1]
if modulus_quadratic_form(T) == 1 || parity == 1
_o = mod(parity, 2)
# the form is of parity `parity`
block0 = [b for b in _blocks(sym2[1]) if b[4] == _o]

o = sym2[2][4]::Int
# no restrictions on determinant and
Expand Down Expand Up @@ -1831,15 +1853,19 @@ function genus(T::TorQuadModule, signature_pair::Tuple{Int, Int})
error("this discriminant form and signature do not define a genus")
end


@doc raw"""
is_genus(T::TorQuadModule, signature_pair::Tuple{Int, Int}) -> Bool
is_genus(T::TorQuadModule, signature_pair::Tuple{Int, Int};
parity::RationalUnion = modulus_quadratic_form(T)) -> Bool
Return if there is an integral lattice whose discriminant form has the bilinear
form of `T`, whose signatures match `signature_pair` and which is of parity
`parity`.
Return if there is an integral lattice with this signature and discriminant form.
By deautl the `parity` is the parity of the quadratic form on `T`.
"""
function is_genus(T::TorQuadModule, signature_pair::Tuple{Int, Int})
function is_genus(T::TorQuadModule, signature_pair::Tuple{Int, Int}; parity::RationalUnion = modulus_quadratic_form(T))
try
genus(T,signature_pair)
genus(T, signature_pair; parity)
return true
catch
return false
Expand Down
32 changes: 32 additions & 0 deletions test/QuadForm/Torsion.jl
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,24 @@
T1 = discriminant_group(L1)
@test genus(T1, (6,0)) == genus(L1)

B = matrix(FlintQQ, 8, 8 ,[2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 3//2, 1//2, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1//2, 0, 1//2, 0, 0, 2//3, 1//6]);
G = matrix(FlintQQ, 8, 8 ,[1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, -3, 0, 0, 0, 0, 0, 0, 0, 0, -42]);
L = integer_lattice(B, gram = G);

T = discriminant_group(L)
@test genus(T, signature_tuple(L)[[1,3]]) == genus(L)
T2 = Hecke._as_finite_bilinear_module(T)
@test genus(T2, signature_tuple(L)[[1,3]]; parity = 2) == genus(L)

B = matrix(FlintQQ, 7, 7 ,[2, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 1//2, 1//2, 1//2, 0, 1//2, 0, 0, 1//2, 1, 1//2, 3//2, 0, 1//2, 0, 1//2, 1, 1//2, 1, 0, 0, 1//2]);
G = matrix(FlintQQ, 7, 7 ,[1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, -10]);
L = integer_lattice(B, gram = G);

T = discriminant_group(L)
@test genus(T, signature_tuple(L)[[1,3]]) == genus(L)
T2 = Hecke._as_finite_bilinear_module(T)
@test genus(T2, signature_tuple(L)[[1,3]]; parity = 2) == genus(L)

#test for is_genus
L = integer_lattice(gram=diagonal_matrix(ZZRingElem[1,2,3,4]))
D = discriminant_group(L)
Expand All @@ -196,6 +214,20 @@
@test is_genus(D, (2,0)) == false
@test is_genus(D, (3,0)) == true

B = matrix(FlintQQ, 7, 7 ,[2, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 9, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1//2, 13//2, 1//2, 1//2, 1, 0, 0, 3//2, 3, 0, 1//2, 1//2, 1//2, 0, 1, 5//2, 1//2, 0, 0, 0, 1//2]);
G = matrix(FlintQQ, 7, 7 ,[1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, -38]);
L = integer_lattice(B, gram = G);

T = discriminant_group(L)
@test is_genus(T, signature_tuple(L)[[1,3]]; parity = 1)

B = matrix(FlintQQ, 6, 6 ,[2, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 1, 6, 1, 0, 0, 0, 0, 5, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1//2, 9, 1//2, 0, 0, 1//2]);
G = matrix(FlintQQ, 6, 6 ,[1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 2]);
L = integer_lattice(B, gram = G);

T = discriminant_group(L)
@test is_genus(T, signature_tuple(L)[[1,3]]; parity = 1)

N, i = normal_form(D)
@test N === normal_form(N)[1]
j = inv(i)
Expand Down

0 comments on commit 6b47e22

Please sign in to comment.