# Integral lattices¶

An integral lattice is a finitely generated free abelian group $$L \cong \ZZ^r$$ equipped with a non-degenerate, symmetric bilinear form $$L \times L \colon \rightarrow \ZZ$$.

Here, lattices have an ambient quadratic space $$\QQ^n$$ and a distinguished basis.

EXAMPLES:

sage: M = Matrix(ZZ, [[0,1], [1,0]])
sage: IntegralLattice(M)
Lattice of degree 2 and rank 2 over Integer Ring
Standard basis
Inner product matrix:
[0 1]
[1 0]


A lattice can be defined by an inner product matrix of the ambient space and a basis:

sage: G = matrix.identity(3)
sage: basis = [[1,-1,0], [0,1,-1]]
sage: L = IntegralLattice(G, basis)
sage: L
Lattice of degree 3 and rank 2 over Integer Ring
Basis matrix:
[ 1 -1  0]
[ 0  1 -1]
Standard scalar product

sage: L.gram_matrix()
[ 2 -1]
[-1  2]


AUTHORS:

• Simon Brandhorst (2017-09): First created

• Paolo Menegatti (2018-03): Added IntegralLatticeDirectSum, IntegralLatticeGluing

This class represents non-degenerate, integral, symmetric free quadratic $$\ZZ$$-modules.

INPUT:

• ambient – an ambient free quadratic module

• basis – a list of elements of ambient or a matrix

• inner_product_matrix – a symmetric matrix over the rationals

EXAMPLES:

sage: IntegralLattice("U",basis=[vector([1,1])])
Lattice of degree 2 and rank 1 over Integer Ring
Basis matrix:
[1 1]
Inner product matrix:
[0 1]
[1 0]

LLL()

Return this lattice with an LLL reduced basis.

EXAMPLES:

sage: L = IntegralLattice('A2')
sage: L.lll() == L
True
sage: G = matrix(ZZ,3,[0,1,0, 1,0,0, 0,0,7])
sage: V = matrix(ZZ,3,[-14,-15,-15, -4,1,16, -5,-5,-4])
sage: L = IntegralLattice(V * G * V.T)
sage: L.lll().gram_matrix()
[0 0 1]
[0 7 0]
[1 0 0]

automorphisms(gens=None, is_finite=None)

Return the orthogonal group of this lattice as a matrix group.

The elements are isometries of the ambient vector space which preserve this lattice. They are represented by matrices with respect to the standard basis.

INPUT:

• gens – a list of matrices (default:None)

• is_finite – bool (default: None) If set to True, then the group is placed in the category of finite groups. Sage does not check this.

OUTPUT:

The matrix group generated by gens. If gens is not specified, then generators of the full orthogonal group of this lattice are computed. They are continued as the identity on the orthogonal complement of the lattice in its ambient space. Currently, we can only compute the orthogonal group for positive definite lattices.

EXAMPLES:

sage: A4 = IntegralLattice("A4")
sage: Aut = A4.orthogonal_group()
sage: Aut
Group of isometries with 5 generators (
[-1  0  0  0]  [0 0 0 1]  [-1 -1 -1  0]  [ 1  0  0  0]  [ 1  0  0  0]
[ 0 -1  0  0]  [0 0 1 0]  [ 0  0  0 -1]  [-1 -1 -1 -1]  [ 0  1  0  0]
[ 0  0 -1  0]  [0 1 0 0]  [ 0  0  1  1]  [ 0  0  0  1]  [ 0  0  1  1]
[ 0  0  0 -1], [1 0 0 0], [ 0  1  0  0], [ 0  0  1  0], [ 0  0  0 -1]
)


The group acts from the right on the lattice and its discriminant group:

sage: x = A4.an_element()
sage: g = Aut.an_element()
sage: g
[ 1  1  1  0]
[ 0  0 -1  0]
[ 0  0  1  1]
[ 0 -1 -1 -1]
sage: x*g
(1, 1, 1, 0)
sage: (x*g).parent()==A4
True
sage: (g*x).parent()
Vector space of dimension 4 over Rational Field
sage: y = A4.discriminant_group().an_element()
sage: y*g
(1)


If the group is finite we can compute the usual things:

sage: Aut.order()
240
sage: conj = Aut.conjugacy_classes_representatives()
sage: len(conj)
14
sage: Aut.structure_description()
'C2 x S5'


The lattice can live in a larger ambient space:

sage: A2 = IntegralLattice(matrix.identity(3),Matrix(ZZ,2,3,[1,-1,0,0,1,-1]))
sage: A2.orthogonal_group()
Group of isometries with 3 generators (
[-1/3  2/3  2/3]  [ 2/3  2/3 -1/3]  [1 0 0]
[ 2/3 -1/3  2/3]  [ 2/3 -1/3  2/3]  [0 0 1]
[ 2/3  2/3 -1/3], [-1/3  2/3  2/3], [0 1 0]
)


