From da09de4681c411f9e01069de4273c328a8d33b8b Mon Sep 17 00:00:00 2001 From: John Cremona Date: Tue, 2 Feb 2021 10:08:12 +0000 Subject: [PATCH] #31317 eclib elliptic curve modular symbols --- src/sage/libs/eclib/__init__.pxd | 2 +- src/sage/libs/eclib/newforms.pyx | 18 +- .../elliptic_curves/ell_modular_symbols.py | 27 ++- .../elliptic_curves/ell_rational_field.py | 166 +++++++++--------- 4 files changed, 117 insertions(+), 96 deletions(-) diff --git a/src/sage/libs/eclib/__init__.pxd b/src/sage/libs/eclib/__init__.pxd index 7ff4ac13fa9..51955c63c0b 100644 --- a/src/sage/libs/eclib/__init__.pxd +++ b/src/sage/libs/eclib/__init__.pxd @@ -141,7 +141,7 @@ cdef extern from "eclib/newforms.h": newforms(long n, int disp) - void createfromcurve(int sign, CurveRed CR) + void createfromcurve(int sign, CurveRed CR, int nap) void display() # Here i is the index of the relevant newform in the space, # which for us will always be 0: diff --git a/src/sage/libs/eclib/newforms.pyx b/src/sage/libs/eclib/newforms.pyx index 8e655f692f0..b50b6061aa2 100644 --- a/src/sage/libs/eclib/newforms.pyx +++ b/src/sage/libs/eclib/newforms.pyx @@ -126,16 +126,20 @@ cdef class ECModularSymbol: sage: ECModularSymbol.__new__(ECModularSymbol) Modular symbol with sign 0 over Rational Field attached to None """ - def __init__(self, E, sign=1): + def __init__(self, E, sign=1, nap=1000): """ Construct the modular symbol object from an elliptic curve. INPUT: - - ``E``- an elliptic curve defined over Q + - ``E``- an elliptic curve defined over Q. + - ``sign`` (int) -- 0 or +1. If +1, only plus modular symbols - of this sign are available. If 0, modular symbols of both - signs are available but the construction is more expensive. + of this sign are available. If 0, modular symbols of both + signs are available but the construction is more expensive. + + - ``nap`` - (int, default 1000): the number of ap of E to use + in determining the normalisation of the modular symbols. EXAMPLES:: @@ -211,7 +215,7 @@ cdef class ECModularSymbol: self.sign = sign self.nfs = new newforms(n, 0) - self.nfs.createfromcurve(sign,CR) + self.nfs.createfromcurve(sign, CR, nap) sig_off() def __dealloc__(self): @@ -245,9 +249,7 @@ cdef class ECModularSymbol: - ``sign`` (int) - either +1, -1 or 0. If the sign of the space is +1, only sign +1 is allowed. Default: self.sign, or +1 when self.sign=0. - - - - ``base_at_infinity`` (bool) - if True, evaluates + - ``base_at_infinity`` (bool) - if True, evaluates {oo,r}. otherwise (default) evaluates {0,r}. OUTPUT: diff --git a/src/sage/schemes/elliptic_curves/ell_modular_symbols.py b/src/sage/schemes/elliptic_curves/ell_modular_symbols.py index a4df3e24f5b..a32f64ea47b 100644 --- a/src/sage/schemes/elliptic_curves/ell_modular_symbols.py +++ b/src/sage/schemes/elliptic_curves/ell_modular_symbols.py @@ -218,9 +218,8 @@ def _repr_(self): self._sign, self._base_ring, self._E) class ModularSymbolECLIB(ModularSymbol): - def __init__(self, E, sign): - r""" - Modular symbols attached to `E` using ``eclib``. + def __init__(self, E, sign, nap=1000): + r"""Modular symbols attached to `E` using ``eclib``. Note that the normalization used within ``eclib`` differs from the normalization chosen here by a factor of 2 in the case of elliptic @@ -233,8 +232,12 @@ def __init__(self, E, sign): INPUT: - ``E`` - an elliptic curve + - ``sign`` - an integer, -1 or 1 + - ``nap`` - (int, default 1000): the number of ap of E to use + in determining the normalisation of the modular symbols. + EXAMPLES:: sage: import sage.schemes.elliptic_curves.ell_modular_symbols @@ -278,7 +281,6 @@ def __init__(self, E, sign): sage: [Mminus(1/i) for i in [1..11]] [0, 0, 1/2, 1/2, 0, 0, -1/2, -1/2, 0, 0, 0] - The scaling factor relative to eclib's normalization is 1/2 for curves of negative discriminant:: sage: [E.discriminant() for E in cremona_curves([14])] @@ -295,6 +297,21 @@ def __init__(self, E, sign): 7/10 sage: m(0) 1/5 + + If ``nap`` is too small, the normalization in eclib may be incorrect. See :trac:`31317`:: + + sage: from sage.schemes.elliptic_curves.ell_modular_symbols import ModularSymbolECLIB + sage: E = EllipticCurve('1590g1') + sage: m = ModularSymbolECLIB(E, sign=+1, nap=300) + sage: [m(a/5) for a in [1..4]] + [1001/153, -1001/153, -1001/153, 1001/153] + + Those values are incorrect. The correct values are:: + + sage: m = ModularSymbolECLIB(E, sign=+1, nap=400) + sage: [m(a/5) for a in [1..4]] + [13/2, -13/2, -13/2, 13/2] + """ from sage.libs.eclib.newforms import ECModularSymbol @@ -306,7 +323,7 @@ def __init__(self, E, sign): self._implementation="eclib" self._base_ring = QQ # The ECModularSymbol class must be initialized with sign=0 to compute minus symbols - self._modsym = ECModularSymbol(E, int(sign==1)) + self._modsym = ECModularSymbol(E, int(sign==1), nap) self.cache = {True: {}, False: {}} def _call_with_caching(self, r, base_at_infinity=True): diff --git a/src/sage/schemes/elliptic_curves/ell_rational_field.py b/src/sage/schemes/elliptic_curves/ell_rational_field.py index 2953e80cac1..ab29f1f1618 100644 --- a/src/sage/schemes/elliptic_curves/ell_rational_field.py +++ b/src/sage/schemes/elliptic_curves/ell_rational_field.py @@ -1093,7 +1093,7 @@ def abelian_variety(self): """ return self.modular_symbol_space(sign=0).abelian_variety() - def _modular_symbol_normalize(self, sign, normalize, implementation): + def _modular_symbol_normalize(self, sign, normalize, implementation, nap): r""" Normalize parameters for :meth:`modular_symbol`. @@ -1112,104 +1112,85 @@ def _modular_symbol_normalize(self, sign, normalize, implementation): raise ValueError("normalize should be one of 'L_ratio', 'period' or 'none'") if implementation not in ["sage", "eclib", "num"]: raise ValueError("Implementation should be one of 'sage', 'num' or 'eclib'") - return (sign, normalize, implementation) + return (sign, normalize, implementation, nap) @cached_method(key = _modular_symbol_normalize) - def modular_symbol(self, sign=+1, normalize=None, implementation='eclib'): - r""" - Return the modular symbol associated to this elliptic curve - with given sign. This is a map that sends any rational number`r` - to a rational number `[r]_E`, defined to be a certain - multiple of the integral of `2 \pi i f(z) dz` - from `\infty` to `r` where `f` is the newform attached to `E`. - - More precisely: If the sign is +1, then the value returned is the - quotient of the real part of this integral by the least positive - period `\Omega_E^{+}` of `E`. In particular for `r=0`, the value - is equal to `L(E,1)/\Omega_E^{+}` (unlike in ``L_ratio`` of - ``lseries()``, where the value is also divided by the number of - connected components of `E(\RR)`). In particular the modular - symbol depends on `E` and not only the isogeny class of `E`. - For sign `-1`, it is the quotient of the imaginary part of the - integral divided by the purely imaginary period of `E` with - smallest positive imaginary part. Note however there is an - issue about these normalizations, hence the optional argument - ``normalize`` explained below + def modular_symbol(self, sign=+1, normalize=None, implementation='eclib', nap=500): + r"""Return the modular symbol map associated to this elliptic curve + with given sign. INPUT: - ``sign`` - +1 (default) or -1. - - ``normalize`` - (default: None); either 'L_ratio', 'period', - or 'none' when ``implementation`` is 'sage'; ignored if - ``implementation`` is ``eclib`` or ``num``. + - ``normalize`` - (default: None); either 'L_ratio', 'period', + or 'none'; ignored unless ``implementation`` is 'sage'. For 'L_ratio', the modular symbol tries to normalize - correctly as explained below by comparing it to - ``L_ratio`` for the curve and some small twists. - The normalization 'period' uses the - ``integral_period_map`` for modular symbols which is known - to be equal to the desired normalization, up to the sign - and a possible power of 2. With normalization 'none', the - modular symbol is almost certainly not correctly - normalized, i.e. all values will be a fixed scalar multiple - of what they should be. However, the initial computation - of the modular symbol is much faster when implementation - ``sage`` is chosen, though evaluation of it after computing - it is no faster. - - - - ``implementation`` - either 'eclib' (default), 'sage' or - 'num'. Here 'eclib' uses John Cremona's implementation in - his eclib library. Instead 'sage' uses an implementation - within Sage which is often quite a bit slower. - Finally 'num' uses an implementation of numerical - modular symbols. + correctly as explained below by comparing it to ``L_ratio`` + for the curve and some small twists. The normalization + 'period' uses the ``integral_period_map`` for modular + symbols which is known to be equal to the desired + normalization, up to the sign and a possible power of 2. + With normalization 'none', the modular symbol is almost + certainly not correctly normalized, i.e. all values will be + a fixed scalar multiple of what they should be. + + - ``implementation`` - either 'eclib' (default), 'sage' or + 'num'. Here, 'eclib' uses Cremona's ``C++`` implementation + in the ``eclib`` library, 'sage' uses an implementation + within Sage which is often quite a bit slower, and 'num' + uses Wuthrich's implementation of numerical modular + symbols. + + - ``nap`` - (int, default 1000); ignored unless implementation + is 'eclib'. The number of ap of E to use in determining the + normalisation of the modular symbols. Using too small a + value can lead to incorrect normalisation. + + DEFINITION: + + The modular symbol map sends any rational number `r` to the + rational number whichis the ratio of the real or imaginary + part (depending on the sign) of the integral of `2 \pi i + f(z) dz` from `\infty` to `r`, where `f` is the newform + attached to `E`, to the real or imaginary period of `E`. + + More precisely: If the sign is +1, then the value returned + is the quotient of the real part of this integral by the + least positive period `\Omega_E^{+}` of `E`. In particular + for `r=0`, the value is equal to `L(E,1)/\Omega_E^{+}` + (unlike in ``L_ratio`` of ``lseries()``, where the value is + also divided by the number of connected components of + `E(\RR)`). In particular the modular symbol depends on `E` + and not only the isogeny class of `E`. For sign `-1`, it + is the quotient of the imaginary part of the integral + divided by the purely imaginary period of `E` with smallest + positive imaginary part. Note however there is an issue + about these normalizations, hence the optional argument + ``normalize`` explained below ALGORITHM: For the implementations 'sage' and 'eclib', the used algorithm starts by finding the space of modular symbols within the full space of all modular symbols of that - level. This initial step will take a very long time if - the conductor is large (e.g. minutes for five digit - conductors). Once the space is determined, each - evaluation is very fast (logarithmic in the - denominator of `r`). - - The implementation 'num' uses a different algorithm. - It uses numerical integration along paths in the upper - half plane. The bounds are rigorously proved so that - the outcome is known to be correct. The initial step - costs no time, instead each evaluation will take more - time than in the above. More information in the - documentation of the class ``ModularSymbolNumerical``. + level. This initial step will take a very long time if the + conductor is large (e.g. minutes for five digit + conductors). Once the space is determined, each evaluation + is very fast (logarithmic in the denominator of `r`). + + The implementation 'num' uses a different algorithm. It + uses numerical integration along paths in the upper half + plane. The bounds are rigorously proved so that the outcome + is known to be correct. The initial step costs no time, + instead each evaluation will take more time than in the + above. More information in the documentation of the class + ``ModularSymbolNumerical``. .. SEEALSO:: :meth:`modular_symbol_numerical` - .. note:: - - The value at a rational number `r` is proportional to the - real or imaginary part of the integral of `2 \pi i f(z) dz` - from `\infty` to `r`, where `f` is the newform attached to - `E`, suitably normalized so that all values of this map - take values in `\QQ`. - - The normalization is such that for sign +1, the value at - the cusp `r` is equal to the quotient of the real part of - `\int_{\infty}^{r}2\pi i f(z)dz` by the least positive - period of `E`, where `f` is the newform attached to the - isogeny class of `E`. This is in contrast to the method - ``L_ratio`` of ``lseries()``, where the value is also - divided by the number of connected components of - `E(\RR)`). In particular the modular symbol depends on `E` - and not only the isogeny class of `E`. For negative - modular symbols, the value is the quotient of the imaginary - part of the above integral by the imaginary part of the - smallest positive imaginary period. - - EXAMPLES:: sage: E = EllipticCurve('37a1') @@ -1298,10 +1279,31 @@ def modular_symbol(self, sign=+1, normalize=None, implementation='eclib'): Modular symbol with sign -1 over Rational Field attached to Elliptic Curve defined by y^2 + y = x^3 - x^2 - 10*x - 20 over Rational Field sage: [Mminus(1/i) for i in [1..11]] [0, 0, 1/2, 1/2, 0, 0, -1/2, -1/2, 0, 0, 0] + + With the default 'eclib' implementation, if ``nap`` is too + small, the normalization may be computed incorrectly. See + :trac:`31317`:: + + sage: E = EllipticCurve('1590g1') + sage: m = E.modular_symbol(nap=300) + sage: [m(a/5) for a in [1..4]] + [1001/153, -1001/153, -1001/153, 1001/153] + + Those values are incorrect. The correct values may be + obtained by increasing ``nap``, as verified by the numerical + implementation:: + + sage: m = E.modular_symbol(nap=400) + sage: [m(a/5) for a in [1..4]] + [13/2, -13/2, -13/2, 13/2] + sage: m = E.modular_symbol(implementation='num') + sage: [m(a/5) for a in [1..4]] + [13/2, -13/2, -13/2, 13/2] + """ - sign, normalize, implementation = self._modular_symbol_normalize(sign, normalize, implementation) + sign, normalize, implementation, nap = self._modular_symbol_normalize(sign, normalize, implementation, nap) if implementation == 'eclib': - M = ell_modular_symbols.ModularSymbolECLIB(self, sign) + M = ell_modular_symbols.ModularSymbolECLIB(self, sign, nap) elif implementation == 'sage': M = ell_modular_symbols.ModularSymbolSage(self, sign, normalize=normalize) else: # implementation == "num":