Modular symbols by numerical integration¶
We describe here the method for computing modular symbols by numerical approximations of the integral of the modular form on a path between cusps.
More precisely, let \(E\) be an elliptic curve and \(f\) the newform associated to the isogeny class of \(E\). If
then the modular symbol \([r]^{+}\) is defined as the quotient of the real part of \(\lambda(\infty\to r)\) by the least positive real period of \(E\). Similarly for the negative modular symbol, it is the quotient of the imaginary part of the above by the smallest positive imaginary part of a period on the imaginary axis.
The theorem of ManinDrinfeld shows that the modular symbols are rational numbers with small denominator. They are used for the computation of special values of the Lfunction of \(E\) twisted by Dirichlet characters and for the computation of \(p\)adic Lfunctions.
ALGORITHM:
The implementation of modular symbols in eclib and directly in sage uses the algorithm described in Cremona’s book [Cre1997] and Stein’s book [St2007]. First the space of all modular symbols of the given level is computed, then the space corresponding to the given newform is determined. Even if these initial steps may take a while, the evaluation afterwards is instantaneous. All computations are done with rational numbers and hence are exact.
Instead the method used here (see [Wu2018] for details) is by evaluating the above integrals \(\lambda(r\to r')\) by numerical approximation. Since we know precise bounds on the denominator, we can make rigorous estimates on the error to guarantee that the result is proven to be the correct rational number.
The paths over which we integrate are split up and AtkinLehner operators are used to compute the badly converging part of the integrals by using the Fourier expansion at other cusps than \(\infty\).
Note
There is one assumption for the correctness of these computations: The Manin constant for the \(X_0\)optimal curve should be \(1\) if the curve lies outside the Cremona tables. This is known for all curves in the Cremona table, but only conjectured for general curves.
EXAMPLES:
The most likely usage for the code is through the functions
modular_symbol
with implementation set to “num” and through
modular_symbol_numerical
:
sage: E = EllipticCurve("5077a1")
sage: M = E.modular_symbol(implementation = "num")
sage: M(0)
0
sage: M(1/123)
4
sage: Mn = E.modular_symbol_numerical(sign=1, prec=30)
sage: Mn(3/123) # abs tol 1e11
3.00000000000018
In more details. A numerical modular symbols M
is created from an
elliptic curve with a chosen sign
(though the other sign will also be
accessible, too):
sage: E = EllipticCurve([101,103])
sage: E.conductor()
35261176
sage: M = E.modular_symbol(implementation="num", sign=1)
sage: M
Numerical modular symbol attached to Elliptic Curve defined by y^2 = x^3 + 101*x + 103 over Rational Field
We can then compute the value \([13/17]^{}\) and \([1/17]^{+}\) by calling
the function M
. The value of \([0]^{+}=0\) tells us that the rank of
this curve is positive:
sage: M(13/17)
1/2
sage: M(1/17,sign=+1)
3
sage: M(0, sign=+1)
0
One can compute the numerical approximation to these rational numbers to any proven binary precision:
sage: M.approximative_value(13/17, prec=2) #abs tol 1e4
0.500003172770455
sage: M.approximative_value(13/17, prec=4) #abs tol 1e6
0.500000296037388
sage: M.approximative_value(0, sign=+1, prec=6) #abs tol 1e8
0.000000000000000
There are a few other things that one can do with M
. The Manin
symbol \(M(c:d)\) for a point \((c:d)\) in the projective line can be
computed.:
sage: M.manin_symbol(1,5)
1
In some cases useful, there is a function that returns all \([a/m]^{+}\) for a fixed denominator \(m\). This is rather quicker for small \(m\) than computing them individually:
sage: M.all_values_for_one_denominator(7)
{1/7: 0, 2/7: 3/2, 3/7: 3/2, 4/7: 3/2, 5/7: 3/2, 6/7: 0}
Finally a word of warning. The algorithm is fast when the cusps involved
are unitary. If the curve is semistable, all cusps are unitary. But
rational numbers with a prime \(p\) dividing the denominator once, but the
conductor more than once, are very difficult. For instance for the above
example, a seemingly harmless command like M(1/2)
would take a very
very long time to return a value. However it is possible to compute them
for smaller conductors:
sage: E = EllipticCurve("664a1")
sage: M = E.modular_symbol(implementation="num")
sage: M(1/2)
0
The problem with nonunitary cusps is dealt with rather easily when one can twist to a semistable curve, like in this example:
sage: C = EllipticCurve("11a1")
sage: E = C.quadratic_twist(101)
sage: M = E.modular_symbol(implementation="num")
sage: M(1/101)
41
AUTHOR:
Chris Wuthrich (201316)

class
sage.schemes.elliptic_curves.mod_sym_num.
ModularSymbolNumerical
¶ Bases:
object
This class assigning to an elliptic curve over \(\QQ\) a modular symbol. Unlike the other implementations this class does not precompute a basis for this space. Instead at each call, it evaluates the integral using numerical approximation. All bounds are very strictly implemented and the output is a correct proven rational number.
INPUT:
E
– an elliptic curve over the rational numbers.sign
– either 1 or +1 (default). This sets the default value ofsign
throughout the class. Both are still accessible.
OUTPUT: a modular symbol
EXAMPLES:
sage: E = EllipticCurve("5077a1") sage: M = E.modular_symbol(implementation="num") sage: M(0) 0 sage: M(77/57) 1 sage: M(33/37, 1) 2 sage: M = E.modular_symbol(sign=1, implementation="num") sage: M(2/7) 2 sage: from sage.schemes.elliptic_curves.mod_sym_num \ ....: import ModularSymbolNumerical sage: M = ModularSymbolNumerical(EllipticCurve("11a1")) sage: M(1/3, 1) 1/2 sage: M(1/2) 4/5

all_values_for_one_denominator
(m, sign=0)¶ Given an integer
m
and asign
, this returns the modular symbols \([a/m]\) for all \(a\) coprime to \(m\) using partial sums. This is much quicker than computing them one by one.This will only work if \(m\) is relatively small and if the cusps \(a/m\) are unitary.
INPUT:
m
– a natural numbersign
– optional either +1 or 1, or 0 (default), in which case the sign passed to the class is taken.
OUTPUT: a dictionary of fractions with denominator \(m\) giving rational numbers.
EXAMPLES:
sage: E = EllipticCurve('5077a1') sage: M = E.modular_symbol(implementation="num") sage: M.all_values_for_one_denominator(7) {1/7: 3, 2/7: 0, 3/7: 3, 4/7: 3, 5/7: 0, 6/7: 3} sage: [M(a/7) for a in [1..6]] [3, 0, 3, 3, 0, 3] sage: M.all_values_for_one_denominator(3,1) {1/3: 4, 2/3: 4} sage: E = EllipticCurve('11a1') sage: M = E.modular_symbol(implementation="num") sage: M.all_values_for_one_denominator(12) {1/12: 1/5, 5/12: 23/10, 7/12: 23/10, 11/12: 1/5} sage: M.all_values_for_one_denominator(12, 1) {1/12: 0, 5/12: 1/2, 7/12: 1/2, 11/12: 0} sage: E = EllipticCurve('20a1') sage: M = E.modular_symbol(implementation="num") sage: M.all_values_for_one_denominator(4) {1/4: 0, 3/4: 0} sage: M.all_values_for_one_denominator(8) {1/8: 1/2, 3/8: 1/2, 5/8: 1/2, 7/8: 1/2}

approximative_value
(r, sign=0, prec=20, use_twist=True)¶ The numerical modular symbol evaluated at rational.
It returns a real number, which should be equal to a rational number to the given binary precision
prec
. In practice the precision is often much higher. See the examples below.INPUT:
r
– a rational (or integer)sign
– optional either +1 or 1, or 0 (default), in which case the sign passed to the class is taken.prec
– an integer (default 20)use_twist
– True (default) allows to use a quadratic twist of the curve to lower the conductor.
OUTPUT: a real number
EXAMPLES:
sage: E = EllipticCurve("5077a1") sage: M = E.modular_symbol(implementation="num") sage: M.approximative_value(123/567) # abs tol 1e11 4.00000000000845 sage: M.approximative_value(123/567,prec=2) # abs tol 1e9 4.00002815242902 sage: E = EllipticCurve([11,88]) sage: E.conductor() 1715296 sage: M = E.modular_symbol(implementation="num") sage: M.approximative_value(0,prec=2) # abs tol 1e11 0.0000176374317982166 sage: M.approximative_value(1/7,prec=2) # abs tol 1e11 0.999981178147778 sage: M.approximative_value(1/7,prec=10) # abs tol 1e11 0.999999972802649

clear_cache
()¶ Clear the cached values in all methods of this class
EXAMPLES:
sage: E = EllipticCurve("11a1") sage: M = E.modular_symbol(implementation="num") sage: M(0) 1/5 sage: M.clear_cache() sage: M(0) 1/5

elliptic_curve
()¶ Return the elliptic curve of this modular symbol.
EXAMPLES:
sage: E = EllipticCurve("15a4") sage: M = E.modular_symbol(implementation="num") sage: M.elliptic_curve() Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 + 35*x  28 over Rational Field

manin_symbol
(u, v, sign=0)¶ Given a pair \((u,v)\) presenting a point in \(\mathbb{P}^1(\mathbb{Z}/N\mathbb{Z})\) and hence a coset of \(\Gamma_0(N)\), this computes the value of the Manin symbol \(M(u:v)\).
INPUT:
u
– an integerv
– an integer such that \((u:v)\) is a projective point modulo \(N\)sign
– optional either +1 or 1, or 0 (default), in which case the sign passed to the class is taken.
EXAMPLES:
sage: E = EllipticCurve('11a1') sage: M = E.modular_symbol(implementation="num") sage: M.manin_symbol(1,3) 1/2 sage: M.manin_symbol(1,3, sign=1) 1/2 sage: M.manin_symbol(1,5) 1 sage: M.manin_symbol(1,5) 1 sage: E = EllipticCurve('14a1') sage: M = E.modular_symbol(implementation="num") sage: M.manin_symbol(1,2) 1/2 sage: M.manin_symbol(17,6) 1/2 sage: M.manin_symbol(1,12) 1/2

transportable_symbol
(r, rr, sign=0)¶ Return the symbol \([r']^+  [r]^+\) where \(r'=\gamma(r)\) for some \(\gamma\in\Gamma_0(N)\). These symbols can be computed by transporting the path into the upper half plane close to one of the unitary cusps. Here we have implemented it only to move close to \(i\infty\) and \(0\).
INPUT:
r
andrr
– two rational numberssign
– optional either +1 or 1, or 0 (default), in which case the sign passed to the class is taken.
OUTPUT: a rational number
EXAMPLES:
sage: E = EllipticCurve("11a1") sage: M = E.modular_symbol(implementation="num") sage: M.transportable_symbol(0/1,2/7) 1/2 sage: E = EllipticCurve("37a1") sage: M = E.modular_symbol(implementation="num") sage: M.transportable_symbol(0/1,1/19) 0 sage: M.transportable_symbol(0/1,1/19,1) 0 sage: E = EllipticCurve("5077a1") sage: M = E.modular_symbol(implementation="num") sage: M.transportable_symbol(0/1,35/144) 3 sage: M.transportable_symbol(0/1,35/144,1) 0 sage: M.transportable_symbol(0/1, 7/31798) 0 sage: M.transportable_symbol(0/1, 7/31798, 1) 5