# Discriminant Groups

A torsion quadratic module is the quotient $M/N$ of two quadratic integer lattices $N \subseteq M$ in the quadratic space $(V,\Phi)$. It inherits a bilinear form

$$$b: M/N \times M/N \to \mathbb{Q} / n \mathbb{Z}$$$

as well as a quadratic form

$$$q: M/N \to \mathbb{Q} / m \mathbb{Z}.$$$

where $n \mathbb{Z} = \Phi(M,N)$ and $m \mathbb{Z} = 2n\mathbb{Z} + \sum_{x \in N} \mathbb{Z} \Phi (x,x)$.

torsion_quadratic_moduleMethod
torsion_quadratic_module(M::ZLat, N::ZLat; gens::Union{Nothing, Vector{<:Vector}} = nothing,
snf::Bool = true,
modulus::QQFieldElem = QQFieldElem(0),
check::Bool = true) -> TorQuadModule

Given a Z-lattice $M$ and a sublattice $N$ of $M$, return the torsion quadratic module $M/N$.

If gens is set, the images of gens will be used as the 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.

### The underlying Type

TorQuadModuleType
TorQuadModule

Example:

julia> A = matrix(ZZ, [[2,0,0,-1],[0,2,0,-1],[0,0,2,-1],[-1,-1,-1,2]]);

julia> L = Zlattice(gram = A);

