예제 #1
0
def TorsionQuadraticForm(q):
    r"""
    Create a torsion quadratic form module from a rational matrix.

    The resulting quadratic form takes values in `\QQ / \ZZ`
    or `\QQ / 2 \ZZ` (depending on ``q``).
    If it takes values modulo `2`, then it is non-degenerate.
    In any case the bilinear form is non-degenerate.

    INPUT:

    - ``q`` -- a symmetric rational matrix

    EXAMPLES::

        sage: q1 = Matrix(QQ,2,[1,1/2,1/2,1])
        sage: TorsionQuadraticForm(q1)
        Finite quadratic module over Integer Ring with invariants (2, 2)
        Gram matrix of the quadratic form with values in Q/2Z:
        [  1 1/2]
        [1/2   1]

    In the following example the quadratic form is degenerate.
    But the bilinear form is still non-degenerate::

        sage: q2 = diagonal_matrix(QQ,[1/4,1/3])
        sage: TorsionQuadraticForm(q2)
        Finite quadratic module over Integer Ring with invariants (12,)
        Gram matrix of the quadratic form with values in Q/Z:
        [7/12]

    TESTS::

        sage: TorsionQuadraticForm(matrix.diagonal([3/4,3/8,3/8]))
        Finite quadratic module over Integer Ring with invariants (4, 8, 8)
        Gram matrix of the quadratic form with values in Q/2Z:
        [3/4   0   0]
        [  0 3/8   0]
        [  0   0 3/8]
    """
    q = matrix(QQ, q)
    if q.nrows() != q.ncols():
        raise ValueError("the input must be a square matrix")
    if q != q.transpose():
        raise ValueError("the input must be a symmetric matrix")

    Q, d = q._clear_denom()
    S, U, V = Q.smith_form()
    D = U * q * V
    Q = FreeQuadraticModule(ZZ, q.ncols(), inner_product_matrix=d**2 * q)
    denoms = [D[i, i].denominator() for i in range(D.ncols())]
    rels = Q.span(diagonal_matrix(ZZ, denoms) * U)
    return TorsionQuadraticModule((1/d)*Q, (1/d)*rels, modulus=1)
def TorsionQuadraticForm(q):
    r"""
    Create a torsion quadratic form module from a rational matrix.

    The resulting quadratic form takes values in `\QQ / \ZZ`
    or `\QQ / 2 \ZZ` (depending on ``q``).
    If it takes values modulo `2`, then it is non-degenerate.
    In any case the bilinear form is non-degenerate.

    INPUT:

    - ``q`` -- a symmetric rational matrix

    EXAMPLES::

        sage: q1 = Matrix(QQ,2,[1,1/2,1/2,1])
        sage: TorsionQuadraticForm(q1)
        Finite quadratic module over Integer Ring with invariants (2, 2)
        Gram matrix of the quadratic form with values in Q/2Z:
        [  1 1/2]
        [1/2   1]

    In the following example the quadratic form is degenerate.
    But the bilinear form is still non-degenerate::

        sage: q2 = diagonal_matrix(QQ,[1/4,1/3])
        sage: TorsionQuadraticForm(q2)
        Finite quadratic module over Integer Ring with invariants (12,)
        Gram matrix of the quadratic form with values in Q/Z:
        [7/12]
    """
    q = matrix(QQ, q)
    if q.nrows() != q.ncols():
        raise ValueError("the input must be a square matrix")
    if q != q.transpose():
        raise ValueError("the input must be a symmetric matrix")

    Q, d = q._clear_denom()
    S, U, V = Q.smith_form()
    D = U * q * V
    Q = FreeQuadraticModule(ZZ, q.ncols(), inner_product_matrix=d**2 * q)
    denoms = [D[i,i].denominator() for i in range(D.ncols())]
    rels = Q.span(diagonal_matrix(ZZ, denoms) * U)
    return TorsionQuadraticModule((1/d)*Q, (1/d)*rels)