It can be negative definite as well:

sage: A2m = IntegralLattice(-Matrix(ZZ,2,[2,1,1,2]))
sage: G = A2m.orthogonal_group()
sage: G.order()
12


If the lattice is indefinite, sage does not know how to compute generators. Can you teach it?:

sage: U = IntegralLattice(Matrix(ZZ,2,[0,1,1,0]))
sage: U.orthogonal_group()
Traceback (most recent call last):
...
NotImplementedError: currently, we can only compute generators for orthogonal groups over definite lattices.


But we can define subgroups:

sage: S = IntegralLattice(Matrix(ZZ,2,[2, 3, 3, 2]))
sage: f = Matrix(ZZ,2,[0,1,-1,3])
sage: S.orthogonal_group([f])
Group of isometries with 1 generator (
[ 0  1]
[-1  3]
)

direct_sum(M)

Return the direct sum of this lattice with M.

INPUT:

• M – a module over $$\ZZ$$

EXAMPLES:

sage: A = IntegralLattice(1)
sage: A.direct_sum(A)
Lattice of degree 2 and rank 2 over Integer Ring
Standard basis
Standard scalar product

discriminant_group(s=0)

Return the discriminant group $$L^\vee / L$$ of this lattice.

INPUT:

• s – an integer (default: 0)

OUTPUT:

The $$s$$ primary part of the discriminant group. If $$s=0$$, returns the whole discriminant group.

EXAMPLES:

sage: L = IntegralLattice(Matrix(ZZ,2,2,[2,1,1,-2])*2)
sage: L.discriminant_group()
Finite quadratic module over Integer Ring with invariants (2, 10)
Gram matrix of the quadratic form with values in Q/2Z:
[  1 1/2]
[1/2 1/5]
sage: L.discriminant_group(2)
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]
sage: L.discriminant_group(5)
Finite quadratic module over Integer Ring with invariants (5,)
Gram matrix of the quadratic form with values in Q/2Z:
[4/5]

dual_lattice()

Return the dual lattice as a FreeQuadraticModule

Let $$L$$ be a lattice. Its dual lattice is

$L^\vee = \{x \in L \otimes \QQ : (x, l) \in \ZZ \; \forall l \in L \}.$

EXAMPLES:

sage: L = IntegralLattice("A2")
sage: Ldual=L.dual_lattice()
sage: Ldual
Free module of degree 2 and rank 2 over Integer Ring
Echelon basis matrix:
[1/3 2/3]
[  0   1]


Since our lattices are always integral, a lattice is contained in its dual:

sage: L.is_submodule(Ldual)
True

genus()

Return the genus of this lattice.

EXAMPLES:

sage: L = IntegralLattice("U")
sage: L.genus()
Genus of
[0 1]
[1 0]
Signature:  (1, 1)
Genus symbol at 2:    1^2

is_even()

Return whether the diagonal entries of the Gram matrix are even.

EXAMPLES:

sage: G = Matrix(ZZ,2,2,[-1,1,1,2])
sage: L = IntegralLattice(G)
sage: L.is_even()
False
sage: L = IntegralLattice("A2")
sage: L.is_even()
True

is_primitive(M)

Return whether M is a primitive submodule of this lattice.

A $$\ZZ$$-submodule M of a $$\ZZ$$-module L is called primitive if the quotient L/M is torsion free.

INPUT:

• M – a submodule of this lattice

EXAMPLES:

sage: U = IntegralLattice("U")
sage: L1 = U.span([vector([1,1])])
sage: L2 = U.span([vector([1,-1])])
sage: U.is_primitive(L1)
True
sage: U.is_primitive(L2)
True
sage: U.is_primitive(L1+L2)
False


We can also compute the index:

sage: (L1+L2).index_in(U)
2

lll()

Return this lattice with an LLL reduced basis.

EXAMPLES:

sage: L = IntegralLattice('A2')
sage: L.lll() == L
True
sage: G = matrix(ZZ,3,[0,1,0, 1,0,0, 0,0,7])
sage: V = matrix(ZZ,3,[-14,-15,-15, -4,1,16, -5,-5,-4])
sage: L = IntegralLattice(V * G * V.T)
sage: L.lll().gram_matrix()
[0 0 1]
[0 7 0]
[1 0 0]

max()

Return the maximum of this lattice.

$\max\{x^2 | x \in L\setminus \{0\}\}$

EXAMPLES:

sage: L = IntegralLattice('A2')
sage: L.maximum()
+Infinity
sage: L.twist(-1).maximum()
-2

