Пример #1
0
    def __init__(self, B):
        r"""
        Initialize the ComputeMinimalPolynomials class.

        INPUT:

        - ``B`` -- a square matrix

        TESTS::

            sage: from sage.matrix.compute_J_ideal import ComputeMinimalPolynomials
            sage: ComputeMinimalPolynomials(matrix([[1, 2]]))
            Traceback (most recent call last):
            ...
            TypeError: square matrix required
        """
        from sage.rings.polynomial.polynomial_ring import polygen

        super(ComputeMinimalPolynomials, self).__init__()
        if not B.is_square():
            raise TypeError("square matrix required")

        self._B = B
        self._D = B.base_ring()
        X = polygen(self._D)
        adjoint = (X - B).adjoint()
        d = B.nrows()**2
        b = matrix(d, 1, adjoint.list())
        self.chi_B = B.charpoly(X)
        self.mu_B = B.minimal_polynomial()
        self._A = matrix.block([[b , -self.chi_B*matrix.identity(d)]])
        self._DX = X.parent()
        self._cache = {}
Пример #2
0
    def __init__(self, B):
        r"""
        Initialize the ComputeMinimalPolynomials class.

        INPUT:

        - ``B`` -- a square matrix

        TESTS::

            sage: from sage.matrix.compute_J_ideal import ComputeMinimalPolynomials
            sage: ComputeMinimalPolynomials(matrix([[1, 2]]))
            Traceback (most recent call last):
            ...
            TypeError: square matrix required
        """
        from sage.rings.polynomial.polynomial_ring import polygen

        super(ComputeMinimalPolynomials, self).__init__()
        if not B.is_square():
            raise TypeError("square matrix required")

        self._B = B
        self._D = B.base_ring()
        X = polygen(self._D)
        adjoint = (X - B).adjoint()
        d = B.nrows()**2
        b = matrix(d, 1, adjoint.list())
        self.chi_B = B.charpoly(X)
        self.mu_B = B.minimal_polynomial()
        self._A = matrix.block([[b, -self.chi_B * matrix.identity(d)]])
        self._DX = X.parent()
        self._cache = {}
        def _symmetric_form_matrix(self):
            r"""
            Return the matrix for the symmetric form `( | )` in
            the weight lattice basis.

            Let `A` be a symmetrizable Cartan matrix with symmetrizer `D`,.
            This returns the matrix `M^t DA M`, where `M` is dependent upon
            the type given below.

            In finite types, `M` is the inverse of the Cartan matrix.

            In affine types, `M` takes the basis
            `(\Lambda_0, \Lambda_1, \ldots, \Lambda_r, \delta)` to
            `(\alpha_0, \ldots, \alpha_r, \Lambda_0)` where `r` is the
            rank of ``self``.

            This is used in computing the symmetric form for affine
            root systems.

            EXAMPLES::

                sage: P = RootSystem(['C',2]).weight_lattice()
                sage: P._symmetric_form_matrix
                [1 1]
                [1 2]

                sage: P = RootSystem(['C',2,1]).weight_lattice()
                sage: P._symmetric_form_matrix
                [0 0 0 1]
                [0 1 1 1]
                [0 1 2 1]
                [1 1 1 0]

                sage: P = RootSystem(['A',4,2]).weight_lattice()
                sage: P._symmetric_form_matrix
                [  0   0   0 1/2]
                [  0   2   2   1]
                [  0   2   4   1]
                [1/2   1   1   0]
            """
            from sage.matrix.constructor import matrix
            ct = self.cartan_type()
            cm = ct.cartan_matrix()
            if cm.det() != 0:
                cm_inv = cm.inverse()
                diag = cm.is_symmetrizable(True)
                return cm_inv.transpose() * matrix.diagonal(diag)

            if not ct.is_affine():
                raise ValueError("only implemented for affine types when the"
                                 " Cartan matrix is singular")

            r = ct.rank()
            a = ct.a()
            # Determine the change of basis matrix
            # La[0], ..., La[r], delta -> al[0], ..., al[r], La[0]
            M = cm.stack( matrix([1] + [0]*(r-1)) )
            M = matrix.block([[ M, matrix([[1]] + [[0]]*r) ]])
            M = M.inverse()

            if a[0] != 1:
                from sage.rings.all import QQ
                S = matrix([~a[0]]+[0]*(r-1))
                A = cm.symmetrized_matrix().change_ring(QQ).stack(S)
            else:
                A = cm.symmetrized_matrix().stack(matrix([1]+[0]*(r-1)))
            A = matrix.block([[A, matrix([[~a[0]]] + [[0]]*r)]])
            return M.transpose() * A * M
