checks.py

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

8018 bytes, as of the version from 2026-10-26 04:51 (current). Recorded here, not run.

"""Independent checks for Dedekind eta CM values -- numberdb.org/T506.

Run: sage -python checks.py [--stored]
A private draft needs its author's key through NUMBERDB_API_KEY or the stdin
convention documented in generate.py. No call in this file writes remotely.
The default checks freshly generated values; --stored checks every live entry.
Reference tables are fetched from NumberDB. This file deliberately evaluates
eta's defining product and theta series without calling modular_eta.
"""
import json
import os
import sys
from decimal import Decimal
from fractions import Fraction
from pathlib import Path
import numberdb.sage as numberdb
from sage.rings.complex_arb import ComplexBallField
from sage.rings.real_arb import RealBallField
from sage.rings.rational_field import QQ
from sage.env import SAGE_VERSION
from numberdb._write import to_text
from generate import DedekindEtaCM, configure_from_stdin
from check import exactness, measure, names_its_rings

R = RealBallField(512)
C = ComplexBallField(512)
MAX_RADIUS = R(QQ(1) / 10**90)
N = 12
M = 256


def number_of(entry):
    return entry['number'] if isinstance(entry, dict) else entry


def rational(f):
    return QQ(f.numerator) / f.denominator


def decimal_bounds(text):
    f = Fraction(text)
    if '.' not in text and 'e' not in text.lower():
        return f, f
    exponent = Decimal(text).as_tuple().exponent
    ulp = Fraction(10)**exponent
    return f - ulp, f + ulp


def read_number(entry):
    text = str(number_of(entry))
    lo, hi = decimal_bounds(text)
    return R(rational(lo)).union(R(rational(hi)))


def narrow(x):
    return x.is_finite() and x.rad_as_ball() < MAX_RADIUS


def agree(a, b, label):
    assert narrow(a) and narrow(b), (label, 'nonfinite or excessively wide ball')
    assert a.overlaps(b), (label, str(a), str(b))


def symmetric_error(bound):
    return (-bound).union(bound)


def euler_product(Q, terms=M):
    """Enclose prod(1-Q**n), including the infinite omitted product.

    For 0<=Q<1, prod(1-a_n)>=1-sum(a_n). Thus the tail from M+1
    lies in [1-Q**(M+1)/(1-Q), 1]. All powers and endpoints use balls.
    """
    assert Q >= 0 and Q < 1
    product = R(1)
    for n in range(1, terms + 1):
        product *= 1-Q**n
    error = Q**(terms+1)/(1-Q)
    assert error < 1
    return product * (R(1)-error).union(R(1))


def eta_product(y):
    Q = (-2*R.pi()*y).exp()
    return (-R.pi()*y/12).exp() * euler_product(Q)


def theta_series(q):
    """Full theta2, theta3, theta4 with geometric upper bounds for tails."""
    s2 = R(0)
    for n in range(N+1):
        s2 += 2*q**((QQ(n)+QQ(1)/2)**2)
    tail2 = 2*q**((QQ(N)+QQ(3)/2)**2)/(1-q**(2*N+4))
    s2 += symmetric_error(tail2)
    s3, s4 = R(1), R(1)
    for n in range(1,N+1):
        term = 2*q**(n*n)
        s3 += term
        s4 += (-1 if n % 2 else 1)*term
    tail = 2*q**((N+1)**2)/(1-q**(2*N+3))
    return s2, s3+symmetric_error(tail), s4+symmetric_error(tail)


def controls():
    agree(R(1).gamma(), R(1), 'Gamma(1)=1')
    agree(euler_product(R(0)), R(1), 'Euler product at Q=0')
    for a,b in zip(theta_series(R(0)),(R(0),R(1),R(1))):
        agree(a,b,'theta at zero nome')
    for a,b in [(R(1),R(2)), (R(0).union(R(2)),R(1)),(R('nan'),R(1))]:
        try:
            agree(a,b,'intentional failure control')
        except AssertionError:
            pass
        else:
            raise AssertionError('a failure control unexpectedly passed')