maximal_overlattice(p=None)

Return a maximal even integral overlattice of this lattice.

INPUT:

• p – (default:None) if given return an overlattice $$M$$ of this lattice $$L$$ that is maximal at $$p$$ and the completions $$M_q = L_q$$ are equal for all primes $$q \neq p$$.

If $$p$$ is $$2$$ or None, then the lattice must be even.

EXAMPLES:

sage: L = IntegralLattice("A4").twist(25*89)
sage: L.maximal_overlattice().determinant()
5
sage: L.maximal_overlattice(89).determinant().factor()
5^9
sage: L.maximal_overlattice(5).determinant().factor()
5 * 89^4

maximum()

Return the maximum of this lattice.

$\max\{x^2 | x \in L\setminus \{0\}\}$

EXAMPLES:

sage: L = IntegralLattice('A2')
sage: L.maximum()
+Infinity
sage: L.twist(-1).maximum()
-2

min()

Return the minimum of this lattice.

$\min\{x^2 | x \in L\setminus \{0\}\}$

EXAMPLES:

sage: L = IntegralLattice('A2')
sage: L.minimum()
2
sage: L.twist(-1).minimum()
-Infinity

minimum()

Return the minimum of this lattice.

$\min\{x^2 | x \in L\setminus \{0\}\}$

EXAMPLES:

sage: L = IntegralLattice('A2')
sage: L.minimum()
2
sage: L.twist(-1).minimum()
-Infinity

orthogonal_complement(M)

Return the orthogonal complement of M in this lattice.

INPUT:

• M – a module in the same ambient space or a list of elements of the ambient space

EXAMPLES:

sage: H5 = Matrix(ZZ,2,[2,1,1,-2])
sage: L = IntegralLattice(H5)
sage: S = L.span([vector([1,1])])
sage: L.orthogonal_complement(S)
Lattice of degree 2 and rank 1 over Integer Ring
Basis matrix:
[1 3]
Inner product matrix:
[ 2  1]
[ 1 -2]

sage: L = IntegralLattice(2)
sage: L.orthogonal_complement([vector(ZZ,[1,0])])
Lattice of degree 2 and rank 1 over Integer Ring
Basis matrix:
[0 1]
Standard scalar product

orthogonal_group(gens=None, is_finite=None)

Return the orthogonal group of this lattice as a matrix group.

The elements are isometries of the ambient vector space which preserve this lattice. They are represented by matrices with respect to the standard basis.

INPUT:

• gens – a list of matrices (default:None)

• is_finite – bool (default: None) If set to True, then the group is placed in the category of finite groups. Sage does not check this.

OUTPUT:

The matrix group generated by gens. If gens is not specified, then generators of the full orthogonal group of this lattice are computed. They are continued as the identity on the orthogonal complement of the lattice in its ambient space. Currently, we can only compute the orthogonal group for positive definite lattices.

EXAMPLES:

sage: A4 = IntegralLattice("A4")
sage: Aut = A4.orthogonal_group()
sage: Aut
Group of isometries with 5 generators (
[-1  0  0  0]  [0 0 0 1]  [-1 -1 -1  0]  [ 1  0  0  0]  [ 1  0  0  0]
[ 0 -1  0  0]  [0 0 1 0]  [ 0  0  0 -1]  [-1 -1 -1 -1]  [ 0  1  0  0]
[ 0  0 -1  0]  [0 1 0 0]  [ 0  0  1  1]  [ 0  0  0  1]  [ 0  0  1  1]
[ 0  0  0 -1], [1 0 0 0], [ 0  1  0  0], [ 0  0  1  0], [ 0  0  0 -1]
)


The group acts from the right on the lattice and its discriminant group:

sage: x = A4.an_element()
sage: g = Aut.an_element()
sage: g
[ 1  1  1  0]
[ 0  0 -1  0]
[ 0  0  1  1]
[ 0 -1 -1 -1]
sage: x*g
(1, 1, 1, 0)
sage: (x*g).parent()==A4
True
sage: (g*x).parent()
Vector space of dimension 4 over Rational Field
sage: y = A4.discriminant_group().an_element()
sage: y*g
(1)


If the group is finite we can compute the usual things:

sage: Aut.order()
240
sage: conj = Aut.conjugacy_classes_representatives()
sage: len(conj)
14
sage: Aut.structure_description()
'C2 x S5'


The lattice can live in a larger ambient space:

sage: A2 = IntegralLattice(matrix.identity(3),Matrix(ZZ,2,3,[1,-1,0,0,1,-1]))
sage: A2.orthogonal_group()
Group of isometries with 3 generators (
[-1/3  2/3  2/3]  [ 2/3  2/3 -1/3]  [1 0 0]
[ 2/3 -1/3  2/3]  [ 2/3 -1/3  2/3]  [0 0 1]
[ 2/3  2/3 -1/3], [-1/3  2/3  2/3], [0 1 0]
)