Пример #4
0
    def p_minimal_polynomials(self, p, s_max=None):
        r"""
        Compute `(p^s)`-minimal polynomials `\nu_s` of `B`.

        Compute a finite subset `\mathcal{S}` of the positive
        integers and `(p^s)`-minimal polynomials
        `\nu_s` for `s \in \mathcal{S}`.

        For `0 < t \le \max \mathcal{S}`, a `(p^t)`-minimal polynomial is
        given by `\nu_s` where
        `s = \min\{ r \in \mathcal{S} \mid r\ge t \}`.
        For `t > \max \mathcal{S}`, the minimal polynomial of `B` is
        also a `(p^t)`-minimal polynomial.

        INPUT:

        - ``p`` -- a prime in `D`

        - ``s_max`` -- a positive integer (default: ``None``); if set, only
          `(p^s)`-minimal polynomials for ``s <= s_max`` are computed
          (see below for details)

        OUTPUT:

        A dictionary. Keys are the finite set `\mathcal{S}`, the values
        are the associated `(p^s)`-minimal polynomials `\nu_s`,
        `s \in \mathcal{S}`.

        Setting ``s_max`` only affects the output if ``s_max`` is at
        most `\max\mathcal{S}` where `\mathcal{S}` denotes the full
        set. In that case, only those `\nu_s` with ``s <= s_max`` are
        returned where ``s_max`` is always included even if it is not
        included in the full set `\mathcal{S}`.

        EXAMPLES::

            sage: from sage.matrix.compute_J_ideal import ComputeMinimalPolynomials
            sage: B = matrix(ZZ, [[1, 0, 1], [1, -2, -1], [10, 0, 0]])
            sage: C = ComputeMinimalPolynomials(B)
            sage: C.p_minimal_polynomials(2)
            {2: x^2 + 3*x + 2}
            sage: set_verbose(1)
            sage: C = ComputeMinimalPolynomials(B)
            sage: C.p_minimal_polynomials(2)
            verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials)
            ------------------------------------------
            verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials)
            p = 2, t = 1:
            verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials)
            Result of lifting:
            verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials)
            F =
            verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials)
            [x^2 + x]
            [      x]
            [      0]
            [      1]
            [      1]
            [  x + 1]
            [      1]
            [      0]
            [      0]
            [  x + 1]
            verbose 1 (...: compute_J_ideal.py, current_nu)
            ------------------------------------------
            verbose 1 (...: compute_J_ideal.py, current_nu)
            (x^2 + x)
            verbose 1 (...: compute_J_ideal.py, current_nu)
            Generators with (p^t)-generating property:
            verbose 1 (...: compute_J_ideal.py, current_nu)
            [x^2 + x]
            verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials)
            nu = x^2 + x
            verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials)
            corresponding columns for G
            verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials)
            [x^2 + x]
            [  x + 2]
            [      0]
            [      1]
            [      1]
            [  x - 1]
            [     -1]
            [     10]
            [      0]
            [  x + 1]
            verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials)
            ------------------------------------------
            verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials)
            p = 2, t = 2:
            verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials)
            Result of lifting:
            verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials)
            F =
            verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials)
            [  2*x^2 + 2*x x^2 + 3*x + 2]
            [          2*x         x + 4]
            [            0             0]
            [            2             1]
            [            2             1]
            [      2*x + 2         x + 1]
            [            2            -1]
            [            0            10]
            [            0             0]
            [      2*x + 2         x + 3]
            verbose 1 (...: compute_J_ideal.py, current_nu)
            ------------------------------------------
            verbose 1 (...: compute_J_ideal.py, current_nu)
            (2*x^2 + 2*x, x^2 + 3*x + 2)
            verbose 1 (...: compute_J_ideal.py, current_nu)
            Generators with (p^t)-generating property:
            verbose 1 (...: compute_J_ideal.py, current_nu)
            [x^2 + 3*x + 2]
            verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials)
            nu = x^2 + 3*x + 2
            verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials)
            corresponding columns for G
            verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials)
            [x^2 + 3*x + 2]
            [        x + 4]
            [            0]
            [            1]
            [            1]
            [        x + 1]
            [           -1]
            [           10]
            [            0]
            [        x + 3]
            verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials)
            ------------------------------------------
            verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials)
            p = 2, t = 3:
            verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials)
            Result of lifting:
            verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials)
            F =
            verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials)
            [x^3 + 7*x^2 + 6*x x^3 + 3*x^2 + 2*x]
            [        x^2 + 8*x         x^2 + 4*x]
            [                0                 0]
            [                x             x + 4]
            [            x + 4                 x]
            [    x^2 + 5*x + 4           x^2 + x]
            [           -x + 4                -x]
            [             10*x              10*x]
            [                0                 0]
            [        x^2 + 7*x     x^2 + 3*x + 4]
            verbose 1 (...: compute_J_ideal.py, current_nu)
            ------------------------------------------
            verbose 1 (...: compute_J_ideal.py, current_nu)
            (x^3 + 7*x^2 + 6*x, x^3 + 3*x^2 + 2*x)
            verbose 1 (...: compute_J_ideal.py, current_nu)
            Generators with (p^t)-generating property:
            verbose 1 (...: compute_J_ideal.py, current_nu)
            [x^3 + 7*x^2 + 6*x, x^3 + 3*x^2 + 2*x]
            verbose 1 (...: compute_J_ideal.py, current_nu)
            [x^3 + 3*x^2 + 2*x]
            verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials)
            nu = x^3 + 3*x^2 + 2*x
            {2: x^2 + 3*x + 2}
            sage: set_verbose(0)
            sage: C.p_minimal_polynomials(2, s_max=1)
            {1: x^2 + x}
            sage: C.p_minimal_polynomials(2, s_max=2)
            {2: x^2 + 3*x + 2}
            sage: C.p_minimal_polynomials(2, s_max=3)
            {2: x^2 + 3*x + 2}

        ALGORITHM:

        [HR2016]_, Algorithm 5.
        """
        from sage.misc.misc import verbose
        from sage.rings.infinity import Infinity

        deg_mu = self.mu_B.degree()
        if s_max is None:
            s_max = Infinity

        if p in self._cache:
            (t, G, p_min_polys) = self._cache[p]
            if t < Infinity:
                nu = G[0][0]
        else:
            t = 0
            p_min_polys = {}
            nu = self._DX(1)
            d = self._A.ncols()
            G = matrix(self._DX, d, 0)

        while t < s_max:
            deg_prev_nu = nu.degree()
            t += 1
            verbose("------------------------------------------")
            verbose("p = %s, t = %s:" % (p, t))

            verbose("Result of lifting:")
            verbose("F =")
            F = lifting(p, t, self._A, G)
            verbose(F)

            nu = self.current_nu(p, t, F[0], nu)

            verbose("nu = %s" % nu)
            if nu.degree() >= deg_mu:
                t = Infinity
                break

            if nu.degree() == deg_prev_nu:
                G = G.delete_columns([G.ncols() - 1])
                del p_min_polys[t - 1]

            column = self.mccoy_column(p, t, nu)
            verbose("corresponding columns for G")
            verbose(column)

            G = matrix.block([[p * G, column]])
            p_min_polys[t] = nu

        self._cache[p] = (t, G, p_min_polys)

        if s_max < t:
            result = {
                r: polynomial
                for r, polynomial in iteritems(p_min_polys) if r < s_max
            }
            next_t_candidates = list(r for r in p_min_polys if r >= s_max)
            if next_t_candidates:
                next_t = min(next_t_candidates)
                result.update({s_max: p_min_polys[next_t] % p**s_max})

            return result

        return p_min_polys
