Modular symbols attached to elliptic curves over \(\QQ\)

To an elliptic curve \(E\) over the rational numbers with conductor \(N\), one can associate a space of modular symbols of level \(N\), because \(E\) is known to be modular. The space is two-dimensional and contains a subspace on which complex conjugation acts as multiplication by \(+1\) and one on which it acts by \(-1\).

There are three implementations of modular symbols, two within Sage and one in Cremona’s eclib library. One can choose here which one is used.

Associated to \(E\) there is a canonical generator in each space. They are maps \([.]^+\) and \([.]^{-}\), both \(\QQ \to\QQ\). They are normalized such that

\[[r]^{+} \Omega^{+} + [r]^{-}\Omega^{-} = \int_{\infty}^r 2\pi i f(z) dz\]

where \(f\) is the newform associated to the isogeny class of \(E\) and \(\Omega^{+}\) is the smallest positive period of the Néron differential of \(E\) and \(\Omega^{-}\) is the smallest positive purely imaginary period. Note that it depends on \(E\) rather than on its isogeny class.

From eclib version v20161230, both plus and minus symbols are available and are correctly normalized. In the Sage implementation, the computation of the space provides initial generators which are not necessarily correctly normalized; here we implement two methods that try to find the correct scaling factor.

Modular symbols are used to compute \(p\)-adic \(L\)-functions.

EXAMPLES:

sage: E = EllipticCurve("19a1")
sage: m = E.modular_symbol()
sage: m(0)
1/3
sage: m(1/17)
-2/3
sage: m2 = E.modular_symbol(-1, implementation='sage')
sage: m2(0)
0
sage: m2(1/5)
1/2

sage: V = E.modular_symbol_space()
sage: V
Modular Symbols subspace of dimension 1 of Modular Symbols space of dimension 2
 for Gamma_0(19) of weight 2 with sign 1 over Rational Field
sage: V.q_eigenform(30)
q - 2*q^3 - 2*q^4 + 3*q^5 - q^7 + q^9 + 3*q^11 + 4*q^12 - 4*q^13 - 6*q^15 + 4*q^16
 - 3*q^17 + q^19 - 6*q^20 + 2*q^21 + 4*q^25 + 4*q^27 + 2*q^28 + 6*q^29 + O(q^30)
>>> from sage.all import *
>>> E = EllipticCurve("19a1")
>>> m = E.modular_symbol()
>>> m(Integer(0))
1/3
>>> m(Integer(1)/Integer(17))
-2/3
>>> m2 = E.modular_symbol(-Integer(1), implementation='sage')
>>> m2(Integer(0))
0
>>> m2(Integer(1)/Integer(5))
1/2

>>> V = E.modular_symbol_space()
>>> V
Modular Symbols subspace of dimension 1 of Modular Symbols space of dimension 2
 for Gamma_0(19) of weight 2 with sign 1 over Rational Field
>>> V.q_eigenform(Integer(30))
q - 2*q^3 - 2*q^4 + 3*q^5 - q^7 + q^9 + 3*q^11 + 4*q^12 - 4*q^13 - 6*q^15 + 4*q^16
 - 3*q^17 + q^19 - 6*q^20 + 2*q^21 + 4*q^25 + 4*q^27 + 2*q^28 + 6*q^29 + O(q^30)
E = EllipticCurve("19a1")
m = E.modular_symbol()
m(0)
m(1/17)
m2 = E.modular_symbol(-1, implementation='sage')
m2(0)
m2(1/5)
V = E.modular_symbol_space()
V
V.q_eigenform(30)

For more details on modular symbols consult the following

REFERENCES:

AUTHORS:

  • William Stein (2007): first version

  • Chris Wuthrich (2008): add scaling and reference to eclib

  • John Cremona (2016): reworked eclib interface

class sage.schemes.elliptic_curves.ell_modular_symbols.ModularSymbol[source]

Bases: SageObject

A modular symbol attached to an elliptic curve, which is the map \(\QQ\to \QQ\) obtained by sending \(r\) to the normalized symmetrized (or anti-symmetrized) integral \(\infty\) to \(r\).

