generate.py

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

3580 bytes, as of the version from 2026-11-09 16:19 (current). Recorded here, not run.

"""Asymptotic coefficients of the Bettin–Conrey reciprocity function -- numberdb.org/T693.

Run with SageMath or passagemath and the numberdb package:
    sage -pip install numberdb               # install the client once
    sage -python generate.py                 # verify every stored entry
    sage -python generate.py --fill          # fill this draft with an API key

This implements Akatsuka–Murakami (2402.14216v1), equation (5.2).
Exact polynomials in x=pi^2 are constructed before evaluation in Arb balls.
The correction index is ell=0,...,60; see argument-selection.md. The generator
can compute a further coefficient by coefficient_polynomials(ell)[ell].
The 192-bit working margin is measured in checks.py; see numerical-evidence.md.
No source values, numerical inputs or other generator modules are needed.
"""
import os
import sys
from functools import lru_cache
from math import factorial
import numberdb.sage as numberdb
from sage.rings.rational_field import QQ
from sage.rings.integer_ring import ZZ
from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
from sage.rings.real_arb import RealBallField
from sage.arith.misc import bernoulli

MAX_ORDER = 60
WORKING_GUARD = 192
R = PolynomialRing(QQ, 'x')
x = R.gen()

@lru_cache(maxsize=None)
def coefficient_polynomials(limit=MAX_ORDER):
    """Q_k=z^k P_k(z), evaluated formally at z^2=-4x; then equation (5.2)."""
    q = [R.one()]
    for k in range(1, limit + 1):
        # Differentiate exp(-z lambda(u)) coefficient by coefficient.
        value = R.zero()
        for r in range(1, k + 1, 2):
            value -= QQ(r) * bernoulli(r + 1) / QQ(factorial(r + 1)) * (-4*x)**((r+1)//2) * q[k-r]
        q.append(value / QQ(k))
    coefficients = []
    for ell in range(limit + 1):
        value = R.zero()
        for k in range(ell + 1):
            # H(k+1,j)/4^j, with j=ell-k, computed by its product.
            h = QQ.one()
            for j in range(1, ell-k+1):
                h *= QQ(4*(k+1)**2-(2*j-1)**2) / QQ(16*j)
            value += h*q[k]
        coefficients.append(value)
    return tuple(coefficients)

def evaluate(poly, digits=100, extra=WORKING_GUARD):
    if poly.degree() <= 0:
        return QQ(poly[0])
    field = RealBallField(numberdb.bits(digits, losing=extra))
    value = poly(field.pi()**2)
    assert value.is_finite() and value.accuracy() >= numberdb.bits(digits)
    return value

class BettinConreyAsymptotic(numberdb.Generator):
    table = 'T693'
    parameters = ('ell',)
    type = 'R'
    digits = 100
    rigour = 'proven'
    files = ('generate.py', 'checks.py', 'check.py', 'argument-selection.md',
             'derivation.md', 'numerical-evidence.md', 'skill-evidence.json',
             'checks-output.json', 'dry-run.log', 'coefficient-polynomials.json')

    def enumerate(self):
        for ell in range(MAX_ORDER + 1):
            yield {'ell': ell}

    def value(self, params, digits):
        ell = int(params['ell'])
        assert ell >= 0
        return evaluate(coefficient_polynomials(max(MAX_ORDER, ell))[ell], digits)

if __name__ == '__main__':
    if os.environ.get('NUMBERDB_KEY_FROM_STDIN') == '1':
        numberdb.configure(api_key=sys.stdin.read().strip())
    generator = BettinConreyAsymptotic()
    if '--fill' in sys.argv or os.environ.get('NUMBERDB_PUBLISH') == '1':
        print(generator.publish(message='Fill all 61 signed asymptotic coefficients with certified 100-digit values'))
    else:
        report = generator.verify(sample=None)
        print(report)
        sys.exit(0 if report.ok else 1)