def test_diagonal_locations(self): # tests the helper function `diagonal_locations` which identifies where # the diagonal elements or blocks occur in the H matrix H = Matrix(GF(2), [[0, 0, 0], [0, 0, 0], [0, 0, 0]]) index_one, index_B = diagonal_locations(H) self.assertEqual(index_one, 3) self.assertEqual(index_B, -1) H = Matrix(GF(2), [[1, 0, 0], [0, 0, 0], [0, 0, 0]]) index_one, index_B = diagonal_locations(H) self.assertEqual(index_one, 0) self.assertEqual(index_B, -1) H = Matrix(GF(2), [[0, 1, 0], [1, 0, 0], [0, 0, 0]]) index_one, index_B = diagonal_locations(H) self.assertEqual(index_one, 3) self.assertEqual(index_B, 0) H = Matrix(GF(2), [[0, 1, 0], [1, 0, 0], [0, 0, 1]]) index_one, index_B = diagonal_locations(H) self.assertEqual(index_one, 2) self.assertEqual(index_B, 0) H = Matrix(GF(2), [[1, 0, 0], [0, 0, 1], [0, 1, 0]]) index_one, index_B = diagonal_locations(H) self.assertEqual(index_one, 0) self.assertEqual(index_B, 1)
def attack(p, t, a, B): """ Solves the hidden number problem using an attack based on the shortest vector problem. The hidden number problem is defined as finding y such that {x_i - t_i * y + a_i = 0 mod p}. More information: Breitner J., Heninger N., "Biased Nonce Sense: Lattice Attacks against Weak ECDSA Signatures in Cryptocurrencies" :param p: the modulus :param t: the t_i values :param a: the a_i values :param B: a bound on the x values :return: a tuple containing y, and a list of x values """ assert len(t) == len(a), "t and a lists should be of equal length." m = len(t) M = Matrix(QQ, m + 2, m + 2) for i in range(m): M[i, i] = p M[m] = t + [B / QQ(p), 0] M[m + 1] = list(map(lambda x: x - B // 2, a)) + [0, B] L = M.LLL() for row in L.rows(): y = (int(row[m] * p) // B) % p if y != 0 and row[m + 1] == B: return int(y), list(map(lambda x: int(x + B // 2), row[:m]))
def TwoPointsMinusInfinity(self, P, Q): # Creates a divisor class of P + Q - g*\infty maxlower = self.lower xP, yP = self.Rextraprec(P[0]), self.Rextraprec(P[1]) xQ, yQ = self.Rextraprec(Q[0]), self.Rextraprec(Q[1]) assert (self.f(xP) - yP**2).abs() <= (yP**2).abs() * self.almostzero assert (self.f(xQ) - yQ**2).abs() <= (yQ**2).abs() * self.almostzero WPQ = Matrix(self.Rextraprec, self.nZ, 3 * self.g + 7) # H0(3D0 - g*\infty) Ev = Matrix(self.Rextraprec, 2, 3 * self.g + 7) # basis of H0(3D0 - g*\infty) evaluated at P and Q Exps=[] for n in range( 2 * self.g + 4): Exps.append((n,0)) if n > self.g: Exps.append(( n - self.g - 1, 1)) for j in range( 3 * self.g + 7): u, v = Exps[j] for i in range( self.nZ ): x, y = self.Z[i] WPQ[ i, j ] = x**u * y**v Ev[0,j] = xP**u * yP**v Ev[1,j] = xQ**u * yQ**v K, upper, lower = Kernel(Ev, 2) assert self.threshold_check(upper, lower), "upper = %s lower = %s" % (RealField(35)(upper), RealField(35)(lower)) maxlower = max(maxlower, lower); #Returns H0(3*D0 - P - Q - g infty) # note that [P + Q + g infty - D0] ~ P + Q - infty return (WPQ * K).change_ring(self.R), maxlower
def delta_rank(f): """Returns the Gamma-rank of the function with LUT f. The Gamma-rank is the rank of the 2^{2n} \times 2^{2n} binary matrix M defined by M[x][y] = 1 if and only if x + y \in \Delta, where \Delta is defined as \Delta = \{ (a, b), DDT_f[a][b] == 2 \} ~. """ n = int(log(len(f), 2)) dim = 2**(2 * n) d = ddt(f) gamma = [(a << n) | b for a, b in itertools.product(xrange(1, 2**n), xrange(0, 2**n)) if d[a][b] == 2] mat_content = [] for x in xrange(0, dim): row = [0 for j in xrange(0, dim)] for y in gamma: row[oplus(x, y)] = 1 mat_content.append(row) mat_gf2 = Matrix(GF(2), dim, dim, mat_content) return mat_gf2.rank()
def extract_basis(v, N): """Returns a subset of v such that the span of these elements is at least as big as v. In particular, if v is a vector space, it returns a base. """ dim = rank_of_vector_set(v, N) i = 0 basis = [] while i < len(v) and v[i] == 0: i += 1 if i == len(v): return [] basis.append(v[i]) if dim == 1: return basis i += 1 r = Matrix(GF(2), 1, N, [tobin(x, N) for x in basis]).rank() while r < dim and i < len(v): new_basis = basis + [v[i]] new_r = Matrix(GF(2), len(new_basis), N, [tobin(x, N) for x in new_basis]).rank() if new_r == dim: return new_basis elif new_r > r: basis = new_basis r = new_r i += 1 return []
def test_lll(self): # do a quick verification of the basis from cryptopals B = Matrix( QQ, [[-_sage_const_2, _sage_const_0, _sage_const_2, _sage_const_0], [ _sage_const_1 / _sage_const_2, -_sage_const_1, _sage_const_0, _sage_const_0 ], [ -_sage_const_1, _sage_const_0, -_sage_const_2, _sage_const_1 / _sage_const_2 ], [-_sage_const_1, _sage_const_1, _sage_const_1, _sage_const_2]]) ex = Matrix(QQ, [[ _sage_const_1 / _sage_const_2, -_sage_const_1, _sage_const_0, _sage_const_0 ], [ -_sage_const_1, _sage_const_0, -_sage_const_2, _sage_const_1 / _sage_const_2 ], [ -_sage_const_1 / _sage_const_2, _sage_const_0, _sage_const_1, _sage_const_2 ], [ -_sage_const_3 / _sage_const_2, -_sage_const_1, _sage_const_2, _sage_const_0 ]]) re = matrix_utils.LLL(B) assert re == ex
def get_shift_and_width_for_coset(self, n): r""" Compute the shift and width of coset nr. n. """ if self._shift.has_key(n) and self._width.has_key(n): return self._shift[n], self._width[n] if not is_squarefree(self._level): raise NotImplementedError, "Only square-free levels implemented" G = Gamma0(self._level) A = self.coset_rep(n) a = A[0, 0] c = A[1, 0] width = G.cusp_width(Cusp(a, c)) if self._verbose > 0: print "width=", width Amat = Matrix(ZZ, 2, 2, A.matrix().list()) h = -1 for j in range(width): Th = Matrix(ZZ, 2, 2, [width, -j, 0, 1]) W = Amat * Th if self._verbose > 1: print "W=", W if self.is_involution(W): h = j break if h < 0: raise ArithmeticError, "Could not find shift!" self._shift[n] = h self._width[n] = width return h, width
def matrix(self): r""" Return the persistence matrix of self. EXAMPLES:: sage: from pGroupCohomology import CohomologyRing sage: CohomologyRing.doctest_setup() # reset, block web access, use temporary workspace sage: H = CohomologyRing(8,3) sage: H.make() sage: B = H.bar_code('UpperCentralSeries') sage: B.matrix() [ 1/(-t + 1) -1/(t^2 - 1) 1] [ 0 1/(t^2 - 2*t + 1) (-t - 1)/(t - 1)] [ 0 0 1/(t^2 - 2*t + 1)] """ from sage.all import Matrix M = Matrix(2 * self._length + 1, 2 * self._length + 1) for X in self._L.items(): if not (isinstance(X[1], int) or M.base_ring().has_coerce_map_from(X[1].parent())): M = M * X[1].parent()(1) M[X[0][0] + self._length, X[0][1] + self._length] = X[1] return M
def PointMinusInfinity(self, P, sign = 0): # Creates a divisor class of P- \inty_{(-1)**sign} maxlower = self.lower assert self.f.degree() == 2*self.g + 2; sqrtan = (-1)**sign * self.Rextraprec( sqrt( self.Rdoubleextraprec(self.an)) ); xP, yP = self.Rextraprec(P[0]),self.Rextraprec(P[1]) assert (self.f(xP) - yP**2).abs() <= (yP**2).abs() * self.almostzero if xP != 0 and self.a0 != 0: x0 = self.Rextraprec(0); else: x0 = xP while x0 == xP: x0 = self.Rextraprec(ZZ.random_element()) y0 = self.Rextraprec( sqrt( self.Rdoubleextraprec( self.f( x0 )))) # P0 = (x0, y0) WP1 = Matrix(self.Rextraprec, self.nZ, 3 * self.g + 6) # H0(3D0 - P0 - g \infty) = H0(3D0 - P0 - g(\infty_{+} + \infty_{-})) EvP = Matrix(self.Rextraprec, 1, 3 * self.g + 6) # the functions of H0(3D0-P0-g \infty) at P B = Matrix(self.Rextraprec, self.nZ, 3 * self.g + 5) # H0(3D0 - \infty_{sign} - P0 - g\infty) # adds the functions x - x0, (x - x0)**1, ..., (x - x0)**(2g+2) to it for j in xrange(2 * self.g + 2 ): for i, (x, y) in enumerate( self.Z ): WP1[ i, j] = B[ i, j] = (x - x0) ** (j + 1) EvP[ 0, j] = (xP - x0) ** (j + 1) # adds y - y0 for i, (x, y) in enumerate( self.Z ): WP1[ i, 2 * self.g + 2 ] = B[ i, 2 * self.g + 2] = y - y0 EvP[ 0, 2 * self.g + 2] = yP - y0 # adds (x - x0) * y ... (x-x0)**(g+1)*y for j in range(1, self.g + 2): for i, (x,y) in enumerate( self.Z ): WP1[ i, 2 * self.g + 2 + j ] = B[ i, 2 * self.g + 2 + j] = (x-x0)**j * y EvP[ 0, 2 * self.g + 2 + j] = (xP - x0)**j * yP # adds (x - x0)**(2g + 3) and y * (x - x0)**(g + 2) for i,(x,y) in enumerate(self.Z): WP1[ i, 3 * self.g + 4 ] = (x - x0)**( 2 * self.g + 3) WP1[ i, 3 * self.g + 5 ] = y * (x - x0)**(self.g + 2) B[ i, 3 * self.g + 4 ] = (x - x0)**( self.g + 2) * ( y - sqrtan * x**(self.g + 1) ) EvP[ 0, 3 * self.g + 4] = (xP - x0) ** ( 2 * self.g + 3) EvP[ 0, 3 * self.g + 5] = yP * (xP - x0)**(self.g + 2) # A = functions in H0(3D0-P0-g \infty) that vanish at P # = H**0(3D0 - P0 - P -g \infty) K, upper, lower = Kernel(EvP, EvP.ncols() - (3 * self.g + 5)) assert self.threshold_check(upper, lower), "upper = %s lower = %s" % (RealField(35)(upper), RealField(35)(lower)) maxlower = max(maxlower, lower); A = WP1 * K # A - B = P0 + P + g \infty - (\infty_{+} + P0 + g\infty) = P - \inty_{+} res, lower = self.Sub(A,B) maxlower = max(maxlower, lower); return res.change_ring(self.R), maxlower;
def upper_bound_index_cusps_in_JG_torsion(G, d, bound=60): """ INPUT: - G - a congruence subgroup - d - integer, the size of the rational cuspidal subgroup - bound (optional, default = 60) - the bound for the primes p up to which to use the hecke matrix `T_p - <p> - p` for bounding the torsion subgroup OUTPUT: - an integer `i` such that `(\#J_G(\QQ)_{tors})/d` is a divisor of `i`. EXAMPLES:: sage: from mdsage import * sage: d = rational_cuspidal_classgroup(Gamma1(23)).cardinality() sage: upper_bound_index_cusps_in_JG_torsion(Gamma1(23),d) 1 """ N = G.level() M = ModularSymbols(G) Sint = cuspidal_integral_structure(M) kill_mat = (M.star_involution().matrix().restrict(Sint) - 1) kill = kill_mat.transpose().change_ring(ZZ).row_module() for p in prime_range(3, bound): if not N % p == 0: kill += kill_torsion_coprime_to_q( p, M).restrict(Sint).change_ring(ZZ).transpose().row_module() if kill.matrix().is_square() and kill.matrix().determinant() == d: #print p break kill_mat = kill.matrix().transpose() #print N,"index of torsion in stuff killed",kill.matrix().determinant()/d if kill.matrix().determinant() == d: return 1 pm = integral_period_mapping(M) period_images1 = [ sum([M.coordinate_vector(M([c, infinity])) for c in cusps]) * pm for cusps in galois_orbits(G) ] m = (Matrix(period_images1) * kill_mat).stack(kill_mat) diag = m.change_ring(ZZ).echelon_form().diagonal() #print diag,prod(diag) assert prod(diag) == kill.matrix().determinant() / d period_images2 = [ M.coordinate_vector(M([c, infinity])) * pm for c in G.cusps() if c != Cusp(oo) ] m = (Matrix(period_images2) * kill_mat).stack(kill_mat) m, denom = m._clear_denom() diag = (m.change_ring(ZZ).echelon_form() / denom).diagonal() #print diag #print prod(i.numerator() for i in diag),"if this is 1 then :)" return prod(i.numerator() for i in diag)
def fractional_CRT_QQ(residues, ps_roots): residues_ZZ = [ r.lift() for r in residues]; primes = [p for p, _ in ps_roots] lift = CRT_list(residues_ZZ, primes); N = prod(primes); M = Matrix([[1 ,lift, N]]); short_vector = Matrix(M.transpose().kernel().basis()).LLL()[0] return -short_vector[0]/short_vector[1];
def __init__(self,M=None,genus_list=None): #print genus_list if M: self.M = copy(M) elif genus_list: self.M = Matrix(StrataGraph.R,len(genus_list)+1,1,[-1]+genus_list) else: self.M = Matrix(StrataGraph.R,1,1,-1)
def __init__(self, parent, matrix, inhomogeneous, is_zero=lambda p: False, relations=[]): ## Checking the input of matrix and vector if (not SAGE_element.is_Matrix(matrix)): matrix = Matrix(matrix) if (not SAGE_element.is_Vector(inhomogeneous)): inhomogeneous = vector(inhomogeneous) if (isinstance(parent, Wrap_w_Sequence_Ring)): parent = parent.base() ## Building the parent of the matrix and the vector pmatrix = matrix.parent().base() pinhom = inhomogeneous.parent().base() ## Setting the variables for the matrix, the parent and the vector self.__parent = pushout(pmatrix, pinhom) self.__matrix = matrix.change_ring(self.__parent) self.__inhomogeneous = inhomogeneous.change_ring(self.__parent) ## Setting the parent for the solutions if (not pushout(parent, self.__parent) == parent): try: self.__matrix = self.__matrix.change_ring(parent) self.__inhomogeneous = self.__inhomogeneous.change_ring(parent) self.__parent = parent except: raise TypeError( "The parent for the solutions must be an extension of the parent for the input" ) self.__solution_parent = parent ## Setting other variables if (relations is None): relations = [] self.__relations = [self.__parent(el) for el in relations] try: self.__gb = ideal(self.parent(), self.__relations).groebner_basis() except AttributeError: try: self.__gb = [ideal(self.parent(), self.__relations).gen()] except: self.__gb = [0] self.__is_zero = is_zero ## Creating the variables for the echelon form self.__echelon = None self.__transformation = None ## Creating the variables for the solutions self.__solution = None self.__syzygy = None
def from_nice_matrix(lists): Mx = Matrix(StrataGraph.R, len(lists), len(lists[0])) Mx[0, 0] = -1 Mx[0, :] = Matrix([lists[0]]) for v in range(1, len(lists)): if lists[v][0] in ZZ: Mx[v, 0] = lists[v][0] continue if lists[v][0].operator() == sage.symbolic.operators.add_vararg: operands = lists[v][0].operands() if len(operands) != 2: raise Exception("Input error!") genus = operands[1] #the genus kappas = operands[0] else: kappas = lists[v][0] genus = 0 if kappas.operator() == sage.symbolic.operators.mul_vararg: for operand in kappas.operands(): Mx[v, 0] += StrataGraph._kappaSR_monom_to_X(operand) Mx[v, 0] += genus else: Mx[v, 0] = StrataGraph._kappaSR_monom_to_X(kappas) + genus X = StrataGraph.Xvar for v in range(1, len(lists)): for edge in range(1, len(lists[0])): if lists[v][edge] in ZZ: Mx[v, edge] = lists[v][edge] else: for operand in lists[v][edge].operands(): if operand in ZZ: Mx[v, edge] += operand elif operand.operator() is None: #it is a ps or ps2 Mx[v, edge] += X elif operand.operator() == operator.pow: #it is ps^n or ps2^n Mx[v, edge] += X * operand.operands()[1] elif operand.operator( ) == sage.symbolic.operators.mul_vararg: #it is a ps^n*ps2^m op1, op2 = operand.operands() if op1.operator() is None: exp1 = 1 else: exp1 = op1.operand()[1] if op2.operator() == None: exp2 = 1 else: exp2 = op2.operand()[1] if exp1 >= exp2: Mx[v, edge] += exp1 * X + exp2 * X**2 else: Mx[v, edge] += exp1 * X**2 + exp2 * X return StrataGraph(Mx)
def __init__(self, M=None, genus_list=None): #print genus_list if M: self.M = copy(M) elif genus_list: self.M = Matrix(StrataGraph.R, len(genus_list) + 1, 1, [-1] + genus_list) else: self.M = Matrix(StrataGraph.R, 1, 1, -1)
def symmetrize_periods(Pa, Pb, tol=1e-4): r"""Returns symmetric a- and b-periods `Pa_symm` and `Pb_symm`, as well as the corresponding symplectic operator `Gamma` such that `Gamma [Pa \\ Pb] = [Pa_symm \\ Pb_symm]`. Parameters ---------- Pa : complex matrix Pb : complex matrix The a- and b-periods, respectively, of a genus `g` Riemann surface. tol : double (Default: 1e-4) Tolerance used to verify integrality of intermediate matrices. Dependent on precision of period matrices. Returns ------- Gamma : integer matrix The symplectic transformation operator. Pa : complex matrix Pb : complex matrix Symmetric a- and b-periods, respectively, of a genus `g` Riemann surface. Notes ----- The algorithm described in Kalla, Klein actually operates on the transposes of the a- and b-period matrices. """ # coerce from numpy, if necessary if isinstance(Pa, numpy.ndarray): Pa = Matrix(CDF, numpy.ascontiguousarray(Pa)) if isinstance(Pb, numpy.ndarray): Pb = Matrix(CDF, numpy.ascontiguousarray(Pb)) # use the transposes of the period matrices and coerce to Sage matrices Pa = Pa.T Pb = Pb.T # use above functions to obtain topological type matrix g,g = Pa.dimensions() R = involution_matrix(Pa, Pb, tol=tol) S = integer_kernel_basis(R) N1 = N1_matrix(Pa, Pb, S, tol=tol) H,Q = symmetric_block_diagonalize(N1) Gamma = symmetric_transformation_matrix(Pa, Pb, S, H, Q, tol=tol) # compute the corresponding symmetric periods stacked_periods = zero_matrix(CDF, 2*g, g) stacked_periods[:g,:] = Pa stacked_periods[g:,:] = Pb stacked_symmetric_periods = Gamma*stacked_periods Pa_symm = stacked_symmetric_periods[:g,:] Pb_symm = stacked_symmetric_periods[g:,:] # transpose results back Pa_symm = Pa_symm.T Pb_symm = Pb_symm.T return Pa_symm, Pb_symm
def symmetrize_periods(Pa, Pb, tol=1e-4): r"""Returns symmetric a- and b-periods `Pa_symm` and `Pb_symm`, as well as the corresponding symplectic operator `Gamma` such that `Gamma [Pa \\ Pb] = [Pa_symm \\ Pb_symm]`. Parameters ---------- Pa : complex matrix Pb : complex matrix The a- and b-periods, respectively, of a genus `g` Riemann surface. tol : double (Default: 1e-4) Tolerance used to verify integrality of intermediate matrices. Dependent on precision of period matrices. Returns ------- Gamma : integer matrix The symplectic transformation operator. Pa : complex matrix Pb : complex matrix Symmetric a- and b-periods, respectively, of a genus `g` Riemann surface. Notes ----- The algorithm described in Kalla, Klein actually operates on the transposes of the a- and b-period matrices. """ # coerce from numpy, if necessary if isinstance(Pa, numpy.ndarray): Pa = Matrix(CDF, numpy.ascontiguousarray(Pa)) if isinstance(Pb, numpy.ndarray): Pb = Matrix(CDF, numpy.ascontiguousarray(Pb)) # use the transposes of the period matrices and coerce to Sage matrices Pa = Pa.T Pb = Pb.T # use above functions to obtain topological type matrix g, g = Pa.dimensions() R = involution_matrix(Pa, Pb, tol=tol) S = integer_kernel_basis(R) N1 = N1_matrix(Pa, Pb, S, tol=tol) H, Q = symmetric_block_diagonalize(N1) Gamma = symmetric_transformation_matrix(Pa, Pb, S, H, Q, tol=tol) # compute the corresponding symmetric periods stacked_periods = zero_matrix(CDF, 2 * g, g) stacked_periods[:g, :] = Pa stacked_periods[g:, :] = Pb stacked_symmetric_periods = Gamma * stacked_periods Pa_symm = stacked_symmetric_periods[:g, :] Pb_symm = stacked_symmetric_periods[g:, :] # transpose results back Pa_symm = Pa_symm.T Pb_symm = Pb_symm.T return Pa_symm, Pb_symm
def as_polynomial_in_E4_and_E6(self,insert_in_db=True): r""" If self is on the full modular group writes self as a polynomial in E_4 and E_6. OUTPUT: -''X'' -- vector (x_1,...,x_n) with f = Sum_{i=0}^{k/6} x_(n-i) E_6^i * E_4^{k/4-i} i.e. x_i is the coefficient of E_6^(k/6-i)* """ if(self.level() != 1): raise NotImplementedError("Only implemented for SL(2,Z). Need more generators in general.") if(self._as_polynomial_in_E4_and_E6 is not None and self._as_polynomial_in_E4_and_E6 != ''): return self._as_polynomial_in_E4_and_E6 d = self._parent.dimension_modular_forms() # dimension of space of modular forms k = self.weight() K = self.base_ring() l = list() # for n in range(d+1): # l.append(self._f.q_expansion(d+2)[n]) # v=vector(l) # (self._f.coefficients(d+1)) v = vector(self.coefficients(range(d),insert_in_db=insert_in_db)) d = dimension_modular_forms(1, k) lv = len(v) if(lv < d): raise ArithmeticError("not enough Fourier coeffs") e4 = EisensteinForms(1, 4).basis()[0].q_expansion(lv + 2) e6 = EisensteinForms(1, 6).basis()[0].q_expansion(lv + 2) m = Matrix(K, lv, d) lima = floor(k / 6) # lima=k\6; if((lima - (k / 2)) % 2 == 1): lima = lima - 1 poldeg = lima col = 0 monomials = dict() while(lima >= 0): deg6 = ZZ(lima) deg4 = (ZZ((ZZ(k / 2) - 3 * lima) / 2)) e6p = (e6 ** deg6) e4p = (e4 ** deg4) monomials[col] = [deg4, deg6] eis = e6p * e4p for i in range(1, lv + 1): m[i - 1, col] = eis.coefficients()[i - 1] lima = lima - 2 col = col + 1 if (col != d): raise ArithmeticError("bug dimension") # return [m,v] if self._verbose > 0: wmf_logger.debug("m={0}".format(m, type(m))) wmf_logger.debug("v={0}".format(v, type(v))) try: X = m.solve_right(v) except: return "" self._as_polynomial_in_E4_and_E6 = [poldeg, monomials, X] return [poldeg, monomials, X]
def modular_univariate(f, N, m, t, X, early_return=True): """ Computes small modular roots of a univariate polynomial. More information: May A., "New RSA Vulnerabilities Using Lattice Reduction Methods (Section 3.2)" :param f: the polynomial :param N: the modulus :param m: the amount of g shifts to use :param t: the amount of h shifts to use :param X: an approximate bound on the roots :param early_return: try to return as early as possible (default: true) :return: a generator generating small roots of the polynomial """ f = f.monic().change_ring(ZZ) x = f.parent().gen() d = f.degree() B = Matrix(ZZ, d * m + t) row = 0 logging.debug("Generating g shifts...") for i in range(m): for j in range(d): g = x**j * N**(m - i) * f**i for col in range(row + 1): B[row, col] = g(x * X)[col] row += 1 logging.debug("Generating h shifts...") for i in range(t): h = x**i * f**m h = h(x * X) for col in range(row + 1): B[row, col] = h[col] row += 1 logging.debug("Executing the LLL algorithm...") B = B.LLL() logging.debug("Reconstructing polynomials...") for row in range(B.nrows()): new_polynomial = 0 for col in range(B.ncols()): new_polynomial += B[row, col] * x**col if new_polynomial.is_constant(): continue new_polynomial = new_polynomial(x / X).change_ring(ZZ) for x0, _ in new_polynomial.roots(): yield int(x0) if early_return: # Assuming that the first "good" polynomial in the lattice doesn't provide roots, we return. return
def tu_decomposition(s, v, verbose=False): """Using the knowledge that v is a subspace of Z_s of dimension n, a TU-decomposition (as defined in [Perrin17]) of s is performed and T and U are returned. """ N = int(log(len(s), 2)) # building B basis = extract_basis(v[0], N) t = len(basis) basis = complete_basis(basis, N) B = Matrix(GF(2), N, N, [tobin(x, N) for x in reversed(basis)]) if verbose: print "B= (rank={})\n{}".format(B.rank(), B.str()) # building A basis = complete_basis(extract_basis(v[1], N), N) A = Matrix(GF(2), N, N, [tobin(x, N) for x in reversed(basis)]) if verbose: print "A= (rank={})\n{}".format(A.rank(), A.str()) # building linear equivalent s_prime s_prime = [ apply_bin_mat(s[apply_bin_mat(x, A.inverse())], B) for x in xrange(0, 2**N) ] # TU decomposition of s' T, U = get_tu_open(s_prime, t) if verbose: print "T=[" for i in xrange(0, 2**(N - t)): print " {} {}".format(T[i], is_permutation(T[i])) print "]\nU=[" for i in xrange(0, 2**t): print " {} {}".format(U[i], is_permutation(U[i])) print "]" return T, U
def convert_magma_matrix_to_sage(m, K): try: return Matrix(K, m.sage()); except: pass from re import sub text = str(m) text = sub('\[\s+', '[', text) text = sub('\s+', ' ', text) text = '['+text.replace(' ',', ').replace('\n',', ')+']' return Matrix(K, sage_eval(text, locals={'r': K.gens()}))
def involution_matrix(Pa, Pb, tol=1e-4): r"""Returns the transformation matrix `R` corresponding to the anti-holomorphic involution on the periods of the Riemann surface. Given an aritrary `2g x g` period matrix `[Pa, Pb]^T` of a genus `g` Riemann surface `X` the action of the anti-holomorphic involution on `X` of these periods is given by left-multiplication by a `2g x 2g` matrix `R`. That is, .. math:: [\tau P_a^T, \tau P_b^T]^T = R [P_a^T, P_b^T]^T Parameters ---------- Pa : complex matrix Pb : complex matrix The a- and b-periods, respectively, of a genus `g` Riemann surface. tol : double (Default: 1e-4) Tolerance used to veryify integrality of transformation matrix. Dependent on precision of period matrices. Returns ------- R : complex matrix The anti-holomorphic involution matrix. Todo ---- For numerical stability, replace matrix inversion with linear system solves. """ g, g = Pa.dimensions() R_RDF = Matrix(RDF, 2 * g, 2 * g) Ig = identity_matrix(RDF, g) M = Im(Pb.T) * Re(Pa) - Im(Pa.T) * Re(Pb) Minv = M.inverse() R_RDF[:g, :g] = (2 * Re(Pb) * Minv * Im(Pa.T) + Ig).T R_RDF[:g, g:] = -2 * Re(Pa) * Minv * Im(Pa.T) R_RDF[g:, :g] = 2 * Re(Pb) * Minv * Im(Pb.T) R_RDF[g:, g:] = -(2 * Re(Pb) * Minv * Im(Pa.T) + Ig) R = R_RDF.round().change_ring(ZZ) # sanity check: make sure that R_RDF is close to integral. we perform this # test here since the matrix returned should be over ZZ error = (R_RDF.round() - R_RDF).norm() if error > tol: raise ValueError( "The anti-holomorphic involution matrix is not " "integral. Try increasing the precision of the input " "period matrices." ) return R
def get_ccz_equivalent_permutation(l, v0, v1, verbose=False): N = int(log(len(l), 2)) mask = sum(int(1 << i) for i in xrange(0, N)) L = v0 + v1 L = Matrix(GF(2), 2 * N, 2 * N, [tobin(x, 2 * N) for x in L]).transpose() if verbose: print "\n\nrank={}\n{}".format(L.rank(), L.str()) new_l = [[0 for b in xrange(0, 2**N)] for a in xrange(0, 2**N)] for a, b in itertools.product(xrange(0, 2**N), xrange(0, 2**N)): v = apply_bin_mat((a << N) | b, L) a_prime, b_prime = v >> N, v & mask new_l[a][b] = l[a_prime][b_prime] return invert_lat(new_l)
def involution_matrix(Pa, Pb, tol=1e-4): r"""Returns the transformation matrix `R` corresponding to the anti-holomorphic involution on the periods of the Riemann surface. Given an aritrary `2g x g` period matrix `[Pa, Pb]^T` of a genus `g` Riemann surface `X` the action of the anti-holomorphic involution on `X` of these periods is given by left-multiplication by a `2g x 2g` matrix `R`. That is, .. math:: [\tau P_a^T, \tau P_b^T]^T = R [P_a^T, P_b^T]^T Parameters ---------- Pa : complex matrix Pb : complex matrix The a- and b-periods, respectively, of a genus `g` Riemann surface. tol : double (Default: 1e-4) Tolerance used to veryify integrality of transformation matrix. Dependent on precision of period matrices. Returns ------- R : complex matrix The anti-holomorphic involution matrix. Todo ---- For numerical stability, replace matrix inversion with linear system solves. """ g,g = Pa.dimensions() R_RDF = Matrix(RDF, 2*g, 2*g) Ig = identity_matrix(RDF, g) M = Im(Pb.T)*Re(Pa) - Im(Pa.T)*Re(Pb) Minv = M.inverse() R_RDF[:g,:g] = (2*Re(Pb)*Minv*Im(Pa.T) + Ig).T R_RDF[:g,g:] = -2*Re(Pa)*Minv*Im(Pa.T) R_RDF[g:,:g] = 2*Re(Pb)*Minv*Im(Pb.T) R_RDF[g:,g:] = -(2*Re(Pb)*Minv*Im(Pa.T) + Ig) R = R_RDF.round().change_ring(ZZ) # sanity check: make sure that R_RDF is close to integral. we perform this # test here since the matrix returned should be over ZZ error = (R_RDF.round() - R_RDF).norm() if error > tol: raise ValueError("The anti-holomorphic involution matrix is not " "integral. Try increasing the precision of the input " "period matrices.") return R
def attack(p, x1, y1, x2, y2): """ Recovers the a and b parameters from an elliptic curve when two points are known. :param p: the prime of the curve base ring :param x1: the x coordinate of the first point :param y1: the y coordinate of the first point :param x2: the x coordinate of the second point :param y2: the y coordinate of the second point :return: a tuple containing the a and b parameters of the elliptic curve """ m = Matrix(GF(p), [[x1, 1], [x2, 1]]) v = vector(GF(p), [y1**2 - x1**3, y2**2 - x2**3]) a, b = m.solve_right(v) return int(a), int(b)
def __init__(self, parent, matrix, is_zero=lambda p: False, relations=[]): ## Checking the parent parameter if (parent.is_field()): parent = parent.base() if (not (isUniPolynomial(parent) or isMPolynomial(parent))): raise TypeError( "The parent for this algorithm must be a polynomial ring.\n\t Got: %s" % parent) ## Checking the matrix input matrix = Matrix(parent, matrix) super().__init__(parent, matrix, vector(parent, matrix.ncols() * [0]), is_zero, relations)
def _get_system_product(self, other, ncols, inhom=False): r''' Method to compute an ansatz system for the addition of a fixed size. This method computes an ansatz system for the addition of two solutions to ``self`` and ``other`` respectively. Namely, if `f(x)` is a solution to ``self`` and `g(x)` is a solution to ``other``, then we can consider the module: .. MATH:: M = \langle \partial^i(f)\partial^j(g)\ :\ i,j\in \mathbb{n}\rangle. which (since `f(x)` and `g(x)` are annihilated by ``self`` and ``other``) is a finitely generated module. Hence, the module generated by `f(x)+g(x)` is also finitely generated. This method creates an ansatz system in the module `M` for computing the linear relation between `h(x) = f(x)g(x)` and its derivatives. INPUT: * ``other``: the operator for the second operand. * ``ncols``: size of the desired system (in number of columns) * ``inhom``: if ``True``, the method return the ansazt matrix system together with a vector representing the inhomogeneous term of the system. OUTPUT: The ansazt system for compute a linear relation with `\partial^{ncols}(f(x)g(x))` and its previous derivatives within the module `M`. ''' from ajpastor.misc.matrix import vector_derivative as der ## Controlling the input ncols if (ncols < 0): raise ValueError("The number of columns must be a natural number") d_matrix = self._get_derivation_matrix_product(other) ## Base case: 0 column, only inhomogeneous term if (ncols == 0): v = self._get_vector_product(other) system = Matrix(d_matrix.parent().base(), [[]]) else: # General case, we build the next column aux_s, new_v = self._get_system_product(other, ncols - 1, True) system = Matrix(aux_s.columns() + [new_v]).transpose() v = der(d_matrix, new_v, self.derivate()) if (inhom): return self._post_proc(system), v else: return self._post_proc(system)
def incidence_matrix_from_multiplicities(n, mu): m = sum(mu.values()) A = Matrix(n, m) j = 0 for support, multiplicity in mu.items(): if multiplicity < 0: raise ValueError('invalid multiplicity') if any(x not in range(n) for x in support): raise ValueError('invalid support') col = [1 if i in support else 0 for i in range(n)] for _ in range(multiplicity): A.set_column(j, col) j += 1 return A
def _save(self, file, var_name='L'): from sage.all import Matrix HH = self.hyperplane_arrangement.parent() A = Matrix( map(lambda H: H.coefficients(), self.hyperplane_arrangement.hyperplanes())).rows() CR = tuple(map(lambda T: tuple(T), self.poset.cover_relations())) FL = self.flat_labels FL_tup = tuple([tuple([x, list(FL[x])]) for x in FL.keys()]) del FL dict_builder = "FL = {x[0] : Set(x[1]) for x in FL_tup}\n" with open(file, "w") as F: F.write( "from sage.all import HyperplaneArrangements, QQ, Poset, Set\n" ) F.write("import hypigu as hi\n") F.write("H = HyperplaneArrangements(QQ, {0})\n".format( HH.variable_names())) del HH F.write("A = H({0})\n".format(A).replace("), ", "),\n")) del A F.write("CR = {0}\n".format(CR).replace("), ", "),\n")) del CR F.write( "P = Poset([range({0}), CR], cover_relations=True)\n".format( len(self.poset._elements))) F.write("FL_tup = {0}\n".format(FL_tup).replace("), ", "),\n")) F.write(dict_builder) F.write("del FL_tup\n") F.write( "{0} = hi.LatticeOfFlats(A, poset=P, flat_labels=FL)\n".format( var_name)) F.write("del H, A, CR, P, FL\n") F.write("print('Loaded a lattice of flats. Variable name: {0}')". format(var_name))
def FZ_matrix_pushforward_basis(self,r): """ Return the matrix of Faber-Zagier relations, using the "pushforward" basis, NOT the kappa monomial basis that the rest of the code uses. :param r: The codimension. :rtype: :class:`Matrix` The columns correspond to the basis elements of the Strata algebra, and each row is a relation. This matrix should be the same as Pixton's original ``tautrel.sage`` program after permuting columns. :: sage: from strataalgebra import * sage: s = StrataAlgebra(QQ,0,(1,2,3,4)) sage: s.FZ_matrix_pushforward_basis(1) [ -9/4 -9/4 -9/4 -153/4 45/4 45/4 45/4 45/4] [ 3/2 3/2 3/2 -33/2 -21/2 15/2 15/2 15/2] [ 3/2 3/2 3/2 -33/2 15/2 -21/2 15/2 15/2] [ 3/2 3/2 3/2 -33/2 15/2 15/2 -21/2 15/2] [ 3/2 3/2 3/2 -33/2 15/2 15/2 15/2 -21/2] [ -15/4 21/4 21/4 -15/4 -21/4 -21/4 15/4 15/4] [ 21/4 -15/4 21/4 -15/4 -21/4 15/4 -21/4 15/4] [ 21/4 21/4 -15/4 -15/4 -21/4 15/4 15/4 -21/4] [ 21/4 21/4 -15/4 -15/4 15/4 -21/4 -21/4 15/4] [ 21/4 -15/4 21/4 -15/4 15/4 -21/4 15/4 -21/4] [ -15/4 21/4 21/4 -15/4 15/4 15/4 -21/4 -21/4] [ -15/4 -15/4 -15/4 -15/4 15/4 15/4 15/4 15/4] .. SEEALSO :: :meth:`~strataalgebra.StrataAlgebra.FZ_matrix` """ return Matrix(self.list_all_FZ(r))
def _lof_from_matroid(A=None, matroid=None): from sage.all import Set, Matrix, Matroid, Poset from functools import reduce if A != None: rows = list(map(lambda H: H.coefficients()[1:], A.hyperplanes())) mat = Matrix(A.base_ring(), rows).transpose() M = Matroid(mat) n = len(A) lbl_map = lambda S: S else: M = matroid.simplify() n = len(M.groundset()) grd_list = list(M.groundset()) l_map = {x: grd_list.index(x) for x in grd_list} lbl_map = lambda S: frozenset([l_map[x] for x in S]) L = M.lattice_of_flats() rank_r = lambda L, r: list( map(lbl_map, filter(lambda x: L.rank(x) == r, L._elements))) rank_1 = [frozenset([k]) for k in range(n)] ranks = reduce(lambda x, y: x + y, [rank_r(L, r) for r in range(2, L.rank() + 1)], [L.bottom()] + rank_1) P = Poset(L, element_labels={x: ranks.index(lbl_map(x)) for x in L._elements}) adj_set = lambda S: Set([x + 1 for x in S]) label_dict = {i: adj_set(ranks[i]) for i in range(len(L))} if A != None: hyp_dict = {i: A[list(ranks[i])[0]] for i in range(1, n + 1)} else: hyp_dict = None return [P, label_dict, hyp_dict]
def reduced_FZ_param_list(G,v,g,d,n): X = StrataGraph.Xvar params = FZ_param_list(n,tuple(range(1,d+1))) graph_params = [] M = Matrix(StrataGraph.R,2,d+1) M[0,0] = -1 for i in range(1,d+1): M[0,i] = i for p in params: G_copy = StrataGraph(G.M) M[1,0] = -g-1 for j in p[0]: M[1,0] += X**j for i in range(1,d+1): M[1,i] = 1 + p[1][i-1][1][0]*X G_p = StrataGraph(M) G_copy.replace_vertex_with_graph(v,G_p) graph_params.append([p,G_copy]) params_reduced = [] graphs_seen = [] for x in graph_params: x[1].compute_invariant() good = True for GG in graphs_seen: if graph_isomorphic(x[1],GG): good = False break if good: graphs_seen.append(x[1]) params_reduced.append(x[0]) return params_reduced
def FZ_matrix(self,r): """ Return the matrix of Faber-Zagier relations. :param r: The codimension. :rtype: :class:`Matrix` The columns correspond to the basis elements of the Strata algebra, and each row is a relation. Notice that this matrix considers the kappa classes to be in the monomial basis. Thus, is different than the output of Pixton's original ``taurel.sage`` program. :: sage: from strataalgebra import * sage: s = StrataAlgebra(QQ,0,(1,2,3,4)) sage: s.FZ_matrix(1) [ -9/4 -9/4 -9/4 -153/4 45/4 45/4 45/4 45/4] [ 3/2 3/2 3/2 -33/2 -21/2 15/2 15/2 15/2] [ 3/2 3/2 3/2 -33/2 15/2 -21/2 15/2 15/2] [ 3/2 3/2 3/2 -33/2 15/2 15/2 -21/2 15/2] [ 3/2 3/2 3/2 -33/2 15/2 15/2 15/2 -21/2] [ -15/4 21/4 21/4 -15/4 -21/4 -21/4 15/4 15/4] [ 21/4 -15/4 21/4 -15/4 -21/4 15/4 -21/4 15/4] [ 21/4 21/4 -15/4 -15/4 -21/4 15/4 15/4 -21/4] [ 21/4 21/4 -15/4 -15/4 15/4 -21/4 -21/4 15/4] [ 21/4 -15/4 21/4 -15/4 15/4 -21/4 15/4 -21/4] [ -15/4 21/4 21/4 -15/4 15/4 15/4 -21/4 -21/4] [ -15/4 -15/4 -15/4 -15/4 15/4 15/4 15/4 15/4] .. SEEALSO :: :meth:`~strataalgebra.StrataAlgebra.FZ_matrix_pushforward_basis`, :meth:`~strataalgebra.StrataAlgebraElement.in_kernel` """ return Matrix(self.list_all_FZ(r))*self._convert_kappa_basis(r)
def build_matrix(P, M, c=1000): factors_M = factor(M) rows = [] # add logarithms for p in P: row = [] for q, e in factors_M: row.extend(Zmod(q**e)(p).generalised_log() ) # generalised_log() uses unit_gens() generators row = [c * x for x in row] # multiply logs by a large constant to help LLL rows.append(row) height = len(rows) width = len(rows[0]) # add unit matrix for i, row in enumerate(rows): row.extend([1 if j == i else 0 for j in range(0, height)]) # add group orders generators_M = [g for q, e in factors_M for g in Zmod(q**e).unit_gens()] for i, g in enumerate(generators_M): rows.append([ g.multiplicative_order() * c if j == i else 0 for j in range(0, width + height) ]) return Matrix(rows)
def random_matrix_eigenvalues(F,n): r""" Give a random matrix together with its eigenvalues. """ l=list() M = MatrixSpace(F,n) U = Matrix(F,n) D = Matrix(F,n) for i in xrange(n): x = F.random_element() l.append(x) D[i,i]=x # now we need a unitary matrix: # make a random choice of vectors and use Gram-Schmidt to orthogonolize U = random_unitary_matrix(F,n) UT = U.transpose().conjugate() A = U*D*UT l.sort(cmp=my_abscmp) return A,U,l
def modular_unit_lattice(G,return_ambient=True,ambient_degree_zero=True): M=G.modular_symbols() period_mapping=integral_period_mapping(M) H1QQ=QQ**period_mapping.ncols() cusps=G.cusps() assert cusps[-1]==Cusp(infinity) n=len(cusps) D=ZZ**n D0=D.submodule_with_basis([D.gen(i)-D.gen(n-1) for i in xrange(n-1)]) period_images=[M.coordinate_vector(M([c,infinity]))*period_mapping for c in cusps if not c.is_infinity()] m=Matrix(period_images).transpose().augment(H1QQ.basis_matrix()).transpose() mint=(m*m.denominator()).change_ring(ZZ) kernel=mint.kernel() F0=kernel.basis_matrix().change_ring(ZZ).transpose()[:n-1].transpose() F0inD0=D0.submodule([D0.linear_combination_of_basis(i) for i in F0]) if return_ambient and ambient_degree_zero: return F0inD0,D0 if return_ambient: return F0inD0,D return F0inD0
def get_T1(K, S, unit_first=True, verbose=False): # Sx is a list of generators of K(S,2) with the unit first or last, assuming h(K)=1 u = -1 if K==QQ else K(K.unit_group().torsion_generator()) from KSp import IdealGenerator Sx = [IdealGenerator(P) for P in S] if unit_first: Sx = [u] + Sx else: Sx = Sx + [u] r = len(Sx) N = prod(S,1) # Initialize T1 to be empty and A to be a matric with 0 rows and r=#Sx columns T1 = [] A = Matrix(GF(2),0,len(Sx)) primes = primes_iter(K,1) p = primes.next() # Repeat the following until A has full rank: take the next prime p # from the iterator, skip if it divides N (=product over S), form the # vector v, and keep p and append v to the bottom of A if it increases # the rank: while A.rank() < r: p = primes.next() while p.divides(N): p = primes.next() if verbose: print("A={} with {} rows and {} cols".format(A,A.nrows(),A.ncols())) v = vector(alphalist(p, Sx)) if verbose: print("v={}".format(v)) A1 = A.stack(v) if A1.rank() > A.rank(): A = A1 T1 = T1 + [p] if verbose: print("new A={} with {} rows and {} cols".format(A,A.nrows(),A.ncols())) print("T1 increases to {}".format(T1)) # the last thing returned is a "decoder" which returns the # discriminant Delta given the splitting beavious at the primes in # T1: B = A.inverse() def decoder(alphalist): e = list(B*vector(alphalist)) return prod([D**ZZ(ei) for D,ei in zip(Sx,e)], 1) return T1, A, decoder
def NonCubicSet(K,S, verbose=False): u = -1 if K==QQ else K(K.unit_group().torsion_generator()) from KSp import IdealGenerator Sx = [u] + [IdealGenerator(P) for P in S] r = len(Sx) d123 = r + binomial(r,2) + binomial(r,3) vecP = vec123(K,Sx) A = Matrix(GF(2),0,d123) N = prod(S,1) primes = primes_iter(K,None) T = [] while A.rank() < d123: p = primes.next() while p.divides(N): p = primes.next() v = vecP(p) if verbose: print("v={}".format(v)) A1 = A.stack(vector(v)) if A1.rank() > A.rank(): A = A1 T.append(p) if verbose: print("new A={} with {} rows and {} cols".format(A,A.nrows(),A.ncols())) print("T increases to {}".format(T)) return T
def solve_vectors(n,d, verbose=False): def w(i,j): return 0 if i==j else (d[Set([i,j])] + d[Set([i])] + d[Set([j])]) W = Matrix(GF(2),[[w(i,j) for j in range(n)] for i in range(n)]) v = vector(GF(2),[d[Set([i])] for i in range(n)]) if verbose: print("W = {}".format(W)) print("v = {}".format(v)) if W==0: if v==0: return 0 else: return v, v Wrows = [wr for wr in W.rows() if wr] if v==0: x = Wrows[0] y = next(wr for wr in Wrows[1:] if wr!=x) return x, y z = W.row(v.support()[0]) y = next(wr for wr in Wrows if wr!=z) return y+z, y
class StrataGraph(object): R = PolynomialRing(ZZ,"X",1,order='lex') Xvar = R.gen() def __init__(self,M=None,genus_list=None): #print genus_list if M: self.M = copy(M) elif genus_list: self.M = Matrix(StrataGraph.R,len(genus_list)+1,1,[-1]+genus_list) else: self.M = Matrix(StrataGraph.R,1,1,-1) def __repr__(self): """ Drew added this. """ name = self.nice_name() if name == None: return repr(self.nice_matrix()) else: return name def num_vertices(self): return self.M.nrows() - 1 def num_edges(self): return self.M.ncols() - 1 def h1(self): return self.M.ncols()-self.M.nrows()+1 def add_vertex(self,g): self.M = self.M.stack(Matrix(1,self.M.ncols())) self.M[-1,0] = g def add_edge(self,i1,i2,marking=0): self.M = self.M.augment(Matrix(self.M.nrows(),1)) self.M[0,-1] = marking if i1 > 0: self.M[i1,-1] += 1 if i2 > 0: self.M[i2,-1] += 1 def del_vertex(self,i): self.M = self.M[:i].stack(self.M[(i+1):]) def del_edge(self,i): self.M = self.M[0:,:i].augment(self.M[0:,(i+1):]) def compute_degree_vec(self): self.degree_vec = [sum(self.M[i,j][0] for j in range(1,self.M.ncols())) for i in range(1,self.M.nrows())] def degree(self,i): return self.degree_vec[i-1] def split_vertex(self,i,row1,row2): self.M = self.M.stack(Matrix(2,self.M.ncols(),row1+row2)) self.add_edge(self.M.nrows()-2, self.M.nrows()-1) self.del_vertex(i) def compute_parity_vec_original(self): X = StrataGraph.Xvar self.parity_vec = [(ZZ(1+self.M[i,0][0]+sum(self.M[i,k][1] for k in range(1,self.M.ncols()))+sum(self.M[i,k][2] for k in range(1,self.M.ncols()))+self.M[i,0].derivative(X).substitute(X=1)) % 2) for i in range(1,self.M.nrows())] def compute_parity_vec(self): X = StrataGraph.Xvar self.parity_vec = [(ZZ(1+self.M[i,0][0]+sum(self.M[i,k][1] for k in range(1,self.M.ncols()))+sum(self.M[i,k][2] for k in range(1,self.M.ncols()))+self.last_parity_summand(i)) % 2) for i in range(1,self.M.nrows())] def last_parity_summand(self,i): return sum(( expon[0] * coef for expon, coef in self.M[i,0].dict().items() )) def parity(self,i): return self.parity_vec[i-1] def replace_vertex_with_graph(self,i,G): X = StrataGraph.Xvar nv = self.num_vertices() ne = self.num_edges() #i should have degree d, there should be no classes near i, and G should have markings 1,...,d and genus equal to the genus of i hedge_list = [] for k in range(1,self.M.ncols()): for j in range(self.M[i,k]): hedge_list.append(k) self.del_vertex(i) for j in range(G.num_edges() - len(hedge_list)): self.add_edge(0,0) for j in range(G.num_vertices()): self.add_vertex(G.M[j+1,0]) col = ne+1 for k in range(1,G.M.ncols()): if G.M[0,k] > 0: mark = ZZ(G.M[0,k]) for j in range(G.num_vertices()): if self.M[nv+j,hedge_list[mark-1]] == 0: self.M[nv+j,hedge_list[mark-1]] = G.M[j+1,k] elif G.M[j+1,k] != 0: a = self.M[nv+j,hedge_list[mark-1]][1] b = G.M[j+1,k][1] self.M[nv+j,hedge_list[mark-1]] = 2 + max(a,b)*X + min(a,b)*X**2 else: for j in range(G.num_vertices()): self.M[nv+j,col] = G.M[j+1,k] col += 1 def compute_invariant(self): self.compute_parity_vec() self.compute_degree_vec() nr,nc = self.M.nrows(),self.M.ncols() self.invariant = [[self.M[i,0], [], [], [[] for j in range(1,nr)]] for i in range(1,nr)] for k in range(1,nc): L = [i for i in range(1,nr) if self.M[i,k] != 0] if len(L) == 1: if self.M[0,k] != 0: self.invariant[L[0]-1][2].append([self.M[0,k],self.M[L[0],k]]) else: self.invariant[L[0]-1][1].append(self.M[L[0],k]) else: self.invariant[L[0]-1][3][L[1]-1].append([self.M[L[0],k],self.M[L[1],k]]) self.invariant[L[1]-1][3][L[0]-1].append([self.M[L[1],k],self.M[L[0],k]]) for i in range(1,nr): self.invariant[i-1][3] = [term for term in self.invariant[i-1][3] if len(term) > 0] for term in self.invariant[i-1][3]: term.sort() self.invariant[i-1][3].sort() self.invariant[i-1][2].sort() self.invariant[i-1][1].sort() vertex_invariants = [[i,self.invariant[i-1]] for i in range(1,nr)] self.invariant.sort() vertex_invariants.sort(key=lambda x: x[1]) self.vertex_groupings = [] for i in range(nr-1): if i == 0 or vertex_invariants[i][1] != vertex_invariants[i-1][1]: self.vertex_groupings.append([]) self.vertex_groupings[-1].append(vertex_invariants[i][0]) #Drew added this self.invariant = tupleit(self.invariant) self.hash = hash(self.invariant) def __eq__(self,other): """ Drew added this. """ return graph_isomorphic(self, other) def __hash__(self): """ Drew added this. Note that it returns a value stored when "compute_invariant" is called. """ try: return self.hash except: self.compute_invariant() return self.hash def codim(self): codim = 0 for v in range(1, self.num_vertices()+1): for expon, coef in self.M[v,0].dict().items(): codim += expon[0]*coef for e in range(1, self.num_edges()+1): for expon, coef in self.M[v,e].dict().items(): if expon[0] > 0: codim += coef for e in range(1, self.num_edges()+1): if self.M[0,e] == 0: codim +=1 return codim def codim_undecorated(self): codim = 0 for e in range(1, self.num_edges()+1): if self.M[0,e] == 0: codim +=1 return codim print "not implemented!!!!!!" return 1 def kappa_on_v(self,v): """ Drew added this. """ #print "kappa",self.M[v,0].dict() for ex, coef in self.M[v,0].dict().items(): if ex[0] != 0: yield ex[0], coef def psi_no_loop_on_v(self,v): for edge in range(1,self.num_edges()+1): if self.M[v,edge][0] == 1: psi_expon = self.M[v,edge][1] if psi_expon > 0: yield edge, psi_expon def psi_loop_on_v(self,v): for edge in range(1,self.num_edges()+1): if self.M[v,edge][0] == 2: psi_expon1 = self.M[v,edge][1] psi_expon2 = self.M[v,edge][2] if psi_expon1 > 0: yield edge, psi_expon1, psi_expon2 def moduli_dim_v(self,v): """ Drew added this. """ return 3*self.M[v,0][0]-3 + sum(( self.M[v,j][0] for j in range(1, self.num_edges()+1 ) )) def num_loops(self): count = 0 for edge in range(1, self.num_edges()+1): for v in range(1, self.num_vertices()+1): if self.M[v,edge][0] == 2: count+=1 break return count def num_undecorated_loops(self): count = 0 for edge in range(1, self.num_edges()+1): for v in range(1, self.num_vertices()+1): if self.M[v,edge] == 2: count+=1 break return count def num_full_edges(self): count = 0 for edge in range(1, self.num_edges()+1): if sum(( self.M[v,edge][0] for v in range(1, self.num_vertices()+1 ))) == 2: count += 1 return count def forget_kappas(self): M = copy(self.M) for v in range(1, self.num_vertices()+1): M[v,0] = M[v,0][0] return StrataGraph(M) def forget_decorations(self): M = Matrix(StrataGraph.R,[[self.M[r,c][0] for c in range(self.M.ncols())] for r in range(self.M.nrows())] ) Gnew = StrataGraph(M) #Gnew.compute_invariant() return Gnew def is_loop(self,edge): for v in range(1, self.num_vertices()+1): if self.M[v,edge][0] == 2: return True if self.M[v,edge][0] == 1: return False raise Exception("Unexpected!") ps_name = "ps" ps2_name = "ps_" def nice_matrix(self): Mnice = Matrix(SR, self.M.nrows(), self.M.ncols()) for edge in range(1, self.num_edges()+1): Mnice[0,edge] = self.M[0,edge] for v in range(1, self.num_vertices()+1): kappas = 1 for expon, coef in self.M[v,0].dict().items(): if expon[0]==0: Mnice[v,0] += coef else: kappas *= var("ka{0}".format(expon[0]))**coef if kappas != 1: Mnice[v,0] += kappas for edge in range(1, self.num_edges()+1): psis = 1 for expon, coef in self.M[v,edge].dict().items(): if expon[0]==0: Mnice[v,edge] += coef elif expon[0]==1: psis *= var(StrataGraph.ps_name)**coef elif expon[0]==2: psis *= var(StrataGraph.ps2_name)**coef if psis != 1: Mnice[v,edge] += psis return Mnice @staticmethod def from_nice_matrix(lists): Mx = Matrix(StrataGraph.R, len(lists), len(lists[0])) Mx[0,0]=-1 Mx[0,:] = Matrix([lists[0]]) for v in range(1, len(lists)): if lists[v][0] in ZZ: Mx[v,0]=lists[v][0] continue if lists[v][0].operator() == sage.symbolic.operators.add_vararg: operands = lists[v][0].operands() if len(operands) != 2: raise Exception("Input error!") genus = operands[1] #the genus kappas = operands[0] else: kappas = lists[v][0] genus = 0 if kappas.operator() == sage.symbolic.operators.mul_vararg: for operand in kappas.operands(): Mx[v,0] += StrataGraph._kappaSR_monom_to_X(operand) Mx[v,0] += genus else: Mx[v,0] = StrataGraph._kappaSR_monom_to_X(kappas) + genus X = StrataGraph.Xvar for v in range(1, len(lists)): for edge in range(1, len(lists[0])): if lists[v][edge] in ZZ: Mx[v,edge] = lists[v][edge] else: for operand in lists[v][edge].operands(): if operand in ZZ: Mx[v,edge] += operand elif operand.operator() is None: #it is a ps or ps2 Mx[v,edge] += X elif operand.operator() == operator.pow: #it is ps^n or ps2^n Mx[v,edge] += X * operand.operands()[1] elif operand.operator() == sage.symbolic.operators.mul_vararg: #it is a ps^n*ps2^m op1,op2 = operand.operands() if op1.operator() is None: exp1 = 1 else: exp1 = op1.operand()[1] if op2.operator() == None: exp2 = 1 else: exp2 = op2.operand()[1] if exp1 >= exp2: Mx[v,edge] += exp1*X + exp2*X**2 else: Mx[v,edge] += exp1*X**2 + exp2*X return StrataGraph(Mx) @staticmethod def _kappaSR_monom_to_X(expr): X = StrataGraph.Xvar if expr in ZZ: return expr elif expr.operator() == None: ka_subscript = Integer(str(expr)[2:]) return X ** ka_subscript elif expr.operator() == operator.pow: ops = expr.operands() expon = ops[1] ka = ops[0] ka_subscript = Integer(str(ka)[2:]) return expon * X ** ka_subscript def nice_name(self): """ :return: """ if self.num_vertices() == 1: num_loops = self.num_loops() if num_loops >1: return None elif num_loops == 1: if self.codim() == 1: return "D_irr" else: return None #else, there are no loops var_strs = [] for expon, coef in self.M[1,0].dict().items(): if expon[0] == 0 or coef == 0: continue if coef == 1: var_strs.append("ka{0}".format(expon[0])) else: var_strs.append("ka{0}^{1}".format(expon[0],coef)) for he in range(1,self.num_edges()+1): #should only be half edges now if self.M[1,he][1] > 1: var_strs.append("ps{0}^{1}".format(self.M[0,he],self.M[1,he][1])) elif self.M[1,he][1] == 1: var_strs.append("ps{0}".format(self.M[0,he])) if len(var_strs) > 0: return "*".join(var_strs) else: return "one" if self.num_vertices() == 2 and self.num_full_edges() == 1 and self.codim() == 1: #it is a boundary divisor v1_marks = [self.M[0,j] for j in range(1, self.num_edges()+1) if self.M[1,j] == 1 and self.M[0,j] != 0] v1_marks.sort() v2_marks = [self.M[0,j] for j in range(1, self.num_edges()+1) if self.M[2,j] == 1 and self.M[0,j] != 0] v2_marks.sort() if v1_marks < v2_marks: g = self.M[1,0] elif v1_marks == v2_marks: if self.M[1,0] <= self.M[2,0]: g = self.M[1,0] else: g = self.M[2,0] else: g = self.M[2,0] #temp = v1_marks v1_marks = v2_marks #v2_marks = temp if len(v1_marks) == 0: return "Dg{0}".format(g) else: return "Dg{0}m".format(g) + "_".join([str(m) for m in v1_marks])
def invariant_generators(self): """ Wraps Singular's invariant_algebra_reynolds and invariant_ring in finvar.lib, with help from Simon King and Martin Albrecht. Computes generators for the polynomial ring `F[x_1,\ldots,x_n]^G`, where G in GL(n,F) is a finite matrix group. In the "good characteristic" case the polynomials returned form a minimal generating set for the algebra of G-invariant polynomials. In the "bad" case, the polynomials returned are primary and secondary invariants, forming a not necessarily minimal generating set for the algebra of G-invariant polynomials. EXAMPLES:: sage: F = GF(7); MS = MatrixSpace(F,2,2) sage: gens = [MS([[0,1],[-1,0]]),MS([[1,1],[2,3]])] sage: G = MatrixGroup(gens) sage: G.invariant_generators() [x1^7*x2 - x1*x2^7, x1^12 - 2*x1^9*x2^3 - x1^6*x2^6 + 2*x1^3*x2^9 + x2^12, x1^18 + 2*x1^15*x2^3 + 3*x1^12*x2^6 + 3*x1^6*x2^12 - 2*x1^3*x2^15 + x2^18] sage: q = 4; a = 2 sage: MS = MatrixSpace(QQ, 2, 2) sage: gen1 = [[1/a,(q-1)/a],[1/a, -1/a]]; gen2 = [[1,0],[0,-1]]; gen3 = [[-1,0],[0,1]] sage: G = MatrixGroup([MS(gen1),MS(gen2),MS(gen3)]) sage: G.cardinality() 12 sage: G.invariant_generators() [x1^2 + 3*x2^2, x1^6 + 15*x1^4*x2^2 + 15*x1^2*x2^4 + 33*x2^6] sage: F = GF(5); MS = MatrixSpace(F,2,2) sage: gens = [MS([[1,2],[-1,1]]),MS([[1,1],[-1,1]])] sage: G = MatrixGroup(gens) sage: G.invariant_generators() # long time (64s on sage.math, 2011) [x1^20 + x1^16*x2^4 + x1^12*x2^8 + x1^8*x2^12 + x1^4*x2^16 + x2^20, x1^20*x2^4 + x1^16*x2^8 + x1^12*x2^12 + x1^8*x2^16 + x1^4*x2^20] sage: F=CyclotomicField(8) sage: z=F.gen() sage: a=z+1/z sage: b=z^2 sage: MS=MatrixSpace(F,2,2) sage: g1=MS([[1/a,1/a],[1/a,-1/a]]) sage: g2=MS([[1,0],[0,b]]) sage: g3=MS([[b,0],[0,1]]) sage: G=MatrixGroup([g1,g2,g3]) sage: G.invariant_generators() # long time (12s on sage.math, 2011) [x1^8 + 14*x1^4*x2^4 + x2^8, x1^24 + 10626/1025*x1^20*x2^4 + 735471/1025*x1^16*x2^8 + 2704156/1025*x1^12*x2^12 + 735471/1025*x1^8*x2^16 + 10626/1025*x1^4*x2^20 + x2^24] AUTHORS: - David Joyner, Simon King and Martin Albrecht. REFERENCES: - Singular reference manual - B. Sturmfels, "Algorithms in invariant theory", Springer-Verlag, 1993. - S. King, "Minimal Generating Sets of non-modular invariant rings of finite groups", arXiv:math.AC/0703035 """ from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.interfaces.singular import singular gens = self.gens() singular.LIB("finvar.lib") n = self.degree() #len((gens[0].matrix()).rows()) F = self.base_ring() q = F.characteristic() ## test if the field is admissible if F.gen()==1: # we got the rationals or GF(prime) FieldStr = str(F.characteristic()) elif hasattr(F,'polynomial'): # we got an algebraic extension if len(F.gens())>1: raise NotImplementedError, "can only deal with finite fields and (simple algebraic extensions of) the rationals" FieldStr = '(%d,%s)'%(F.characteristic(),str(F.gen())) else: # we have a transcendental extension FieldStr = '(%d,%s)'%(F.characteristic(),','.join([str(p) for p in F.gens()])) ## Setting Singular's variable names ## We need to make sure that field generator and variables get different names. if str(F.gen())[0]=='x': VarStr = 'y' else: VarStr = 'x' VarNames='('+','.join((VarStr+str(i+1) for i in range(n)))+')' R=singular.ring(FieldStr,VarNames,'dp') if hasattr(F,'polynomial') and F.gen()!=1: # we have to define minpoly singular.eval('minpoly = '+str(F.polynomial()).replace('x',str(F.gen()))) A = [singular.matrix(n,n,str((x.matrix()).list())) for x in gens] Lgens = ','.join((x.name() for x in A)) PR = PolynomialRing(F,n,[VarStr+str(i) for i in range(1,n+1)]) if q == 0 or (q > 0 and self.cardinality()%q != 0): from sage.all import Integer, Matrix try: gapself = gap(self) # test whether the backwards transformation works as well: for i in range(self.ngens()): if Matrix(gapself.gen(i+1),F) != self.gen(i).matrix(): raise ValueError except (TypeError,ValueError): gapself is None if gapself is not None: ReyName = 't'+singular._next_var_name() singular.eval('matrix %s[%d][%d]'%(ReyName,self.cardinality(),n)) En = gapself.Enumerator() for i in range(1,self.cardinality()+1): M = Matrix(En[i],F) D = [{} for foobar in range(self.degree())] for x,y in M.dict().items(): D[x[0]][x[1]] = y for row in range(self.degree()): for t in D[row].items(): singular.eval('%s[%d,%d]=%s[%d,%d]+(%s)*var(%d)'%(ReyName,i,row+1,ReyName,i,row+1, repr(t[1]),t[0]+1)) foobar = singular(ReyName) IRName = 't'+singular._next_var_name() singular.eval('matrix %s = invariant_algebra_reynolds(%s)'%(IRName,ReyName)) else: ReyName = 't'+singular._next_var_name() singular.eval('list %s=group_reynolds((%s))'%(ReyName,Lgens)) IRName = 't'+singular._next_var_name() singular.eval('matrix %s = invariant_algebra_reynolds(%s[1])'%(IRName,ReyName)) OUT = [singular.eval(IRName+'[1,%d]'%(j)) for j in range(1,1+singular('ncols('+IRName+')'))] return [PR(gen) for gen in OUT] if self.cardinality()%q == 0: PName = 't'+singular._next_var_name() SName = 't'+singular._next_var_name() singular.eval('matrix %s,%s=invariant_ring(%s)'%(PName,SName,Lgens)) OUT = [singular.eval(PName+'[1,%d]'%(j)) for j in range(1,1+singular('ncols('+PName+')'))] + [singular.eval(SName+'[1,%d]'%(j)) for j in range(2,1+singular('ncols('+SName+')'))] return [PR(gen) for gen in OUT]
def invariant_generators(self): r""" Return invariant ring generators. Computes generators for the polynomial ring `F[x_1,\ldots,x_n]^G`, where `G` in `GL(n,F)` is a finite matrix group. In the "good characteristic" case the polynomials returned form a minimal generating set for the algebra of `G`-invariant polynomials. In the "bad" case, the polynomials returned are primary and secondary invariants, forming a not necessarily minimal generating set for the algebra of `G`-invariant polynomials. ALGORITHM: Wraps Singular's ``invariant_algebra_reynolds`` and ``invariant_ring`` in ``finvar.lib``. EXAMPLES:: sage: F = GF(7); MS = MatrixSpace(F,2,2) sage: gens = [MS([[0,1],[-1,0]]),MS([[1,1],[2,3]])] sage: G = MatrixGroup(gens) sage: G.invariant_generators() [x1^7*x2 - x1*x2^7, x1^12 - 2*x1^9*x2^3 - x1^6*x2^6 + 2*x1^3*x2^9 + x2^12, x1^18 + 2*x1^15*x2^3 + 3*x1^12*x2^6 + 3*x1^6*x2^12 - 2*x1^3*x2^15 + x2^18] sage: q = 4; a = 2 sage: MS = MatrixSpace(QQ, 2, 2) sage: gen1 = [[1/a,(q-1)/a],[1/a, -1/a]]; gen2 = [[1,0],[0,-1]]; gen3 = [[-1,0],[0,1]] sage: G = MatrixGroup([MS(gen1),MS(gen2),MS(gen3)]) sage: G.cardinality() 12 sage: G.invariant_generators() [x1^2 + 3*x2^2, x1^6 + 15*x1^4*x2^2 + 15*x1^2*x2^4 + 33*x2^6] sage: F = CyclotomicField(8) sage: z = F.gen() sage: a = z+1/z sage: b = z^2 sage: MS = MatrixSpace(F,2,2) sage: g1 = MS([[1/a, 1/a], [1/a, -1/a]]) sage: g2 = MS([[-b, 0], [0, b]]) sage: G=MatrixGroup([g1,g2]) sage: G.invariant_generators() [x1^4 + 2*x1^2*x2^2 + x2^4, x1^5*x2 - x1*x2^5, x1^8 + 28/9*x1^6*x2^2 + 70/9*x1^4*x2^4 + 28/9*x1^2*x2^6 + x2^8] AUTHORS: - David Joyner, Simon King and Martin Albrecht. REFERENCES: - Singular reference manual - [Stu1993]_ - S. King, "Minimal Generating Sets of non-modular invariant rings of finite groups", :arxiv:`math/0703035`. """ from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.interfaces.singular import singular gens = self.gens() singular.LIB("finvar.lib") n = self.degree() #len((gens[0].matrix()).rows()) F = self.base_ring() q = F.characteristic() ## test if the field is admissible if F.gen()==1: # we got the rationals or GF(prime) FieldStr = str(F.characteristic()) elif hasattr(F,'polynomial'): # we got an algebraic extension if len(F.gens())>1: raise NotImplementedError("can only deal with finite fields and (simple algebraic extensions of) the rationals") FieldStr = '(%d,%s)'%(F.characteristic(),str(F.gen())) else: # we have a transcendental extension FieldStr = '(%d,%s)'%(F.characteristic(),','.join([str(p) for p in F.gens()])) ## Setting Singular's variable names ## We need to make sure that field generator and variables get different names. if str(F.gen())[0]=='x': VarStr = 'y' else: VarStr = 'x' VarNames='('+','.join((VarStr+str(i+1) for i in range(n)))+')' R=singular.ring(FieldStr,VarNames,'dp') if hasattr(F,'polynomial') and F.gen()!=1: # we have to define minpoly singular.eval('minpoly = '+str(F.polynomial()).replace('x',str(F.gen()))) A = [singular.matrix(n,n,str((x.matrix()).list())) for x in gens] Lgens = ','.join((x.name() for x in A)) PR = PolynomialRing(F,n,[VarStr+str(i) for i in range(1,n+1)]) if q == 0 or (q > 0 and self.cardinality()%q != 0): from sage.all import Integer, Matrix try: elements = [ g.matrix() for g in self.list() ] except (TypeError,ValueError): elements if elements is not None: ReyName = 't'+singular._next_var_name() singular.eval('matrix %s[%d][%d]'%(ReyName,self.cardinality(),n)) for i in range(1,self.cardinality()+1): M = Matrix(F, elements[i-1]) D = [{} for foobar in range(self.degree())] for x,y in M.dict().items(): D[x[0]][x[1]] = y for row in range(self.degree()): for t in D[row].items(): singular.eval('%s[%d,%d]=%s[%d,%d]+(%s)*var(%d)' %(ReyName,i,row+1,ReyName,i,row+1, repr(t[1]),t[0]+1)) foobar = singular(ReyName) IRName = 't'+singular._next_var_name() singular.eval('matrix %s = invariant_algebra_reynolds(%s)'%(IRName,ReyName)) else: ReyName = 't'+singular._next_var_name() singular.eval('list %s=group_reynolds((%s))'%(ReyName,Lgens)) IRName = 't'+singular._next_var_name() singular.eval('matrix %s = invariant_algebra_reynolds(%s[1])'%(IRName,ReyName)) OUT = [singular.eval(IRName+'[1,%d]'%(j)) for j in range(1,1+singular('ncols('+IRName+')'))] return [PR(gen) for gen in OUT] if self.cardinality()%q == 0: PName = 't'+singular._next_var_name() SName = 't'+singular._next_var_name() singular.eval('matrix %s,%s=invariant_ring(%s)'%(PName,SName,Lgens)) OUT = [ singular.eval(PName+'[1,%d]'%(j)) for j in range(1,1+singular('ncols('+PName+')')) ] + [ singular.eval(SName+'[1,%d]'%(j)) for j in range(2,1+singular('ncols('+SName+')')) ] return [PR(gen) for gen in OUT]
def invariants_eps(FQM, TM, use_reduction = True, proof = False, debug = 0): r""" Computes the invariants of a direct summand in the decomposition for weight one-half modular forms. Such a summand is of the form \[ \mathbb{C}[(\mathbb{Z}/2N\mathbb{Z}, -x^2/4N)]^\epsilon \otimes \mathbb{C}[M], \] where $M$ is a given finite quadratic module and $\epsilon$ acts on the first factor via $\mathfrak{e}_\mu \mapsto \mathfrak{e}_{-\mu}$. INPUT: - FQM: A given finite quadratic module, referred to as $M$ above - TM: A cyclic module of the form as given abve (the first factor) NOTE: We do not check that TM is of the stated form. The function is an auxiliary function usually only called by ``weight_one_half_dim``. EXAMPLES: NONE """ eps = True if TM != None and FQM != None: TMM = TM+FQM elif TM != None: TMM = TM else: TMM = FQM eps = False debug2 = 0 if debug > 1: print "FQM = {0}, TM = {1}, TMM = {2}".format(FQM, TM, TMM) debug2 = 1 if debug > 2: debug2=debug inv = invariants(TMM, use_reduction, proof=proof, debug=debug2) if debug > 1: print inv if type(inv) in [list,tuple]: V = inv[1] else: V = inv d = [0,0] if V.dimension() != 0: el = list() M = Matrix(V.base_ring(), V.ambient_module().dimension()) if eps: f = 1 if TMM.signature() % 4 == 0 else -1 for v in inv[0]: #Get the coordinate of this isotropic element vv = v.c_list() #change the first coordinate to its negative (the eps-action) vv[0] = -vv[0] vv = TMM(vv,can_coords=True) #since the isotropic elements are taken up to the action of +-1, we need to check #if we have this element (vv) or its negative (-vv) in the list #we append the index of the match, together with a sign to the list `el`, #where the sign is -1 if -vv is in inv[0] and the signature is 2 mod 4 #(i.e. the std generator of the center acts as -1) if inv[0].count(vv) > 0: el.append((inv[0].index(vv),1)) else: el.append((inv[0].index(-vv),f)) #We create the entries of the matrix M #which acts as eps on the space spanned by the isotropic vectors (mod +-1) for i in range(len(el)): M[el[i][0],i] = el[i][1] #import pdb; pdb.set_trace() if debug > 1: print "M={0}, V={1}".format(M, V) try: KM = (M-M.parent().one()).kernel_on(V) if debug > 1: print "KM for ev 1 = {0}".format(KM) d[0] = KM.dimension() KM = (M+M.parent().one()).kernel_on(V) if debug > 1: print "KM for ev -1 = {0}".format(KM) d[1] = KM.dimension() except Exception as e: raise RuntimeError("Error occured for {0}, {1}".format(FQM.jordan_decomposition().genus_symbol(), e), M, V) else: d = [V.dimension(), 0] if debug > 1: print d return d
def algo64(K, S, BB, T2=None, unit_first = True, verbose=False): r = 1+len(S) if T2 is None: T2 = get_T2(QQ,S, unit_first) if verbose: print("T2 = {}".format(T2)) assert len(T2)==r*(r+1)//2 from KSp import IdealGenerator Sx = [IdealGenerator(P) for P in S] u = -1 if K==QQ else K(K.unit_group().torsion_generator()) Sx = [u] + Sx if unit_first else Sx+[u] if verbose: print("Basis for K(S,2): {}".format(Sx)) #BBdict = dict((k,BB(T2[k])) for k in T2) ap = BB_trace(BB) apdict = dict((k,ap(T2[k])) for k in T2) t4 = BB_t4(BB) t4dict = dict((k,t4(T2[k])) for k in T2) if verbose: print("apdict = {}".format(apdict)) print("t4dict = {}".format(t4dict)) v = vector(GF(2),[t4dict[Set([i])] for i in range(r)]) if verbose: print("v = {}".format(v)) def w(i,j): if i==j: return 0 else: return (t4dict[Set([i,j])] + t4dict[Set([i])] + t4dict[Set([j])]) # Note that W ignores the first coordinate Wd = Matrix(GF(2),[[w(i,j) for j in range(1,r)] for i in range(1,r)]) vd = vector(v.list()[1:]) rkWd = Wd.rank() if verbose: print("W' = \n{} with rank {}".format(Wd,rkWd)) # Case 1: rank(W)=2 if rkWd==2: # x' and y' are any two distinct nonzero rows of W Wrows = [wr for wr in Wd.rows() if wr] xd = Wrows[0] yd = next(wr for wr in Wrows if wr!=xd) zd = xd+yd ud = vd - vector(xi*yi for xi,yi in zip(list(xd),list(yd))) if verbose: print("x' = {}".format(xd)) print("y' = {}".format(yd)) print("z' = {}".format(zd)) print("u' = {}".format(ud)) print("v' = {}".format(vd)) u = vector([1]+ud.list()) v = vector([0]+vd.list()) if t4dict[Set([0])]==1: x = vector([1]+xd.list()) y = vector([1]+yd.list()) z = vector([1]+zd.list()) if verbose: print("x = {}".format(x)) print("y = {}".format(y)) print("z = {}".format(z)) print("u = {}".format(u)) print("v = {}".format(v)) else: raise NotImplementedError("t_4(p_1) case not yet implemented in algo64") return [vec_to_disc(Sx,vec) for vec in [u,x,y,z]] # W==0 raise NotImplementedError("rank(W)=0 case not yet implemented in algo64")
def algo63(K, S, BB, T2=None, unit_first = True, verbose=False): r = 1+len(S) if T2 is None: T2 = get_T2(QQ,S, unit_first) if verbose: print("T2 = {}".format(T2)) assert len(T2)==r*(r+1)//2 from KSp import IdealGenerator Sx = [IdealGenerator(P) for P in S] u = -1 if K==QQ else K(K.unit_group().torsion_generator()) Sx = [u] + Sx if unit_first else Sx+[u] if verbose: print("Basis for K(S,2): {}".format(Sx)) BBdict = dict((k,BB(T2[k])) for k in T2) ap = BB_trace(BB) apdict = dict((k,ap(T2[k])) for k in T2) t2 = BB_t2(BB) t2dict = dict((k,t2(T2[k])) for k in T2) if verbose: print("apdict = {}".format(apdict)) print("t2dict = {}".format(t2dict)) v = vector(GF(2),[t2dict[Set([i])] for i in range(r)]) if verbose: print("v = {}".format(v)) def w(i,j): if i==j: return 0 else: return (t2dict[Set([i,j])] + t2dict[Set([i])] + t2dict[Set([j])]) W = Matrix(GF(2),[[w(i,j) for j in range(r)] for i in range(r)]) rkW = W.rank() if verbose: print("W = \n{} with rank {}".format(W,rkW)) # Case 1: rank(W)=2 if rkW==2: # x and y are any two distinct nonzero rows of W Wrows = [wr for wr in W.rows() if wr] x = Wrows[0] y = next(wr for wr in Wrows if wr!=x) z = x+y u = v - vector(xi*yi for xi,yi in zip(list(x),list(y))) if verbose: print("x = {}".format(x)) print("y = {}".format(y)) print("z = {}".format(z)) print("u = {}".format(u)) else: # W==0 # y=0, x=z u = v y = u-u # zero vector if verbose: print("u = {}".format(u)) print("y = {}".format(y)) t3dict = {} for k in T2: t = 1 if t2dict[k]==0 else -1 t3dict[k] = (BBdict[k](t)//8)%2 s = solve_vectors(r,t3dict) if not s: z = x = y # = 0 if verbose: print("x and y are both zero, so class has >4 vertices") else: x, _ = s z = x if verbose: print("x = {}".format(x)) print("y = {}".format(y)) print("z = {}".format(z)) return [vec_to_disc(Sx,vec) for vec in [u,x,y,z]]
def wrapper(*args): mat = Matrix(f(*args)) mat.set_immutable() return mat
def invariants_eps(FQM, TM, use_reduction = True, proof = False, debug = 0): r""" Computes the invariants of a direct summand in the decomposition for weight one-half modular forms. Such a summand is of the form \[ \mathbb{C}[(\mathbb{Z}/2N\mathbb{Z}, -x^2/4N)]^\epsilon \otimes \mathbb{C}[M], \] where $M$ is a given finite quadratic module and $\epsilon$ acts on the first factor via $\mathfrak{e}_\mu \mapsto \mathfrak{e}_{-\mu}$. INPUT: - FQM: A given finite quadratic module, referred to as $M$ above - TM: A cyclic module of the form as given abve (the first factor) NOTE: We do not check that TM is of the stated form. The function is an auxiliary function usually only called by ``weight_one_half_dim``. EXAMPLES: NONE """ eps = True if TM != None and FQM != None: TMM = TM+FQM elif TM != None: TMM = TM else: TMM = FQM eps = False if debug > 1: print "FQM = {0}, TM = {1}, TMM = {2}".format(FQM, TM, TMM) if debug > 2: debug2=debug else: debug2=0 inv = invariants(TMM, use_reduction, proof, debug2) if debug > 1: print inv if type(inv) in [list,tuple]: V = inv[1] else: V = inv d = [0,0] if V.dimension() != 0: el = list() M = Matrix(V.base_ring(), V.ambient_module().dimension()) if eps: f = 1 if TMM.signature() % 4 == 0 else -1 for v in inv[0]: vv = v.c_list() vv[0] = -vv[0] vv = TMM(vv,can_coords=True) if inv[0].count(vv) > 0: el.append((inv[0].index(vv),1)) else: el.append((inv[0].index(-vv),f)) for i in range(len(el)): M[el[i][0],i] = el[i][1] if debug > 1: print "M={0}, V={1}".format(M, V) try: KM = (M-M.parent().one()).kernel_on(V) if debug > 1: print "KM for ev 1 = {0}".format(KM) d[0] = KM.dimension() KM = (M+M.parent().one()).kernel_on(V) if debug > 1: print "KM for ev -1 = {0}".format(KM) d[1] = KM.dimension() except: raise RuntimeError("Error occured for {0}".format(FQM.jordan_decomposition().genus_symbol()), M, V) else: d = [V.dimension(), 0] if debug > 1: print d return d