Skip to content
This repository has been archived by the owner on Jan 30, 2023. It is now read-only.

Commit

Permalink
#31317 eclib elliptic curve modular symbols
Browse files Browse the repository at this point in the history
  • Loading branch information
JohnCremona committed Feb 2, 2021
1 parent 13b4090 commit da09de4
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 96 deletions.
2 changes: 1 addition & 1 deletion src/sage/libs/eclib/__init__.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
18 changes: 10 additions & 8 deletions src/sage/libs/eclib/newforms.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -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::
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -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:
Expand Down
27 changes: 22 additions & 5 deletions src/sage/schemes/elliptic_curves/ell_modular_symbols.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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])]
Expand All @@ -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

Expand All @@ -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):
Expand Down
166 changes: 84 additions & 82 deletions src/sage/schemes/elliptic_curves/ell_rational_field.py
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
Expand All @@ -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')
Expand Down Expand Up @@ -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":
Expand Down

0 comments on commit da09de4

Please sign in to comment.