It can be negative definite as well:

sage: A2m = IntegralLattice(-Matrix(ZZ,2,[2,1,1,2]))
sage: G = A2m.orthogonal_group()
sage: G.order()
12


If the lattice is indefinite, sage does not know how to compute generators. Can you teach it?:

sage: U = IntegralLattice(Matrix(ZZ,2,[0,1,1,0]))
sage: U.orthogonal_group()
Traceback (most recent call last):
...
NotImplementedError: currently, we can only compute generators for orthogonal groups over definite lattices.


But we can define subgroups:

sage: S = IntegralLattice(Matrix(ZZ,2,[2, 3, 3, 2]))
sage: f = Matrix(ZZ,2,[0,1,-1,3])
sage: S.orthogonal_group([f])
Group of isometries with 1 generator (
[ 0  1]
[-1  3]
)

overlattice(gens)

Return the lattice spanned by this lattice and gens.

INPUT:

• gens – a list of elements or a rational matrix

EXAMPLES:

sage: L = IntegralLattice(Matrix(ZZ,2,2,[2,0,0,2]))
sage: M = L.overlattice([vector([1,1])/2])
sage: M.gram_matrix()
[1 1]
[1 2]


Return the quadratic form given by $$q(x)=(x,x)$$.

EXAMPLES:

sage: L = IntegralLattice("A2")
sage: q
Quadratic form in 2 variables over Integer Ring with coefficients:
[ 2 -2 ]
[ * 2 ]

short_vectors(n, **kwargs)

Return the short vectors of length $$< n$$.

INPUT:

• n – an integer

• further key word arguments are passed on to sage.quadratic_forms.short_vector_list_up_to_length().

OUTPUT:

• a list $$L$$ where L[k] is the list of vectors of lengths $$k$$

EXAMPLES:

sage: A2 = IntegralLattice('A2')
sage: A2.short_vectors(3)
[[(0, 0)], [], [(1, 1), (-1, -1), (0, 1), (0, -1), (1, 0), (-1, 0)]]
sage: A2.short_vectors(3,up_to_sign_flag=True)
[[(0, 0)], [], [(1, 1), (0, 1), (1, 0)]]

signature()

Return the signature of this lattice, which is defined as the difference between the number of positive eigenvalues and the number of negative eigenvalues in the Gram matrix.

EXAMPLES:

sage: U = IntegralLattice("U")
sage: U.signature()
0

signature_pair()

Return the signature tuple $$(n_+,n_-)$$ of this lattice.

Here $$n_+$$ (resp. $$n_-$$) is the number of positive (resp. negative) eigenvalues of the Gram matrix.

EXAMPLES:

sage: A2 = IntegralLattice("A2")
sage: A2.signature_pair()
(2, 0)

sublattice(basis)

Return the sublattice spanned by basis.

INPUT:

• basis – A list of elements of this lattice.

EXAMPLES:

sage: U = IntegralLattice("U")
sage: S = U.sublattice([vector([1,1])])
sage: S
Lattice of degree 2 and rank 1 over Integer Ring
Basis matrix:
[1 1]
Inner product matrix:
[0 1]
[1 0]
sage: U.sublattice([vector([1,-1])/2])
Traceback (most recent call last):
...
sage: S.sublattice([vector([1,-1])])
Traceback (most recent call last):
...
ValueError: the basis (= [(1, -1)]) does not span a submodule


Return the tensor product of self and other.

INPUT:

• other – an integral lattice

• discard_basis – a boolean (default: False). If True, then the lattice

returned is equipped with the standard basis.

EXAMPLES:

sage: L = IntegralLattice("D3", [[1,-1,0], [0,1,-1]])
sage: L1 = L.tensor_product(L)
sage: L2 = L.tensor_product(L, True)
sage: L1
Lattice of degree 9 and rank 4 over Integer Ring
Basis matrix:
[ 1 -1  0 -1  1  0  0  0  0]
[ 0  1 -1  0 -1  1  0  0  0]
[ 0  0  0  1 -1  0 -1  1  0]
[ 0  0  0  0  1 -1  0 -1  1]
Inner product matrix:
[ 4 -2 -2 -2  1  1 -2  1  1]
[-2  4  0  1 -2  0  1 -2  0]
[-2  0  4  1  0 -2  1  0 -2]
[-2  1  1  4 -2 -2  0  0  0]
[ 1 -2  0 -2  4  0  0  0  0]
[ 1  0 -2 -2  0  4  0  0  0]
[-2  1  1  0  0  0  4 -2 -2]
[ 1 -2  0  0  0  0 -2  4  0]
[ 1  0 -2  0  0  0 -2  0  4]
sage: L1.gram_matrix()
[ 36 -12 -12   4]
[-12  24   4  -8]
[-12   4  24  -8]
[  4  -8  -8  16]
sage: L2
Lattice of degree 4 and rank 4 over Integer Ring
Standard basis
Inner product matrix:
[ 36 -12 -12   4]
[-12  24   4  -8]
[-12   4  24  -8]
[  4  -8  -8  16]