예제 #3
0
    def twists_matrix(self):
        r"""
        Return a diagonal matrix describing the twist corresponding to
        each simple object in the ``FusionRing``.

        EXAMPLES::

            sage: B21=FusionRing("B2",1)
            sage: [x.twist() for x in B21.basis().list()]
            [0, 1, 5/8]
            sage: [B21.root_of_unity(x.twist()) for x in B21.basis().list()]
            [1, -1, zeta32^10]
            sage: B21.twists_matrix()
            [        1         0         0]
            [        0        -1         0]
            [        0         0 zeta32^10]
        """
        B = self.basis()
        return diagonal_matrix(B[x].ribbon() for x in self.get_order())
예제 #4
0
def lift_for_SL(A, N=None):
    r"""
    Lift a matrix `A` from `SL_m(\ZZ / N\ZZ)` to `SL_m(\ZZ)`.

    This follows [Shi1971]_, Lemma 1.38, p. 21.

    INPUT:

    - ``A`` -- a square matrix with coefficients in `\ZZ / N\ZZ` (or `\ZZ`)

    - ``N`` -- the modulus (optional) required only if the matrix ``A``
      has coefficients in `\ZZ`

    EXAMPLES::

        sage: from sage.modular.local_comp.liftings import lift_for_SL
        sage: A = matrix(Zmod(11), 4, 4, [6, 0, 0, 9, 1, 6, 9, 4, 4, 4, 8, 0, 4, 0, 0, 8])
        sage: A.det()
        1
        sage: L = lift_for_SL(A)
        sage: L.det()
        1
        sage: (L - A) == 0
        True

        sage: B = matrix(Zmod(19), 4, 4, [1, 6, 10, 4, 4, 14, 15, 4, 13, 0, 1, 15, 15, 15, 17, 10])
        sage: B.det()
        1
        sage: L = lift_for_SL(B)
        sage: L.det()
        1
        sage: (L - B) == 0
        True

    TESTS::

        sage: lift_for_SL(matrix(3,3,[1,2,0,3,4,0,0,0,1]),3)
        [10 14  3]
        [ 9 10  3]
        [ 3  3  1]

        sage: A = matrix(Zmod(7), 2, [1,0,0,1])
        sage: L = lift_for_SL(A)
        sage: L.parent()
        Full MatrixSpace of 2 by 2 dense matrices over Integer Ring

        sage: A = matrix(Zmod(7), 1, [1])
        sage: L = lift_for_SL(A); L
        [1]

        sage: A = matrix(ZZ, 2, [1,0,0,1])
        sage: lift_for_SL(A)
        Traceback (most recent call last):
        ...
        ValueError: you must choose the modulus

        sage: for _ in range(100):
        ....:     d = randint(0, 10)
        ....:     p = choice([2,3,5,7,11])
        ....:     M = random_matrix(Zmod(p), d, algorithm='unimodular')
        ....:     assert lift_for_SL(M).det() == 1
    """
    from sage.matrix.special import (identity_matrix, diagonal_matrix,
                                     block_diagonal_matrix)
    from sage.misc.misc_c import prod

    ring = A.parent().base_ring()
    if N is None:
        if ring is ZZ:
            raise ValueError('you must choose the modulus')
        else:
            N = ring.characteristic()

    m = A.nrows()
    if m <= 1:
        return identity_matrix(ZZ, m)

    AZZ = A.change_ring(ZZ)
    D, U, V = AZZ.smith_form()
    diag = diagonal_matrix([-1] + [1] * (m - 1))
    if U.det() == -1:
        U = diag * U
    if V.det() == -1:
        V = V * diag

    a = [U.row(i) * AZZ * V.column(i) for i in range(m)]
    b = prod(a[1:])

    Winv = identity_matrix(m)
    Winv[1, 0] = 1 - b
    Winv[0, 1] = -1
    Winv[1, 1] = b

    Xinv = identity_matrix(m)
    Xinv[0, 1] = a[1]

    Cp = diagonal_matrix(a[1:])
    Cp[0, 0] *= a[0]
    C = lift_for_SL(Cp, N)

    Cpp = block_diagonal_matrix(identity_matrix(1), C)
    Cpp[1, 0] = 1 - a[0]

    return (~U * Winv * Cpp * Xinv * ~V).change_ring(ZZ)
