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)
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())
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)
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