# Finite $$\ZZ$$-modules with with bilinear and quadratic forms.¶

AUTHORS:

• Simon Brandhorst (2017-09): First created

Create a torsion quadratic form module from a rational matrix.

The resulting quadratic form takes values in $$\QQ / \ZZ$$ or $$\QQ / 2 \ZZ$$ (depending on q). If it takes values modulo $$2$$, then it is non-degenerate. In any case the bilinear form is non-degenerate.

INPUT:

• q – a symmetric rational matrix

EXAMPLES:

sage: q1 = Matrix(QQ,2,[1,1/2,1/2,1])
Finite quadratic module over Integer Ring with invariants (2, 2)
Gram matrix of the quadratic form with values in Q/2Z:
[  1 1/2]
[1/2   1]


In the following example the quadratic form is degenerate. But the bilinear form is still non-degenerate:

sage: q2 = diagonal_matrix(QQ,[1/4,1/3])
Finite quadratic module over Integer Ring with invariants (12,)
Gram matrix of the quadratic form with values in Q/Z:
[7/12]


Finite quotients with a bilinear and a quadratic form.

Let $$V$$ be a symmetric FreeQuadraticModule and $$W \subseteq V$$ a submodule of the same rank as $$V$$. The quotient $$V / W$$ is a torsion quadratic module. It inherits a bilinear form $$b$$ and a quadratic form $$q$$.

$$b: V \times V \to \QQ / m\ZZ$$, where $$m\ZZ = (V,W)$$ and $$b(x,y) = (x,y) + m\ZZ$$

$$q: V \to \QQ / n\ZZ$$, where $$n\ZZ = 2(V,W) + \ZZ \{ (w,w) | w \in W \}$$

INPUT:

• V – a FreeModule with a symmetric inner product matrix

• W – a submodule of V of the same rank as V

• check – bool (default: True)

• modulus – a rational number dividing $$m$$ (default: $$m$$); the inner product $$b$$ is defined in $$\QQ /$$ modulus $$\ZZ$$

• modulus_qf – a rational number dividing $$n$$ (default: $$n$$); the quadratic form $$q$$ is defined in $$\QQ /$$ modulus_qf $$\ZZ$$

EXAMPLES:

sage: from sage.modules.torsion_quadratic_module import TorsionQuadraticModule
sage: V = FreeModule(ZZ, 3)
sage: T
Finite quadratic module over Integer Ring with invariants (5, 5, 5)
Gram matrix of the quadratic form with values in Q/5Z:
[1 0 0]
[0 1 0]
[0 0 1]

Element
all_submodules()

Return a list of all submodules of self.

Warning

This method creates all submodules in memory. The number of submodules grows rapidly with the number of generators. For example consider a vector space of dimension $$n$$ over a finite field of prime order $$p$$. The number of subspaces is (very) roughly $$p^{(n^2-n)/2}$$.

EXAMPLES:

sage: D = IntegralLattice("D4").discriminant_group()
sage: D.all_submodules()
[Finite quadratic module over Integer Ring with invariants ()
Gram matrix of the quadratic form with values in Q/2Z:
[],
Finite quadratic module over Integer Ring with invariants (2,)
Gram matrix of the quadratic form with values in Q/2Z:
,
Finite quadratic module over Integer Ring with invariants (2,)
Gram matrix of the quadratic form with values in Q/2Z:
,
Finite quadratic module over Integer Ring with invariants (2,)
Gram matrix of the quadratic form with values in Q/2Z:
,
Finite quadratic module over Integer Ring with invariants (2, 2)
Gram matrix of the quadratic form with values in Q/2Z:
[  1 1/2]
[1/2   1]]

brown_invariant()

Return the Brown invariant of this torsion quadratic form.

Let $$(D,q)$$ be a torsion quadratic module with values in $$\QQ / 2 \ZZ$$. The Brown invariant $$Br(D,q) \in \Zmod{8}$$ 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.

OUTPUT:

• an element of $$\Zmod{8}$$

EXAMPLES:

sage: L = IntegralLattice("D4")
sage: D = L.discriminant_group()
sage: D.brown_invariant()
4


We require the quadratic form to be defined modulo $$2 \ZZ$$:

