def taut_polynomial_via_tree_and_smith(tri, angle, cycles = [], alpha = True, mode = "taut"): # set up assert tri.homology().rank() == 1 # need the polynomial ring to be a PID ZH = group_ring(tri, angle, cycles, alpha = alpha, ring = QQ) # ditto P = ZH.polynomial_ring() ET = edges_to_triangles_matrix(tri, angle, cycles, ZH, P, mode = mode) _, non_tree_faces, _ = spanning_dual_tree(tri) ET = ET.transpose() ET = Matrix([row for i, row in enumerate(ET) if i in non_tree_faces]).transpose() # compute via smith normal form ETs = ET.smith_form()[0] a = tri.countEdges() ETs_reduced = Matrix([row[:a] for row in ETs]) return normalise_poly(ETs_reduced.determinant(), ZH, P)
def faces_in_smith(triangulation, angle_structure, cycles): N = edge_equation_matrix_taut_reduced(triangulation, angle_structure, cycles) N = Matrix(N) N = N.transpose() return N.smith_form()
def order_of_euler_class(delta, E): """ Given the coboundary operator delta and an Euler two-cocycle E, returns k if [E] is k--torsion. By convention, returns zero if [E] is non-torsion. Note that the trivial element is 1--torsion. """ delta = Matrix(delta) E = vector(E) # Note that E is a coboundary if there is a one-cocycle C solving # # E = C*delta # # We can find C (if it exists at all) using Smith normal form. D, U, V = delta.smith_form() assert D == U*delta*V # So we are trying to solve # # C*delta = C*U.inverse()*D*V.inverse() = E # # for a one-cochain C. Multiply by V to get # # C*delta*V = C*U.inverse()*D = E*V # # Now set # # B = C*U.inverse(), and so B*U = C # # and rewrite to get # # B*U*delta*V = B*D = E*V # # So define E' by: Ep = E*V # Finally we attempt to solve B * D = Ep. Note that D is # diagonal: so if we can solve all of the equations # B[i] * D[i][i] == Ep[i] # with B[i] integers, then [E] = 0 in cohomology. diag = diagonal(D) if any( (diag[i] == 0 and Ep[i] != 0) for i in range(len(Ep)) ): return 0 # All zeros are at the end in Smith normal form. Since we've # passed the above we can now remove them. first_zero = diag.index(0) diag = diag[:first_zero] Ep = Ep[:first_zero] # Since diag[i] is (now) never zero we can divide to get the # fractions Ep[i]/diag[i] and then find the scaling that makes # them simultaneously integral. denoms = [ diag[i] / gcd(Ep[i], diag[i]) for i in range(len(Ep)) ] return lcm(denoms)