Return the lattice with inner product matrix scaled by s.

INPUT:

• s – a nonzero integer

• discard_basis – a boolean (default: False). If True, then the lattice returned is equipped with the standard basis.

EXAMPLES:

sage: L = IntegralLattice("A4")
sage: L.twist(3)
Lattice of degree 4 and rank 4 over Integer Ring
Standard basis
Inner product matrix:
[ 6 -3  0  0]
[-3  6 -3  0]
[ 0 -3  6 -3]
[ 0  0 -3  6]
sage: L = IntegralLattice(3,[[2,1,0],[0,1,1]])
sage: L
Lattice of degree 3 and rank 2 over Integer Ring
Basis matrix:
[2 1 0]
[0 1 1]
Standard scalar product
sage: L.twist(1)
Lattice of degree 3 and rank 2 over Integer Ring
Basis matrix:
[2 1 0]
[0 1 1]
Standard scalar product
sage: L.twist(1, True)
Lattice of degree 2 and rank 2 over Integer Ring
Standard basis
Inner product matrix:
[5 1]
[1 2]


Return the integral lattice spanned by basis in the ambient space.

A lattice is a finitely generated free abelian group $$L \cong \ZZ^r$$ equipped with a non-degenerate, symmetric bilinear form $$L \times L \colon \rightarrow \ZZ$$. Here, lattices have an ambient quadratic space $$\QQ^n$$ and a distinguished basis.

INPUT:

The input is a descriptor of the lattice and a (optional) basis. - data – can be one of the following:

• a symmetric matrix over the rationals – the inner product matrix

• an integer – the dimension for an Euclidean lattice

• a symmetric Cartan type or anything recognized by CartanMatrix (see also Cartan types) – for a root lattice

• the string "U" or "H" – for hyperbolic lattices

• basis – (optional) a matrix whose rows form a basis of the lattice, or a list of module elements forming a basis

OUTPUT:

A lattice in the ambient space defined by the inner_product_matrix. Unless specified, the basis of the lattice is the standard basis.

EXAMPLES:

sage: H5 = Matrix(ZZ, 2, [2,1,1,-2])
sage: IntegralLattice(H5)
Lattice of degree 2 and rank 2 over Integer Ring
Standard basis
Inner product matrix:
[ 2  1]
[ 1 -2]


A basis can be specified too:

sage: IntegralLattice(H5, Matrix([1,1]))
Lattice of degree 2 and rank 1 over Integer Ring
Basis matrix:
[1 1]
Inner product matrix:
[ 2  1]
[ 1 -2]


We can define an Euclidean lattice just by its dimension:

sage: IntegralLattice(3)
Lattice of degree 3 and rank 3 over Integer Ring
Standard basis
Standard scalar product


Here is an example of the $$A_2$$ root lattice in Euclidean space:

sage: basis = Matrix([[1,-1,0], [0,1,-1]])
sage: A2 = IntegralLattice(3, basis)
sage: A2
Lattice of degree 3 and rank 2 over Integer Ring
Basis matrix:
[ 1 -1  0]
[ 0  1 -1]
Standard scalar product
sage: A2.gram_matrix()
[ 2 -1]
[-1  2]


We use "U" or "H" for defining a hyperbolic lattice:

sage: L1 = IntegralLattice("U")
sage: L1
Lattice of degree 2 and rank 2 over Integer Ring
Standard basis
Inner product matrix:
[0 1]
[1 0]
sage: L1 == IntegralLattice("H")
True


We can construct root lattices by specifying their type (see Cartan types and CartanMatrix):

sage: IntegralLattice(["E", 7])
Lattice of degree 7 and rank 7 over Integer Ring
Standard basis
Inner product matrix:
[ 2  0 -1  0  0  0  0]
[ 0  2  0 -1  0  0  0]
[-1  0  2 -1  0  0  0]
[ 0 -1 -1  2 -1  0  0]
[ 0  0  0 -1  2 -1  0]
[ 0  0  0  0 -1  2 -1]
[ 0  0  0  0  0 -1  2]
sage: IntegralLattice(["A", 2])
Lattice of degree 2 and rank 2 over Integer Ring
Standard basis
Inner product matrix:
[ 2 -1]
[-1  2]
sage: IntegralLattice("D3")
Lattice of degree 3 and rank 3 over Integer Ring
Standard basis
Inner product matrix:
[ 2 -1 -1]
[-1  2  0]
[-1  0  2]
sage: IntegralLattice(["D", 4])
Lattice of degree 4 and rank 4 over Integer Ring
Standard basis
Inner product matrix:
[ 2 -1  0  0]
[-1  2 -1 -1]
[ 0 -1  2  0]
[ 0 -1  0  2]