This is as defined in [MTT1986], but normalized to depend on the curve and not only its isogeny class as in [SW2013].

See the documentation of E.modular_symbol() in elliptic curves over the rational numbers for help.

base_ring()[source]

Return the base ring for this modular symbol.

EXAMPLES:

sage: m = EllipticCurve('11a1').modular_symbol()
sage: m.base_ring()
Rational Field
>>> from sage.all import *
>>> m = EllipticCurve('11a1').modular_symbol()
>>> m.base_ring()
Rational Field
m = EllipticCurve('11a1').modular_symbol()
m.base_ring()
elliptic_curve()[source]

Return the elliptic curve of this modular symbol.

EXAMPLES:

sage: m = EllipticCurve('11a1').modular_symbol()
sage: m.elliptic_curve()
Elliptic Curve defined by y^2 + y = x^3 - x^2 - 10*x - 20 over Rational Field
>>> from sage.all import *
>>> m = EllipticCurve('11a1').modular_symbol()
>>> m.elliptic_curve()
Elliptic Curve defined by y^2 + y = x^3 - x^2 - 10*x - 20 over Rational Field
m = EllipticCurve('11a1').modular_symbol()
m.elliptic_curve()
sign()[source]

Return the sign of this elliptic curve modular symbol.

EXAMPLES:

sage: m = EllipticCurve('11a1').modular_symbol()
sage: m.sign()
1
sage: m = EllipticCurve('11a1').modular_symbol(sign=-1, implementation='sage')
sage: m.sign()
-1
>>> from sage.all import *
>>> m = EllipticCurve('11a1').modular_symbol()
>>> m.sign()
1
>>> m = EllipticCurve('11a1').modular_symbol(sign=-Integer(1), implementation='sage')
>>> m.sign()
-1
m = EllipticCurve('11a1').modular_symbol()
m.sign()
m = EllipticCurve('11a1').modular_symbol(sign=-1, implementation='sage')
m.sign()
class sage.schemes.elliptic_curves.ell_modular_symbols.ModularSymbolECLIB(E, sign, nap=1000)[source]

Bases: ModularSymbol

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 curves with negative discriminant (with one real component) since the convention there is to write the above integral as \([r]^{+}x+[r]^{-}yi\), where the lattice is \(\left<2x,x+yi\right>\), so that \(\Omega^{+}=2x\) and \(\Omega^{-}=2yi\). We allow for this below.

INPUT:

  • E – an elliptic curve

  • sign – integer; -1 or 1

  • nap – integer (default: 1000); the number of ap of E to use in determining the normalisation of the modular symbols

EXAMPLES:

sage: from sage.schemes.elliptic_curves.ell_modular_symbols import ModularSymbolECLIB
sage: E = EllipticCurve('11a1')
sage: M = ModularSymbolECLIB(E,+1)
sage: M
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: M(0)
1/5
sage: E = EllipticCurve('11a2')
sage: M = ModularSymbolECLIB(E,+1)
sage: M(0)
1
>>> from sage.all import *
>>> from sage.schemes.elliptic_curves.ell_modular_symbols import ModularSymbolECLIB
>>> E = EllipticCurve('11a1')
>>> M = ModularSymbolECLIB(E,+Integer(1))
>>> M
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
>>> M(Integer(0))
1/5
>>> E = EllipticCurve('11a2')
>>> M = ModularSymbolECLIB(E,+Integer(1))
>>> M(Integer(0))
1
from sage.schemes.elliptic_curves.ell_modular_symbols import ModularSymbolECLIB
E = EllipticCurve('11a1')
M = ModularSymbolECLIB(E,+1)
M
M(0)
E = EllipticCurve('11a2')
M = ModularSymbolECLIB(E,+1)
M(0)

This is a rank 1 case with vanishing positive twists:

sage: E = EllipticCurve('121b1')
sage: M = ModularSymbolECLIB(E,+1)
sage: M(0)
0
sage: M(1/7)
1/2

sage: M = EllipticCurve('121d1').modular_symbol(implementation='eclib')
sage: M(0)
2

