예제 #1
0
    def __init__(self, A, elt=None, check=True):
        """
        TESTS::

            sage: A = FiniteDimensionalAlgebra(GF(3), [Matrix([[1,0], [0,1]]), Matrix([[0,1], [0,0]])])
            sage: A(QQ(4))
            Traceback (most recent call last):
            ...
            TypeError: elt should be a vector, a matrix, or an element of the base field

            sage: B = FiniteDimensionalAlgebra(QQ, [Matrix([[1,0], [0,1]]), Matrix([[0,1], [-1,0]])])
            sage: elt = B(Matrix([[1,1], [-1,1]])); elt
            e0 + e1
            sage: TestSuite(elt).run()
            sage: B(Matrix([[0,1], [1,0]]))
            Traceback (most recent call last):
            ...
            ValueError: matrix does not define an element of the algebra
        """
        AlgebraElement.__init__(self, A)
        k = A.base_ring()
        n = A.degree()
        if elt is None:
            self._vector = vector(k, n)
            self._matrix = Matrix(k, n)
        else:
            if isinstance(elt, int):
                elt = Integer(elt)
            elif isinstance(elt, list):
                elt = vector(elt)
            if A == elt.parent():
                self._vector = elt._vector.base_extend(k)
                self._matrix = elt._matrix.base_extend(k)
            elif k.has_coerce_map_from(elt.parent()):
                e = k(elt)
                if e == 0:
                    self._vector = vector(k, n)
                    self._matrix = Matrix(k, n)
                elif A.is_unitary():
                    self._vector = A._one * e
                    self._matrix = Matrix.identity(k, n) * e
                else:
                    raise TypeError("algebra is not unitary")
            elif is_Vector(elt):
                self._vector = elt.base_extend(k)
                self._matrix = Matrix(
                    k, sum([elt[i] * A.table()[i] for i in range(n)]))
            elif is_Matrix(elt):
                if not A.is_unitary():
                    raise TypeError("algebra is not unitary")
                self._vector = A._one * elt
                if not check or sum(
                    [self._vector[i] * A.table()[i] for i in range(n)]) == elt:
                    self._matrix = elt
                else:
                    raise ValueError(
                        "matrix does not define an element of the algebra")
            else:
                raise TypeError("elt should be a vector, a matrix, " +
                                "or an element of the base field")
    def __init__(self, A, elt=None, check=True):
        """
        TESTS::

            sage: A = FiniteDimensionalAlgebra(GF(3), [Matrix([[1,0], [0,1]]), Matrix([[0,1], [0,0]])])
            sage: A(QQ(4))
            Traceback (most recent call last):
            ...
            TypeError: elt should be a vector, a matrix, or an element of the base field

            sage: B = FiniteDimensionalAlgebra(QQ, [Matrix([[1,0], [0,1]]), Matrix([[0,1], [-1,0]])])
            sage: elt = B(Matrix([[1,1], [-1,1]])); elt
            e0 + e1
            sage: TestSuite(elt).run()
            sage: B(Matrix([[0,1], [1,0]]))
            Traceback (most recent call last):
            ...
            ValueError: matrix does not define an element of the algebra
        """
        AlgebraElement.__init__(self, A)
        k = A.base_ring()
        n = A.degree()
        if elt is None:
            self._vector = vector(k, n)
            self._matrix = Matrix(k, n)
        else:
            if isinstance(elt, int):
                elt = Integer(elt)
            elif isinstance(elt, list):
                elt = vector(elt)
            if A == elt.parent():
                self._vector = elt._vector.base_extend(k)
                self._matrix = elt._matrix.base_extend(k)
            elif k.has_coerce_map_from(elt.parent()):
                e = k(elt)
                if e == 0:
                    self._vector = vector(k, n)
                    self._matrix = Matrix(k, n)
                elif A.is_unitary():
                    self._vector = A._one * e
                    self._matrix = Matrix.identity(k, n) * e
                else:
                    raise TypeError("algebra is not unitary")
            elif is_Vector(elt):
                self._vector = elt.base_extend(k)
                self._matrix = Matrix(k, sum([elt[i] * A.table()[i] for i in xrange(n)]))
            elif is_Matrix(elt):
                if not A.is_unitary():
                    raise TypeError("algebra is not unitary")
                self._vector = A._one * elt
                if not check or sum([self._vector[i]*A.table()[i] for i in xrange(n)]) == elt:
                    self._matrix = elt
                else:
                    raise ValueError("matrix does not define an element of the algebra")
            else:
                raise TypeError("elt should be a vector, a matrix, " +
                                "or an element of the base field")
예제 #3
0
    def is_unitary(self):
        """
        Return ``True`` if ``self`` has a two-sided multiplicative
        identity element.

        EXAMPLES::

            sage: A = FiniteDimensionalAlgebra(QQ, [])
            sage: A.is_unitary()
            True

            sage: B = FiniteDimensionalAlgebra(QQ, [Matrix([[1,0], [0,1]]), Matrix([[0,1], [-1,0]])])
            sage: B.is_unitary()
            True

            sage: C = FiniteDimensionalAlgebra(QQ, [Matrix([[0,0], [0,0]]), Matrix([[0,0], [0,0]])])
            sage: C.is_unitary()
            False

            sage: D = FiniteDimensionalAlgebra(QQ, [Matrix([[1,0], [0,1]]), Matrix([[1,0], [0,1]])])
            sage: D.is_unitary()
            False

        .. NOTE::

            If a finite-dimensional algebra over a field admits a left identity,
            then this is the unique left identity, and it is also a
            right identity.
        """
        k = self.base_ring()
        n = self.degree()
        # B is obtained by concatenating the elements of
        # self.table(), and v by concatenating the rows of
        # the n times n identity matrix.
        B = reduce(lambda x, y: x.augment(y),
                   self.table(), Matrix(k, n, 0))
        v = vector(Matrix.identity(k, n).list())
        try:
            self._one = B.solve_left(v)
            return True
        except ValueError:
            return False
