def direct_sum(self, M): r""" 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 Basis matrix: [1 0] [0 1] Inner product matrix: [1 0] [0 1] """ IM = matrix.block_diagonal( [self.inner_product_matrix(), M.inner_product_matrix()]) ambient = FreeQuadraticModule(ZZ, self.degree() + M.degree(), IM) smzero = matrix.zero(self.rank(), M.degree()) mszero = matrix.zero(M.rank(), self.degree()) basis = self.basis_matrix().augment(smzero).stack( mszero.augment(M.basis_matrix())) ipm = ambient.inner_product_matrix() return FreeQuadraticModule_integer_symmetric(ambient=ambient, basis=basis, inner_product_matrix=ipm, already_echelonized=False)
def direct_sum(self, M): r""" 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 Basis matrix: [1 0] [0 1] Inner product matrix: [1 0] [0 1] """ IM = matrix.block_diagonal([self.inner_product_matrix(), M.inner_product_matrix()]) ambient = FreeQuadraticModule(ZZ, self.degree() + M.degree(), IM) smzero = matrix.zero(self.rank(), M.degree()) mszero = matrix.zero(M.rank(), self.degree()) basis = self.basis_matrix().augment(smzero).stack( mszero.augment(M.basis_matrix())) ipm = ambient.inner_product_matrix() return FreeQuadraticModule_integer_symmetric(ambient=ambient, basis=basis, inner_product_matrix=ipm, already_echelonized=False)
def TorsionQuadraticForm(q): r""" 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]) sage: TorsionQuadraticForm(q1) 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]) sage: TorsionQuadraticForm(q2) Finite quadratic module over Integer Ring with invariants (12,) Gram matrix of the quadratic form with values in Q/Z: [7/12] TESTS:: sage: TorsionQuadraticForm(matrix.diagonal([3/4,3/8,3/8])) Finite quadratic module over Integer Ring with invariants (4, 8, 8) Gram matrix of the quadratic form with values in Q/2Z: [3/4 0 0] [ 0 3/8 0] [ 0 0 3/8] """ q = matrix(QQ, q) if q.nrows() != q.ncols(): raise ValueError("the input must be a square matrix") if q != q.transpose(): raise ValueError("the input must be a symmetric matrix") Q, d = q._clear_denom() S, U, V = Q.smith_form() D = U * q * V Q = FreeQuadraticModule(ZZ, q.ncols(), inner_product_matrix=d**2 * q) denoms = [D[i, i].denominator() for i in range(D.ncols())] rels = Q.span(diagonal_matrix(ZZ, denoms) * U) return TorsionQuadraticModule((1/d)*Q, (1/d)*rels, modulus=1)
def twist(self, s): r""" 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] """ s = self.base_ring().fraction_field()(s) n = self.V().degree() inner_product_matrix = s * self.V().inner_product_matrix() ambient = FreeQuadraticModule(self.base_ring(), n, inner_product_matrix) V = ambient.span(self.V().basis()) W = ambient.span(self.W().basis()) return TorsionQuadraticModule(V, W)
def TorsionQuadraticForm(q): r""" 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]) sage: TorsionQuadraticForm(q1) 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]) sage: TorsionQuadraticForm(q2) Finite quadratic module over Integer Ring with invariants (12,) Gram matrix of the quadratic form with values in Q/Z: [7/12] """ q = matrix(QQ, q) if q.nrows() != q.ncols(): raise ValueError("the input must be a square matrix") if q != q.transpose(): raise ValueError("the input must be a symmetric matrix") Q, d = q._clear_denom() S, U, V = Q.smith_form() D = U * q * V Q = FreeQuadraticModule(ZZ, q.ncols(), inner_product_matrix=d**2 * q) denoms = [D[i,i].denominator() for i in range(D.ncols())] rels = Q.span(diagonal_matrix(ZZ, denoms) * U) return TorsionQuadraticModule((1/d)*Q, (1/d)*rels)
def IntegralLattice(inner_product_matrix, basis=None): r""" Return the integral lattice spanned by ``basis`` in the ambient space. A lattice is a finitely generated free abelian group `L \cong \Z^r` equipped with a non-degenerate, symmetric bilinear form `L \times L \colon \rightarrow \Z`. Here, lattices have an ambient quadratic space `\Q^n` and a distinguished basis. INPUT: - ``inner_product_matrix`` -- a symmetric matrix over the rationals - ``basis`` -- a list of elements of ambient or a matrix 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: from sage.modules.free_quadratic_module_integer_symmetric import IntegralLattice sage: IntegralLattice(Matrix(ZZ,2,2,[0,1,1,0])) Lattice of degree 2 and rank 2 over Integer Ring Basis matrix: [1 0] [0 1] Inner product matrix: [0 1] [1 0] We can specify a basis as well:: sage: IntegralLattice(Matrix(ZZ,2,2,[0,1,1,0]),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] """ if basis is None: basis = matrix.identity(ZZ, inner_product_matrix.ncols()) if inner_product_matrix != inner_product_matrix.transpose(): raise ValueError("Argument inner_product_matrix must be symmetric\n%s" % inner_product_matrix) A = FreeQuadraticModule(ZZ, inner_product_matrix.ncols(), inner_product_matrix=inner_product_matrix) return FreeQuadraticModule_integer_symmetric(ambient=A, basis=basis, inner_product_matrix=inner_product_matrix, already_echelonized=False)
def IntegralLattice(data, basis=None): r""" 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 :class:`CartanMatrix` (see also :mod:`Cartan types <sage.combinat.root_system.cartan_type>`) -- 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 Basis matrix: [1 0] [0 1] 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 Basis matrix: [1 0 0] [0 1 0] [0 0 1] Inner product matrix: [1 0 0] [0 1 0] [0 0 1] 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] Inner product matrix: [1 0 0] [0 1 0] [0 0 1] 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 Basis matrix: [1 0] [0 1] Inner product matrix: [0 1] [1 0] sage: L1 == IntegralLattice("H") True We can construct root lattices by specifying their type (see :mod:`Cartan types <sage.combinat.root_system.cartan_type>` and :class:`CartanMatrix`):: sage: IntegralLattice(["E", 7]) Lattice of degree 7 and rank 7 over Integer Ring Basis matrix: [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 1] 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 Basis matrix: [1 0] [0 1] Inner product matrix: [ 2 -1] [-1 2] sage: IntegralLattice("D3") Lattice of degree 3 and rank 3 over Integer Ring Basis matrix: [1 0 0] [0 1 0] [0 0 1] 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 Basis matrix: [1 0 0 0] [0 1 0 0] [0 0 1 0] [0 0 0 1] 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] Inner product matrix: [1 0 0 0] [0 1 0 0] [0 0 1 0] [0 0 0 1] 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] TESTS:: sage: IntegralLattice(["A", 1, 1]) Traceback (most recent call last): ... ValueError: lattices must be nondegenerate; use FreeQuadraticModule instead sage: IntegralLattice(["D", 3, 1]) Traceback (most recent call last): ... ValueError: lattices must be nondegenerate; use FreeQuadraticModule instead """ if is_Matrix(data): inner_product_matrix = data elif isinstance(data, Integer): inner_product_matrix = matrix.identity(ZZ, data) elif data == "U" or data == "H": inner_product_matrix = matrix([[0, 1], [1, 0]]) else: inner_product_matrix = CartanMatrix(data) if basis is None: basis = matrix.identity(ZZ, inner_product_matrix.ncols()) if inner_product_matrix != inner_product_matrix.transpose(): raise ValueError("the inner product matrix must be symmetric\n%s" % inner_product_matrix) A = FreeQuadraticModule(ZZ, inner_product_matrix.ncols(), inner_product_matrix=inner_product_matrix) return FreeQuadraticModule_integer_symmetric( ambient=A, basis=basis, inner_product_matrix=A.inner_product_matrix(), already_echelonized=False)
def IntegralLatticeDirectSum(Lattices, return_embeddings=False): r""" 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 TESTS:: sage: IntegralLatticeDirectSum([IntegralLattice("D4")]) Lattice of degree 4 and rank 4 over Integer Ring Basis matrix: [1 0 0 0] [0 1 0 0] [0 0 1 0] [0 0 0 1] Inner product matrix: [ 2 -1 0 0] [-1 2 -1 -1] [ 0 -1 2 0] [ 0 -1 0 2] sage: L1 = IntegralLattice(2 * matrix.identity(2), [[1/2, 1/2]]) sage: L2 = IntegralLattice("A3", [[1, 1, 2]]) sage: [L, phi] = IntegralLatticeDirectSum([L1, L2], True) sage: L Lattice of degree 5 and rank 2 over Integer Ring Basis matrix: [1/2 1/2 0 0 0] [ 0 0 1 1 2] Inner product matrix: [ 2 0 0 0 0] [ 0 2 0 0 0] [ 0 0 2 -1 0] [ 0 0 -1 2 -1] [ 0 0 0 -1 2] """ for L in Lattices: if not isinstance(L, FreeQuadraticModule_integer_symmetric): raise ValueError("Lattices must be a list of lattices") N = len(Lattices) dims = [L_i.dimension() for L_i in Lattices] degrees = [L_i.degree() for L_i in Lattices] degree_tot = sum(degrees) sum_degree = [sum(degrees[:i]) for i in range(N + 1)] inner_product_list = [copy(L_i.inner_product_matrix()) for L_i in Lattices] IM = matrix.block_diagonal(inner_product_list) ambient = FreeQuadraticModule(ZZ, degree_tot, inner_product_matrix=IM) basis = [ matrix.block(1, 3, [ matrix.zero(dims[i], sum_degree[i]), Lattices[i].basis_matrix(), matrix.zero(dims[i], sum_degree[-1] - sum_degree[i + 1]) ]) for i in range(N) ] basis_matrix = matrix.block(N, 1, basis) ipm = ambient.inner_product_matrix() direct_sum = FreeQuadraticModule_integer_symmetric( ambient=ambient, basis=basis_matrix, inner_product_matrix=ipm, already_echelonized=False) if not return_embeddings: return direct_sum sum_dims = [sum(dims[:i]) for i in range(N + 1)] phi = [ Lattices[i].hom(direct_sum.basis()[sum_dims[i]:sum_dims[i + 1]]) for i in range(N) ] return [direct_sum, phi]
def twist(self, s, discard_basis=False): r""" 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 Basis matrix: [1 0 0 0] [0 1 0 0] [0 0 1 0] [0 0 0 1] 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] Inner product matrix: [1 0 0] [0 1 0] [0 0 1] sage: L.twist(1) Lattice of degree 3 and rank 2 over Integer Ring Basis matrix: [2 1 0] [0 1 1] Inner product matrix: [1 0 0] [0 1 0] [0 0 1] sage: L.twist(1, True) Lattice of degree 2 and rank 2 over Integer Ring Basis matrix: [1 0] [0 1] Inner product matrix: [5 1] [1 2] """ try: s = self.base_ring()(s) except TypeError: raise ValueError( "the scaling factor must be an element of the base ring.") if s == 0: raise ValueError("the scaling factor must be non zero") if discard_basis: return IntegralLattice(s * self.gram_matrix()) else: n = self.degree() inner_product_matrix = s * self.inner_product_matrix() ambient = FreeQuadraticModule(self.base_ring(), n, inner_product_matrix) return FreeQuadraticModule_integer_symmetric( ambient=ambient, basis=self.basis(), inner_product_matrix=inner_product_matrix)
def tensor_product(self, other, discard_basis=False): r""" 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 Basis matrix: [1 0 0 0] [0 1 0 0] [0 0 1 0] [0 0 0 1] Inner product matrix: [ 36 -12 -12 4] [-12 24 4 -8] [-12 4 24 -8] [ 4 -8 -8 16] """ if not isinstance(other, FreeQuadraticModule_integer_symmetric): raise ValueError("other (=%s) must be an integral lattice" % other) if discard_basis: gram_matrix = self.gram_matrix().tensor_product( other.gram_matrix()) return IntegralLattice(gram_matrix) else: inner_product_matrix = self.inner_product_matrix().tensor_product( other.inner_product_matrix()) basis_matrix = self.basis_matrix().tensor_product( other.basis_matrix()) n = self.degree() m = other.degree() ambient = FreeQuadraticModule(self.base_ring(), m * n, inner_product_matrix) return FreeQuadraticModule_integer_symmetric( ambient=ambient, basis=basis_matrix, inner_product_matrix=ambient.inner_product_matrix())
def IntegralLattice(data, basis=None): r""" 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 a euclidian lattice * a symmetric Cartan type or anything recognized by :class:`CartanMatrix` (see also :mod:`Cartan types <sage.combinat.root_system.cartan_type>`) -- 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 Basis matrix: [1 0] [0 1] 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 a Euclidian lattice just by its dimension:: sage: IntegralLattice(3) Lattice of degree 3 and rank 3 over Integer Ring Basis matrix: [1 0 0] [0 1 0] [0 0 1] Inner product matrix: [1 0 0] [0 1 0] [0 0 1] Here is an example of the `A_2` root lattice in Euclidian 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] Inner product matrix: [1 0 0] [0 1 0] [0 0 1] 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 Basis matrix: [1 0] [0 1] Inner product matrix: [0 1] [1 0] sage: L1 == IntegralLattice("H") True We can construct root lattices by specifying their type (see :mod:`Cartan types <sage.combinat.root_system.cartan_type>` and :class:`CartanMatrix`):: sage: IntegralLattice(["E", 7]) Lattice of degree 7 and rank 7 over Integer Ring Basis matrix: [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 1] 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 Basis matrix: [1 0] [0 1] Inner product matrix: [ 2 -1] [-1 2] sage: IntegralLattice("D3") Lattice of degree 3 and rank 3 over Integer Ring Basis matrix: [1 0 0] [0 1 0] [0 0 1] 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 Basis matrix: [1 0 0 0] [0 1 0 0] [0 0 1 0] [0 0 0 1] 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] Inner product matrix: [1 0 0 0] [0 1 0 0] [0 0 1 0] [0 0 0 1] 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] TESTS:: sage: IntegralLattice(["A", 1, 1]) Traceback (most recent call last): ... ValueError: lattices must be nondegenerate; use FreeQuadraticModule instead sage: IntegralLattice(["D", 3, 1]) Traceback (most recent call last): ... ValueError: lattices must be nondegenerate; use FreeQuadraticModule instead """ if is_Matrix(data): inner_product_matrix = data elif isinstance(data, Integer): inner_product_matrix = matrix.identity(ZZ, data) elif data == "U" or data == "H": inner_product_matrix = matrix([[0,1],[1,0]]) else: inner_product_matrix = CartanMatrix(data) if basis is None: basis = matrix.identity(ZZ, inner_product_matrix.ncols()) if inner_product_matrix != inner_product_matrix.transpose(): raise ValueError("the inner product matrix must be symmetric\n%s" % inner_product_matrix) A = FreeQuadraticModule(ZZ, inner_product_matrix.ncols(), inner_product_matrix=inner_product_matrix) return FreeQuadraticModule_integer_symmetric(ambient=A, basis=basis, inner_product_matrix=A.inner_product_matrix(), already_echelonized=False)
def IntegralLatticeDirectSum(Lattices, return_embeddings=False): r""" 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 TESTS:: sage: IntegralLatticeDirectSum([IntegralLattice("D4")]) Lattice of degree 4 and rank 4 over Integer Ring Basis matrix: [1 0 0 0] [0 1 0 0] [0 0 1 0] [0 0 0 1] Inner product matrix: [ 2 -1 0 0] [-1 2 -1 -1] [ 0 -1 2 0] [ 0 -1 0 2] sage: L1 = IntegralLattice(2 * matrix.identity(2), [[1/2, 1/2]]) sage: L2 = IntegralLattice("A3", [[1, 1, 2]]) sage: [L, phi] = IntegralLatticeDirectSum([L1, L2], True) sage: L Lattice of degree 5 and rank 2 over Integer Ring Basis matrix: [1/2 1/2 0 0 0] [ 0 0 1 1 2] Inner product matrix: [ 2 0 0 0 0] [ 0 2 0 0 0] [ 0 0 2 -1 0] [ 0 0 -1 2 -1] [ 0 0 0 -1 2] """ for L in Lattices: if not isinstance(L, FreeQuadraticModule_integer_symmetric): raise ValueError("Lattices must be a list of lattices") N = len(Lattices) dims = [L_i.dimension() for L_i in Lattices] degrees = [L_i.degree() for L_i in Lattices] dim_tot = sum(dims) degree_tot = sum(degrees) sum_degree = [sum(degrees[:i]) for i in range(N+1)] inner_product_list = [copy(L_i.inner_product_matrix()) for L_i in Lattices] IM = matrix.block_diagonal(inner_product_list) ambient = FreeQuadraticModule(ZZ, degree_tot, inner_product_matrix=IM) basis = [matrix.block(1, 3, [matrix.zero(dims[i], sum_degree[i]), Lattices[i].basis_matrix(), matrix.zero(dims[i], sum_degree[-1] - sum_degree[i+1]) ]) for i in range(N)] basis_matrix = matrix.block(N, 1, basis) ipm = ambient.inner_product_matrix() direct_sum = FreeQuadraticModule_integer_symmetric(ambient=ambient, basis=basis_matrix, inner_product_matrix=ipm, already_echelonized=False) if not return_embeddings: return direct_sum sum_dims = [sum(dims[:i]) for i in range(N+1)] phi = [Lattices[i].hom(direct_sum.basis()[sum_dims[i]:sum_dims[i+1]]) for i in range(N)] return [direct_sum, phi]
def tensor_product(self, other, discard_basis=False): r""" 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 Basis matrix: [1 0 0 0] [0 1 0 0] [0 0 1 0] [0 0 0 1] Inner product matrix: [ 36 -12 -12 4] [-12 24 4 -8] [-12 4 24 -8] [ 4 -8 -8 16] """ if not isinstance(other, FreeQuadraticModule_integer_symmetric): raise ValueError("other (=%s) must be an integral lattice" % other) if discard_basis: gram_matrix = self.gram_matrix().tensor_product(other.gram_matrix()) return IntegralLattice(gram_matrix) else: inner_product_matrix = self.inner_product_matrix().tensor_product(other.inner_product_matrix()) basis_matrix = self.basis_matrix().tensor_product(other.basis_matrix()) n = self.degree() m = other.degree() ambient = FreeQuadraticModule(self.base_ring(), m * n, inner_product_matrix) return FreeQuadraticModule_integer_symmetric(ambient=ambient, basis=basis_matrix, inner_product_matrix=ambient.inner_product_matrix())