sage: from sage.modules.torsion_quadratic_module import TorsionQuadraticModule
sage: T.brown_invariant()
Traceback (most recent call last):
...
ValueError: the torsion quadratic form must have values in QQ / 2 ZZ

gens()

Return generators of self.

There is no assumption on the generators except that they generate the module.

EXAMPLES:

sage: from sage.modules.torsion_quadratic_module import TorsionQuadraticModule
sage: V = FreeModule(ZZ, 3)
sage: T.gens()
((1, 0, 0), (0, 1, 0), (0, 0, 1))

genus(signature_pair)

Return the genus defined by self and the signature_pair.

If no such genus exists, raise a ValueError.

REFERENCES:

[Nik1977] Corollary 1.9.4 and 1.16.3.

EXAMPLES:

sage: L = IntegralLattice("D4").direct_sum(IntegralLattice("A2"))
sage: D = L.discriminant_group()
sage: genus = D.genus(L.signature_pair())
sage: genus
Genus of
None
Signature:  (6, 0)
Genus symbol at 2:    1^4:2^-2
Genus symbol at 3:     1^-5 3^-1
sage: genus == L.genus()
True


Let $$H$$ be an even unimodular lattice of signature $$(9, 1)$$. Then $$L = D_4 + A_2$$ is primitively embedded in $$H$$. We compute the discriminant form of the orthogonal complement of $$L$$ in $$H$$:

sage: DK = D.twist(-1)
sage: DK
Finite quadratic module over Integer Ring with invariants (2, 6)
Gram matrix of the quadratic form with values in Q/2Z:
[  1 1/2]
[1/2 1/3]


We know that $$K$$ has signature $$(5, 1)$$ and thus we can compute the genus of $$K$$ as:

sage: DK.genus((3,1))
Genus of
None
Signature:  (3, 1)
Genus symbol at 2:    1^2:2^-2
Genus symbol at 3:     1^-3 3^1


We can also compute the genus of an odd lattice from its discriminant form:

sage: L = IntegralLattice(matrix.diagonal(range(1,5)))
sage: D = L.discriminant_group()
sage: D.genus((4,0))
Genus of
None
Signature:  (4, 0)
Genus symbol at 2:    [1^-2 2^1 4^1]_6
Genus symbol at 3:     1^-3 3^1

gram_matrix_bilinear()

Return the Gram matrix with respect to the generators.

OUTPUT:

A rational matrix G with G[i,j] given by the inner product of the $$i$$-th and $$j$$-th generator. Its entries are only well defined $$\mod (V, W)$$.

EXAMPLES:

sage: from sage.modules.torsion_quadratic_module import TorsionQuadraticModule
sage: V = FreeQuadraticModule(ZZ, 3, matrix.identity(3)*5)
sage: T.gram_matrix_bilinear()
[1/5   0   0]
[  0 1/5   0]
[  0   0 1/5]


The Gram matrix of the quadratic form with respect to the generators.

OUTPUT:

• a rational matrix Gq with Gq[i,j] = gens[i]*gens[j] and G[i,i] = gens[i].q()

EXAMPLES:

sage: from sage.modules.torsion_quadratic_module import TorsionQuadraticModule
sage: D4_gram = Matrix(ZZ, [[2,0,0,-1],[0,2,0,-1],[0,0,2,-1],[-1,-1,-1,2]])
sage: D4 = FreeQuadraticModule(ZZ, 4, D4_gram)
sage: D4dual = D4.span(D4_gram.inverse())
[  1 1/2]
[1/2   1]
sage: discrForm.gram_matrix_bilinear()
[  0 1/2]
[1/2   0]

is_genus(signature_pair, even=True)

Return True if there is a lattice with this signature and discriminant form.

Todo

implement the same for odd lattices

INPUT:

• signature_pair – a tuple of non negative integers (s_plus, s_minus)

• even – bool (default: True)

EXAMPLES:

sage: L = IntegralLattice("D4").direct_sum(IntegralLattice(3 * Matrix(ZZ,2,[2,1,1,2])))
sage: D = L.discriminant_group()
sage: D.is_genus((6,0))
True


Let us see if there is a lattice in the genus defined by the same discriminant form but with a different signature:

sage: D.is_genus((4,2))
False
sage: D.is_genus((16,2))
True

normal_form(partial=False)

Return the normal form of this torsion quadratic module.

Two torsion quadratic modules are isomorphic if and only if they have the same value modules and the same normal form.

A torsion quadratic module $$(T,q)$$ with values in $$\QQ/n\ZZ$$ is in normal form if the rescaled quadratic module $$(T, q/n)$$ with values in $$\QQ/\ZZ$$ is in normal form.

For the definition of normal form see [MirMor2009] IV Definition 4.6. Below are some of its properties. Let $$p$$ be odd and $$u$$ be the smallest non-square modulo $$p$$. The normal form is a diagonal matrix with diagonal entries either $$p^n$$ or $$u p^n$$.

If $$p = 2$$ is even, then the normal form consists of 1 x 1 blocks of the form

$(0), \quad 2^n(1),\quad 2^n(3),\quad 2^n(5) ,\quad 2^n(7)$

or of $$2 \times 2$$ blocks of the form

$\begin{split}2^n \left(\begin{matrix} 2 & 1\\ 1 & 2 \end{matrix}\right), \quad 2^n \left(\begin{matrix} 0 & 1\\ 1 & 0 \end{matrix}\right).\end{split}$

The blocks are ordered by their valuation.

INPUT:

• partial - bool (default: False) return only a partial normal form it is not unique but still useful to extract invariants

OUTPUT:

EXAMPLES:

sage: L1=IntegralLattice(matrix([[-2,0,0],[0,1,0],[0,0,4]]))
sage: L1.discriminant_group().normal_form()
Finite quadratic module over Integer Ring with invariants (2, 4)
Gram matrix of the quadratic form with values in Q/Z:
[1/2   0]
[  0 1/4]
sage: L2=IntegralLattice(matrix([[-2,0,0],[0,1,0],[0,0,-4]]))
sage: L2.discriminant_group().normal_form()
Finite quadratic module over Integer Ring with invariants (2, 4)
Gram matrix of the quadratic form with values in Q/Z:
[1/2   0]
[  0 1/4]


We check that trac ticket #24864 is fixed:

sage: L1=IntegralLattice(matrix([[-4,0,0],[0,4,0],[0,0,-2]]))
sage: AL1=L1.discriminant_group()
sage: L2=IntegralLattice(matrix([[-4,0,0],[0,-4,0],[0,0,2]]))
sage: AL2=L2.discriminant_group()
sage: AL1.normal_form()
Finite quadratic module over Integer Ring with invariants (2, 4, 4)
Gram matrix of the quadratic form with values in Q/2Z:
[1/2   0   0]
[  0 1/4   0]
[  0   0 5/4]
sage: AL2.normal_form()
Finite quadratic module over Integer Ring with invariants (2, 4, 4)
Gram matrix of the quadratic form with values in Q/2Z:
[1/2   0   0]
[  0 1/4   0]
[  0   0 5/4]


Some exotic cases:

sage: from sage.modules.torsion_quadratic_module import TorsionQuadraticModule
sage: D4_gram = Matrix(ZZ,4,4,[2,0,0,-1,0,2,0,-1,0,0,2,-1,-1,-1,-1,2])
sage: D4dual = D4.span(D4_gram.inverse())
sage: T
Finite quadratic module over Integer Ring with invariants (6, 6, 12, 12)
Gram matrix of the quadratic form with values in Q/(1/3)Z:
[ 1/18  1/12  5/36  1/36]
[ 1/12   1/6  1/36   1/9]
[ 5/36  1/36  1/36 11/72]
[ 1/36   1/9 11/72  1/36]
sage: T.normal_form()
Finite quadratic module over Integer Ring with invariants (6, 6, 12, 12)
Gram matrix of the quadratic form with values in Q/(1/3)Z:
[ 1/6 1/12    0    0    0    0    0    0]
[1/12  1/6    0    0    0    0    0    0]
[   0    0 1/12 1/24    0    0    0    0]
[   0    0 1/24 1/12    0    0    0    0]
[   0    0    0    0  1/9    0    0    0]
[   0    0    0    0    0  1/9    0    0]
[   0    0    0    0    0    0  1/9    0]
[   0    0    0    0    0    0    0  1/9]