Пример #5
0
def lifting(p, t, A, G):
    r"""
    Compute generators of `\{f \in D[X]^d \mid Af \equiv 0 \pmod{p^{t}}\}` given
    generators of `\{f\in D[X]^d \mid Af \equiv 0\pmod{p^{t-1}}\}`.

    INPUT:

    - ``p`` -- a prime element of some principal ideal domain `D`

    - ``t`` -- a non-negative integer

    - ``A`` -- a `c\times d` matrix over `D[X]`

    - ``G`` -- a matrix over `D[X]`. The columns of
      `\begin{pmatrix}p^{t-1}I& G\end{pmatrix}` are generators
      of `\{ f\in D[X]^d \mid Af \equiv 0\pmod{p^{t-1}}\}`;
      can be set to ``None`` if ``t`` is zero

    OUTPUT:

    A matrix `F` over `D[X]` such that the columns of
    `\begin{pmatrix}p^tI&F&pG\end{pmatrix}` are generators of
    `\{ f\in D[X]^d \mid Af \equiv 0\pmod{p^t}\}`.

    EXAMPLES::

        sage: from sage.matrix.compute_J_ideal import lifting
        sage: X = polygen(ZZ, 'X')
        sage: A = matrix([[1, X], [2*X, X^2]])
        sage: G0 = lifting(5, 0, A, None)
        sage: G1 = lifting(5, 1, A, G0); G1
        []
        sage: (A*G1 % 5).is_zero()
        True
        sage: A = matrix([[1, X, X^2], [2*X, X^2, 3*X^3]])
        sage: G0 = lifting(5, 0, A, None)
        sage: G1 = lifting(5, 1, A, G0); G1
        [3*X^2]
        [    X]
        [    1]
        sage: (A*G1 % 5).is_zero()
        True
        sage: G2 = lifting(5, 2, A, G1); G2
        [15*X^2 23*X^2]
        [   5*X      X]
        [     5      1]
        sage: (A*G2 % 25).is_zero()
        True
        sage: lifting(5, 10, A, G1)
        Traceback (most recent call last):
        ...
        ValueError: A*G not zero mod 5^9

    ALGORITHM:

    [HR2016]_, Algorithm 1.

    TESTS::

        sage: A = matrix([[1, X], [X, X^2]])
        sage: G0 = lifting(5, 0, A, None)
        sage: G1 = lifting(5, 1, A, G0); G1
        Traceback (most recent call last):
        ...
        ValueError: [  1   X|]
        [  X X^2|] does not have full rank.
    """
    from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing

    DX = A.parent().base()
    (X, ) = DX.variable_names()
    D = DX.base_ring()
    d = A.ncols()
    c = A.nrows()

    if t == 0:
        return matrix(DX, d, 0)

    if not (A * G % p**(t - 1)).is_zero():
        raise ValueError("A*G not zero mod %s^%s" % (p, t - 1))

    R = A * G / p**(t - 1)
    R.change_ring(DX)

    AR = matrix.block([[A, R]])
    Fp = D.quotient(p * D)
    FpX = PolynomialRing(Fp, name=X)

    ARb = AR.change_ring(FpX)
    (Db, Sb, Tb) = ARb.smith_form()
    #assert Sb * ARb * Tb == Db
    #assert all(i == j or Db[i, j].is_zero()
    #           for i in range(Db.nrows())
    #           for j in range(Db.ncols()))

    r = Db.rank()
    if r != c:
        raise ValueError("{} does not have full rank.".format(ARb))

    T = Tb.change_ring(DX)

    F1 = matrix.block([[p**(t - 1) * matrix.identity(d), G]]) * T
    F = F1.matrix_from_columns(range(r, F1.ncols()))
    assert (A * F % (p**t)).is_zero(), "A*F=%s" % (A * F)

    return F
