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))