예제 #5
0
def signed_hermite_normal_form(A):
    """
    Signed Hermite normal form of an integer matrix A, see [PP19, Section 6].
    This is a normal form up to left-multiplication by invertible matrices and change of sign of the columns.
    A matrix in signed Hermite normal form is also in left Hermite normal form.
    """
    r = A.nrows()
    n = A.ncols()
    G_basis = []  # Z_2-basis of G
    m = 0  # rank of A[:,:j]

    for j in range(n):
        A = A.echelon_form()
        q = A[m, j] if m < r else 0  # pivot

        phi = []
        for S in G_basis:
            H, U = (A[:, :j] * S[:j, :j]).echelon_form(transformation=True)
            assert H == A[:, :j]
            phi.append(U)

        G_basis.append(diagonal_matrix([-1 if i == j else 1 for i in range(n)]))
        phi.append(diagonal_matrix([-1 if i < m else 1 for i in range(r)]))
        assert len(G_basis) == len(phi)

        for i in reversed(range(m)):
            # find possible values of A[i,j]
            x = A[i, j]
            if q > 0:
                x %= q

            orbit = [x]
            columns = [A[:, j]]
            construction = [identity_matrix(n)]  # which element of G gives a certain element of the orbit

            new_elements = True
            while new_elements:
                new_elements = False
                for h, U in enumerate(phi):
                    for k, v in enumerate(columns):
                        w = U * v
                        y = w[i, 0]
                        if q > 0:
                            y %= q
                        if y not in orbit:
                            orbit.append(y)
                            columns.append(w)
                            construction.append(G_basis[h] * construction[k])
                            new_elements = True

            assert len(orbit) in [1, 2, 4]

            # find action of G on the orbit
            action = []
            for h, U in enumerate(phi):
                if q > 0:
                    action.append({x: (U * columns[k])[i, 0] % q for k, x in enumerate(orbit)})
                else:
                    action.append({x: (U * columns[k])[i, 0] for k, x in enumerate(orbit)})

            # select the minimal possible value
            u = min(orbit)
            k = orbit.index(u)
            S = construction[k]

            # change A
            A = (A * S).echelon_form()
            assert A[i, j] == u

            # update the stabilizer G
            G_new_basis = []  # basis for the new stabilizer
            new_phi = []  # value of phi on the new basis elements
            complement_basis = {}  # dictionary of the form {x: h}, where G_basis[h] sends u to x
            for h, S in enumerate(G_basis):
                if action[h][u] == u:
                    # this basis element fixes u
                    G_new_basis.append(S)
                    new_phi.append(phi[h])

                elif action[h][u] in complement_basis:
                    # we already encountered a basis element which sends u to action[h][u]
                    G_new_basis.append(S * G_basis[complement_basis[action[h][u]]])
                    new_phi.append(phi[h] * phi[complement_basis[action[h][u]]])

                elif len(complement_basis) == 2:
                    # the product of the two basis elements of the complement sends u to action[h][u]
                    x, y = list(complement_basis)
                    G_new_basis.append(S * G_basis[complement_basis[x]] * G_basis[complement_basis[y]])
                    new_phi.append(phi[h] * phi[complement_basis[x]] * phi[complement_basis[y]])

                else:
                    # add S to the basis of the complement
                    complement_basis[action[h][u]] = h

            assert len(G_new_basis) + len(complement_basis) == len(G_basis)
            G_basis = G_new_basis
            phi = new_phi
            assert len(G_basis) == len(phi)

        if q != 0:
            m += 1

    return A