orthogonal_group(gens=None, check=False)

Orthogonal group of the associated torsion quadratic form.

Warning

This is can be smaller than the orthogonal group of the bilinear form.

INPUT:

• gens – a list of generators, for instance square matrices,

something that acts on self, or an automorphism of the underlying abelian group

• check – perform additional checks on the generators

EXAMPLES:

You can provide generators to obtain a subgroup of the full orthogonal group:

sage: D = TorsionQuadraticForm(matrix.identity(2)/2)
sage: f = matrix(2,[0,1,1,0])
sage: D.orthogonal_group(gens=[f]).order()
2


If no generators are given a slow brute force approach is used to calculate the full orthogonal group:

sage: D = TorsionQuadraticForm(matrix.identity(3)/2)
sage: OD = D.orthogonal_group()
sage: OD.order()
6
sage: fd = D.hom([D.1,D.0,D.2])
sage: OD(fd)
[0 1 0]
[1 0 0]
[0 0 1]


We compute the kernel of the action of the orthogonal group of $$L$$ on the discriminant group.

sage: L = IntegralLattice(‘A4’) sage: O = L.orthogonal_group() sage: D = L.discriminant_group() sage: Obar = D.orthogonal_group(O.gens()) sage: O.order() 240 sage: Obar.order() 2 sage: phi = O.hom(Obar.gens()) sage: phi.kernel().order() 120

orthogonal_submodule_to(S)

Return the submodule orthogonal to S.

INPUT:

• S – a submodule, list, or tuple of generators

EXAMPLES:

sage: from sage.modules.torsion_quadratic_module import TorsionQuadraticModule
sage: V = FreeModule(ZZ, 10)
sage: S = T.submodule(T.gens()[:5])
sage: O = T.orthogonal_submodule_to(S)
sage: O
Finite quadratic module over Integer Ring with invariants (3, 3, 3, 3, 3)
Gram matrix of the quadratic form with values in Q/3Z:
[1 0 0 0 0]
[0 1 0 0 0]
[0 0 1 0 0]
[0 0 0 1 0]
[0 0 0 0 1]
sage: O.V() + S.V() == T.V()
True

primary_part(m)

Return the m-primary part of this torsion quadratic module as a submodule.

INPUT:

• m – an integer

OUTPUT:

• a submodule

EXAMPLES:

sage: from sage.modules.torsion_quadratic_module import TorsionQuadraticModule
sage: T
Finite quadratic module over Integer Ring with invariants (6, 6, 6)
Gram matrix of the quadratic form with values in Q/(1/3)Z:
[1/36    0    0]
[   0 1/36    0]
[   0    0 1/36]
sage: T.primary_part(2)
Finite quadratic module over Integer Ring with invariants (2, 2, 2)
Gram matrix of the quadratic form with values in Q/(1/3)Z:
[1/4   0   0]
[  0 1/4   0]
[  0   0 1/4]

submodule_with_gens(gens)

Return a submodule with generators given by gens.

INPUT:

• gens – a list of generators that convert into self

OUTPUT:

• a submodule with the specified generators

EXAMPLES:

sage: from sage.modules.torsion_quadratic_module import TorsionQuadraticModule
sage: g = T.gens()
sage: new_gens = [2*g, 5*g]
sage: T.submodule_with_gens(new_gens)
Finite quadratic module over Integer Ring with invariants (10,)
Gram matrix of the quadratic form with values in Q/2Z:
[2/5   0]
[  0 1/2]


The generators do not need to be independent:

sage: new_gens = [g, 2*g, g, g]
sage: T.submodule_with_gens(new_gens)
Finite quadratic module over Integer Ring with invariants (10, 10)
Gram matrix of the quadratic form with values in Q/2Z:
[1/10    0 1/10    0]
[   0  2/5    0  1/5]
[1/10    0 1/10    0]
[   0  1/5    0 1/10]

twist(s)

Return the torsion quadratic module with quadratic form scaled by s.

If the old form was defined modulo $$n$$, then the new form is defined modulo $$n s$$.

INPUT:

• s - a rational number

EXAMPLES:

sage: q = TorsionQuadraticForm(matrix.diagonal([3/9, 1/9]))
sage: q.twist(-1)
Finite quadratic module over Integer Ring with invariants (3, 9)
Gram matrix of the quadratic form with values in Q/Z:
[2/3   0]
[  0 8/9]


This form is defined modulo $$3$$:

sage: q.twist(3)
Finite quadratic module over Integer Ring with invariants (3, 9)
Gram matrix of the quadratic form with values in Q/3Z:
[  1   0]
[  0 1/3]


The next form is defined modulo $$4$$:

sage: q.twist(4)
Finite quadratic module over Integer Ring with invariants (3, 9)
Gram matrix of the quadratic form with values in Q/4Z:
[4/3   0]
[  0 4/9]

value_module()

Return $$\QQ / m\ZZ$$ with $$m = (V, W)$$.

This is where the inner product takes values.

EXAMPLES:

sage: A2 = Matrix(ZZ, 2, 2, [2,-1,-1,2])
sage: L = IntegralLattice(2*A2)
sage: D = L.discriminant_group()
sage: D
Finite quadratic module over Integer Ring with invariants (2, 6)
Gram matrix of the quadratic form with values in Q/2Z:
[  1 1/2]
[1/2 1/3]
sage: D.value_module()
Q/Z

value_module_qf()

Return $$\QQ / n\ZZ$$ with $$n\ZZ = (V,W) + \ZZ \{ (w,w) | w \in W \}$$.

This is where the torsion quadratic form takes values.

EXAMPLES:

sage: A2 = Matrix(ZZ, 2, 2, [2,-1,-1,2])
sage: L = IntegralLattice(2*A2)
sage: D = L.discriminant_group()
sage: D
Finite quadratic module over Integer Ring with invariants (2, 6)
Gram matrix of the quadratic form with values in Q/2Z:
[  1 1/2]
[1/2 1/3]
sage: D.value_module_qf()
Q/2Z


An element of a torsion quadratic module.

INPUT:

• parent – parent

• x – element of parent.V()

• check – bool (default: True)

b(other)

Compute the inner product of two elements.

OUTPUT:

• an element of $$\QQ / m\ZZ$$ with $$m\ZZ = (V, W)$$

EXAMPLES:

sage: from sage.modules.torsion_quadratic_module import TorsionQuadraticModule
sage: V = (1/2)*ZZ^2; W = ZZ^2
sage: g = T.gens()
sage: x = g
sage: y = g + g
sage: x
(1, 0)
sage: x*y
1/4


The inner product has further aliases:

sage: x.inner_product(y)
1/4
sage: x.b(y)
1/4

inner_product(other)

Compute the inner product of two elements.

OUTPUT:

• an element of $$\QQ / m\ZZ$$ with $$m\ZZ = (V, W)$$

EXAMPLES:

sage: from sage.modules.torsion_quadratic_module import TorsionQuadraticModule
sage: V = (1/2)*ZZ^2; W = ZZ^2
sage: g = T.gens()
sage: x = g
sage: y = g + g
sage: x
(1, 0)
sage: x*y
1/4


The inner product has further aliases:

sage: x.inner_product(y)
1/4
sage: x.b(y)
1/4

q()

Compute the quadratic_product of self.

OUTPUT:

• an element of $$\QQ / n\ZZ$$ where $$n\ZZ = 2(V,W) + \ZZ \{ (w,w) | w \in W \}$$

EXAMPLES:

sage: from sage.modules.torsion_quadratic_module import TorsionQuadraticModule
sage: W = FreeQuadraticModule(ZZ, 2, 2*matrix.identity(2))
sage: V = (1/2) * W
sage: x = T.gen(0)
sage: x
(1, 0)
1/2
Q/2Z
sage: x*x
1/2
sage: (x*x).parent()
Q/Z


Compute the quadratic_product of self.

OUTPUT:

• an element of $$\QQ / n\ZZ$$ where $$n\ZZ = 2(V,W) + \ZZ \{ (w,w) | w \in W \}$$

EXAMPLES:

sage: from sage.modules.torsion_quadratic_module import TorsionQuadraticModule
sage: W = FreeQuadraticModule(ZZ, 2, 2*matrix.identity(2))
sage: V = (1/2) * W