julia> T = Hecke.discriminant_group(L)
Finite quadratic module over Integer Ring with underlying abelian group
GrpAb: (Z/2)^2
Gram matrix of the quadratic form with values in Q/2Z
[   1   1//2]
[1//2      1]

We represent torsion quadratic modules as quotients of $\Z$-lattices by a full rank sublattice.

We store them as a $\Z$-lattice M together with a projection p : M -> A onto an abelian group A. The bilinear structure of A is induced via p, that is <a, b> = <p^-1(a), p^-1(a)> with values in $\Q/n\Z$, where $n$ is the modulus and depends on the kernel of p.

Elements of A are basically just elements of the underlying abelian group. To move between M and A, we use the lift function lift : M -> A and coercion A(m).

Examples

julia> R = rescale(root_lattice(:D,4),2);

julia> D = discriminant_group(R);

julia> A = abelian_group(D)
GrpAb: (Z/2)^2 x (Z/4)^2

julia> d = D
[1, 0, 0, 0]

julia> d == D(A(d))
true

julia> lift(d)
4-element Vector{QQFieldElem}:
1
1
3//2
1

N.B. Since there are no elements of $\Z$-latties, we think of elements of M as elements of the ambient vector space. Thus if v::Vector is such an element then the coordinates with respec to the basis of M are given by solve_left(basis_matrix(M), v).

Most of the functionality mirrors that of AbGrp its elements and homomorphisms. Here we display the part that is specific to elements of torsion quadratic modules.

### Attributes

abelian_groupMethod
abelian_group(T::TorQuadModule) -> GrpAbFinGen

Return the underlying abelian group of T.

coverMethod
cover(T::TorQuadModule) -> ZLat

For $T=M/N$ this returns $M$.

relationsMethod
relations(T::TorQuadModule) -> ZLat

For $T=M/N$ this returns $N$.

value_moduleMethod
value_module(T::TorQuadModule)

Return the value module Q/nZ of the bilinear form of T.

value_module_quadratic_formMethod
value_module_quadratic_form(T::TorQuadModule)

Return the value module Q/mZ of the quadratic form of T.

gram_matrix_bilinearMethod
gram_matrix_bilinear(T::TorQuadModule) -> QQMatrix

Return the gram matrix of the bilinear form of T.

gram_matrix_quadraticMethod
gram_matrix_quadratic(T::TorQuadModule) -> QQMatrix

Return the 'gram matrix' of the quadratic form of T.

The off diagonal entries are given by the bilinear form whereas the diagonal entries are given by the quadratic form.

modulus_bilinear_formMethod
modulus_bilinear_form(T::TorQuadModule) -> QQFieldElem

Return the modulus of the value module of the bilinear form ofT.

modulus_quadratic_formMethod
modulus_quadratic_form(T::TorQuadModule) -> QQFieldElem

Return the modulus of the value module of the quadratic form of T.

### Elements

quadratic_productMethod
quadratic_product(a::TorQuadModuleElem)

Return the quadratic product of a.

It is defined in terms of a representative: for $b + M \in M/N=T$ this returns $\Phi(b,b) + n \Z$..

inner_productMethod
inner_product(a::TorQuadModuleElem, b::TorQuadModuleElem)

Return the inner product of a and b.

### Lift to the cover

liftMethod
lift(a::TorQuadModuleElem) -> Vector{QQFieldElem}

Lift a to the ambient space of cover(parent(a)).

For $a + N \in M/N$ this returns the representative $a$.

representativeMethod
representative(a::TorQuadModuleElem) -> Vector{QQFieldElem}

For $a + N \in M/N$ this returns the representative $a$. An alias for lift(a).

### Orthogonal submodules

orthogonal_submoduleMethod
orthogonal_submodule(T::TorQuadModule, S::TorQuadModule)-> TorQuadModule

Return the orthogonal submodule to the submodule S of T.

### Isometry

is_isometric_with_isometryMethod
is_isometric_with_isometry(T::TorQuadModule, U::TorQuadModule)
-> Bool, TorQuadModuleMor

Return whether the torsion quadratic modules T and U are isometric. If yes, it also returns an isometry $T \to U$.

If T and U are not semi-regular it requires that they both split into a direct sum of their respective quadratic radical (see radical_quadratic).

It requires that both T and U have modulus 1: in case one of them do not, they should be rescaled (see rescale).

Examples

julia> T = torsion_quadratic_module(QQ[2//3 2//3    0    0    0;
2//3 2//3 2//3    0 2//3;
0 2//3 2//3 2//3    0;
0    0 2//3 2//3    0;
0 2//3    0    0 2//3])
Finite quadratic module over Integer Ring with underlying abelian group
GrpAb: (Z/3)^5
Gram matrix of the quadratic form with values in Q/2Z
[2//3   2//3      0      0      0]
[2//3   2//3   2//3      0   2//3]
[   0   2//3   2//3   2//3      0]
[   0      0   2//3   2//3      0]
[   0   2//3      0      0   2//3]

julia> U = torsion_quadratic_module(QQ[4//3    0    0    0    0;
0 4//3    0    0    0;
0    0 4//3    0    0;
0    0    0 4//3    0;
0    0    0    0 4//3])
Finite quadratic module over Integer Ring with underlying abelian group
GrpAb: (Z/3)^5
Gram matrix of the quadratic form with values in Q/2Z
[4//3      0      0      0      0]
[   0   4//3      0      0      0]
[   0      0   4//3      0      0]
[   0      0      0   4//3      0]
[   0      0      0      0   4//3]

julia> bool, phi = is_isometric_with_isometry(T,U)
(true, Map with following data
Domain:
=======
TorQuadModule [2//3 2//3 0 0 0; 2//3 2//3 2//3 0 2//3; 0 2//3 2//3 2//3 0; 0 0 2//3 2//3 0; 0 2//3 0 0 2//3]
Codomain:
=========
TorQuadModule [4//3 0 0 0 0; 0 4//3 0 0 0; 0 0 4//3 0 0; 0 0 0 4//3 0; 0 0 0 0 4//3])

julia> is_bijective(phi)
true

julia> T2, _ = sub(T, [-T, T+T+T])
(TorQuadModule: (Z/3)^2 [2//3 1//3; 1//3 2//3], Map with following data
Domain:
=======
Codomain:
=========
TorQuadModule [2//3 2//3 0 0 0; 2//3 2//3 2//3 0 2//3; 0 2//3 2//3 2//3 0; 0 0 2//3 2//3 0; 0 2//3 0 0 2//3])

julia> U2, _ = sub(T, [T, T+T+T])
(TorQuadModule: (Z/3)^2 [2//3 2//3; 2//3 2//3], Map with following data
Domain:
=======
Codomain:
=========
TorQuadModule [2//3 2//3 0 0 0; 2//3 2//3 2//3 0 2//3; 0 2//3 2//3 2//3 0; 0 0 2//3 2//3 0; 0 2//3 0 0 2//3])

julia> bool, phi = is_isometric_with_isometry(U2, T2)
(true, Map with following data
Domain:
=======
Codomain:
=========

julia> is_bijective(phi)
true
is_anti_isometric_with_anti_isometryMethod
is_anti_isometric_with_anti_isometry(T::TorQuadModule, U::TorQuadModule)
-> Bool, TorQuadModuleMor

Return whether there exists an anti-isometry between the torsion quadratic modules T and U. If yes, it returns such an anti-isometry $T \to U$.

If T and U are not semi-regular it requires that they both split into a direct sum of their respective quadratic radical (see radical_quadratic).

It requires that both T and U have modulus 1: in case one of them do not, they should be rescaled (see rescale).

Examples

julia> T = torsion_quadratic_module(QQ[4//5;])
Finite quadratic module over Integer Ring with underlying abelian group
GrpAb: Z/5
Gram matrix of the quadratic form with values in Q/2Z
[4//5]

julia> bool, phi = is_anti_isometric_with_anti_isometry(T, T)
(true, Map with following data
Domain:
=======
Codomain:
=========

julia> a = gens(T);

julia> a*a == -phi(a)*phi(a)
true

julia> G = matrix(FlintQQ, 6, 6 , [3 3 0 0 0  0;
3 3 3 0 3  0;
0 3 3 3 0  0;
0 0 3 3 0  0;
0 3 0 0 3  0;
0 0 0 0 0 10]);

julia> B = matrix(QQ, 6, 6 , [1    0    0    0    0    0;
0 1//3 1//3 2//3 1//3    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//5]);

julia> M = lattice(V, B);

julia> B2 = matrix(FlintQQ, 6, 6 , [ 1  0 -1  1  0 0;
0  0  1 -1  0 0;
-1  1  1 -1 -1 0;
1 -1 -1  2  1 0;
0  0 -1  1  1 0;
0  0  0  0  0 1]);

julia> N = lattice(V, B2);

Finite quadratic module over Integer Ring with underlying abelian group
GrpAb: Z/15
Gram matrix of the quadratic form with values in Q/Z
[3//5]

julia> bool, phi = is_anti_isometric_with_anti_isometry(T,T)
(true, Map with following data
Domain:
=======
Codomain:
=========

julia> a = gens(T);

julia> a*a == -phi(a)*phi(a)
true

### Primary and elementary modules

is_primary_with_prime(T::TorQuadModule)
is_elementary(T::TorQuadModule, p::Union{Integer, ZZRingElem})

### Smith normal form

snf(T::TorQuadModule)
is_snf(T::TorQuadModule)

## Discriminant Groups

See V. V. Nikulin (1979) for the general theory of discriminant groups. They are particularly useful to work with primitive embeddings of integral integer quadratic lattices.

### From a lattice

discriminant_groupMethod
discriminant_group(L::ZLat) -> TorQuadModule

Return the discriminant group of L.

The discriminant group of an integral lattice L is the finite abelian group D = dual(L)/L.

It comes equipped with the discriminant bilinear form

$$$D \times D \to \Q / \Z \qquad (x,y) \mapsto \Phi(x,y) + \Z.$$$

If L is even, then the discriminant group is equipped with the discriminant quadratic form $D \to \Q / 2 \Z, x \mapsto \Phi(x,x) + 2\Z$.

### From a Matrix

torsion_quadratic_moduleMethod
torsion_quadratic_module(q::QQMatrix) -> TorQuadModule

Return a torsion quadratic module with gram matrix given by q and value module Q/Z. If all the diagonal entries of q have: either even numerator or even denominator, then the value module of the quadratic form is Q/2Z

Example

julia> torsion_quadratic_module(QQ[1//6;])
Finite quadratic module over Integer Ring with underlying abelian group
GrpAb: Z/6
Gram matrix of the quadratic form with values in Q/2Z
[1//6]

Finite quadratic module over Integer Ring with underlying abelian group
GrpAb: Z/2
Gram matrix of the quadratic form with values in Q/2Z
[1//2]

Finite quadratic module over Integer Ring with underlying abelian group
GrpAb: Z/2
Gram matrix of the quadratic form with values in Q/2Z
[3//2]

Finite quadratic module over Integer Ring with underlying abelian group
GrpAb: Z/3
Gram matrix of the quadratic form with values in Q/Z
[1//3]

### Rescaling the form

rescaleMethod
rescale(T::TorQuadModule, k::RingElement) -> TorQuadModule

Return the torsion quadratic module with quadratic form scaled by $k$, where k is a non-zero rational number. If the old form was defined modulo n, then the new form is defined modulo n k.

### Invariants

is_degenerateMethod
is_degenerate(T::TorQuadModule) -> Bool

Return true if the underlying bilinear form is degenerate.

radical_bilinearMethod
radical_bilinear(T::TorQuadModule) -> TorQuadModule, TorQuadModuleMor

Return the radical \{x \in T | b(x,T) = 0\} of the bilinear form b on T.

radical_quadraticMethod
radical_quadratic(T::TorQuadModule) -> TorQuadModule, TorQuadModuleMor

Return the radical \{x \in T | b(x,T) = 0 and q(x)=0\} of the quadratic form q on T.

normal_formMethod
normal_form(T::TorQuadModule; partial=false) -> TorQuadModule, TorQuadModuleMor

Return the normal form N of the given torsion quadratic module T along with the projection T -> N.

Let K be the radical of the quadratic form of T. Then N = T/K is half-regular. Two half-regular torsion quadratic modules are isometric if and only if they have equal normal forms.

### Genus

genusMethod
genus(T::TorQuadModule, signature_pair::Tuple{Int, Int}) -> ZGenus

Return the genus of an integer lattice with discriminant group T and the given signature_pair. If no such genus exists, raise an error.

Reference

V. V. Nikulin (1979) Corollary 1.9.4 and 1.16.3.

brown_invariantMethod
brown_invariant(self::TorQuadModule) -> Nemo.zzModRingElem

Return the Brown invariant of this torsion quadratic form.

Let (D,q) be a torsion quadratic module with values in Q / 2Z. The Brown invariant Br(D,q) in Z/8Z is defined by the equation

$$$\exp \left( \frac{2 \pi i }{8} Br(q)\right) = \frac{1}{\sqrt{D}} \sum_{x \in D} \exp(i \pi q(x)).$$$

The Brown invariant is additive with respect to direct sums of torsion quadratic modules.

Examples

julia> L = Zlattice(gram=matrix(ZZ, [[2,-1,0,0],[-1,2,-1,-1],[0,-1,2,0],[0,-1,0,2]]));

julia> T = Hecke.discriminant_group(L);

julia> brown_invariant(T)
4
is_genusMethod
is_genus(T::TorQuadModule, signature_pair::Tuple{Int, Int}) -> Bool

Return if there is an integral lattice with this signature and discriminant form.

### Categorical constructions

direct_sumMethod
direct_sum(x::Vararg{TorQuadModule}) -> TorQuadModule, Vector{TorQuadModuleMor}
direct_sum(x::Vector{TorQuadModule}) -> TorQuadModule, Vector{TorQuadModuleMor}

Given a collection of torsion quadratic modules $T_1, \ldots, T_n$, return their direct sum $T := T_1\oplus \ldots \oplus T_n$, together with the injections $T_i \to T$.

For objects of type TorQuadModule, finite direct sums and finite direct products agree and they are therefore called biproducts. If one wants to obtain T as a direct product with the projections $T \to T_i$, one should call direct_product(x). If one wants to obtain T as a biproduct with the injections $T_i \to T$ and the projections $T \to T_i$, one should call biproduct(x).

direct_productMethod
direct_product(x::Vararg{TorQuadModule}) -> TorQuadModule, Vector{TorQuadModuleMor}
direct_product(x::Vector{TorQuadModule}) -> TorQuadModule, Vector{TorQuadModuleMor}

Given a collection of torsion quadratic modules $T_1, \ldots, T_n$, return their direct product $T := T_1\times \ldots \times T_n$, together with the projections $T \to T_i$.

For objects of type TorQuadModule, finite direct sums and finite direct products agree and they are therefore called biproducts. If one wants to obtain T as a direct sum with the inctions $T_i \to T$, one should call direct_sum(x). If one wants to obtain T as a biproduct with the injections $T_i \to T$ and the projections $T \to T_i$, one should call biproduct(x).

biproductMethod
biproduct(x::Vararg{TorQuadModule}) -> TorQuadModule, Vector{TorQuadModuleMor}, Vector{TorQuadModuleMor}
biproduct(x::Vector{TorQuadModule}) -> TorQuadModule, Vector{TorQuadModuleMor}, Vector{TorQuadModuleMor}

Given a collection of torsion quadratic modules $T_1, \ldots, T_n$, return their biproduct $T := T_1\oplus \ldots \oplus T_n$, together with the injections $T_i \to T$ and the projections $T \to T_i$.

For objects of type TorQuadModule, finite direct sums and finite direct products agree and they are therefore called biproducts. If one wants to obtain T as a direct sum with the inctions $T_i \to T$, one should call direct_sum(x). If one wants to obtain T as a direct product with the projections $T \to T_i$, one should call direct_product(x).