Пример #6
0
        def _symmetric_form_matrix(self):
            r"""
            Return the matrix for the symmetric form `( | )` in
            the weight lattice basis.

            Let `A` be a symmetrizable Cartan matrix with symmetrizer `D`,.
            This returns the matrix `M^t DA M`, where `M` is dependent upon
            the type given below.

            In finite types, `M` is the inverse of the Cartan matrix.

            In affine types, `M` takes the basis
            `(\Lambda_0, \Lambda_1, \ldots, \Lambda_r, \delta)` to
            `(\alpha_0, \ldots, \alpha_r, \Lambda_0)` where `r` is the
            rank of ``self``.

            This is used in computing the symmetric form for affine
            root systems.

            EXAMPLES::

                sage: P = RootSystem(['C',2]).weight_lattice()
                sage: P._symmetric_form_matrix
                [1 1]
                [1 2]

                sage: P = RootSystem(['C',2,1]).weight_lattice()
                sage: P._symmetric_form_matrix
                [0 0 0 1]
                [0 1 1 1]
                [0 1 2 1]
                [1 1 1 0]

                sage: P = RootSystem(['A',4,2]).weight_lattice()
                sage: P._symmetric_form_matrix
                [  0   0   0 1/2]
                [  0   2   2   1]
                [  0   2   4   1]
                [1/2   1   1   0]
            """
            from sage.matrix.constructor import matrix
            ct = self.cartan_type()
            cm = ct.cartan_matrix()
            if cm.det() != 0:
                cm_inv = cm.inverse()
                diag = cm.is_symmetrizable(True)
                return cm_inv.transpose() * matrix.diagonal(diag)

            if not ct.is_affine():
                raise ValueError("only implemented for affine types when the"
                                 " Cartan matrix is singular")

            r = ct.rank()
            a = ct.a()
            # Determine the change of basis matrix
            # La[0], ..., La[r], delta -> al[0], ..., al[r], La[0]
            M = cm.stack(matrix([1] + [0] * (r - 1)))
            M = matrix.block([[M, matrix([[1]] + [[0]] * r)]])
            M = M.inverse()

            if a[0] != 1:
                from sage.rings.all import QQ
                S = matrix([~a[0]] + [0] * (r - 1))
                A = cm.symmetrized_matrix().change_ring(QQ).stack(S)
            else:
                A = cm.symmetrized_matrix().stack(matrix([1] + [0] * (r - 1)))
            A = matrix.block([[A, matrix([[~a[0]]] + [[0]] * r)]])
            return M.transpose() * A * M