예제 #4
0
파일: normal_form.py 프로젝트: yazici/sage
def _jordan_2_adic(G):
    r"""
    Transform a symmetric matrix over the `2`-adic integers into jordan form.

    Note that if the precision is too low, this method fails.
    The method is only tested for input over `\ZZ_2` of ``'type=fixed-mod'``.

    INPUT:

    - ``G`` -- symmetric `n` by `n` matrix in `\ZZ_p`

    OUTPUT:

    - ``D`` -- the jordan matrix
    - ``B`` -- transformation matrix, i.e, ``D = B * G * B^T``

    The matrix ``D`` is a block diagonal matrix consisting
    of `1` by `1` and `2` by `2` blocks.
    The `2` by `2` blocks are matrices of the form
    `[[2a,  b], [b, 2c]] * 2^k`
    with `b` of valuation `0`.

    EXAMPLES::

        sage: from sage.quadratic_forms.genera.normal_form import _jordan_2_adic
        sage: R = Zp(2, prec=3, print_mode='terse', show_prec=False)
        sage: A4 = Matrix(R,4,[2, -1, 0, 0, -1, 2, -1, 0, 0, -1, 2, -1, 0, 0, -1, 2])
        sage: A4
        [2 7 0 0]
        [7 2 7 0]
        [0 7 2 7]
        [0 0 7 2]
        sage: D, B = _jordan_2_adic(A4)
        sage: D
        [ 2  7  0  0]
        [ 7  2  0  0]
        [ 0  0 12  7]
        [ 0  0  7  2]
        sage: D == B*A4*B.T
        True
        sage: B.determinant().valuation() == 0
        True
    """
    R = G.base_ring()
    D = copy(G)
    n = G.ncols()

    # transformation matrix
    B = Matrix.identity(R, n)

    # indices of the diagonal entrys which are already used
    cnt = 0
    minval = None
    while cnt < n:
        pivot = _find_min_p(D, cnt)
        piv1 = pivot[1]
        piv2 = pivot[2]
        minval = pivot[0]
        # the smallest valuation is on the diagonal
        if piv1 == piv2:
            # move pivot to position [cnt,cnt]
            if piv1 != cnt:
                B.swap_rows(cnt, piv1)
                D.swap_rows(cnt, piv1)
                D.swap_columns(cnt, piv1)
            # we are already orthogonal to the part with i < cnt
            # now make the rest orthogonal too
            for i in range(cnt + 1, n):
                if D[i, cnt] != 0:
                    c = D[i, cnt] // D[cnt, cnt]
                    B[i, :] += -c * B[cnt, :]
                    D[i, :] += -c * D[cnt, :]
                    D[:, i] += -c * D[:, cnt]
            cnt = cnt + 1
        # the smallest valuation is off the diagonal
        else:
            # move this 2 x 2 block to the top left (starting from cnt)
            if piv1 != cnt:
                B.swap_rows(cnt, piv1)
                D.swap_rows(cnt, piv1)
                D.swap_columns(cnt, piv1)
            if piv2 != cnt + 1:
                B.swap_rows(cnt + 1, piv2)
                D.swap_rows(cnt + 1, piv2)
                D.swap_columns(cnt + 1, piv2)
            # we split off a 2 x 2 block
            # if it is the last 2 x 2 block, there is nothing to do.
            if cnt != n - 2:
                content = R(2**minval)
                eqn_mat = D[cnt:cnt + 2, cnt:cnt + 2].list()
                eqn_mat = Matrix(R, 2, 2, [e // content for e in eqn_mat])
                # calculate the inverse without using division
                inv = eqn_mat.adjugate() * eqn_mat.det().inverse_of_unit()
                B1 = B[cnt:cnt + 2, :]
                B2 = D[cnt + 2:, cnt:cnt + 2] * inv
                for i in range(B2.nrows()):
                    for j in range(B2.ncols()):
                        B2[i, j] = B2[i, j] // content
                B[cnt + 2:, :] -= B2 * B1
                D[cnt:, cnt:] = B[cnt:, :] * G * B[cnt:, :].transpose()
            cnt += 2
    return D, B
예제 #5
0
파일: normal_form.py 프로젝트: yazici/sage
def _jordan_odd_adic(G):
    r"""
    Return the Jordan decomposition of a symmetric matrix over an odd prime.

    INPUT:

    - a symmetric matrix over `\ZZ_p` of type ``'fixed-mod'``

    OUTPUT:

    - ``D`` -- a diagonal matrix
    - ``B`` -- a unimodular matrix such that ``D = B * G * B.T``

    EXAMPLES::

        sage: from sage.quadratic_forms.genera.normal_form import _jordan_odd_adic
        sage: R = Zp(3, prec=2, print_mode='terse', show_prec=False)
        sage: A4 = Matrix(R,4,[2, -1, 0, 0, -1, 2, -1, 0, 0, -1, 2, -1, 0, 0, -1, 2])
        sage: A4
        [2 8 0 0]
        [8 2 8 0]
        [0 8 2 8]
        [0 0 8 2]
        sage: D, B = _jordan_odd_adic(A4)
        sage: D
        [2 0 0 0]
        [0 2 0 0]
        [0 0 1 0]
        [0 0 0 8]
        sage: D == B*A4*B.T
        True
        sage: B.determinant().valuation() == 0
        True
    """
    R = G.base_ring()
    D = copy(G)
    n = G.ncols()

    # transformation matrix
    B = Matrix.identity(R, n)

    # indices of the diagonal entrys which are already used
    cnt = 0
    minval = 0
    while cnt < n:
        pivot = _find_min_p(D, cnt, minval)
        piv1 = pivot[1]
        piv2 = pivot[2]
        minval = pivot[0]
        # the smallest valuation is on the diagonal
        if piv1 == piv2:
            # move pivot to position [cnt,cnt]
            if piv1 != cnt:
                B.swap_rows(cnt, piv1)
                D.swap_rows(cnt, piv1)
                D.swap_columns(cnt, piv1)
            # we are already orthogonal to the part with i < cnt
            # now make the rest orthogonal too
            for i in range(cnt + 1, n):
                if D[i, cnt] != 0:
                    c = D[i, cnt] // D[cnt, cnt]
                    B[i, :] += -c * B[cnt, :]
                    D[i, :] += -c * D[cnt, :]
                    D[:, i] += -c * D[:, cnt]
            cnt = cnt + 1
        else:
            # the smallest valuation is off the diagonal
            row = pivot[1]
            col = pivot[2]
            B[row, :] += B[col, :]
            D[row, :] += D[col, :]
            D[:, row] += D[:, col]
            # the smallest valuation is now on the diagonal
    return D, B
예제 #6
0
파일: normal_form.py 프로젝트: yazici/sage
def p_adic_normal_form(G, p, precision=None, partial=False, debug=False):
    r"""
    Return the transformation to the `p`-adic normal form of a symmetric matrix.

    Two ``p-adic`` quadratic forms are integrally equivalent if and only if
    their Gram matrices have the same normal form.

    Let `p` be odd and `u` be the smallest non-square modulo `p`.
    The normal form is a block diagonal matrix
    with blocks `p^k G_k` such that `G_k` is either the identity matrix or
    the identity matrix with the last diagonal entry replaced by `u`.

    If `p=2` is even, define the `1` by `1` matrices::

        sage: W1 = Matrix([1]); W1
        [1]
        sage: W3 = Matrix([3]); W3
        [3]
        sage: W5 = Matrix([5]); W5
        [5]
        sage: W7 = Matrix([7]); W7
        [7]

    and the `2` by `2` matrices::

        sage: U = Matrix(2,[0,1,1,0]); U
        [0 1]
        [1 0]
        sage: V = Matrix(2,[2,1,1,2]); V
        [2 1]
        [1 2]

    For `p=2` the partial normal form is a block diagonal matrix with blocks
    `2^k G_k` such that $G_k$ is a block diagonal matrix of the form
    `[U`, ... , `U`, `V`, `Wa`, `Wb]`
    where we allow `V`, `Wa`, `Wb` to be `0 \times 0` matrices.

    Further restrictions to the full normal form apply.
    We refer to [MirMor2009]_ IV Definition 4.6. for the details.

    INPUT:

    - ``G`` -- a symmetric `n` by `n` matrix in `\QQ`
    - ``p`` -- a prime number -- it is not checked whether it is prime
    - ``precision`` -- if not set, the minimal possible is taken
    - ``partial`` --  boolean (default: ``False``) if set, only the
      partial normal form is returned.

    OUTPUT:

    - ``D`` -- the jordan matrix over `\QQ_p`
    - ``B`` -- invertible transformation matrix over `\ZZ_p`,
      i.e, ``D = B * G * B^T``

    EXAMPLES::

        sage: from sage.quadratic_forms.genera.normal_form import p_adic_normal_form
        sage: D4 = Matrix(ZZ, 4, [2,-1,-1,-1,-1,2,0,0,-1,0,2,0,-1,0,0,2])
        sage: D4
        [ 2 -1 -1 -1]
        [-1  2  0  0]
        [-1  0  2  0]
        [-1  0  0  2]
        sage: D, B = p_adic_normal_form(D4, 2)
        sage: D
        [  2   1   0   0]
        [  1   2   0   0]
        [  0   0 2^2   2]
        [  0   0   2 2^2]
        sage: D == B * D4 * B.T
        True
        sage: A4 = Matrix(ZZ, 4, [2, -1, 0, 0, -1, 2, -1, 0, 0, -1, 2, -1, 0, 0, -1, 2])
        sage: A4
        [ 2 -1  0  0]
        [-1  2 -1  0]
        [ 0 -1  2 -1]
        [ 0  0 -1  2]
        sage: D, B = p_adic_normal_form(A4, 2)
        sage: D
        [0 1 0 0]
        [1 0 0 0]
        [0 0 2 1]
        [0 0 1 2]

    We can handle degenerate forms::

        sage: A4_extended = Matrix(ZZ, 5, [2, -1, 0, 0, -1, -1, 2, -1, 0, 0, 0, -1, 2, -1, 0, 0, 0, -1, 2, -1, -1, 0, 0, -1, 2])
        sage: D, B = p_adic_normal_form(A4_extended, 5)
        sage: D
        [1 0 0 0 0]
        [0 1 0 0 0]
        [0 0 1 0 0]
        [0 0 0 5 0]
        [0 0 0 0 0]

    and denominators::

        sage: A4dual = A4.inverse()
        sage: D, B = p_adic_normal_form(A4dual, 5)
        sage: D
        [5^-1    0    0    0]
        [   0    1    0    0]
        [   0    0    1    0]
        [   0    0    0    1]

    TESTS::

        sage: Z = Matrix(ZZ,0,[])
        sage: p_adic_normal_form(Z, 3)
        ([], [])
        sage: Z = matrix.zero(10)
        sage: p_adic_normal_form(Z, 3)[0] == 0
        True
    """
    p = ZZ(p)
    # input checks!!
    G0, denom = G._clear_denom()
    d = denom.valuation(p)
    r = G0.rank()
    if r != G0.ncols():
        U = G0.hermite_form(transformation=True)[1]
    else:
        U = G0.parent().identity_matrix()
    kernel = U[r:, :]
    nondeg = U[:r, :]

    # continue with the non-degenerate part
    G = nondeg * G * nondeg.T * p**d
    if precision is None:
        # in Zp(2) we have to calculate at least mod 8 for things to make sense.
        precision = G.det().valuation(p) + 4
    R = Zp(p, prec=precision, type='fixed-mod')
    G = G.change_ring(R)
    G.set_immutable()  # is not changed during computation
    D = copy(G)  # is transformed into jordan form
    n = G.ncols()
    # The trivial case
    if n == 0:
        return G.parent().zero(), G.parent().zero()
    # the transformation matrix is called B
    B = Matrix.identity(R, n)
    if p == 2:
        D, B = _jordan_2_adic(G)
    else:
        D, B = _jordan_odd_adic(G)
    D, B1 = _normalize(D)
    B = B1 * B
    # we have reached a normal form for p != 2
    # for p == 2 extra work is necessary
    if p == 2:
        D, B1 = _two_adic_normal_forms(D, partial=partial)
        B = B1 * B
    nondeg = B * nondeg
    B = nondeg.stack(kernel)
    D = Matrix.block_diagonal([D, Matrix.zero(kernel.nrows())])
    if debug:
        assert B.determinant().valuation() == 0  # B is invertible!
        if p == 2:
            assert B * G * B.T == Matrix.block_diagonal(
                collect_small_blocks(D))
        else:
            assert B * G * B.T == Matrix.diagonal(D.diagonal())
    return D / p**d, B
    def primary_decomposition(self):
        """
        Return the primary decomposition of ``self``.

        .. NOTE::

            ``self`` must be unitary, commutative and associative.

        OUTPUT:

        - a list consisting of the quotient maps ``self`` -> `A`,
          with `A` running through the primary factors of ``self``

        EXAMPLES::

            sage: A = FiniteDimensionalAlgebra(GF(3), [Matrix([[1, 0], [0, 1]]), Matrix([[0, 1], [0, 0]])])
            sage: A.primary_decomposition()
            [Morphism from Finite-dimensional algebra of degree 2 over Finite Field of size 3 to Finite-dimensional algebra of degree 2 over Finite Field of size 3 given by matrix [1 0]
            [0 1]]

            sage: B = FiniteDimensionalAlgebra(QQ, [Matrix([[1,0,0], [0,1,0], [0,0,0]]), Matrix([[0,1,0], [0,0,0], [0,0,0]]), Matrix([[0,0,0], [0,0,0], [0,0,1]])])
            sage: B.primary_decomposition()
            [Morphism from Finite-dimensional algebra of degree 3 over Rational Field to Finite-dimensional algebra of degree 1 over Rational Field given by matrix [0]
            [0]
            [1], Morphism from Finite-dimensional algebra of degree 3 over Rational Field to Finite-dimensional algebra of degree 2 over Rational Field given by matrix [1 0]
            [0 1]
            [0 0]]
        """
        k = self.base_ring()
        n = self.degree()
        if n == 0:
            return []
        if not (self.is_unitary() and self.is_commutative()
                and (self._assume_associative or self.is_associative())):
            raise TypeError("algebra must be unitary, commutative and associative")
        # Start with the trivial decomposition of self.
        components = [Matrix.identity(k, n)]
        for b in self.table():
            # Use the action of the basis element b to refine our
            # decomposition of self.
            components_new = []
            for c in components:
                # Compute the matrix of b on the component c, find its
                # characteristic polynomial, and factor it.
                b_c = c.solve_left(c * b)
                fact = b_c.characteristic_polynomial().factor()
                if len(fact) == 1:
                    components_new.append(c)
                else:
                    for f in fact:
                        h, a = f
                        e = h(b_c) ** a
                        ker_e = e.kernel().basis_matrix()
                        components_new.append(ker_e * c)
            components = components_new
        quotients = []
        for i in range(len(components)):
            I = Matrix(k, 0, n)
            for j,c in enumerate(components):
                if j != i:
                    I = I.stack(c)
            quotients.append(self.quotient_map(self.ideal(I, given_by_matrix=True)))
        return quotients
예제 #8
0
    def __getitem__(self, key):
        r"""
        Return a slice of the sequence.

        EXAMPLES::

            sage: C.<x> = CFiniteSequences(QQ)
            sage: r = C.from_recurrence([3,3],[2,1])
            sage: r[2]
            9
            sage: r[101]
            16158686318788579168659644539538474790082623100896663971001
            sage: r = C(1/(1-x))
            sage: r[5]
            1
            sage: r = C(x)
            sage: r[0]
            0
            sage: r[1]
            1
            sage: r = C(0)
            sage: r[66]
            0
            sage: lucas = C.from_recurrence([1,1],[2,1])
            sage: lucas[5:10]
            [11, 18, 29, 47, 76]
            sage: r = C((2-x)/x/(1-x-x*x))
            sage: r[0:4]
            [1, 3, 4, 7]
            sage: r = C(1-2*x^2)
            sage: r[0:4]
            [1, 0, -2, 0]
            sage: r[-1:4]             # not tested, python will not allow this!
            [0, 1, 0 -2, 0]
            sage: r = C((-2*x^3 + x^2 + 1)/(-2*x + 1))
            sage: r[0:5]              # handle ogf > 1
            [1, 2, 5, 8, 16]
            sage: r[-2]
            0
            sage: r = C((-2*x^3 + x^2 - x + 1)/(2*x^2 - 3*x + 1))
            sage: r[0:5]
            [1, 2, 5, 9, 17]
            sage: s=C((1-x)/(-x^2 - x + 1))
            sage: s[0:5]
            [1, 0, 1, 1, 2]
            sage: s=C((1+x^20+x^40)/(1-x^12)/(1-x^30))
            sage: s[0:20]
            [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0]
            sage: s=C(1/((1-x^2)*(1-x^6)*(1-x^8)*(1-x^12)))
            sage: s[999998]
            289362268629630
        """
        if isinstance(key, slice):
            m = max(key.start, key.stop)
            return [self[ii] for ii in range(*key.indices(m + 1))]
        elif isinstance(key, Integral):
            from sage.matrix.constructor import Matrix
            d = self._deg
            if (self._off <= key and key < self._off + len(self._a)):
                return self._a[key - self._off]
            elif d == 0:
                return 0
            (quo, rem) = self.numerator().quo_rem(self.denominator())
            wp = quo[key - self._off]
            if key < self._off:
                return wp
            A = Matrix(QQ, 1, d, self._c)
            B = Matrix.identity(QQ, d - 1)
            C = Matrix(QQ, d - 1, 1, 0)
            if quo == 0:
                V = Matrix(QQ, d, 1, self._a[:d][::-1])
            else:
                V = Matrix(QQ, d, 1, self._aa[:d][::-1])
            M = Matrix.block([[A], [B, C]], subdivide=False)

            return wp + list(M**(key - self._off) * V)[d - 1][0]
        else:
            raise TypeError("invalid argument type")
예제 #9
0
def _jordan_2_adic(G):
    r"""
    Transform a symmetric matrix over the `2`-adic integers into jordan form.

    Note that if the precision is too low, this method fails.
    The method is only tested for input over `\ZZ_2` of ``'type=fixed-mod'``.

    INPUT:

    - ``G`` -- symmetric `n` by `n` matrix in `\ZZ_p`

    OUTPUT:

    - ``D`` -- the jordan matrix
    - ``B`` -- transformation matrix, i.e, ``D = B * G * B^T``

    The matrix ``D`` is a block diagonal matrix consisting
    of `1` by `1` and `2` by `2` blocks.
    The `2` by `2` blocks are matrices of the form
    `[[2a,  b], [b, 2c]] * 2^k`
    with `b` of valuation `0`.

    EXAMPLES::

        sage: from sage.quadratic_forms.genera.normal_form import _jordan_2_adic
        sage: R = Zp(2, prec=3, print_mode='terse', show_prec=False)
        sage: A4 = Matrix(R,4,[2, -1, 0, 0, -1, 2, -1, 0, 0, -1, 2, -1, 0, 0, -1, 2])
        sage: A4
        [2 7 0 0]
        [7 2 7 0]
        [0 7 2 7]
        [0 0 7 2]
        sage: D, B = _jordan_2_adic(A4)
        sage: D
        [ 2  7  0  0]
        [ 7  2  0  0]
        [ 0  0 12  7]
        [ 0  0  7  2]
        sage: D == B*A4*B.T
        True
        sage: B.determinant().valuation() == 0
        True
    """
    R = G.base_ring()
    D = copy(G)
    n = G.ncols()

    # transformation matrix
    B = Matrix.identity(R, n)

    # indices of the diagonal entrys which are already used
    cnt = 0
    minval = None
    while cnt < n:
            pivot = _find_min_p(D, cnt)
            piv1 = pivot[1]
            piv2 = pivot[2]
            minval_last = minval
            minval = pivot[0]
            # the smallest valuation is on the diagonal
            if piv1 == piv2:
                # move pivot to position [cnt,cnt]
                if piv1 != cnt:
                    B.swap_rows(cnt, piv1)
                    D.swap_rows(cnt, piv1)
                    D.swap_columns(cnt, piv1)
                # we are already orthogonal to the part with i < cnt
                # now make the rest orthogonal too
                for i in range(cnt+1, n):
                    if D[i, cnt] != 0:
                        c = D[i, cnt]//D[cnt, cnt]
                        B[i, :] += -c * B[cnt, :]
                        D[i, :] += -c * D[cnt, :]
                        D[:, i] += -c * D[:, cnt]
                cnt = cnt + 1
            # the smallest valuation is off the diagonal
            else:
                # move this 2 x 2 block to the top left (starting from cnt)
                if piv1 != cnt:
                    B.swap_rows(cnt, piv1)
                    D.swap_rows(cnt, piv1)
                    D.swap_columns(cnt, piv1)
                if piv2 != cnt+1:
                    B.swap_rows(cnt+1, piv2)
                    D.swap_rows(cnt+1, piv2)
                    D.swap_columns(cnt+1, piv2)
                # we split off a 2 x 2 block
                # if it is the last 2 x 2 block, there is nothing to do.
                if cnt != n-2:
                    content = R(2 ** minval)
                    eqn_mat = D[cnt:cnt+2, cnt:cnt+2].list()
                    eqn_mat = Matrix(R, 2, 2, [e // content for e in eqn_mat])
                    # calculate the inverse without using division
                    inv = eqn_mat.adjoint() * eqn_mat.det().inverse_of_unit()
                    B1 = B[cnt:cnt+2, :]
                    B2 = D[cnt+2:, cnt:cnt+2] * inv
                    for i in range(B2.nrows()):
                        for j in range(B2.ncols()):
                            B2[i, j]=B2[i, j] // content
                    B[cnt+2:, :] -= B2 * B1
                    D[cnt:, cnt:] = B[cnt:, :] * G * B[cnt:, :].transpose()
                cnt += 2
    return D, B
예제 #10
0
def _jordan_odd_adic(G):
    r"""
    Return the Jordan decomposition of a symmetric matrix over an odd prime.

    INPUT:

    - a symmetric matrix over `\ZZ_p` of type ``'fixed-mod'``

    OUTPUT:

    - ``D`` -- a diagonal matrix
    - ``B`` -- a unimodular matrix such that ``D = B * G * B.T``

    EXAMPLES::

        sage: from sage.quadratic_forms.genera.normal_form import _jordan_odd_adic
        sage: R = Zp(3, prec=2, print_mode='terse', show_prec=False)
        sage: A4 = Matrix(R,4,[2, -1, 0, 0, -1, 2, -1, 0, 0, -1, 2, -1, 0, 0, -1, 2])
        sage: A4
        [2 8 0 0]
        [8 2 8 0]
        [0 8 2 8]
        [0 0 8 2]
        sage: D, B = _jordan_odd_adic(A4)
        sage: D
        [2 0 0 0]
        [0 2 0 0]
        [0 0 1 0]
        [0 0 0 8]
        sage: D == B*A4*B.T
        True
        sage: B.determinant().valuation() == 0
        True
    """
    R = G.base_ring()
    D = copy(G)
    n = G.ncols()

    # transformation matrix
    B = Matrix.identity(R, n)

    # indices of the diagonal entrys which are already used
    cnt = 0
    minval = 0
    while cnt < n:
        pivot = _find_min_p(D, cnt, minval)
        piv1 = pivot[1]
        piv2 = pivot[2]
        minval = pivot[0]
        # the smallest valuation is on the diagonal
        if piv1 == piv2:
            # move pivot to position [cnt,cnt]
            if piv1 != cnt:
                B.swap_rows(cnt, piv1)
                D.swap_rows(cnt, piv1)
                D.swap_columns(cnt, piv1)
            # we are already orthogonal to the part with i < cnt
            # now make the rest orthogonal too
            for i in range(cnt+1,n):
                if D[i, cnt]!= 0:
                    c = D[i, cnt] // D[cnt, cnt]
                    B[i, :] += - c * B[cnt, :]
                    D[i, :] += - c * D[cnt, :]
                    D[:, i] += - c * D[:, cnt]
            cnt = cnt + 1
        else:
            # the smallest valuation is off the diagonal
            row = pivot[1]
            col = pivot[2]
            B[row, :] += B[col, :]
            D[row, :] += D[col, :]
            D[:, row] += D[:, col]
            # the smallest valuation is now on the diagonal
    return D, B
예제 #11
0
def p_adic_normal_form(G, p, precision=None, partial=False, debug=False):
    r"""
    Return the transformation to the `p`-adic normal form of a symmetric matrix.

    Two ``p-adic`` quadratic forms are integrally equivalent if and only if
    their Gram matrices have the same normal form.

    Let `p` be odd and `u` be the smallest non-square modulo `p`.
    The normal form is a block diagonal matrix
    with blocks `p^k G_k` such that `G_k` is either the identity matrix or
    the identity matrix with the last diagonal entry replaced by `u`.

    If `p=2` is even, define the `1` by `1` matrices::

        sage: W1 = Matrix([1]); W1
        [1]
        sage: W3 = Matrix([3]); W3
        [3]
        sage: W5 = Matrix([5]); W5
        [5]
        sage: W7 = Matrix([7]); W7
        [7]

    and the `2` by `2` matrices::

        sage: U = Matrix(2,[0,1,1,0]); U
        [0 1]
        [1 0]
        sage: V = Matrix(2,[2,1,1,2]); V
        [2 1]
        [1 2]

    For `p=2` the partial normal form is a block diagonal matrix with blocks
    `2^k G_k` such that $G_k$ is a block diagonal matrix of the form
    `[U`, ... , `U`, `V`, `Wa`, `Wb]`
    where we allow `V`, `Wa`, `Wb` to be `0 \times 0` matrices.

    Further restrictions to the full normal form apply.
    We refer to [MirMor2009]_ IV Definition 4.6. for the details.

    INPUT:

    - ``G`` -- a symmetric `n` by `n` matrix in `\QQ`
    - ``p`` -- a prime number -- it is not checked whether it is prime
    - ``precision`` -- if not set, the minimal possible is taken
    - ``partial`` --  boolean (default: ``False``) if set, only the
      partial normal form is returned.

    OUTPUT:

    - ``D`` -- the jordan matrix over `\QQ_p`
    - ``B`` -- invertible transformation matrix over `\ZZ_p`,
      i.e, ``D = B * G * B^T``

    EXAMPLES::

        sage: from sage.quadratic_forms.genera.normal_form import p_adic_normal_form
        sage: D4 = Matrix(ZZ, 4, [2,-1,-1,-1,-1,2,0,0,-1,0,2,0,-1,0,0,2])
        sage: D4
        [ 2 -1 -1 -1]
        [-1  2  0  0]
        [-1  0  2  0]
        [-1  0  0  2]
        sage: D, B = p_adic_normal_form(D4, 2)
        sage: D
        [  2   1   0   0]
        [  1   2   0   0]
        [  0   0 2^2   2]
        [  0   0   2 2^2]
        sage: D == B * D4 * B.T
        True
        sage: A4 = Matrix(ZZ, 4, [2, -1, 0, 0, -1, 2, -1, 0, 0, -1, 2, -1, 0, 0, -1, 2])
        sage: A4
        [ 2 -1  0  0]
        [-1  2 -1  0]
        [ 0 -1  2 -1]
        [ 0  0 -1  2]
        sage: D, B = p_adic_normal_form(A4, 2)
        sage: D
        [0 1 0 0]
        [1 0 0 0]
        [0 0 2 1]
        [0 0 1 2]

    We can handle degenerate forms::

        sage: A4_extended = Matrix(ZZ, 5, [2, -1, 0, 0, -1, -1, 2, -1, 0, 0, 0, -1, 2, -1, 0, 0, 0, -1, 2, -1, -1, 0, 0, -1, 2])
        sage: D, B = p_adic_normal_form(A4_extended, 5)
        sage: D
        [1 0 0 0 0]
        [0 1 0 0 0]
        [0 0 1 0 0]
        [0 0 0 5 0]
        [0 0 0 0 0]

    and denominators::

        sage: A4dual = A4.inverse()
        sage: D, B = p_adic_normal_form(A4dual, 5)
        sage: D
        [5^-1    0    0    0]
        [   0    1    0    0]
        [   0    0    1    0]
        [   0    0    0    1]

    TESTS::

        sage: Z = Matrix(ZZ,0,[])
        sage: p_adic_normal_form(Z, 3)
        ([], [])
        sage: Z = matrix.zero(10)
        sage: p_adic_normal_form(Z, 3)[0] == 0
        True
    """
    p = ZZ(p)
    # input checks!!
    G0, denom = G._clear_denom()
    d = denom.valuation(p)
    r = G0.rank()
    if r != G0.ncols():
        U  = G0.hermite_form(transformation=True)[1]
    else:
        U = G0.parent().identity_matrix()
    kernel = U[r:,:]
    nondeg = U[:r,:]

    # continue with the non-degenerate part
    G = nondeg * G * nondeg.T * p**d
    if precision == None:
        # in Zp(2) we have to calculate at least mod 8 for things to make sense.
        precision = G.det().valuation(p) + 4
    R = Zp(p, prec = precision, type = 'fixed-mod')
    G = G.change_ring(R)
    G.set_immutable() # is not changed during computation
    D = copy(G)    # is transformed into jordan form
    n = G.ncols()
    # The trivial case
    if n == 0:
        return G.parent().zero(), G.parent().zero()
    # the transformation matrix is called B
    B = Matrix.identity(R, n)
    if(p == 2):
        D, B = _jordan_2_adic(G)
    else:
        D, B = _jordan_odd_adic(G)
    D, B1 = _normalize(D)
    B = B1 * B
    # we have reached a normal form for p != 2
    # for p == 2 extra work is necessary
    if p==2:
        D, B1 = _two_adic_normal_forms(D, partial=partial)
        B = B1 * B
    nondeg = B * nondeg
    B = nondeg.stack(kernel)
    D = Matrix.block_diagonal([D, Matrix.zero(kernel.nrows())])
    if debug:
        assert B.determinant().valuation() == 0     # B is invertible!
        if p==2:
            assert B*G*B.T == Matrix.block_diagonal(collect_small_blocks(D))
        else:
            assert B*G*B.T == Matrix.diagonal(D.diagonal())
    return D/p**d, B
예제 #12
0
    def __getitem__(self, key):
        r"""
        Return a slice of the sequence.

        EXAMPLES::

            sage: C.<x> = CFiniteSequences(QQ)
            sage: r = C.from_recurrence([3,3],[2,1])
            sage: r[2]
            9
            sage: r[101]
            16158686318788579168659644539538474790082623100896663971001
            sage: r = C(1/(1-x))
            sage: r[5]
            1
            sage: r = C(x)
            sage: r[0]
            0
            sage: r[1]
            1
            sage: r = C(0)
            sage: r[66]
            0
            sage: lucas = C.from_recurrence([1,1],[2,1])
            sage: lucas[5:10]
            [11, 18, 29, 47, 76]
            sage: r = C((2-x)/x/(1-x-x*x))
            sage: r[0:4]
            [1, 3, 4, 7]
            sage: r = C(1-2*x^2)
            sage: r[0:4]
            [1, 0, -2, 0]
            sage: r[-1:4]             # not tested, python will not allow this!
            [0, 1, 0 -2, 0]
            sage: r = C((-2*x^3 + x^2 + 1)/(-2*x + 1))
            sage: r[0:5]              # handle ogf > 1
            [1, 2, 5, 8, 16]
            sage: r[-2]
            0
            sage: r = C((-2*x^3 + x^2 - x + 1)/(2*x^2 - 3*x + 1))
            sage: r[0:5]
            [1, 2, 5, 9, 17]
            sage: s=C((1-x)/(-x^2 - x + 1))
            sage: s[0:5]
            [1, 0, 1, 1, 2]
            sage: s=C((1+x^20+x^40)/(1-x^12)/(1-x^30))
            sage: s[0:20]
            [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0]
            sage: s=C(1/((1-x^2)*(1-x^6)*(1-x^8)*(1-x^12)))
            sage: s[999998]
            289362268629630
        """
        if isinstance(key, slice):
            m = max(key.start, key.stop)
            return [self[ii] for ii in xrange(*key.indices(m + 1))]
        elif isinstance(key, (int, Integer)):
            from sage.matrix.constructor import Matrix

            d = self._deg
            if self._off <= key and key < self._off + len(self._a):
                return self._a[key - self._off]
            elif d == 0:
                return 0
            (quo, rem) = self.numerator().quo_rem(self.denominator())
            wp = quo[key - self._off]
            if key < self._off:
                return wp
            A = Matrix(QQ, 1, d, self._c)
            B = Matrix.identity(QQ, d - 1)
            C = Matrix(QQ, d - 1, 1, 0)
            if quo == 0:
                V = Matrix(QQ, d, 1, self._a[:d][::-1])
            else:
                V = Matrix(QQ, d, 1, self._aa[:d][::-1])
            M = Matrix.block([[A], [B, C]], subdivide=False)

            return wp + list(M ** (key - self._off) * V)[d - 1][0]
        else:
            raise TypeError("invalid argument type")
예제 #13
0
    def primary_decomposition(self):
        """
        Return the primary decomposition of ``self``.

        .. NOTE::

            ``self`` must be unitary, commutative and associative.

        OUTPUT:

        - a list consisting of the quotient maps ``self`` -> `A`,
          with `A` running through the primary factors of ``self``

        EXAMPLES::

            sage: A = FiniteDimensionalAlgebra(GF(3), [Matrix([[1, 0], [0, 1]]), Matrix([[0, 1], [0, 0]])])
            sage: A.primary_decomposition()
            [Morphism from Finite-dimensional algebra of degree 2 over Finite Field of size 3 to Finite-dimensional algebra of degree 2 over Finite Field of size 3 given by matrix [1 0]
            [0 1]]

            sage: B = FiniteDimensionalAlgebra(QQ, [Matrix([[1,0,0], [0,1,0], [0,0,0]]), Matrix([[0,1,0], [0,0,0], [0,0,0]]), Matrix([[0,0,0], [0,0,0], [0,0,1]])])
            sage: B.primary_decomposition()
            [Morphism from Finite-dimensional algebra of degree 3 over Rational Field to Finite-dimensional algebra of degree 1 over Rational Field given by matrix [0]
            [0]
            [1], Morphism from Finite-dimensional algebra of degree 3 over Rational Field to Finite-dimensional algebra of degree 2 over Rational Field given by matrix [1 0]
            [0 1]
            [0 0]]
        """
        k = self.base_ring()
        n = self.degree()
        if n == 0:
            return []
        if not (self.is_unitary() and self.is_commutative() and
                (self._assume_associative or self.is_associative())):
            raise TypeError(
                "algebra must be unitary, commutative and associative")
        # Start with the trivial decomposition of self.
        components = [Matrix.identity(k, n)]
        for b in self.table():
            # Use the action of the basis element b to refine our
            # decomposition of self.
            components_new = []
            for c in components:
                # Compute the matrix of b on the component c, find its
                # characteristic polynomial, and factor it.
                b_c = c.solve_left(c * b)
                fact = b_c.characteristic_polynomial().factor()
                if len(fact) == 1:
                    components_new.append(c)
                else:
                    for f in fact:
                        h, a = f
                        e = h(b_c)**a
                        ker_e = e.kernel().basis_matrix()
                        components_new.append(ker_e * c)
            components = components_new
        quotients = []
        for i in range(len(components)):
            I = Matrix(k, 0, n)
            for j, c in enumerate(components):
                if j != i:
                    I = I.stack(c)
            quotients.append(
                self.quotient_map(self.ideal(I, given_by_matrix=True)))
        return quotients
def _diagonal_isometry(V, W):
    r"""
    Given two diagonal, rationally equivalent quadratic forms, computes a
    transition matrix mapping from one to the other.

    .. NOTE::

        This function is an auxiliary method of ``isometry``, which is
        the method that should be called as it performs error-checking
        that is not present in this function.

    INPUT:

    - ``V`` -- a diagonal quadratic form
    - ``W`` -- a diagonal quadratic form

    OUTPUT:

    - A matrix ``T`` representing the isometry transformation, such that if
      ``VM`` is the gram matrix of ``V`` and ``WM`` is the gram matrix of
      ``W``, then ``VM == T.transpose() * WM * T`` yields ``True``.

    EXAMPLES::

        sage: from sage.quadratic_forms.quadratic_form__equivalence_testing import _diagonal_isometry

        sage: Q = DiagonalQuadraticForm(QQ, [1, 2, 4])
        sage: F = DiagonalQuadraticForm(QQ, [2, 2, 2])

        sage: T = _diagonal_isometry(Q, F); T
        [   0    1    0]
        [-1/2    0    1]
        [ 1/2    0    1]
        sage: Q.Gram_matrix() == T.T * F.Gram_matrix() * T
        True

        sage: T = _diagonal_isometry(F, Q); T
        [   0   -1   -1]
        [   1    0    0]
        [   0 -1/2  1/2]
        sage: F.Gram_matrix() == T.T * Q.Gram_matrix() * T
        True
    """
    import copy
    from sage.quadratic_forms.quadratic_form import DiagonalQuadraticForm
    from sage.matrix.constructor import Matrix
    from sage.modules.free_module_element import vector

    # We need to modify V and W, so copy them into Q and F respectively.
    Q, F = copy.deepcopy(V), copy.deepcopy(W)
    # Let FM denote the Gram matrix of F.
    FM = F.Gram_matrix()
    n = Q.dim()

    # This matrix represents a new basis for W, where the columns of the
    # matrix are the vectors of the basis. We initialize it to the standard basis.
    change_of_basis_matrix = Matrix.identity(QQ, n)

    # The goal of this loop is to obtain a new basis for W such that the
    # Gram matrix of V with respect to the standard basis equals the Gram matrix
    # of W with respect to the new basis.
    for i in range(n):
        # If the first terms are not equal...
        if Q.Gram_matrix()[0][0] != F.Gram_matrix()[0][0]:
            # Find a vector w in F such that F(w) equals the first term of Q.
            w = F.solve(Q.Gram_matrix()[0][0])
            w = vector(QQ, i * [0] + w.list())

            # We want to extend the basis of W to include the vector w.
            # Find a non-fixed vector in the current basis to replace by w.
            j = i
            # The new set of vectors must still be linearly independent (i.e. the matrix is non-singular).
            while True:
                temp_matrix = Matrix(change_of_basis_matrix)
                temp_matrix.set_column(j, change_of_basis_matrix * w)
                if not temp_matrix.is_singular():
                    break
                j = j + 1

            change_of_basis_matrix = temp_matrix

            # We want to fix w to be the basis vector at position i, so swap it with whatever is already there.
            col = change_of_basis_matrix.column(i)
            change_of_basis_matrix.set_column(i,
                                              change_of_basis_matrix.column(j))
            change_of_basis_matrix.set_column(j, col)

            # Orthogonalize the basis.
            change_of_basis_matrix = _gram_schmidt(change_of_basis_matrix, i,
                                                   W.bilinear_map)

            # Obtain the diagonal gram matrix of F.
            FM = W(change_of_basis_matrix).Gram_matrix_rational()

        # Now we have that QM[0][0] == FM[0][0] where QM and FM are the Gram matrices
        # of Q and F respectively. We remove the first variable from each form and continue.
        F = DiagonalQuadraticForm(F.base_ring(), FM.diagonal())
        F = F.extract_variables(range(i + 1, F.dim()))
        Q = Q.extract_variables(range(1, Q.dim()))

    return change_of_basis_matrix