예제 #1
0
    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)
예제 #3
0
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)
예제 #4
0
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]
예제 #5
0
    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())