Computing Heegner Points with Sage
William Stein
University of Washington
{{{id=5|
///
}}}
Relevant References
WARNING: Sage's Heegner points code is optimized for doing theoretical research into Kolyvagin's Euler System of Heegner Points.
It is not designed (yet!) to be used as a tool for actually efficiently computing points on curves. Mark Watkins has written code that is in Magma that is very highly optimized for finding points on curves (but not useful for research into Euler systems).
{{{id=94|
///
}}}
{{{id=3|
///
}}}
{{{id=17|
///
}}}
Computing Heegner Points $y_K$
{{{id=34|
E = EllipticCurve('37a'); E
///
Elliptic Curve defined by y^2 + y = x^3 - x over Rational Field
}}}
{{{id=28|
E.plot()
///
}}}
{{{id=29|
E.rank()
///
1
}}}
{{{id=30|
E.heegner_discriminants(100)
///
[-3, -4, -7, -11, -40, -47, -67, -71, -83, -84, -95]
}}}
First a discriminant of class number 1
{{{id=38|
QuadraticField(-3,'a').class_number()
///
1
}}}
{{{id=33|
P = E.heegner_point(-3); P
///
Heegner point of discriminant -3 on elliptic curve of conductor 37
}}}
{{{id=32|
P.numerical_approx()
///
(-1.00000000000000 + 2.18015221574935e-17*I : 3.24718550659409e-17 + 4.34765071166687e-17*I : 1.00000000000000)
}}}
{{{id=39|
P.numerical_approx(prec=100)
///
(-1.0000000000000000000000000000 - 9.6895704450769185435636090117e-31*I : -5.1662376187963059639095337283e-31 - 1.9379140828452461806982502823e-30*I : 1.0000000000000000000000000000)
}}}
{{{id=36|
P.point_exact()
///
(-1 : 0 : 1)
}}}
Compute the image of the Heegner point on the upper half plane as a complex number, where we view $E$ as $\mathbf{C}/\Lambda$.
{{{id=40|
P.map_to_complex_numbers()
///
0.204680500375773 - 1.22569469099340*I
}}}
The point in the upper half plane (really on the modular curve $X_0(N)$) that maps to the above point $P$.
{{{id=44|
t = P.heegner_point_on_X0N(); t
///
Heegner point 1/74*sqrt(-3) - 21/74 of discriminant -3 on X_0(37)
}}}
{{{id=82|
///
}}}
{{{id=81|
///
}}}
{{{id=45|
///
}}}
Next, an example in which the class number is $>1$.
{{{id=49|
for D in E.heegner_discriminants(100):
print D, QuadraticField(D,'a').class_number()
///
-3 1
-4 1
-7 1
-11 1
-40 2
-47 5
-67 1
-71 7
-83 3
-84 4
-95 8
}}}
{{{id=50|
P = E.heegner_point(-47); P
///
Heegner point of discriminant -47 on elliptic curve of conductor 37
}}}
{{{id=53|
P.heegner_point_on_X0N()
///
Heegner point 1/74*sqrt(-47) - 29/74 of discriminant -47 on X_0(37)
}}}
{{{id=51|
P.numerical_approx(100)
///
(0.41295030345265522481209547079 - 1.2506651800425362123442536977*I : -1.2882357148097753052431772001 - 1.6283769583240139393113357931*I : 1.0000000000000000000000000000)
}}}
{{{id=48|
y_1 = P.point_exact(100); show(y_1)
///
\newcommand{\Bold}[1]{\mathbf{#1}}\left(a : a^{4} + a - 1 : 1\right)
}}}
Note that $y_1$ is the Heegner point defined over the Hilbert Class Field. It has not been traced down to $K$.
{{{id=41|
K = y_1[0].parent(); K
///
Number Field in a with defining polynomial x^5 - x^4 + x^3 + x^2 - 2*x + 1
}}}
Computing $y_K$ when $h_K>1$
Not Implemented!
Project: The Heegner points package in Sage doesn't have a function that just computes $y_K = {\rm trace}_{H/K}(y_1)$. Implement such a function.
{{{id=63|
///
}}}
{{{id=27|
///
}}}
Computing Heegner Points $y_1$
{{{id=2|
E = EllipticCurve('5077a'); E
///
Elliptic Curve defined by y^2 + y = x^3 - 7*x + 6 over Rational Field
}}}
{{{id=19|
E.rank()
///
3
}}}
{{{id=18|
E.heegner_discriminants(100)
///
[-3, -4, -7, -19, -40, -47, -55, -59, -71, -79, -84, -88]
}}}
{{{id=20|
P = E.heegner_point(-7); P
///
Heegner point of discriminant -7 on elliptic curve of conductor 5077
}}}
{{{id=21|
P.numerical_approx()
///
(5.24763468960140e25 - 9.42689096571972e25*I : 2.63080809434194e37 + 1.12035605894541e39*I : 1.00000000000000)
}}}
{{{id=22|
P.numerical_approx(100)
///
(0 : 1.0000000000000000000000000000 : 0)
}}}
Bug in released Sage -- see 9302. (It just gets the $x$-coordinate and lifts, and didn't do the right thing for point at infinity.)
{{{id=24|
P.point_exact(100)
///
(0 : 1 : 0)
}}}
{{{id=23|
///
}}}
{{{id=13|
///
}}}
{{{id=12|
///
}}}
Computing Higher Heegner Points $y_n$
First our rank 1 curve 37a again:
{{{id=61|
E = EllipticCurve('37a'); E
///
Elliptic Curve defined by y^2 + y = x^3 - x over Rational Field
}}}
{{{id=60|
E.rank()
///
1
}}}
{{{id=65|
P = E.heegner_point(-3, 5); P
///
Heegner point of discriminant -3 and conductor 5 on elliptic curve of conductor 37
}}}
{{{id=64|
P.numerical_approx(100)
///
(1.5000000000000000000000000000 - 1.9364916731037084425896326999*I : -1.5904435122427102073514607000e-29 - 3.8729833462074168851792653998*I : 1.0000000000000000000000000000)
}}}
{{{id=59|
P.point_exact(100)
///
Traceback (most recent call last):
File "", line 1, in
File "_sage_input_104.py", line 10, in
exec compile(u'open("___code___.py","w").write("# -*- coding: utf-8 -*-\\n" + _support_.preparse_worksheet_cell(base64.b64decode("UC5wb2ludF9leGFjdCgxMDAp"),globals())+"\\n"); execfile(os.path.abspath("___code___.py"))' + '\n', '', 'single')
File "", line 1, in
File "/private/var/folders/FE/FEo498bGEOeewp0B4AIIP++++TM/-Tmp-/tmph90X0d/___code___.py", line 3, in
exec compile(u'P.point_exact(_sage_const_100 )' + '\n', '', 'single')
File "", line 1, in
File "/Users/wstein/sage/build/sage/local/lib/python2.6/site-packages/sage/schemes/elliptic_curves/heegner.py", line 3360, in point_exact
f = self.x_poly_exact(prec, algorithm=algorithm)
File "/Users/wstein/sage/build/sage/local/lib/python2.6/site-packages/sage/misc/cachefunc.py", line 322, in __call__
return self._cachedmethod._instance_call(self._instance, *args, **kwds)
File "/Users/wstein/sage/build/sage/local/lib/python2.6/site-packages/sage/misc/cachefunc.py", line 466, in _instance_call
cache[key] = self._cachedfunc.f(inst, *args, **kwds)
File "/Users/wstein/sage/build/sage/local/lib/python2.6/site-packages/sage/schemes/elliptic_curves/heegner.py", line 3243, in x_poly_exact
n = self.ring_class_field().degree_over_K()
File "/Users/wstein/sage/build/sage/local/lib/python2.6/site-packages/sage/misc/cachefunc.py", line 322, in __call__
return self._cachedmethod._instance_call(self._instance, *args, **kwds)
File "/Users/wstein/sage/build/sage/local/lib/python2.6/site-packages/sage/misc/cachefunc.py", line 466, in _instance_call
cache[key] = self._cachedfunc.f(inst, *args, **kwds)
File "/Users/wstein/sage/build/sage/local/lib/python2.6/site-packages/sage/schemes/elliptic_curves/heegner.py", line 370, in degree_over_K
return K.class_number() * self.degree_over_H()
File "/Users/wstein/sage/build/sage/local/lib/python2.6/site-packages/sage/misc/cachefunc.py", line 322, in __call__
return self._cachedmethod._instance_call(self._instance, *args, **kwds)
File "/Users/wstein/sage/build/sage/local/lib/python2.6/site-packages/sage/misc/cachefunc.py", line 466, in _instance_call
cache[key] = self._cachedfunc.f(inst, *args, **kwds)
File "/Users/wstein/sage/build/sage/local/lib/python2.6/site-packages/sage/schemes/elliptic_curves/heegner.py", line 419, in degree_over_H
raise NotImplementedError
NotImplementedError
}}}
Project: Implement this. See sage/schemes/elliptic_curves/heegner.py, line 419: "if K.discriminant() > -5:"
The problem is just that some things aren't implemented when $K=\mathbf{Q}(i)$ or $K=\mathbf{Q}(\sqrt{-3})$.
{{{id=67|
E = EllipticCurve('37a'); E
P = E.heegner_point(-7, 5); P
///
Heegner point of discriminant -7 and conductor 5 on elliptic curve of conductor 37
}}}
{{{id=10|
P.numerical_approx()
///
(5.83468606702153 + 10.1265099943474*I : -0.320931045360298 - 40.0244674238506*I : 1.00000000000000)
}}}
{{{id=9|
y_5 = P.point_exact(); y_5
///
(a : 2/369*a^5 - 11/123*a^4 + 389/369*a^3 - 144/41*a^2 + 1184/369*a - 208/123 : 1)
}}}
{{{id=70|
factor(y_5[0].parent().discriminant())
///
-1 * 5^5 * 7^3
}}}
{{{id=69|
///
}}}
{{{id=68|
///
}}}
Computing Information About Kolyvagin Classes
{{{id=15|
E = EllipticCurve('389a'); E
///
Elliptic Curve defined by y^2 + y = x^3 + x^2 - 2*x over Rational Field
}}}
{{{id=73|
E.heegner_discriminants(20)
///
[-4, -7, -11, -19]
}}}
{{{id=8|
H = heegner_points(389); H
///
Set of all Heegner points on X_0(389)
}}}
{{{id=72|
H5 = H.reduce_mod(5); H5
///
Heegner points on X_0(389) over F_5
}}}
{{{id=78|
K = QuadraticField(-7,'a')
///
}}}
{{{id=77|
p = 5
for c in primes(100):
a_c = E.ap(c)
print c, a_c % p, (c+1) % p, sage.schemes.elliptic_curves.heegner.is_inert(-7,c)
///
2 3 3 False
3 3 4 True
5 2 1 True
7 0 3 False
11 1 2 False
13 2 4 True
17 4 3 True
19 0 0 True
23 1 4 False
29 4 0 False
31 4 2 True
37 2 3 False
41 2 2 True
43 2 4 False
47 3 3 True
53 4 4 False
59 3 0 True
61 2 2 True
67 0 3 False
71 0 2 False
73 3 4 True
79 2 0 False
83 3 4 True
89 2 0 True
97 1 3 True
}}}
{{{id=76|
///
}}}
Verify Kolvyagin's Conjecture for a rank 2 curve:
{{{id=88|
H5
///
Heegner points on X_0(389) over F_5
}}}
The following calculation shows that if we consider the Kolyvagin class for $D=-7$
$$ [P_{17}] \in E(K_{17})/3 E(K_{17})$$
and take its image in $E(\mathbf{F}_{5^2})/3E(\mathbf{F}_{5^2})$, we get $0$.
{{{id=1|
H5.kolyvagin_point_on_curve(D=-7, c=17, E=E, p=3)
///
[0, 0]
}}}
However, if we consider
$$ [P_{41}] \in E(K_{41})/3 E(K_{41})$$
and take its image in $E(\mathbf{F}_{5^2})/3E(\mathbf{F}_{5^2})$, we get something nonzero!
{{{id=90|
H5.kolyvagin_point_on_curve(D=-7, c=41, E=E, p=3)
///
[1, 0]
}}}
This computation is the first ever provable verification of Kolyvagin's conjecture for a curve of rank at least 2:
Conjecture (Kolyvagin): Some equivalence class $[P_n] \in E(K_n)/p^m E(K_n)$ is nonzero.
{{{id=86|
///
}}}
{{{id=85|
///
}}}
{{{id=84|
///
}}}
{{{id=93|
///
}}}
{{{id=83|
///
}}}