Пример #7
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]
Пример #8
0
def abnormal_factor_system(L, Q, r, m, s, homogeneous=True):
    r"""
    Return a matrix for the linear system to find `Q` as a common factor
    in the abnormal polynomials of layer `m` in rank `r`.

    INPUT:

    - ``L`` -- the Lie algebra to do the computations in
    - ``Q`` -- the polynomial to look for as a common factor
    - ``r`` -- the rank of the Lie algebra
    - ``m`` -- the layer of abnormal polynomials to search
    - ``s`` -- the step to search in
    - ``homogeneous`` -- a boolean; if ``True`` only the highest order component
      of the matrix is computed. Otherwise the full system is computed.

    OUTPUT:

    A pair (L,A), where `L` is the ambient free Lie algebra quotient and
    `A` is a block matrix of coefficients of the linear system. The variables
    are ordered so that the first block has `\dim f_r^{s}` columns describing
    the covector and the rest are the auxiliary multiplier polynomial variables.
    """
    PR = Q.parent()
    weights = PR.term_order().weights()

    # list all relevant monomials up to degree s-m-deg(Q)
    def monomial_list(deg):
        return reversed(list(WeightedIntegerVectors(deg, weights)))

    if homogeneous:
        mons = [tuple(mon) for mon in monomial_list(s - m)]
        smons = [PR.monomial(*mon) for mon in monomial_list(s - m - Q.degree())]
        # extract the maximal degree monomials of the polynomial Q
        qmons = [qm for qm in Q.monomials() if qm.degree() == Q.degree()]
        P = abnormal_polynomials(L, r, m, s)
        supp_basis = [X.leading_support() for X in L.graded_basis(s)]
    else:
        mons = [tuple(mon) for k in range(s - m + 1)
                           for mon in monomial_list(k)]
        smons = [PR.monomial(*mon) for k in range(s - m - Q.degree() + 1)
                                   for mon in monomial_list(k)]
        qmons = Q.monomials()
        P = None
        for k in range(m, s + 1):
            Pk = abnormal_polynomials(L, r, m, k)
            if not P:
                # make a copy to not ruin cache
                P = deepcopy(Pk)
                continue
            for X, PX in Pk.items():
                for Y, c in PX.items():
                    P[X][Y] = c
        supp_basis = [X.leading_support() for k in range(m, s + 1)
                      for X in L.graded_basis(k)]

    def lie_elem_to_dict(X):
        mc = X.monomial_coefficients()
        return {supp_basis.index(m): c for m, c in mc.items()}

    # form the linear system as a sparse matrix
    typestr = "homogeneous" if homogeneous else "full"
    verbose("forming %s linear system in step %d" % (typestr, s))
    covec_block = {}
    aux_blocks = {X: {} for X in P}
    i = 0
    for mon in mons:
        pmon = PR.monomial(*mon)

        # S parameters are all coefficients of monomials
        sc = {}
        for qmon in qmons:
            q, r = pmon.quo_rem(qmon)
            if r.is_zero() and q.degree() <= s - m - Q.degree():
                c = Q.monomial_coefficient(qmon)
                if q in smons:
                    sc[smons.index(q)] = -c

        for X, PX in P.items():
            # compute the coefficient of mon in P_X - S_X * Q
            # as a row vector of the coefficients of the P and S params

            # P parameters are the covector
            pc = PX.get(tuple(mon), L.zero())
            for j, c in lie_elem_to_dict(pc).items():
                covec_block[(i, j)] = c

            for j, c in sc.items():
                aux_blocks[X][(i, j)] = c

            i += 1

    numrows = i
    verbose("%d equations, %d covector vars, %d x %d auxiliary vars" % (numrows,
            len(supp_basis), len(P), len(smons)))
    R = PR.base_ring()
    covecmat = matrix(R, numrows, len(supp_basis), covec_block)
    auxmats = [matrix(R, numrows, len(smons), d) for d in aux_blocks.values()]
    return matrix.block(R, [covecmat] + auxmats, ncols=1 + len(auxmats))