sage: E = EllipticCurve('15a1')
sage: [C.modular_symbol(implementation='eclib')(0) for C in E.isogeny_class()]
[1/4, 1/8, 1/4, 1/2, 1/8, 1/16, 1/2, 1]
>>> from sage.all import *
>>> E = EllipticCurve('121b1')
>>> M = ModularSymbolECLIB(E,+Integer(1))
>>> M(Integer(0))
0
>>> M(Integer(1)/Integer(7))
1/2

>>> M = EllipticCurve('121d1').modular_symbol(implementation='eclib')
>>> M(Integer(0))
2

>>> E = EllipticCurve('15a1')
>>> [C.modular_symbol(implementation='eclib')(Integer(0)) for C in E.isogeny_class()]
[1/4, 1/8, 1/4, 1/2, 1/8, 1/16, 1/2, 1]
E = EllipticCurve('121b1')
M = ModularSymbolECLIB(E,+1)
M(0)
M(1/7)
M = EllipticCurve('121d1').modular_symbol(implementation='eclib')
M(0)
E = EllipticCurve('15a1')
[C.modular_symbol(implementation='eclib')(0) for C in E.isogeny_class()]

Since Issue #10256, the interface for negative modular symbols in eclib is available:

sage: E = EllipticCurve('11a1')
sage: Mplus = E.modular_symbol(+1); Mplus
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: [Mplus(1/i) for i in [1..11]]
[1/5, -4/5, -3/10, 7/10, 6/5, 6/5, 7/10, -3/10, -4/5, 1/5, 0]
sage: Mminus = E.modular_symbol(-1); Mminus
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]
>>> from sage.all import *
>>> E = EllipticCurve('11a1')
>>> Mplus = E.modular_symbol(+Integer(1)); Mplus
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
>>> [Mplus(Integer(1)/i) for i in (ellipsis_range(Integer(1),Ellipsis,Integer(11)))]
[1/5, -4/5, -3/10, 7/10, 6/5, 6/5, 7/10, -3/10, -4/5, 1/5, 0]
>>> Mminus = E.modular_symbol(-Integer(1)); Mminus
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
>>> [Mminus(Integer(1)/i) for i in (ellipsis_range(Integer(1),Ellipsis,Integer(11)))]
[0, 0, 1/2, 1/2, 0, 0, -1/2, -1/2, 0, 0, 0]
E = EllipticCurve('11a1')
Mplus = E.modular_symbol(+1); Mplus
[Mplus(1/i) for i in [1..11]]
Mminus = E.modular_symbol(-1); Mminus
[Mminus(1/i) for i in [1..11]]

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])]
[-21952, 941192, -1835008, -28, 25088, 98]
sage: [E.modular_symbol()._scaling for E in cremona_curves([14])]
[1/2, 1, 1/2, 1/2, 1, 1]
>>> from sage.all import *
>>> [E.discriminant() for E in cremona_curves([Integer(14)])]
[-21952, 941192, -1835008, -28, 25088, 98]
>>> [E.modular_symbol()._scaling for E in cremona_curves([Integer(14)])]
[1/2, 1, 1/2, 1/2, 1, 1]
[E.discriminant() for E in cremona_curves([14])]
[E.modular_symbol()._scaling for E in cremona_curves([14])]

TESTS (for Issue #10236):

sage: E = EllipticCurve('11a1')
sage: m = E.modular_symbol(implementation='eclib')
sage: m(1/7)
7/10
sage: m(0)
1/5
>>> from sage.all import *
>>> E = EllipticCurve('11a1')
>>> m = E.modular_symbol(implementation='eclib')
>>> m(Integer(1)/Integer(7))
7/10
>>> m(Integer(0))
1/5
E = EllipticCurve('11a1')
m = E.modular_symbol(implementation='eclib')
m(1/7)
m(0)

If nap is too small, the normalization in eclib used to be incorrect (see Issue #31317), but since eclib version v20210310 the value of nap is increased automatically by eclib:

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]]
[13/2, -13/2, -13/2, 13/2]
>>> from sage.all import *
>>> from sage.schemes.elliptic_curves.ell_modular_symbols import ModularSymbolECLIB
>>> E = EllipticCurve('1590g1')
>>> m = ModularSymbolECLIB(E, sign=+Integer(1), nap=Integer(300))
>>> [m(a/Integer(5)) for a in (ellipsis_range(Integer(1),Ellipsis,Integer(4)))]
[13/2, -13/2, -13/2, 13/2]
from sage.schemes.elliptic_curves.ell_modular_symbols import ModularSymbolECLIB
E = EllipticCurve('1590g1')
m = ModularSymbolECLIB(E, sign=+1, nap=300)
[m(a/5) for a in [1..4]]

