def __init__(self, conductor, gammaV, weight, eps, poles=None, residues='automatic', prec=53, init=None): """ Initialization of Dokchitser calculator EXAMPLES:: sage: L = Dokchitser(conductor=1, gammaV=[0], weight=1, eps=1, poles=[1], residues=[-1], init='1') sage: L.num_coeffs() 4 """ self.conductor = conductor self.gammaV = gammaV self.weight = weight self.eps = eps self.poles = poles if poles is not None else [] self.residues = residues self.prec = prec self.__CC = ComplexField(self.prec) self.__RR = self.__CC._real_field() self.__initialized = False if init is not None: self.init_coeffs(init)
def zeta_symmetric(s): r""" Completed function `\xi(s)` that satisfies `\xi(s) = \xi(1-s)` and has zeros at the same points as the Riemann zeta function. INPUT: - ``s`` - real or complex number If s is a real number the computation is done using the MPFR library. When the input is not real, the computation is done using the PARI C library. More precisely, .. math:: xi(s) = \gamma(s/2 + 1) * (s-1) * \pi^{-s/2} * \zeta(s). EXAMPLES:: sage: zeta_symmetric(0.7) 0.497580414651127 sage: zeta_symmetric(1-0.7) 0.497580414651127 sage: RR = RealField(200) sage: zeta_symmetric(RR(0.7)) 0.49758041465112690357779107525638385212657443284080589766062 sage: C.<i> = ComplexField() sage: zeta_symmetric(0.5 + i*14.0) 0.000201294444235258 + 1.49077798716757e-19*I sage: zeta_symmetric(0.5 + i*14.1) 0.0000489893483255687 + 4.40457132572236e-20*I sage: zeta_symmetric(0.5 + i*14.2) -0.0000868931282620101 + 7.11507675693612e-20*I REFERENCE: - I copied the definition of xi from http://web.viu.ca/pughg/RiemannZeta/RiemannZetaLong.html """ if not (is_ComplexNumber(s) or is_RealNumber(s)): s = ComplexField()(s) R = s.parent() if s == 1: # deal with poles, hopefully return R(0.5) return (s / 2 + 1).gamma() * (s - 1) * (R.pi() ** (-s / 2)) * s.zeta()
def zeta_symmetric(s): r""" Completed function `\xi(s)` that satisfies `\xi(s) = \xi(1-s)` and has zeros at the same points as the Riemann zeta function. INPUT: - ``s`` - real or complex number If s is a real number the computation is done using the MPFR library. When the input is not real, the computation is done using the PARI C library. More precisely, .. MATH:: xi(s) = \gamma(s/2 + 1) * (s-1) * \pi^{-s/2} * \zeta(s). EXAMPLES:: sage: zeta_symmetric(0.7) 0.497580414651127 sage: zeta_symmetric(1-0.7) 0.497580414651127 sage: RR = RealField(200) sage: zeta_symmetric(RR(0.7)) 0.49758041465112690357779107525638385212657443284080589766062 sage: C.<i> = ComplexField() sage: zeta_symmetric(0.5 + i*14.0) 0.000201294444235258 + 1.49077798716757e-19*I sage: zeta_symmetric(0.5 + i*14.1) 0.0000489893483255687 + 4.40457132572236e-20*I sage: zeta_symmetric(0.5 + i*14.2) -0.0000868931282620101 + 7.11507675693612e-20*I REFERENCE: - I copied the definition of xi from http://web.viu.ca/pughg/RiemannZeta/RiemannZetaLong.html """ if not (is_ComplexNumber(s) or is_RealNumber(s)): s = ComplexField()(s) R = s.parent() if s == 1: # deal with poles, hopefully return R(0.5) return (s / 2 + 1).gamma() * (s - 1) * (R.pi()**(-s / 2)) * s.zeta()
def _evalf_(self, x, y, parent=None, algorithm='mpmath'): """ EXAMPLES:: sage: gamma_inc_lower(3,2.) 0.646647167633873 sage: gamma_inc_lower(3,2).n(200) 0.646647167633873081060005050275155... sage: gamma_inc_lower(0,2.) +infinity """ R = parent or s_parent(x) # C is the complex version of R # prec is the precision of R if R is float: prec = 53 C = complex else: try: prec = R.precision() except AttributeError: prec = 53 try: C = R.complex_field() except AttributeError: C = R if algorithm == 'pari': try: v = ComplexField(prec)(x).gamma() - ComplexField(prec)( x).gamma_inc(y) except AttributeError: if not (is_ComplexNumber(x)): if is_ComplexNumber(y): C = y.parent() else: C = ComplexField() x = C(x) v = ComplexField(prec)(x).gamma() - ComplexField(prec)( x).gamma_inc(y) else: import mpmath v = ComplexField(prec)(mpmath_utils.call(mpmath.gammainc, x, 0, y, parent=R)) if v.is_real(): return R(v) else: return C(v)
def _do_sqrt(x, prec=None, extend=True, all=False): r""" Used internally to compute the square root of x. INPUT: - ``x`` - a number - ``prec`` - None (default) or a positive integer (bits of precision) If not None, then compute the square root numerically to prec bits of precision. - ``extend`` - bool (default: True); this is a place holder, and is always ignored since in the symbolic ring everything has a square root. - ``extend`` - bool (default: True); whether to extend the base ring to find roots. The extend parameter is ignored if prec is a positive integer. - ``all`` - bool (default: False); whether to return a list of all the square roots of x. EXAMPLES:: sage: from sage.functions.other import _do_sqrt sage: _do_sqrt(3) sqrt(3) sage: _do_sqrt(3,prec=10) 1.7 sage: _do_sqrt(3,prec=100) 1.7320508075688772935274463415 sage: _do_sqrt(3,all=True) [sqrt(3), -sqrt(3)] Note that the extend parameter is ignored in the symbolic ring:: sage: _do_sqrt(3,extend=False) sqrt(3) """ from sage.rings.all import RealField, ComplexField if prec: if x >= 0: return RealField(prec)(x).sqrt(all=all) else: return ComplexField(prec)(x).sqrt(all=all) if x == -1: from sage.symbolic.pynac import I z = I else: z = SR(x)**one_half if all: if z: return [z, -z] else: return [z] return z
def map_to_complex_numbers(self, z, prec=None): """ Evaluate ``self`` at a point `z \in X_0(N)` where `z` is given by a representative in the upper half plane, returning a point in the complex numbers. All computations are done with ``prec`` bits of precision. If ``prec`` is not given, use the precision of `z`. Use self(z) to compute the image of z on the Weierstrass equation of the curve. EXAMPLES:: sage: E = EllipticCurve('37a'); phi = E.modular_parametrization() sage: tau = (sqrt(7)*I - 17)/74 sage: z = phi.map_to_complex_numbers(tau); z 0.929592715285395 - 1.22569469099340*I sage: E.elliptic_exponential(z) (...e-16 - ...e-16*I : ...e-16 + ...e-16*I : 1.00000000000000) sage: phi(tau) (...e-16 - ...e-16*I : ...e-16 + ...e-16*I : 1.00000000000000) """ if prec is None: try: prec = z.parent().prec() except AttributeError: prec = 53 CC = ComplexField(prec) if z in QQ: raise NotImplementedError z = CC(z) if z.imag() <= 0: raise ValueError("Point must be in the upper half plane") # TODO: for very small imaginary part, maybe try to transform under # \Gamma_0(N) to a better representative? q = (2 * CC.gen() * CC.pi() * z).exp() # nterms'th term is less than 2**-(prec+10) (c.f. eclib code) nterms = (-(prec + 10) / q.abs().log2()).ceil() # Use Horner's rule to sum the integral of the form enumerated_an = list(enumerate(self._E.anlist(nterms)))[1:] lattice_point = 0 for n, an in reversed(enumerated_an): lattice_point += an / n lattice_point *= q return lattice_point
def _evalf_(self, x, parent_d=None): """ EXAMPLES:: sage: arg(0.0) 0.000000000000000 sage: arg(3.0) 0.000000000000000 sage: arg(3.00000000000000000000000000) 0.00000000000000000000000000 sage: arg(3.00000000000000000000000000).prec() 90 sage: arg(ComplexIntervalField(90)(3)).prec() 90 sage: arg(ComplexIntervalField(90)(3)).parent() Real Interval Field with 90 bits of precision sage: arg(3.0r) 0.000000000000000 sage: arg(RDF(3)) 0.0 sage: arg(RDF(3)).parent() Real Double Field sage: arg(-2.5) 3.14159265358979 sage: arg(2.0+3*i) 0.982793723247329 """ try: return x.arg() except AttributeError: pass # try to find a parent that support .arg() if parent_d is None: parent_d = parent(x) try: parent_d = parent_d.complex_field() except AttributeError: from sage.rings.complex_field import ComplexField try: parent_d = ComplexField(x.prec()) except AttributeError: parent_d = ComplexField() return parent_d(x).arg()
def _evalf_(self, x, y, parent=None, algorithm='mpmath'): """ EXAMPLES:: sage: gamma_inc_lower(3,2.) 0.646647167633873 sage: gamma_inc_lower(3,2).n(200) 0.646647167633873081060005050275155... sage: gamma_inc_lower(0,2.) +infinity """ R = parent or s_parent(x) # C is the complex version of R # prec is the precision of R if R is float: prec = 53 C = complex else: try: prec = R.precision() except AttributeError: prec = 53 try: C = R.complex_field() except AttributeError: C = R if algorithm == 'pari': try: v = ComplexField(prec)(x).gamma() - ComplexField(prec)(x).gamma_inc(y) except AttributeError: if not (is_ComplexNumber(x)): if is_ComplexNumber(y): C = y.parent() else: C = ComplexField() x = C(x) v = ComplexField(prec)(x).gamma() - ComplexField(prec)(x).gamma_inc(y) else: import mpmath v = ComplexField(prec)(mpmath_utils.call(mpmath.gammainc, x, 0, y, parent=R)) if v.is_real(): return R(v) else: return C(v)
def __init__(self, lfun, prec=None): """ Initialization of the L-function from a PARI L-function. INPUT: - lfun -- a PARI :pari:`lfun` object or an instance of :class:`lfun_generic` - prec -- integer (default: 53) number of *bits* of precision EXAMPLES:: 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 """ if isinstance(lfun, lfun_generic): # preparation using motivic data self._L = lfun.__pari__() if prec is None: prec = lfun.prec elif isinstance(lfun, Gen): # already some PARI lfun self._L = lfun else: # create a PARI lfunction from other input data self._L = pari.lfuncreate(lfun) self._conductor = ZZ(self._L[4]) # needs check self._weight = ZZ(self._L[3]) # needs check if prec is None: self.prec = 53 else: self.prec = PyNumber_Index(prec) self._RR = RealField(self.prec) self._CC = ComplexField(self.prec) # Complex field used for inputs, which ensures exact 1-to-1 # conversion to/from PARI. Since PARI objects have a precision # in machine words (not bits), this is typically higher. For # example, the default of 53 bits of precision would become 64. self._CCin = ComplexField(pari.bitprecision(self._RR(1)))
def _evalf_(self, x, y, parent=None): """ EXAMPLES:: sage: gamma_inc(0,2) -Ei(-2) sage: gamma_inc(0,2.) 0.0489005107080611 sage: gamma_inc(3,2).n() 1.35335283236613 """ try: return x.gamma_inc(y) except AttributeError: if not (is_ComplexNumber(x)): if is_ComplexNumber(y): C = y.parent() else: C = ComplexField() x = C(x) return x.gamma_inc(y)
def __init__(self, conductor, gammaV, weight, eps, \ poles=[], residues='automatic', prec=53, init=None): """ Initialization of Dokchitser calculator EXAMPLES:: sage: L = Dokchitser(conductor=1, gammaV=[0], weight=1, eps=1, poles=[1], residues=[-1], init='1') sage: L.num_coeffs() 4 """ self.conductor = conductor self.gammaV = gammaV self.weight = weight self.eps = eps self.poles = poles self.residues = residues self.prec = prec self.__CC = ComplexField(self.prec) self.__RR = self.__CC._real_field() self.__initialized = False if init is not None: self.init_coeffs(init)
def _evalf_(self, x, y, parent=None, algorithm='pari'): """ EXAMPLES:: sage: gamma_inc(0,2) -Ei(-2) sage: gamma_inc(0,2.) 0.0489005107080611 sage: gamma_inc(0,2).n(algorithm='pari') 0.0489005107080611 sage: gamma_inc(0,2).n(200) 0.048900510708061119567239835228... sage: gamma_inc(3,2).n() 1.35335283236613 TESTS: Check that :trac:`7099` is fixed:: sage: R = RealField(1024) sage: gamma(R(9), R(10^-3)) # rel tol 1e-308 40319.99999999999999999999999999988898884344822911869926361916294165058203634104838326009191542490601781777105678829520585311300510347676330951251563007679436243294653538925717144381702105700908686088851362675381239820118402497959018315224423868693918493033078310647199219674433536605771315869983788442389633 sage: numerical_approx(gamma(9, 10^(-3)) - gamma(9), digits=40) # abs tol 1e-36 -1.110111598370794007949063502542063148294e-28 Check that :trac:`17328` is fixed:: sage: gamma_inc(float(-1), float(-1)) (-0.8231640121031085+3.141592653589793j) sage: gamma_inc(RR(-1), RR(-1)) -0.823164012103109 + 3.14159265358979*I sage: gamma_inc(-1, float(-log(3))) - gamma_inc(-1, float(-log(2))) # abs tol 1e-15 (1.2730972164471142+0j) Check that :trac:`17130` is fixed:: sage: r = gamma_inc(float(0), float(1)); r 0.21938393439552029 sage: type(r) <... 'float'> """ R = parent or s_parent(x) # C is the complex version of R # prec is the precision of R if R is float: prec = 53 C = complex else: try: prec = R.precision() except AttributeError: prec = 53 try: C = R.complex_field() except AttributeError: C = R if algorithm == 'pari': v = ComplexField(prec)(x).gamma_inc(y) else: import mpmath v = ComplexField(prec)(mpmath_utils.call(mpmath.gammainc, x, y, parent=R)) if v.is_real(): return R(v) else: return C(v)
def _coerce_map_from_(self, P): r""" Return whether ``P`` coerces into this symbolic subring. INPUT: - ``P`` -- a parent. OUTPUT: A boolean or ``None``. TESTS:: sage: from sage.symbolic.subring import GenericSymbolicSubring sage: GenericSymbolicSubring(vars=tuple()).has_coerce_map_from(SR) # indirect doctest # not tested see #19231 False :: sage: from sage.symbolic.subring import SymbolicSubring sage: C = SymbolicSubring(no_variables=True) sage: C.has_coerce_map_from(ZZ) # indirect doctest True sage: C.has_coerce_map_from(QQ) # indirect doctest True sage: C.has_coerce_map_from(RR) # indirect doctest True sage: C.has_coerce_map_from(RIF) # indirect doctest True sage: C.has_coerce_map_from(CC) # indirect doctest True sage: C.has_coerce_map_from(CIF) # indirect doctest True sage: C.has_coerce_map_from(AA) # indirect doctest True sage: C.has_coerce_map_from(QQbar) # indirect doctest True sage: C.has_coerce_map_from(SR) # indirect doctest False """ if P == SR: # Workaround; can be deleted once #19231 is fixed return False from sage.rings.real_mpfr import mpfr_prec_min from sage.rings.all import (ComplexField, RLF, CLF, AA, QQbar, InfinityRing) from sage.rings.real_mpfi import is_RealIntervalField from sage.rings.complex_interval_field import is_ComplexIntervalField if isinstance(P, type): return SR._coerce_map_from_(P) elif RLF.has_coerce_map_from(P) or \ CLF.has_coerce_map_from(P) or \ AA.has_coerce_map_from(P) or \ QQbar.has_coerce_map_from(P): return True elif (P is InfinityRing or is_RealIntervalField(P) or is_ComplexIntervalField(P)): return True elif ComplexField(mpfr_prec_min()).has_coerce_map_from(P): return P not in (RLF, CLF, AA, QQbar)
def hilbert_class_polynomial(D, algorithm=None): r""" Return the Hilbert class polynomial for discriminant `D`. INPUT: - ``D`` (int) -- a negative integer congruent to 0 or 1 modulo 4. - ``algorithm`` (string, default None). OUTPUT: (integer polynomial) The Hilbert class polynomial for the discriminant `D`. ALGORITHM: - If ``algorithm`` = "arb" (default): Use Arb's implementation which uses complex interval arithmetic. - If ``algorithm`` = "sage": Use complex approximations to the roots. - If ``algorithm`` = "magma": Call the appropriate Magma function (if available). AUTHORS: - Sage implementation originally by Eduardo Ocampo Alvarez and AndreyTimofeev - Sage implementation corrected by John Cremona (using corrected precision bounds from Andreas Enge) - Magma implementation by David Kohel EXAMPLES:: sage: hilbert_class_polynomial(-4) x - 1728 sage: hilbert_class_polynomial(-7) x + 3375 sage: hilbert_class_polynomial(-23) x^3 + 3491750*x^2 - 5151296875*x + 12771880859375 sage: hilbert_class_polynomial(-37*4) x^2 - 39660183801072000*x - 7898242515936467904000000 sage: hilbert_class_polynomial(-37*4, algorithm="magma") # optional - magma x^2 - 39660183801072000*x - 7898242515936467904000000 sage: hilbert_class_polynomial(-163) x + 262537412640768000 sage: hilbert_class_polynomial(-163, algorithm="sage") x + 262537412640768000 sage: hilbert_class_polynomial(-163, algorithm="magma") # optional - magma x + 262537412640768000 TESTS:: sage: all([hilbert_class_polynomial(d, algorithm="arb") == \ ....: hilbert_class_polynomial(d, algorithm="sage") \ ....: for d in range(-1,-100,-1) if d%4 in [0,1]]) True """ if algorithm is None: algorithm = "arb" D = Integer(D) if D >= 0: raise ValueError("D (=%s) must be negative" % D) if not (D % 4 in [0, 1]): raise ValueError("D (=%s) must be a discriminant" % D) if algorithm == "arb": import sage.libs.arb.arith return sage.libs.arb.arith.hilbert_class_polynomial(D) if algorithm == "magma": magma.eval("R<x> := PolynomialRing(IntegerRing())") f = str(magma.eval("HilbertClassPolynomial(%s)" % D)) return IntegerRing()['x'](f) if algorithm != "sage": raise ValueError("%s is not a valid algorithm" % algorithm) from sage.quadratic_forms.binary_qf import BinaryQF_reduced_representatives from sage.rings.all import RR, ComplexField from sage.functions.all import elliptic_j # get all primitive reduced quadratic forms, (necessary to exclude # imprimitive forms when D is not a fundamental discriminant): rqf = BinaryQF_reduced_representatives(D, primitive_only=True) # compute needed precision # # NB: [https://arxiv.org/abs/0802.0979v1], quoting Enge (2006), is # incorrect. Enge writes (2009-04-20 email to John Cremona) "The # source is my paper on class polynomials # [https://hal.inria.fr/inria-00001040] It was pointed out to me by # the referee after ANTS that the constant given there was # wrong. The final version contains a corrected constant on p.7 # which is consistent with your example. It says: # "The logarithm of the absolute value of the coefficient in front # of X^j is bounded above by # # log (2*k_2) * h + pi * sqrt(|D|) * sum (1/A_i) # # independently of j", where k_2 \approx 10.163. h = len(rqf) # class number c1 = 3.05682737291380 # log(2*10.63) c2 = sum([1 / RR(qf[0]) for qf in rqf], RR(0)) prec = c2 * RR(3.142) * RR(D).abs().sqrt() + h * c1 # bound on log prec = prec * 1.45 # bound on log_2 (1/log(2) = 1.44..) prec = 10 + prec.ceil() # allow for rounding error # set appropriate precision for further computing Dsqrt = D.sqrt(prec=prec) R = ComplexField(prec)['t'] t = R.gen() pol = R(1) for qf in rqf: a, b, c = list(qf) tau = (b + Dsqrt) / (a << 1) pol *= (t - elliptic_j(tau)) coeffs = [cof.real().round() for cof in pol.coefficients(sparse=False)] return IntegerRing()['x'](coeffs)
def hilbert_class_polynomial(D, algorithm=None): r""" Returns the Hilbert class polynomial for discriminant `D`. INPUT: - ``D`` (int) -- a negative integer congruent to 0 or 1 modulo 4. - ``algorithm`` (string, default None). OUTPUT: (integer polynomial) The Hilbert class polynomial for the discriminant `D`. ALGORITHM: - If ``algorithm`` = "arb" (default): Use Arb's implementation which uses complex interval arithmetic. - If ``algorithm`` = "sage": Use complex approximations to the roots. - If ``algorithm`` = "magma": Call the appropriate Magma function (if available). AUTHORS: - Sage implementation originally by Eduardo Ocampo Alvarez and AndreyTimofeev - Sage implementation corrected by John Cremona (using corrected precision bounds from Andreas Enge) - Magma implementation by David Kohel EXAMPLES:: sage: hilbert_class_polynomial(-4) x - 1728 sage: hilbert_class_polynomial(-7) x + 3375 sage: hilbert_class_polynomial(-23) x^3 + 3491750*x^2 - 5151296875*x + 12771880859375 sage: hilbert_class_polynomial(-37*4) x^2 - 39660183801072000*x - 7898242515936467904000000 sage: hilbert_class_polynomial(-37*4, algorithm="magma") # optional - magma x^2 - 39660183801072000*x - 7898242515936467904000000 sage: hilbert_class_polynomial(-163) x + 262537412640768000 sage: hilbert_class_polynomial(-163, algorithm="sage") x + 262537412640768000 sage: hilbert_class_polynomial(-163, algorithm="magma") # optional - magma x + 262537412640768000 TESTS:: sage: all([hilbert_class_polynomial(d, algorithm="arb") == \ ....: hilbert_class_polynomial(d, algorithm="sage") \ ....: for d in range(-1,-100,-1) if d%4 in [0,1]]) True """ if algorithm is None: algorithm = "arb" D = Integer(D) if D >= 0: raise ValueError("D (=%s) must be negative"%D) if not (D%4 in [0,1]): raise ValueError("D (=%s) must be a discriminant"%D) if algorithm == "arb": import sage.libs.arb.arith return sage.libs.arb.arith.hilbert_class_polynomial(D) if algorithm == "magma": magma.eval("R<x> := PolynomialRing(IntegerRing())") f = str(magma.eval("HilbertClassPolynomial(%s)"%D)) return IntegerRing()['x'](f) if algorithm != "sage": raise ValueError("%s is not a valid algorithm"%algorithm) from sage.quadratic_forms.binary_qf import BinaryQF_reduced_representatives from sage.rings.all import RR, ZZ, ComplexField from sage.functions.all import elliptic_j # get all primitive reduced quadratic forms, (necessary to exclude # imprimitive forms when D is not a fundamental discriminant): rqf = BinaryQF_reduced_representatives(D, primitive_only=True) # compute needed precision # # NB: [http://arxiv.org/abs/0802.0979v1], quoting Enge (2006), is # incorrect. Enge writes (2009-04-20 email to John Cremona) "The # source is my paper on class polynomials # [http://hal.inria.fr/inria-00001040] It was pointed out to me by # the referee after ANTS that the constant given there was # wrong. The final version contains a corrected constant on p.7 # which is consistent with your example. It says: # "The logarithm of the absolute value of the coefficient in front # of X^j is bounded above by # # log (2*k_2) * h + pi * sqrt(|D|) * sum (1/A_i) # # independently of j", where k_2 \approx 10.163. h = len(rqf) # class number c1 = 3.05682737291380 # log(2*10.63) c2 = sum([1/RR(qf[0]) for qf in rqf], RR(0)) prec = c2*RR(3.142)*RR(D).abs().sqrt() + h*c1 # bound on log prec = prec * 1.45 # bound on log_2 (1/log(2) = 1.44..) prec = 10 + prec.ceil() # allow for rounding error # set appropriate precision for further computing Dsqrt = D.sqrt(prec=prec) R = ComplexField(prec)['t'] t = R.gen() pol = R(1) for qf in rqf: a, b, c = list(qf) tau = (b+Dsqrt)/(a<<1) pol *= (t - elliptic_j(tau)) coeffs = [cof.real().round() for cof in pol.coefficients(sparse=False)] return IntegerRing()['x'](coeffs)
class Dokchitser(SageObject): r""" Dokchitser's `L`-functions Calculator Create a Dokchitser `L`-series with Dokchitser(conductor, gammaV, weight, eps, poles, residues, init, prec) where - ``conductor`` - integer, the conductor - ``gammaV`` - list of Gamma-factor parameters, e.g. [0] for Riemann zeta, [0,1] for ell.curves, (see examples). - ``weight`` - positive real number, usually an integer e.g. 1 for Riemann zeta, 2 for `H^1` of curves/`\QQ` - ``eps`` - complex number; sign in functional equation - ``poles`` - (default: []) list of points where `L^*(s)` has (simple) poles; only poles with `Re(s)>weight/2` should be included - ``residues`` - vector of residues of `L^*(s)` in those poles or set residues='automatic' (default value) - ``prec`` - integer (default: 53) number of *bits* of precision RIEMANN ZETA FUNCTION: We compute with the Riemann Zeta function. :: sage: L = Dokchitser(conductor=1, gammaV=[0], weight=1, eps=1, poles=[1], residues=[-1], init='1') sage: L Dokchitser L-series of conductor 1 and weight 1 sage: L(1) Traceback (most recent call last): ... ArithmeticError sage: L(2) 1.64493406684823 sage: L(2, 1.1) 1.64493406684823 sage: L.derivative(2) -0.937548254315844 sage: h = RR('0.0000000000001') sage: (zeta(2+h) - zeta(2.))/h -0.937028232783632 sage: L.taylor_series(2, k=5) 1.64493406684823 - 0.937548254315844*z + 0.994640117149451*z^2 - 1.00002430047384*z^3 + 1.00006193307...*z^4 + O(z^5) RANK 1 ELLIPTIC CURVE: We compute with the `L`-series of a rank `1` curve. :: sage: E = EllipticCurve('37a') sage: L = E.lseries().dokchitser(); L Dokchitser L-function associated to Elliptic Curve defined by y^2 + y = x^3 - x over Rational Field sage: L(1) 0.000000000000000 sage: L.derivative(1) 0.305999773834052 sage: L.derivative(1,2) 0.373095594536324 sage: L.num_coeffs() 48 sage: L.taylor_series(1,4) 0.000000000000000 + 0.305999773834052*z + 0.186547797268162*z^2 - 0.136791463097188*z^3 + O(z^4) sage: L.check_functional_equation() 6.11218974700000e-18 # 32-bit 6.04442711160669e-18 # 64-bit RANK 2 ELLIPTIC CURVE: We compute the leading coefficient and Taylor expansion of the `L`-series of a rank `2` curve. :: sage: E = EllipticCurve('389a') sage: L = E.lseries().dokchitser() sage: L.num_coeffs () 156 sage: L.derivative(1,E.rank()) 1.51863300057685 sage: L.taylor_series(1,4) -1.27685190980159e-23 + (7.23588070754027e-24)*z + 0.759316500288427*z^2 - 0.430302337583362*z^3 + O(z^4) # 32-bit -2.72911738151096e-23 + (1.54658247036311e-23)*z + 0.759316500288427*z^2 - 0.430302337583362*z^3 + O(z^4) # 64-bit RAMANUJAN DELTA L-FUNCTION: The coefficients are given by Ramanujan's tau function:: sage: L = Dokchitser(conductor=1, gammaV=[0,1], weight=12, eps=1) sage: pari_precode = 'tau(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: L.init_coeffs('tau(k)', pari_precode=pari_precode) We redefine the default bound on the coefficients: Deligne's estimate on tau(n) is better than the default coefgrow(n)=`(4n)^{11/2}` (by a factor 1024), so re-defining coefgrow() improves efficiency (slightly faster). :: sage: L.num_coeffs() 12 sage: L.set_coeff_growth('2*n^(11/2)') sage: L.num_coeffs() 11 Now we're ready to evaluate, etc. :: sage: L(1) 0.0374412812685155 sage: L(1, 1.1) 0.0374412812685155 sage: L.taylor_series(1,3) 0.0374412812685155 + 0.0709221123619322*z + 0.0380744761270520*z^2 + O(z^3) """ def __init__(self, conductor, gammaV, weight, eps, \ poles=[], residues='automatic', prec=53, init=None): """ Initialization of Dokchitser calculator EXAMPLES:: sage: L = Dokchitser(conductor=1, gammaV=[0], weight=1, eps=1, poles=[1], residues=[-1], init='1') sage: L.num_coeffs() 4 """ self.conductor = conductor self.gammaV = gammaV self.weight = weight self.eps = eps self.poles = poles self.residues = residues self.prec = prec self.__CC = ComplexField(self.prec) self.__RR = self.__CC._real_field() if not init is None: self.init_coeffs(init) self.__init = init else: self.__init = False def __reduce__(self): D = copy.copy(self.__dict__) if '_Dokchitser__gp' in D: del D['_Dokchitser__gp'] return reduce_load_dokchitser, (D, ) def _repr_(self): z = "Dokchitser L-series of conductor %s and weight %s"%( self.conductor, self.weight) return z def __del__(self): self.gp().quit() def gp(self): """ Return the gp interpreter that is used to implement this Dokchitser L-function. EXAMPLES:: sage: E = EllipticCurve('11a') sage: L = E.lseries().dokchitser() sage: L(2) 0.546048036215014 sage: L.gp() PARI/GP interpreter """ try: return self.__gp except AttributeError: logfile = None # For debugging import os from sage.env import DOT_SAGE logfile = os.path.join(DOT_SAGE, 'dokchitser.log') g = sage.interfaces.gp.Gp(script_subdirectory='dokchitser', logfile=logfile) g.read('computel.gp') self.__gp = g self._gp_eval('default(realprecision, %s)'%(self.prec//3 + 2)) self._gp_eval('conductor = %s'%self.conductor) self._gp_eval('gammaV = %s'%self.gammaV) self._gp_eval('weight = %s'%self.weight) self._gp_eval('sgn = %s'%self.eps) self._gp_eval('Lpoles = %s'%self.poles) self._gp_eval('Lresidues = %s'%self.residues) g._dokchitser = True return g def _gp_eval(self, s): try: t = self.gp().eval(s) except (RuntimeError, TypeError): raise RuntimeError("Unable to create L-series, due to precision or other limits in PARI.") if '***' in t: raise RuntimeError("Unable to create L-series, due to precision or other limits in PARI.") return t def __check_init(self): if not self.__init: raise ValueError("you must call init_coeffs on the L-function first") 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() sage: L.num_coeffs() 26 sage: E = EllipticCurve('5077a') sage: L = E.lseries().dokchitser() sage: L.num_coeffs() 568 sage: L = Dokchitser(conductor=1, gammaV=[0], weight=1, eps=1, poles=[1], residues=[-1], init='1') sage: L.num_coeffs() 4 """ return Integer(self.gp().eval('cflength(%s)'%T)) def init_coeffs(self, v, cutoff=1, w=None, pari_precode='', max_imaginary_part=0, max_asymp_coeffs=40): """ 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 string (pari function of k) - ``cutoff`` - real number = 1 (default: 1) - ``w`` - list of complex numbers or string (pari function of k) - ``pari_precode`` - some code to execute in pari before calling initLdata - ``max_imaginary_part`` - (default: 0): redefine if you want to compute L(s) for s having large imaginary part, - ``max_asymp_coeffs`` - (default: 40): at most this many terms are generated in asymptotic series for phi(t) and G(s,t). EXAMPLES:: sage: L = Dokchitser(conductor=1, gammaV=[0,1], weight=12, eps=1) sage: pari_precode = 'tau(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: L.init_coeffs('tau(k)', pari_precode=pari_precode) 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(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 = Dokchitser(conductor=1, gammaV=[0,1], weight=12, eps=1) sage: L2.init_coeffs(list(delta_qexp(1000))[1:]) sage: L2(14) 0.998583063162746 TESTS: Verify that setting the `w` parameter does not raise an error (see :trac:`10937`). Note that the meaning of `w` does not seem to be documented anywhere in Dokchitser's package yet, so there is no claim that the example below is meaningful! :: sage: L2 = Dokchitser(conductor=1, gammaV=[0,1], weight=12, eps=1) sage: L2.init_coeffs(list(delta_qexp(1000))[1:], w=[1..1000]) """ if isinstance(v, tuple) and w is None: v, cutoff, w, pari_precode, max_imaginary_part, max_asymp_coeffs = v self.__init = (v, cutoff, w, pari_precode, max_imaginary_part, max_asymp_coeffs) gp = self.gp() if pari_precode != '': self._gp_eval(pari_precode) RR = self.__CC._real_field() cutoff = RR(cutoff) if isinstance(v, str): if w is None: self._gp_eval('initLdata("%s", %s)'%(v, cutoff)) return self._gp_eval('initLdata("%s",%s,"%s")'%(v,cutoff,w)) return if not isinstance(v, (list, tuple)): raise TypeError("v (=%s) must be a list, tuple, or string"%v) CC = self.__CC v = ','.join([CC(a)._pari_init_() for a in v]) self._gp_eval('Avec = [%s]'%v) if w is None: self._gp_eval('initLdata("Avec[k]", %s)'%cutoff) return w = ','.join([CC(a)._pari_init_() for a in w]) self._gp_eval('Bvec = [%s]'%w) self._gp_eval('initLdata("Avec[k]",%s,"Bvec[k]")'%cutoff) def __to_CC(self, s): s = s.replace('.E','.0E').replace(' ','') return self.__CC(sage_eval(s, locals={'I':self.__CC.gen(0)})) def _clear_value_cache(self): del self.__values def __call__(self, s, c=None): r""" INPUT: - ``s`` - complex number .. note:: Evaluation of the function takes a long time, so each evaluation is cached. Call ``self._clear_value_cache()`` to clear the evaluation cache. EXAMPLES:: sage: E = EllipticCurve('5077a') sage: L = E.lseries().dokchitser(100) sage: L(1) 0.00000000000000000000000000000 sage: L(1+I) -1.3085436607849493358323930438 + 0.81298000036784359634835412129*I """ self.__check_init() s = self.__CC(s) try: return self.__values[s] except AttributeError: self.__values = {} except KeyError: pass z = self.gp().eval('L(%s)'%s) if 'pole' in z: print(z) raise ArithmeticError elif '***' in z: print(z) raise RuntimeError elif 'Warning' in z: i = z.rfind('\n') msg = z[:i].replace('digits','decimal digits') verbose(msg, level=-1) ans = self.__to_CC(z[i+1:]) self.__values[s] = ans return ans ans = self.__to_CC(z) self.__values[s] = ans return ans def derivative(self, s, k=1): r""" Return the `k`-th derivative of the `L`-series at `s`. .. warning:: If `k` is greater than the order of vanishing of `L` at `s` you may get nonsense. EXAMPLES:: sage: E = EllipticCurve('389a') sage: L = E.lseries().dokchitser() sage: L.derivative(1,E.rank()) 1.51863300057685 """ self.__check_init() s = self.__CC(s) k = Integer(k) z = self.gp().eval('L(%s,,%s)'%(s,k)) if 'pole' in z: raise ArithmeticError(z) elif 'Warning' in z: i = z.rfind('\n') msg = z[:i].replace('digits','decimal digits') verbose(msg, level=-1) return self.__CC(z[i:]) return self.__CC(z) def taylor_series(self, a=0, k=6, var='z'): r""" Return the first `k` terms of the Taylor series expansion of the `L`-series about `a`. This is returned as a series in ``var``, where you should view ``var`` as equal to `s-a`. Thus this function returns the formal power series whose coefficients are `L^{(n)}(a)/n!`. INPUT: - ``a`` - complex number (default: 0); point about which to expand - ``k`` - integer (default: 6), series is `O(``var``^k)` - ``var`` - string (default: 'z'), variable of power series EXAMPLES:: sage: L = Dokchitser(conductor=1, gammaV=[0], weight=1, eps=1, poles=[1], residues=[-1], init='1') sage: L.taylor_series(2, 3) 1.64493406684823 - 0.937548254315844*z + 0.994640117149451*z^2 + O(z^3) sage: E = EllipticCurve('37a') sage: L = E.lseries().dokchitser() sage: L.taylor_series(1) 0.000000000000000 + 0.305999773834052*z + 0.186547797268162*z^2 - 0.136791463097188*z^3 + 0.0161066468496401*z^4 + 0.0185955175398802*z^5 + O(z^6) We compute a Taylor series where each coefficient is to high precision. :: sage: E = EllipticCurve('389a') sage: L = E.lseries().dokchitser(200) sage: L.taylor_series(1,3) -9.094...e-82 + (5.1538...e-82)*z + 0.75931650028842677023019260789472201907809751649492435158581*z^2 + O(z^3) """ self.__check_init() a = self.__CC(a) k = Integer(k) try: z = self.gp()('Vec(Lseries(%s,,%s))'%(a,k-1)) except TypeError as msg: raise RuntimeError("%s\nUnable to compute Taylor expansion (try lowering the number of terms)"%msg) r = repr(z) if 'pole' in r: raise ArithmeticError(r) elif 'Warning' in r: i = r.rfind('\n') msg = r[:i].replace('digits','decimal digits') verbose(msg, level=-1) v = list(z) K = self.__CC v = [K(repr(x)) for x in v] R = self.__CC[[var]] return R(v,len(v)) def check_functional_equation(self, T=1.2): r""" Verifies how well numerically the functional equation is satisfied, and also determines the residues if ``self.poles != []`` and residues='automatic'. More specifically: for `T>1` (default 1.2), ``self.check_functional_equation(T)`` should ideally return 0 (to the current precision). - if what this function returns does not look like 0 at all, probably the functional equation is wrong (i.e. some of the parameters gammaV, conductor etc., or the coefficients are wrong), - if checkfeq(T) is to be used, more coefficients have to be generated (approximately T times more), e.g. call cflength(1.3), initLdata("a(k)",1.3), checkfeq(1.3) - T=1 always (!) returns 0, so T has to be away from 1 - default value `T=1.2` seems to give a reasonable balance - if you don't have to verify the functional equation or the L-values, call num_coeffs(1) and initLdata("a(k)",1), you need slightly less coefficients. EXAMPLES:: sage: L = Dokchitser(conductor=1, gammaV=[0], weight=1, eps=1, poles=[1], residues=[-1], init='1') sage: L.check_functional_equation() -1.35525271600000e-20 # 32-bit -2.71050543121376e-20 # 64-bit If we choose the sign in functional equation for the `\zeta` function incorrectly, the functional equation doesn't check out. :: sage: L = Dokchitser(conductor=1, gammaV=[0], weight=1, eps=-11, poles=[1], residues=[-1], init='1') sage: L.check_functional_equation() -9.73967861488124 """ self.__check_init() z = self.gp().eval('checkfeq(%s)'%T).replace(' ','') return self.__CC(z) def set_coeff_growth(self, coefgrow): r""" You might have to redefine the coefficient growth function if the `a_n` of the `L`-series are not given by the following PARI function:: coefgrow(n) = if(length(Lpoles), 1.5*n^(vecmax(real(Lpoles))-1), sqrt(4*n)^(weight-1)); INPUT: - ``coefgrow`` - string that evaluates to a PARI function of n that defines a coefgrow function. EXAMPLE:: sage: L = Dokchitser(conductor=1, gammaV=[0,1], weight=12, eps=1) sage: pari_precode = 'tau(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: L.init_coeffs('tau(k)', pari_precode=pari_precode) sage: L.set_coeff_growth('2*n^(11/2)') sage: L(1) 0.0374412812685155 """ if not isinstance(coefgrow, str): raise TypeError("coefgrow must be a string") g = self.gp() g.eval('coefgrow(n) = %s'%(coefgrow.replace('\n',' ')))
class Dokchitser(SageObject): r""" Dokchitser's `L`-functions Calculator Create a Dokchitser `L`-series with Dokchitser(conductor, gammaV, weight, eps, poles, residues, init, prec) where - ``conductor`` - integer, the conductor - ``gammaV`` - list of Gamma-factor parameters, e.g. [0] for Riemann zeta, [0,1] for ell.curves, (see examples). - ``weight`` - positive real number, usually an integer e.g. 1 for Riemann zeta, 2 for `H^1` of curves/`\QQ` - ``eps`` - complex number; sign in functional equation - ``poles`` - (default: []) list of points where `L^*(s)` has (simple) poles; only poles with `Re(s)>weight/2` should be included - ``residues`` - vector of residues of `L^*(s)` in those poles or set residues='automatic' (default value) - ``prec`` - integer (default: 53) number of *bits* of precision RIEMANN ZETA FUNCTION: We compute with the Riemann Zeta function. :: sage: L = Dokchitser(conductor=1, gammaV=[0], weight=1, eps=1, poles=[1], residues=[-1], init='1') sage: L Dokchitser L-series of conductor 1 and weight 1 sage: L(1) Traceback (most recent call last): ... ArithmeticError sage: L(2) 1.64493406684823 sage: L(2, 1.1) 1.64493406684823 sage: L.derivative(2) -0.937548254315844 sage: h = RR('0.0000000000001') sage: (zeta(2+h) - zeta(2.))/h -0.937028232783632 sage: L.taylor_series(2, k=5) 1.64493406684823 - 0.937548254315844*z + 0.994640117149451*z^2 - 1.00002430047384*z^3 + 1.00006193307...*z^4 + O(z^5) RANK 1 ELLIPTIC CURVE: We compute with the `L`-series of a rank `1` curve. :: sage: E = EllipticCurve('37a') sage: L = E.lseries().dokchitser(); L Dokchitser L-function associated to Elliptic Curve defined by y^2 + y = x^3 - x over Rational Field sage: L(1) 0.000000000000000 sage: L.derivative(1) 0.305999773834052 sage: L.derivative(1,2) 0.373095594536324 sage: L.num_coeffs() 48 sage: L.taylor_series(1,4) 0.000000000000000 + 0.305999773834052*z + 0.186547797268162*z^2 - 0.136791463097188*z^3 + O(z^4) sage: L.check_functional_equation() 6.11218974700000e-18 # 32-bit 6.04442711160669e-18 # 64-bit RANK 2 ELLIPTIC CURVE: We compute the leading coefficient and Taylor expansion of the `L`-series of a rank `2` curve. :: sage: E = EllipticCurve('389a') sage: L = E.lseries().dokchitser() sage: L.num_coeffs () 156 sage: L.derivative(1,E.rank()) 1.51863300057685 sage: L.taylor_series(1,4) -1.27685190980159e-23 + (7.23588070754027e-24)*z + 0.759316500288427*z^2 - 0.430302337583362*z^3 + O(z^4) # 32-bit -2.72911738151096e-23 + (1.54658247036311e-23)*z + 0.759316500288427*z^2 - 0.430302337583362*z^3 + O(z^4) # 64-bit RAMANUJAN DELTA L-FUNCTION: The coefficients are given by Ramanujan's tau function:: sage: L = Dokchitser(conductor=1, gammaV=[0,1], weight=12, eps=1) sage: pari_precode = 'tau(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: L.init_coeffs('tau(k)', pari_precode=pari_precode) We redefine the default bound on the coefficients: Deligne's estimate on tau(n) is better than the default coefgrow(n)=`(4n)^{11/2}` (by a factor 1024), so re-defining coefgrow() improves efficiency (slightly faster). :: sage: L.num_coeffs() 12 sage: L.set_coeff_growth('2*n^(11/2)') sage: L.num_coeffs() 11 Now we're ready to evaluate, etc. :: sage: L(1) 0.0374412812685155 sage: L(1, 1.1) 0.0374412812685155 sage: L.taylor_series(1,3) 0.0374412812685155 + 0.0709221123619322*z + 0.0380744761270520*z^2 + O(z^3) """ def __init__(self, conductor, gammaV, weight, eps, \ poles=[], residues='automatic', prec=53, init=None): """ Initialization of Dokchitser calculator EXAMPLES:: sage: L = Dokchitser(conductor=1, gammaV=[0], weight=1, eps=1, poles=[1], residues=[-1], init='1') sage: L.num_coeffs() 4 """ self.conductor = conductor self.gammaV = gammaV self.weight = weight self.eps = eps self.poles = poles self.residues = residues self.prec = prec self.__CC = ComplexField(self.prec) self.__RR = self.__CC._real_field() if not init is None: self.init_coeffs(init) self.__init = init else: self.__init = False def __reduce__(self): D = copy.copy(self.__dict__) if '_Dokchitser__gp' in D: del D['_Dokchitser__gp'] return reduce_load_dokchitser, (D, ) def _repr_(self): z = "Dokchitser L-series of conductor %s and weight %s"%( self.conductor, self.weight) return z def __del__(self): self.gp().quit() def gp(self): """ Return the gp interpreter that is used to implement this Dokchitser L-function. EXAMPLES:: sage: E = EllipticCurve('11a') sage: L = E.lseries().dokchitser() sage: L(2) 0.546048036215014 sage: L.gp() PARI/GP interpreter """ try: return self.__gp except AttributeError: logfile = None # For debugging import os from sage.env import DOT_SAGE logfile = os.path.join(DOT_SAGE, 'dokchitser.log') g = sage.interfaces.gp.Gp(script_subdirectory='dokchitser', logfile=logfile) g.read('computel.gp') self.__gp = g self._gp_eval('default(realprecision, %s)'%(self.prec//3 + 2)) self._gp_eval('conductor = %s'%self.conductor) self._gp_eval('gammaV = %s'%self.gammaV) self._gp_eval('weight = %s'%self.weight) self._gp_eval('sgn = %s'%self.eps) self._gp_eval('Lpoles = %s'%self.poles) self._gp_eval('Lresidues = %s'%self.residues) g._dokchitser = True return g def _gp_eval(self, s): try: t = self.gp().eval(s) except (RuntimeError, TypeError): raise RuntimeError("Unable to create L-series, due to precision or other limits in PARI.") if '***' in t: raise RuntimeError("Unable to create L-series, due to precision or other limits in PARI.") return t def __check_init(self): if not self.__init: raise ValueError("you must call init_coeffs on the L-function first") 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() sage: L.num_coeffs() 26 sage: E = EllipticCurve('5077a') sage: L = E.lseries().dokchitser() sage: L.num_coeffs() 568 sage: L = Dokchitser(conductor=1, gammaV=[0], weight=1, eps=1, poles=[1], residues=[-1], init='1') sage: L.num_coeffs() 4 """ return Integer(self.gp().eval('cflength(%s)'%T)) def init_coeffs(self, v, cutoff=1, w=None, pari_precode='', max_imaginary_part=0, max_asymp_coeffs=40): """ 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 string (pari function of k) - ``cutoff`` - real number = 1 (default: 1) - ``w`` - list of complex numbers or string (pari function of k) - ``pari_precode`` - some code to execute in pari before calling initLdata - ``max_imaginary_part`` - (default: 0): redefine if you want to compute L(s) for s having large imaginary part, - ``max_asymp_coeffs`` - (default: 40): at most this many terms are generated in asymptotic series for phi(t) and G(s,t). EXAMPLES:: sage: L = Dokchitser(conductor=1, gammaV=[0,1], weight=12, eps=1) sage: pari_precode = 'tau(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: L.init_coeffs('tau(k)', pari_precode=pari_precode) 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(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 = Dokchitser(conductor=1, gammaV=[0,1], weight=12, eps=1) sage: L2.init_coeffs(list(delta_qexp(1000))[1:]) sage: L2(14) 0.998583063162746 TESTS: Verify that setting the `w` parameter does not raise an error (see :trac:`10937`). Note that the meaning of `w` does not seem to be documented anywhere in Dokchitser's package yet, so there is no claim that the example below is meaningful! :: sage: L2 = Dokchitser(conductor=1, gammaV=[0,1], weight=12, eps=1) sage: L2.init_coeffs(list(delta_qexp(1000))[1:], w=[1..1000]) """ if isinstance(v, tuple) and w is None: v, cutoff, w, pari_precode, max_imaginary_part, max_asymp_coeffs = v self.__init = (v, cutoff, w, pari_precode, max_imaginary_part, max_asymp_coeffs) gp = self.gp() if pari_precode != '': self._gp_eval(pari_precode) RR = self.__CC._real_field() cutoff = RR(cutoff) if isinstance(v, str): if w is None: self._gp_eval('initLdata("%s", %s)'%(v, cutoff)) return self._gp_eval('initLdata("%s",%s,"%s")'%(v,cutoff,w)) return if not isinstance(v, (list, tuple)): raise TypeError("v (=%s) must be a list, tuple, or string"%v) CC = self.__CC v = ','.join([CC(a)._pari_init_() for a in v]) self._gp_eval('Avec = [%s]'%v) if w is None: self._gp_eval('initLdata("Avec[k]", %s)'%cutoff) return w = ','.join([CC(a)._pari_init_() for a in w]) self._gp_eval('Bvec = [%s]'%w) self._gp_eval('initLdata("Avec[k]",%s,"Bvec[k]")'%cutoff) def __to_CC(self, s): s = s.replace('.E','.0E').replace(' ','') return self.__CC(sage_eval(s, locals={'I':self.__CC.gen(0)})) def _clear_value_cache(self): del self.__values def __call__(self, s, c=None): r""" INPUT: - ``s`` - complex number .. note:: Evaluation of the function takes a long time, so each evaluation is cached. Call ``self._clear_value_cache()`` to clear the evaluation cache. EXAMPLES:: sage: E = EllipticCurve('5077a') sage: L = E.lseries().dokchitser(100) sage: L(1) 0.00000000000000000000000000000 sage: L(1+I) -1.3085436607849493358323930438 + 0.81298000036784359634835412129*I """ self.__check_init() s = self.__CC(s) try: return self.__values[s] except AttributeError: self.__values = {} except KeyError: pass z = self.gp().eval('L(%s)'%s) if 'pole' in z: print(z) raise ArithmeticError elif '***' in z: print(z) raise RuntimeError elif 'Warning' in z: i = z.rfind('\n') msg = z[:i].replace('digits','decimal digits') verbose(msg, level=-1) ans = self.__to_CC(z[i+1:]) self.__values[s] = ans return ans ans = self.__to_CC(z) self.__values[s] = ans return ans def derivative(self, s, k=1): r""" Return the `k`-th derivative of the `L`-series at `s`. .. warning:: If `k` is greater than the order of vanishing of `L` at `s` you may get nonsense. EXAMPLES:: sage: E = EllipticCurve('389a') sage: L = E.lseries().dokchitser() sage: L.derivative(1,E.rank()) 1.51863300057685 """ self.__check_init() s = self.__CC(s) k = Integer(k) z = self.gp().eval('L(%s,,%s)'%(s,k)) if 'pole' in z: raise ArithmeticError(z) elif 'Warning' in z: i = z.rfind('\n') msg = z[:i].replace('digits','decimal digits') verbose(msg, level=-1) return self.__CC(z[i:]) return self.__CC(z) def taylor_series(self, a=0, k=6, var='z'): r""" Return the first `k` terms of the Taylor series expansion of the `L`-series about `a`. This is returned as a series in ``var``, where you should view ``var`` as equal to `s-a`. Thus this function returns the formal power series whose coefficients are `L^{(n)}(a)/n!`. INPUT: - ``a`` - complex number (default: 0); point about which to expand - ``k`` - integer (default: 6), series is `O(``var``^k)` - ``var`` - string (default: 'z'), variable of power series EXAMPLES:: sage: L = Dokchitser(conductor=1, gammaV=[0], weight=1, eps=1, poles=[1], residues=[-1], init='1') sage: L.taylor_series(2, 3) 1.64493406684823 - 0.937548254315844*z + 0.994640117149451*z^2 + O(z^3) sage: E = EllipticCurve('37a') sage: L = E.lseries().dokchitser() sage: L.taylor_series(1) 0.000000000000000 + 0.305999773834052*z + 0.186547797268162*z^2 - 0.136791463097188*z^3 + 0.0161066468496401*z^4 + 0.0185955175398802*z^5 + O(z^6) We compute a Taylor series where each coefficient is to high precision. :: sage: E = EllipticCurve('389a') sage: L = E.lseries().dokchitser(200) sage: L.taylor_series(1,3) -9.094...e-82 + (5.1538...e-82)*z + 0.75931650028842677023019260789472201907809751649492435158581*z^2 + O(z^3) """ self.__check_init() a = self.__CC(a) k = Integer(k) try: z = self.gp()('Vec(Lseries(%s,,%s))'%(a,k-1)) except TypeError as msg: raise RuntimeError("%s\nUnable to compute Taylor expansion (try lowering the number of terms)"%msg) r = repr(z) if 'pole' in r: raise ArithmeticError(r) elif 'Warning' in r: i = r.rfind('\n') msg = r[:i].replace('digits','decimal digits') verbose(msg, level=-1) v = list(z) K = self.__CC v = [K(repr(x)) for x in v] R = self.__CC[[var]] return R(v,len(v)) def check_functional_equation(self, T=1.2): r""" Verifies how well numerically the functional equation is satisfied, and also determines the residues if ``self.poles != []`` and residues='automatic'. More specifically: for `T>1` (default 1.2), ``self.check_functional_equation(T)`` should ideally return 0 (to the current precision). - if what this function returns does not look like 0 at all, probably the functional equation is wrong (i.e. some of the parameters gammaV, conductor etc., or the coefficients are wrong), - if checkfeq(T) is to be used, more coefficients have to be generated (approximately T times more), e.g. call cflength(1.3), initLdata("a(k)",1.3), checkfeq(1.3) - T=1 always (!) returns 0, so T has to be away from 1 - default value `T=1.2` seems to give a reasonable balance - if you don't have to verify the functional equation or the L-values, call num_coeffs(1) and initLdata("a(k)",1), you need slightly less coefficients. EXAMPLES:: sage: L = Dokchitser(conductor=1, gammaV=[0], weight=1, eps=1, poles=[1], residues=[-1], init='1') sage: L.check_functional_equation() -1.35525271600000e-20 # 32-bit -2.71050543121376e-20 # 64-bit If we choose the sign in functional equation for the `\zeta` function incorrectly, the functional equation doesn't check out. :: sage: L = Dokchitser(conductor=1, gammaV=[0], weight=1, eps=-11, poles=[1], residues=[-1], init='1') sage: L.check_functional_equation() -9.73967861488124 """ self.__check_init() z = self.gp().eval('checkfeq(%s)'%T).replace(' ','') return self.__CC(z) def set_coeff_growth(self, coefgrow): r""" You might have to redefine the coefficient growth function if the `a_n` of the `L`-series are not given by the following PARI function:: coefgrow(n) = if(length(Lpoles), 1.5*n^(vecmax(real(Lpoles))-1), sqrt(4*n)^(weight-1)); INPUT: - ``coefgrow`` - string that evaluates to a PARI function of n that defines a coefgrow function. EXAMPLES:: sage: L = Dokchitser(conductor=1, gammaV=[0,1], weight=12, eps=1) sage: pari_precode = 'tau(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: L.init_coeffs('tau(k)', pari_precode=pari_precode) sage: L.set_coeff_growth('2*n^(11/2)') sage: L(1) 0.0374412812685155 """ if not isinstance(coefgrow, str): raise TypeError("coefgrow must be a string") g = self.gp() g.eval('coefgrow(n) = %s'%(coefgrow.replace('\n',' ')))
""" Complex Elliptic Curve L-series """ from sage.structure.sage_object import SageObject from sage.rings.all import (RealField, RationalField, ComplexField) from math import sqrt, exp, ceil import sage.functions.transcendental as transcendental R = RealField() Q = RationalField() C = ComplexField() import sage.misc.all as misc class Lseries_ell(SageObject): """ An elliptic curve $L$-series. EXAMPLES: """ def __init__(self, E): """ Create an elliptic curve $L$-series. EXAMPLES: sage: EllipticCurve([1..5]).lseries() Complex L-series of the Elliptic Curve defined by y^2 + x*y + 3*y = x^3 + 2*x^2 + 4*x + 5 over Rational Field """ self.__E = E
class Dokchitser(SageObject): r""" Dokchitser's `L`-functions Calculator Create a Dokchitser `L`-series with Dokchitser(conductor, gammaV, weight, eps, poles, residues, init, prec) where - ``conductor`` -- integer, the conductor - ``gammaV`` -- list of Gamma-factor parameters, e.g. [0] for Riemann zeta, [0,1] for ell.curves, (see examples). - ``weight`` -- positive real number, usually an integer e.g. 1 for Riemann zeta, 2 for `H^1` of curves/`\QQ` - ``eps`` -- complex number; sign in functional equation - ``poles`` -- (default: []) list of points where `L^*(s)` has (simple) poles; only poles with `Re(s)>weight/2` should be included - ``residues`` -- vector of residues of `L^*(s)` in those poles or set residues='automatic' (default value) - ``prec`` -- integer (default: 53) number of *bits* of precision RIEMANN ZETA FUNCTION: We compute with the Riemann Zeta function. :: sage: L = Dokchitser(conductor=1, gammaV=[0], weight=1, eps=1, poles=[1], residues=[-1], init='1') sage: L Dokchitser L-series of conductor 1 and weight 1 sage: L(1) Traceback (most recent call last): ... ArithmeticError sage: L(2) 1.64493406684823 sage: L(2, 1.1) 1.64493406684823 sage: L.derivative(2) -0.937548254315844 sage: h = RR('0.0000000000001') sage: (zeta(2+h) - zeta(2.))/h -0.937028232783632 sage: L.taylor_series(2, k=5) 1.64493406684823 - 0.937548254315844*z + 0.994640117149451*z^2 - 1.00002430047384*z^3 + 1.00006193307...*z^4 + O(z^5) RANK 1 ELLIPTIC CURVE: We compute with the `L`-series of a rank `1` curve. :: sage: E = EllipticCurve('37a') sage: L = E.lseries().dokchitser(algorithm='gp'); L Dokchitser L-function associated to Elliptic Curve defined by y^2 + y = x^3 - x over Rational Field sage: L(1) 0.000000000000000 sage: L.derivative(1) 0.305999773834052 sage: L.derivative(1,2) 0.373095594536324 sage: L.num_coeffs() 48 sage: L.taylor_series(1,4) 0.000000000000000 + 0.305999773834052*z + 0.186547797268162*z^2 - 0.136791463097188*z^3 + O(z^4) sage: L.check_functional_equation() # abs tol 1e-19 6.04442711160669e-18 RANK 2 ELLIPTIC CURVE: We compute the leading coefficient and Taylor expansion of the `L`-series of a rank `2` elliptic curve. :: sage: E = EllipticCurve('389a') sage: L = E.lseries().dokchitser(algorithm='gp') sage: L.num_coeffs() 156 sage: L.derivative(1,E.rank()) 1.51863300057685 sage: L.taylor_series(1,4) -1.27685190980159e-23 + (7.23588070754027e-24)*z + 0.759316500288427*z^2 - 0.430302337583362*z^3 + O(z^4) # 32-bit -2.72911738151096e-23 + (1.54658247036311e-23)*z + 0.759316500288427*z^2 - 0.430302337583362*z^3 + O(z^4) # 64-bit NUMBER FIELD: We compute with the Dedekind zeta function of a number field. :: sage: x = var('x') sage: K = NumberField(x**4 - x**2 - 1,'a') sage: L = K.zeta_function(algorithm='gp') sage: L.conductor 400 sage: L.num_coeffs() 264 sage: L(2) 1.10398438736918 sage: L.taylor_series(2,3) 1.10398438736918 - 0.215822638498759*z + 0.279836437522536*z^2 + O(z^3) RAMANUJAN DELTA L-FUNCTION: The coefficients are given by Ramanujan's tau function:: sage: L = Dokchitser(conductor=1, gammaV=[0,1], weight=12, eps=1) sage: pari_precode = 'tau(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: L.init_coeffs('tau(k)', pari_precode=pari_precode) We redefine the default bound on the coefficients: Deligne's estimate on tau(n) is better than the default coefgrow(n)=`(4n)^{11/2}` (by a factor 1024), so re-defining coefgrow() improves efficiency (slightly faster). :: sage: L.num_coeffs() 12 sage: L.set_coeff_growth('2*n^(11/2)') sage: L.num_coeffs() 11 Now we're ready to evaluate, etc. :: sage: L(1) 0.0374412812685155 sage: L(1, 1.1) 0.0374412812685155 sage: L.taylor_series(1,3) 0.0374412812685155 + 0.0709221123619322*z + 0.0380744761270520*z^2 + O(z^3) """ __gp = None __globals = set() # set of global variables defined in a run of the # computel.gp script that are replaced by indexed copies # in the computel.gp.template __globals_re = None __instance = 0 # Monotonically increasing unique instance ID __n_instances = 0 # Number of currently allocated instances __template_filename = os.path.join(SAGE_EXTCODE, 'pari', 'dokchitser', 'computel.gp.template') __init = False def __new__(cls, *args, **kwargs): inst = super(Dokchitser, cls).__new__(cls, *args, **kwargs) inst.__instance = cls.__instance cls.__n_instances += 1 cls.__instance += 1 return inst def __init__(self, conductor, gammaV, weight, eps, poles=None, residues='automatic', prec=53, init=None): """ Initialization of Dokchitser calculator EXAMPLES:: sage: L = Dokchitser(conductor=1, gammaV=[0], weight=1, eps=1, poles=[1], residues=[-1], init='1') sage: L.num_coeffs() 4 """ self.conductor = conductor self.gammaV = gammaV self.weight = weight self.eps = eps self.poles = poles if poles is not None else [] self.residues = residues self.prec = prec self.__CC = ComplexField(self.prec) self.__RR = self.__CC._real_field() self.__initialized = False if init is not None: self.init_coeffs(init) def __reduce__(self): D = copy.copy(self.__dict__) if '_Dokchitser__gp' in D: del D['_Dokchitser__gp'] return reduce_load_dokchitser, (D, ) def _repr_(self): return "Dokchitser L-series of conductor %s and weight %s" % ( self.conductor, self.weight) def __del__(self): self._teardown_gp(self.__instance) def gp(self): """ Return the gp interpreter that is used to implement this Dokchitser L-function. EXAMPLES:: sage: E = EllipticCurve('11a') sage: L = E.lseries().dokchitser(algorithm='gp') sage: L(2) 0.546048036215014 sage: L.gp() PARI/GP interpreter """ if self.__gp is None: self._instantiate_gp() elif self.__initialized: return self.__gp self.__initialized = True with open(self.__template_filename) as tf: template = string.Template(tf.read()) tmp_script = os.path.join(SAGE_TMP, 'computel_%s.gp' % self.__instance) with open(tmp_script, 'w') as f: f.write(template.substitute(i=str(self.__instance))) try: self.__gp.read(tmp_script) finally: os.unlink(tmp_script) self._gp_eval('default(realprecision, %s)' % (self.prec // 3 + 2)) self._gp_set_inst('conductor', self.conductor) self._gp_set_inst('gammaV', self.gammaV) self._gp_set_inst('weight', self.weight) self._gp_set_inst('sgn', self.eps) self._gp_set_inst('Lpoles', self.poles) self._gp_set_inst('Lresidues', self.residues) return self.__gp @classmethod def _instantiate_gp(cls): from sage.env import DOT_SAGE logfile = os.path.join(DOT_SAGE, 'dokchitser.log') cls.__gp = sage.interfaces.gp.Gp(script_subdirectory='dokchitser', logfile=logfile) # Read the script template and parse out all indexed global variables # (easy because they all end in "_$i" and there's nothing else in the # script that uses $) global_re = re.compile(r'([a-zA-Z0-9]+)_\$i') with open(cls.__template_filename) as f: for line in f: for m in global_re.finditer(line): cls.__globals.add(m.group(1)) cls.__globals_re = re.compile( '([^a-zA-Z0-9_]|^)(%s)([^a-zA-Z0-9_]|$)' % '|'.join(cls.__globals)) return @classmethod def _teardown_gp(cls, instance=None): cls.__n_instances -= 1 if cls.__n_instances == 0: cls.__gp.quit() elif instance is not None: # Clean up all global variables created by this instance for varname in cls.__globals: cls.__gp.eval('kill(%s_%s)' % (varname, instance)) def _gp_call_inst(self, func, *args): """ Call the specified PARI function in the GP interpreter, with the instance number of this `Dokchitser` instance automatically appended. For example, ``self._gp_call_inst('L', 1)`` is equivalent to ``self.gp().eval('L_N(1)')`` where ``N`` is ``self.__instance``. """ cmd = '%s_%d(%s)' % (func, self.__instance, ','.join( str(a) for a in args)) return self._gp_eval(cmd) def _gp_set_inst(self, varname, value): """ Like ``_gp_call_inst`` but sets the variable given by ``varname`` to the given value, appending ``_N`` to the variable name. If ``varname`` is a function (e.g. ``'func(n)'``) then this sets ``func_N(n)``). """ if '(' in varname: funcname, args = varname.split('(', 1) varname = '%s_%s(%s' % (funcname, self.__instance, args) else: varname = '%s_%s' % (varname, self.__instance) cmd = '%s = %s' % (varname, value) return self._gp_eval(cmd) def _gp_eval(self, s): try: t = self.gp().eval(s) except (RuntimeError, TypeError): raise RuntimeError( "Unable to create L-series, due to precision or other limits in PARI." ) if not self.__init and '***' in t: # After init_coeffs is called, future calls to this method should # return the full output for further parsing raise RuntimeError( "Unable to create L-series, due to precision or other limits in PARI." ) return t def __check_init(self): if not self.__init: raise ValueError( "you must call init_coeffs on the L-function first") 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='gp') sage: L.num_coeffs() 26 sage: E = EllipticCurve('5077a') sage: L = E.lseries().dokchitser(algorithm='gp') sage: L.num_coeffs() 568 sage: L = Dokchitser(conductor=1, gammaV=[0], weight=1, eps=1, poles=[1], residues=[-1], init='1') sage: L.num_coeffs() 4 Verify that ``num_coeffs`` works with non-real spectral parameters, e.g. for the L-function of the level 10 Maass form with eigenvalue 2.7341055592527126:: sage: ev = 2.7341055592527126 sage: L = Dokchitser(conductor=10, gammaV=[ev*i, -ev*i],weight=2,eps=1) sage: L.num_coeffs() 26 """ return Integer(self._gp_call_inst('cflength', T)) def init_coeffs(self, v, cutoff=1, w=None, pari_precode='', max_imaginary_part=0, max_asymp_coeffs=40): """ 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 string (pari function of k) - ``cutoff`` -- real number = 1 (default: 1) - ``w`` -- list of complex numbers or string (pari function of k) - ``pari_precode`` -- some code to execute in pari before calling initLdata - ``max_imaginary_part`` -- (default: 0): redefine if you want to compute L(s) for s having large imaginary part, - ``max_asymp_coeffs`` -- (default: 40): at most this many terms are generated in asymptotic series for phi(t) and G(s,t). EXAMPLES:: sage: L = Dokchitser(conductor=1, gammaV=[0,1], weight=12, eps=1) sage: pari_precode = 'tau(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: L.init_coeffs('tau(k)', pari_precode=pari_precode) 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(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 = Dokchitser(conductor=1, gammaV=[0,1], weight=12, eps=1) sage: L2.init_coeffs(list(delta_qexp(1000))[1:]) sage: L2(14) 0.998583063162746 TESTS: Verify that setting the `w` parameter does not raise an error (see :trac:`10937`). Note that the meaning of `w` does not seem to be documented anywhere in Dokchitser's package yet, so there is no claim that the example below is meaningful! :: sage: L2 = Dokchitser(conductor=1, gammaV=[0,1], weight=12, eps=1) sage: L2.init_coeffs(list(delta_qexp(1000))[1:], w=[1..1000]) """ if isinstance(v, tuple) and w is None: v, cutoff, w, pari_precode, max_imaginary_part, max_asymp_coeffs = v if pari_precode: # Must have __globals_re instantiated if self.__gp is None: self._instantiate_gp() def repl(m): return '%s%s_%d%s' % (m.group(1), m.group(2), self.__instance, m.group(3)) # If any of the pre-code contains references to some of the # templated global variables we must replace those as well pari_precode = self.__globals_re.sub(repl, pari_precode) if pari_precode != '': self._gp_eval(pari_precode) RR = self.__CC._real_field() cutoff = RR(cutoff) if isinstance(v, str): if w is None: self._gp_call_inst('initLdata', '"%s"' % v, cutoff) else: self._gp_call_inst('initLdata', '"%s"' % v, cutoff, '"%s"' % w) elif not isinstance(v, (list, tuple)): raise TypeError("v (=%s) must be a list, tuple, or string" % v) else: CC = self.__CC v = ','.join([CC(a)._pari_init_() for a in v]) self._gp_eval('Avec = [%s]' % v) if w is None: self._gp_call_inst('initLdata', '"Avec[k]"', cutoff) else: w = ','.join([CC(a)._pari_init_() for a in w]) self._gp_eval('Bvec = [%s]' % w) self._gp_call_inst('initLdata', '"Avec[k]"', cutoff, '"Bvec[k]"') self.__init = (v, cutoff, w, pari_precode, max_imaginary_part, max_asymp_coeffs) def __to_CC(self, s): s = s.replace('.E', '.0E').replace(' ', '') return self.__CC(sage_eval(s, locals={'I': self.__CC.gen(0)})) def _clear_value_cache(self): del self.__values def __call__(self, s, c=None): r""" INPUT: - ``s`` -- complex number .. NOTE:: Evaluation of the function takes a long time, so each evaluation is cached. Call ``self._clear_value_cache()`` to clear the evaluation cache. EXAMPLES:: sage: E = EllipticCurve('5077a') sage: L = E.lseries().dokchitser(100, algorithm='gp') sage: L(1) 0.00000000000000000000000000000 sage: L(1+I) -1.3085436607849493358323930438 + 0.81298000036784359634835412129*I """ self.__check_init() s = self.__CC(s) try: return self.__values[s] except AttributeError: self.__values = {} except KeyError: pass z = self._gp_call_inst('L', s) if 'pole' in z: print(z) raise ArithmeticError elif '***' in z: print(z) raise RuntimeError elif 'Warning' in z: i = z.rfind('\n') msg = z[:i].replace('digits', 'decimal digits') verbose(msg, level=-1) ans = self.__to_CC(z[i + 1:]) self.__values[s] = ans return ans ans = self.__to_CC(z) self.__values[s] = ans return ans def derivative(self, s, k=1): r""" Return the `k`-th derivative of the `L`-series at `s`. .. WARNING:: If `k` is greater than the order of vanishing of `L` at `s` you may get nonsense. EXAMPLES:: sage: E = EllipticCurve('389a') sage: L = E.lseries().dokchitser(algorithm='gp') sage: L.derivative(1,E.rank()) 1.51863300057685 """ self.__check_init() s = self.__CC(s) k = Integer(k) z = self._gp_call_inst('L', s, '', k) if 'pole' in z: raise ArithmeticError(z) elif 'Warning' in z: i = z.rfind('\n') msg = z[:i].replace('digits', 'decimal digits') verbose(msg, level=-1) return self.__CC(z[i:]) return self.__CC(z) def taylor_series(self, a=0, k=6, var='z'): r""" Return the first `k` terms of the Taylor series expansion of the `L`-series about `a`. This is returned as a series in ``var``, where you should view ``var`` as equal to `s-a`. Thus this function returns the formal power series whose coefficients are `L^{(n)}(a)/n!`. INPUT: - ``a`` -- complex number (default: 0); point about which to expand - ``k`` -- integer (default: 6), series is `O(``var``^k)` - ``var`` -- string (default: 'z'), variable of power series EXAMPLES:: sage: L = Dokchitser(conductor=1, gammaV=[0], weight=1, eps=1, poles=[1], residues=[-1], init='1') sage: L.taylor_series(2, 3) 1.64493406684823 - 0.937548254315844*z + 0.994640117149451*z^2 + O(z^3) sage: E = EllipticCurve('37a') sage: L = E.lseries().dokchitser(algorithm='gp') sage: L.taylor_series(1) 0.000000000000000 + 0.305999773834052*z + 0.186547797268162*z^2 - 0.136791463097188*z^3 + 0.0161066468496401*z^4 + 0.0185955175398802*z^5 + O(z^6) We compute a Taylor series where each coefficient is to high precision. :: sage: E = EllipticCurve('389a') sage: L = E.lseries().dokchitser(200, algorithm='gp') sage: L.taylor_series(1,3) ...e-82 + (...e-82)*z + 0.75931650028842677023019260789472201907809751649492435158581*z^2 + O(z^3) Check that :trac:`25402` is fixed:: sage: L = EllipticCurve("24a1").modular_form().lseries() sage: L.taylor_series(-1, 3) 0.000000000000000 - 0.702565506265199*z + 0.638929001045535*z^2 + O(z^3) Check that :trac:`25965` is fixed:: sage: L2 = EllipticCurve("37a1").modular_form().lseries(); L2 L-series associated to the cusp form q - 2*q^2 - 3*q^3 + 2*q^4 - 2*q^5 + O(q^6) sage: L2.taylor_series(0,4) 0.000000000000000 - 0.357620466127498*z + 0.273373112603865*z^2 + 0.303362857047671*z^3 + O(z^4) sage: L2.taylor_series(0,1) O(z^1) sage: L2(0) 0.000000000000000 """ self.__check_init() a = self.__CC(a) k = Integer(k) try: z = self._gp_call_inst('Lseries', a, '', k - 1) z = self.gp()('Vecrev(Pol(%s))' % z) except TypeError as msg: raise RuntimeError( "%s\nUnable to compute Taylor expansion (try lowering the number of terms)" % msg) r = repr(z) if 'pole' in r: raise ArithmeticError(r) elif 'Warning' in r: i = r.rfind('\n') msg = r[:i].replace('digits', 'decimal digits') verbose(msg, level=-1) v = list(z) K = self.__CC v = [K(repr(x)) for x in v] R = self.__CC[[var]] return R(v, k) def check_functional_equation(self, T=1.2): r""" Verifies how well numerically the functional equation is satisfied, and also determines the residues if ``self.poles != []`` and residues='automatic'. More specifically: for `T>1` (default 1.2), ``self.check_functional_equation(T)`` should ideally return 0 (to the current precision). - if what this function returns does not look like 0 at all, probably the functional equation is wrong (i.e. some of the parameters gammaV, conductor etc., or the coefficients are wrong), - if checkfeq(T) is to be used, more coefficients have to be generated (approximately T times more), e.g. call cflength(1.3), initLdata("a(k)",1.3), checkfeq(1.3) - T=1 always (!) returns 0, so T has to be away from 1 - default value `T=1.2` seems to give a reasonable balance - if you don't have to verify the functional equation or the L-values, call num_coeffs(1) and initLdata("a(k)",1), you need slightly less coefficients. EXAMPLES:: sage: L = Dokchitser(conductor=1, gammaV=[0], weight=1, eps=1, poles=[1], residues=[-1], init='1') sage: L.check_functional_equation() # abs tol 1e-19 -2.71050543121376e-20 If we choose the sign in functional equation for the `\zeta` function incorrectly, the functional equation doesn't check out. :: sage: L = Dokchitser(conductor=1, gammaV=[0], weight=1, eps=-11, poles=[1], residues=[-1], init='1') sage: L.check_functional_equation() -9.73967861488124 """ self.__check_init() z = self._gp_call_inst('checkfeq', T) z = z.replace(' ', '') return self.__CC(z) def set_coeff_growth(self, coefgrow): r""" You might have to redefine the coefficient growth function if the `a_n` of the `L`-series are not given by the following PARI function:: coefgrow(n) = if(length(Lpoles), 1.5*n^(vecmax(real(Lpoles))-1), sqrt(4*n)^(weight-1)); INPUT: - ``coefgrow`` -- string that evaluates to a PARI function of n that defines a coefgrow function. EXAMPLES:: sage: L = Dokchitser(conductor=1, gammaV=[0,1], weight=12, eps=1) sage: pari_precode = 'tau(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: L.init_coeffs('tau(k)', pari_precode=pari_precode) sage: L.set_coeff_growth('2*n^(11/2)') sage: L(1) 0.0374412812685155 """ if not isinstance(coefgrow, str): raise TypeError("coefgrow must be a string") self._gp_set_inst('coefgrow(n)', coefgrow.replace('\n', ' '))