Пример #9
0
    def p_minimal_polynomials(self, p, s_max=None):
        r"""
        Compute `(p^s)`-minimal polynomials `\nu_s` of `B`.

        Compute a finite subset `\mathcal{S}` of the positive
        integers and `(p^s)`-minimal polynomials
        `\nu_s` for `s \in \mathcal{S}`.

        For `0 < t \le \max \mathcal{S}`, a `(p^t)`-minimal polynomial is
        given by `\nu_s` where
        `s = \min\{ r \in \mathcal{S} \mid r\ge t \}`.
        For `t > \max \mathcal{S}`, the minimal polynomial of `B` is
        also a `(p^t)`-minimal polynomial.

        INPUT:

        - ``p`` -- a prime in `D`

        - ``s_max`` -- a positive integer (default: ``None``); if set, only
          `(p^s)`-minimal polynomials for ``s <= s_max`` are computed
          (see below for details)

        OUTPUT:

        A dictionary. Keys are the finite set `\mathcal{S}`, the values
        are the associated `(p^s)`-minimal polynomials `\nu_s`,
        `s \in \mathcal{S}`.

        Setting ``s_max`` only affects the output if ``s_max`` is at
        most `\max\mathcal{S}` where `\mathcal{S}` denotes the full
        set. In that case, only those `\nu_s` with ``s <= s_max`` are
        returned where ``s_max`` is always included even if it is not
        included in the full set `\mathcal{S}`.

        EXAMPLES::

            sage: from sage.matrix.compute_J_ideal import ComputeMinimalPolynomials
            sage: B = matrix(ZZ, [[1, 0, 1], [1, -2, -1], [10, 0, 0]])
            sage: C = ComputeMinimalPolynomials(B)
            sage: C.p_minimal_polynomials(2)
            {2: x^2 + 3*x + 2}
            sage: set_verbose(1)
            sage: C = ComputeMinimalPolynomials(B)
            sage: C.p_minimal_polynomials(2)
            verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials)
            ------------------------------------------
            verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials)
            p = 2, t = 1:
            verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials)
            Result of lifting:
            verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials)
            F =
            verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials)
            [x^2 + x]
            [      x]
            [      0]
            [      1]
            [      1]
            [  x + 1]
            [      1]
            [      0]
            [      0]
            [  x + 1]
            verbose 1 (...: compute_J_ideal.py, current_nu)
            ------------------------------------------
            verbose 1 (...: compute_J_ideal.py, current_nu)
            (x^2 + x)
            verbose 1 (...: compute_J_ideal.py, current_nu)
            Generators with (p^t)-generating property:
            verbose 1 (...: compute_J_ideal.py, current_nu)
            [x^2 + x]
            verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials)
            nu = x^2 + x
            verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials)
            corresponding columns for G
            verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials)
            [x^2 + x]
            [  x + 2]
            [      0]
            [      1]
            [      1]
            [  x - 1]
            [     -1]
            [     10]
            [      0]
            [  x + 1]
            verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials)
            ------------------------------------------
            verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials)
            p = 2, t = 2:
            verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials)
            Result of lifting:
            verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials)
            F =
            verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials)
            [  2*x^2 + 2*x x^2 + 3*x + 2]
            [          2*x         x + 4]
            [            0             0]
            [            2             1]
            [            2             1]
            [      2*x + 2         x + 1]
            [            2            -1]
            [            0            10]
            [            0             0]
            [      2*x + 2         x + 3]
            verbose 1 (...: compute_J_ideal.py, current_nu)
            ------------------------------------------
            verbose 1 (...: compute_J_ideal.py, current_nu)
            (2*x^2 + 2*x, x^2 + 3*x + 2)
            verbose 1 (...: compute_J_ideal.py, current_nu)
            Generators with (p^t)-generating property:
            verbose 1 (...: compute_J_ideal.py, current_nu)
            [x^2 + 3*x + 2]
            verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials)
            nu = x^2 + 3*x + 2
            verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials)
            corresponding columns for G
            verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials)
            [x^2 + 3*x + 2]
            [        x + 4]
            [            0]
            [            1]
            [            1]
            [        x + 1]
            [           -1]
            [           10]
            [            0]
            [        x + 3]
            verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials)
            ------------------------------------------
            verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials)
            p = 2, t = 3:
            verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials)
            Result of lifting:
            verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials)
            F =
            verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials)
            [x^3 + 7*x^2 + 6*x x^3 + 3*x^2 + 2*x]
            [        x^2 + 8*x         x^2 + 4*x]
            [                0                 0]
            [                x             x + 4]
            [            x + 4                 x]
            [    x^2 + 5*x + 4           x^2 + x]
            [           -x + 4                -x]
            [             10*x              10*x]
            [                0                 0]
            [        x^2 + 7*x     x^2 + 3*x + 4]
            verbose 1 (...: compute_J_ideal.py, current_nu)
            ------------------------------------------
            verbose 1 (...: compute_J_ideal.py, current_nu)
            (x^3 + 7*x^2 + 6*x, x^3 + 3*x^2 + 2*x)
            verbose 1 (...: compute_J_ideal.py, current_nu)
            Generators with (p^t)-generating property:
            verbose 1 (...: compute_J_ideal.py, current_nu)
            [x^3 + 7*x^2 + 6*x, x^3 + 3*x^2 + 2*x]
            verbose 1 (...: compute_J_ideal.py, current_nu)
            [x^3 + 3*x^2 + 2*x]
            verbose 1 (...: compute_J_ideal.py, p_minimal_polynomials)
            nu = x^3 + 3*x^2 + 2*x
            {2: x^2 + 3*x + 2}
            sage: set_verbose(0)
            sage: C.p_minimal_polynomials(2, s_max=1)
            {1: x^2 + x}
            sage: C.p_minimal_polynomials(2, s_max=2)
            {2: x^2 + 3*x + 2}
            sage: C.p_minimal_polynomials(2, s_max=3)
            {2: x^2 + 3*x + 2}

        ALGORITHM:

        [HR2016]_, Algorithm 5.
        """
        from sage.misc.misc import verbose
        from sage.rings.infinity import Infinity

        deg_mu = self.mu_B.degree()
        if s_max is None:
            s_max = Infinity

        if p in self._cache:
            (t, G, p_min_polys) = self._cache[p]
            if t < Infinity:
                nu = G[0][0]
        else:
            t = 0
            p_min_polys = {}
            nu = self._DX(1)
            d = self._A.ncols()
            G = matrix(self._DX, d, 0)


        while t < s_max:
            deg_prev_nu = nu.degree()
            t += 1
            verbose("------------------------------------------")
            verbose("p = %s, t = %s:" % (p, t))

            verbose("Result of lifting:")
            verbose("F =")
            F = lifting(p, t, self._A, G)
            verbose(F)

            nu = self.current_nu(p, t, F[0], nu)

            verbose("nu = %s" % nu)
            if nu.degree() >= deg_mu:
                t = Infinity
                break

            if nu.degree() == deg_prev_nu:
                G = G.delete_columns([G.ncols() - 1])
                del p_min_polys[t-1]

            column = self.mccoy_column(p, t, nu)
            verbose("corresponding columns for G")
            verbose(column)

            G = matrix.block([[p * G, column]])
            p_min_polys[t] = nu

        self._cache[p] = (t, G, p_min_polys)

        if s_max < t:
            result = {r: polynomial
                      for r, polynomial in iteritems(p_min_polys)
                      if r < s_max}
            next_t_candidates = list(r for r in p_min_polys if r >= s_max)
            if next_t_candidates:
                next_t = min(next_t_candidates)
                result.update({s_max: p_min_polys[next_t] % p**s_max})

            return result

        return p_min_polys
