generate.py

back to table · edit · history · where entries came from · files · download

6455 bytes, as of the version from 2026-09-19 12:48. Recorded here, not run.

"""Regulators of elliptic curves over real quadratic fields -- numberdb.org/T293.

For an elliptic curve E over K = Q(sqrt(D)), this stores the regulator of the
Mordell-Weil lattice with the absolute Neron-Tate height pairing. The table
lists positive-rank curves; in the range used here, every source curve has
rank 1, so the regulator is the height of the recorded generator.

Run it with SageMath:

    $ sage -pip install numberdb          # once
    $ sage -python generate.py            # check the table against this code
    $ sage -python generate.py --publish  # fill the draft, with NUMBERDB_API_KEY set

The curve list and regulator values are from John Cremona's ecnf-data
repository, pinned in curve_data.py. Each row is checked against the source
height column and against the Birch-Swinnerton-Dyer quotient using ecnf-data's
Lvalue, Omega, torsion, finite Tamagawa product and Sha.
"""

import os
import sys
from decimal import Decimal, ROUND_DOWN, localcontext

import numberdb.sage as numberdb

from curve_data import MAX_CONDUCTOR_NORM, RECORDS, SOURCE_COMMIT


TABLE = os.environ.get("NUMBERDB_TABLE", "T293")
DIGITS = 35
BSD_RELATIVE_TOLERANCE = Decimal("5e-30")

_RECORDS_BY_KEY = {
    (str(record["D"]), "%s-%s%s" % (
        record["conductor"],
        record["class"],
        record["curve"],
    )): record
    for record in RECORDS
}


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 _decimal(text):
    return Decimal(str(text).strip())


def _relative_error(got, expected):
    got = _decimal(got)
    expected = _decimal(expected)
    scale = max(abs(expected), Decimal(1))
    return abs(got - expected) / scale


def _require_close(label, got, expected, tolerance):
    error = _relative_error(got, expected)
    if error > tolerance:
        raise ArithmeticError(
            "%s: relative error %s is larger than %s; got %s, expected %s"
            % (label, error, tolerance, got, expected)
        )


def _truncate_significant(text, digits):
    value = _decimal(text)
    if not value:
        return "0"
    exponent = value.adjusted()
    quantum = Decimal(1).scaleb(exponent - digits + 1)
    with localcontext() as context:
        context.prec = max(80, digits + abs(exponent) + 10)
        truncated = value.quantize(quantum, rounding=ROUND_DOWN)
    if -7 < exponent < digits:
        return format(truncated, "f")
    mantissa, _, power = format(truncated, "e").partition("e")
    return "%se%d" % (mantissa, int(power))


def _rank_one_height(record):
    heights = record["heights"]
    if not (heights.startswith("[") and heights.endswith("]")):
        raise ArithmeticError("%s: malformed height list %s"
                              % (record["label"], heights))
    pieces = [part for part in heights[1:-1].split(",") if part]
    if len(pieces) != 1:
        raise ArithmeticError("%s: expected one generator height, got %s"
                              % (record["label"], heights))
    return pieces[0]


def _check_rank_and_height(record):
    rank = int(record["rank"])
    if rank != 1:
        raise ArithmeticError("%s: rank %s is outside this generator's check"
                              % (record["label"], rank))
    if int(record["ngens"]) != 1:
        raise ArithmeticError("%s: rank-one row has ngens=%s"
                              % (record["label"], record["ngens"]))
    _require_close(
        "%s regulator against generator height" % record["label"],
        record["regulator"],
        _rank_one_height(record),
        Decimal("1e-37"),
    )


def _check_bsd_quotient(record):
    with localcontext() as context:
        context.prec = 90
        numerator = (
            _decimal(record["lvalue"])
            * Decimal(int(record["torsion_order"]) ** 2)
            * Decimal(int(record["D"])).sqrt()
        )
        denominator = (
            (Decimal(2) ** int(record["rank"]))
            * _decimal(record["omega"])
            * _decimal(record["regulator"])
            * Decimal(int(record["tamagawa_product"]))
        )
        quotient = numerator / denominator

    _require_close(
        "%s BSD quotient" % record["label"],
        quotient,
        Decimal(int(record["sha"])),
        BSD_RELATIVE_TOLERANCE,
    )


def _equation_with_w(record):
    equation = record["equation"].replace("\\phi", "w")
    if record["D"] != 5:
        equation = equation.replace("a", "w")
    return equation


def _entry_comment(record):
    return (
        "LMFDB curve %s has conductor ideal $%s$, rank $%d$, and equation $%s$."
        % (
            record["label"],
            record["conductor_ideal"],
            int(record["rank"]),
            _equation_with_w(record),
        )
    )


class RealQuadraticEllipticRegulators(numberdb.Generator):
    """Generator for T293."""

    table = TABLE
    parameters = ("D", "label")
    type = "R"
    digits = DIGITS
    rigour = "heuristic"
    files = ("generate.py", "curve_data.py")

    def enumerate(self):
        for record in RECORDS:
            yield {
                "D": str(record["D"]),
                "label": "%s-%s%s" % (
                    record["conductor"],
                    record["class"],
                    record["curve"],
                ),
            }

    def value(self, params, digits):
        record = _RECORDS_BY_KEY[(str(params["D"]), params["label"])]
        _check_rank_and_height(record)
        _check_bsd_quotient(record)
        return {
            "number": _truncate_significant(record["regulator"], digits),
            "comment": _entry_comment(record),
        }


if __name__ == "__main__":
    _key_from_stdin()
    generator = RealQuadraticEllipticRegulators()
    if os.environ.get("NUMBERDB_PUBLISH") == "1" or "--publish" in sys.argv:
        print(generator.publish(
            message=(
                "elliptic-curve regulators over real quadratic fields from "
                "ecnf-data commit %s for conductor norm <= %d"
                % (SOURCE_COMMIT[:12], MAX_CONDUCTOR_NORM)
            ),
            overwrite=False,
        ))
    else:
        report = generator.verify(sample=None)
        print(report)
        sys.exit(0 if report.ok else 1)