We can specify a basis as well:

sage: G = Matrix(ZZ, 2, [0,1,1,0])
sage: B = [vector([1,1])]
sage: IntegralLattice(G, basis=B)
Lattice of degree 2 and rank 1 over Integer Ring
Basis matrix:
[1 1]
Inner product matrix:
[0 1]
[1 0]
sage: IntegralLattice(["A", 3], [[1,1,1]])
Lattice of degree 3 and rank 1 over Integer Ring
Basis matrix:
[1 1 1]
Inner product matrix:
[ 2 -1  0]
[-1  2 -1]
[ 0 -1  2]
sage: IntegralLattice(4, [[1,1,1,1]])
Lattice of degree 4 and rank 1 over Integer Ring
Basis matrix:
[1 1 1 1]
Standard scalar product
sage: IntegralLattice("A2", [[1,1]])
Lattice of degree 2 and rank 1 over Integer Ring
Basis matrix:
[1 1]
Inner product matrix:
[ 2 -1]
[-1  2]


Return the direct sum of the lattices contained in the list Lattices.

INPUT:

• Lattices – a list of lattices [L_1,...,L_n]

• return_embeddings – (default: False) a boolean

OUTPUT:

The direct sum of the $$L_i$$ if return_embeddings is False or the tuple [L, phi] where $$L$$ is the direct sum of $$L_i$$ and phi is the list of embeddings from $$L_i$$ to $$L$$.

EXAMPLES:

sage: from sage.modules.free_quadratic_module_integer_symmetric import IntegralLatticeDirectSum
sage: L1 = IntegralLattice("D4")
sage: L2 = IntegralLattice("A3", [[1, 1, 2]])
sage: L3 = IntegralLattice("A4", [[0, 1, 1, 2], [1, 2, 3, 1]])
sage: Lattices = [L1, L2, L3]
sage: IntegralLatticeDirectSum([L1, L2, L3])
Lattice of degree 11 and rank 7 over Integer Ring
Basis matrix:
[1 0 0 0 0 0 0 0 0 0 0]
[0 1 0 0 0 0 0 0 0 0 0]
[0 0 1 0 0 0 0 0 0 0 0]
[0 0 0 1 0 0 0 0 0 0 0]
[0 0 0 0 1 1 2 0 0 0 0]
[0 0 0 0 0 0 0 0 1 1 2]
[0 0 0 0 0 0 0 1 2 3 1]
Inner product matrix:
[ 2 -1  0  0  0  0  0  0  0  0  0]
[-1  2 -1 -1  0  0  0  0  0  0  0]
[ 0 -1  2  0  0  0  0  0  0  0  0]
[ 0 -1  0  2  0  0  0  0  0  0  0]
[ 0  0  0  0  2 -1  0  0  0  0  0]
[ 0  0  0  0 -1  2 -1  0  0  0  0]
[ 0  0  0  0  0 -1  2  0  0  0  0]
[ 0  0  0  0  0  0  0  2 -1  0  0]
[ 0  0  0  0  0  0  0 -1  2 -1  0]
[ 0  0  0  0  0  0  0  0 -1  2 -1]
[ 0  0  0  0  0  0  0  0  0 -1  2]
sage: [L, phi] = IntegralLatticeDirectSum([L1, L2, L3], True)
sage: LL3 = L.sublattice(phi[2].image().basis_matrix())
sage: L3.discriminant() == LL3.discriminant()
True
sage: x = L3([1, 2, 3, 1])
sage: phi[2](x).inner_product(phi[2](x)) == x.inner_product(x)
True


Return an overlattice of the direct sum as defined by glue.

INPUT:

• Lattices – a list of lattices $$[L_1,...,L_n]$$

• glue – a list where the elements are lists in the form $$[g_1,...,g_n]$$; here $$g_i$$ is an element of the discriminant group of $$L_i$$

• return_embeddings – (default: False) a boolean

OUTPUT:

The glued lattice given by $$L_i$$ and glue if return_embeddings is False or the tuple [L, phi] where $$L$$ is the glued lattice and phi the list of embeddings from $$L_i$$ to $$L$$

EXAMPLES:

A single lattice can be glued. This is the same as taking an overlattice:

sage: from sage.modules.free_quadratic_module_integer_symmetric import IntegralLatticeGluing
sage: L1 = IntegralLattice(matrix([[4]]))
sage: g1 = L1.discriminant_group().gens()[0]
sage: glue = [[2 * g1]]
sage: L = IntegralLatticeGluing([L1], glue)
sage: L
Lattice of degree 1 and rank 1 over Integer Ring
Basis matrix:
[1/2]
Inner product matrix:
[4]
sage: L.gram_matrix()
[1]
sage: IntegralLatticeGluing([L1], glue, return_embeddings=True)
[Lattice of degree 1 and rank 1 over Integer Ring
Basis matrix:
[1/2]
Inner product matrix:
[4], [Free module morphism defined by the matrix
[2]
Domain: Lattice of degree 1 and rank 1 over Integer Ring
Standard basis
Inner product matrix:
[4]
Codomain: Lattice of degree 1 and rank 1 over Integer Ring
Basis matrix:
[1/2]
Inner product matrix:
[4]]]

sage: L1 = IntegralLattice([[2]])
sage: L2 = IntegralLattice([[2]])
sage: AL1 = L1.discriminant_group()
sage: AL2 = L2.discriminant_group()
sage: AL1
Finite quadratic module over Integer Ring with invariants (2,)
Gram matrix of the quadratic form with values in Q/2Z:
[1/2]
sage: g1 = L1.discriminant_group().gens()[0]
sage: g2 = L2.discriminant_group().gens()[0]
sage: glue = [[g1, g2]]
sage: IntegralLatticeGluing([L1, L2], glue)
Lattice of degree 2 and rank 2 over Integer Ring
Basis matrix:
[1/2 1/2]
[  0   1]
Inner product matrix:
[2 0]
[0 2]

sage: L1 = IntegralLattice("A4")
sage: L2 = IntegralLattice("A4")
sage: g1 = L1.discriminant_group().gens()[0]
sage: g2 = L2.discriminant_group().gens()[0]
sage: glue = [[g1, 2 * g2]]
sage: [V, phi] = IntegralLatticeGluing([L1, L2], glue, True)
sage: V
Lattice of degree 8 and rank 8 over Integer Ring
Basis matrix:
[1/5 2/5 3/5 4/5 2/5 4/5 1/5 3/5]
[  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   1   0   0]
[  0   0   0   0   0   0   1   0]
[  0   0   0   0   0   0   0   1]
Inner product matrix:
[ 2 -1  0  0  0  0  0  0]
[-1  2 -1  0  0  0  0  0]
[ 0 -1  2 -1  0  0  0  0]
[ 0  0 -1  2  0  0  0  0]
[ 0  0  0  0  2 -1  0  0]
[ 0  0  0  0 -1  2 -1  0]
[ 0  0  0  0  0 -1  2 -1]
[ 0  0  0  0  0  0 -1  2]
sage: V.sublattice(phi[0].image().basis_matrix())
Lattice of degree 8 and rank 4 over Integer Ring
Basis matrix:
[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]
Inner product matrix:
[ 2 -1  0  0  0  0  0  0]
[-1  2 -1  0  0  0  0  0]
[ 0 -1  2 -1  0  0  0  0]
[ 0  0 -1  2  0  0  0  0]
[ 0  0  0  0  2 -1  0  0]
[ 0  0  0  0 -1  2 -1  0]
[ 0  0  0  0  0 -1  2 -1]
[ 0  0  0  0  0  0 -1  2]


Different gluings can be composed:

sage: D4 = IntegralLattice("D4")
sage: D4.discriminant_group()
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]
sage: L2 = IntegralLattice(2 * matrix.identity(2))
sage: L2.discriminant_group()
Finite quadratic module over Integer Ring with invariants (2, 2)
Gram matrix of the quadratic form with values in Q/2Z:
[1/2   0]
[  0 1/2]
sage: g1 = D4.discriminant_group().gens()[0]
sage: g2 = L2.discriminant_group().gens()[0] + L2.discriminant_group().gens()[1]
sage: D6, phi = IntegralLatticeGluing([D4, L2], [[g1, g2]], True)
Finite quadratic module over Integer Ring with invariants (2, 2)
Gram matrix of the quadratic form with values in Q/2Z:
[3/2   0]
[  0 3/2]
sage: f2, g2 = L2.discriminant_group().gens()
sage: E8, psi = IntegralLatticeGluing([D6, L2], [[f1, f2], [g1, g2]], True)
sage: D4embed = E8.sublattice(psi[0](phi[0].image()).basis_matrix())
sage: x = D4([1, 0, 0, 0])
sage: psi[0](phi[0](x)).inner_product(psi[0](phi[0](x)))==x.inner_product(x)
True
sage: D4embed
Lattice of degree 8 and rank 4 over Integer Ring
Basis matrix:
[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]
Inner product matrix:
[ 2 -1  0  0  0  0  0  0]
[-1  2 -1 -1  0  0  0  0]
[ 0 -1  2  0  0  0  0  0]
[ 0 -1  0  2  0  0  0  0]
[ 0  0  0  0  2  0  0  0]
[ 0  0  0  0  0  2  0  0]
[ 0  0  0  0  0  0  2  0]
[ 0  0  0  0  0  0  0  2]