Пример #10
0
def lifting(p, t, A, G):
    r"""
    Compute generators of `\{f \in D[X]^d \mid Af \equiv 0 \pmod{p^{t}}\}` given
    generators of `\{f\in D[X]^d \mid Af \equiv 0\pmod{p^{t-1}}\}`.

    INPUT:

    - ``p`` -- a prime element of some principal ideal domain `D`

    - ``t`` -- a non-negative integer

    - ``A`` -- a `c\times d` matrix over `D[X]`

    - ``G`` -- a matrix over `D[X]`. The columns of
      `\begin{pmatrix}p^{t-1}I& G\end{pmatrix}` are generators
      of `\{ f\in D[X]^d \mid Af \equiv 0\pmod{p^{t-1}}\}`;
      can be set to ``None`` if ``t`` is zero

    OUTPUT:

    A matrix `F` over `D[X]` such that the columns of
    `\begin{pmatrix}p^tI&F&pG\end{pmatrix}` are generators of
    `\{ f\in D[X]^d \mid Af \equiv 0\pmod{p^t}\}`.

    EXAMPLES::

        sage: from sage.matrix.compute_J_ideal import lifting
        sage: X = polygen(ZZ, 'X')
        sage: A = matrix([[1, X], [2*X, X^2]])
        sage: G0 = lifting(5, 0, A, None)
        sage: G1 = lifting(5, 1, A, G0); G1
        []
        sage: (A*G1 % 5).is_zero()
        True
        sage: A = matrix([[1, X, X^2], [2*X, X^2, 3*X^3]])
        sage: G0 = lifting(5, 0, A, None)
        sage: G1 = lifting(5, 1, A, G0); G1
        [3*X^2]
        [    X]
        [    1]
        sage: (A*G1 % 5).is_zero()
        True
        sage: G2 = lifting(5, 2, A, G1); G2
        [15*X^2 23*X^2]
        [   5*X      X]
        [     5      1]
        sage: (A*G2 % 25).is_zero()
        True
        sage: lifting(5, 10, A, G1)
        Traceback (most recent call last):
        ...
        ValueError: A*G not zero mod 5^9

    ALGORITHM:

    [HR2016]_, Algorithm 1.

    TESTS::

        sage: A = matrix([[1, X], [X, X^2]])
        sage: G0 = lifting(5, 0, A, None)
        sage: G1 = lifting(5, 1, A, G0); G1
        Traceback (most recent call last):
        ...
        ValueError: [  1   X|]
        [  X X^2|] does not have full rank.
    """
    from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing


    DX = A.parent().base()
    (X,) = DX.gens()
    D = DX.base_ring()
    d = A.ncols()
    c = A.nrows()

    if t == 0:
        return matrix(DX, d, 0)

    if not (A*G % p**(t-1)).is_zero():
        raise ValueError("A*G not zero mod %s^%s" % (p, t-1))


    R = A*G/p**(t-1)
    R.change_ring(DX)

    AR = matrix.block([[A, R]])
    Fp = D.quotient(p*D)
    FpX = PolynomialRing(Fp, name=X)

    ARb = AR.change_ring(FpX)
    (Db, Sb, Tb) = ARb.smith_form()
    #assert Sb * ARb * Tb == Db
    #assert all(i == j or Db[i, j].is_zero()
    #           for i in range(Db.nrows())
    #           for j in range(Db.ncols()))

    r = Db.rank()
    if r != c:
        raise ValueError("{} does not have full rank.".format(ARb))

    T = Tb.change_ring(DX)

    F1 = matrix.block([[p**(t-1) * matrix.identity(d), G]])*T
    F = F1.matrix_from_columns(range(r, F1.ncols()))
    assert (A*F % (p**t)).is_zero(), "A*F=%s" % (A*F)

    return F
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]