def num_coeffs(self, T=1): """ Return number of coefficients `a_n` that are needed in order to perform most relevant `L`-function computations to the desired precision. EXAMPLES:: sage: E = EllipticCurve('11a') sage: L = E.lseries().dokchitser(algorithm="pari") sage: L.num_coeffs() 27 sage: E = EllipticCurve('5077a') sage: L = E.lseries().dokchitser(algorithm="pari") sage: L.num_coeffs() 591 sage: from sage.lfunctions.pari import lfun_generic, LFunction sage: lf = lfun_generic(conductor=1, gammaV=[0], weight=1, eps=1, poles=[1], residues=[-1], v=pari('k->vector(k,n,1)')) sage: L = LFunction(lf) sage: L.num_coeffs() 4 """ # lfuncost takes a domain domain = [pari(1) / 2, 1, 1] return ZZ(pari.lfuncost(self._L, domain)[0])
def __init__(self, x, degree): """ EXAMPLES:: sage: PariGroup([6, -1, 2, "S3"], 3) PARI group [6, -1, 2, S3] of degree 3 sage: R.<x> = PolynomialRing(QQ) sage: f = x^4 - 17*x^3 - 2*x + 1 sage: G = f.galois_group(pari_group=True); G PARI group [24, -1, 5, "S4"] of degree 4 """ self.__x = pari(x) self.__degree = Integer(degree)
def is_Q_curve(E, maxp=100, certificate=False, verbose=False): r""" Return whether ``E`` is a `\QQ`-curve, with optional certificate. INPUT: - ``E`` (elliptic curve) -- an elliptic curve over a number field. - ``maxp`` (int, default 100): bound on primes used for checking necessary local conditions. The result will not depend on this, but using a larger value may return ``False`` faster. - ``certificate`` (bool, default ``False``): if ``True`` then a second value is returned giving a certificate for the `\QQ`-curve property. OUTPUT: If ``certificate`` is ``False``: either ``True`` (if `E` is a `\QQ`-curve), or ``False``. If ``certificate`` is ``True``: a tuple consisting of a boolean flag as before and a certificate, defined as follows: - when the flag is ``True``, so `E` is a `\QQ`-curve: - either {'CM':`D`} where `D` is a negative discriminant, when `E` has potential CM with discriminant `D`; - otherwise {'CM': `0`, 'core_poly': `f`, 'rho': `\rho`, 'r': `r`, 'N': `N`}, when `E` is a non-CM `\QQ`-curve, where the core polynomial `f` is an irreducible monic polynomial over `QQ` of degree `2^\rho`, all of whose roots are `j`-invariants of curves isogenous to `E`, the core level `N` is a square-free integer with `r` prime factors which is the LCM of the degrees of the isogenies between these conjugates. For example, if there exists a curve `E'` isogenous to `E` with `j(E')=j\in\QQ`, then the certificate is {'CM':0, 'r':0, 'rho':0, 'core_poly': x-j, 'N':1}. - when the flag is ``False``, so `E` is not a `\QQ`-curve, the certificate is a prime `p` such that the reductions of `E` at the primes dividing `p` are inconsistent with the property of being a `\QQ`-curve. See the ALGORITHM section for details. ALGORITHM: See [CrNa2020]_ for details. 1. If `E` has rational `j`-invariant, or has CM, then return ``True``. 2. Replace `E` by a curve defined over `K=\QQ(j(E))`. Let `N` be the conductor norm. 3. For all primes `p\mid N` check that the valuations of `j` at all `P\mid p` are either all negative or all non-negative; if not, return ``False``. 4. For `p\le maxp`, `p\not\mid N`, check that either `E` is ordinary mod `P` for all `P\mid p`, or `E` is supersingular mod `P` for all `P\mid p`; if neither, return ``False``. If all are ordinary, check that the integers `a_P(E)^2-4N(P)` have the same square-free part; if not, return ``False``. 5. Compute the `K`-isogeny class of `E` using the "heuristic" option (which is faster, but not guaranteed to be complete). Check whether the set of `j`-invariants of curves in the class of `2`-power degree contains a complete Galois orbit. If so, return ``True``. 6. Otherwise repeat step 4 for more primes, and if still undecided, repeat Step 5 without the "heuristic" option, to get the complete `K`-isogeny class (which will probably be no bigger than before). Now return ``True`` if the set of `j`-invariants of curves in the class contains a complete Galois orbit, otherwise return ``False``. EXAMPLES: A non-CM curve over `\QQ` and a CM curve over `\QQ` are both trivially `\QQ`-curves:: sage: from sage.schemes.elliptic_curves.Qcurves import is_Q_curve sage: E = EllipticCurve([1,2,3,4,5]) sage: flag, cert = is_Q_curve(E, certificate=True) sage: flag True sage: cert {'CM': 0, 'N': 1, 'core_poly': x, 'r': 0, 'rho': 0} sage: E = EllipticCurve(j=8000) sage: flag, cert = is_Q_curve(E, certificate=True) sage: flag True sage: cert {'CM': -8} A non-`\QQ`-curve over a quartic field. The local data at bad primes above `3` is inconsistent:: sage: from sage.schemes.elliptic_curves.Qcurves import is_Q_curve sage: R.<x> = PolynomialRing(QQ) sage: K.<a> = NumberField(R([3, 0, -5, 0, 1])) sage: E = EllipticCurve([K([-3,-4,1,1]),K([4,-1,-1,0]),K([-2,0,1,0]),K([-621,778,138,-178]),K([9509,2046,-24728,10380])]) sage: is_Q_curve(E, certificate=True, verbose=True) Checking whether Elliptic Curve defined by y^2 + (a^3+a^2-4*a-3)*x*y + (a^2-2)*y = x^3 + (-a^2-a+4)*x^2 + (-178*a^3+138*a^2+778*a-621)*x + (10380*a^3-24728*a^2+2046*a+9509) over Number Field in a with defining polynomial x^4 - 5*x^2 + 3 is a Q-curve No: inconsistency at the 2 primes dividing 3 - potentially multiplicative: [True, False] (False, 3) A non-`\QQ`-curve over a quadratic field. The local data at bad primes is consistent, but the local test at good primes above `13` is not:: sage: K.<a> = NumberField(R([-10, 0, 1])) sage: E = EllipticCurve([K([0,1]),K([-1,-1]),K([0,0]),K([-236,40]),K([-1840,464])]) sage: is_Q_curve(E, certificate=True, verbose=True) Checking whether Elliptic Curve defined by y^2 + a*x*y = x^3 + (-a-1)*x^2 + (40*a-236)*x + (464*a-1840) over Number Field in a with defining polynomial x^2 - 10 is a Q-curve Applying local tests at good primes above p<=100 No: inconsistency at the 2 ordinary primes dividing 13 - Frobenius discriminants mod squares: [-1, -3] No: local test at p=13 failed (False, 13) A quadratic `\QQ`-curve with CM discriminant `-15` (`j`-invariant not in `\QQ`):: sage: from sage.schemes.elliptic_curves.Qcurves import is_Q_curve sage: R.<x> = PolynomialRing(QQ) sage: K.<a> = NumberField(R([-1, -1, 1])) sage: E = EllipticCurve([K([1,0]),K([-1,0]),K([0,1]),K([0,-2]),K([0,1])]) sage: is_Q_curve(E, certificate=True, verbose=True) Checking whether Elliptic Curve defined by y^2 + x*y + a*y = x^3 + (-1)*x^2 + (-2*a)*x + a over Number Field in a with defining polynomial x^2 - x - 1 is a Q-curve Yes: E is CM (discriminant -15) (True, {'CM': -15}) An example over `\QQ(\sqrt{2},\sqrt{3})`. The `j`-invariant is in `\QQ(\sqrt{6})`, so computations will be done over that field, and in fact there is an isogenous curve with rational `j`, so we have a so-called rational `\QQ`-curve:: sage: K.<a> = NumberField(R([1, 0, -4, 0, 1])) sage: E = EllipticCurve([K([-2,-4,1,1]),K([0,1,0,0]),K([0,1,0,0]),K([-4780,9170,1265,-2463]),K([163923,-316598,-43876,84852])]) sage: flag, cert = is_Q_curve(E, certificate=True) sage: flag True sage: cert {'CM': 0, 'N': 1, 'core_degs': [1], 'core_poly': x - 85184/3, 'r': 0, 'rho': 0} Over the same field, a so-called strict `\QQ`-curve which is not isogenous to one with rational `j`, but whose core field is quadratic. In fact the isogeny class over `K` consists of `6` curves, four with conjugate quartic `j`-invariants and `2` with quadratic conjugate `j`-invariants in `\QQ(\sqrt{3})` (but which are not base-changes from the quadratic subfield):: sage: E = EllipticCurve([K([0,-3,0,1]),K([1,4,0,-1]),K([0,0,0,0]),K([-2,-16,0,4]),K([-19,-32,4,8])]) sage: flag, cert = is_Q_curve(E, certificate=True) sage: flag True sage: cert {'CM': 0, 'N': 2, 'core_degs': [1, 2], 'core_poly': x^2 - 840064*x + 1593413632, 'r': 1, 'rho': 1} """ from sage.rings.number_field.number_field_base import is_NumberField if verbose: print("Checking whether {} is a Q-curve".format(E)) try: assert is_NumberField(E.base_field()) except (AttributeError, AssertionError): raise TypeError( "{} must be an elliptic curve defined over a number field in is_Q_curve()" ) from sage.rings.integer_ring import ZZ from sage.arith.functions import lcm from sage.libs.pari import pari from sage.rings.number_field.number_field import NumberField from sage.schemes.elliptic_curves.constructor import EllipticCurve from sage.schemes.elliptic_curves.cm import cm_j_invariants_and_orders, is_cm_j_invariant # Step 1 # all curves with rational j-invariant are Q-curves: jE = E.j_invariant() if jE in QQ: if verbose: print("Yes: j(E) is in QQ") if certificate: # test for CM for d, f, j in cm_j_invariants_and_orders(QQ): if jE == j: return True, {'CM': d * f**2} # else not CM return True, { 'CM': ZZ(0), 'r': ZZ(0), 'rho': ZZ(0), 'N': ZZ(1), 'core_poly': polygen(QQ) } else: return True # CM curves are Q-curves: flag, df = is_cm_j_invariant(jE) if flag: d, f = df D = d * f**2 if verbose: print("Yes: E is CM (discriminant {})".format(D)) if certificate: return True, {'CM': D} else: return True # Step 2: replace E by a curve defined over Q(j(E)): K = E.base_field() jpoly = jE.minpoly() if jpoly.degree() < K.degree(): if verbose: print("switching to smaller base field: j's minpoly is {}".format( jpoly)) f = pari(jpoly).polredbest().sage({'x': jpoly.parent().gen()}) K2 = NumberField(f, 'b') jE = jpoly.roots(K2)[0][0] if verbose: print("New j is {} over {}, with minpoly {}".format( jE, K2, jE.minpoly())) #assert jE.minpoly()==jpoly E = EllipticCurve(j=jE) K = K2 if verbose: print("New test curve is {}".format(E)) # Step 3: check primes of bad reduction NN = E.conductor().norm() for p in NN.support(): Plist = K.primes_above(p) if len(Plist) < 2: continue # pot_mult = potential multiplicative reduction pot_mult = [jE.valuation(P) < 0 for P in Plist] consistent = all(pot_mult) or not any(pot_mult) if not consistent: if verbose: print("No: inconsistency at the {} primes dividing {}".format( len(Plist), p)) print(" - potentially multiplicative: {}".format(pot_mult)) if certificate: return False, p else: return False # Step 4 check: primes P of good reduction above p<=B: if verbose: print("Applying local tests at good primes above p<={}".format(maxp)) res4, p = Step4Test(E, B=maxp, oldB=0, verbose=verbose) if not res4: if verbose: print("No: local test at p={} failed".format(p)) if certificate: return False, p else: return False if verbose: print("...all local tests pass for p<={}".format(maxp)) # Step 5: compute the (partial) K-isogeny class of E and test the # set of j-invariants in the class: C = E.isogeny_class(algorithm='heuristic', minimal_models=False) jC = [E2.j_invariant() for E2 in C] centrejpols = conjugacy_test(jC, verbose=verbose) if centrejpols: if verbose: print( "Yes: the isogeny class contains a complete conjugacy class of j-invariants" ) if certificate: for f in centrejpols: rho = f.degree().valuation(2) centre_indices = [i for i, j in enumerate(jC) if f(j) == 0] M = C.matrix() core_degs = [M[centre_indices[0], i] for i in centre_indices] level = lcm(core_degs) if level.is_squarefree(): r = len(level.prime_divisors()) cert = { 'CM': ZZ(0), 'core_poly': f, 'rho': rho, 'r': r, 'N': level, 'core_degs': core_degs } return True, cert print("No central curve found") else: return True # Now we are undecided. This can happen if either (1) E is not a # Q-curve but we did not use enough primes in Step 4 to detect # this, or (2) E is a Q-curve but in Step 5 we did not compute the # complete isogeny class. Case (2) is most unlikely since the # heuristic bound used in computing isogeny classes means that we # have all isogenous curves linked to E by an isogeny of degree # supported on primes<1000. # We first rerun Step 4 with a larger bound. xmaxp = 10 * maxp if verbose: print( "Undecided after first round, so we apply more local tests, up to {}" .format(xmaxp)) res4, p = Step4Test(E, B=xmaxp, oldB=maxp, verbose=verbose) if not res4: if verbose: print("No: local test at p={} failed".format(p)) if certificate: return False, p else: return False # Now we rerun Step 5 using a rigorous computation of the complete # isogeny class. This will probably contain no more curves than # before, in which case -- since we already tested that the set of # j-invariants does not contain a complete Galois conjugacy class # -- we can deduce that E is not a Q-curve. if verbose: print("...all local tests pass for p<={}".format(xmaxp)) print("We now compute the complete isogeny class...") Cfull = E.isogeny_class(minimal_models=False) jCfull = [E2.j_invariant() for E2 in Cfull] if len(jC) == len(jCfull): if verbose: print("...and find that we already had the complete class:so No") if certificate: return False, 0 else: return False if verbose: print( "...and find that the class contains {} curves, not just the {} we computed originally" .format(len(jCfull), len(jC))) centrejpols = conjugacy_test(jCfull, verbose=verbose) if cert: if verbose: print( "Yes: the isogeny class contains a complete conjugacy class of j-invariants" ) if certificate: return True, centrejpols else: return True if verbose: print( "No: the isogeny class does *not* contain a complete conjugacy class of j-invariants" ) if certificate: return False, 0 else: return False
def lfun_character(chi): """ Create the L-function of a primitive Dirichlet character. If the given character is not primitive, it is replaced by its associated primitive character. OUTPUT: one :pari:`lfun` object EXAMPLES:: sage: from sage.lfunctions.pari import lfun_character, LFunction sage: chi = DirichletGroup(6).gen().primitive_character() sage: L = LFunction(lfun_character(chi)) sage: L(3) 1.20205690315959 TESTS: A non-primitive one:: sage: L = LFunction(lfun_character(DirichletGroup(6).gen())) sage: L(4) 1.08232323371114 With complex arguments:: sage: from sage.lfunctions.pari import lfun_character, LFunction sage: chi = DirichletGroup(6, CC).gen().primitive_character() sage: L = LFunction(lfun_character(chi)) sage: L(3) 1.20205690315959 """ if not chi.is_primitive(): chi = chi.primitive_character() conductor = chi.conductor() G = pari.znstar(conductor, 1) pari_orders = [pari(o) for o in G[2][1]] pari_gens = IntegerModRing(conductor).unit_gens(algorithm="pari") # should coincide with G[2][2] values_on_gens = (chi(x) for x in pari_gens) # now compute the input for pari (list of exponents) P = chi.parent() if is_ComplexField(P.base_ring()): zeta = P.zeta() zeta_argument = zeta.argument() v = [int(x.argument() / zeta_argument) for x in values_on_gens] else: dlog = P._zeta_dlog v = [dlog[x] for x in values_on_gens] m = P.zeta_order() v = [(vi * oi) // m for vi, oi in zip(v, pari_orders)] return pari.lfuncreate([G, v])
def init_coeffs(self, v, cutoff=None, w=1, *args, **kwds): """ Set the coefficients `a_n` of the `L`-series. If `L(s)` is not equal to its dual, pass the coefficients of the dual as the second optional argument. INPUT: - ``v`` -- list of complex numbers or unary function - ``cutoff`` -- unused - ``w`` -- list of complex numbers or unary function EXAMPLES:: sage: from sage.lfunctions.pari import lfun_generic, LFunction sage: lf = lfun_generic(conductor=1, gammaV=[0,1], weight=12, eps=1) sage: pari_coeffs = pari('k->vector(k,n,(5*sigma(n,3)+7*sigma(n,5))*n/12 - 35*sum(k=1,n-1,(6*k-4*(n-k))*sigma(k,3)*sigma(n-k,5)))') sage: lf.init_coeffs(pari_coeffs) Evaluate the resulting L-function at a point, and compare with the answer that one gets "by definition" (of L-function attached to a modular form):: sage: L = LFunction(lf) sage: L(14) 0.998583063162746 sage: a = delta_qexp(1000) sage: sum(a[n]/float(n)^14 for n in range(1,1000)) 0.9985830631627459 Illustrate that one can give a list of complex numbers for v (see :trac:`10937`):: sage: l2 = lfun_generic(conductor=1, gammaV=[0,1], weight=12, eps=1) sage: l2.init_coeffs(list(delta_qexp(1000))[1:]) sage: L2 = LFunction(l2) sage: L2(14) 0.998583063162746 TESTS: Verify that setting the `w` parameter does not raise an error (see :trac:`10937`):: sage: L2 = lfun_generic(conductor=1, gammaV=[0,1], weight=12, eps=1) sage: L2.init_coeffs(list(delta_qexp(1000))[1:], w=[1..1000]) Additional arguments are ignored for compatibility with the old Dokchitser script:: sage: L2.init_coeffs(list(delta_qexp(1000))[1:], foo="bar") doctest:...: DeprecationWarning: additional arguments for initializing an lfun_generic are ignored See https://trac.sagemath.org/26098 for details. """ if args or kwds: from sage.misc.superseded import deprecation deprecation( 26098, "additional arguments for initializing an lfun_generic are ignored" ) v = pari(v) if v.type() not in ('t_CLOSURE', 't_VEC'): raise TypeError("v (coefficients) must be a list or a function") # w = 0 means a*_n = a_n # w = 1 means a*_n = complex conjugate of a_n # otherwise w must be a list of coefficients w = pari(w) if w.type() not in ('t_INT', 't_CLOSURE', 't_VEC'): raise TypeError( "w (dual coefficients) must be a list or a function or the special value 0 or 1" ) if not self.poles: self._L = pari.lfuncreate( [v, w, self.gammaV, self.weight, self.conductor, self.eps]) else: # TODO # poles = list(zip(poles, residues)) self._L = pari.lfuncreate([ v, w, self.gammaV, self.weight, self.conductor, self.eps, self.poles[0] ])