def space(self): r''' Calculates the space of cocyles modulo coboundaries, as a Z-module. TESTS: sage: from darmonpoints.sarithgroup import * sage: from darmonpoints.cohomology_abstract import * sage: from darmonpoints.ocmodule import * sage: GS = BigArithGroup(5, 6,1,use_shapiro=False,outfile='/tmp/darmonpoints.tmp') # optional - magma sage: G = GS.large_group() # optional - magma sage: V = OCVn(5,1) # optional - magma sage: Coh = CohomologyGroup(G,V,trivial_action = False) # optional - magma ''' verb = get_verbose() set_verbose(0) V = self.coefficient_module() R = V.base_ring() Vdim = V.dimension() G = self.group() gens = G.gens() ambient = R**(Vdim * len(gens)) # Now find the subspace of cocycles A = Matrix(R, Vdim * len(gens), 0) for r in G.get_relation_words(): Alist = self.fox_gradient(r) newA = block_matrix(Alist, nrows = 1) A = A.augment(newA.transpose()) A = A.transpose() cocycles = ambient.submodule([ambient(o) for o in A.right_kernel_matrix().rows()]) gmat = block_matrix([self._gen_pows[i][1] - 1 for i in range(len(G.gens()))], nrows = len(G.gens())) coboundaries = cocycles.submodule([ambient(o) for o in gmat.columns()]) ans = cocycles.quotient(coboundaries) set_verbose(verb) return ans
def diagonal_matrix(self): r""" Returns a diagonal matrix `D` and a matrix `T` such that `T^t A T = D` holds, where `(x, y, z) A (x, y, z)^t` is the defining polynomial of the conic ``self``. EXAMPLES: :: sage: c = Conic(QQ, [1,2,3,4,5,6]) sage: d, t = c.diagonal_matrix(); d, t ( [ 1 0 0] [ 1 -1 -7/6] [ 0 3 0] [ 0 1 -1/3] [ 0 0 41/12], [ 0 0 1] ) sage: t.transpose()*c.symmetric_matrix()*t [ 1 0 0] [ 0 3 0] [ 0 0 41/12] Diagonal matrices are only defined in characteristic different from `2`: :: sage: c = Conic(GF(4, 'a'), [0, 1, 1, 1, 1, 1]) sage: c.is_smooth() True sage: c.diagonal_matrix() Traceback (most recent call last): ... ValueError: The conic self (= Projective Conic Curve over Finite Field in a of size 2^2 defined by x*y + y^2 + x*z + y*z + z^2) has no symmetric matrix because the base field has characteristic 2 """ A = self.symmetric_matrix() B = self.base_ring() basis = [vector(B,{2:0,i:1}) for i in range(3)] for i in range(3): zerovalue = (basis[i]*A*basis[i].column()== 0) if zerovalue: for j in range(i+1,3): if basis[j]*A*basis[j].column() != 0: b = basis[i] basis[i] = basis[j] basis[j] = b zerovalue = False if zerovalue: for j in range(i+1,3): if basis[i]*A*basis[j].column() != 0: basis[i] = basis[i]+basis[j] zerovalue = False if not zerovalue: l = (basis[i]*A*basis[i].column()) for j in range(i+1,3): basis[j] = basis[j] - \ (basis[i]*A*basis[j].column())/l * basis[i] T = Matrix(basis).transpose() return T.transpose()*A*T, T
def diagonal_matrix(self): r""" Returns a diagonal matrix `D` and a matrix `T` such that `T^t A T = D` holds, where `(x, y, z) A (x, y, z)^t` is the defining polynomial of the conic ``self``. EXAMPLES: :: sage: c = Conic(QQ, [1,2,3,4,5,6]) sage: d, t = c.diagonal_matrix(); d, t ( [ 1 0 0] [ 1 -1 -7/6] [ 0 3 0] [ 0 1 -1/3] [ 0 0 41/12], [ 0 0 1] ) sage: t.transpose()*c.symmetric_matrix()*t [ 1 0 0] [ 0 3 0] [ 0 0 41/12] Diagonal matrices are only defined in characteristic different from `2`: :: sage: c = Conic(GF(4, 'a'), [0, 1, 1, 1, 1, 1]) sage: c.is_smooth() True sage: c.diagonal_matrix() Traceback (most recent call last): ... ValueError: The conic self (= Projective Conic Curve over Finite Field in a of size 2^2 defined by x*y + y^2 + x*z + y*z + z^2) has no symmetric matrix because the base field has characteristic 2 """ A = self.symmetric_matrix() B = self.base_ring() basis = [vector(B,{2:0,i:1}) for i in range(3)] for i in range(3): zerovalue = (basis[i]*A*basis[i].column()== 0) if zerovalue: for j in range(i+1,3): if basis[j]*A*basis[j].column() != 0: b = basis[i] basis[i] = basis[j] basis[j] = b zerovalue = False if zerovalue: for j in range(i+1,3): if basis[i]*A*basis[j].column() != 0: basis[i] = basis[i]+basis[j] zerovalue = False if not zerovalue: l = (basis[i]*A*basis[i].column()) for j in range(i+1,3): basis[j] = basis[j] - \ (basis[i]*A*basis[j].column())/l * basis[i] T = Matrix(basis).transpose() return T.transpose()*A*T, T
def LocalizedMomentMatrix(self, idx): """ Returns localized moment matrix corresponding to $g_{idx}$ """ tmp_vec = self.MonomialsVec(self.Relaxation - self.HalfDegs[idx]) m = Matrix(1, len(tmp_vec), tmp_vec) return self.Constraints[idx] * (m.transpose() * m)
def LocalizedMomentMatrix(self, idx): """ Returns localized moment matrix corresponding to $g_{idx}$ """ tmp_vec = self.MonomialsVec(self.Relaxation-self.HalfDegs[idx]) m = Matrix(1, len(tmp_vec), tmp_vec) return self.Constraints[idx]* (m.transpose() * m)
def decompose(self): """ Gives an SOS decomposition of f if exists as a list of polynomials of at most half degree of f. This method also fills the 'Info' as 'minimize' does. In addition, returns Info['is sos'] which is Boolean depending on the status of sdp solver. """ n = self.NumVars N0 = self.NumMonomials(n, self.MainPolyHlfDeg) N1 = self.NumMonomials(n, self.MainPolyTotDeg) self.MatSize = [N0, N1] vec = self.MonomialsVec(self.MainPolyHlfDeg) m = Matrix(1, N0, vec) Mmnt = m.transpose() * m Blck = [[] for i in range(N1)] C = [] h = Matrix(self.Field, N0, N0, 0) C.append(h) decomp = [] for i in range(N1): p = self.Monomials[i] A = self.Calpha(p, Mmnt) Blck[i].append(A) from SDP import SDP sos_sdp = SDP.sdp(solver=self.solver, Settings={'detail': self.detail}) sos_sdp.solve(C, self.PolyCoefFullVec(), Blck) if sos_sdp.Info['Status'] == 'Optimal': self.Info['status'] = 'Feasible' GramMtx = Matrix(sos_sdp.Info['X'][0]) try: self.Info[ 'Message'] = "A SOS decomposition of the polynomial were found." self.Info['is sos'] = True H1 = GramMtx.cholesky() tmpM = Matrix(1, N0, vec) decomp = list(tmpM * H1)[0] self.Info['Wall'] = sos_sdp.Info['Wall'] self.Info['CPU'] = sos_sdp.Info['CPU'] except: self.Info[ 'Message'] = "The given polynomial seems to be a sum of squares, but no SOS decomposition were extracted." self.Info['is sos'] = False self.Info['Wall'] = sos_sdp.Info['Wall'] self.Info['CPU'] = sos_sdp.Info['CPU'] else: self.Info[ 'Message'] = "The given polynomial is not a sum of squares." self.Info['status'] = 'Infeasible' self.Info['is sos'] = False self.Info["Size"] = self.MatSize return decomp
def linear_approximation_matrix(self): """ Return linear approximation matrix ``A`` for this S-box. Let ``i_b`` be the ``b``-th bit of ``i`` and ``o_b`` the ``b``-th bit of ``o``. Then ``v = A[i,o]`` encodes the bias of the equation ``sum( i_b * x_i ) = sum( o_b * y_i )`` if ``x_i`` and ``y_i`` represent the input and output variables of the S-box. See [He2002]_ for an introduction to linear cryptanalysis. EXAMPLES:: sage: from sage.crypto.sbox import SBox sage: S = SBox(7,6,0,4,2,5,1,3) sage: S.linear_approximation_matrix() [ 4 0 0 0 0 0 0 0] [ 0 0 0 0 2 2 2 -2] [ 0 0 -2 -2 -2 2 0 0] [ 0 0 -2 2 0 0 -2 -2] [ 0 2 0 2 -2 0 2 0] [ 0 -2 0 2 0 2 0 2] [ 0 -2 -2 0 0 -2 2 0] [ 0 -2 2 0 -2 0 0 -2] According to this matrix the first bit of the input is equal to the third bit of the output 6 out of 8 times:: sage: for i in srange(8): print(S.to_bits(i)[0] == S.to_bits(S(i))[2]) False True True True False True True True """ m = self.m n = self.n nrows = 1<<m ncols = 1<<n B = BooleanFunction(self.m) L = [] for j in range(ncols): for i in range(nrows): B[i] = ZZ(self(i)&j).popcount() L.append(B.walsh_hadamard_transform()) A = Matrix(ZZ, ncols, nrows, L) A = -A.transpose()/2 A.set_immutable() return A
def angle_equations(M): """ Given a snappy manifold M, returns the matrix of left-hand-sides of angle equations (that is, the tet, edge, and cusp equations). """ num_tet = M.num_tetrahedra() G = M.gluing_equations() T = Matrix(ZZ, [tet_vector(i, num_tet) for i in range(num_tet)]) return T.transpose().augment(G.transpose()).transpose() # sigh
def linear_approximation_matrix(self): """ Return linear approximation matrix ``A`` for this S-box. Let ``i_b`` be the ``b``-th bit of ``i`` and ``o_b`` the ``b``-th bit of ``o``. Then ``v = A[i,o]`` encodes the bias of the equation ``sum( i_b * x_i ) = sum( o_b * y_i )`` if ``x_i`` and ``y_i`` represent the input and output variables of the S-box. See [He2002]_ for an introduction to linear cryptanalysis. EXAMPLES:: sage: from sage.crypto.sbox import SBox sage: S = SBox(7,6,0,4,2,5,1,3) sage: S.linear_approximation_matrix() [ 4 0 0 0 0 0 0 0] [ 0 0 0 0 2 2 2 -2] [ 0 0 -2 -2 -2 2 0 0] [ 0 0 -2 2 0 0 -2 -2] [ 0 2 0 2 -2 0 2 0] [ 0 -2 0 2 0 2 0 2] [ 0 -2 -2 0 0 -2 2 0] [ 0 -2 2 0 -2 0 0 -2] According to this matrix the first bit of the input is equal to the third bit of the output 6 out of 8 times:: sage: for i in srange(8): print(S.to_bits(i)[0] == S.to_bits(S(i))[2]) False True True True False True True True """ m = self.m n = self.n nrows = 1<<m ncols = 1<<n B = BooleanFunction(self.m) L = [] for j in range(ncols): for i in range(nrows): B[i] = ZZ(self(i)&j).popcount() L.append(B.walsh_hadamard_transform()) A = Matrix(ZZ, ncols, nrows, L) A = -A.transpose()/2 A.set_immutable() return A
def linear_relation(self, List, Psi, verbose=True, prec=None): for Phi in List: assert Phi.valuation() >= 0, "Symbols must be integral" assert Psi.valuation() >= 0 R = self.base() Rbase = R.base() w = R.gen() d = len(List) if d == 0: if Psi.is_zero(): return [None, R(1)] else: return [None, 0] if prec is None: M, var_prec = Psi.precision_absolute() else: M, var_prec = prec p = self.prime() V = R**d RSR = LaurentSeriesRing(Rbase, R.variable_name()) VSR = RSR**self.source().ngens() List_TMs = [VSR(Phi.list_of_total_measures()) for Phi in List] Psi_TMs = VSR(Psi.list_of_total_measures()) A = Matrix(RSR, List_TMs).transpose() try: sol = V([vv.power_series() for vv in A.solve_right(Psi_TMs)]) except ValueError: #try "least squares" if verbose: print "Trying least squares." sol = (A.transpose() * A).solve_right(A.transpose() * Psi_TMs) #check precision (could make this better, checking each power of w) p_prec = M diff = Psi_TMs - sum([sol[i] * List_TMs[i] for i in range(len(List_TMs))]) for i in diff: for j in i.list(): if p_prec > j.valuation(): p_prec = j.valuation() if verbose: print "p-adic precision is now", p_prec #Is this right? sol = V([R([j.add_bigoh(p_prec) for j in i.list()]) for i in sol]) return [sol, R(-1)]
def projection_to_homology(tri,angle): non_tree_as_cycles = non_tree_edge_cycles(tri, angle) Q = Matrix(non_tree_as_cycles) S, U, V = faces_in_smith(tri,angle,[]) rank, dimU, dimV = rank_of_quotient(S) U = Matrix(U) P = U.transpose().inverse() P = P.delete_rows(range(0, dimU-rank)) A = P*Q return A
def decompose(self): """ Gives an SOS decomposition of f if exists as a list of polynomials of at most half degree of f. This method also fills the 'Info' as 'minimize' does. In addition, returns Info['is sos'] which is Boolean depending on the status of sdp solver. """ n = self.NumVars N0 = self.NumMonomials(n, self.MainPolyHlfDeg) N1 = self.NumMonomials(n, self.MainPolyTotDeg) self.MatSize = [N0, N1] vec = self.MonomialsVec(self.MainPolyHlfDeg) m = Matrix(1, N0, vec) Mmnt = m.transpose() * m Blck = [[] for i in range(N1)] C = [] h = Matrix(self.Field, N0, N0, 0) C.append(h) decomp = [] for i in range(N1): p = self.Monomials[i] A = self.Calpha(p, Mmnt) Blck[i].append(A) from SDP import SDP sos_sdp = SDP.sdp(solver = self.solver, Settings = {'detail':self.detail}) sos_sdp.solve(C, self.PolyCoefFullVec(), Blck) if sos_sdp.Info['Status'] == 'Optimal': self.Info['status'] = 'Feasible' GramMtx = Matrix(sos_sdp.Info['X'][0]) try: self.Info['Message'] = "A SOS decomposition of the polynomial were found." self.Info['is sos'] = True H1 = GramMtx.cholesky(); tmpM = Matrix(1, N0, vec) decomp = list(tmpM*H1)[0] self.Info['Wall'] = sos_sdp.Info['Wall'] self.Info['CPU'] = sos_sdp.Info['CPU'] except: self.Info['Message'] = "The given polynomial seems to be a sum of squares, but no SOS decomposition were extracted." self.Info['is sos'] = False self.Info['Wall'] = sos_sdp.Info['Wall'] self.Info['CPU'] = sos_sdp.Info['CPU'] else: self.Info['Message'] = "The given polynomial is not a sum of squares." self.Info['status'] = 'Infeasible' self.Info['is sos']= False self.Info["Size"] = self.MatSize return decomp
def monomial_multiplier(elts, ZH): if all(elt == 0 for elt in elts): # Zero (unlike other constants) has valuation -\infty. This # can show up as an issue when computing the big polynomial. # For an example, compute ET for # "kLLLMPPkcdgfehijjijhshassqhdqr_1222011022" return ZH(1) elts = [ZH(elt) for elt in elts] A = Matrix(ZZ, join_lists([uniform_exponents(p) for p in elts])) min_exp = tuple([min(row) for row in A.transpose()]) return ZH({min_exp: 1})
def poly_dual_basis(P, poly_basis): r""" Return a collection of polynomials which are dual under the differential bilinear form to a given homogeneous collection INPUT: - ``P`` -- a polynomial ring - ``poly_basis`` -- a collection of polynomials in ``P`` which are homogeneous and linearly independent OUTPUT: - the dual basis of the polynomials in ``poly_basis`` in their span EXAMPLES: sage: P.<x, y> = PolynomialRing(QQ) sage: poly_basis = (1, x, x+y) sage: poly_dual_basis(P, poly_basis) [1, x - y, y] sage: poly_basis = (1, 2*x - y, x^2, x^2 + x*y) sage: poly_dual_basis(P, poly_basis) [1, 2/5*x - 1/5*y, 1/2*x^2 - x*y, x*y] """ # recast poly_basis to ensure elements are all from P poly_basis = [P(p) for p in poly_basis] # compute max degree of basis polynomials for linear algebra computations deg = max([p.degree() for p in poly_basis]) # construct polynomial free module for linear algebra computations monoms = Monomials(P, (0, deg)) poly_module = PolynomialFreeModule(P, basis=monoms) # compute the values of the bilinear form <m|m> for basis monomials m bilinear_form_coeffs = [] for b in poly_module.basis().keys(): # each b is a monomial in P of degree at most deg b = P(b) bilinear_form_coeffs.append(prod(map(factorial, b.degrees()))) # compute dual basis A = Matrix([poly_module(p).to_vector() for p in poly_basis]) D = Matrix.diagonal(bilinear_form_coeffs, sparse=False) B = (A * D * A.transpose()).inverse() # reconstruct dual basis polynomials from corresponding vectors dual_basis = [] for col in B.columns(): q = sum([coeff * p for coeff, p in zip(col, poly_basis)]) dual_basis.append(q) return dual_basis
def space(self): r''' Calculates the space of cocyles modulo coboundaries, as a Z-module. TESTS: sage: from darmonpoints.sarithgroup import * sage: from darmonpoints.cohomology_abstract import * sage: from darmonpoints.ocmodule import * sage: GS = BigArithGroup(5, 6,1,use_shapiro=False,outfile='/tmp/darmonpoints.tmp') # optional - magma sage: G = GS.large_group() # optional - magma sage: V = OCVn(5,1) # optional - magma sage: Coh = CohomologyGroup(G,V,trivial_action = False) # optional - magma ''' verb = get_verbose() set_verbose(0) V = self.coefficient_module() R = V.base_ring() Vdim = V.dimension() G = self.group() gens = G.gens() ambient = R**(Vdim * len(gens)) # Now find the subspace of cocycles A = Matrix(R, Vdim * len(gens), 0) for nr, r in enumerate(G.get_relation_words()): set_verbose(verb) verbose('Processing relation word %s' % nr) set_verbose(0) Alist = [ MatrixSpace(R, Vdim, Vdim)(self.GA_to_local(o)) for o in self.fox_gradient(tuple(r)) ] newA = block_matrix(Alist, nrows=1) A = A.augment(newA.transpose()) A = A.transpose() cocycles = ambient.submodule( [ambient(o) for o in A.right_kernel_matrix().rows()]) gmat = block_matrix( [self._acting_matrix(g, Vdim) - 1 for g in G.gens()], nrows=len(G.gens())) coboundaries = cocycles.submodule([ambient(o) for o in gmat.columns()]) ans = cocycles.quotient(coboundaries) set_verbose(verb) return ans
def taut_cone_homological_dim(tri, angle): # find the dimension of the projection of the taut cone into # homology # boundaries of tets bdys = zeroth_coboundary(tri) bdys = matrix_transpose(bdys) rays = taut_rays(tri, angle) # but these are all 'upwards', so we need to fix the # co-orientations coorient = is_transverse_taut(tri, angle, return_type = "face_coorientations") rays = [[int(a * b) for a, b in zip(coorient, ray)] for ray in rays] # now work in the space of two-chains Rays = IntegerLattice(rays + bdys) Bdys = Matrix(bdys) Cobs = Bdys.transpose() Anns = Cobs.kernel() return Rays.intersection(Anns).dimension()
def to_elt(alp): ref = self.domain().reflection(alp) m = Matrix([ref(x).to_vector() for x in self.domain().basis()]) return self(m.transpose())
def make_regular_matroid_from_matroid(matroid): r""" Attempt to construct a regular representation of a matroid. INPUT: - ``matroid`` -- a matroid. OUTPUT: Return a `(0, 1, -1)`-matrix over the integers such that, if the input is a regular matroid, then the output is a totally unimodular matrix representing that matroid. EXAMPLES:: sage: from sage.matroids.utilities import make_regular_matroid_from_matroid sage: make_regular_matroid_from_matroid( ....: matroids.CompleteGraphic(6)).is_isomorphic( ....: matroids.CompleteGraphic(6)) True """ import sage.matroids.linear_matroid M = matroid if isinstance(M, sage.matroids.linear_matroid.RegularMatroid): return M rk = M.full_rank() # First create a reduced 0-1 matrix B = list(M.basis()) NB = list(M.groundset().difference(B)) dB = {} i = 0 for e in B: dB[e] = i i += 1 dNB = {} i = 0 for e in NB: dNB[e] = i i += 1 A = Matrix(ZZ, len(B), len(NB), 0) G = BipartiteGraph( A.transpose() ) # Sage's BipartiteGraph uses the column set as first color class. This is an edgeless graph. for e in NB: C = M.circuit(B + [e]) for f in C.difference([e]): A[dB[f], dNB[e]] = 1 # Change some entries from -1 to 1 entries = BipartiteGraph(A.transpose()).edges(labels=False) while len(entries) > 0: L = [G.shortest_path(u, v) for u, v in entries] mindex, minval = min(enumerate(L), key=lambda x: len(x[1])) # if minval = 0, there is an edge not spanned by the current subgraph. Its entry is free to be scaled any way. if len(minval) > 0: # Check the subdeterminant S = frozenset(L[mindex]) rows = [] cols = [] for i in S: if i < rk: rows.append(i) else: cols.append(i - rk) if A[rows, cols].det() != 0: A[entries[mindex][0], entries[mindex][1] - rk] = -1 G.add_edge(entries[mindex][0], entries[mindex][1]) entries.pop(mindex) return sage.matroids.linear_matroid.RegularMatroid(groundset=B + NB, reduced_matrix=A)
def automorphisms(self): """ Return a list of the automorphisms of the quadratic form. EXAMPLES:: sage: Q = DiagonalQuadraticForm(ZZ, [1,1,1]) sage: Q.number_of_automorphisms() # optional -- souvigner 48 sage: 2^3 * factorial(3) 48 sage: len(Q.automorphisms()) 48 :: sage: Q = DiagonalQuadraticForm(ZZ, [1,3,5,7]) sage: Q.number_of_automorphisms() # optional -- souvigner 16 sage: aut = Q.automorphisms() sage: len(aut) 16 sage: print([Q(M) == Q for M in aut]) [True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True] sage: Q = QuadraticForm(ZZ, 3, [2, 1, 2, 2, 1, 3]) sage: Q.automorphisms() [ [1 0 0] [-1 0 0] [0 1 0] [ 0 -1 0] [0 0 1], [ 0 0 -1] ] :: sage: Q = DiagonalQuadraticForm(ZZ, [1, -1]) sage: Q.automorphisms() Traceback (most recent call last): ... ValueError: not a definite form in QuadraticForm.automorphisms() """ ## only for definite forms if not self.is_definite(): raise ValueError("not a definite form in QuadraticForm.automorphisms()") ## Check for a cached value try: return self.__automorphisms except AttributeError: pass ## Find a basis of short vectors, and their lengths basis, pivot_lengths = self.basis_of_short_vectors(show_lengths=True) ## List the relevant vectors by length max_len = max(pivot_lengths) vector_list_by_length = self.short_primitive_vector_list_up_to_length(max_len + 1) ## Make the matrix A:e_i |--> v_i to our new basis. A = Matrix(basis).transpose() Ainv = A.inverse() # A1 = A.inverse() * A.det() # Q1 = A1.transpose() * self.matrix() * A1 ## This is the matrix of Q # Q = self.matrix() * A.det()**2 Q2 = A.transpose() * self.matrix() * A ## This is the matrix of Q in the new basis Q3 = self.matrix() ## Determine all automorphisms n = self.dim() Auto_list = [] # ct = 0 ## DIAGNOSTIC # print "n = " + str(n) # print "pivot_lengths = " + str(pivot_lengths) # print "vector_list_by_length = " + str(vector_list_by_length) # print "length of vector_list_by_length = " + str(len(vector_list_by_length)) for index_vec in mrange([len(vector_list_by_length[pivot_lengths[i]]) for i in range(n)]): M = Matrix([vector_list_by_length[pivot_lengths[i]][index_vec[i]] for i in range(n)]).transpose() # Q1 = self.matrix() # if self(M) == self: # ct += 1 # print "ct = ", ct, " M = " # print M # print if M.transpose() * Q3 * M == Q2: ## THIS DOES THE SAME THING! =( Auto_list.append(M * Ainv) ## Cache the answer and return the list self.__automorphisms = Auto_list self.__number_of_automorphisms = len(Auto_list) return Auto_list
def make_regular_matroid_from_matroid(matroid): r""" Attempt to construct a regular representation of a matroid. INPUT: - ``matroid`` -- a matroid. OUTPUT: Return a `(0, 1, -1)`-matrix over the integers such that, if the input is a regular matroid, then the output is a totally unimodular matrix representing that matroid. EXAMPLES:: sage: from sage.matroids.utilities import make_regular_matroid_from_matroid sage: make_regular_matroid_from_matroid( ....: matroids.CompleteGraphic(6)).is_isomorphic( ....: matroids.CompleteGraphic(6)) True """ import sage.matroids.linear_matroid M = matroid if isinstance(M, sage.matroids.linear_matroid.RegularMatroid): return M rk = M.full_rank() # First create a reduced 0-1 matrix B = list(M.basis()) NB = list(M.groundset().difference(B)) dB = {} i = 0 for e in B: dB[e] = i i += 1 dNB = {} i = 0 for e in NB: dNB[e] = i i += 1 A = Matrix(ZZ, len(B), len(NB), 0) G = BipartiteGraph(A.transpose()) # Sage's BipartiteGraph uses the column set as first color class. This is an edgeless graph. for e in NB: C = M.circuit(B + [e]) for f in C.difference([e]): A[dB[f], dNB[e]] = 1 # Change some entries from -1 to 1 entries = BipartiteGraph(A.transpose()).edges(labels=False) while len(entries) > 0: L = [G.shortest_path(u, v) for u, v in entries] mindex, minval = min(enumerate(L), key=lambda x: len(x[1])) # if minval = 0, there is an edge not spanned by the current subgraph. Its entry is free to be scaled any way. if len(minval) > 0: # Check the subdeterminant S = frozenset(L[mindex]) rows = [] cols = [] for i in S: if i < rk: rows.append(i) else: cols.append(i - rk) if A[rows, cols].det() != 0: A[entries[mindex][0], entries[mindex][1] - rk] = -1 G.add_edge(entries[mindex][0], entries[mindex][1]) entries.pop(mindex) return sage.matroids.linear_matroid.RegularMatroid(groundset=B + NB, reduced_matrix=A)
def is_globally_equivalent_to(self, other, return_matrix=False, check_theta_to_precision='sturm', check_local_equivalence=True): """ Determines if the current quadratic form is equivalent to the given form over ZZ. If return_matrix is True, then we also return the transformation matrix M so that self(M) == other. INPUT: a QuadraticForm OUTPUT: boolean, and optionally a matrix EXAMPLES:: sage: Q = DiagonalQuadraticForm(ZZ, [1,1,1,1]) sage: M = Matrix(ZZ, 4, 4, [1,2,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1]) sage: Q1 = Q(M) sage: Q.(Q1) # optional -- souvigner True sage: MM = Q.is_globally_equivalent_to(Q1, return_matrix=True) # optional -- souvigner sage: Q(MM) == Q1 # optional -- souvigner True :: sage: Q1 = QuadraticForm(ZZ, 3, [1, 0, -1, 2, -1, 5]) sage: Q2 = QuadraticForm(ZZ, 3, [2, 1, 2, 2, 1, 3]) sage: Q3 = QuadraticForm(ZZ, 3, [8, 6, 5, 3, 4, 2]) sage: Q1.is_globally_equivalent_to(Q2) # optional -- souvigner False sage: Q1.is_globally_equivalent_to(Q3) # optional -- souvigner True sage: M = Q1.is_globally_equivalent_to(Q3, True) ; M # optional -- souvigner [-1 -1 0] [ 1 1 1] [-1 0 0] sage: Q1(M) == Q3 # optional -- souvigner True :: sage: Q = DiagonalQuadraticForm(ZZ, [1, -1]) sage: Q.is_globally_equivalent_to(Q) Traceback (most recent call last): ... ValueError: not a definite form in QuadraticForm.is_globally_equivalent_to() """ ## only for definite forms if not self.is_definite(): raise ValueError, "not a definite form in QuadraticForm.is_globally_equivalent_to()" ## Check that other is a QuadraticForm #if not isinstance(other, QuadraticForm): if not is_QuadraticForm(other): raise TypeError, "Oops! You must compare two quadratic forms, but the argument is not a quadratic form. =(" ## Now use the Souvigner code by default! =) return other.is_globally_equivalent__souvigner(self, return_matrix) ## Note: We switch this because the Souvigner code has the opposite mapping convention to us. (It takes the second argument to the first!) ## ---------------------------------- Unused Code below --------------------------------------------------------- ## Check if the forms are locally equivalent if (check_local_equivalence == True): if not self.is_locally_equivalent_to(other): return False ## Check that the forms have the same theta function up to the desired precision (this can be set so that it determines the cusp form) if check_theta_to_precision != None: if self.theta_series(check_theta_to_precision, var_str='', safe_flag=False) != other.theta_series(check_theta_to_precision, var_str='', safe_flag=False): return False ## Make all possible matrices which give an isomorphism -- can we do this more intelligently? ## ------------------------------------------------------------------------------------------ ## Find a basis of short vectors for one form, and try to match them with vectors of that length in the other one. basis_for_self, self_lengths = self.basis_of_short_vectors(show_lengths=True) max_len = max(self_lengths) short_vectors_of_other = other.short_vector_list_up_to_length(max_len + 1) ## Make the matrix A:e_i |--> v_i to our new basis. A = Matrix(basis_for_self).transpose() Q2 = A.transpose() * self.matrix() * A ## This is the matrix of 'self' in the new basis Q3 = other.matrix() ## Determine all automorphisms n = self.dim() Auto_list = [] ## DIAGNOSTIC #print "n = " + str(n) #print "pivot_lengths = " + str(pivot_lengths) #print "vector_list_by_length = " + str(vector_list_by_length) #print "length of vector_list_by_length = " + str(len(vector_list_by_length)) for index_vec in mrange([len(short_vectors_of_other[self_lengths[i]]) for i in range(n)]): M = Matrix([short_vectors_of_other[self_lengths[i]][index_vec[i]] for i in range(n)]).transpose() if M.transpose() * Q3 * M == Q2: if return_matrix: return A * M.inverse() else: return True ## If we got here, then there is no isomorphism return False
def is_globally_equivalent__souvigner(self, other, return_transformation=False): """ Uses the Souvigner code to compute the number of automorphisms. INPUT: a QuadraticForm OUTPUT: boolean, and optionally a matrix EXAMPLES:: sage: Q = QuadraticForm(ZZ, 3, [1, 0, -1, 2, -1, 5]) sage: Q1 = QuadraticForm(ZZ, 3, [8, 6, 5, 3, 4, 2]) sage: M = Q.is_globally_equivalent__souvigner(Q1, True) ; M # optional -- souvigner [ 0 0 -1] [ 1 0 0] [-1 1 1] sage: Q1(M) == Q # optional -- souvigner True """ ## Write an input text file F_filename = '/tmp/tmp_isom_input' + str(random()) + ".txt" F = open(F_filename, 'w') #F = tempfile.NamedTemporaryFile(prefix='tmp_isom_input', suffix=".txt") ## This failed because it may have hyphens, which are interpreted badly by the Souvigner code. F.write("\n #1 \n") ## Write the first form n = self.dim() F.write(str(n) + "x0 \n") ## Use the lower-triangular form for i in range(n): for j in range(i+1): if i == j: F.write(str(2 * self[i,j]) + " ") else: F.write(str(self[i,j]) + " ") F.write("\n") ## Write the second form F.write("\n") n = self.dim() F.write(str(n) + "x0 \n") ## Use the lower-triangular form for i in range(n): for j in range(i+1): if i == j: F.write(str(2 * other[i,j]) + " ") else: F.write(str(other[i,j]) + " ") F.write("\n") F.flush() #print "Input filename = ", F.name #os.system("less " + F.name) ## Call the Souvigner automorphism code souvigner_isom_path = os.path.join(SAGE_LOCAL,'bin','Souvigner_ISOM') G1 = tempfile.NamedTemporaryFile(prefix='tmp_isom_ouput', suffix=".txt") #print "Output filename = ", G1.name #print "Executing the shell command: " + souvigner_isom_path + " '" + F.name + "' > '" + G1.name + "'" os.system(souvigner_isom_path + " '" + F.name + "' > '" + G1.name +"'") ## Read the output G2 = open(G1.name, 'r') line = G2.readline() if line.startswith("Error:"): raise RuntimeError, "There is a problem using the souvigner code... " + line elif line.find("not isomorphic") != -1: ## Checking if this text appears, if so then they're not isomorphic! return False else: ## Decide whether to read the transformation matrix, and return true if not return_transformation: F.close() G1.close() G2.close() os.system("rm -f " + F_filename) return True else: ## Try to read the isomorphism matrix M = Matrix(ZZ, n, n) for i in range(n): new_row_text = G2.readline().split() #print new_row_text for j in range(n): M[i,j] = new_row_text[j] ## Remove temporary files and return the value F.close() G1.close() G2.close() os.system("rm -f " + F_filename) if return_transformation: return M.transpose() else: return True #return True, M ## Raise and error if we're here: raise RuntimeError, "Oops! There is a problem..."
def to_elt(alp): ref = self.domain().reflection(alp) m = Matrix([ref(x).to_vector() for x in self.domain().basis()]) return self(m.transpose())
def automorphisms(self): """ Return a list of the automorphisms of the quadratic form. EXAMPLES:: sage: Q = DiagonalQuadraticForm(ZZ, [1,1,1]) sage: Q.number_of_automorphisms() # optional -- souvigner 48 sage: 2^3 * factorial(3) 48 sage: len(Q.automorphisms()) 48 :: sage: Q = DiagonalQuadraticForm(ZZ, [1,3,5,7]) sage: Q.number_of_automorphisms() # optional -- souvigner 16 sage: aut = Q.automorphisms() sage: len(aut) 16 sage: print([Q(M) == Q for M in aut]) [True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True] sage: Q = QuadraticForm(ZZ, 3, [2, 1, 2, 2, 1, 3]) sage: Q.automorphisms() [ [1 0 0] [-1 0 0] [0 1 0] [ 0 -1 0] [0 0 1], [ 0 0 -1] ] :: sage: Q = DiagonalQuadraticForm(ZZ, [1, -1]) sage: Q.automorphisms() Traceback (most recent call last): ... ValueError: not a definite form in QuadraticForm.automorphisms() """ ## only for definite forms if not self.is_definite(): raise ValueError("not a definite form in QuadraticForm.automorphisms()") ## Check for a cached value try: return self.__automorphisms except AttributeError: pass ## Find a basis of short vectors, and their lengths basis, pivot_lengths = self.basis_of_short_vectors(show_lengths=True) ## List the relevant vectors by length max_len = max(pivot_lengths) vector_list_by_length = self.short_primitive_vector_list_up_to_length(max_len + 1) ## Make the matrix A:e_i |--> v_i to our new basis. A = Matrix(basis).transpose() Ainv = A.inverse() #A1 = A.inverse() * A.det() #Q1 = A1.transpose() * self.matrix() * A1 ## This is the matrix of Q #Q = self.matrix() * A.det()**2 Q2 = A.transpose() * self.matrix() * A ## This is the matrix of Q in the new basis Q3 = self.matrix() ## Determine all automorphisms n = self.dim() Auto_list = [] #ct = 0 ## DIAGNOSTIC #print "n = " + str(n) #print "pivot_lengths = " + str(pivot_lengths) #print "vector_list_by_length = " + str(vector_list_by_length) #print "length of vector_list_by_length = " + str(len(vector_list_by_length)) for index_vec in mrange([len(vector_list_by_length[pivot_lengths[i]]) for i in range(n)]): M = Matrix([vector_list_by_length[pivot_lengths[i]][index_vec[i]] for i in range(n)]).transpose() #Q1 = self.matrix() #if self(M) == self: #ct += 1 #print "ct = ", ct, " M = " #print M #print if M.transpose() * Q3 * M == Q2: ## THIS DOES THE SAME THING! =( Auto_list.append(M * Ainv) ## Cache the answer and return the list self.__automorphisms = Auto_list self.__number_of_automorphisms = len(Auto_list) return Auto_list
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 is_globally_equivalent__souvigner(self, other, return_transformation=False): """ Uses the Souvigner code to compute the number of automorphisms. INPUT: a QuadraticForm OUTPUT: boolean, and optionally a matrix EXAMPLES:: sage: Q = QuadraticForm(ZZ, 3, [1, 0, -1, 2, -1, 5]) sage: Q1 = QuadraticForm(ZZ, 3, [8, 6, 5, 3, 4, 2]) sage: M = Q.is_globally_equivalent__souvigner(Q1, True) ; M # optional -- souvigner [ 0 0 -1] [ 1 0 0] [-1 1 1] sage: Q1(M) == Q # optional -- souvigner True """ ## Write an input text file F_filename = '/tmp/tmp_isom_input' + str(random()) + ".txt" F = open(F_filename, 'w') #F = tempfile.NamedTemporaryFile(prefix='tmp_isom_input', suffix=".txt") ## This failed because it may have hyphens, which are interpreted badly by the Souvigner code. F.write("\n #1 \n") ## Write the first form n = self.dim() F.write(str(n) + "x0 \n") ## Use the lower-triangular form for i in range(n): for j in range(i + 1): if i == j: F.write(str(2 * self[i, j]) + " ") else: F.write(str(self[i, j]) + " ") F.write("\n") ## Write the second form F.write("\n") n = self.dim() F.write(str(n) + "x0 \n") ## Use the lower-triangular form for i in range(n): for j in range(i + 1): if i == j: F.write(str(2 * other[i, j]) + " ") else: F.write(str(other[i, j]) + " ") F.write("\n") F.flush() #print "Input filename = ", F.name #os.system("less " + F.name) ## Call the Souvigner automorphism code souvigner_isom_path = os.path.join(SAGE_LOCAL, 'bin', 'Souvigner_ISOM') G1 = tempfile.NamedTemporaryFile(prefix='tmp_isom_ouput', suffix=".txt") #print "Output filename = ", G1.name #print "Executing the shell command: " + souvigner_isom_path + " '" + F.name + "' > '" + G1.name + "'" os.system(souvigner_isom_path + " '" + F.name + "' > '" + G1.name + "'") ## Read the output G2 = open(G1.name, 'r') line = G2.readline() if line.startswith("Error:"): raise RuntimeError("There is a problem using the souvigner code... " + line) elif line.find( "not isomorphic" ) != -1: ## Checking if this text appears, if so then they're not isomorphic! return False else: ## Decide whether to read the transformation matrix, and return true if not return_transformation: F.close() G1.close() G2.close() os.system("rm -f " + F_filename) return True else: ## Try to read the isomorphism matrix M = Matrix(ZZ, n, n) for i in range(n): new_row_text = G2.readline().split() #print new_row_text for j in range(n): M[i, j] = new_row_text[j] ## Remove temporary files and return the value F.close() G1.close() G2.close() os.system("rm -f " + F_filename) if return_transformation: return M.transpose() else: return True #return True, M ## Raise and error if we're here: raise RuntimeError("Oops! There is a problem...")
def is_globally_equivalent_to(self, other, return_matrix=False, check_theta_to_precision='sturm', check_local_equivalence=True): """ Determines if the current quadratic form is equivalent to the given form over ZZ. If return_matrix is True, then we also return the transformation matrix M so that self(M) == other. INPUT: a QuadraticForm OUTPUT: boolean, and optionally a matrix EXAMPLES:: sage: Q = DiagonalQuadraticForm(ZZ, [1,1,1,1]) sage: M = Matrix(ZZ, 4, 4, [1,2,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1]) sage: Q1 = Q(M) sage: Q.(Q1) # optional -- souvigner True sage: MM = Q.is_globally_equivalent_to(Q1, return_matrix=True) # optional -- souvigner sage: Q(MM) == Q1 # optional -- souvigner True :: sage: Q1 = QuadraticForm(ZZ, 3, [1, 0, -1, 2, -1, 5]) sage: Q2 = QuadraticForm(ZZ, 3, [2, 1, 2, 2, 1, 3]) sage: Q3 = QuadraticForm(ZZ, 3, [8, 6, 5, 3, 4, 2]) sage: Q1.is_globally_equivalent_to(Q2) # optional -- souvigner False sage: Q1.is_globally_equivalent_to(Q3) # optional -- souvigner True sage: M = Q1.is_globally_equivalent_to(Q3, True) ; M # optional -- souvigner [-1 -1 0] [ 1 1 1] [-1 0 0] sage: Q1(M) == Q3 # optional -- souvigner True :: sage: Q = DiagonalQuadraticForm(ZZ, [1, -1]) sage: Q.is_globally_equivalent_to(Q) Traceback (most recent call last): ... ValueError: not a definite form in QuadraticForm.is_globally_equivalent_to() """ ## only for definite forms if not self.is_definite(): raise ValueError( "not a definite form in QuadraticForm.is_globally_equivalent_to()") ## Check that other is a QuadraticForm #if not isinstance(other, QuadraticForm): if not is_QuadraticForm(other): raise TypeError( "Oops! You must compare two quadratic forms, but the argument is not a quadratic form. =(" ) ## Now use the Souvigner code by default! =) return other.is_globally_equivalent__souvigner( self, return_matrix ) ## Note: We switch this because the Souvigner code has the opposite mapping convention to us. (It takes the second argument to the first!) ## ---------------------------------- Unused Code below --------------------------------------------------------- ## Check if the forms are locally equivalent if (check_local_equivalence == True): if not self.is_locally_equivalent_to(other): return False ## Check that the forms have the same theta function up to the desired precision (this can be set so that it determines the cusp form) if check_theta_to_precision is not None: if self.theta_series( check_theta_to_precision, var_str='', safe_flag=False) != other.theta_series( check_theta_to_precision, var_str='', safe_flag=False): return False ## Make all possible matrices which give an isomorphism -- can we do this more intelligently? ## ------------------------------------------------------------------------------------------ ## Find a basis of short vectors for one form, and try to match them with vectors of that length in the other one. basis_for_self, self_lengths = self.basis_of_short_vectors( show_lengths=True) max_len = max(self_lengths) short_vectors_of_other = other.short_vector_list_up_to_length(max_len + 1) ## Make the matrix A:e_i |--> v_i to our new basis. A = Matrix(basis_for_self).transpose() Q2 = A.transpose() * self.matrix( ) * A ## This is the matrix of 'self' in the new basis Q3 = other.matrix() ## Determine all automorphisms n = self.dim() Auto_list = [] ## DIAGNOSTIC #print "n = " + str(n) #print "pivot_lengths = " + str(pivot_lengths) #print "vector_list_by_length = " + str(vector_list_by_length) #print "length of vector_list_by_length = " + str(len(vector_list_by_length)) for index_vec in mrange( [len(short_vectors_of_other[self_lengths[i]]) for i in range(n)]): M = Matrix([ short_vectors_of_other[self_lengths[i]][index_vec[i]] for i in range(n) ]).transpose() if M.transpose() * Q3 * M == Q2: if return_matrix: return A * M.inverse() else: return True ## If we got here, then there is no isomorphism return False
def _borcherds_product_polyhedron(self, pole_order, prec, verbose=False): r""" Construct a polyhedron representing a cone of Heegner divisors. For internal use in the methods borcherds_input_basis() and borcherds_input_Qbasis(). INPUT: - ``pole_order`` -- pole order - ``prec`` -- precision OUTPUT: a tuple consisting of an integral matrix M, a Polyhedron p, and a WeilRepModularFormsBasis X """ K = self.weilrep().base_field() O_K = K.maximal_order() S = self.gram_matrix() wt = self.input_wt() w = self.weilrep() rds = w.rds() norm_dict = w.norm_dict() X = w.nearly_holomorphic_modular_forms_basis(wt, pole_order, prec, verbose=verbose) N = len([g for g in rds if not norm_dict[tuple(g)]]) v_list = w.coefficient_vector_exponents(0, 1, starting_from=-pole_order, include_vectors=True) exp_list = [v[1] for v in v_list] d = w._ds_to_hds() v_list = [vector(d[tuple(v[0])]) for v in v_list] d = K.discriminant() positive = [] zero = vector([0] * (len(exp_list) + 1)) M = Matrix([ x.coefficient_vector(starting_from=-pole_order, ending_with=0)[:-N] for x in X ]) vs = M.transpose().kernel().basis() prec = floor(min(exp_list) / max(filter(bool, exp_list))) norm_list = w._norm_form().short_vector_list_up_to_length(prec + 1) units = w._units() _w = w._w() norm_list = [[a + b * _w for a, b in x] for x in norm_list] excluded_list = set([]) if d >= -4: if d == -4: f = w.multiplication_by_i() f2 = None else: f = w.multiplication_by_zeta() f2 = f * f ys = [] mult = len(units) // 2 for i, n in enumerate(exp_list): if i not in excluded_list: ieq = copy(zero) ieq[i + 1] = 1 v1 = v_list[i] if d >= -4: v2 = f(v1) j = next(j for j, x in enumerate(v_list) if exp_list[i] == exp_list[j] and (all( t in O_K for t in x - v2) or all(t in O_K for t in x + v2))) ieq[j + 1] += 1 if i != j: excluded_list.add(j) y = copy(zero) y[i + 1] = 1 y[j + 1] = -1 ys.append(y) if f2 is not None: v2 = f2(v1) j = next(j for j, x in enumerate(v_list) if exp_list[i] == exp_list[j] and (all( t in O_K for t in x - v2) or all(t in O_K for t in x + v2))) ieq[j + 1] += 1 if i != j: excluded_list.add(j) y = copy(zero) y[i + 1] = 1 y[j + 1] = -1 ys.append(y) for j, m in enumerate(exp_list[:i]): if j not in excluded_list: N = m / n if N in ZZ and N > 1: v2 = v_list[j] ieq[j + 1] = mult * any( all(t in O_K for t in x * v1 + u * v2) for x in norm_list[N] for u in units) positive.append(ieq) # * denominator(ieq) p = Polyhedron(ieqs=positive, eqns=[vector([0] + list(v)) for v in vs] + ys) return M, p, X