def lfsr_connection_polynomial(s): """ INPUT: - ``s`` -- a sequence of elements of a finite field of even length OUTPUT: - ``C(x)`` -- the connection polynomial of the minimal LFSR. This implements the algorithm in section 3 of J. L. Massey's article [Mas1969]_. EXAMPLES:: sage: F = GF(2) sage: F Finite Field of size 2 sage: o = F(0); l = F(1) sage: key = [l,o,o,l]; fill = [l,l,o,l]; n = 20 sage: s = lfsr_sequence(key,fill,n); s [1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0] sage: lfsr_connection_polynomial(s) x^4 + x + 1 sage: from sage.matrix.berlekamp_massey import berlekamp_massey sage: berlekamp_massey(s) x^4 + x^3 + 1 Notice that ``berlekamp_massey`` returns the reverse of the connection polynomial (and is potentially must faster than this implementation). """ # Initialization: FF = s[0].base_ring() R = PolynomialRing(FF, "x") x = R.gen() C = R(1); B = R(1); m = 1; b = FF(1); L = 0; N = 0 while N < len(s): if L > 0: r = min(L+1,C.degree()+1) d = s[N] + sum([(C.list())[i]*s[N-i] for i in range(1,r)]) if L == 0: d = s[N] if d == 0: m += 1 N += 1 if d > 0: if 2*L > N: C = C - d*b**(-1)*x**m*B m += 1 N += 1 else: T = C C = C - d*b**(-1)*x**m*B L = N + 1 - L m = 1 b = d B = T N += 1 return C
def __init__(self, R, n, q=None): """ TESTS:: sage: HeckeAlgebraSymmetricGroupT(QQ, 3) Hecke algebra of the symmetric group of order 3 on the T basis over Univariate Polynomial Ring in q over Rational Field :: sage: HeckeAlgebraSymmetricGroupT(QQ, 3, q=1) Hecke algebra of the symmetric group of order 3 with q=1 on the T basis over Rational Field """ self.n = n self._basis_keys = permutation.Permutations(n) self._name = "Hecke algebra of the symmetric group of order %s"%self.n self._one = permutation.Permutation(range(1,n+1)) if q is None: q = PolynomialRing(R, 'q').gen() R = q.parent() else: if q not in R: raise ValueError, "q must be in R (= %s)"%R self._name += " with q=%s"%q self._q = q CombinatorialAlgebra.__init__(self, R) # _repr_ customization: output the basis element indexed by [1,2,3] as [1,2,3] self.print_options(prefix="")
def _sage_(self): """ EXAMPLES: sage: m = lie('[[1,0,3,3],[12,4,-4,7],[-1,9,8,0],[3,-5,-2,9]]') # optional - lie sage: m.sage() # optional - lie [ 1 0 3 3] [12 4 -4 7] [-1 9 8 0] [ 3 -5 -2 9] """ t = self.type() if t == "grp": raise ValueError, "cannot convert Lie groups to native Sage objects" elif t == "mat": import sage.matrix.constructor return sage.matrix.constructor.matrix(eval(str(self).replace("\n", "").strip())) elif t == "pol": import sage.misc.misc from sage.rings.all import PolynomialRing, QQ # Figure out the number of variables s = str(self) open_bracket = s.find("[") close_bracket = s.find("]") nvars = len(s[open_bracket:close_bracket].split(",")) # create the polynomial ring R = PolynomialRing(QQ, nvars, "x") x = R.gens() pol = R(0) # Split up the polynomials into terms terms = [] for termgrp in s.split(" - "): # The first entry in termgrp has # a negative coefficient termgrp = "-" + termgrp.strip() terms += termgrp.split("+") # Make sure we don't accidentally add a negative # sign to the first monomial if s[0] != "-": terms[0] = terms[0][1:] # go through all the terms in s for term in terms: xpos = term.find("X") coef = eval(term[:xpos].strip()) exps = eval(term[xpos + 1 :].strip()) monomial = sage.misc.misc.prod(map(lambda i: x[i] ** exps[i], range(nvars))) pol += coef * monomial return pol elif t == "tex": return repr(self) elif t == "vid": return None else: return ExpectElement._sage_(self)
def compute_tau0(v0,gamma,wD,return_exact = False): r''' INPUT: - v0: F -> its localization at p - gamma: the image of wD (the generator for an order of F) under an optimal embedding OUTPUT: The element tau_0 such that gamma * [tau_0,1] = wD * [tau_0,1] ''' R = PolynomialRing(QQ,names = 'X') X = R.gen() F = v0.domain() Cp = v0.codomain() assert wD.minpoly() == gamma.minpoly() a,b,c,d = gamma.list() tau0_vec = (c*X**2+(d-a)*X-b).roots(F) tau0 = v0(tau0_vec[0][0]) idx = 0 if c * tau0 + d != v0(wD): tau0 = v0(tau0_vec[1][0]) idx = 1 return tau0_vec[idx][0] if return_exact == True else tau0
def weil_representation(self) : r""" OUTPUT: - A pair of matrices corresponding to T and S. """ disc_bilinear = lambda a, b: (self._dual_basis * vector(QQ, a.lift())) * self._L * (self._dual_basis * vector(QQ, b.lift())) disc_quadratic = lambda a: disc_bilinear(a, a) / ZZ(2) zeta_order = ZZ(lcm([8, 12, prod(self.invariants())] + map(lambda ed: 2 * ed, self.invariants()))) K = CyclotomicField(zeta_order); zeta = K.gen() R = PolynomialRing(K, 'x'); x = R.gen() # sqrt2s = (x**2 - 2).factor() # if sqrt2s[0][0][0].complex_embedding().real() > 0 : # sqrt2 = sqrt2s[0][0][0] # else : # sqrt2 = sqrt2s[0][1] Ldet_rts = (x**2 - prod(self.invariants())).factor() if Ldet_rts[0][0][0].complex_embedding().real() > 0 : Ldet_rt = Ldet_rts[0][0][0] else : Ldet_rt = Ldet_rts[0][0][0] Tmat = diagonal_matrix( K, [zeta**(zeta_order*disc_quadratic(a)) for a in self] ) Smat = zeta**(zeta_order / 8 * self._L.nrows()) / Ldet_rt \ * matrix( K, [ [ zeta**ZZ(-zeta_order * disc_bilinear(gamma,delta)) for delta in self ] for gamma in self ]) return (Tmat, Smat)
def is_cm_j_invariant(j): """ Return whether or not this is a CM `j`-invariant. INPUT: - ``j`` -- an element of a number field `K` OUTPUT: A pair (bool, (d,f)) which is either (False, None) if `j` is not a CM j-invariant or (True, (d,f)) if `j` is the `j`-invariant of the imaginary quadratic order of discriminant `D=df^2` where `d` is the associated fundamental discriminant and `f` the index. .. note:: The current implementation makes use of the classification of all orders of class number up to 100, and hence will raise an error if `j` is an algebraic integer of degree greater than this. It would be possible to implement a more general version, using the fact that `d` must be supported on the primes dividing the discriminant of the minimal polynomial of `j`. EXAMPLES:: sage: from sage.schemes.elliptic_curves.cm import is_cm_j_invariant sage: is_cm_j_invariant(0) (True, (-3, 1)) sage: is_cm_j_invariant(8000) (True, (-8, 1)) sage: K.<a> = QuadraticField(5) sage: is_cm_j_invariant(282880*a + 632000) (True, (-20, 1)) sage: K.<a> = NumberField(x^3 - 2) sage: is_cm_j_invariant(31710790944000*a^2 + 39953093016000*a + 50337742902000) (True, (-3, 6)) TESTS:: sage: from sage.schemes.elliptic_curves.cm import is_cm_j_invariant sage: all([is_cm_j_invariant(j) == (True, (d,f)) for d,f,j in cm_j_invariants_and_orders(QQ)]) True """ from sage.rings.all import NumberFieldElement if not isinstance(j, NumberFieldElement) and not j in QQ: raise NotImplementedError("is_cm_j_invariant() is only implemented for number field elements") if not j.is_integral(): return False, None jpol = PolynomialRing(QQ,'x')([-j,1]) if j in QQ else j.absolute_minpoly() h = jpol.degree() if h>100: raise NotImplementedError("CM data only available for class numbers up to 100") for d,f in cm_orders(h): if jpol == hilbert_class_polynomial(d*f**2): return True, (d,f) return False, None
def _sage_(self): """ EXAMPLES:: sage: m = lie('[[1,0,3,3],[12,4,-4,7],[-1,9,8,0],[3,-5,-2,9]]') # optional - lie sage: m.sage() # optional - lie [ 1 0 3 3] [12 4 -4 7] [-1 9 8 0] [ 3 -5 -2 9] """ t = self.type() if t == 'grp': raise ValueError("cannot convert Lie groups to native Sage objects") elif t == 'mat': import sage.matrix.constructor return sage.matrix.constructor.matrix( eval( str(self).replace('\n','').strip()) ) elif t == 'pol': from sage.rings.all import PolynomialRing, QQ #Figure out the number of variables s = str(self) open_bracket = s.find('[') close_bracket = s.find(']') nvars = len(s[open_bracket:close_bracket].split(',')) #create the polynomial ring R = PolynomialRing(QQ, nvars, 'x') x = R.gens() pol = R(0) #Split up the polynomials into terms terms = [] for termgrp in s.split(' - '): #The first entry in termgrp has #a negative coefficient termgrp = "-"+termgrp.strip() terms += termgrp.split('+') #Make sure we don't accidentally add a negative #sign to the first monomial if s[0] != "-": terms[0] = terms[0][1:] #go through all the terms in s for term in terms: xpos = term.find('X') coef = eval(term[:xpos].strip()) exps = eval(term[xpos+1:].strip()) monomial = prod([x[i]**exps[i] for i in range(nvars)]) pol += coef * monomial return pol elif t == 'tex': return repr(self) elif t == 'vid': return None else: return ExpectElement._sage_(self)
def eisenstein_basis(N, k, verbose=False): r""" Find spanning list of 'easy' generators for the subspace of `M_k(\Gamma_0(N))` generated by level 1 Eisenstein series and their images of even integer weights up to `k`. INPUT: - N -- positive integer - k -- positive integer - ``verbose`` -- bool (default: False) OUTPUT: - list of monomials in images of level 1 Eisenstein series - prec of q-expansions needed to determine element of `M_k(\Gamma_0(N))`. EXAMPLES:: sage: from psage.modform.rational.special import eisenstein_basis sage: eisenstein_basis(5,4) ([E4(q^5)^1, E4(q^1)^1, E2^*(q^5)^2], 3) sage: eisenstein_basis(11,2,verbose=True) # warning below because of verbose Warning -- not enough series. ([E2^*(q^11)^1], 2) sage: eisenstein_basis(11,2,verbose=False) ([E2^*(q^11)^1], 2) """ assert N > 1 if k % 2 != 0: return [] # Make list E of Eisenstein series, to enough precision to # determine them, until we span space. M = ModularForms(N, k) prec = M.echelon_basis()[-1].valuation() + 1 gens = eisenstein_gens(N, k, prec) R = PolynomialRing(ZZ, len(gens), ['E%sq%s'%(g[1],g[0]) for g in gens]) z = [(R.gen(i), g[1]) for i, g in enumerate(gens)] m = monomials(z, k) A = QQ**prec V = A.zero_subspace() E = [] for i, z in enumerate(m): d = z.degrees() f = prod(g[2]**d[i] for i, g in enumerate(gens) if d[i]) v = A(f.padded_list(prec)) if v not in V: V = V + A.span([v]) w = [(gens[i][0],gens[i][1],d[i]) for i in range(len(d)) if d[i]] E.append(EisensteinMonomial(w)) if V.dimension() == M.dimension(): return E, prec if verbose: print "Warning -- not enough series." return E, prec
def local_coordinates_at_infinity(self, prec = 20, name = 't'): """ For the genus `g` hyperelliptic curve `y^2 = f(x)`, return `(x(t), y(t))` such that `(y(t))^2 = f(x(t))`, where `t = x^g/y` is the local parameter at infinity INPUT: - ``prec`` -- desired precision of the local coordinates - ``name`` -- generator of the power series ring (default: ``t``) OUTPUT: `(x(t),y(t))` such that `y(t)^2 = f(x(t))` and `t = x^g/y` is the local parameter at infinity EXAMPLES:: sage: R.<x> = QQ['x'] sage: H = HyperellipticCurve(x^5-5*x^2+1) sage: x,y = H.local_coordinates_at_infinity(10) sage: x t^-2 + 5*t^4 - t^8 - 50*t^10 + O(t^12) sage: y t^-5 + 10*t - 2*t^5 - 75*t^7 + 50*t^11 + O(t^12) :: sage: R.<x> = QQ['x'] sage: H = HyperellipticCurve(x^3-x+1) sage: x,y = H.local_coordinates_at_infinity(10) sage: x t^-2 + t^2 - t^4 - t^6 + 3*t^8 + O(t^12) sage: y t^-3 + t - t^3 - t^5 + 3*t^7 - 10*t^11 + O(t^12) AUTHOR: - Jennifer Balakrishnan (2007-12) """ g = self.genus() pol = self.hyperelliptic_polynomials()[0] K = LaurentSeriesRing(self.base_ring(), name, default_prec=prec+2) t = K.gen() L = PolynomialRing(self.base_ring(),'x') x = L.gen() i = 0 w = (x**g/t)**2-pol wprime = w.derivative(x) x = t**-2 for i in range((RR(log(prec+2)/log(2))).ceil()): x = x-w(x)/wprime(x) y = x**g/t return x+O(t**(prec+2)) , y+O(t**(prec+2))
def local_coordinates_at_infinity(self, prec=20, name="t"): """ For the genus g hyperelliptic curve y^2 = f(x), returns (x(t), y(t)) such that (y(t))^2 = f(x(t)), where t = x^g/y is the local parameter at infinity INPUT: - prec: desired precision of the local coordinates - name: gen of the power series ring (default: 't') OUTPUT: (x(t),y(t)) such that y(t)^2 = f(x(t)) and t = x^g/y is the local parameter at infinity EXAMPLES: sage: R.<x> = QQ['x'] sage: H = HyperellipticCurve(x^5-5*x^2+1) sage: x,y = H.local_coordinates_at_infinity(10) sage: x t^-2 + 5*t^4 - t^8 - 50*t^10 + O(t^12) sage: y t^-5 + 10*t - 2*t^5 - 75*t^7 + 50*t^11 + O(t^12) sage: R.<x> = QQ['x'] sage: H = HyperellipticCurve(x^3-x+1) sage: x,y = H.local_coordinates_at_infinity(10) sage: x t^-2 + t^2 - t^4 - t^6 + 3*t^8 + O(t^12) sage: y t^-3 + t - t^3 - t^5 + 3*t^7 - 10*t^11 + O(t^12) AUTHOR: - Jennifer Balakrishnan (2007-12) """ g = self.genus() pol = self.hyperelliptic_polynomials()[0] K = LaurentSeriesRing(self.base_ring(), name) t = K.gen() K.set_default_prec(prec + 2) L = PolynomialRing(self.base_ring(), "x") x = L.gen() i = 0 w = (x ** g / t) ** 2 - pol wprime = w.derivative(x) x = t ** -2 for i in range((RR(log(prec + 2) / log(2))).ceil()): x = x - w(x) / wprime(x) y = x ** g / t return x + O(t ** (prec + 2)), y + O(t ** (prec + 2))
def tiny_integrals_on_basis(self, P, Q): r""" Evaluate the integrals `\{\int_P^Q x^i dx/2y \}_{i=0}^{2g-1}` by formally integrating a power series in a local parameter `t`. `P` and `Q` MUST be in the same residue disc for this result to make sense. INPUT: - P a point on self - Q a point on self (in the same residue disc as P) OUTPUT: The integrals `\{\int_P^Q x^i dx/2y \}_{i=0}^{2g-1}` EXAMPLES:: sage: K = pAdicField(17, 5) sage: E = EllipticCurve(K, [-31/3, -2501/108]) # 11a sage: P = E(K(14/3), K(11/2)) sage: TP = E.teichmuller(P); sage: E.tiny_integrals_on_basis(P, TP) (17 + 14*17^2 + 17^3 + 8*17^4 + O(17^5), 16*17 + 5*17^2 + 8*17^3 + 14*17^4 + O(17^5)) :: sage: K = pAdicField(11, 5) sage: x = polygen(K) sage: C = HyperellipticCurve(x^5 + 33/16*x^4 + 3/4*x^3 + 3/8*x^2 - 1/4*x + 1/16) sage: P = C.lift_x(11^(-2)) sage: Q = C.lift_x(3*11^(-2)) sage: C.tiny_integrals_on_basis(P,Q) (3*11^3 + 7*11^4 + 4*11^5 + 7*11^6 + 5*11^7 + O(11^8), 3*11 + 10*11^2 + 8*11^3 + 9*11^4 + 7*11^5 + O(11^6), 4*11^-1 + 2 + 6*11 + 6*11^2 + 7*11^3 + O(11^4), 11^-3 + 6*11^-2 + 2*11^-1 + 2 + O(11^2)) Note that this fails if the points are not in the same residue disc:: sage: S = C(0,1/4) sage: C.tiny_integrals_on_basis(P,S) Traceback (most recent call last): ... ValueError: (11^-2 + O(11^3) : 11^-5 + 8*11^-2 + O(11^0) : 1 + O(11^5)) and (0 : 3 + 8*11 + 2*11^2 + 8*11^3 + 2*11^4 + O(11^5) : 1 + O(11^5)) are not in the same residue disc """ if P == Q: V = VectorSpace(self.base_ring(), 2*self.genus()) return V(0) R = PolynomialRing(self.base_ring(), ['x', 'y']) x, y = R.gens() return self.tiny_integrals([x**i for i in range(2*self.genus())], P, Q)
def __init__(self, params, asym=False): (self.n, self.q, sigma, self.sigma_prime, self.k) = params S, x = PolynomialRing(ZZ, 'x').objgen() self.R = S.quotient_ring(S.ideal(x**self.n + 1)) Sq = PolynomialRing(Zmod(self.q), 'x') self.Rq = Sq.quotient_ring(Sq.ideal(x**self.n + 1)) # draw z_is uniformly from Rq and compute its inverse in Rq if asym: z = [self.Rq.random_element() for i in range(self.k)] self.zinv = [z_i**(-1) for z_i in z] else: # or do symmetric version z = self.Rq.random_element() zinv = z**(-1) z, self.zinv = zip(*[(z,zinv) for i in range(self.k)]) # set up some discrete Gaussians DGSL_sigma = DGSL(ZZ**self.n, sigma) self.D_sigma = lambda: self.Rq(list(DGSL_sigma())) # discrete Gaussian in ZZ^n with stddev sigma_prime, yields random level-0 encodings DGSL_sigmap_ZZ = DGSL(ZZ**self.n, self.sigma_prime) self.D_sigmap_ZZ = lambda: self.Rq(list(DGSL_sigmap_ZZ())) # draw g repeatedly from a Gaussian distribution of Z^n (with param sigma) # until g^(-1) in QQ[x]/<x^n + 1> is small (< n^2) Sk = PolynomialRing(QQ, 'x') K = Sk.quotient_ring(Sk.ideal(x**self.n + 1)) while True: l = self.D_sigma() ginv_K = K(mod_near_poly(l, self.q))**(-1) ginv_size = vector(ginv_K).norm() if ginv_size < self.n**2: g = self.Rq(l) self.ginv = g**(-1) break # discrete Gaussian in I = <g>, yields random encodings of 0 short_g = vector(ZZ, mod_near_poly(g,self.q)) DGSL_sigmap_I = DGSL(short_g, self.sigma_prime) self.D_sigmap_I = lambda: self.Rq(list(DGSL_sigmap_I())) # compute zero-testing parameter p_zt # randomly draw h (in Rq) from a discrete Gaussian with param q^(1/2) self.h = self.Rq(list(DGSL(ZZ**self.n, round(sqrt(self.q)))())) # create p_zt self.p_zt = self.ginv * self.h * prod(z)
def _check_muqt(mu, q, t, pi=None): """ EXAMPLES:: sage: from sage.combinat.sf.ns_macdonald import _check_muqt sage: P, q, t, n, R, x = _check_muqt([0,0,1],None,None) sage: P Fraction Field of Multivariate Polynomial Ring in q, t over Rational Field sage: q q sage: t t sage: n Nonattacking fillings of [0, 0, 1] sage: R Multivariate Polynomial Ring in x0, x1, x2 over Fraction Field of Multivariate Polynomial Ring in q, t over Rational Field sage: x (x0, x1, x2) :: sage: q,t = var('q,t') sage: P, q, t, n, R, x = _check_muqt([0,0,1],q,None) Traceback (most recent call last): ... ValueError: you must specify either both q and t or neither of them :: sage: P, q, t, n, R, x = _check_muqt([0,0,1],q,2) Traceback (most recent call last): ... ValueError: the parents of q and t must be the same """ if q is None and t is None: P = PolynomialRing(QQ,'q,t').fraction_field() q,t = P.gens() elif q is not None and t is not None: if q.parent() != t.parent(): raise ValueError("the parents of q and t must be the same") P = q.parent() else: raise ValueError("you must specify either both q and t or neither of them") n = NonattackingFillings(mu, pi) R = PolynomialRing(P, len(n._shape), 'x') x = R.gens() return P, q, t, n, R, x
def __init__(self, group, base_ring, red_hom): r""" Abstract (Hecke) forms ring. INPUT: - ``group`` - The Hecke triangle group (default: ``HeckeTriangleGroup(3)``) - ``base_ring`` - The base_ring (default: ``ZZ``). - ``red_hom`` - If True then results of binary operations are considered homogeneous whenever it makes sense (default: False). This is mainly used by the (Hecke) forms. OUTPUT: The corresponding abstract (Hecke) forms ring. EXAMPLES:: sage: from graded_ring import ModularFormsRing sage: MR = ModularFormsRing(group=5, base_ring=ZZ, red_hom=True) sage: MR ModularFormsRing(n=5) over Integer Ring sage: MR.group() Hecke triangle group for n = 5 sage: MR.base_ring() Integer Ring sage: MR.has_reduce_hom() True sage: MR.is_homogeneous() False """ from graded_ring import canonical_parameters (group, base_ring, red_hom) = canonical_parameters(group, base_ring, red_hom) if (group == infinity): raise NotImplementedError #if (not group.is_arithmetic() and base_ring.characteristic()>0): # raise NotImplementedError #if (base_ring.characteristic().divides(2*group.n()*(group.n()-2))): # raise NotImplementedError if (base_ring.characteristic() > 0): raise NotImplementedError self._group = group self._red_hom = red_hom self._base_ring = base_ring self._coeff_ring = FractionField(PolynomialRing(base_ring,'d')) self._pol_ring = PolynomialRing(base_ring,'x,y,z,d') self._rat_field = FractionField(self._pol_ring) # default values self._weight = None self._ep = None self._analytic_type = self.AT(["quasi", "mero"]) self.default_prec(10) self.disp_prec(5) self.default_num_prec(53)
def curve_over_ram_extn(self, deg): r""" Returns self over $\Q_p(p^(1/deg))$ INPUT: - deg: the degree of the ramified extension OUTPUT: self over $\Q_p(p^(1/deg))$ EXAMPLES:: sage: R.<x> = QQ['x'] sage: H = HyperellipticCurve(x^5-23*x^3+18*x^2+40*x) sage: K = Qp(11,5) sage: HK = H.change_ring(K) sage: HL = HK.curve_over_ram_extn(2) sage: HL Hyperelliptic Curve over Eisenstein Extension of 11-adic Field with capped relative precision 5 in a defined by (1 + O(11^5))*x^2 + (O(11^6))*x + (10*11 + 10*11^2 + 10*11^3 + 10*11^4 + 10*11^5 + O(11^6)) defined by (1 + O(a^10))*y^2 = (1 + O(a^10))*x^5 + (10 + 8*a^2 + 10*a^4 + 10*a^6 + 10*a^8 + O(a^10))*x^3 + (7 + a^2 + O(a^10))*x^2 + (7 + 3*a^2 + O(a^10))*x AUTHOR: - Jennifer Balakrishnan """ from sage.schemes.hyperelliptic_curves.constructor import HyperellipticCurve K = self.base_ring() p = K.prime() A = PolynomialRing(QQ, "x") x = A.gen() J = K.extension(x ** deg - p, names="a") pol = self.hyperelliptic_polynomials()[0] H = HyperellipticCurve(A(pol)) HJ = H.change_ring(J) self._curve_over_ram_extn = HJ self._curve_over_ram_extn._curve_over_Qp = self return HJ
def ubs(f): r""" Given a sextic form `f`, return a dictionary of the invariants of Mestre, p 317 [M]_. `f` may be homogeneous in two variables or inhomogeneous in one. EXAMPLES:: sage: from sage.schemes.hyperelliptic_curves.invariants import ubs sage: x = QQ['x'].0 sage: ubs(x^6 + 1) {'A': 2, 'C': -2/9, 'B': 2/3, 'D': 0, 'f': x^6 + h^6, 'i': 2*x^2*h^2, 'Delta': -2/3*x^2*h^2, 'y1': 0, 'y3': 0, 'y2': 0} sage: R.<u, v> = QQ[] sage: ubs(u^6 + v^6) {'A': 2, 'C': -2/9, 'B': 2/3, 'D': 0, 'f': u^6 + v^6, 'i': 2*u^2*v^2, 'Delta': -2/3*u^2*v^2, 'y1': 0, 'y3': 0, 'y2': 0} sage: R.<t> = GF(31)[] sage: ubs(t^6 + 2*t^5 + t^2 + 3*t + 1) {'A': 0, 'C': -15, 'B': -12, 'D': -15, 'f': t^6 + 2*t^5*h + t^2*h^4 + 3*t*h^5 + h^6, 'i': -4*t^4 + 10*t^3*h + 2*t^2*h^2 - 9*t*h^3 - 7*h^4, 'Delta': -10*t^4 + 12*t^3*h + 7*t^2*h^2 - 5*t*h^3 + 2*h^4, 'y1': 4*t^2 - 10*t*h - 13*h^2, 'y3': 4*t^2 - 4*t*h - 9*h^2, 'y2': 6*t^2 - 4*t*h + 2*h^2} """ ub = Ueberschiebung if f.parent().ngens() == 1: f = PolynomialRing(f.parent().base_ring(), 1, f.parent().variable_name())(f) x1, x2 = f.homogenize().parent().gens() f = sum([ f[i]*x1**i*x2**(6-i) for i in range(7) ]) U = {} U['f'] = f U['i'] = ub(f, f, 4) U['Delta'] = ub(U['i'], U['i'], 2) U['y1'] = ub(f, U['i'], 4) U['y2'] = ub(U['i'], U['y1'], 2) U['y3'] = ub(U['i'], U['y2'], 2) U['A'] = ub(f, f, 6) U['B'] = ub(U['i'], U['i'], 4) U['C'] = ub(U['i'], U['Delta'], 4) U['D'] = ub(U['y3'], U['y1'], 2) return U
def generator_relations(self, K) : """ An ideal `I` in a polynomial ring `R` over `K`, such that the associated ring is `R / I` surjects onto the ring of modular forms with coefficients in `K`. INPUT: - `K` -- A ring. OUTPUT: An ideal in a polynomial ring. TESTS:: sage: from psage.modform.fourier_expansion_framework.modularforms.modularform_testtype import * sage: t = ModularFormTestType_vectorvalued() sage: t.generator_relations(QQ) Ideal (g1^2 - g2, g1^3 - g3, g1^4 - g4, g1^5 - g5) of Multivariate Polynomial Ring in g1, g2, g3, g4, g5, v1, v2, v3 over Rational Field """ if K.has_coerce_map_from(ZZ) : R = PolynomialRing(K, self.non_vector_valued()._generator_names(K) + self._generator_names(K)) return R.ideal().parent()(self.non_vector_valued().generator_relations(K)) raise NotImplementedError
def generator_relations(self, K) : """ An ideal `I` in a polynomial ring `R` over `K`, such that the associated ring is `R / I` surjects onto the ring of modular forms with coefficients in `K`. INPUT: - `K` -- A ring. OUTPUT: An ideal in a polynomial ring. TESTS:: sage: from psage.modform.fourier_expansion_framework.modularforms.modularform_testtype import * sage: t = ModularFormTestType_scalar() sage: t.generator_relations(QQ) Ideal (g1^2 - g2, g1^3 - g3, g1^4 - g4, g1^5 - g5) of Multivariate Polynomial Ring in g1, g2, g3, g4, g5 over Rational Field """ if K.has_coerce_map_from(ZZ) : R = PolynomialRing(K, self._generator_names(K)) g1 = R.gen(0) return R.ideal([g1**i - g for (i,g) in list(enumerate([None] + list(R.gens())))[2:]]) raise NotImplementedError
def coordinate_ring(self): """ Return the coordinate ring of this scheme, if defined. EXAMPLES:: sage: R = AffineSpace(2, GF(9,'alpha'), 'z').coordinate_ring(); R Multivariate Polynomial Ring in z0, z1 over Finite Field in alpha of size 3^2 sage: AffineSpace(3, R, 'x').coordinate_ring() Multivariate Polynomial Ring in x0, x1, x2 over Multivariate Polynomial Ring in z0, z1 over Finite Field in alpha of size 3^2 """ try: return self._coordinate_ring except AttributeError: self._coordinate_ring = PolynomialRing(self.base_ring(), self.dimension_relative(), names=self.variable_names()) return self._coordinate_ring
def _load(self, path, filename): print(filename) i = 0 while filename[i].isalpha(): i += 1 j = len(filename) - 1 while filename[j].isalpha() or filename[j] in [".", "_"]: j -= 1 S = sorted([eval(z) for z in filename[i:j + 1].split("-")]) data = open(path + "/" + filename).read() data = data.replace("^", "**") x = PolynomialRing(RationalField(), 'x').gen() v = eval(data) s = tuple(S) if s in self.root: self.root[s] += v self.root[s].sort() else: self.root[s] = v
def _init(self, path): """ Create the database from scratch from the PARI files on John Jones's web page, downloaded (e.g., via wget) to a local directory, which is specified as path above. INPUT: - ``path`` - (default works on William Stein install.) path must be the path to Jones's Number_Fields directory http://hobbes.la.asu.edu/Number_Fields These files should have been downloaded using wget. EXAMPLES: This is how to create the database from scratch, assuming that the number fields are in the default directory above: From a cold start of Sage:: sage: J = JonesDatabase() sage: J._init() # not tested ... This takes about 5 seconds. """ from sage.misc.misc import sage_makedirs n = 0 x = PolynomialRing(RationalField(), 'x').gen() self.root = {} self.root[tuple([])] = [x - 1] if not os.path.exists(path): raise IOError("Path %s does not exist." % path) for X in os.listdir(path): if X[-4:] == "solo": Z = path + "/" + X print(X) for Y in os.listdir(Z): if Y[-3:] == ".gp": self._load(Z, Y) sage_makedirs(JONESDATA) save(self.root, JONESDATA + "/jones.sobj")
def _check_muqt(mu, q, t, pi=None): """ EXAMPLES:: sage: from sage.combinat.sf.ns_macdonald import _check_muqt sage: P, q, t, n, R, x = _check_muqt([0,0,1],None,None) sage: P Fraction Field of Multivariate Polynomial Ring in q, t over Rational Field sage: q q sage: t t sage: n Nonattacking fillings of [0, 0, 1] sage: R Multivariate Polynomial Ring in x0, x1, x2 over Fraction Field of Multivariate Polynomial Ring in q, t over Rational Field sage: x (x0, x1, x2) :: sage: q,t = var('q,t') sage: P, q, t, n, R, x = _check_muqt([0,0,1],q,None) Traceback (most recent call last): ... ValueError: you must specify either both q and t or neither of them :: sage: P, q, t, n, R, x = _check_muqt([0,0,1],q,2) Traceback (most recent call last): ... ValueError: the parents of q and t must be the same """ if q is None and t is None: P = PolynomialRing(QQ, 'q,t').fraction_field() q, t = P.gens() elif q is not None and t is not None: if q.parent() != t.parent(): raise ValueError("the parents of q and t must be the same") P = q.parent() else: raise ValueError( "you must specify either both q and t or neither of them") n = NonattackingFillings(mu, pi) R = PolynomialRing(P, len(n._shape), 'x') x = R.gens() return P, q, t, n, R, x
def satake_polynomial(self): r""" Return the Satake polynomial of this representation, i.e.~the polynomial whose roots are `\chi_1(p), \chi_2(p)` where this representation is `\pi(\chi_1, \chi_2)`. Concretely, this is the polynomial .. math:: X^2 - p^{(j - k + 2)/2} a_p(f) X + p^{j + 1} \varepsilon(p)`. An error will be raised if `j \ne k \bmod 2`. EXAMPLES:: sage: LocalComponent(Newform('11a'), 17).satake_polynomial() X^2 + 2*X + 17 sage: LocalComponent(Newform('11a'), 17, twist_factor = -2).satake_polynomial() X^2 + 2/17*X + 1/17 """ p = self.prime() return PolynomialRing(self.coefficient_field(), 'X')([ self.central_character()(p) * p, -self.newform()[p] * p**((self.twist_factor() - self.newform().weight() + 2) / 2), 1 ])
def elkies_first_step(E, l, lam): q = E.base_field().order() lam = GF(l)(lam) Phi = ClassicalModularPolynomialDatabase()[l] x = PolynomialRing(E.base_field(), 'x').gen() f = Phi(x, E.j_invariant()) j_1, j_2 = f.roots()[0][0], f.roots()[1][0] E1 = elkies_mod_poly(E, j_1, l) try: I = EllipticCurveIsogeny(E, None, E1, l) except: I = l_isogeny(E, E1, l) r = lam.multiplicative_order() k = GF(q ** r) ext = extend_field(E, r) try: P = ext.lift_x(I.kernel_polynomial().any_root(k)) except: return j_2 if ext(P[0] ** q, P[1] ** q) == Integer(lam) * P: return j_1 else: return j_2
def expand(self): """ EXAMPLES:: sage: X = SchubertPolynomialRing(ZZ) sage: X([2,1,3]).expand() x0 sage: map(lambda x: x.expand(), [X(p) for p in Permutations(3)]) [1, x0 + x1, x0, x0*x1, x0^2, x0^2*x1] TESTS: Calling .expand() should always return an element of an MPolynomialRing :: sage: X = SchubertPolynomialRing(ZZ) sage: f = X([1]); f X[1] sage: type(f.expand()) <type 'sage.rings.polynomial.multi_polynomial_libsingular.MPolynomial_libsingular'> sage: f.expand() 1 sage: f = X([1,2]) sage: type(f.expand()) <type 'sage.rings.polynomial.multi_polynomial_libsingular.MPolynomial_libsingular'> sage: f = X([1,3,2,4]) sage: type(f.expand()) <type 'sage.rings.polynomial.multi_polynomial_libsingular.MPolynomial_libsingular'> """ p = symmetrica.t_SCHUBERT_POLYNOM(self) if not is_MPolynomial(p): R = PolynomialRing(self.parent().base_ring(), 1, 'x') p = R(p) return p
def differential_operator(f, g, k): r""" Return the differential operator `(f g)_k` symbolically in the polynomial ring in ``dfdx, dfdy, dgdx, dgdy``. This is defined by Mestre on p 315 [MJ1991]_: .. MATH:: (f g)_k = \frac{(m - k)! (n - k)!}{m! n!} \left( \frac{\partial f}{\partial x} \frac{\partial g}{\partial y} - \frac{\partial f}{\partial y} \frac{\partial g}{\partial x} \right)^k . EXAMPLES:: sage: from sage.schemes.hyperelliptic_curves.invariants import differential_operator sage: R.<x, y> = QQ[] sage: differential_operator(x, y, 0) 1 sage: differential_operator(x, y, 1) -dfdy*dgdx + dfdx*dgdy sage: differential_operator(x*y, x*y, 2) 1/4*dfdy^2*dgdx^2 - 1/2*dfdx*dfdy*dgdx*dgdy + 1/4*dfdx^2*dgdy^2 sage: differential_operator(x^2*y, x*y^2, 2) 1/36*dfdy^2*dgdx^2 - 1/18*dfdx*dfdy*dgdx*dgdy + 1/36*dfdx^2*dgdy^2 sage: differential_operator(x^2*y, x*y^2, 4) 1/576*dfdy^4*dgdx^4 - 1/144*dfdx*dfdy^3*dgdx^3*dgdy + 1/96*dfdx^2*dfdy^2*dgdx^2*dgdy^2 - 1/144*dfdx^3*dfdy*dgdx*dgdy^3 + 1/576*dfdx^4*dgdy^4 """ (x, y) = f.parent().gens() n = max(ZZ(f.degree()), ZZ(k)) m = max(ZZ(g.degree()), ZZ(k)) R, (fx, fy, gx, gy) = PolynomialRing(f.base_ring(), 4, 'dfdx,dfdy,dgdx,dgdy').objgens() const = (m - k).factorial() * (n - k).factorial() / (m.factorial() * n.factorial()) U = f.base_ring()(const) * (fx * gy - fy * gx)**k return U
class FormsRing_abstract(Parent): r""" Abstract (Hecke) forms ring. This should never be called directly. Instead one should instantiate one of the derived classes of this class. """ from graded_ring_element import FormsRingElement Element = FormsRingElement from analytic_type import AnalyticType AT = AnalyticType() def __init__(self, group, base_ring, red_hom): r""" Abstract (Hecke) forms ring. INPUT: - ``group`` - The Hecke triangle group (default: ``HeckeTriangleGroup(3)``) - ``base_ring`` - The base_ring (default: ``ZZ``). - ``red_hom`` - If True then results of binary operations are considered homogeneous whenever it makes sense (default: False). This is mainly used by the (Hecke) forms. OUTPUT: The corresponding abstract (Hecke) forms ring. EXAMPLES:: sage: from graded_ring import ModularFormsRing sage: MR = ModularFormsRing(group=5, base_ring=ZZ, red_hom=True) sage: MR ModularFormsRing(n=5) over Integer Ring sage: MR.group() Hecke triangle group for n = 5 sage: MR.base_ring() Integer Ring sage: MR.has_reduce_hom() True sage: MR.is_homogeneous() False """ from graded_ring import canonical_parameters (group, base_ring, red_hom) = canonical_parameters(group, base_ring, red_hom) if (group == infinity): raise NotImplementedError #if (not group.is_arithmetic() and base_ring.characteristic()>0): # raise NotImplementedError #if (base_ring.characteristic().divides(2*group.n()*(group.n()-2))): # raise NotImplementedError if (base_ring.characteristic() > 0): raise NotImplementedError self._group = group self._red_hom = red_hom self._base_ring = base_ring self._coeff_ring = FractionField(PolynomialRing(base_ring,'d')) self._pol_ring = PolynomialRing(base_ring,'x,y,z,d') self._rat_field = FractionField(self._pol_ring) # default values self._weight = None self._ep = None self._analytic_type = self.AT(["quasi", "mero"]) self.default_prec(10) self.disp_prec(5) self.default_num_prec(53) #super(FormsRing_abstract, self).__init__(self.coeff_ring()) def _repr_(self): r""" Return the string representation of ``self``. EXAMPLES:: sage: from graded_ring import QModularFormsRing sage: QModularFormsRing(group=4) QuasiModularFormsRing(n=4) over Integer Ring """ return "{}FormsRing(n={}) over {}".format(self._analytic_type.analytic_space_name(), self._group.n(), self._base_ring) def _latex_(self): r""" Return the LaTeX representation of ``self``. EXAMPLES:: sage: from graded_ring import QWeakModularFormsRing sage: latex(QWeakModularFormsRing()) \mathcal{ QM^! }_{n=3}(\Bold{Z}) """ from sage.misc.latex import latex return "\\mathcal{{ {} }}_{{n={}}}({})".format(self._analytic_type.latex_space_name(), self._group.n(), latex(self._base_ring)) def _element_constructor_(self, x): r""" Return ``x`` coerced/converted into this forms ring. EXAMPLES:: sage: from graded_ring import ModularFormsRing sage: MR = ModularFormsRing() sage: (x,y,z,d) = MR.pol_ring().gens() sage: MR(x^3) f_rho^3 sage: el = MR.Delta().full_reduce() sage: MR(el) f_rho^3*d - f_i^2*d sage: el.parent() == MR False sage: MR(el).parent() == MR True """ from graded_ring_element import FormsRingElement if isinstance(x, FormsRingElement): x = self._rat_field(x._rat) else: x = self._rat_field(x) return self.element_class(self, x) def _coerce_map_from_(self, S): r""" Return whether or not there exists a coercion from ``S`` to ``self``. EXAMPLES:: sage: from graded_ring import QWeakModularFormsRing, ModularFormsRing, CuspFormsRing sage: MR1 = QWeakModularFormsRing(base_ring=CC) sage: MR2 = ModularFormsRing() sage: MR3 = CuspFormsRing() sage: MR3.has_coerce_map_from(MR2) False sage: MR1.has_coerce_map_from(MR2) True sage: MR2.has_coerce_map_from(MR3) True sage: MR3.has_coerce_map_from(ZZ) False sage: MR1.has_coerce_map_from(ZZ) True sage: from space import ModularForms, CuspForms sage: MF2 = ModularForms(k=6, ep=-1) sage: MF3 = CuspForms(k=12, ep=1) sage: MR1.has_coerce_map_from(MF2) True sage: MR2.has_coerce_map_from(MF3) True """ from space import FormsSpace_abstract if ( isinstance(S, FormsRing_abstract)\ and self._group == S._group\ and self._analytic_type >= S._analytic_type\ and self.base_ring().has_coerce_map_from(S.base_ring()) ): return True # TODO: This case never occurs: remove it? elif isinstance(S, FormsSpace_abstract): return self._coerce_map_from_(S.graded_ring()) elif (self.AT("holo") <= self._analytic_type) and (self.coeff_ring().has_coerce_map_from(S)): return True else: return False def _an_element_(self): r""" Return an element of ``self``. EXAMPLES:: sage: from graded_ring import CuspFormsRing sage: from space import WeakModularForms sage: CuspFormsRing().an_element() f_rho^3*d - f_i^2*d sage: CuspFormsRing().an_element() == CuspFormsRing().Delta() True sage: WeakModularForms().an_element() O(q^5) sage: WeakModularForms().an_element() == WeakModularForms().zero() True """ return self(self.Delta()) def default_prec(self, prec = None): r""" Set the default precision ``prec`` for the Fourier expansion. If ``prec=None`` (default) then the current default precision is returned instead. Note: This is also used as the default precision for the Fourier expansion when evaluating forms. EXAMPLES:: sage: from graded_ring import ModularFormsRing sage: from space import ModularForms sage: MR = ModularFormsRing() sage: MR.default_prec(3) sage: MR.default_prec() 3 sage: MR.Delta().q_expansion_fixed_d() q - 24*q^2 + O(q^3) sage: MF = ModularForms(k=4) sage: MF.default_prec(2) sage: MF.E4() 1 + 240*q + O(q^2) sage: MF.default_prec() 2 """ if (prec is not None): self._prec = ZZ(prec) else: return self._prec def disp_prec(self, prec = None): r""" Set the maximal display precision to ``prec``. If ``prec="max"`` the precision is set to the default precision. If ``prec=None`` (default) then the current display precision is returned instead. Note: This is used for displaying/representing (elements of) ``self`` as Fourier expansions. EXAMPLES:: sage: from space import ModularForms sage: MF = ModularForms(k=4) sage: MF.default_prec(5) sage: MF.disp_prec(3) sage: MF.disp_prec() 3 sage: MF.E4() 1 + 240*q + 2160*q^2 + O(q^3) sage: MF.disp_prec("max") sage: MF.E4() 1 + 240*q + 2160*q^2 + 6720*q^3 + 17520*q^4 + O(q^5) """ if (prec == "max"): self._disp_prec = self._prec; elif (prec is not None): self._disp_prec = ZZ(prec) else: return self._disp_prec def default_num_prec(self, prec = None): r""" Set the default numerical precision to ``prec`` (default: ``53``). If ``prec=None`` (default) the current default numerical precision is returned instead. EXAMPLES:: sage: from space import ModularForms sage: MF = ModularForms(k=6) sage: MF.default_prec(20) sage: MF.default_num_prec(10) sage: MF.default_num_prec() 10 sage: E6 = MF.E6() sage: E6(i) # rel tol 1e-4 -0.0020 sage: MF.default_num_prec(100) sage: E6(i) # rel tol 1e-25 0.00000000000000000000000000000 sage: MF = ModularForms(group=5, k=4/3) sage: F_rho = MF.F_rho() sage: F_rho.q_expansion(prec=2)[1] 7/(100*d) sage: MF.default_num_prec(10) sage: F_rho.q_expansion_fixed_d(prec=2)[1] # rel tol 1e-1 9.9 sage: MF.default_num_prec(100) sage: F_rho.q_expansion_fixed_d(prec=2)[1] # rel tol 1e-25 9.9259324351079591527601778294 """ if (prec is not None): self._num_prec = ZZ(prec) else: return self._num_prec def change_ring(self, new_base_ring): r""" Return the same space as ``self`` but over a new base ring ``new_base_ring``. EXAMPLES:: sage: from graded_ring import ModularFormsRing sage: ModularFormsRing().change_ring(CC) ModularFormsRing(n=3) over Complex Field with 53 bits of precision """ return self.__class__.__base__(self._group, new_base_ring, self._red_hom) def graded_ring(self): r""" Return the graded ring containing ``self``. EXAMPLES:: sage: from graded_ring import ModularFormsRing, CuspFormsRing sage: from space import CuspForms sage: MR = ModularFormsRing(group=5) sage: MR.graded_ring() == MR True sage: CF=CuspForms(k=12) sage: CF.graded_ring() == CuspFormsRing() False sage: CF.graded_ring() == CuspFormsRing(red_hom=True) True sage: CF.subspace([CF.Delta()]).graded_ring() == CuspFormsRing(red_hom=True) True """ return self.extend_type(ring=True) def extend_type(self, analytic_type=None, ring=False): r""" Return a new space which contains (elements of) ``self`` with the analytic type of ``self`` extended by ``analytic_type``, possibly extended to a graded ring in case ``ring`` is ``True``. INPUT: - ``analytic_type`` - An ``AnalyticType`` or something which coerces into it (default: ``None``). - ``ring`` - Whether to extend to a graded ring (default: ``False``). OUTPUT: The new extended space. EXAMPLES:: sage: from graded_ring import ModularFormsRing sage: from space import CuspForms sage: MR = ModularFormsRing(group=5) sage: MR.extend_type(["quasi", "weak"]) QuasiWeakModularFormsRing(n=5) over Integer Ring sage: CF=CuspForms(k=12) sage: CF.extend_type("holo") ModularForms(n=3, k=12, ep=1) over Integer Ring sage: CF.extend_type("quasi", ring=True) QuasiCuspFormsRing(n=3) over Integer Ring sage: CF.subspace([CF.Delta()]).extend_type() CuspForms(n=3, k=12, ep=1) over Integer Ring """ if analytic_type == None: analytic_type = self._analytic_type else: analytic_type = self._analytic_type.extend_by(analytic_type) if (ring or not self.is_homogeneous()): return FormsRing(analytic_type, group=self.group(), base_ring=self.base_ring(), red_hom=self.has_reduce_hom()) else: return FormsSpace(analytic_type, group=self.group(), base_ring=self.base_ring(), k=self.weight(), ep=self.ep()) def reduce_type(self, analytic_type=None, degree=None): r""" Return a new space with analytic properties shared by both ``self`` and ``analytic_type``, possibly reduced to its homogeneous space of the given ``degree`` (if ``degree`` is set). Elements of the new space are contained in ``self``. INPUT: - ``analytic_type`` - An ``AnalyticType`` or something which coerces into it (default: ``None``). - ``degree`` - ``None`` (default) or the degree of the homogeneous component to which ``self`` should be reduced. OUTPUT: The new reduced space. EXAMPLES:: sage: from graded_ring import QModularFormsRing sage: from space import QModularForms sage: MR = QModularFormsRing() sage: MR.reduce_type(["quasi", "cusp"]) QuasiCuspFormsRing(n=3) over Integer Ring sage: MR.reduce_type("cusp", degree=(12,1)) CuspForms(n=3, k=12, ep=1) over Integer Ring sage: MF=QModularForms(k=6) sage: MF.reduce_type("holo") ModularForms(n=3, k=6, ep=-1) over Integer Ring sage: MF.reduce_type([]) ZeroForms(n=3, k=6, ep=-1) over Integer Ring """ if analytic_type == None: analytic_type = self._analytic_type else: analytic_type = self._analytic_type.reduce_to(analytic_type) if (degree == None and not self.is_homogeneous()): return FormsRing(analytic_type, group=self.group(), base_ring=self.base_ring(), red_hom=self.has_reduce_hom()) elif (degree == None): return FormsSpace(analytic_type, group=self.group(), base_ring=self.base_ring(), k=self.weight(), ep=self.ep()) else: (weight, ep) = degree if (self.is_homogeneous() and (weight != self.weight() or ep!=self.ep())): analytic_type = self._analytic_type.reduce_to([]) return FormsSpace(analytic_type, group=self.group(), base_ring=self.base_ring(), k=weight, ep=ep) def construction(self): r""" Return a functor that constructs ``self`` (used by the coercion machinery). EXAMPLES:: sage: from graded_ring import ModularFormsRing sage: ModularFormsRing().construction() (ModularFormsRingFunctor(n=3), BaseFacade(Integer Ring)) """ from functors import FormsRingFunctor, BaseFacade return FormsRingFunctor(self._analytic_type, self._group, self._red_hom), BaseFacade(self._base_ring) @cached_method def group(self): r""" Return the (Hecke triangle) group of ``self``. EXAMPLES:: sage: from graded_ring import ModularFormsRing sage: MR = ModularFormsRing(group=7) sage: MR.group() Hecke triangle group for n = 7 sage: from space import CuspForms sage: CF = CuspForms(group=7, k=4/5) sage: CF.group() Hecke triangle group for n = 7 """ return self._group @cached_method def hecke_n(self): r""" Return the parameter ``n`` of the (Hecke triangle) group of ``self``. EXAMPLES:: sage: from graded_ring import ModularFormsRing sage: MR = ModularFormsRing(group=7) sage: MR.hecke_n() 7 sage: from space import CuspForms sage: CF = CuspForms(group=7, k=4/5) sage: CF.hecke_n() 7 """ return self._group.n() @cached_method def base_ring(self): r""" Return base ring of ``self``. EXAMPLES:: sage: from graded_ring import ModularFormsRing sage: ModularFormsRing().base_ring() Integer Ring sage: from space import CuspForms sage: CuspForms(k=12, base_ring=AA).base_ring() Algebraic Real Field """ return self._base_ring @cached_method def coeff_ring(self): r""" Return coefficient ring of ``self``. EXAMPLES:: sage: from graded_ring import ModularFormsRing sage: ModularFormsRing().coeff_ring() Fraction Field of Univariate Polynomial Ring in d over Integer Ring sage: from space import CuspForms sage: CuspForms(k=12, base_ring=AA).coeff_ring() Fraction Field of Univariate Polynomial Ring in d over Algebraic Real Field """ return self._coeff_ring @cached_method def pol_ring(self): r""" Return the underlying polynomial ring used by ``self``. EXAMPLES:: sage: from graded_ring import ModularFormsRing sage: ModularFormsRing().pol_ring() Multivariate Polynomial Ring in x, y, z, d over Integer Ring sage: from space import CuspForms sage: CuspForms(k=12, base_ring=AA).pol_ring() Multivariate Polynomial Ring in x, y, z, d over Algebraic Real Field """ return self._pol_ring @cached_method def rat_field(self): r""" Return the underlying rational field used by ``self`` to construct/represent elements. EXAMPLES:: sage: from graded_ring import ModularFormsRing sage: ModularFormsRing().rat_field() Fraction Field of Multivariate Polynomial Ring in x, y, z, d over Integer Ring sage: from space import CuspForms sage: CuspForms(k=12, base_ring=AA).rat_field() Fraction Field of Multivariate Polynomial Ring in x, y, z, d over Algebraic Real Field """ return self._rat_field @cached_method def diff_alg(self): r""" Return the algebra of differential operators (over QQ) which is used on rational functions representing elements of ``self``. EXAMPLES:: sage: from graded_ring import ModularFormsRing sage: ModularFormsRing().diff_alg() Noncommutative Multivariate Polynomial Ring in X, Y, Z, dX, dY, dZ over Rational Field, nc-relations: {dY*Y: Y*dY + 1, dZ*Z: Z*dZ + 1, dX*X: X*dX + 1} sage: from space import CuspForms sage: CuspForms(k=12, base_ring=AA).diff_alg() Noncommutative Multivariate Polynomial Ring in X, Y, Z, dX, dY, dZ over Rational Field, nc-relations: {dY*Y: Y*dY + 1, dZ*Z: Z*dZ + 1, dX*X: X*dX + 1} """ # We only use two operators for now which do not involve 'd', so for performance # reason we choose FractionField(base_ring) instead of self.coeff_ring(). free_alg = FreeAlgebra(FractionField(ZZ),6,'X,Y,Z,dX,dY,dZ') (X,Y,Z,dX,dY,dZ) = free_alg.gens() diff_alg = free_alg.g_algebra({dX*X:1+X*dX,dY*Y:1+Y*dY,dZ*Z:1+Z*dZ}) return diff_alg @cached_method def _derivative_op(self): r""" Return the differential operator in ``self.diff_alg()`` corresponding to the derivative of forms. EXAMPLES:: sage: from graded_ring import ModularFormsRing sage: ModularFormsRing(group=7)._derivative_op() -1/2*X^6*dY - 5/28*X^5*dZ + 1/7*X*Z*dX + 1/2*Y*Z*dY + 5/28*Z^2*dZ - 1/7*Y*dX """ (X,Y,Z,dX,dY,dZ) = self.diff_alg().gens() return 1/self._group.n() * (X*Z-Y)*dX\ + ZZ(1)/ZZ(2) * (Y*Z-X**(self._group.n()-1))*dY\ + (self._group.n()-2) / (4*self._group.n()) * (Z**2-X**(self._group.n()-2))*dZ @cached_method def _serre_derivative_op(self): r""" Return the differential operator in ``self.diff_alg()`` corresponding to the serre derivative of forms. EXAMPLES:: sage: from graded_ring import ModularFormsRing sage: ModularFormsRing(group=8)._serre_derivative_op() -1/2*X^7*dY - 3/16*X^6*dZ - 3/16*Z^2*dZ - 1/8*Y*dX """ (X,Y,Z,dX,dY,dZ) = self.diff_alg().gens() return - 1/self._group.n() * Y*dX\ - ZZ(1)/ZZ(2) * X**(self._group.n()-1)*dY\ - (self._group.n()-2) / (4*self._group.n()) * (Z**2+X**(self._group.n()-2))*dZ @cached_method def has_reduce_hom(self): r""" Return whether the method ``reduce`` should reduce homogeneous elements to the corresponding homogeneous space. This is mainly used by binary operations on homogeneous spaces which temporarily produce an element of ``self`` but want to consider it as a homogeneous element (also see ``reduce``). EXAMPLES:: sage: from graded_ring import ModularFormsRing sage: ModularFormsRing().has_reduce_hom() False sage: ModularFormsRing(red_hom=True).has_reduce_hom() True sage: from space import ModularForms sage: ModularForms(k=6).has_reduce_hom() True sage: ModularForms(k=6).graded_ring().has_reduce_hom() True """ return self._red_hom def is_homogeneous(self): r""" Return whether ``self`` is homogeneous component. EXAMPLES:: sage: from graded_ring import ModularFormsRing sage: ModularFormsRing().is_homogeneous() False sage: from space import ModularForms sage: ModularForms(k=6).is_homogeneous() True """ return self._weight != None def is_modular(self): r""" Return whether ``self`` only contains modular elements. EXAMPLES:: sage: from graded_ring import QWeakModularFormsRing, CuspFormsRing sage: QWeakModularFormsRing().is_modular() False sage: CuspFormsRing(group=7).is_modular() True sage: from space import QWeakModularForms, CuspForms sage: QWeakModularForms(k=10).is_modular() False sage: CuspForms(group=7, k=12, base_ring=AA).is_modular() True """ return not (self.AT("quasi") <= self._analytic_type) def is_weakly_holomorphic(self): r""" Return whether ``self`` only contains weakly holomorphic modular elements. EXAMPLES:: sage: from graded_ring import QMModularFormsRing, QWeakModularFormsRing, CuspFormsRing sage: QMModularFormsRing().is_weakly_holomorphic() False sage: QWeakModularFormsRing().is_weakly_holomorphic() True sage: from space import MModularForms, CuspForms sage: MModularForms(k=10).is_weakly_holomorphic() False sage: CuspForms(group=7, k=12, base_ring=AA).is_weakly_holomorphic() True """ return (self.AT("weak", "quasi") >= self._analytic_type) def is_holomorphic(self): r""" Return whether ``self`` only contains holomorphic modular elements. EXAMPLES:: sage: from graded_ring import QWeakModularFormsRing, QModularFormsRing sage: QWeakModularFormsRing().is_holomorphic() False sage: QModularFormsRing().is_holomorphic() True sage: from space import WeakModularForms, CuspForms sage: WeakModularForms(k=10).is_holomorphic() False sage: CuspForms(group=7, k=12, base_ring=AA).is_holomorphic() True """ return (self.AT("holo", "quasi") >= self._analytic_type) def is_cuspidal(self): r""" Return whether ``self`` only contains cuspidal elements. EXAMPLES:: sage: from graded_ring import QModularFormsRing, QCuspFormsRing sage: QModularFormsRing().is_cuspidal() False sage: QCuspFormsRing().is_cuspidal() True sage: from space import ModularForms, QCuspForms sage: ModularForms(k=12).is_cuspidal() False sage: QCuspForms(k=12).is_cuspidal() True """ return (self.AT("cusp", "quasi") >= self._analytic_type) def is_zerospace(self): r""" Return whether ``self`` is the (0-dimensional) zero space. EXAMPLES:: sage: from graded_ring import ModularFormsRing sage: ModularFormsRing().is_zerospace() False sage: from space import ModularForms, CuspForms sage: ModularForms(k=12).is_zerospace() False sage: CuspForms(k=12).reduce_type([]).is_zerospace() True """ return (self.AT(["quasi"]) >= self._analytic_type) def analytic_type(self): r""" Return the analytic type of ``self``. EXAMPLES:: sage: from graded_ring import QMModularFormsRing, QWeakModularFormsRing sage: QMModularFormsRing().analytic_type() quasi meromorphic modular sage: QWeakModularFormsRing().analytic_type() quasi weakly holomorphic modular sage: from space import MModularForms, CuspForms sage: MModularForms(k=10).analytic_type() meromorphic modular sage: CuspForms(group=7, k=12, base_ring=AA).analytic_type() cuspidal """ return self._analytic_type def homogeneous_space(self, k, ep): r""" Return the homogeneous component of degree (``k``, ``e``) of ``self``. EXAMPLES:: sage: from graded_ring import QMModularFormsRing, QWeakModularFormsRing sage: QMModularFormsRing(group=7).homogeneous_space(k=2, ep=-1) QuasiMeromorphicModularForms(n=7, k=2, ep=-1) over Integer Ring """ return self.reduce_type(degree = (k,ep)) @cached_method def J_inv(self): r""" Return the J-invariant (Hauptmodul) of the group of ``self``. It is normalized such that ``J_inv(infinity) = infinity``, it has real Fourier coefficients starting with ``d > 0`` and ``J_inv(i) = 1`` It lies in a (weak) extension of the graded ring of ``self``. In case ``has_reduce_hom`` is ``True`` it is given as an element of the corresponding homogeneous space. EXAMPLES:: sage: from graded_ring import QMModularFormsRing, WeakModularFormsRing, CuspFormsRing sage: MR = WeakModularFormsRing(group=7) sage: J_inv = MR.J_inv() sage: J_inv in MR True sage: CuspFormsRing(group=7).J_inv() == J_inv True sage: J_inv f_rho^7/(f_rho^7 - f_i^2) sage: QMModularFormsRing(group=7).J_inv() == QMModularFormsRing(group=7)(J_inv) True sage: from space import WeakModularForms, CuspForms sage: MF = WeakModularForms(group=5, k=0) sage: J_inv = MF.J_inv() sage: J_inv in MF True sage: WeakModularFormsRing(group=5, red_hom=True).J_inv() == J_inv True sage: CuspForms(group=5, k=12).J_inv() == J_inv True sage: MF.disp_prec(3) sage: J_inv d*q^-1 + 79/200 + 42877/(640000*d)*q + 12957/(2000000*d^2)*q^2 + O(q^3) sage: WeakModularForms().J_inv() 1/1728*q^-1 + 31/72 + 1823/16*q + 335840/27*q^2 + 16005555/32*q^3 + 11716352*q^4 + O(q^5) """ (x,y,z,d) = self._pol_ring.gens() return self.extend_type("weak", ring=True)(x**self._group.n()/(x**self._group.n()-y**2)).reduce() @cached_method def j_inv(self): r""" Return the j-invariant (Hauptmodul) of the group of ``self``. It is normalized such that ``j_inv(infinity) = infinity``, and such that it has real Fourier coefficients starting with ``1``. It lies in a (weak) extension of the graded ring of ``self``. In case ``has_reduce_hom`` is ``True`` it is given as an element of the corresponding homogeneous space. EXAMPLES:: sage: from graded_ring import QMModularFormsRing, WeakModularFormsRing, CuspFormsRing sage: MR = WeakModularFormsRing(group=7) sage: j_inv = MR.j_inv() sage: j_inv in MR True sage: CuspFormsRing(group=7).j_inv() == j_inv True sage: j_inv f_rho^7/(f_rho^7*d - f_i^2*d) sage: QMModularFormsRing(group=7).j_inv() == QMModularFormsRing(group=7)(j_inv) True sage: from space import WeakModularForms, CuspForms sage: MF = WeakModularForms(group=5, k=0) sage: j_inv = MF.j_inv() sage: j_inv in MF True sage: WeakModularFormsRing(group=5, red_hom=True).j_inv() == j_inv True sage: CuspForms(group=5, k=12).j_inv() == j_inv True sage: MF.disp_prec(3) sage: j_inv q^-1 + 79/(200*d) + 42877/(640000*d^2)*q + 12957/(2000000*d^3)*q^2 + O(q^3) sage: WeakModularForms().j_inv() q^-1 + 744 + 196884*q + 21493760*q^2 + 864299970*q^3 + 20245856256*q^4 + O(q^5) """ (x,y,z,d) = self._pol_ring.gens() return self.extend_type("weak", ring=True)(1/d*x**self._group.n()/(x**self._group.n()-y**2)).reduce() @cached_method def F_rho(self): r""" Return the generator ``F_rho`` of the graded ring of ``self``. Up to the group action ``F_rho`` has exactly one simple zero at ``rho``. ``F_rho`` is normalized such that its first nontrivial Fourier coefficient is ``1``. The polynomial variable ``x`` exactly corresponds to ``F_rho``. It lies in a (cuspidal) extension of the graded ring of ``self``. In case ``has_reduce_hom`` is ``True`` it is given as an element of the corresponding homogeneous space. EXAMPLES:: sage: from graded_ring import QMModularFormsRing, ModularFormsRing, CuspFormsRing sage: MR = ModularFormsRing(group=7) sage: F_rho = MR.F_rho() sage: F_rho in MR True sage: CuspFormsRing(group=7).F_rho() == F_rho True sage: F_rho f_rho sage: QMModularFormsRing(group=7).F_rho() == QMModularFormsRing(group=7)(F_rho) True sage: from space import ModularForms, CuspForms sage: MF = ModularForms(group=5, k=4/3) sage: F_rho = MF.F_rho() sage: F_rho in MF True sage: ModularFormsRing(group=5, red_hom=True).F_rho() == F_rho True sage: CuspForms(group=5, k=12).F_rho() == F_rho True sage: MF.disp_prec(3) sage: F_rho 1 + 7/(100*d)*q + 21/(160000*d^2)*q^2 + O(q^3) sage: ModularForms(k=4).F_rho() == ModularForms(k=4).E4() True sage: ModularForms(k=4).F_rho() 1 + 240*q + 2160*q^2 + 6720*q^3 + 17520*q^4 + O(q^5) """ (x,y,z,d) = self._pol_ring.gens() return self.extend_type("holo", ring=True)(x).reduce() @cached_method def F_i(self): r""" Return the generator ``F_i`` of the graded ring of ``self``. Up to the group action ``F_i`` has exactly one simple zero at ``i``. ``F_i`` is normalized such that its first nontrivial Fourier coefficient is ``1``. The polynomial variable ``y`` exactly corresponds to ``F_i``. It lies in a (holomorphic) extension of the graded ring of ``self``. In case ``has_reduce_hom`` is ``True`` it is given as an element of the corresponding homogeneous space. EXAMPLES:: sage: from graded_ring import QMModularFormsRing, ModularFormsRing, CuspFormsRing sage: MR = ModularFormsRing(group=7) sage: F_i = MR.F_i() sage: F_i in MR True sage: CuspFormsRing(group=7).F_i() == F_i True sage: F_i f_i sage: QMModularFormsRing(group=7).F_i() == QMModularFormsRing(group=7)(F_i) True sage: from space import ModularForms, CuspForms sage: MF = ModularForms(group=5, k=10/3) sage: F_i = MF.F_i() sage: F_i in MF True sage: ModularFormsRing(group=5, red_hom=True).F_i() == F_i True sage: CuspForms(group=5, k=12).F_i() == F_i True sage: MF.disp_prec(3) sage: F_i 1 - 13/(40*d)*q - 351/(64000*d^2)*q^2 + O(q^3) sage: ModularForms(k=6).F_i() == ModularForms(k=4).E6() True sage: ModularForms(k=6).F_i() 1 - 504*q - 16632*q^2 - 122976*q^3 - 532728*q^4 + O(q^5) """ (x,y,z,d) = self._pol_ring.gens() return self.extend_type("holo", ring=True)(y).reduce() @cached_method def F_inf(self): r""" Return the first nontrivial cusp form ``F_inf`` of the graded ring of ``self``. Up to the group action ``F_inf`` has exactly one simple zero at ``infinity``. ``F_inf`` is normalized such that its first nontrivial Fourier coefficient is ``1``. It lies in a (holomorphic) extension of the graded ring of ``self``. In case ``has_reduce_hom`` is ``True`` it is given as an element of the corresponding homogeneous space. EXAMPLES:: sage: from graded_ring import QMModularFormsRing, CuspFormsRing sage: MR = CuspFormsRing(group=7) sage: F_inf = MR.F_inf() sage: F_inf in MR True sage: F_inf f_rho^7*d - f_i^2*d sage: QMModularFormsRing(group=7).F_inf() == QMModularFormsRing(group=7)(F_inf) True sage: from space import CuspForms sage: MF = CuspForms(group=5, k=20/3) sage: F_inf = MF.F_inf() sage: F_inf in MF True sage: CuspFormsRing(group=5, red_hom=True).F_inf() == F_inf True sage: CuspForms(group=5, k=0).F_inf() == F_inf True sage: MF.disp_prec(3) sage: F_inf q - 9/(200*d)*q^2 + O(q^3) sage: CuspForms(k=12).F_inf() == CuspForms(k=12).Delta() True sage: CuspForms(k=12).F_inf() q - 24*q^2 + 252*q^3 - 1472*q^4 + O(q^5) """ (x,y,z,d) = self._pol_ring.gens() return self.extend_type("cusp", ring=True)(d*(x**self._group.n()-y**2)).reduce() @cached_method def G_inv(self): r""" If ``2`` divides ``n``: Return the G-invariant of the group of ``self``. The G-invariant is analogous to the G-invariant but has multiplier ``-1``. I.e. ``G_inv(-1/t) = -G_inv(t)``. It is a holomorphic square root of ``J_inv*(J_inv-1)`` with real Fourier coefficients. If ``2`` does not divide ``n`` the function doesn't exist and an exception is raised. It lies in a (weak) extension of the graded ring of ``self``. In case ``has_reduce_hom`` is ``True`` it is given as an element of the corresponding homogeneous space. EXAMPLES:: sage: from graded_ring import QMModularFormsRing, WeakModularFormsRing, CuspFormsRing sage: MR = WeakModularFormsRing(group=8) sage: G_inv = MR.G_inv() sage: G_inv in MR True sage: CuspFormsRing(group=8).G_inv() == G_inv True sage: G_inv f_rho^4*f_i*d/(f_rho^8 - f_i^2) sage: QMModularFormsRing(group=8).G_inv() == QMModularFormsRing(group=8)(G_inv) True sage: from space import WeakModularForms, CuspForms sage: MF = WeakModularForms(group=8, k=0, ep=-1) sage: G_inv = MF.G_inv() sage: G_inv in MF True sage: WeakModularFormsRing(group=8, red_hom=True).G_inv() == G_inv True sage: CuspForms(group=8, k=12, ep=1).G_inv() == G_inv True sage: MF.disp_prec(3) sage: G_inv d^2*q^-1 - 15*d/128 - 15139/262144*q - 11575/(1572864*d)*q^2 + O(q^3) sage: WeakModularForms(group=4, k=0, ep=-1).G_inv() 1/65536*q^-1 - 3/8192 - 955/16384*q - 49/32*q^2 - 608799/32768*q^3 - 659/4*q^4 + O(q^5) """ if (ZZ(2).divides(self._group.n())): (x,y,z,d) = self._pol_ring.gens() return self.extend_type("weak", ring=True)(d*y*x**(self._group.n()/ZZ(2))/(x**self._group.n()-y**2)).reduce() else: raise Exception("G_inv doesn't exists for n={}.".format(self._group.n())) @cached_method def g_inv(self): r""" If ``2`` divides ``n``: Return the g-invariant of the group of ``self``. The g-invariant is analogous to the j-invariant but has multiplier ``-1``. I.e. ``g_inv(-1/t) = -g_inv(t)``. It is a (normalized) holomorphic square root of ``J_inv*(J_inv-1)``, normalized such that its first nontrivial Fourier coefficient is ``1``. If ``2`` does not divide ``n`` the function doesn't exist and an exception is raised. It lies in a (weak) extension of the graded ring of ``self``. In case ``has_reduce_hom`` is ``True`` it is given as an element of the corresponding homogeneous space. EXAMPLES:: sage: from graded_ring import QMModularFormsRing, WeakModularFormsRing, CuspFormsRing sage: MR = WeakModularFormsRing(group=8) sage: g_inv = MR.g_inv() sage: g_inv in MR True sage: CuspFormsRing(group=8).g_inv() == g_inv True sage: g_inv f_rho^4*f_i/(f_rho^8*d - f_i^2*d) sage: QMModularFormsRing(group=8).g_inv() == QMModularFormsRing(group=8)(g_inv) True sage: from space import WeakModularForms, CuspForms sage: MF = WeakModularForms(group=8, k=0, ep=-1) sage: g_inv = MF.g_inv() sage: g_inv in MF True sage: WeakModularFormsRing(group=8, red_hom=True).g_inv() == g_inv True sage: CuspForms(group=8, k=12, ep=1).g_inv() == g_inv True sage: MF.disp_prec(3) sage: g_inv q^-1 - 15/(128*d) - 15139/(262144*d^2)*q - 11575/(1572864*d^3)*q^2 + O(q^3) sage: WeakModularForms(group=4, k=0, ep=-1).g_inv() q^-1 - 24 - 3820*q - 100352*q^2 - 1217598*q^3 - 10797056*q^4 + O(q^5) """ if (ZZ(2).divides(self._group.n())): (x,y,z,d) = self._pol_ring.gens() return self.extend_type("weak", ring=True)(1/d*y*x**(self._group.n()/ZZ(2))/(x**self._group.n()-y**2)).reduce() else: raise Exception("g_inv doesn't exists for n={}.".format(self._group.n())) @cached_method def E4(self): r""" Return the normalized Eisenstein series of weight ``4`` of the graded ring of ``self``. It is equal to ``F_rho^(n-2)``. It lies in a (holomorphic) extension of the graded ring of ``self``. In case ``has_reduce_hom`` is ``True`` it is given as an element of the corresponding homogeneous space. EXAMPLES:: sage: from graded_ring import QMModularFormsRing, ModularFormsRing, CuspFormsRing sage: MR = ModularFormsRing(group=7) sage: E4 = MR.E4() sage: E4 in MR True sage: CuspFormsRing(group=7).E4() == E4 True sage: E4 f_rho^5 sage: QMModularFormsRing(group=7).E4() == QMModularFormsRing(group=7)(E4) True sage: from space import ModularForms, CuspForms sage: MF = ModularForms(group=5, k=4) sage: E4 = MF.E4() sage: E4 in MF True sage: ModularFormsRing(group=5, red_hom=True).E4() == E4 True sage: CuspForms(group=5, k=12).E4() == E4 True sage: MF.disp_prec(3) sage: E4 1 + 21/(100*d)*q + 483/(32000*d^2)*q^2 + O(q^3) sage: ModularForms(k=4).F_rho() == ModularForms(k=4).E4() True sage: ModularForms(k=4).E4() 1 + 240*q + 2160*q^2 + 6720*q^3 + 17520*q^4 + O(q^5) """ (x,y,z,d) = self._pol_ring.gens() return self.extend_type("holo", ring=True)(x**(self._group.n()-2)).reduce() @cached_method def E6(self): r""" Return the normalized Eisenstein series of weight ``6`` of the graded ring of ``self``, It is equal to ``F_rho^(n-3) * F_i``. It lies in a (holomorphic) extension of the graded ring of ``self``. In case ``has_reduce_hom`` is ``True`` it is given as an element of the corresponding homogeneous space. EXAMPLES:: sage: from graded_ring import QMModularFormsRing, ModularFormsRing, CuspFormsRing sage: MR = ModularFormsRing(group=7) sage: E6 = MR.E6() sage: E6 in MR True sage: CuspFormsRing(group=7).E6() == E6 True sage: E6 f_rho^4*f_i sage: QMModularFormsRing(group=7).E6() == QMModularFormsRing(group=7)(E6) True sage: from space import ModularForms, CuspForms sage: MF = ModularForms(group=5, k=6) sage: E6 = MF.E6() sage: E6 in MF True sage: ModularFormsRing(group=5, red_hom=True).E6() == E6 True sage: CuspForms(group=5, k=12).E6() == E6 True sage: MF.disp_prec(3) sage: E6 1 - 37/(200*d)*q - 14663/(320000*d^2)*q^2 + O(q^3) sage: ModularForms(k=6).F_i() == ModularForms(k=6).E6() True sage: ModularForms(k=6).E6() 1 - 504*q - 16632*q^2 - 122976*q^3 - 532728*q^4 + O(q^5) """ (x,y,z,d) = self._pol_ring.gens() return self.extend_type("holo", ring=True)(x**(self._group.n()-3)*y).reduce() @cached_method def Delta(self): r""" Return an analog of the Delta-function of the graded ring of ``self``. It is a cusp form of weight ``12`` and is equal to ``d*(E4^3 - E6^2)`` or (in terms of the generators) ``d*x^(2*n-6)*(x^n - y^2)``. It lies in a (cuspidal) extension of the graded ring of ``self``. In case ``has_reduce_hom`` is ``True`` it is given as an element of the corresponding homogeneous space. EXAMPLES:: sage: from graded_ring import QMModularFormsRing, CuspFormsRing sage: MR = CuspFormsRing(group=7) sage: Delta = MR.Delta() sage: Delta in MR True sage: Delta f_rho^15*d - f_rho^8*f_i^2*d sage: QMModularFormsRing(group=7).Delta() == QMModularFormsRing(group=7)(Delta) True sage: from space import CuspForms, ModularForms sage: MF = CuspForms(group=5, k=12) sage: Delta = MF.Delta() sage: Delta in MF True sage: CuspFormsRing(group=5, red_hom=True).Delta() == Delta True sage: CuspForms(group=5, k=0).Delta() == Delta True sage: MF.disp_prec(3) sage: Delta q + 47/(200*d)*q^2 + O(q^3) sage: d = ModularForms(group=5).coeff_ring().gen() sage: Delta == (d*(ModularForms(group=5).E4()^3-ModularForms(group=5).E6()^2)) True sage: CuspForms(k=12).F_inf() == CuspForms(k=12).Delta() True sage: CuspForms(k=12).Delta() q - 24*q^2 + 252*q^3 - 1472*q^4 + O(q^5) """ (x,y,z,d) = self._pol_ring.gens() return self.extend_type("cusp", ring=True)(d*x**(2*self._group.n()-6)*(x**self._group.n()-y**2)).reduce() @cached_method def E2(self): r""" Return the normalized quasi holomorphic Eisenstein series of weight ``2`` of the graded ring of ``self``. It is also a generator of the graded ring of ``self`` and the polynomial variable ``z`` exactly corresponds to ``E2``. It lies in a (quasi holomorphic) extension of the graded ring of ``self``. In case ``has_reduce_hom`` is ``True`` it is given as an element of the corresponding homogeneous space. EXAMPLES:: sage: from graded_ring import QMModularFormsRing, QModularFormsRing, CuspFormsRing sage: MR = QModularFormsRing(group=7) sage: E2 = MR.E2() sage: E2 in MR True sage: CuspFormsRing(group=7).E2() == E2 True sage: E2 E2 sage: QMModularFormsRing(group=7).E2() == QMModularFormsRing(group=7)(E2) True sage: from space import QModularForms, CuspForms sage: MF = QModularForms(group=5, k=2) sage: E2 = MF.E2() sage: E2 in MF True sage: QModularFormsRing(group=5, red_hom=True).E2() == E2 True sage: CuspForms(group=5, k=12, ep=1).E2() == E2 True sage: MF.disp_prec(3) sage: E2 1 - 9/(200*d)*q - 369/(320000*d^2)*q^2 + O(q^3) sage: F_inf = MF.F_inf() sage: E2 == F_inf.derivative() / F_inf True sage: QModularForms(k=2).E2() 1 - 24*q - 72*q^2 - 96*q^3 - 168*q^4 + O(q^5) """ (x,y,z,d) = self._pol_ring.gens() return self.extend_type(["holo", "quasi"], ring=True)(z).reduce()
def segre_embedding(self, PP=None, var='u'): r""" Return the Segre embedding of this space into the appropriate projective space. INPUT: - ``PP`` -- (default: ``None``) ambient image projective space; this is constructed if it is not given. - ``var`` -- string, variable name of the image projective space, default `u` (optional). OUTPUT: Hom -- from this space to the appropriate subscheme of projective space. .. TODO:: Cartesian products with more than two components. EXAMPLES:: sage: X.<y0,y1,y2,y3,y4,y5> = ProductProjectiveSpaces(ZZ, [2, 2]) sage: phi = X.segre_embedding(); phi Scheme morphism: From: Product of projective spaces P^2 x P^2 over Integer Ring To: Closed subscheme of Projective Space of dimension 8 over Integer Ring defined by: -u5*u7 + u4*u8, -u5*u6 + u3*u8, -u4*u6 + u3*u7, -u2*u7 + u1*u8, -u2*u4 + u1*u5, -u2*u6 + u0*u8, -u1*u6 + u0*u7, -u2*u3 + u0*u5, -u1*u3 + u0*u4 Defn: Defined by sending (y0 : y1 : y2 , y3 : y4 : y5) to (y0*y3 : y0*y4 : y0*y5 : y1*y3 : y1*y4 : y1*y5 : y2*y3 : y2*y4 : y2*y5). :: sage: T = ProductProjectiveSpaces([1, 2], CC, 'z') sage: T.segre_embedding() Scheme morphism: From: Product of projective spaces P^1 x P^2 over Complex Field with 53 bits of precision To: Closed subscheme of Projective Space of dimension 5 over Complex Field with 53 bits of precision defined by: -u2*u4 + u1*u5, -u2*u3 + u0*u5, -u1*u3 + u0*u4 Defn: Defined by sending (z0 : z1 , z2 : z3 : z4) to (z0*z2 : z0*z3 : z0*z4 : z1*z2 : z1*z3 : z1*z4). :: sage: T = ProductProjectiveSpaces([1, 2, 1], QQ, 'z') sage: T.segre_embedding() Scheme morphism: From: Product of projective spaces P^1 x P^2 x P^1 over Rational Field To: Closed subscheme of Projective Space of dimension 11 over Rational Field defined by: -u9*u10 + u8*u11, -u7*u10 + u6*u11, -u7*u8 + u6*u9, -u5*u10 + u4*u11, -u5*u8 + u4*u9, -u5*u6 + u4*u7, -u5*u9 + u3*u11, -u5*u8 + u3*u10, -u5*u8 + u2*u11, -u4*u8 + u2*u10, -u3*u8 + u2*u9, -u3*u6 + u2*u7, -u3*u4 + u2*u5, -u5*u7 + u1*u11, -u5*u6 + u1*u10, -u3*u7 + u1*u9, -u3*u6 + u1*u8, -u5*u6 + u0*u11, -u4*u6 + u0*u10, -u3*u6 + u0*u9, -u2*u6 + u0*u8, -u1*u6 + u0*u7, -u1*u4 + u0*u5, -u1*u2 + u0*u3 Defn: Defined by sending (z0 : z1 , z2 : z3 : z4 , z5 : z6) to (z0*z2*z5 : z0*z2*z6 : z0*z3*z5 : z0*z3*z6 : z0*z4*z5 : z0*z4*z6 : z1*z2*z5 : z1*z2*z6 : z1*z3*z5 : z1*z3*z6 : z1*z4*z5 : z1*z4*z6). """ N = self._dims M = prod([n + 1 for n in N]) - 1 CR = self.coordinate_ring() vars = list(self.coordinate_ring().variable_names()) + [ var + str(i) for i in range(M + 1) ] R = PolynomialRing(self.base_ring(), self.ngens() + M + 1, vars, order='lex') #set-up the elimination for the segre embedding mapping = [] k = self.ngens() index = self.num_components() * [0] for count in range(M + 1): mapping.append( R.gen(k + count) - prod([CR(self[i].gen(index[i])) for i in range(len(index))])) for i in range(len(index) - 1, -1, -1): if index[i] == N[i]: index[i] = 0 else: index[i] += 1 break #only increment once #change the defining ideal of the subscheme into the variables I = R.ideal(list(self.defining_polynomials()) + mapping) J = I.groebner_basis() s = set(R.gens()[:self.ngens()]) n = len(J) - 1 L = [] while s.isdisjoint(J[n].variables()): L.append(J[n]) n = n - 1 #create new subscheme if PP is None: PS = ProjectiveSpace(self.base_ring(), M, R.variable_names()[self.ngens():]) Y = PS.subscheme(L) else: if PP.dimension_relative() != M: raise ValueError( "projective Space %s must be dimension %s") % (PP, M) S = PP.coordinate_ring() psi = R.hom([0] * k + list(S.gens()), S) L = [psi(l) for l in L] Y = PP.subscheme(L) #create embedding for points mapping = [] index = self.num_components() * [0] for count in range(M + 1): mapping.append( prod([CR(self[i].gen(index[i])) for i in range(len(index))])) for i in range(len(index) - 1, -1, -1): if index[i] == N[i]: index[i] = 0 else: index[i] += 1 break #only increment once phi = self.hom(mapping, Y) return phi
def has_rational_point(self, point = False, read_cache = True, \ algorithm = 'default'): r""" Always returns ``True`` because self has a point defined over its finite base field `B`. If ``point`` is True, then returns a second output `S`, which is a rational point if one exists. Points are cached. If ``read_cache`` is True, then cached information is used for the output if available. If no cached point is available or ``read_cache`` is False, then random `y`-coordinates are tried if ``self`` is smooth and a singular point is returned otherwise. EXAMPLES:: sage: Conic(FiniteField(37), [1, 2, 3, 4, 5, 6]).has_rational_point() True sage: C = Conic(FiniteField(2), [1, 1, 1, 1, 1, 0]); C Projective Conic Curve over Finite Field of size 2 defined by x^2 + x*y + y^2 + x*z + y*z sage: C.has_rational_point(point = True) # output is random (True, (0 : 0 : 1)) sage: p = next_prime(10^50) sage: F = FiniteField(p) sage: C = Conic(F, [1, 2, 3]); C Projective Conic Curve over Finite Field of size 100000000000000000000000000000000000000000000000151 defined by x^2 + 2*y^2 + 3*z^2 sage: C.has_rational_point(point = True) # output is random (True, (14971942941468509742682168602989039212496867586852 : 75235465708017792892762202088174741054630437326388 : 1) sage: F.<a> = FiniteField(7^20) sage: C = Conic([1, a, -5]); C Projective Conic Curve over Finite Field in a of size 7^20 defined by x^2 + (a)*y^2 + 2*z^2 sage: C.has_rational_point(point = True) # output is random (True, (a^18 + 2*a^17 + 4*a^16 + 6*a^13 + a^12 + 6*a^11 + 3*a^10 + 4*a^9 + 2*a^8 + 4*a^7 + a^6 + 4*a^4 + 6*a^2 + 3*a + 6 : 5*a^19 + 5*a^18 + 5*a^17 + a^16 + 2*a^15 + 3*a^14 + 4*a^13 + 5*a^12 + a^11 + 3*a^10 + 2*a^8 + 3*a^7 + 4*a^6 + 4*a^5 + 6*a^3 + 5*a^2 + 2*a + 4 : 1)) TESTS:: sage: l = Sequence(cartesian_product_iterator([[0, 1] for i in range(6)])) sage: bigF = GF(next_prime(2^100)) sage: bigF2 = GF(next_prime(2^50)^2, 'b') sage: m = [[F(b) for b in a] for a in l for F in [GF(2), GF(4, 'a'), GF(5), GF(9, 'a'), bigF, bigF2]] sage: m += [[F.random_element() for i in range(6)] for j in range(20) for F in [GF(5), bigF]] sage: c = [Conic(a) for a in m if a != [0,0,0,0,0,0]] sage: assert all(C.has_rational_point() for C in c) sage: r = randrange(0, 5) sage: assert all(C.defining_polynomial()(Sequence(C.has_rational_point(point = True)[1])) == 0 for C in c[r::5]) # long time (1.4s on sage.math, 2013) """ if not point: return True if read_cache: if self._rational_point is not None: return True, self._rational_point B = self.base_ring() s, pt = self.has_singular_point(point = True) if s: return True, pt while True: x = B.random_element() Y = PolynomialRing(B,'Y').gen() r = self.defining_polynomial()([x,Y,1]).roots() if len(r) > 0: return True, self.point([x,r[0][0],B(1)])
def __call__(self, Q, P): """ Compute and return the :class:`ReductionData` corresponding to the genus 2 curve `y^2 + Q(x) y = P(x)`. EXAMPLES:: sage: x = polygen(QQ) sage: genus2reduction(x^3 - 2*x^2 - 2*x + 1, -5*x^5) Reduction data about this proper smooth genus 2 curve: y^2 + (x^3 - 2*x^2 - 2*x + 1)*y = -5*x^5 A Minimal Equation (away from 2): y^2 = x^6 - 240*x^4 - 2550*x^3 - 11400*x^2 - 24100*x - 19855 Minimal Discriminant (away from 2): 56675000 Conductor (away from 2): 1416875 Local Data: p=2 (potential) stable reduction: (II), j=1 p=5 (potential) stable reduction: (I) reduction at p: [V] page 156, (3), f=4 p=2267 (potential) stable reduction: (II), j=432 reduction at p: [I{1-0-0}] page 170, (1), f=1 :: sage: genus2reduction(x^2 + 1, -5*x^5) Reduction data about this proper smooth genus 2 curve: y^2 + (x^2 + 1)*y = -5*x^5 A Minimal Equation (away from 2): y^2 = -20*x^5 + x^4 + 2*x^2 + 1 Minimal Discriminant (away from 2): 48838125 Conductor: 32025 Local Data: p=3 (potential) stable reduction: (II), j=1 reduction at p: [I{1-0-0}] page 170, (1), f=1 p=5 (potential) stable reduction: (IV) reduction at p: [I{1-1-2}] page 182, (5), f=2 p=7 (potential) stable reduction: (II), j=4 reduction at p: [I{1-0-0}] page 170, (1), f=1 p=61 (potential) stable reduction: (II), j=57 reduction at p: [I{2-0-0}] page 170, (2), f=1 Verify that we fix :trac:`5573`:: sage: genus2reduction(x^3 + x^2 + x,-2*x^5 + 3*x^4 - x^3 - x^2 - 6*x - 2) Reduction data about this proper smooth genus 2 curve: y^2 + (x^3 + x^2 + x)*y = -2*x^5 + 3*x^4 - x^3 - x^2 - 6*x - 2 A Minimal Equation (away from 2): y^2 = x^6 + 18*x^3 + 36*x^2 - 27 Minimal Discriminant (away from 2): 1520984142 Conductor: 954 Local Data: p=2 (potential) stable reduction: (II), j=1 reduction at p: [I{1-0-0}] page 170, (1), f=1 p=3 (potential) stable reduction: (I) reduction at p: [II] page 155, (1), f=2 p=53 (potential) stable reduction: (II), j=12 reduction at p: [I{1-0-0}] page 170, (1), f=1 """ R = PolynomialRing(QQ, 'x') P = R(P) Q = R(Q) if P.degree() > 6: raise ValueError("P (=%s) must have degree at most 6" % P) if Q.degree() >= 4: raise ValueError("Q (=%s) must have degree at most 3" % Q) res = pari.genus2red([P, Q]) conductor = ZZ(res[0]) minimal_equation = R(res[2]) minimal_disc = QQ(res[2].poldisc()).abs() if minimal_equation.degree() == 5: minimal_disc *= minimal_equation[5]**2 # Multiply with suitable power of 2 of the form 2^(2*(d-1) - 12) b = 2 * (minimal_equation.degree() - 1) k = QQ((12 - minimal_disc.valuation(2), b)).ceil() minimal_disc >>= 12 - b * k minimal_disc = ZZ(minimal_disc) local_data = {} for red in res[3]: p = ZZ(red[0]) t = red[1] data = "(potential) stable reduction: (%s)" % roman_numeral[int( t[0])] t = t[1] if len(t) == 1: data += ", j=%s" % t[0].lift() elif len(t) == 2: data += ", j1+j2=%s, j1*j2=%s" % (t[0].lift(), t[1].lift()) t = red[2] if t: data += "\nreduction at p: %s, " % str(t[0]).replace( '"', '').replace("(tame) ", "") data += divisors_to_string(t[1]) + ", f=" + str( res[0].valuation(red[0])) local_data[p] = data prime_to_2_conductor_only = (-1 in res[1].component(2)) return ReductionData(res, P, Q, minimal_equation, minimal_disc, local_data, conductor, prime_to_2_conductor_only)
def Conic(base_field, F=None, names=None, unique=True): r""" Return the plane projective conic curve defined by ``F`` over ``base_field``. The input form ``Conic(F, names=None)`` is also accepted, in which case the fraction field of the base ring of ``F`` is used as base field. INPUT: - ``base_field`` -- The base field of the conic. - ``names`` -- a list, tuple, or comma separated string of three variable names specifying the names of the coordinate functions of the ambient space `\Bold{P}^3`. If not specified or read off from ``F``, then this defaults to ``'x,y,z'``. - ``F`` -- a polynomial, list, matrix, ternary quadratic form, or list or tuple of 5 points in the plane. If ``F`` is a polynomial or quadratic form, then the output is the curve in the projective plane defined by ``F = 0``. If ``F`` is a polynomial, then it must be a polynomial of degree at most 2 in 2 variables, or a homogeneous polynomial in of degree 2 in 3 variables. If ``F`` is a matrix, then the output is the zero locus of `(x,y,z) F (x,y,z)^t`. If ``F`` is a list of coefficients, then it has length 3 or 6 and gives the coefficients of the monomials `x^2, y^2, z^2` or all 6 monomials `x^2, xy, xz, y^2, yz, z^2` in lexicographic order. If ``F`` is a list of 5 points in the plane, then the output is a conic through those points. - ``unique`` -- Used only if ``F`` is a list of points in the plane. If the conic through the points is not unique, then raise ``ValueError`` if and only if ``unique`` is True OUTPUT: A plane projective conic curve defined by ``F`` over a field. EXAMPLES: Conic curves given by polynomials :: sage: X,Y,Z = QQ['X,Y,Z'].gens() sage: Conic(X^2 - X*Y + Y^2 - Z^2) Projective Conic Curve over Rational Field defined by X^2 - X*Y + Y^2 - Z^2 sage: x,y = GF(7)['x,y'].gens() sage: Conic(x^2 - x + 2*y^2 - 3, 'U,V,W') Projective Conic Curve over Finite Field of size 7 defined by U^2 + 2*V^2 - U*W - 3*W^2 Conic curves given by matrices :: sage: Conic(matrix(QQ, [[1, 2, 0], [4, 0, 0], [7, 0, 9]]), 'x,y,z') Projective Conic Curve over Rational Field defined by x^2 + 6*x*y + 7*x*z + 9*z^2 sage: x,y,z = GF(11)['x,y,z'].gens() sage: C = Conic(x^2+y^2-2*z^2); C Projective Conic Curve over Finite Field of size 11 defined by x^2 + y^2 - 2*z^2 sage: Conic(C.symmetric_matrix(), 'x,y,z') Projective Conic Curve over Finite Field of size 11 defined by x^2 + y^2 - 2*z^2 Conics given by coefficients :: sage: Conic(QQ, [1,2,3]) Projective Conic Curve over Rational Field defined by x^2 + 2*y^2 + 3*z^2 sage: Conic(GF(7), [1,2,3,4,5,6], 'X') Projective Conic Curve over Finite Field of size 7 defined by X0^2 + 2*X0*X1 - 3*X1^2 + 3*X0*X2 - 2*X1*X2 - X2^2 The conic through a set of points :: sage: C = Conic(QQ, [[10,2],[3,4],[-7,6],[7,8],[9,10]]); C Projective Conic Curve over Rational Field defined by x^2 + 13/4*x*y - 17/4*y^2 - 35/2*x*z + 91/4*y*z - 37/2*z^2 sage: C.rational_point() (10 : 2 : 1) sage: C.point([3,4]) (3 : 4 : 1) sage: a=AffineSpace(GF(13),2) sage: Conic([a([x,x^2]) for x in range(5)]) Projective Conic Curve over Finite Field of size 13 defined by x^2 - y*z """ if not (is_IntegralDomain(base_field) or base_field == None): if names is None: names = F F = base_field base_field = None if isinstance(F, (list,tuple)): if len(F) == 1: return Conic(base_field, F[0], names) if names == None: names = 'x,y,z' if len(F) == 5: L=[] for f in F: if isinstance(f, SchemeMorphism_point_affine): C = Sequence(f, universe = base_field) if len(C) != 2: raise TypeError, "points in F (=%s) must be planar"%F C.append(1) elif isinstance(f, SchemeMorphism_point_projective_field): C = Sequence(f, universe = base_field) elif isinstance(f, (list, tuple)): C = Sequence(f, universe = base_field) if len(C) == 2: C.append(1) else: raise TypeError, "F (=%s) must be a sequence of planar " \ "points" % F if len(C) != 3: raise TypeError, "points in F (=%s) must be planar" % F P = C.universe() if not is_IntegralDomain(P): raise TypeError, "coordinates of points in F (=%s) must " \ "be in an integral domain" % F L.append(Sequence([C[0]**2, C[0]*C[1], C[0]*C[2], C[1]**2, C[1]*C[2], C[2]**2], P.fraction_field())) M=Matrix(L) if unique and M.rank() != 5: raise ValueError, "points in F (=%s) do not define a unique " \ "conic" % F con = Conic(base_field, Sequence(M.right_kernel().gen()), names) con.point(F[0]) return con F = Sequence(F, universe = base_field) base_field = F.universe().fraction_field() temp_ring = PolynomialRing(base_field, 3, names) (x,y,z) = temp_ring.gens() if len(F) == 3: return Conic(F[0]*x**2 + F[1]*y**2 + F[2]*z**2) if len(F) == 6: return Conic(F[0]*x**2 + F[1]*x*y + F[2]*x*z + F[3]*y**2 + \ F[4]*y*z + F[5]*z**2) raise TypeError, "F (=%s) must be a sequence of 3 or 6" \ "coefficients" % F if is_QuadraticForm(F): F = F.matrix() if is_Matrix(F) and F.is_square() and F.ncols() == 3: if names == None: names = 'x,y,z' temp_ring = PolynomialRing(F.base_ring(), 3, names) F = vector(temp_ring.gens()) * F * vector(temp_ring.gens()) if not is_MPolynomial(F): raise TypeError, "F (=%s) must be a three-variable polynomial or " \ "a sequence of points or coefficients" % F if F.total_degree() != 2: raise TypeError, "F (=%s) must have degree 2" % F if base_field == None: base_field = F.base_ring() if not is_IntegralDomain(base_field): raise ValueError, "Base field (=%s) must be a field" % base_field base_field = base_field.fraction_field() if names == None: names = F.parent().variable_names() pol_ring = PolynomialRing(base_field, 3, names) if F.parent().ngens() == 2: (x,y,z) = pol_ring.gens() F = pol_ring(F(x/z,y/z)*z**2) if F == 0: raise ValueError, "F must be nonzero over base field %s" % base_field if F.total_degree() != 2: raise TypeError, "F (=%s) must have degree 2 over base field %s" % \ (F, base_field) if F.parent().ngens() == 3: P2 = ProjectiveSpace(2, base_field, names) if is_PrimeFiniteField(base_field): return ProjectiveConic_prime_finite_field(P2, F) if is_FiniteField(base_field): return ProjectiveConic_finite_field(P2, F) if is_RationalField(base_field): return ProjectiveConic_rational_field(P2, F) if is_NumberField(base_field): return ProjectiveConic_number_field(P2, F) return ProjectiveConic_field(P2, F) raise TypeError, "Number of variables of F (=%s) must be 2 or 3" % F
def is_cm_j_invariant(j, method='new'): """ Return whether or not this is a CM `j`-invariant. INPUT: - ``j`` -- an element of a number field `K` OUTPUT: A pair (bool, (d,f)) which is either (False, None) if `j` is not a CM j-invariant or (True, (d,f)) if `j` is the `j`-invariant of the imaginary quadratic order of discriminant `D=df^2` where `d` is the associated fundamental discriminant and `f` the index. .. note:: The current implementation makes use of the classification of all orders of class number up to 100, and hence will raise an error if `j` is an algebraic integer of degree greater than this. It would be possible to implement a more general version, using the fact that `d` must be supported on the primes dividing the discriminant of the minimal polynomial of `j`. EXAMPLES:: sage: from sage.schemes.elliptic_curves.cm import is_cm_j_invariant sage: is_cm_j_invariant(0) (True, (-3, 1)) sage: is_cm_j_invariant(8000) (True, (-8, 1)) sage: K.<a> = QuadraticField(5) sage: is_cm_j_invariant(282880*a + 632000) (True, (-20, 1)) sage: K.<a> = NumberField(x^3 - 2) sage: is_cm_j_invariant(31710790944000*a^2 + 39953093016000*a + 50337742902000) (True, (-3, 6)) TESTS:: sage: from sage.schemes.elliptic_curves.cm import is_cm_j_invariant sage: all([is_cm_j_invariant(j) == (True, (d,f)) for d,f,j in cm_j_invariants_and_orders(QQ)]) True """ # First we check that j is an algebraic number: from sage.rings.all import NumberFieldElement, NumberField if not isinstance(j, NumberFieldElement) and not j in QQ: raise NotImplementedError( "is_cm_j_invariant() is only implemented for number field elements" ) # for j in ZZ we have a lookup-table: if j in ZZ: j = ZZ(j) table = dict([(jj, (d, f)) for d, f, jj in cm_j_invariants_and_orders(QQ)]) if j in table: return True, table[j] return False, None # Otherwise if j is in Q then it is not integral so is not CM: if j in QQ: return False, None # Now j has degree at least 2. If it is not integral so is not CM: if not j.is_integral(): return False, None # Next we find its minimal polynomial and degree h, and if h is # less than the degree of j.parent() we recreate j as an element # of Q(j): jpol = PolynomialRing(QQ, 'x')([-j, 1 ]) if j in QQ else j.absolute_minpoly() h = jpol.degree() # This will be used as a fall-back if we cannot determine the # result using local data. For this to be necessary there would # have to be very few primes of degree 1 and norm under 1000, # since we only need to find one prime of degree 1, good # reduction for which a_P is nonzero. if method == 'old': if h > 100: raise NotImplementedError( "CM data only available for class numbers up to 100") for d, f in cm_orders(h): if jpol == hilbert_class_polynomial(d * f**2): return True, (d, f) return False, None # replace j by a clone whose parent is Q(j), if necessary: K = j.parent() if h < K.absolute_degree(): K = NumberField(jpol, 'j') j = K.gen() # Construct an elliptic curve with j-invariant j, with # integral model: from sage.schemes.elliptic_curves.all import EllipticCurve E = EllipticCurve(j=j).integral_model() D = E.discriminant() prime_bound = 1000 # test primes of degree 1 up to this norm max_primes = 20 # test at most this many primes num_prime = 0 cmd = 0 cmf = 0 # Test primes of good reduction. If E has CM then for half the # primes P we will have a_P=0, and for all other prime P the CM # field is Q(sqrt(a_P^2-4N(P))). Hence if these fields are # different for two primes then E does not have CM. If they are # all equal for the primes tested, then we have a candidate CM # field. Moreover the discriminant of the endomorphism ring # divides all the values a_P^2-4N(P), since that is the # discriminant of the order containing the Frobenius at P. So we # end up with a finite number (usually one) of candidate # discriminants to test. Each is tested by checking that its class # number is h, and if so then that j is a root of its Hilbert # class polynomial. In practice non CM curves will be eliminated # by the local test at a small number of primes (probably just 2). for P in K.primes_of_degree_one_iter(prime_bound): if num_prime > max_primes: if cmd: # we have a candidate CM field already break else: # we need to try more primes max_primes *= 2 if D.valuation(P) > 0: # skip bad primes continue aP = E.reduction(P).trace_of_frobenius() if aP == 0: # skip supersingular primes continue num_prime += 1 DP = aP**2 - 4 * P.norm() dP = DP.squarefree_part() fP = ZZ(DP // dP).isqrt() if cmd == 0: # first one, so store d and f cmd = dP cmf = fP elif cmd != dP: # inconsistent with previous return False, None else: # consistent d, so update f cmf = cmf.gcd(fP) if cmd == 0: # no conclusion, we found no degree 1 primes, revert to old method return is_cm_j_invariant(j, method='old') # it looks like cm by disc cmd * f**2 where f divides cmf if cmd % 4 != 1: cmd = cmd * 4 cmf = cmf // 2 # Now we must check if h(cmd*f**2)==h for f|cmf; if so we check # whether j is a root of the associated Hilbert class polynomial. for f in cmf.divisors(): # only positive divisors d = cmd * f**2 if h != d.class_number(): continue pol = hilbert_class_polynomial(d) if pol(j) == 0: return True, (cmd, f) return False, None
def _normalize_2x2(G): r""" Normalize this indecomposable `2` by `2` block. INPUT: ``G`` - a `2` by `2` matrix over `\ZZ_p` with ``type = 'fixed-mod'`` of the form:: [2a b] [ b 2c] * 2^n with `b` of valuation 1. OUTPUT: A unimodular `2` by `2` matrix ``B`` over `\ZZ_p` with ``B * G * B.transpose()`` either:: [0 1] [2 1] [1 0] * 2^n or [1 2] * 2^n EXAMPLES:: sage: from sage.quadratic_forms.genera.normal_form import _normalize_2x2 sage: R = Zp(2, prec = 15, type = 'fixed-mod', print_mode='series', show_prec=False) sage: G = Matrix(R, 2, [-17*2,3,3,23*2]) sage: B =_normalize_2x2(G) sage: B * G * B.T [2 1] [1 2] sage: G = Matrix(R,2,[-17*4,3,3,23*2]) sage: B = _normalize_2x2(G) sage: B*G*B.T [0 1] [1 0] sage: G = 2^3 * Matrix(R, 2, [-17*2,3,3,23*2]) sage: B = _normalize_2x2(G) sage: B * G * B.T [2^4 2^3] [2^3 2^4] """ from sage.rings.all import PolynomialRing from sage.modules.free_module_element import vector B = copy(G.parent().identity_matrix()) R = G.base_ring() P = PolynomialRing(R, 'x') x = P.gen() # The input must be an even block odd1 = (G[0, 0].valuation() < G[1, 0].valuation()) odd2 = (G[1, 1].valuation() < G[1, 0].valuation()) if odd1 or odd2: raise ValueError("Not a valid 2 x 2 block.") scale = 2 ** G[0,1].valuation() D = Matrix(R, 2, 2, [d // scale for d in G.list()]) # now D is of the form # [2a b ] # [b 2c] # where b has valuation 1. G = copy(D) # Make sure G[1, 1] has valuation 1. if D[1, 1].valuation() > D[0, 0].valuation(): B.swap_columns(0, 1) D.swap_columns(0, 1) D.swap_rows(0, 1) if D[1, 1].valuation() != 1: # this works because # D[0, 0] has valuation at least 2 B[1, :] += B[0, :] D = B * G * B.transpose() assert D[1, 1].valuation() == 1 if mod(D.det(), 8) == 3: # in this case we can transform D to # 2 1 # 1 2 # Find a point of norm 2 # solve: 2 == D[1,1]*x^2 + 2*D[1,0]*x + D[0,0] pol = (D[1,1]*x**2 + 2*D[1,0]*x + D[0,0]-2) // 2 # somehow else pari can get a hickup see `trac`:#24065 pol = pol // pol.leading_coefficient() sol = pol.roots()[0][0] B[0, 1] = sol D = B * G * B.transpose() # make D[0, 1] = 1 B[1, :] *= D[1, 0].inverse_of_unit() D = B * G * B.transpose() # solve: v*D*v == 2 with v = (x, -2*x+1) if D[1, 1] != 2: v = vector([x, -2*x + 1]) pol = (v*D*v - 2) // 2 # somehow else pari can get a hickup `trac`:#24065 pol = pol // pol.leading_coefficient() sol = pol.roots()[0][0] B[1, :] = sol * B[0,:] + (-2*sol + 1)*B[1, :] D = B * G * B.transpose() # check the result assert D == Matrix(G.parent(), 2, 2, [2, 1, 1, 2]), "D1 \n %r" %D elif mod(D.det(), 8) == 7: # in this case we can transform D to # 0 1 # 1 0 # Find a point representing 0 # solve: 0 == D[1,1]*x^2 + 2*D[1,0]*x + D[0,0] pol = (D[1,1]*x**2 + 2*D[1,0]*x + D[0,0])//2 # somehow else pari can get a hickup, see `trac`:#24065 pol = pol // pol.leading_coefficient() sol = pol.roots()[0][0] B[0,:] += sol*B[1, :] D = B * G * B.transpose() # make the second basis vector have 0 square as well. B[1, :] = B[1, :] - D[1, 1]//(2*D[0, 1])*B[0,:] D = B * G * B.transpose() # rescale to get D[0,1] = 1 B[0, :] *= D[1, 0].inverse_of_unit() D = B * G * B.transpose() # check the result assert D == Matrix(G.parent(), 2, 2, [0, 1, 1, 0]), "D2 \n %r" %D return B
def parametrization(self, point=None, morphism=True): r""" Return a parametrization `f` of ``self`` together with the inverse of `f`. If ``point`` is specified, then that point is used for the parametrization. Otherwise, use ``self.rational_point()`` to find a point. If ``morphism`` is True, then `f` is returned in the form of a Scheme morphism. Otherwise, it is a tuple of polynomials that gives the parametrization. ALGORITHM: Uses the PARI/GP function ``qfparam``. EXAMPLES :: sage: c = Conic([1,1,-1]) sage: c.parametrization() (Scheme morphism: From: Projective Space of dimension 1 over Rational Field To: Projective Conic Curve over Rational Field defined by x^2 + y^2 - z^2 Defn: Defined on coordinates by sending (x : y) to (2*x*y : x^2 - y^2 : x^2 + y^2), Scheme morphism: From: Projective Conic Curve over Rational Field defined by x^2 + y^2 - z^2 To: Projective Space of dimension 1 over Rational Field Defn: Defined on coordinates by sending (x : y : z) to (1/2*x : -1/2*y + 1/2*z)) An example with ``morphism = False`` :: sage: R.<x,y,z> = QQ[] sage: C = Curve(7*x^2 + 2*y*z + z^2) sage: (p, i) = C.parametrization(morphism = False); (p, i) ([-2*x*y, x^2 + 7*y^2, -2*x^2], [-1/2*x, 1/7*y + 1/14*z]) sage: C.defining_polynomial()(p) 0 sage: i[0](p) / i[1](p) x/y A ``ValueError`` is raised if ``self`` has no rational point :: sage: C = Conic(x^2 + 2*y^2 + z^2) sage: C.parametrization() Traceback (most recent call last): ... ValueError: Conic Projective Conic Curve over Rational Field defined by x^2 + 2*y^2 + z^2 has no rational points over Rational Field! A ``ValueError`` is raised if ``self`` is not smooth :: sage: C = Conic(x^2 + y^2) sage: C.parametrization() Traceback (most recent call last): ... ValueError: The conic self (=Projective Conic Curve over Rational Field defined by x^2 + y^2) is not smooth, hence does not have a parametrization. """ if (not self._parametrization is None) and not point: par = self._parametrization else: if not self.is_smooth(): raise ValueError( "The conic self (=%s) is not smooth, hence does not have a parametrization." % self) if point is None: point = self.rational_point() point = Sequence(point) Q = PolynomialRing(QQ, 'x,y') [x, y] = Q.gens() gens = self.ambient_space().gens() M = self.symmetric_matrix() M *= lcm([t.denominator() for t in M.list()]) par1 = qfparam(M, point) B = Matrix([[par1[i][j] for j in range(3)] for i in range(3)]) # self is in the image of B and does not lie on a line, # hence B is invertible A = B.inverse() par2 = [sum([A[i, j] * gens[j] for j in range(3)]) for i in [1, 0]] par = ([Q(pol(x / y) * y**2) for pol in par1], par2) if self._parametrization is None: self._parametrization = par if not morphism: return par P1 = ProjectiveSpace(self.base_ring(), 1, 'x,y') return P1.hom(par[0], self), self.Hom(P1)(par[1], check=False)
def _load_euler_table(self, n, force=False, verbose=False): r""" Load the euler table for self over the degree n extension of $\mathbb{F}_q$ to disk. If self is the versal j-curve, the table is pulled from SAGE_ROOT/data/jcurve_euler_tables . Otherwise, the table is pulled from the `user` table SAGE_ROOT/data/local_euler_tables . This should eventually be implemented using MongoDB. It currently doesn't check if the key exist. If the key doesn't exist, a RuntimeError is raised by sage.database.db. This RuntimeError should be sufficient, so key checking may not be necessary. INPUT: - n -- the degree of the extension of F_q - force -- boolean that overwrites self's euler table with one from database EXAMPLES:: sage: import psage sage: K.<t> = psage.FunctionField(GF(11)) sage: E = psage.ellff_EllipticCurve(K,[0,0,0,-27*t/(t-1728),54*t/(t-1728)]) sage: E._euler_table(1) Traceback (most recent call last): ... RuntimeError: table is empty sage: E._load_euler_table(1) sage: E._euler_table(1) [0, 0, 4, -6, 3, 5, 1, -2, 4, -2, 3, 1] """ import os SAGE_ROOT = os.environ['SAGE_ROOT'] K = self.K R = self.R t = K.gens()[0] p = self.p d = self.d q = self.q R2 = PolynomialRing(GF(q), 's') s = R2.gens()[0] a1n = R2(0) a1d = R2(1) a2n = R2(0) a2d = R2(1) a3n = R2(0) a3d = R2(1) a4n = R2(self.a4.numerator().coeffs()) a4d = R2(self.a4.denominator().coeffs()) a6n = R2(self.a6.numerator().coeffs()) a6d = R2(self.a6.denominator().coeffs()) ainvs = [0, 0, 0, self.a4, self.a6] ainvs_pairs = ((a1n, a1d), (a2n, a2d), (a3n, a3d), (a4n, a4d), (a6n, a6d)) # recognize if self is j-curve and use special repository if ainvs == [0,0,0,-27*t*(t-1728)**3,54*t*(t-1728)**5]: if verbose: print 'j-curve recognized; saving euler table to database' if not os.path.exists(SAGE_ROOT + '/data/jcurve_euler_tables/jcurve_euler_tables'): print 'Database does not exist; cannot load from it' else: euler_db = jCurveEulerTables() # check that keys exist? self._set_euler_table(n, euler_db[q][n], force) # work with user's repository of euler tables else: if not os.path.exists(SAGE_ROOT + '/data/local_euler_tables/local_euler_tables'): print 'Database does not exist; cannot load from it' else: local_euler_db = LocalEulerTables() # check that keys exist? self._set_euler_table(n, local_euler_db[ainvs_pairs][q][n], force)
def segre_embedding(self, PP=None, var='u'): r""" Return the Segre embedding of ``self`` into the appropriate projective space. INPUT: - ``PP`` -- (default: ``None``) ambient image projective space; this is constructed if it is not given. - ``var`` -- string, variable name of the image projective space, default `u` (optional) OUTPUT: Hom -- from ``self`` to the appropriate subscheme of projective space .. TODO:: Cartesian products with more than two components EXAMPLES:: sage: X.<y0,y1,y2,y3,y4,y5> = ProductProjectiveSpaces(ZZ,[2,2]) sage: phi = X.segre_embedding(); phi Scheme morphism: From: Product of projective spaces P^2 x P^2 over Integer Ring To: Closed subscheme of Projective Space of dimension 8 over Integer Ring defined by: -u5*u7 + u4*u8, -u5*u6 + u3*u8, -u4*u6 + u3*u7, -u2*u7 + u1*u8, -u2*u4 + u1*u5, -u2*u6 + u0*u8, -u1*u6 + u0*u7, -u2*u3 + u0*u5, -u1*u3 + u0*u4 Defn: Defined by sending (y0 : y1 : y2 , y3 : y4 : y5) to (y0*y3 : y0*y4 : y0*y5 : y1*y3 : y1*y4 : y1*y5 : y2*y3 : y2*y4 : y2*y5). :: sage: T = ProductProjectiveSpaces([1,2],CC,'z') sage: T.segre_embedding() Scheme morphism: From: Product of projective spaces P^1 x P^2 over Complex Field with 53 bits of precision To: Closed subscheme of Projective Space of dimension 5 over Complex Field with 53 bits of precision defined by: -u2*u4 + u1*u5, -u2*u3 + u0*u5, -u1*u3 + u0*u4 Defn: Defined by sending (z0 : z1 , z2 : z3 : z4) to (z0*z2 : z0*z3 : z0*z4 : z1*z2 : z1*z3 : z1*z4). """ N = self._dims if len(N) > 2: raise NotImplementedError("Cannot have more than two components.") M = (N[0]+1)*(N[1]+1)-1 vars = list(self.coordinate_ring().variable_names()) + [var + str(i) for i in range(M+1)] R = PolynomialRing(self.base_ring(),self.ngens()+M+1, vars, order='lex') #set-up the elimination for the segre embedding mapping = [] k = self.ngens() for i in range(N[0]+1): for j in range(N[0]+1,N[0]+N[1]+2): mapping.append(R.gen(k)-R(self.gen(i)*self.gen(j))) k+=1 #change the defining ideal of the subscheme into the variables I = R.ideal(list(self.defining_polynomials()) + mapping) J = I.groebner_basis() s = set(R.gens()[:self.ngens()]) n = len(J)-1 L = [] while s.isdisjoint(J[n].variables()): L.append(J[n]) n = n-1 #create new subscheme if PP is None: PS = ProjectiveSpace(self.base_ring(),M,R.gens()[self.ngens():]) Y = PS.subscheme(L) else: if PP.dimension_relative()!= M: raise ValueError("Projective Space %s must be dimension %s")%(PP, M) S = PP.coordinate_ring() psi = R.hom([0]*(N[0]+N[1]+2) + list(S.gens()),S) L = [psi(l) for l in L] Y = PP.subscheme(L) #create embedding for points mapping = [] for i in range(N[0]+1): for j in range(N[0]+1,N[0]+N[1]+2): mapping.append(self.gen(i)*self.gen(j)) phi = self.hom(mapping,Y) return phi
def to_sage(self): """ EXAMPLES: sage: macaulay2(ZZ).to_sage() #optional Integer Ring sage: macaulay2(QQ).to_sage() #optional Rational Field sage: macaulay2(2).to_sage() #optional 2 sage: macaulay2(1/2).to_sage() #optional 1/2 sage: macaulay2(2/1).to_sage() #optional 2 sage: _.parent() #optional Rational Field sage: macaulay2([1,2,3]).to_sage() #optional [1, 2, 3] sage: m = matrix([[1,2],[3,4]]) sage: macaulay2(m).to_sage() #optional [1 2] [3 4] sage: macaulay2(QQ['x,y']).to_sage() #optional Multivariate Polynomial Ring in x, y over Rational Field sage: macaulay2(QQ['x']).to_sage() #optional Univariate Polynomial Ring in x over Rational Field sage: macaulay2(GF(7)['x,y']).to_sage() #optional Multivariate Polynomial Ring in x, y over Finite Field of size 7 sage: macaulay2(GF(7)).to_sage() #optional Finite Field of size 7 sage: macaulay2(GF(49, 'a')).to_sage() #optional Finite Field in a of size 7^2 sage: R.<x,y> = QQ[] sage: macaulay2(x^2+y^2+1).to_sage() #optional x^2 + y^2 + 1 sage: R = macaulay2("QQ[x,y]") #optional sage: I = macaulay2("ideal (x,y)") #optional sage: I.to_sage() #optional Ideal (x, y) of Multivariate Polynomial Ring in x, y over Rational Field sage: X = R/I #optional sage: X.to_sage() #optional Quotient of Multivariate Polynomial Ring in x, y over Rational Field by the ideal (x, y) sage: R = macaulay2("QQ^2") #optional sage: R.to_sage() #optional Vector space of dimension 2 over Rational Field sage: m = macaulay2('"hello"') #optional sage: m.to_sage() #optional 'hello' """ repr_str = str(self) cls_str = str(self.cls()) cls_cls_str = str(self.cls().cls()) if repr_str == "ZZ": from sage.rings.all import ZZ return ZZ elif repr_str == "QQ": from sage.rings.all import QQ return QQ if cls_cls_str == "Type": if cls_str == "List": return [entry.to_sage() for entry in self] elif cls_str == "Matrix": from sage.matrix.all import matrix base_ring = self.ring().to_sage() entries = self.entries().to_sage() return matrix(base_ring, entries) elif cls_str == "Ideal": parent = self.ring().to_sage() gens = self.gens().entries().flatten().to_sage() return parent.ideal(*gens) elif cls_str == "QuotientRing": #Handle the ZZ/n case if "ZZ" in repr_str and "--" in repr_str: from sage.rings.all import ZZ, GF external_string = self.external_string() zz, n = external_string.split("/") #Note that n must be prime since it is #coming from Macaulay 2 return GF(ZZ(n)) ambient = self.ambient().to_sage() ideal = self.ideal().to_sage() return ambient.quotient(ideal) elif cls_str == "PolynomialRing": from sage.rings.all import PolynomialRing from sage.rings.polynomial.term_order import inv_macaulay2_name_mapping #Get the base ring base_ring = self.coefficientRing().to_sage() #Get a string list of generators gens = str(self.gens())[1:-1] # Check that we are dealing with default degrees, i.e. 1's. if self.degrees().any("x -> x != {1}").to_sage(): raise ValueError, "cannot convert Macaulay2 polynomial ring with non-default degrees to Sage" #Handle the term order external_string = self.external_string() order = None if "MonomialOrder" not in external_string: order = "degrevlex" else: for order_name in inv_macaulay2_name_mapping: if order_name in external_string: order = inv_macaulay2_name_mapping[order_name] if len(gens) > 1 and order is None: raise ValueError, "cannot convert Macaulay2's term order to a Sage term order" return PolynomialRing(base_ring, order=order, names=gens) elif cls_str == "GaloisField": from sage.rings.all import ZZ, GF gf, n = repr_str.split(" ") n = ZZ(n) if n.is_prime(): return GF(n) else: gen = str(self.gens())[1:-1] return GF(n, gen) elif cls_str == "Boolean": if repr_str == "true": return True elif repr_str == "false": return False elif cls_str == "String": return str(repr_str) elif cls_str == "Module": from sage.modules.all import FreeModule if self.isFreeModule().to_sage(): ring = self.ring().to_sage() rank = self.rank().to_sage() return FreeModule(ring, rank) else: #Handle the integers and rationals separately if cls_str == "ZZ": from sage.rings.all import ZZ return ZZ(repr_str) elif cls_str == "QQ": from sage.rings.all import QQ repr_str = self.external_string() if "/" not in repr_str: repr_str = repr_str + "/1" return QQ(repr_str) m2_parent = self.cls() parent = m2_parent.to_sage() if cls_cls_str == "PolynomialRing": from sage.misc.sage_eval import sage_eval gens_dict = parent.gens_dict() return sage_eval(self.external_string(), gens_dict) from sage.misc.sage_eval import sage_eval try: return sage_eval(repr_str) except Exception: raise NotImplementedError, "cannot convert %s to a Sage object" % repr_str
def parametrization(self, point=None, morphism=True): r""" Return a parametrization `f` of ``self`` together with the inverse of `f`. If ``point`` is specified, then that point is used for the parametrization. Otherwise, use ``self.rational_point()`` to find a point. If ``morphism`` is True, then `f` is returned in the form of a Scheme morphism. Otherwise, it is a tuple of polynomials that gives the parametrization. EXAMPLES: An example over a finite field :: sage: c = Conic(GF(2), [1,1,1,1,1,0]) sage: c.parametrization() (Scheme morphism: From: Projective Space of dimension 1 over Finite Field of size 2 To: Projective Conic Curve over Finite Field of size 2 defined by x^2 + x*y + y^2 + x*z + y*z Defn: Defined on coordinates by sending (x : y) to (x*y + y^2 : x^2 + x*y : x^2 + x*y + y^2), Scheme morphism: From: Projective Conic Curve over Finite Field of size 2 defined by x^2 + x*y + y^2 + x*z + y*z To: Projective Space of dimension 1 over Finite Field of size 2 Defn: Defined on coordinates by sending (x : y : z) to (y : x)) An example with ``morphism = False`` :: sage: R.<x,y,z> = QQ[] sage: C = Curve(7*x^2 + 2*y*z + z^2) sage: (p, i) = C.parametrization(morphism = False); (p, i) ([-2*x*y, x^2 + 7*y^2, -2*x^2], [-1/2*x, 1/7*y + 1/14*z]) sage: C.defining_polynomial()(p) 0 sage: i[0](p) / i[1](p) x/y A ``ValueError`` is raised if ``self`` has no rational point :: sage: C = Conic(x^2 + y^2 + 7*z^2) sage: C.parametrization() Traceback (most recent call last): ... ValueError: Conic Projective Conic Curve over Rational Field defined by x^2 + y^2 + 7*z^2 has no rational points over Rational Field! A ``ValueError`` is raised if ``self`` is not smooth :: sage: C = Conic(x^2 + y^2) sage: C.parametrization() Traceback (most recent call last): ... ValueError: The conic self (=Projective Conic Curve over Rational Field defined by x^2 + y^2) is not smooth, hence does not have a parametrization. """ if (not self._parametrization is None) and not point: par = self._parametrization else: if not self.is_smooth(): raise ValueError("The conic self (=%s) is not smooth, hence does not have a parametrization." % self) if point is None: point = self.rational_point() point = Sequence(point) B = self.base_ring() Q = PolynomialRing(B, 'x,y') [x, y] = Q.gens() gens = self.ambient_space().gens() P = PolynomialRing(B, 4, ['X', 'Y', 'T0', 'T1']) [X, Y, T0, T1] = P.gens() c3 = [j for j in range(2,-1,-1) if point[j] != 0][0] c1 = [j for j in range(3) if j != c3][0] c2 = [j for j in range(3) if j != c3 and j != c1][0] L = [0,0,0] L[c1] = Y*T1*point[c1] + Y*T0 L[c2] = Y*T1*point[c2] + X*T0 L[c3] = Y*T1*point[c3] bezout = P(self.defining_polynomial()(L) / T0) t = [bezout([x,y,0,-1]),bezout([x,y,1,0])] par = (tuple([Q(p([x,y,t[0],t[1]])/y) for p in L]), tuple([gens[m]*point[c3]-gens[c3]*point[m] for m in [c2,c1]])) if self._parametrization is None: self._parametrization = par if not morphism: return par P1 = ProjectiveSpace(self.base_ring(), 1, 'x,y') return P1.hom(par[0],self), self.Hom(P1)(par[1], check = False)
def sage(self, var="x", base_field=RationalField()): from sage.rings.all import PolynomialRing, QQ, Integer PP = PolynomialRing(QQ, var) return PP([Integer(val) for val in self])
def segre_embedding(self, PP=None, var='u'): r""" Return the Segre embedding of this space into the appropriate projective space. INPUT: - ``PP`` -- (default: ``None``) ambient image projective space; this is constructed if it is not given. - ``var`` -- string, variable name of the image projective space, default `u` (optional). OUTPUT: Hom -- from this space to the appropriate subscheme of projective space. .. TODO:: Cartesian products with more than two components. EXAMPLES:: sage: X.<y0,y1,y2,y3,y4,y5> = ProductProjectiveSpaces(ZZ, [2, 2]) sage: phi = X.segre_embedding(); phi Scheme morphism: From: Product of projective spaces P^2 x P^2 over Integer Ring To: Closed subscheme of Projective Space of dimension 8 over Integer Ring defined by: -u5*u7 + u4*u8, -u5*u6 + u3*u8, -u4*u6 + u3*u7, -u2*u7 + u1*u8, -u2*u4 + u1*u5, -u2*u6 + u0*u8, -u1*u6 + u0*u7, -u2*u3 + u0*u5, -u1*u3 + u0*u4 Defn: Defined by sending (y0 : y1 : y2 , y3 : y4 : y5) to (y0*y3 : y0*y4 : y0*y5 : y1*y3 : y1*y4 : y1*y5 : y2*y3 : y2*y4 : y2*y5). :: sage: T = ProductProjectiveSpaces([1, 2], CC, 'z') sage: T.segre_embedding() Scheme morphism: From: Product of projective spaces P^1 x P^2 over Complex Field with 53 bits of precision To: Closed subscheme of Projective Space of dimension 5 over Complex Field with 53 bits of precision defined by: -u2*u4 + u1*u5, -u2*u3 + u0*u5, -u1*u3 + u0*u4 Defn: Defined by sending (z0 : z1 , z2 : z3 : z4) to (z0*z2 : z0*z3 : z0*z4 : z1*z2 : z1*z3 : z1*z4). :: sage: T = ProductProjectiveSpaces([1, 2, 1], QQ, 'z') sage: T.segre_embedding() Scheme morphism: From: Product of projective spaces P^1 x P^2 x P^1 over Rational Field To: Closed subscheme of Projective Space of dimension 11 over Rational Field defined by: -u9*u10 + u8*u11, -u7*u10 + u6*u11, -u7*u8 + u6*u9, -u5*u10 + u4*u11, -u5*u8 + u4*u9, -u5*u6 + u4*u7, -u5*u9 + u3*u11, -u5*u8 + u3*u10, -u5*u8 + u2*u11, -u4*u8 + u2*u10, -u3*u8 + u2*u9, -u3*u6 + u2*u7, -u3*u4 + u2*u5, -u5*u7 + u1*u11, -u5*u6 + u1*u10, -u3*u7 + u1*u9, -u3*u6 + u1*u8, -u5*u6 + u0*u11, -u4*u6 + u0*u10, -u3*u6 + u0*u9, -u2*u6 + u0*u8, -u1*u6 + u0*u7, -u1*u4 + u0*u5, -u1*u2 + u0*u3 Defn: Defined by sending (z0 : z1 , z2 : z3 : z4 , z5 : z6) to (z0*z2*z5 : z0*z2*z6 : z0*z3*z5 : z0*z3*z6 : z0*z4*z5 : z0*z4*z6 : z1*z2*z5 : z1*z2*z6 : z1*z3*z5 : z1*z3*z6 : z1*z4*z5 : z1*z4*z6). """ N = self._dims M = prod([n+1 for n in N]) - 1 CR = self.coordinate_ring() vars = list(self.coordinate_ring().variable_names()) + [var + str(i) for i in range(M+1)] R = PolynomialRing(self.base_ring(), self.ngens()+M+1, vars, order='lex') #set-up the elimination for the segre embedding mapping = [] k = self.ngens() index = self.num_components()*[0] for count in range(M + 1): mapping.append(R.gen(k+count)-prod([CR(self[i].gen(index[i])) for i in range(len(index))])) for i in range(len(index)-1, -1, -1): if index[i] == N[i]: index[i] = 0 else: index[i] += 1 break #only increment once #change the defining ideal of the subscheme into the variables I = R.ideal(list(self.defining_polynomials()) + mapping) J = I.groebner_basis() s = set(R.gens()[:self.ngens()]) n = len(J)-1 L = [] while s.isdisjoint(J[n].variables()): L.append(J[n]) n = n-1 #create new subscheme if PP is None: PS = ProjectiveSpace(self.base_ring(), M, R.variable_names()[self.ngens():]) Y = PS.subscheme(L) else: if PP.dimension_relative() != M: raise ValueError("projective Space %s must be dimension %s")%(PP, M) S = PP.coordinate_ring() psi = R.hom([0]*k + list(S.gens()), S) L = [psi(l) for l in L] Y = PP.subscheme(L) #create embedding for points mapping = [] index = self.num_components()*[0] for count in range(M + 1): mapping.append(prod([CR(self[i].gen(index[i])) for i in range(len(index))])) for i in range(len(index)-1, -1, -1): if index[i] == N[i]: index[i] = 0 else: index[i] += 1 break #only increment once phi = self.hom(mapping, Y) return phi
- 82*x^2 + 28*n + 4)/(-720*n^6*x^4 - 1644*n^5*x^4 + 540*n^6*x^2 - 1320*n^4*x^4 + 1233*n^5*x^2 - 429*n^3*x^4 + 180*n^6 + 980*n^4*x^2 - 45*n^2*x^4 + 456*n^5 + 311*n^3*x^2 + 449*n^4 + 32*n^2*x^2 + 213*n^3 + 48*n^2 + 4*n))*Dx + (144*n^5*x^5 - 420*n^4*x^5 - 180*n^5*x^3 - 1584*n^3*x^5 + 165*n^4*x^3 - 1575*n^2*x^5 - 180*n^5*x + 1163*n^3*x^3 - 633*n*x^5 - 501*n^4*x + 1303*n^2*x^3 - 90*x^5 - 599*n^3*x + 561*n*x^3 - 388*n^2*x + 84*x^3 - 132*n*x - 18*x)/(-720*n^6*x^4 - 1644*n^5*x^4 + 540*n^6*x^2 - 1320*n^4*x^4 + 1233*n^5*x^2 - 429*n^3*x^4 + 180*n^6 + 980*n^4*x^2 - 45*n^2*x^4 + 456*n^5 + 311*n^3*x^2 + 449*n^4 + 32*n^2*x^2 + 213*n^3 + 48*n^2 + 4*n)]) """ from sage.rings.all import PolynomialRing, ZZ from ore_algebra import OreAlgebra Pols, (n, x) = PolynomialRing(ZZ, ('n', 'x')).objgens() Alg, (Sn, Dx) = OreAlgebra(Pols, 'Sn', 'Dx').objgens() ideal = Alg.ideal([ -72-904*n-4986*n**2-16020*n**3-33410*n**4-47100*n**5-44172*n**6-25056*n**7-6480*n**8+1008*x**2+13044*n*x**2+71034*n**2*x**2+210375*n**3*x**2+364335*n**4*x**2+365457*n**5*x**2+190179*n**6*x**2+31320*n**7*x**2-6480*n**8*x**2-1800*x**4-25020*n*x**4-150372*n**2*x**4-510048*n**3*x**4-1067184*n**4*x**4-1409472*n**5*x**4-1146816*n**6*x**4-525312*n**7*x**4-103680*n**8*x**4+ (-632*x-8312*n*x-46742*n**2*x-145356*n**3*x-269478*n**4*x-297432*n**5*x-180792*n**6*x-46656*n**7*x+3816*x**3+47520*n*x**3+250812*n**2*x**3+727164*n**3*x**3+1251180*n**4*x**3+1278180*n**5*x**3+717984*n**6*x**3+171072*n**7*x**3-3600*x**5-42840*n*x**5-216864*n**2*x**5-605088*n**3*x**5-1004544*n**4*x**5-991872*n**5*x**5-539136*n**6*x**5-124416*n**7*x**5)*Dx+ (64+640*n+2656*n**2+5856*n**3+7236*n**4+4752*n**5+1296*n**6-868*x**2-9316*n*x**2-41149*n**2*x**2-95610*n**3*x**2-123237*n**4*x**2-83592*n**5*x**2-23328*n**6*x**2+1704*x**4+18036*n*x**4+78669*n**2*x**4+180762*n**3*x**4+230625*n**4*x**4+154872*n**5*x**4+42768*n**6*x**4-900*x**6-9360*n*x**6-40176*n**2*x**6-91008*n**3*x**6-114624*n**4*x**6-76032*n**5*x**6-20736*n**6*x**6)*Dx**2+ (-1824*n*x-21792*n**2*x-108048*n**3*x-288060*n**4*x-445896*n**5*x-400788*n**6*x-193752*n**7*x-38880*n**8*x+5400*n*x**3+64980*n**2*x**3+327600*n**3*x**3+898380*n**4*x**3+1449000*n**5*x**3+1375920*n**6*x**3+712800*n**7*x**3+155520*n**8*x**3)*Sn+ (96+1296*n+7416*n**2+23340*n**3+43668*n**4+48600*n**5+29808*n**6+7776*n**7+768*n*x**2+8616*n**2*x**2+39324*n**3*x**2+93528*n**4*x**2+122364*n**5*x**2+83592*n**6*x**2+23328*n**7*x**2-1080*n*x**4-11916*n**2*x**4-53604*n**3*x**4-126072*n**4*x**4-163728*n**5*x**4-111456*n**6*x**4-31104*n**7*x**4)*Dx*Sn, -5400-105480*n-921894*n**2-4761120*n**3-16159284*n**4-37911216*n**5-62936502*n**6-74364984*n**7-61974504*n**8-35484480*n**9-13242528*n**10-2892672*n**11-279936*n**12+129384*x**2+1892580*n*x**2+12015312*n**2*x**2+43193121*n**3*x**2+95701575*n**4*x**2+131196846*n**5*x**2+98852124*n**6*x**2+10703601*n**7*x**2-56892051*n**8*x**2-60342732*n**9*x**2-30114504*n**10*x**2-7791552*n**11*x**2-839808*n**12*x**2-1359792*x**4-21051288*n*x**4-144751216*n**2*x**4-583851859*n**3*x**4-1537579119*n**4*x**4-2785188713*n**5*x**4-3560012897*n**6*x**4-3237959820*n**7*x**4-2082127284*n**8*x**4-924290676*n**9*x**4-269209656*n**10*x**4-46243872*n**11*x**4-3545856*n**12*x**4+4784400*x**6+76503120*n*x**6+547035412*n**2*x**6+2312068003*n**3*x**6+6433195773*n**4*x**6+12418820978*n**5*x**6+17064655448*n**6*x**6+16828542087*n**7*x**6+11828817747*n**8*x**6+5783182056*n**9*x**6+1867777056*n**10*x**6+357944832*n**11*x**6+30792960*n**12*x**6-6463800*x**8-105459264*n*x**8-771454650*n**2*x**8-3343723785*n**3*x**8-9559454217*n**4*x**8-18985142175*n**5*x**8-26851473837*n**6*x**8-27246680724*n**7*x**8-19685010348*n**8*x**8-9874992096*n**9*x**8-3265152768*n**10*x**8-639000576*n**11*x**8-55987200*n**12*x**8+2910600*x**10+48143340*n*x**10+357510924*n**2*x**10+1574659656*n**3*x**10+4577650848*n**4*x**10+9245867904*n**5*x**10+13295069568*n**6*x**10+13705751808*n**7*x**10+10048822272*n**8*x**10+5108714496*n**9*x**10+1709227008*n**10*x**10+337913856*n**11*x**10+29859840*n**12*x**10+ (-33000*x-555000*n*x-4110810*n**2*x-17685984*n**3*x-49070892*n**4*x-92127504*n**5*x-119350890*n**6*x-106644168*n**7*x-64400184*n**8*x-25038720*n**9*x-5645376*n**10*x-559872*n**11*x+929184*x**3+14492616*n*x**3+100446642*n**2*x**3+407717712*n**3*x**3+1075419702*n**4*x**3+1932881640*n**5*x**3+2412492228*n**6*x**3+2088674316*n**7*x**3+1228199760*n**8*x**3+466941024*n**9*x**3+103296384*n**10*x**3+10077696*n**11*x**3-6070944*x**5-92314064*n*x**5-624602362*n**2*x**5-2478730156*n**3*x**5-6402603824*n**4*x**5-11288513388*n**5*x**5-13845657870*n**6*x**5-11800443960*n**7*x**5-6842534616*n**8*x**5-2569322592*n**9*x**5-562158144*n**10*x**5-54307584*n**11*x**5+15191760*x**7+226758656*n*x**7+1507085686*n**2*x**7+5879513080*n**3*x**7+14942273834*n**4*x**7+25944633648*n**5*x**7+31369911012*n**6*x**7+26385003396*n**7*x**7+15115595328*n**8*x**7+5613929856*n**9*x**7+1216228608*n**10*x**7+116453376*n**11*x**7-15838200*x**9-233026488*n*x**9-1527463044*n**2*x**9-5880802428*n**3*x**9-14758732548*n**4*x**9-25322095260*n**5*x**9-30275079984*n**6*x**9-25198350480*n**7*x**9-14296383360*n**8*x**9-5262672384*n**9*x**9-1130941440*n**10*x**9-107495424*n**11*x**9+5821200*x**11+84644280*n*x**11+548643888*n**2*x**11+2089987776*n**3*x**11+5192713728*n**4*x**11+8825220864*n**5*x**11+10457685504*n**6*x**11+8631760896*n**7*x**11+4859523072*n**8*x**11+1776162816*n**9*x**11+379219968*n**10*x**11+35831808*n**11*x**11)*Dx+ (-127116*x**2-1683972*n*x**2-9769107*n**2*x**2-32626704*n**3*x**2-69401658*n**4*x**2-98184600*n**5*x**2-93524139*n**6*x**2-59220396*n**7*x**2-23862600*n**8*x**2-5528736*n**9*x**2-559872*n**10*x**2+1235652*x**4+16340320*n*x**4+94671764*n**2*x**4+315934898*n**3*x**4+671781259*n**4*x**4+950275572*n**5*x**4+905174019*n**6*x**4+573165072*n**7*x**4+230925168*n**8*x**4+53483328*n**9*x**4+5412096*n**10*x**4-4351656*x**6-57385616*n*x**6-331761754*n**2*x**6-1105455826*n**3*x**6-2348151407*n**4*x**6-3319311384*n**5*x**6-3160178217*n**6*x**6-2000166588*n**7*x**6-805459464*n**8*x**6-186429600*n**9*x**6-18849024*n**10*x**6+6960120*x**8+91464280*n*x**8+527278436*n**2*x**8+1753070030*n**3*x**8+3717559717*n**4*x**8+5248348596*n**5*x**8+4991631093*n**6*x**8+3156609528*n**7*x**8+1270139616*n**8*x**8+293746176*n**9*x**8+29673216*n**10*x**8-5172300*x**10-67713132*n*x**10-389113131*n**2*x**10-1290378654*n**3*x**10-2730781959*n**4*x**10-3848942328*n**5*x**10-3655802916*n**6*x**10-2309277600*n**7*x**10-928288512*n**8*x**10-214493184*n**9*x**10-21648384*n**10*x**10+1455300*x**12+18978120*n*x**12+108693792*n**2*x**12+359456256*n**3*x**12+758994048*n**4*x**12+1067814144*n**5*x**12+1012700160*n**6*x**12+638889984*n**7*x**12+256545792*n**8*x**12+59222016*n**9*x**12+5971968*n**10*x**12)*Dx**2+ (50688*x+721152*n*x+4483584*n**2*x+16102656*n**3*x+37203264*n**4*x+58222944*n**5*x+63169416*n**6*x+47639124*n**7*x+24518700*n**8*x+8209512*n**9*x+1609632*n**10*x+139968*n**11*x+2754792*n*x**3+40133340*n**2*x**3+256857168*n**3*x**3+954119016*n**4*x**3+2288062296*n**5*x**3+3724089648*n**6*x**3+4203524664*n**7*x**3+3294832572*n**8*x**3+1759833864*n**9*x**3+610765920*n**10*x**3+124104960*n**11*x**3+11197440*n**12*x**3-11506824*n*x**5-167233044*n**2*x**5-1069000116*n**3*x**5-3970620108*n**4*x**5-9530229264*n**5*x**5-15535043832*n**6*x**5-17566817004*n**7*x**5-13794112512*n**8*x**5-7379007984*n**9*x**5-2563762752*n**10*x**5-521240832*n**11*x**5-47029248*n**12*x**5+17451000*n*x**7+253133700*n**2*x**7+1616458560*n**3*x**7+6003388332*n**4*x**7+14418672312*n**5*x**7+23531831040*n**6*x**7+26649246528*n**7*x**7+20958160320*n**8*x**7+11226625920*n**9*x**7+3904547328*n**10*x**7+794271744*n**11*x**7+71663616*n**12*x**7-8731800*n*x**9-126384300*n**2*x**9-805886028*n**3*x**9-2990795472*n**4*x**9-7182502416*n**5*x**9-11726815104*n**6*x**9-13289785920*n**7*x**9-10460448000*n**8*x**9-5607823104*n**9*x**9-1951589376*n**10*x**9-397135872*n**11*x**9-35831808*n**12*x**9)*Sn+ (-46080-761088*n-5602944*n**2-24290688*n**3-69000576*n**4-135145680*n**5-186937800*n**6-183825792*n**7-127461384*n**8-60764256*n**9-18911232*n**10-3452544*n**11-279936*n**12-368640*n*x**2-5247744*n**2*x**2-32684736*n**3*x**2-117586080*n**4*x**2-271573680*n**5*x**2-422997000*n**6*x**2-453713832*n**7*x**2-335454264*n**8*x**2-167741928*n**9*x**2-54105408*n**10*x**2-10147680*n**11*x**2-839808*n**12*x**2+518400*n*x**4+7283520*n**2*x**4+44859168*n**3*x**4+159936048*n**4*x**4+366849864*n**5*x**4+568549728*n**6*x**4+607740264*n**7*x**4+448328736*n**8*x**4+223881408*n**9*x**4+72161280*n**10*x**4+13530240*n**11*x**4+1119744*n**12*x**4)*Sn**2,
def _sage_(self): """ EXAMPLES: sage: m = lie('[[1,0,3,3],[12,4,-4,7],[-1,9,8,0],[3,-5,-2,9]]') # optional - lie sage: m.sage() # optional - lie [ 1 0 3 3] [12 4 -4 7] [-1 9 8 0] [ 3 -5 -2 9] """ t = self.type() if t == 'grp': raise ValueError( "cannot convert Lie groups to native Sage objects") elif t == 'mat': import sage.matrix.constructor return sage.matrix.constructor.matrix( eval(str(self).replace('\n', '').strip())) elif t == 'pol': import sage.misc.misc from sage.rings.all import PolynomialRing, QQ #Figure out the number of variables s = str(self) open_bracket = s.find('[') close_bracket = s.find(']') nvars = len(s[open_bracket:close_bracket].split(',')) #create the polynomial ring R = PolynomialRing(QQ, nvars, 'x') x = R.gens() pol = R(0) #Split up the polynomials into terms terms = [] for termgrp in s.split(' - '): #The first entry in termgrp has #a negative coefficient termgrp = "-" + termgrp.strip() terms += termgrp.split('+') #Make sure we don't accidentally add a negative #sign to the first monomial if s[0] != "-": terms[0] = terms[0][1:] #go through all the terms in s for term in terms: xpos = term.find('X') coef = eval(term[:xpos].strip()) exps = eval(term[xpos + 1:].strip()) monomial = sage.misc.misc.prod( map(lambda i: x[i]**exps[i], range(nvars))) pol += coef * monomial return pol elif t == 'tex': return repr(self) elif t == 'vid': return None else: return ExpectElement._sage_(self)
def __call__(self, P): r""" Returns a rational point P in the abstract Homset J(K), given: 0. A point P in J = Jac(C), returning P; 1. A point P on the curve C such that J = Jac(C), where C is an odd degree model, returning [P - oo]; 2. A pair of points (P, Q) on the curve C such that J = Jac(C), returning [P-Q]; 2. A list of polynomials (a,b) such that `b^2 + h*b - f = 0 mod a`, returning [(a(x),y-b(x))]. EXAMPLES:: sage: P.<x> = PolynomialRing(QQ) sage: f = x^5 - x + 1; h = x sage: C = HyperellipticCurve(f,h,'u,v') sage: P = C(0,1,1) sage: J = C.jacobian() sage: Q = J(QQ)(P) sage: for i in range(6): i*Q (1) (u, v - 1) (u^2, v + u - 1) (u^2, v + 1) (u, v + 1) (1) :: sage: F.<a> = GF(3) sage: R.<x> = F[] sage: f = x^5-1 sage: C = HyperellipticCurve(f) sage: J = C.jacobian() sage: X = J(F) sage: a = x^2-x+1 sage: b = -x +1 sage: c = x-1 sage: d = 0 sage: D1 = X([a,b]) sage: D1 (x^2 + 2*x + 1, y + x + 2) sage: D2 = X([c,d]) sage: D2 (x + 2, y) sage: D1+D2 (x^2 + 2*x + 2, y + 2*x + 1) """ if isinstance(P, (int, long, Integer)) and P == 0: R = PolynomialRing(self.value_ring(), 'x') return JacobianMorphism_divisor_class_field(self, (R(1), R(0))) elif isinstance(P, (list, tuple)): if len(P) == 1 and P[0] == 0: R = PolynomialRing(self.value_ring(), 'x') return JacobianMorphism_divisor_class_field(self, (R(1), R(0))) elif len(P) == 2: P1 = P[0] P2 = P[1] if is_Integer(P1) and is_Integer(P2): R = PolynomialRing(self.value_ring(), 'x') P1 = R(P1) P2 = R(P2) return JacobianMorphism_divisor_class_field( self, tuple([P1, P2])) if is_Integer(P1) and is_Polynomial(P2): R = PolynomialRing(self.value_ring(), 'x') P1 = R(P1) return JacobianMorphism_divisor_class_field( self, tuple([P1, P2])) if is_Integer(P2) and is_Polynomial(P1): R = PolynomialRing(self.value_ring(), 'x') P2 = R(P2) return JacobianMorphism_divisor_class_field( self, tuple([P1, P2])) if is_Polynomial(P1) and is_Polynomial(P2): return JacobianMorphism_divisor_class_field(self, tuple(P)) if is_SchemeMorphism(P1) and is_SchemeMorphism(P2): return self(P1) - self(P2) raise TypeError("Argument P (= %s) must have length 2." % P) elif isinstance( P, JacobianMorphism_divisor_class_field) and self == P.parent(): return P elif is_SchemeMorphism(P): x0 = P[0] y0 = P[1] R, x = PolynomialRing(self.value_ring(), 'x').objgen() return self((x - x0, R(y0))) raise TypeError( "Argument P (= %s) does not determine a divisor class" % P)
def _normalize_2x2(G): r""" Normalize this indecomposable `2` by `2` block. INPUT: ``G`` - a `2` by `2` matrix over `\ZZ_p` with ``type = 'fixed-mod'`` of the form:: [2a b] [ b 2c] * 2^n with `b` of valuation 1. OUTPUT: A unimodular `2` by `2` matrix ``B`` over `\ZZ_p` with ``B * G * B.transpose()`` either:: [0 1] [2 1] [1 0] * 2^n or [1 2] * 2^n EXAMPLES:: sage: from sage.quadratic_forms.genera.normal_form import _normalize_2x2 sage: R = Zp(2, prec = 15, type = 'fixed-mod', print_mode='series', show_prec=False) sage: G = Matrix(R, 2, [-17*2,3,3,23*2]) sage: B =_normalize_2x2(G) sage: B * G * B.T [2 1] [1 2] sage: G = Matrix(R,2,[-17*4,3,3,23*2]) sage: B = _normalize_2x2(G) sage: B*G*B.T [0 1] [1 0] sage: G = 2^3 * Matrix(R, 2, [-17*2,3,3,23*2]) sage: B = _normalize_2x2(G) sage: B * G * B.T [2^4 2^3] [2^3 2^4] """ from sage.rings.all import PolynomialRing from sage.modules.free_module_element import vector B = copy(G.parent().identity_matrix()) R = G.base_ring() P = PolynomialRing(R, 'x') x = P.gen() # The input must be an even block odd1 = (G[0, 0].valuation() < G[1, 0].valuation()) odd2 = (G[1, 1].valuation() < G[1, 0].valuation()) if odd1 or odd2: raise ValueError("Not a valid 2 x 2 block.") scale = 2**G[0, 1].valuation() D = Matrix(R, 2, 2, [d // scale for d in G.list()]) # now D is of the form # [2a b ] # [b 2c] # where b has valuation 1. G = copy(D) # Make sure G[1, 1] has valuation 1. if D[1, 1].valuation() > D[0, 0].valuation(): B.swap_columns(0, 1) D.swap_columns(0, 1) D.swap_rows(0, 1) if D[1, 1].valuation() != 1: # this works because # D[0, 0] has valuation at least 2 B[1, :] += B[0, :] D = B * G * B.transpose() assert D[1, 1].valuation() == 1 if mod(D.det(), 8) == 3: # in this case we can transform D to # 2 1 # 1 2 # Find a point of norm 2 # solve: 2 == D[1,1]*x^2 + 2*D[1,0]*x + D[0,0] pol = (D[1, 1] * x**2 + 2 * D[1, 0] * x + D[0, 0] - 2) // 2 # somehow else pari can get a hickup see trac #24065 pol = pol // pol.leading_coefficient() sol = pol.roots()[0][0] B[0, 1] = sol D = B * G * B.transpose() # make D[0, 1] = 1 B[1, :] *= D[1, 0].inverse_of_unit() D = B * G * B.transpose() # solve: v*D*v == 2 with v = (x, -2*x+1) if D[1, 1] != 2: v = vector([x, -2 * x + 1]) pol = (v * D * v - 2) // 2 # somehow else pari can get a hickup see trac #24065 pol = pol // pol.leading_coefficient() sol = pol.roots()[0][0] B[1, :] = sol * B[0, :] + (-2 * sol + 1) * B[1, :] D = B * G * B.transpose() # check the result assert D == Matrix(G.parent(), 2, 2, [2, 1, 1, 2]), "D1 \n %r" % D elif mod(D.det(), 8) == 7: # in this case we can transform D to # 0 1 # 1 0 # Find a point representing 0 # solve: 0 == D[1,1]*x^2 + 2*D[1,0]*x + D[0,0] pol = (D[1, 1] * x**2 + 2 * D[1, 0] * x + D[0, 0]) // 2 # somehow else pari can get a hickup, see trac #24065 pol = pol // pol.leading_coefficient() sol = pol.roots()[0][0] B[0, :] += sol * B[1, :] D = B * G * B.transpose() # make the second basis vector have 0 square as well. B[1, :] = B[1, :] - D[1, 1] // (2 * D[0, 1]) * B[0, :] D = B * G * B.transpose() # rescale to get D[0,1] = 1 B[0, :] *= D[1, 0].inverse_of_unit() D = B * G * B.transpose() # check the result assert D == Matrix(G.parent(), 2, 2, [0, 1, 1, 0]), "D2 \n %r" % D return B
def _biquadratic_syzygy_quartic(quadratic1, quadratic2, variables=None): r""" Helper function for the Weierstrass form of a biquadratic in $`\mathbb{P}^3$ The invariants and covariants of a quaternary biquadratic satisfy the relation :meth:`sage.rings.invariant_theory.TwoQuaternaryQuadratics.syzygy`, which is (modulo the two quadratic equations) of the form $J^2 = p_4(T, T')$ where * $J$, $T$, $T'$ are the covariants of the biquadratic. * $p_4$ is some quartic polynomial whose coefficients are invariants of the biquadratic. INPUT: See :func:`WeierstrassForm_P3` OUTPUT: A triple consisting of - The quaternary biquadratic as an algebraic form :class:`~sage.rings.invariant_theory.TwoQuaternaryQuadratics` - The binary quartic $p_4$ as a :class:`~sage.rings.invariant_theory.BinaryQuartic` - The dictionary of variable substitutions from the variables of the quartic to the variables of the biquadratic. EXAMPLES:: sage: from sage.schemes.toric.weierstrass_higher import _biquadratic_syzygy_quartic sage: R.<w,x,y,z> = QQ[] sage: _biquadratic_syzygy_quartic(w^2+x^2+y^2, z^2) (Joint quaternary quadratic with coefficients (1, 1, 1, 0, 0, 0, 0, 0, 0, 0) and quaternary quadratic with coefficients (0, 0, 0, 1, 0, 0, 0, 0, 0, 0), Binary quartic with coefficients (0, 0, 0, -1, 0), {aux...}) """ w, x, y, z = _check_polynomials_P3(quadratic1, quadratic2, variables) biquadratic = invariant_theory.quaternary_biquadratic( quadratic1, quadratic2, [w, x, y, z]) # construct auxiliary polynomial ring to work with the rhs of the syzygy R = biquadratic.ring() n = R.ngens() R_aux = PolynomialRing(R.base_ring(), n + 2, 'aux') to_aux = dict() from_aux = dict() for var, var_aux in zip(R.gens(), R_aux.gens()[0:n]): to_aux[var] = var_aux from_aux[var_aux] = var T, T_prime = R_aux.gens()[n:] from_aux[T] = biquadratic.T_covariant() from_aux[T_prime] = biquadratic.T_prime_covariant() # Syzygy is J^2 = syz_rhs + (terms that vanish on the biquadratic) with # J = biquadratic.J_covariant() syz_rhs = T**4 * biquadratic.Delta_invariant().subs(to_aux) \ - T**3*T_prime * biquadratic.Theta_invariant().subs(to_aux) \ + T**2*T_prime**2 * biquadratic.Phi_invariant().subs(to_aux) \ - T*T_prime**3 * biquadratic.Theta_prime_invariant().subs(to_aux) \ + T_prime**4 * biquadratic.Delta_prime_invariant().subs(to_aux) quartic = invariant_theory.binary_quartic(syz_rhs, [T, T_prime]) return (biquadratic, quartic, from_aux)
def is_cm_j_invariant(j, method='new'): """ Return whether or not this is a CM `j`-invariant. INPUT: - ``j`` -- an element of a number field `K` OUTPUT: A pair (bool, (d,f)) which is either (False, None) if `j` is not a CM j-invariant or (True, (d,f)) if `j` is the `j`-invariant of the imaginary quadratic order of discriminant `D=df^2` where `d` is the associated fundamental discriminant and `f` the index. .. note:: The current implementation makes use of the classification of all orders of class number up to 100, and hence will raise an error if `j` is an algebraic integer of degree greater than this. It would be possible to implement a more general version, using the fact that `d` must be supported on the primes dividing the discriminant of the minimal polynomial of `j`. EXAMPLES:: sage: from sage.schemes.elliptic_curves.cm import is_cm_j_invariant sage: is_cm_j_invariant(0) (True, (-3, 1)) sage: is_cm_j_invariant(8000) (True, (-8, 1)) sage: K.<a> = QuadraticField(5) sage: is_cm_j_invariant(282880*a + 632000) (True, (-20, 1)) sage: K.<a> = NumberField(x^3 - 2) sage: is_cm_j_invariant(31710790944000*a^2 + 39953093016000*a + 50337742902000) (True, (-3, 6)) TESTS:: sage: from sage.schemes.elliptic_curves.cm import is_cm_j_invariant sage: all([is_cm_j_invariant(j) == (True, (d,f)) for d,f,j in cm_j_invariants_and_orders(QQ)]) True """ # First we check that j is an algebraic number: from sage.rings.all import NumberFieldElement, NumberField if not isinstance(j, NumberFieldElement) and not j in QQ: raise NotImplementedError("is_cm_j_invariant() is only implemented for number field elements") # for j in ZZ we have a lookup-table: if j in ZZ: j = ZZ(j) table = dict([(jj,(d,f)) for d,f,jj in cm_j_invariants_and_orders(QQ)]) if j in table: return True, table[j] return False, None # Otherwise if j is in Q then it is not integral so is not CM: if j in QQ: return False, None # Now j has degree at least 2. If it is not integral so is not CM: if not j.is_integral(): return False, None # Next we find its minimal polynomial and degree h, and if h is # less than the degree of j.parent() we recreate j as an element # of Q(j): jpol = PolynomialRing(QQ,'x')([-j,1]) if j in QQ else j.absolute_minpoly() h = jpol.degree() # This will be used as a fall-back if we cannot determine the # result using local data. For this to be necessary there would # have to be very few primes of degree 1 and norm under 1000, # since we only need to find one prime of degree 1, good # reduction for which a_P is nonzero. if method=='old': if h>100: raise NotImplementedError("CM data only available for class numbers up to 100") for d,f in cm_orders(h): if jpol == hilbert_class_polynomial(d*f**2): return True, (d,f) return False, None # replace j by a clone whose parent is Q(j), if necessary: K = j.parent() if h < K.absolute_degree(): K = NumberField(jpol, 'j') j = K.gen() # Construct an elliptic curve with j-invariant j, with # integral model: from sage.schemes.elliptic_curves.all import EllipticCurve E = EllipticCurve(j=j).integral_model() D = E.discriminant() prime_bound = 1000 # test primes of degree 1 up to this norm max_primes = 20 # test at most this many primes num_prime = 0 cmd = 0 cmf = 0 # Test primes of good reduction. If E has CM then for half the # primes P we will have a_P=0, and for all other prime P the CM # field is Q(sqrt(a_P^2-4N(P))). Hence if these fields are # different for two primes then E does not have CM. If they are # all equal for the primes tested, then we have a candidate CM # field. Moreover the discriminant of the endomorphism ring # divides all the values a_P^2-4N(P), since that is the # discriminant of the order containing the Frobenius at P. So we # end up with a finite number (usually one) of candidate # discriminats to test. Each is tested by checking that its class # number is h, and if so then that j is a root of its Hilbert # class polynomial. In practice non CM curves will be eliminated # by the local test at a small number of primes (probably just 2). for P in K.primes_of_degree_one_iter(prime_bound): if num_prime > max_primes: if cmd: # we have a candidate CM field already break else: # we need to try more primes max_primes *=2 if D.valuation(P)>0: # skip bad primes continue aP = E.reduction(P).trace_of_frobenius() if aP == 0: # skip supersingular primes continue num_prime += 1 DP = aP**2 - 4*P.norm() dP = DP.squarefree_part() fP = ZZ(DP//dP).isqrt() if cmd==0: # first one, so store d and f cmd = dP cmf = fP elif cmd != dP: # inconsistent with previous return False, None else: # consistent d, so update f cmf = cmf.gcd(fP) if cmd==0: # no conclusion, we found no degree 1 primes, revert to old method return is_cm_j_invariant(j, method='old') # it looks like cm by disc cmd * f**2 where f divides cmf if cmd%4!=1: cmd = cmd*4 cmf = cmf//2 # Now we must check if h(cmd*f**2)==h for f|cmf; if so we check # whether j is a root of the associated Hilbert class polynomial. for f in cmf.divisors(): # only positive divisors d = cmd*f**2 if h != d.class_number(): continue pol = hilbert_class_polynomial(d) if pol(j)==0: return True, (cmd,f) return False, None
def _solve_mod_prime_power(eqns, p, m, vars): r""" Internal help function for solve_mod, does little checking since it expects solve_mod to do that Return all solutions to an equation or list of equations modulo p^m. Each equation must involve only polynomials in 1 or many variables. The solutions are returned as `n`-tuples, where `n` is the number of variables in vars. INPUT: - ``eqns`` - equation or list of equations - ``p`` - a prime - ``i`` - an integer > 0 - ``vars`` - a list of variables to solve for EXAMPLES:: sage: var('x,y') (x, y) sage: solve_mod([x^2 + 2 == x, x^2 + y == y^2], 14) [(4, 2), (4, 6), (4, 9), (4, 13)] sage: solve_mod([x^2 == 1, 4*x == 11], 15) [(14,)] Fermat's equation modulo 3 with exponent 5:: sage: var('x,y,z') (x, y, z) sage: solve_mod([x^5 + y^5 == z^5], 3) [(0, 0, 0), (0, 1, 1), (0, 2, 2), (1, 0, 1), (1, 1, 2), (1, 2, 0), (2, 0, 2), (2, 1, 0), (2, 2, 1)] We solve a simple equation modulo 2:: sage: x,y = var('x,y') sage: solve_mod([x == y], 2) [(0, 0), (1, 1)] .. warning:: Currently this constructs possible solutions by building up from the smallest prime factor of the modulus. The interface is good, but the algorithm is horrible if the modulus isn't the product of many small primes! Sage *does* have the ability to do something much faster in certain cases at least by using the Chinese Remainder Theorem, Groebner basis, linear algebra techniques, etc. But for a lot of toy problems this function as is might be useful. At the very least, it establishes an interface. TESTS: Confirm we can reproduce the first few terms of :oeis:`A187719`:: sage: from sage.symbolic.relation import _solve_mod_prime_power sage: [sorted(_solve_mod_prime_power([x^2==41], 10, i, [x]))[0][0] for i in [1..13]] [1, 21, 71, 1179, 2429, 47571, 1296179, 8703821, 26452429, 526452429, 13241296179, 19473547571, 2263241296179] """ from sage.rings.all import Integers, PolynomialRing from sage.modules.all import vector from sage.misc.all import cartesian_product_iterator mrunning = 1 ans = [] for mi in range(m): mrunning *= p R = Integers(mrunning) S = PolynomialRing(R, len(vars), vars) eqns_mod = [S(eq) for eq in eqns] if mi == 0: possibles = cartesian_product_iterator( [range(len(R)) for _ in range(len(vars))]) else: shifts = cartesian_product_iterator( [range(p) for _ in range(len(vars))]) pairs = cartesian_product_iterator([shifts, ans]) possibles = (tuple(vector(t) + vector(shift) * (mrunning // p)) for shift, t in pairs) ans = list(t for t in possibles if all(e(*t) == 0 for e in eqns_mod)) if not ans: return ans return ans
def has_rational_point(self, point = False, algorithm = 'default', read_cache = True): r""" Returns True if and only if the conic ``self`` has a point over its base field `F(t)`, which is a field of rational functions. If ``point`` is True, then returns a second output, which is a rational point if one exists. Points are cached whenever they are found. Cached information is used if and only if ``read_cache`` is True. The default algorithm does not (yet) work for all base fields `F`. In particular, sage is required to have: * an algorithm for finding the square root of elements in finite extensions of `F`; * a factorization and gcd algorithm for `F[t]`; * an algorithm for solving conics over `F`. ALGORITHM: The parameter ``algorithm`` specifies the algorithm to be used: * ``'default'`` -- use a native Sage implementation, based on the algorithm Conic in [HC2006]_. * ``'magma'`` (requires Magma to be installed) -- delegates the task to the Magma computer algebra system. EXAMPLES: We can find points for function fields over (extensions of) `\QQ` and finite fields:: sage: K.<t> = FractionField(PolynomialRing(QQ, 't')) sage: C = Conic(K, [t^2-2, 2*t^3, -2*t^3-13*t^2-2*t+18]) sage: C.has_rational_point(point=True) (True, (-3 : (t + 1)/t : 1)) sage: R.<t> = FiniteField(23)[] sage: C = Conic([2, t^2+1, t^2+5]) sage: C.has_rational_point() True sage: C.has_rational_point(point=True) (True, (5*t : 8 : 1)) sage: F.<i> = QuadraticField(-1) sage: R.<t> = F[] sage: C = Conic([1,i*t,-t^2+4]) sage: C.has_rational_point(point = True) verbose 0 (3369: multi_polynomial_ideal.py, groebner_basis) Warning: falling back to very slow toy implementation. ... (True, (-t - 2*i : -2*i : 1)) It works on non-diagonal conics as well:: sage: K.<t> = QQ[] sage: C = Conic([4, -4, 8, 1, -4, t + 4]) sage: C.has_rational_point(point=True) (True, (1/2 : 1 : 0)) If no point exists output still depends on the argument ``point``:: sage: K.<t> = QQ[] sage: C = Conic(K, [t^2, (t-1), -2*(t-1)]) sage: C.has_rational_point() False sage: C.has_rational_point(point=True) (False, None) Due to limitations in Sage of algorithms we depend on, it is not yet possible to find points on conics over multivariate function fields (see the requirements above):: sage: F.<t1> = FractionField(QQ['t1']) sage: K.<t2> = FractionField(F['t2']) sage: a = K(1) sage: b = 2*t2^2+2*t1*t2-t1^2 sage: c = -3*t2^4-4*t1*t2^3+8*t1^2*t2^2+16*t1^3-t2-48*t1^4 sage: C = Conic([a,b,c]) sage: C.has_rational_point() ... Traceback (most recent call last): ... NotImplementedError: is_square() not implemented for elements of Univariate Quotient Polynomial Ring in tbar over Fraction Field of Univariate Polynomial Ring in t1 over Rational Field with modulus tbar^2 + t1*tbar - 1/2*t1^2 In some cases, the algorithm requires us to be able to solve conics over `F`. In particular, the following does not work:: sage: P.<u> = QQ[] sage: E = P.fraction_field() sage: Q.<Y> = E[] sage: F.<v> = E.extension(Y^2 - u^3 - 1) sage: R.<t> = F[] sage: K = R.fraction_field() sage: C = Conic(K, [u, v, 1]) sage: C.has_rational_point() ... Traceback (most recent call last): ... NotImplementedError: has_rational_point not implemented for conics over base field Univariate Quotient Polynomial Ring in v over Fraction Field of Univariate Polynomial Ring in u over Rational Field with modulus v^2 - u^3 - 1 ``has_rational_point`` fails for some conics over function fields over finite fields, due to :trac:`20003`:: sage: K.<t> = PolynomialRing(GF(7)) sage: C = Conic([5*t^2+4, t^2+3*t+3, 6*t^2+3*t+2, 5*t^2+5, 4*t+3, 4*t^2+t+5]) sage: C.has_rational_point() ... Traceback (most recent call last): ... TypeError: self (=Scheme morphism: From: Projective Conic Curve over Fraction Field of Univariate Polynomial Ring in t over Finite Field of size 7 defined by (5*t^2 + 4)*x^2 + ((6*t^3 + 3*t^2 + 5*t + 5)/(t + 3))*y^2 + ((6*t^6 + 3*t^5 + t^3 + 6*t^2 + 6*t + 2)/(t^4 + t^3 + 4*t^2 + 3*t + 1))*z^2 To: Projective Conic Curve over Fraction Field of Univariate Polynomial Ring in t over Finite Field of size 7 defined by (5*t^2 + 4)*x^2 + (t^2 + 3*t + 3)*x*y + (5*t^2 + 5)*y^2 + (6*t^2 + 3*t + 2)*x*z + (4*t + 3)*y*z + (4*t^2 + t + 5)*z^2 Defn: Defined on coordinates by sending (x : y : z) to (x + ((2*t + 5)/(t + 3))*y + ((3*t^4 + 2*t^3 + 5*t^2 + 5*t + 3)/(t^4 + t^3 + 4*t^2 + 3*t + 1))*z : y + ((6*t^3 + 6*t^2 + 3*t + 6)/(t^3 + 4*t^2 + 2*t + 2))*z : z)) domain must equal right (=Scheme morphism: From: Projective Conic Curve over Fraction Field of Univariate Polynomial Ring in t over Finite Field of size 7 defined by (5*t^3 + 6*t^2 + 3*t + 3)*x^2 + (t + 4)*y^2 + (6*t^7 + 2*t^5 + t^4 + 2*t^3 + 3*t^2 + 6*t + 6)*z^2 To: Projective Conic Curve over Fraction Field of Univariate Polynomial Ring in t over Finite Field of size 7 defined by (5/(t^3 + 4*t^2 + 2*t + 2))*x^2 + (1/(t^3 + 3*t^2 + 5*t + 1))*y^2 + ((6*t^6 + 3*t^5 + t^3 + 6*t^2 + 6*t + 2)/(t^9 + 5*t^8 + t^7 + 6*t^6 + 3*t^5 + 4*t^3 + t^2 + 5*t + 3))*z^2 Defn: Defined on coordinates by sending (x : y : z) to ((t^3 + 4*t^2 + 2*t + 2)*x : (t^2 + 5)*y : (t^5 + 4*t^4 + t^2 + 3*t + 3)*z)) codomain TESTS:: sage: K.<t> = FractionField(PolynomialRing(QQ, 't')) sage: a = (2*t^2 - 3/2*t + 1)/(37/3*t^2 + t - 1/4) sage: b = (1/2*t^2 + 1/3)/(-73*t^2 - 2*t + 11/4) sage: c = (6934/3*t^6 + 8798/3*t^5 - 947/18*t^4 + 3949/9*t^3 + 20983/18*t^2 + 28/3*t - 131/3)/(-2701/3*t^4 - 293/3*t^3 + 301/6*t^2 + 13/4*t - 11/16) sage: C = Conic([a,b,c]) sage: C.has_rational_point(point=True) (True, (4*t + 4 : 2*t + 2 : 1)) A long time test:: sage: K.<t> = FractionField(PolynomialRing(QQ, 't')) sage: a = (-1/3*t^6 - 14*t^5 - 1/4*t^4 + 7/2*t^2 - 1/2*t - 1)/(24/5*t^6 - t^5 - 1/4*t^4 + t^3 - 3*t^2 + 8/5*t + 5) sage: b = (-3*t^3 + 8*t + 1/2)/(-1/3*t^3 + 3/2*t^2 + 1/12*t + 1/2) sage: c = (1232009/225*t^25 - 1015925057/8100*t^24 + 1035477411553/1458000*t^23 + 7901338091/30375*t^22 - 1421379260447/729000*t^21 + 266121260843/972000*t^20 + 80808723191/486000*t^19 - 516656082523/972000*t^18 + 21521589529/40500*t^17 + 4654758997/21600*t^16 - 20064038625227/9720000*t^15 - 173054270347/324000*t^14 + 536200870559/540000*t^13 - 12710739349/50625*t^12 - 197968226971/135000*t^11 - 134122025657/810000*t^10 + 22685316301/120000*t^9 - 2230847689/21600*t^8 - 70624099679/270000*t^7 - 4298763061/270000*t^6 - 41239/216000*t^5 - 13523/36000*t^4 + 493/36000*t^3 + 83/2400*t^2 + 1/300*t + 1/200)/(-27378/125*t^17 + 504387/500*t^16 - 97911/2000*t^15 + 1023531/4000*t^14 + 1874841/8000*t^13 + 865381/12000*t^12 + 15287/375*t^11 + 6039821/6000*t^10 + 599437/1500*t^9 + 18659/250*t^8 + 1218059/6000*t^7 + 2025127/3000*t^6 + 1222759/6000*t^5 + 38573/200*t^4 + 8323/125*t^3 + 15453/125*t^2 + 17031/500*t + 441/10) sage: C = Conic([a,b,c]) sage: C.has_rational_point(point = True) # long time (4 seconds) (True, ((-2/117*t^8 + 304/1053*t^7 + 40/117*t^6 - 1/27*t^5 - 110/351*t^4 - 2/195*t^3 + 11/351*t^2 + 1/117)/(t^4 + 2/39*t^3 + 4/117*t^2 + 2/39*t + 14/39) : -5/3*t^4 + 19*t^3 : 1)) """ from constructor import Conic if read_cache: if self._rational_point is not None: return (True, self._rational_point) if point else True if algorithm != 'default': return ProjectiveConic_field.has_rational_point(self, point, algorithm, read_cache) # Default algorithm if self.base_ring().characteristic() == 2: raise NotImplementedError("has_rational_point not implemented \ for function field of characteristic 2.") new_conic, transformation, inverse = self.diagonalization() coeff = new_conic.coefficients() if coeff[0] == 0: return (True, transformation([1,0,0])) if point else True elif coeff[3] == 0: return (True, transformation([0,1,0])) if point else True elif coeff[5] == 0: return (True, transformation([0,0,1])) if point else True # We save the coefficients of the reduced form in coeff # A zero of the reduced conic can be multiplied by multipliers # to get a zero of the old conic (coeff, multipliers) = new_conic._reduce_conic() new_conic = Conic(coeff) transformation = transformation \ * new_conic.hom(diagonal_matrix(multipliers)) if coeff[0].degree() % 2 == coeff[1].degree() % 2 and \ coeff[1].degree() % 2 == coeff[2].degree() % 2: case = 0 else: case = 1 t, = self.base_ring().base().gens() # t in F[t] supp = [] roots = [[], [], []] remove = None # loop through the coefficients and find a root of f_i (as in # [HC2006]) modulo each element in the coefficients' support for i in (0,1,2): supp.append(list(coeff[i].factor())) for p in supp[i]: if p[1] != 1: raise ValueError("Expected factor of exponent 1.") # Convert to monic factor x = p[0]/list(p[0])[-1] N = p[0].base_ring().extension(x, 'tbar') R = PolynomialRing(N, 'u') u, = R.gens() # If p[0] has degree 1, sage might forget the "defining # polynomial" of N, so we define our own modulo operation if p[0].degree() == 1: mod = t.parent().hom([-x[0]]) else: mod = N if i == 0: x = -mod(coeff[2])/mod(coeff[1]) elif i == 1: x = -mod(coeff[0])/mod(coeff[2]) else: x = -mod(coeff[1])/mod(coeff[0]) if x.is_square(): root = N(x.sqrt()) else: return (False, None) if point else False # if case == 0 and p[0] has degree 1, we switch to case # 1 and remove this factor out of the support. In [HC2006] # this is done later, in FindPoint. if case == 0 and p[0].degree() == 1: case = 1 # remove later so the loop iterator stays in place. remove = (i,p) else: roots[i].append(root) if remove: supp[remove[0]].remove(remove[1]) supp = [[p[0] for p in supp[i]] for i in (0,1,2)] if case == 0: # Find a solution of (5) in [HC2006] leading_conic = Conic(self.base_ring().base_ring(), [coeff[0].leading_coefficient(), coeff[1].leading_coefficient(), coeff[2].leading_coefficient()]) has_point = leading_conic.has_rational_point(True) if has_point[0]: if point: pt = new_conic.find_point(supp, roots, case, has_point[1]) else: pt = True return (True, transformation(pt)) if point else True else: return (False, None) if point else False # case == 1: if point: pt = new_conic.find_point(supp, roots, case) else: pt = True return (True, transformation(pt)) if point else True
def root_extension_field(self, D): r""" Return the quadratic extension field of the base field by the square root of the given discriminant ``D``. INPUT: - ``D`` -- An element of the base ring of ``self`` corresponding to a discriminant. OUTPUT: A relative (at most quadratic) extension to the base field of self in the variable ``e`` which corresponds to ``sqrt(D)``. If the extension degree is ``1`` then the base field is returned. The correct embedding is the positive resp. positive imaginary one. Unfortunately no default embedding can be specified for relative number fields yet. EXAMPLES:: sage: from sage.modular.modform_hecketriangle.hecke_triangle_groups import HeckeTriangleGroup sage: G = HeckeTriangleGroup(n=infinity) sage: G.root_extension_field(32) Number Field in e with defining polynomial x^2 - 32 sage: G.root_extension_field(-4) Number Field in e with defining polynomial x^2 + 4 sage: G.root_extension_field(4) == G.base_field() True sage: G = HeckeTriangleGroup(n=7) sage: lam = G.lam() sage: D = 4*lam^2 + 4*lam - 4 sage: G.root_extension_field(D) Number Field in e with defining polynomial x^2 - 4*lam^2 - 4*lam + 4 over its base field sage: G.root_extension_field(4) == G.base_field() True sage: D = lam^2 - 4 sage: G.root_extension_field(D) Number Field in e with defining polynomial x^2 - lam^2 + 4 over its base field """ K = self.base_field() x = PolynomialRing(K, 'x').gen() D = self.base_ring()(D) if D.is_square(): return K else: # unfortunately we can't set embeddings for relative extensions :-( # return K.extension(x**2 - D, 'e', embedding=AA(D).sqrt()) L = K.extension(x**2 - D, 'e') #e = AA(D).sqrt() #emb = L.hom([e]) #L._unset_embedding() #L.register_embedding(emb) #return NumberField(L.absolute_polynomial(), 'e', structure=AbsoluteFromRelative(L), embedding=(???)) return L
def parametrization(self, point=None, morphism=True): r""" Return a parametrization `f` of ``self`` together with the inverse of `f`. If ``point`` is specified, then that point is used for the parametrization. Otherwise, use ``self.rational_point()`` to find a point. If ``morphism`` is True, then `f` is returned in the form of a Scheme morphism. Otherwise, it is a tuple of polynomials that gives the parametrization. EXAMPLES: An example over a finite field :: sage: c = Conic(GF(2), [1,1,1,1,1,0]) sage: c.parametrization() (Scheme morphism: From: Projective Space of dimension 1 over Finite Field of size 2 To: Projective Conic Curve over Finite Field of size 2 defined by x^2 + x*y + y^2 + x*z + y*z Defn: Defined on coordinates by sending (x : y) to (x*y + y^2 : x^2 + x*y : x^2 + x*y + y^2), Scheme morphism: From: Projective Conic Curve over Finite Field of size 2 defined by x^2 + x*y + y^2 + x*z + y*z To: Projective Space of dimension 1 over Finite Field of size 2 Defn: Defined on coordinates by sending (x : y : z) to (y : x)) An example with ``morphism = False`` :: sage: R.<x,y,z> = QQ[] sage: C = Curve(7*x^2 + 2*y*z + z^2) sage: (p, i) = C.parametrization(morphism = False); (p, i) ([-2*x*y, 7*x^2 + y^2, -2*y^2], [-1/2*x, -1/2*z]) sage: C.defining_polynomial()(p) 0 sage: i[0](p) / i[1](p) x/y A ``ValueError`` is raised if ``self`` has no rational point :: sage: C = Conic(x^2 + y^2 + 7*z^2) sage: C.parametrization() Traceback (most recent call last): ... ValueError: Conic Projective Conic Curve over Rational Field defined by x^2 + y^2 + 7*z^2 has no rational points over Rational Field! A ``ValueError`` is raised if ``self`` is not smooth :: sage: C = Conic(x^2 + y^2) sage: C.parametrization() Traceback (most recent call last): ... ValueError: The conic self (=Projective Conic Curve over Rational Field defined by x^2 + y^2) is not smooth, hence does not have a parametrization. """ if (not self._parametrization is None) and not point: par = self._parametrization else: if not self.is_smooth(): raise ValueError, "The conic self (=%s) is not smooth, hence does not have a parametrization." % self if point == None: point = self.rational_point() point = Sequence(point) B = self.base_ring() Q = PolynomialRing(B, 'x,y') [x, y] = Q.gens() gens = self.ambient_space().gens() P = PolynomialRing(B, 4, ['X', 'Y', 'T0', 'T1']) [X, Y, T0, T1] = P.gens() c3 = [j for j in range(2,-1,-1) if point[j] != 0][0] c1 = [j for j in range(3) if j != c3][0] c2 = [j for j in range(3) if j != c3 and j != c1][0] L = [0,0,0] L[c1] = Y*T1*point[c1] + Y*T0 L[c2] = Y*T1*point[c2] + X*T0 L[c3] = Y*T1*point[c3] bezout = P(self.defining_polynomial()(L) / T0) t = [bezout([x,y,0,-1]),bezout([x,y,1,0])] par = (tuple([Q(p([x,y,t[0],t[1]])/y) for p in L]), tuple([gens[m]*point[c3]-gens[c3]*point[m] for m in [c2,c1]])) if self._parametrization is None: self._parametrization = par if not morphism: return par P1 = ProjectiveSpace(self.base_ring(), 1, 'x,y') return P1.hom(par[0],self), self.Hom(P1)(par[1], check = False)
def ubs(f): r""" Given a sextic form `f`, return a dictionary of the invariants of Mestre, p 317 [MJ1991]_. `f` may be homogeneous in two variables or inhomogeneous in one. EXAMPLES:: sage: from sage.schemes.hyperelliptic_curves.invariants import ubs sage: x = QQ['x'].0 sage: ubs(x^6 + 1) {'A': 2, 'B': 2/3, 'C': -2/9, 'D': 0, 'Delta': -2/3*x^2*h^2, 'f': x^6 + h^6, 'i': 2*x^2*h^2, 'y1': 0, 'y2': 0, 'y3': 0} sage: R.<u, v> = QQ[] sage: ubs(u^6 + v^6) {'A': 2, 'B': 2/3, 'C': -2/9, 'D': 0, 'Delta': -2/3*u^2*v^2, 'f': u^6 + v^6, 'i': 2*u^2*v^2, 'y1': 0, 'y2': 0, 'y3': 0} sage: R.<t> = GF(31)[] sage: ubs(t^6 + 2*t^5 + t^2 + 3*t + 1) {'A': 0, 'B': -12, 'C': -15, 'D': -15, 'Delta': -10*t^4 + 12*t^3*h + 7*t^2*h^2 - 5*t*h^3 + 2*h^4, 'f': t^6 + 2*t^5*h + t^2*h^4 + 3*t*h^5 + h^6, 'i': -4*t^4 + 10*t^3*h + 2*t^2*h^2 - 9*t*h^3 - 7*h^4, 'y1': 4*t^2 - 10*t*h - 13*h^2, 'y2': 6*t^2 - 4*t*h + 2*h^2, 'y3': 4*t^2 - 4*t*h - 9*h^2} """ ub = Ueberschiebung if f.parent().ngens() == 1: f = PolynomialRing(f.parent().base_ring(), 1, f.parent().variable_name())(f) x1, x2 = f.homogenize().parent().gens() f = sum([f[i] * x1**i * x2**(6 - i) for i in range(7)]) U = {} U['f'] = f U['i'] = ub(f, f, 4) U['Delta'] = ub(U['i'], U['i'], 2) U['y1'] = ub(f, U['i'], 4) U['y2'] = ub(U['i'], U['y1'], 2) U['y3'] = ub(U['i'], U['y2'], 2) U['A'] = ub(f, f, 6) U['B'] = ub(U['i'], U['i'], 4) U['C'] = ub(U['i'], U['Delta'], 4) U['D'] = ub(U['y3'], U['y1'], 2) return U
def parametrization(self, point=None, morphism=True): r""" Return a parametrization `f` of ``self`` together with the inverse of `f`. If ``point`` is specified, then that point is used for the parametrization. Otherwise, use ``self.rational_point()`` to find a point. If ``morphism`` is True, then `f` is returned in the form of a Scheme morphism. Otherwise, it is a tuple of polynomials that gives the parametrization. ALGORITHM: Uses the PARI/GP function ``qfparam``. EXAMPLES :: sage: c = Conic([1,1,-1]) sage: c.parametrization() (Scheme morphism: From: Projective Space of dimension 1 over Rational Field To: Projective Conic Curve over Rational Field defined by x^2 + y^2 - z^2 Defn: Defined on coordinates by sending (x : y) to (2*x*y : x^2 - y^2 : x^2 + y^2), Scheme morphism: From: Projective Conic Curve over Rational Field defined by x^2 + y^2 - z^2 To: Projective Space of dimension 1 over Rational Field Defn: Defined on coordinates by sending (x : y : z) to (1/2*x : -1/2*y + 1/2*z)) An example with ``morphism = False`` :: sage: R.<x,y,z> = QQ[] sage: C = Curve(7*x^2 + 2*y*z + z^2) sage: (p, i) = C.parametrization(morphism = False); (p, i) ([-2*x*y, x^2 + 7*y^2, -2*x^2], [-1/2*x, 1/7*y + 1/14*z]) sage: C.defining_polynomial()(p) 0 sage: i[0](p) / i[1](p) x/y A ``ValueError`` is raised if ``self`` has no rational point :: sage: C = Conic(x^2 + 2*y^2 + z^2) sage: C.parametrization() Traceback (most recent call last): ... ValueError: Conic Projective Conic Curve over Rational Field defined by x^2 + 2*y^2 + z^2 has no rational points over Rational Field! A ``ValueError`` is raised if ``self`` is not smooth :: sage: C = Conic(x^2 + y^2) sage: C.parametrization() Traceback (most recent call last): ... ValueError: The conic self (=Projective Conic Curve over Rational Field defined by x^2 + y^2) is not smooth, hence does not have a parametrization. """ if (not self._parametrization is None) and not point: par = self._parametrization else: if not self.is_smooth(): raise ValueError("The conic self (=%s) is not smooth, hence does not have a parametrization." % self) if point is None: point = self.rational_point() point = Sequence(point) Q = PolynomialRing(QQ, 'x,y') [x, y] = Q.gens() gens = self.ambient_space().gens() M = self.symmetric_matrix() M *= lcm([ t.denominator() for t in M.list() ]) par1 = qfparam(M, point) B = Matrix([[par1[i][j] for j in range(3)] for i in range(3)]) # self is in the image of B and does not lie on a line, # hence B is invertible A = B.inverse() par2 = [sum([A[i,j]*gens[j] for j in range(3)]) for i in [1,0]] par = ([Q(pol(x/y)*y**2) for pol in par1], par2) if self._parametrization is None: self._parametrization = par if not morphism: return par P1 = ProjectiveSpace(self.base_ring(), 1, 'x,y') return P1.hom(par[0],self), self.Hom(P1)(par[1], check = False)
def mod5family(a, b): """ Formulas for computing the family of elliptic curves with congruent mod-5 representation. EXAMPLES:: sage: from sage.schemes.elliptic_curves.mod5family import mod5family sage: mod5family(0,1) Elliptic Curve defined by y^2 = x^3 + (t^30+30*t^29+435*t^28+4060*t^27+27405*t^26+142506*t^25+593775*t^24+2035800*t^23+5852925*t^22+14307150*t^21+30045015*t^20+54627300*t^19+86493225*t^18+119759850*t^17+145422675*t^16+155117520*t^15+145422675*t^14+119759850*t^13+86493225*t^12+54627300*t^11+30045015*t^10+14307150*t^9+5852925*t^8+2035800*t^7+593775*t^6+142506*t^5+27405*t^4+4060*t^3+435*t^2+30*t+1) over Fraction Field of Univariate Polynomial Ring in t over Rational Field """ J = 4 * a**3 / (4 * a**3 + 27 * b**2) alpha = [0 for _ in range(21)] alpha[0] = 1 alpha[1] = 0 alpha[2] = 190 * (J - 1) alpha[3] = -2280 * (J - 1)**2 alpha[4] = 855 * (J - 1)**2 * (-17 + 16 * J) alpha[5] = 3648 * (J - 1)**3 * (17 - 9 * J) alpha[6] = 11400 * (J - 1)**3 * (17 - 8 * J) alpha[7] = -27360 * (J - 1)**4 * (17 + 26 * J) alpha[8] = 7410 * (J - 1)**4 * (-119 - 448 * J + 432 * J**2) alpha[9] = 79040 * (J - 1)**5 * (17 + 145 * J - 108 * J**2) alpha[10] = 8892 * (J - 1)**5 * (187 + 2640 * J - 5104 * J**2 + 1152 * J**3) alpha[11] = 98800 * (J - 1)**6 * (-17 - 388 * J + 864 * J**2) alpha[12] = 7410 * (J - 1)**6 * (-187 - 6160 * J + 24464 * J**2 - 24192 * J**3) alpha[13] = 54720 * (J - 1)**7 * (17 + 795 * J - 3944 * J**2 + 9072 * J**3) alpha[14] = 2280 * (J - 1)**7 * (221 + 13832 * J - 103792 * J**2 + 554112 * J**3 - 373248 * J**4) alpha[15] = 1824 * (J - 1)**8 * (-119 - 9842 * J + 92608 * J**2 - 911520 * J**3 + 373248 * J**4) alpha[16] = 4275 * (J - 1)**8 * (-17 - 1792 * J + 23264 * J**2 - 378368 * J**3 + 338688 * J**4) alpha[17] = 18240 * (J - 1)**9 * (1 + 133 * J - 2132 * J**2 + 54000 * J**3 - 15552 * J**4) alpha[18] = 190 * (J - 1)**9 * (17 + 2784 * J - 58080 * J**2 + 2116864 * J**3 - 946944 * J**4 + 2985984 * J**5) alpha[19] = 360 * (J - 1)**10 * (-1 + 28 * J - 1152 * J**2) * ( 1 + 228 * J + 176 * J**2 + 1728 * J**3) alpha[20] = (J - 1)**10 * (-19 - 4560 * J + 144096 * J**2 - 9859328 * J**3 - 8798976 * J**4 - 226934784 * J**5 + 429981696 * J**6) beta = [0 for _ in range(31)] beta[0] = 1 beta[1] = 30 beta[2] = -435 * (J - 1) beta[3] = 580 * (J - 1) * (-7 + 9 * J) beta[4] = 3915 * (J - 1)**2 * (7 - 8 * J) beta[5] = 1566 * (J - 1)**2 * (91 - 78 * J + 48 * J**2) beta[6] = -84825 * (J - 1)**3 * (7 + 16 * J) beta[7] = 156600 * (J - 1)**3 * (-13 - 91 * J + 92 * J**2) beta[8] = 450225 * (J - 1)**4 * (13 + 208 * J - 144 * J**2) beta[9] = 100050 * (J - 1)**4 * (143 + 4004 * J - 5632 * J**2 + 1728 * J**3) beta[10] = 30015 * (J - 1)**5 * (-1001 - 45760 * J + 44880 * J**2 - 6912 * J**3) beta[11] = 600300 * (J - 1)**5 * (-91 - 6175 * J + 9272 * J**2 - 2736 * J**3) beta[12] = 950475 * (J - 1)**6 * (91 + 8840 * J - 7824 * J**2) beta[13] = 17108550 * (J - 1)**6 * (7 + 926 * J - 1072 * J**2 + 544 * J**3) beta[14] = 145422675 * (J - 1)**7 * (-1 - 176 * J + 48 * J**2 - 384 * J**3) beta[15] = 155117520 * (J - 1)**8 * (1 + 228 * J + 176 * J**2 + 1728 * J**3) beta[16] = 145422675 * (J - 1)**8 * (1 + 288 * J + 288 * J**2 + 5120 * J**3 - 6912 * J**4) beta[17] = 17108550 * (J - 1)**8 * (7 + 2504 * J + 3584 * J**2 + 93184 * J**3 - 283392 * J**4 + 165888 * J**5) beta[18] = 950475 * (J - 1)**9 * (-91 - 39936 * J - 122976 * J**2 - 2960384 * J**3 + 11577600 * J**4 - 5971968 * J**5) beta[19] = 600300 * (J - 1)**9 * (-91 - 48243 * J - 191568 * J**2 - 6310304 * J**3 + 40515072 * J**4 - 46455552 * J**5 + 11943936 * J**6) beta[20] = 30015 * (J - 1)**10 * (1001 + 634920 * J + 3880800 * J**2 + 142879744 * J**3 - 1168475904 * J**4 + 1188919296 * J**5 - 143327232 * J**6) beta[21] = 100050 * (J - 1)**10 * (143 + 107250 * J + 808368 * J**2 + 38518336 * J**3 - 451953408 * J**4 + 757651968 * J**5 - 367276032 * J**6) beta[22] = 450225 * (J - 1)**11 * (-13 - 11440 * J - 117216 * J**2 - 6444800 * J**3 + 94192384 * J**4 - 142000128 * J**5 + 95551488 * J**6) beta[23] = 156600 * (J - 1)**11 * ( -13 - 13299 * J - 163284 * J**2 - 11171552 * J**3 + 217203840 * J**4 - 474406656 * J**5 + 747740160 * J**6 - 429981696 * J**7) beta[24] = 6525*(J - 1)**12*(91 + 107536*J + 1680624*J**2 + 132912128*J**3 -\ 3147511552*J**4 + 6260502528*J**5 - 21054173184*J**6 + 10319560704*J**7) beta[25] = 1566*(J - 1)**12*(91 + 123292*J + 2261248*J**2 + 216211904*J**3 - \ 6487793920*J**4 + 17369596928*J**5 - 97854234624*J**6 + 96136740864*J**7 - 20639121408*J**8) beta[26] = 3915*(J - 1)**13*(-7 - 10816*J - 242352*J**2 - 26620160*J**3 + 953885440*J**4 - \ 2350596096*J**5 + 26796552192*J**6 - 13329432576*J**7) beta[27] = 580*(J - 1)**13*(-7 - 12259*J - 317176*J**2 - 41205008*J**3 + \ 1808220160*J**4 - 5714806016*J**5 + 93590857728*J**6 - 70131806208*J**7 - 36118462464*J**8) beta[28] = 435*(J - 1)**14*(1 + 1976*J + 60720*J**2 + 8987648*J**3 - 463120640*J**4 + 1359157248*J**5 - \ 40644882432*J**6 - 5016453120*J**7 + 61917364224*J**8) beta[29] = 30*(J - 1)**14*(1 + 2218*J + 77680*J**2 + 13365152*J**3 - \ 822366976*J**4 + 2990693888*J**5 - 118286217216*J**6 - 24514928640*J**7 + 509958291456*J**8 - 743008370688*J**9) beta[30] = (J - 1)**15*(-1 - 2480*J - 101040*J**2 - 19642496*J**3 + 1399023872*J**4 - \ 4759216128*J**5 + 315623485440*J**6 + 471904911360*J**7 - 2600529297408*J**8 + 8916100448256*J**9) R = PolynomialRing(QQ, 't') b4 = a * R(alpha) b6 = b * R(beta) c2 = b4 c3 = b6 d = lcm(c2.denominator(), c3.denominator()) F = FractionField(R) E = EllipticCurve(F, [c2 * d**4, c3 * d**6]) return E
def gauss_sum(a, p, f, prec=20, factored=False, algorithm='pari', parent=None): r""" Return the Gauss sum `g_q(a)` as a `p`-adic number. The Gauss sum `g_q(a)` is defined by .. MATH:: g_q(a)= \sum_{u\in F_q^*} \omega(u)^{-a} \zeta_q^u, where `q = p^f`, `\omega` is the Teichmüller character and `\zeta_q` is some arbitrary choice of primitive `q`-th root of unity. The computation is adapted from the main theorem in Alain Robert's paper *The Gross-Koblitz formula revisited*, Rend. Sem. Mat. Univ. Padova 105 (2001), 157--170. Let `p` be a prime, `f` a positive integer, `q=p^f`, and `\pi` be the unique root of `f(x) = x^{p-1}+p` congruent to `\zeta_p - 1` modulo `(\zeta_p - 1)^2`. Let `0\leq a < q-1`. Then the Gross-Koblitz formula gives us the value of the Gauss sum `g_q(a)` as a product of `p`-adic Gamma functions as follows: .. MATH:: g_q(a) = -\pi^s \prod_{0\leq i < f} \Gamma_p(a^{(i)}/(q-1)), where `s` is the sum of the digits of `a` in base `p` and the `a^{(i)}` have `p`-adic expansions obtained from cyclic permutations of that of `a`. INPUT: - ``a`` -- integer - ``p`` -- prime - ``f`` -- positive integer - ``prec`` -- positive integer (optional, 20 by default) - ``factored`` - boolean (optional, False by default) - ``algorithm`` - flag passed to p-adic Gamma function (optional, "pari" by default) OUTPUT: If ``factored`` is ``False``, returns a `p`-adic number in an Eisenstein extension of `\QQ_p`. This number has the form `pi^e * z` where `pi` is as above, `e` is some nonnegative integer, and `z` is an element of `\ZZ_p`; if ``factored`` is ``True``, the pair `(e,z)` is returned instead, and the Eisenstein extension is not formed. .. NOTE:: This is based on GP code written by Adriana Salerno. EXAMPLES: In this example, we verify that `g_3(0) = -1`:: sage: from sage.rings.padics.misc import gauss_sum sage: -gauss_sum(0,3,1) 1 + O(pi^40) Next, we verify that `g_5(a) g_5(-a) = 5 (-1)^a`:: sage: from sage.rings.padics.misc import gauss_sum sage: gauss_sum(2,5,1)^2-5 O(pi^84) sage: gauss_sum(1,5,1)*gauss_sum(3,5,1)+5 O(pi^84) Finally, we compute a non-trivial value:: sage: from sage.rings.padics.misc import gauss_sum sage: gauss_sum(2,13,2) 6*pi^2 + 7*pi^14 + 11*pi^26 + 3*pi^62 + 6*pi^74 + 3*pi^86 + 5*pi^98 + pi^110 + 7*pi^134 + 9*pi^146 + 4*pi^158 + 6*pi^170 + 4*pi^194 + pi^206 + 6*pi^218 + 9*pi^230 + O(pi^242) sage: gauss_sum(2,13,2,prec=5,factored=True) (2, 6 + 6*13 + 10*13^2 + O(13^5)) .. SEEALSO:: - :func:`sage.arith.misc.gauss_sum` for general finite fields - :meth:`sage.modular.dirichlet.DirichletCharacter.gauss_sum` for prime finite fields - :meth:`sage.modular.dirichlet.DirichletCharacter.gauss_sum_numerical` for prime finite fields """ from sage.rings.padics.factory import Zp from sage.rings.all import PolynomialRing q = p**f a = a % (q - 1) if parent is None: R = Zp(p, prec) else: R = parent out = -R.one() if a != 0: t = R(1 / (q - 1)) for i in range(f): out *= (a * t).gamma(algorithm) a = (a * p) % (q - 1) s = sum(a.digits(base=p)) if factored: return s, out X = PolynomialRing(R, name='X').gen() pi = R.ext(X**(p - 1) + p, names='pi').gen() out *= pi**s return out
def coleman_integrals_on_basis(self, P, Q, algorithm=None): r""" Computes the Coleman integrals `\{\int_P^Q x^i dx/2y \}_{i=0}^{2g-1}` INPUT: - P point on self - Q point on self - algorithm (optional) = None (uses Frobenius) or teichmuller (uses Teichmuller points) OUTPUT: the Coleman integrals `\{\int_P^Q x^i dx/2y \}_{i=0}^{2g-1}` EXAMPLES:: sage: K = pAdicField(11, 5) sage: x = polygen(K) sage: C = HyperellipticCurve(x^5 + 33/16*x^4 + 3/4*x^3 + 3/8*x^2 - 1/4*x + 1/16) sage: P = C.lift_x(2) sage: Q = C.lift_x(3) sage: C.coleman_integrals_on_basis(P, Q) (10*11 + 6*11^3 + 2*11^4 + O(11^5), 11 + 9*11^2 + 7*11^3 + 9*11^4 + O(11^5), 3 + 10*11 + 5*11^2 + 9*11^3 + 4*11^4 + O(11^5), 3 + 11 + 5*11^2 + 4*11^4 + O(11^5)) sage: C.coleman_integrals_on_basis(P, Q, algorithm='teichmuller') (10*11 + 6*11^3 + 2*11^4 + O(11^5), 11 + 9*11^2 + 7*11^3 + 9*11^4 + O(11^5), 3 + 10*11 + 5*11^2 + 9*11^3 + 4*11^4 + O(11^5), 3 + 11 + 5*11^2 + 4*11^4 + O(11^5)) :: sage: K = pAdicField(11,5) sage: x = polygen(K) sage: C = HyperellipticCurve(x^5 + 33/16*x^4 + 3/4*x^3 + 3/8*x^2 - 1/4*x + 1/16) sage: P = C.lift_x(11^(-2)) sage: Q = C.lift_x(3*11^(-2)) sage: C.coleman_integrals_on_basis(P, Q) (3*11^3 + 7*11^4 + 4*11^5 + 7*11^6 + 5*11^7 + O(11^8), 3*11 + 10*11^2 + 8*11^3 + 9*11^4 + 7*11^5 + O(11^6), 4*11^-1 + 2 + 6*11 + 6*11^2 + 7*11^3 + O(11^4), 11^-3 + 6*11^-2 + 2*11^-1 + 2 + O(11^2)) :: sage: R = C(0,1/4) sage: a = C.coleman_integrals_on_basis(P,R) # long time (7s on sage.math, 2011) sage: b = C.coleman_integrals_on_basis(R,Q) # long time (9s on sage.math, 2011) sage: c = C.coleman_integrals_on_basis(P,Q) # long time sage: a+b == c # long time True :: sage: R.<x> = QQ['x'] sage: H = HyperellipticCurve(x^3-10*x+9) sage: K = Qp(5,8) sage: HK = H.change_ring(K) sage: S = HK(1,0) sage: P = HK(0,3) sage: T = HK(0,1,0) sage: Q = HK.lift_x(5^-2) sage: R = HK.lift_x(4*5^-2) sage: HK.coleman_integrals_on_basis(S,P) (2*5^2 + 5^4 + 5^5 + 3*5^6 + 3*5^7 + 2*5^8 + O(5^9), 5 + 2*5^2 + 4*5^3 + 2*5^4 + 3*5^6 + 4*5^7 + 2*5^8 + O(5^9)) sage: HK.coleman_integrals_on_basis(T,P) (2*5^2 + 5^4 + 5^5 + 3*5^6 + 3*5^7 + 2*5^8 + O(5^9), 5 + 2*5^2 + 4*5^3 + 2*5^4 + 3*5^6 + 4*5^7 + 2*5^8 + O(5^9)) sage: HK.coleman_integrals_on_basis(P,S) == -HK.coleman_integrals_on_basis(S,P) True sage: HK.coleman_integrals_on_basis(S,Q) (4*5 + 4*5^2 + 4*5^3 + O(5^4), 5^-1 + O(5^3)) sage: HK.coleman_integrals_on_basis(Q,R) (4*5 + 2*5^2 + 2*5^3 + 2*5^4 + 5^5 + 5^6 + 5^7 + 3*5^8 + O(5^9), 2*5^-1 + 4 + 4*5 + 4*5^2 + 4*5^3 + 2*5^4 + 3*5^5 + 2*5^6 + O(5^7)) sage: HK.coleman_integrals_on_basis(S,R) == HK.coleman_integrals_on_basis(S,Q) + HK.coleman_integrals_on_basis(Q,R) True sage: HK.coleman_integrals_on_basis(T,T) (0, 0) sage: HK.coleman_integrals_on_basis(S,T) (0, 0) AUTHORS: - Robert Bradshaw (2007-03): non-Weierstrass points - Jennifer Balakrishnan and Robert Bradshaw (2010-02): Weierstrass points """ import sage.schemes.hyperelliptic_curves.monsky_washnitzer as monsky_washnitzer from sage.misc.profiler import Profiler prof = Profiler() prof("setup") K = self.base_ring() p = K.prime() prec = K.precision_cap() g = self.genus() dim = 2 * g V = VectorSpace(K, dim) #if P or Q is Weierstrass, use the Frobenius algorithm if self.is_weierstrass(P): if self.is_weierstrass(Q): return V(0) else: PP = None QQ = Q TP = None TQ = self.frobenius(Q) elif self.is_weierstrass(Q): PP = P QQ = None TQ = None TP = self.frobenius(P) elif self.is_same_disc(P, Q): return self.tiny_integrals_on_basis(P, Q) elif algorithm == 'teichmuller': prof("teichmuller") PP = TP = self.teichmuller(P) QQ = TQ = self.teichmuller(Q) evalP, evalQ = TP, TQ else: prof("frobPQ") TP = self.frobenius(P) TQ = self.frobenius(Q) PP, QQ = P, Q prof("tiny integrals") if TP is None: P_to_TP = V(0) else: if TP is not None: TPv = (TP[0]**g / TP[1]).valuation() xTPv = TP[0].valuation() else: xTPv = TPv = +Infinity if TQ is not None: TQv = (TQ[0]**g / TQ[1]).valuation() xTQv = TQ[0].valuation() else: xTQv = TQv = +Infinity offset = (2 * g - 1) * max(TPv, TQv) if offset == +Infinity: offset = (2 * g - 1) * min(TPv, TQv) if (offset > prec and (xTPv < 0 or xTQv < 0) and (self.residue_disc(P) == self.change_ring(GF(p))(0, 1, 0) or self.residue_disc(Q) == self.change_ring(GF(p))(0, 1, 0))): newprec = offset + prec K = pAdicField(p, newprec) A = PolynomialRing(RationalField(), 'x') f = A(self.hyperelliptic_polynomials()[0]) from sage.schemes.hyperelliptic_curves.constructor import HyperellipticCurve self = HyperellipticCurve(f).change_ring(K) xP = P[0] xPv = xP.valuation() xPnew = K( sum(c * p**(xPv + i) for i, c in enumerate(xP.expansion()))) PP = P = self.lift_x(xPnew) TP = self.frobenius(P) xQ = Q[0] xQv = xQ.valuation() xQnew = K( sum(c * p**(xQv + i) for i, c in enumerate(xQ.expansion()))) QQ = Q = self.lift_x(xQnew) TQ = self.frobenius(Q) V = VectorSpace(K, dim) P_to_TP = V(self.tiny_integrals_on_basis(P, TP)) if TQ is None: TQ_to_Q = V(0) else: TQ_to_Q = V(self.tiny_integrals_on_basis(TQ, Q)) prof("mw calc") try: M_frob, forms = self._frob_calc except AttributeError: M_frob, forms = self._frob_calc = monsky_washnitzer.matrix_of_frobenius_hyperelliptic( self) prof("eval f") R = forms[0].base_ring() try: prof("eval f %s" % R) if PP is None: L = [-f(R(QQ[0]), R(QQ[1])) for f in forms] ##changed elif QQ is None: L = [f(R(PP[0]), R(PP[1])) for f in forms] else: L = [ f(R(PP[0]), R(PP[1])) - f(R(QQ[0]), R(QQ[1])) for f in forms ] except ValueError: prof("changing rings") forms = [f.change_ring(self.base_ring()) for f in forms] prof("eval f %s" % self.base_ring()) if PP is None: L = [-f(QQ[0], QQ[1]) for f in forms] ##changed elif QQ is None: L = [f(PP[0], PP[1]) for f in forms] else: L = [f(PP[0], PP[1]) - f(QQ[0], QQ[1]) for f in forms] b = V(L) if PP is None: b -= TQ_to_Q elif QQ is None: b -= P_to_TP elif algorithm != 'teichmuller': b -= P_to_TP + TQ_to_Q prof("lin alg") M_sys = matrix(K, M_frob).transpose() - 1 TP_to_TQ = M_sys**(-1) * b prof("done") # print prof if algorithm == 'teichmuller': return P_to_TP + TP_to_TQ + TQ_to_Q else: return TP_to_TQ
def _biquadratic_syzygy_quartic(quadratic1, quadratic2, variables=None): r""" Helper function for the Weierstrass form of a biquadratic in $`\mathbb{P}^3$ The invariants and covariants of a quaternary biquadratic satisfy the relation :meth:`sage.rings.invariant_theory.TwoQuaternaryQuadratics.syzygy`, which is (modulo the two quadratic equations) of the form $J^2 = p_4(T, T')$ where * $J$, $T$, $T'$ are the covariants of the biquadratic. * $p_4$ is some quartic polynomial whose coefficients are invariants of the biquadratic. INPUT: See :func:`WeierstrassForm_P3` OUTPUT: A triple consisting of - The quaternary biquadratic as an algebraic form :class:`~sage.rings.invariant_theory.TwoQuaternaryQuadratics` - The binary quartic $p_4$ as a :class:`~sage.rings.invariant_theory.BinaryQuartic` - The dictionary of variable substitutions from the variables of the quartic to the variables of the biquadratic. EXAMPLES:: sage: from sage.schemes.toric.weierstrass_higher import _biquadratic_syzygy_quartic sage: R.<w,x,y,z> = QQ[] sage: _biquadratic_syzygy_quartic(w^2+x^2+y^2, z^2) (Joint quaternary quadratic with coefficients (1, 1, 1, 0, 0, 0, 0, 0, 0, 0) and quaternary quadratic with coefficients (0, 0, 0, 1, 0, 0, 0, 0, 0, 0), Binary quartic with coefficients (0, 0, 0, -1, 0), {aux...}) """ w, x, y, z = _check_polynomials_P3(quadratic1, quadratic2, variables) biquadratic = invariant_theory.quaternary_biquadratic(quadratic1, quadratic2, [w, x, y, z]) # construct auxiliary polynomial ring to work with the rhs of the syzygy R = biquadratic.ring() n = R.ngens() R_aux = PolynomialRing(R.base_ring(), n+2, 'aux') to_aux = dict() from_aux = dict() for var, var_aux in zip(R.gens(), R_aux.gens()[0:n]): to_aux[var] = var_aux from_aux[var_aux] = var T, T_prime = R_aux.gens()[n:] from_aux[T] = biquadratic.T_covariant() from_aux[T_prime] = biquadratic.T_prime_covariant() # Syzygy is J^2 = syz_rhs + (terms that vanish on the biquadratic) with # J = biquadratic.J_covariant() syz_rhs = T**4 * biquadratic.Delta_invariant().subs(to_aux) \ - T**3*T_prime * biquadratic.Theta_invariant().subs(to_aux) \ + T**2*T_prime**2 * biquadratic.Phi_invariant().subs(to_aux) \ - T*T_prime**3 * biquadratic.Theta_prime_invariant().subs(to_aux) \ + T_prime**4 * biquadratic.Delta_prime_invariant().subs(to_aux) quartic = invariant_theory.binary_quartic(syz_rhs, [T, T_prime]) return (biquadratic, quartic, from_aux)
t, = self.base_ring().base().gens() # t in F[t] supp = [] roots = [[], [], []] remove = None # loop through the coefficients and find a root of f_i (as in # [HC2006]) modulo each element in the coefficients' support for i in (0, 1, 2): supp.append(list(coeff[i].factor())) for p in supp[i]: if p[1] != 1: raise ValueError("Expected factor of exponent 1.") # Convert to monic factor x = p[0] / list(p[0])[-1] N = p[0].base_ring().extension(x, 'tbar') R = PolynomialRing(N, 'u') u, = R.gens() # If p[0] has degree 1, sage might forget the "defining # polynomial" of N, so we define our own modulo operation if p[0].degree() == 1: mod = t.parent().hom([-x[0]]) else: mod = N if i == 0: x = -mod(coeff[2]) / mod(coeff[1]) elif i == 1: x = -mod(coeff[0]) / mod(coeff[2]) else: x = -mod(coeff[1]) / mod(coeff[0]) if x.is_square(): root = N(x.sqrt())
def _sage_(self): """ Convert self to a Sage object. EXAMPLES:: sage: a = axiom(1/2); a #optional - axiom 1 - 2 sage: a.sage() #optional - axiom 1/2 sage: _.parent() #optional - axiom Rational Field sage: gp(axiom(1/2)) #optional - axiom 1/2 sage: fricas(1/2).sage() #optional - fricas 1/2 DoubleFloat's in Axiom are converted to be in RDF in Sage. :: sage: axiom(2.0).as_type('DoubleFloat').sage() #optional - axiom 2.0 sage: _.parent() #optional - axiom Real Double Field sage: axiom(2.1234)._sage_() #optional - axiom 2.12340000000000 sage: _.parent() #optional - axiom Real Field with 53 bits of precision sage: a = RealField(100)(pi) sage: axiom(a)._sage_() #optional - axiom 3.1415926535897932384626433833 sage: _.parent() #optional - axiom Real Field with 100 bits of precision sage: axiom(a)._sage_() == a #optional - axiom True sage: axiom(2.0)._sage_() #optional - axiom 2.00000000000000 sage: _.parent() #optional - axiom Real Field with 53 bits of precision We can also convert Axiom's polynomials to Sage polynomials. sage: a = axiom(x^2 + 1) #optional - axiom sage: a.type() #optional - axiom Polynomial Integer sage: a.sage() #optional - axiom x^2 + 1 sage: _.parent() #optional - axiom Univariate Polynomial Ring in x over Integer Ring sage: axiom('x^2 + y^2 + 1/2').sage() #optional - axiom y^2 + x^2 + 1/2 sage: _.parent() #optional - axiom Multivariate Polynomial Ring in y, x over Rational Field """ P = self._check_valid() type = str(self.type()) if type in ["Type", "Domain"]: return self._sage_domain() if type == "Float": from sage.rings.all import RealField, ZZ prec = max(self.mantissa().length()._sage_(), 53) R = RealField(prec) x, e, b = self.unparsed_input_form().lstrip('float(').rstrip( ')').split(',') return R(ZZ(x) * ZZ(b)**ZZ(e)) elif type == "DoubleFloat": from sage.rings.all import RDF return RDF(repr(self)) elif type.startswith('Polynomial'): from sage.rings.all import PolynomialRing base_ring = P(type.lstrip('Polynomial '))._sage_domain() vars = str(self.variables())[1:-1] R = PolynomialRing(base_ring, vars) return R(self.unparsed_input_form()) #If all else fails, try using the unparsed input form try: import sage.misc.sage_eval return sage.misc.sage_eval.sage_eval(self.unparsed_input_form()) except: raise NotImplementedError
def rational_type(f, n=ZZ(3), base_ring=ZZ): r""" Return the basic analytic properties that can be determined directly from the specified rational function ``f`` which is interpreted as a representation of an element of a FormsRing for the Hecke Triangle group with parameter ``n`` and the specified ``base_ring``. In particular the following degree of the generators is assumed: `deg(1) := (0, 1)` `deg(x) := (4/(n-2), 1)` `deg(y) := (2n/(n-2), -1)` `deg(z) := (2, -1)` The meaning of homogeneous elements changes accordingly. INPUT: - ``f`` - A rational function in ``x,y,z,d`` over ``base_ring``. - ``n`` - An integer greater or equal to ``3`` corresponding to the ``HeckeTriangleGroup`` with that parameter (default: ``3``). - ``base_ring``` - The base ring of the corresponding forms ring, resp. polynomial ring (default: ``ZZ``). OUTPUT: A tuple ``(elem, h**o, k, ep, analytic_type)`` describing the basic analytic properties of ``f`` (with the interpretation indicated above). - ``elem`` - ``True`` if ``f`` has a homogeneous denominator. - ``h**o`` - ``True`` if ``f`` also has a homogeneous numerator. - ``k`` - ``None`` if ``f`` is not homogeneneous, otherwise the weight of ``f`` (which is the first component of its degree). - ``ep`` - ``None`` if ``f`` is not homogeneous, otherwise the multiplier of ``f`` (which is the second component of its degree) - ``analytic_type`` - The ``AnalyticType`` of ``f``. For the zero function the degree ``(0, 1)`` is choosen. This function is (heavily) used to determine the type of elements and to check if the element really is contained in its parent. EXAMPLES:: sage: (x,y,z,d) = var("x,y,z,d") sage: rational_type(0, n=4) (True, True, 0, 1, zero) sage: rational_type(1, n=12) (True, True, 0, 1, modular) sage: rational_type(x^3 - y^2) (True, True, 12, 1, cuspidal) sage: rational_type(x * z, n=7) (True, True, 14/5, -1, quasi modular) sage: rational_type(1/(x^3 - y^2) + z/d) (True, False, None, None, quasi weakly holomorphic modular) sage: rational_type(x^3/(x^3 - y^2)) (True, True, 0, 1, weakly holomorphic modular) sage: rational_type(1/(x + z)) (False, False, None, None, None) sage: rational_type(1/x + 1/z) (True, False, None, None, quasi meromorphic modular) sage: rational_type(d/x, n=10) (True, True, -1/2, 1, meromorphic modular) sage: rational_type(1.1 * z * (x^8-y^2), n=8, base_ring=CC) (True, True, 22/3, -1, quasi cuspidal) """ from analytic_type import AnalyticType AT = AnalyticType() # Determine whether f is zero if (f == 0): # elem, h**o, k, ep, analytic_type return (True, True, QQ(0), ZZ(1), AT([])) analytic_type = AT(["quasi", "mero"]) R = PolynomialRing(base_ring,'x,y,z,d') F = FractionField(R) (x,y,z,d) = R.gens() R2 = PolynomialRing(PolynomialRing(base_ring, 'd'), 'x,y,z') dhom = R.hom( R2.gens() + (R2.base().gen(),), R2) f = F(f) n = ZZ(n) num = R(f.numerator()) denom = R(f.denominator()) hom_num = R( num.subs(x=x**4, y=y**(2*n), z=z**(2*(n-2))) ) hom_denom = R( denom.subs(x=x**4, y=y**(2*n), z=z**(2*(n-2))) ) ep_num = set([ZZ(1) - 2*(( sum([g.exponents()[0][m] for m in [1,2]]) )%2) for g in dhom(num).monomials()]) ep_denom = set([ZZ(1) - 2*(( sum([g.exponents()[0][m] for m in [1,2]]) )%2) for g in dhom(denom).monomials()]) # Determine whether the denominator of f is homogeneous if (len(ep_denom) == 1 and dhom(hom_denom).is_homogeneous()): elem = True else: # elem, h**o, k, ep, analytic_type return (False, False, None, None, None) # Determine whether f is homogeneous if (len(ep_num) == 1 and dhom(hom_num).is_homogeneous()): h**o = True weight = (dhom(hom_num).degree() - dhom(hom_denom).degree()) / (n-2) ep = ep_num.pop() / ep_denom.pop() # TODO: decompose f (resp. its degrees) into homogeneous parts else: h**o = False weight = None ep = None # Note that we intentially leave out the d-factor! finf_pol = x**n-y**2 # Determine whether f is modular if not ( (num.degree(z) > 0) or (denom.degree(z) > 0) ): analytic_type = analytic_type.reduce_to("mero") # Determine whether f is holomorphic if (dhom(denom).is_constant()): analytic_type = analytic_type.reduce_to(["quasi", "holo"]) # Determine whether f is cuspidal in the sense that finf divides it... # Bug in singular: finf_pol.dividess(1.0) fails over RR if (not dhom(num).is_constant()) and finf_pol.divides(num): analytic_type = analytic_type.reduce_to(["quasi", "cusp"]) else: # -> because of a bug with singular in case some cases try: while (finf_pol.divides(denom)): # a simple "denom /= finf_pol" is strangely not enough for non-exact rings denom = denom.quo_rem(finf_pol)[0] denom = R(denom) except TypeError: pass # Determine whether f is weakly holomorphic in the sense that at most powers of finf occur in denom if (dhom(denom).is_constant()): analytic_type = analytic_type.reduce_to(["quasi", "weak"]) return (elem, h**o, weight, ep, analytic_type)