back to table · edit · history · where entries came from · files · download
5784 bytes, as of the version from 2026-09-10 22:48 (current). Recorded here, not run.
"""Faltings heights of elliptic curves over Q -- numberdb.org/T211.
h(E) = -1/2 log(area(C/Lambda_E))
Run it with SageMath:
$ sage -pip install numberdb # once
$ sage -python generate.py # check the table against this code
$ sage -python generate.py --publish # send it, with NUMBERDB_API_KEY set
The period lattice area is computed directly in Arb from the agm formulas
Sage uses for its period basis. Sage's own faltings_height() is used only as
an outside check, not as the value source.
"""
import os
import re
import sys
import numberdb.sage as numberdb
from sage.databases.cremona import CremonaDatabase, cremona_to_lmfdb, sort_key
from sage.rings.complex_arb import ComplexBallField
from sage.rings.integer_ring import ZZ
from sage.rings.rational_field import QQ
from sage.rings.real_arb import RealBallField
from sage.schemes.elliptic_curves.constructor import EllipticCurve
import sage.rings.qqbar as qqbar
qqbar._init_qqbar()
MAX_CONDUCTOR = 100
WORKING_GUARD_BITS = 192
_CURVES = None
_CURVES_BY_KEY = None
def _key_from_stdin():
if os.environ.get("NUMBERDB_KEY_FROM_STDIN") != "1":
return
token = sys.stdin.read().strip()
if "=" in token and token.split("=", 1)[0].isupper():
token = token.split("=", 1)[1].strip().strip("'\"")
if token:
os.environ["NUMBERDB_API_KEY"] = token
def _lmfdb_url(label):
found = re.fullmatch(r"([0-9]+)\.([a-z]+)([0-9]+)", label)
if found is None:
raise ValueError("not an LMFDB curve label: %s" % (label,))
conductor, iso, curve_number = found.groups()
return "https://www.lmfdb.org/EllipticCurve/Q/%s/%s/%s" % (
conductor, iso, curve_number)
def curves(max_conductor=MAX_CONDUCTOR):
global _CURVES
if _CURVES is not None and max_conductor == MAX_CONDUCTOR:
return _CURVES
database = CremonaDatabase()
found = []
for conductor in range(1, max_conductor + 1):
for label, data in sorted(database.allcurves(conductor).items(),
key=lambda item: sort_key(item[0])):
curve = EllipticCurve(QQ, data[0]).minimal_model()
c4, c6 = curve.c_invariants()
cremona_label = "%s%s" % (conductor, label)
lmfdb_label = cremona_to_lmfdb(cremona_label, database)
found.append({
"N": ZZ(conductor),
"c4": ZZ(c4),
"c6": ZZ(c6),
"cremona_label": cremona_label,
"lmfdb_label": lmfdb_label,
"lmfdb_url": _lmfdb_url(lmfdb_label),
"curve": curve,
})
if max_conductor == MAX_CONDUCTOR:
_CURVES = found
return found
def curves_by_key():
global _CURVES_BY_KEY
if _CURVES_BY_KEY is None:
by_key = {}
for record in curves():
key = (record["N"], record["c4"], record["c6"])
if key in by_key:
raise ArithmeticError("duplicate curve key %s" % (key,))
by_key[key] = record
_CURVES_BY_KEY = by_key
return _CURVES_BY_KEY
def _coerce_params(params):
return (ZZ(params["N"]), ZZ(params["c4"]), ZZ(params["c6"]))
def period_lattice_area(curve, bits):
lattice = curve.period_lattice()
real_field = RealBallField(bits)
complex_field = ComplexBallField(bits)
pi = real_field.pi()
if lattice.real_flag == 1:
a, b, c = [real_field(value) for value in lattice._abc]
omega_1 = pi / a.agm(b)
omega_2_imag = pi / a.agm(c)
return omega_1 * omega_2_imag
a = complex_field(lattice._abc[0])
x = a.real().abs()
y = a.imag().abs()
r = a.abs()
omega_1 = pi / r.agm(x)
omega_2_imag = pi / r.agm(y)
return omega_1 * omega_2_imag / real_field(2)
def faltings_height(curve, digits, stable=False):
bits = numberdb.bits(digits, losing=WORKING_GUARD_BITS)
real_field = RealBallField(bits)
height = -period_lattice_area(curve, bits).log() / real_field(2)
if not stable:
return height
j = curve.j_invariant()
denominator = real_field(ZZ(j.denominator()))
discriminant = real_field(abs(ZZ(curve.discriminant())))
return height + (denominator.log() - discriminant.log()) / real_field(12)
class FaltingsHeights(numberdb.Generator):
table = os.environ.get("NUMBERDB_TABLE", "T211")
parameters = ("N", "c4", "c6", "quantity")
type = "R"
digits = 100
rigour = "proven"
def enumerate(self, max_conductor=MAX_CONDUCTOR):
for record in curves(max_conductor):
base = {
"N": record["N"],
"c4": record["c4"],
"c6": record["c6"],
}
yield dict(base, quantity="h")
if not record["curve"].is_semistable():
yield dict(base, quantity="stable")
def value(self, params, digits):
record = curves_by_key()[_coerce_params(params)]
value = faltings_height(record["curve"], digits,
stable=(params["quantity"] == "stable"))
return {
"number": value,
"comment": (
"HREF{%s}[LMFDB %s], Cremona label %s."
% (record["lmfdb_url"], record["lmfdb_label"],
record["cremona_label"])
),
}
if __name__ == "__main__":
_key_from_stdin()
generator = FaltingsHeights()
if os.environ.get("NUMBERDB_PUBLISH") == "1" or "--publish" in sys.argv:
print(generator.publish(
message="Faltings heights for Sage mini Cremona curves, N <= %d"
% (MAX_CONDUCTOR,)))
else:
report = generator.verify(sample=None)
print(report)
sys.exit(0 if report.ok else 1)