def fast_determinant_of_laurent_poly_matrix(A): """ Return the determinant of the given matrix up to a power of t^n, using the faster algorithm for polynomial entries. """ R = A.base_ring() if not hasattr(R, 'polynomial_ring'): assert False return det(A) expshift = -minimum_exponents(A.list()) P = PolynomialRing( R.base_ring(), R.variable_names()) # Note: P.polynomial_ring() doesn't work here! Ap = matrix(P, A.nrows(), A.ncols(), [convert_laurent_to_poly(p, expshift, P) for p in A.list()]) return det(Ap)
def compute_torsion(G, bits_prec, alpha=None, phi=None, phialpha=None, return_parts=False, return_as_poly=True, wada_conventions=False, symmetry_test=True): if alpha: F = alpha('a').base_ring() elif phialpha: F = phialpha('a').base_ring().base_ring() epsilon = ZZ(2)**(-bits_prec // 3) if not F.is_exact() else None big_epsilon = ZZ(2)**(-bits_prec // 5) if not F.is_exact() else None gens, rels = G.generators(), G.relators() k = len(gens) if phi == None: phi = MapToGroupRingOfFreeAbelianization(G, F) # Make sure this special algorithm applies. assert len(rels) == len(gens) - 1 and len(phi.range().gens()) == 1 # Want the first variable to be homologically nontrivial i0 = [i for i, g in enumerate(gens) if phi(g) != 1][0] gens = gens[i0:] + gens[:i0] if phialpha == None: phialpha = PhiAlpha(phi, alpha) # Boundary maps for chain complex if not wada_conventions: # Using the conventions of our paper. d2 = [[fox_derivative_with_involution(R, phialpha, g) for R in rels] for g in gens] d2 = block_matrix(d2, nrows=k, ncols=k - 1) d1 = [phialpha(g.swapcase()) - 1 for g in gens] d1 = block_matrix(d1, nrows=1, ncols=k) dsquared = d1 * d2 else: # Using those implicit in Wada's paper. d2 = [[fox_derivative(R, phialpha, g) for g in gens] for R in rels] d2 = block_matrix(sum(d2, []), nrows=k - 1, ncols=k) d1 = [phialpha(g) - 1 for g in gens] d1 = block_matrix(d1, nrows=k, ncols=1) dsquared = d2 * d1 if not matrix_has_small_entries(dsquared, epsilon): raise TorsionComputationError("(boundary)^2 != 0") T = last_square_submatrix(d2) if return_as_poly: T = fast_determinant_of_laurent_poly_matrix(T) else: T = det(T) B = first_square_submatrix(d1) B = det(B) if return_as_poly: T = clean_laurent_to_poly(T, epsilon) B = clean_laurent_to_poly(B, epsilon) else: T = clean_laurent(T, epsilon) B = clean_laurent(B, epsilon) if return_parts: return (T, B) q, r = T.quo_rem(B) ans = clean_laurent_to_poly(q, epsilon) if univ_abs(r) > epsilon: raise TorsionComputationError("Division failed") # Now do a quick sanity check if symmetry_test: coeffs = ans.coefficients() error = max( [univ_abs(a - b) for a, b in zip(coeffs, reversed(coeffs))]) if error > epsilon: raise TorsionComputationError( "Torsion polynomial doesn't seem symmetric") return ans
def isDegenerate(self): return det(self.matrix) == 0
def __init__(self, m): self.matrix = matrix(m) self.dim = self.matrix.nrows() if (self.dim != self.matrix.ncols() or not symmetric(self.matrix)): raise ValueError("Matrix must be square and symmetric.") self.det = det(self.matrix)