These values are correct, and increasing nap has no effect. The correct values may verified by the numerical implementation:

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]
sage: m = E.modular_symbol(implementation='num')
sage: [m(a/5) for a in [1..4]]
[13/2, -13/2, -13/2, 13/2]
>>> from sage.all import *
>>> m = ModularSymbolECLIB(E, sign=+Integer(1), nap=Integer(400))
>>> [m(a/Integer(5)) for a in (ellipsis_range(Integer(1),Ellipsis,Integer(4)))]
[13/2, -13/2, -13/2, 13/2]
>>> m = E.modular_symbol(implementation='num')
>>> [m(a/Integer(5)) for a in (ellipsis_range(Integer(1),Ellipsis,Integer(4)))]
[13/2, -13/2, -13/2, 13/2]
m = ModularSymbolECLIB(E, sign=+1, nap=400)
[m(a/5) for a in [1..4]]
m = E.modular_symbol(implementation='num')
[m(a/5) for a in [1..4]]
class sage.schemes.elliptic_curves.ell_modular_symbols.ModularSymbolSage(E, sign, normalize='L_ratio')[source]

Bases: ModularSymbol

Modular symbols attached to \(E\) using sage.

INPUT:

  • E – an elliptic curve

  • sign – integer; -1 or 1

  • normalize – either 'L_ratio' (default), 'period', or 'none'; For 'L_ratio', the modular symbol is correctly normalized by comparing it to the quotient of \(L(E,1)\) by the least positive period for the curve and some small twists. The normalization 'period' uses the integral_period_map for modular symbols and is known to be equal to the above normalization up to the sign and a possible power of 2. For '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. But the initial computation of the modular symbol is much faster, though evaluation of it after computing it won’t be any faster.

EXAMPLES:

sage: E = EllipticCurve('11a1')
sage: from sage.schemes.elliptic_curves.ell_modular_symbols import ModularSymbolSage
sage: M = ModularSymbolSage(E, +1)
sage: M
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: M(0)
1/5
sage: E = EllipticCurve('11a2')
sage: M = ModularSymbolSage(E, +1)
sage: M(0)
1
sage: M = ModularSymbolSage(E, -1)
sage: M(1/3)
1/2
>>> from sage.all import *
>>> E = EllipticCurve('11a1')
>>> from sage.schemes.elliptic_curves.ell_modular_symbols import ModularSymbolSage
>>> M = ModularSymbolSage(E, +Integer(1))
>>> M
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
>>> M(Integer(0))
1/5
>>> E = EllipticCurve('11a2')
>>> M = ModularSymbolSage(E, +Integer(1))
>>> M(Integer(0))
1
>>> M = ModularSymbolSage(E, -Integer(1))
>>> M(Integer(1)/Integer(3))
1/2
E = EllipticCurve('11a1')
from sage.schemes.elliptic_curves.ell_modular_symbols import ModularSymbolSage
M = ModularSymbolSage(E, +1)
M
M(0)
E = EllipticCurve('11a2')
M = ModularSymbolSage(E, +1)
M(0)
M = ModularSymbolSage(E, -1)
M(1/3)

This is a rank 1 case with vanishing positive twists. The modular symbol is adjusted by -2:

sage: E = EllipticCurve('121b1')
sage: M = ModularSymbolSage(E, -1, normalize='L_ratio')
sage: M(1/3)
1
sage: M._scaling
1

sage: M = EllipticCurve('121d1').modular_symbol(implementation='sage')
sage: M(0)
2
sage: M = EllipticCurve('121d1').modular_symbol(implementation='sage',
....:                                           normalize='none')
sage: M(0)
1