def run(stored=False):
    controls()
    generator = DedekindEtaCM()
    assert not names_its_rings(str(Path(__file__).with_name('generate.py')))
    fresh = {int(p['r']): number_of(generator.value(p,100))
             for p in generator.enumerate()}
    assert not exactness(fresh)
    written = {r:to_text(v,100) for r,v in fresh.items()}
    for r,v in fresh.items():
        lo,hi = decimal_bounds(written[r])
        assert v.lower().exact_rational() >= rational(lo)
        assert v.upper().exact_rational() <= rational(hi)
        assert len(Decimal(written[r]).as_tuple().digits) == 100
    refs = {tid:numberdb.table(tid)['Numbers'] for tid in
            ['T295','T296','T297','T299','T504','T505']}
    if stored:
        records=numberdb.table('T506')['Numbers']
        if isinstance(records,dict):
            raw={int(r):v for r,v in records.items()}
        else:
            raw={int(v['params']['r']):v for v in records}
        assert set(raw)==set(range(1,13))
        assert list(raw)==list(range(1,13))
        values={r:read_number(v) for r,v in raw.items()}
    else:
        values=fresh
    rows=[]
    for r,eta in values.items():
        y=R(r).sqrt(); q=(-R.pi()*y).exp()
        direct=eta_product(y)
        agree(eta,direct,f'r={r} explicit eta product')
        t2,t3,t4=theta_series(q)
        agree(2*eta**3,t2*t3*t4,f'r={r} theta identity')
        agree(t3,read_number(refs['T504'][str(r)]),f'r={r} T504')
        agree(t4,read_number(refs['T505'][str(r)]),f'r={r} T505')
        agree(t3**4,t2**4+t4**4,f'r={r} Jacobi quartic')
        agree(eta_product(1/y),y.sqrt()*eta,f'r={r} modular reciprocity')
        m=read_number(refs['T295'][str(r)]['parameter'])
        K=read_number(refs['T296'][str(r)])
        agree(m,(t2/t3)**4,f'r={r} lambda')
        agree(K,R.pi()*t3**2/2,f'r={r} K from theta')
        elliptic=R(2)**(-QQ(1)/3)*(m*(1-m))**(QQ(1)/12)*(2*K/R.pi()).sqrt()
        agree(eta,elliptic,f'r={r} elliptic formula')
        G=read_number(refs['T297'][str(r)]['ramanujan'])
        f=read_number(refs['T297'][str(r)]['weber'])
        agree(eta,t3/f**2,f'r={r} Weber relation')
        agree(eta,t3/(R(2).sqrt()*G**2),f'r={r} Ramanujan relation')
        E=C(m).elliptic_e()
        assert E.real().is_finite() and E.imag().is_finite() and E.imag().contains_zero()
        K_eta=R.pi()/2*(R(2)**(QQ(1)/3)*eta/(m*(1-m))**(QQ(1)/12))**2
        alpha=R.pi()/(4*K_eta**2)+y-y*E.real()/K_eta
        agree(alpha,read_number(refs['T299'][str(r)]),f'r={r} elliptic alpha')
        # Protect against both the q/Q confusion and deleting the square factor.
        assert not eta.overlaps(eta_product(2*y))
        rows.append({'r':r,'number':written[r],
                     'production_accuracy_bits':int(fresh[r].accuracy()),
                     'production_radius':str(fresh[r].rad()),
                     'product_accuracy_bits':int(direct.accuracy()),
                     'product_radius':str(direct.rad())})
    gamma=R(QQ(1)/4).gamma()/(2*R.pi()**(QQ(3)/4))
    agree(values[1],gamma,'square lattice Gamma formula')
    assert not values[4].overlaps(values[1])
    assert not values[8].overlaps(values[2])
    gamma_stored=numberdb.table('Values_of_the_Gamma_function_at_rational_numbers')['Numbers']['1/4']
    agree(values[1],read_number(gamma_stored)/(2*R.pi()**(QQ(3)/4)),
          'square lattice from stored Gamma(1/4)')
    return {'table':'T506','mode':'stored' if stored else 'fresh','passed':True,
            'entries':12,'working_bits':numberdb.bits(100,losing=96),
            'min_production_accuracy_bits':min(int(v.accuracy()) for v in fresh.values()),
            'min_independent_product_accuracy_bits':min(v['product_accuracy_bits'] for v in rows),
            'serialized_measure':measure(written),'raw_measure':measure(fresh),
            'controls':'Gamma(1), Q=0 product, zero-nome theta constants; rejects disagreement, broad balls and NaN',
            'checks_per_row':['explicit product enclosure','theta product identity','T504 theta3','T505 theta4','Jacobi quartic','modular reciprocity','T295 lambda','T296 K','elliptic eta formula','T297 Weber','T297 Ramanujan','T299 alpha','wrong nome rejected'],
            'extra_checks':['Gamma square-point formula','stored Gamma(1/4)','square factors retained','100 digits and full-ball containment'],
            'versions':{'Sage':SAGE_VERSION,'numberdb':numberdb.__version__},'rows':rows}


if __name__=='__main__':
    configure_from_stdin()
    print(json.dumps(run(stored='--stored' in sys.argv or os.environ.get('CHECK_STORED')=='1'),indent=2))