The input may be a list of three or more lattices:

sage: A7 = IntegralLattice("A7")
sage: D5 = IntegralLattice("D5")
sage: gA7 = A7.discriminant_group().gens()[0]
sage: gD5 = D5.discriminant_group().gens()[0]
sage: [L, phi] = IntegralLatticeGluing([A7, A7, D5, D5],
....:                          [[gA7, gA7, gD5, 2 * gD5],
....:                          [gA7, 7 * gA7, 2 * gD5, gD5]], True)
sage: L.determinant()
1
sage: B = phi[0].matrix()
sage: B*L.gram_matrix()*B.transpose()==A7.gram_matrix()
True


The gluing takes place in the direct sum of the respective ambient spaces:

sage: L1 = IntegralLattice("D4", [[1, 1, 0, 0], [0, 1, 1, 0]])
sage: L2 = IntegralLattice("E6", [[0, 2, 0, 0, 0, 0], [0, 0, 0, 0, 1, 1]])
sage: [f1, f2] = L1.discriminant_group().gens()
sage: [g1, g2] = L2.discriminant_group().gens()
sage: [L, phi] = IntegralLatticeGluing([L1, L2], [[f1, g1], [f2, 2 * g2]], True)
sage: phi[0]
Free module morphism defined by the matrix
[ 2  2 -2 -1]
[ 0  2 -1  0]
Domain: Lattice of degree 4 and rank 2 over Integer Ring
Basis matrix:
[1 1 0 0]
[0 1 1 0]
Inner product matrix:
[ 2 -1  0  0]
[-1  2 -1 -1]
[ 0 -1  2  0]
[ 0 -1  0  2]
Codomain: Lattice of degree 10 and rank 4 over Integer Ring
Basis matrix:
[ 1/2    0 -1/2    0    0  1/2    0    0  1/2  1/2]
[   0  1/2  1/2    0    0  1/2    0    0    0    0]
[   0    0    0    0    0    1    0    0    0    0]
[   0    0    0    0    0    0    0    0    1    1]
Inner product matrix:
[ 2 -1  0  0  0  0  0  0  0  0]
[-1  2 -1 -1  0  0  0  0  0  0]
[ 0 -1  2  0  0  0  0  0  0  0]
[ 0 -1  0  2  0  0  0  0  0  0]
[ 0  0  0  0  2  0 -1  0  0  0]
[ 0  0  0  0  0  2  0 -1  0  0]
[ 0  0  0  0 -1  0  2 -1  0  0]
[ 0  0  0  0  0 -1 -1  2 -1  0]
[ 0  0  0  0  0  0  0 -1  2 -1]
[ 0  0  0  0  0  0  0  0 -1  2]
sage: B = phi[0].matrix()
sage: B * L.gram_matrix() * B.transpose()==L1.gram_matrix()
True


Return a local modification of $$M$$ that matches $$G$$ at $$p$$.

INPUT:

• M – a $$\ZZ_p$$-maximal lattice

• G – the gram matrix of a lattice

isomorphic to $$M$$ over $$\QQ_p$$

• p – a prime number

OUTPUT:

an integral lattice $$M'$$ in the ambient space of $$M$$ such that $$M$$ and $$M'$$ are locally equal at all completions except at $$p$$ where $$M'$$ is locally equivalent to the lattice with gram matrix $$G$$

EXAMPLES:

sage: from sage.modules.free_quadratic_module_integer_symmetric import local_modification
sage: L = IntegralLattice("A3").twist(15)
sage: M = L.maximal_overlattice()
sage: for p in prime_divisors(L.determinant()):
....:     M = local_modification(M, L.gram_matrix(), p)
sage: M.genus() == L.genus()
True
sage: L = IntegralLattice("D4").twist(3*4)
sage: M = L.maximal_overlattice()
sage: local_modification(M, L.gram_matrix(), 2)
Lattice of degree 4 and rank 4 over Integer Ring
Basis matrix:
[1/3   0 2/3 2/3]
[  0 1/3   0 2/3]
[  0   0   1   0]
[  0   0   0   1]
Inner product matrix:
[ 24 -12   0   0]
[-12  24 -12 -12]
[  0 -12  24   0]
[  0 -12   0  24]