sage: E = EllipticCurve('15a1')
sage: [C.modular_symbol(implementation='sage', normalize='L_ratio')(0)
....:  for C in E.isogeny_class()]
[1/4, 1/8, 1/4, 1/2, 1/8, 1/16, 1/2, 1]
sage: [C.modular_symbol(implementation='sage', normalize='period')(0)
....:  for C in E.isogeny_class()]
[1/8, 1/16, 1/8, 1/4, 1/16, 1/32, 1/4, 1/2]
sage: [C.modular_symbol(implementation='sage', normalize='none')(0)
....:  for C in E.isogeny_class()]
[1, 1, 1, 1, 1, 1, 1, 1]
>>> from sage.all import *
>>> E = EllipticCurve('121b1')
>>> M = ModularSymbolSage(E, -Integer(1), normalize='L_ratio')
>>> M(Integer(1)/Integer(3))
1
>>> M._scaling
1

>>> M = EllipticCurve('121d1').modular_symbol(implementation='sage')
>>> M(Integer(0))
2
>>> M = EllipticCurve('121d1').modular_symbol(implementation='sage',
...                                           normalize='none')
>>> M(Integer(0))
1

>>> E = EllipticCurve('15a1')
>>> [C.modular_symbol(implementation='sage', normalize='L_ratio')(Integer(0))
...  for C in E.isogeny_class()]
[1/4, 1/8, 1/4, 1/2, 1/8, 1/16, 1/2, 1]
>>> [C.modular_symbol(implementation='sage', normalize='period')(Integer(0))
...  for C in E.isogeny_class()]
[1/8, 1/16, 1/8, 1/4, 1/16, 1/32, 1/4, 1/2]
>>> [C.modular_symbol(implementation='sage', normalize='none')(Integer(0))
...  for C in E.isogeny_class()]
[1, 1, 1, 1, 1, 1, 1, 1]
E = EllipticCurve('121b1')
M = ModularSymbolSage(E, -1, normalize='L_ratio')
M(1/3)
M._scaling
M = EllipticCurve('121d1').modular_symbol(implementation='sage')
M(0)
M = EllipticCurve('121d1').modular_symbol(implementation='sage',
                                          normalize='none')
M(0)
E = EllipticCurve('15a1')
[C.modular_symbol(implementation='sage', normalize='L_ratio')(0)
 for C in E.isogeny_class()]
[C.modular_symbol(implementation='sage', normalize='period')(0)
 for C in E.isogeny_class()]
[C.modular_symbol(implementation='sage', normalize='none')(0)
 for C in E.isogeny_class()]
sage.schemes.elliptic_curves.ell_modular_symbols.modular_symbol_space(E, sign, base_ring, bound=None)[source]

Create the space of modular symbols of a given sign over a give base_ring, attached to the isogeny class of the elliptic curve E.

INPUT:

  • E – an elliptic curve over \(\QQ\)

  • sign – integer; -1, 0, or 1

  • base_ring – ring

  • bound – (default: None) maximum number of Hecke operators to use to cut out modular symbols factor. If None, use enough to provably get the correct answer.

OUTPUT: a space of modular symbols

EXAMPLES:

sage: from sage.schemes.elliptic_curves.ell_modular_symbols import modular_symbol_space
sage: E = EllipticCurve('11a1')
sage: M = modular_symbol_space(E, -1, GF(37))
sage: M
Modular Symbols space of dimension 1 for Gamma_0(11) of weight 2 with sign -1
 over Finite Field of size 37
>>> from sage.all import *
>>> from sage.schemes.elliptic_curves.ell_modular_symbols import modular_symbol_space
>>> E = EllipticCurve('11a1')
>>> M = modular_symbol_space(E, -Integer(1), GF(Integer(37)))
>>> M
Modular Symbols space of dimension 1 for Gamma_0(11) of weight 2 with sign -1
 over Finite Field of size 37
from sage.schemes.elliptic_curves.ell_modular_symbols import modular_symbol_space
E = EllipticCurve('11a1')
M = modular_symbol_space(E, -1, GF(37))
M