back to table · edit · history · where entries came from · files · download
8571 bytes, as of the version from 2026-10-26 04:16 (current). Recorded here, not run.
"""Independent numerical checks for Jacobi theta₄ constants, numberdb.org/T505.
sage -python checks.py
sage -python checks.py --stored
The default checks every generated row, using attached corpus snapshots when
available. --stored reads T505 and its comparison tables from the API afresh.
For drafts supply NUMBERDB_API_KEY, or NUMBERDB_KEY_FROM_STDIN=1 and stdin.
No mode writes to NumberDB. Standard output is a JSON evidence record.
Independent definitions at 512 bits:
* elementary theta sums through N=14 with geometric absolute tail bounds;
* the theta4 product through M=64, with omitted product in [1-S,1], where
S=(q^(2M+2)+2*q^(2M+1))/(1-q^2);
* eta's product through M=64, with tail in [1-Q^(M+1)/(1-Q),1].
The inequality product(1-a_j)>=1-sum(a_j), for 0<=a_j<=1, proves both
product bounds. Ball arithmetic encloses q and all bound evaluations.
Every comparison first requires finite, narrow balls. Known-value controls,
an intentionally wrong pair, a wide ball, and a NaN run before the data.
Checks involving T295, T296 and T297 use their actual serialized intervals,
not decimal midpoints. The Gamma value provides an independent square-point
check. The package's shared exactness and size checks are reused unchanged.
"""
import os
import sys
import json
from pathlib import Path
from fractions import Fraction
from decimal import Decimal
import numberdb.sage as numberdb
from sage.rings.real_arb import RealBallField
from sage.rings.complex_arb import ComplexBallField
from sage.rings.rational_field import QQ
from sage.env import SAGE_VERSION
from numberdb._write import to_text
from generate import ThetaFourSingular
from check import exactness, measure, names_its_rings
R = RealBallField(512)
C = ComplexBallField(512)
N = 14
M = 64
COUNTS = {}
def rational(x):
f = Fraction(x)
return QQ(f.numerator) / QQ(f.denominator)
def value_of(x):
return x['number'] if isinstance(x, dict) else x
def interval_of(x):
text = str(value_of(x))
center = rational(text)
if '.' not in text and 'e' not in text.lower():
return R(center)
ulp = QQ(10) ** int(Decimal(text).as_tuple().exponent)
return R(center - ulp).union(R(center + ulp))
def narrow(x, label):
if not x.is_finite() or not R(x.rad()) < R(QQ(1) / 10**90):
raise AssertionError(label + ': nonfinite or wide ball')
def compare(a, b, label):
narrow(a, label)
narrow(b, label)
if not a.overlaps(b):
raise AssertionError(label + ': disjoint enclosures')
COUNTS[label] = COUNTS.get(label, 0) + 1
def controls():
compare(R(2).sqrt()**2, R(2), 'control-square-root')
compare(R(1).gamma(), R(1), 'control-gamma')
compare(R(0).exp(), R(1), 'control-exp')
assert interval_of('1/2').contains_exact(QQ(1)/2)
d = interval_of('1.25')
assert d.contains_exact(QQ(124)/100) and d.contains_exact(QQ(126)/100)
for a, b in [(R(1), R(2)), (R(0).add_error(1), R(0)), (R('nan'), R(0))]:
try:
compare(a, b, 'must-fail')
except AssertionError:
pass
else:
raise AssertionError('a negative control incorrectly passed')
return {'known_value_controls': 3, 'serialization_controls': 2,
'rejected_wrong_wide_nan': 3}
def theta_sums(q):
t2 = 2 * sum((q**(QQ(2*n+1)**2/4) for n in range(N+1)), R(0))
t3 = 1 + 2 * sum((q**(n*n) for n in range(1,N+1)), R(0))
t4 = 1 + 2 * sum((((-1)**n)*q**(n*n) for n in range(1,N+1)), R(0))
e34 = 2*q**((N+1)**2)/(1-q**(2*N+3))
e2 = 2*q**(QQ(2*N+3)**2/4)/(1-q**(2*N+4))
return (t2.add_error(e2.upper()), t3.add_error(e34.upper()),
t4.add_error(e34.upper()))
def omitted_product_bound(s):
assert s > 0 and s < 1
return (R(1)-s).union(R(1))
def theta_product(q):
finite = R(1)
for n in range(1,M+1):
finite *= (1-q**(2*n)) * (1-q**(2*n-1))**2
s = (q**(2*M+2)+2*q**(2*M+1))/(1-q**2)
return finite * omitted_product_bound(s)
def eta_product(q):
Q = q*q
finite = q**(QQ(1)/12)
finite_Q = Q**(QQ(1)/24)
for n in range(1,M+1):
finite *= 1-Q**n
finite_Q *= 1-Q**n
tail = omitted_product_bound(Q**(M+1)/(1-Q))
compare(finite*tail, finite_Q*tail, 'eta-q-Q-convention')
return finite*tail
def document(tid, fresh=False):
path = Path(__file__).with_name(tid+'.json')
if not fresh and path.exists():
return json.loads(path.read_text())
return numberdb.table(tid)
def main(stored=False):
control_results = controls()
g = ThetaFourSingular()
assert not names_its_rings(str(Path(__file__).with_name('generate.py')))
generated = {p['r']:g.value(p,100) for p in g.enumerate()}
assert not exactness(generated)
raw = {r:value_of(v) for r,v in generated.items()}
written = {r:to_text(v,100) for r,v in raw.items()}
for r,v in raw.items():
center = rational(written[r])
ulp = QQ(10)**int(Decimal(written[r]).as_tuple().exponent)
assert center-ulp <= v.lower().exact_rational() and v.upper().exact_rational() <= center+ulp, 'serialized ball containment '+r
actual = document('T505',fresh=True)['Numbers'] if stored else written
assert list(actual)==[str(r) for r in range(1,13)], 'range and displayed order'
actual = {r:interval_of(x) for r,x in actual.items()}
docs = {tid:document(tid,fresh=stored)['Numbers'] for tid in ['T295','T296','T297','T504']}
details=[]
for r in range(1,13):
key=str(r)
v=actual[key]
q=(-R.pi()*R(r).sqrt()).exp()
t2,t3,t4=theta_sums(q)
product=theta_product(q)
eta=eta_product(q)
compare(v,t4,'elementary-series')
wrong_q2=theta_sums(q*q)[2]
assert not v.overlaps(wrong_q2), 'q-versus-Q negative control'
COUNTS['rejected-Q-for-q'] = COUNTS.get('rejected-Q-for-q',0)+1
compare(v,product,'theta4-product')
compare(v**4+t2**4,t3**4,'quartic')
compare(t2*t3*v,2*eta**3,'eta-identity')
m=interval_of(docs['T295'][key]['parameter'])
kval=interval_of(docs['T296'][key])
G=interval_of(docs['T297'][key]['ramanujan'])
f=interval_of(docs['T297'][key]['weber'])
t3stored=interval_of(docs['T504'][key])
compare(t3,t3stored,'theta3-sibling')
compare((t2/t3)**4,m,'singular-parameter')
compare(v/t3,(1-m)**(QQ(1)/4),'complementary-modulus')
compare(v,(2*kval/R.pi()).sqrt()*(1-m)**(QQ(1)/4),'elliptic-integral')
kcalc=C(m).elliptic_k().real()
kcomp=C(1-m).elliptic_k().real()
compare(kcalc,kval,'elliptic-K-source')
compare(kcomp,R(r).sqrt()*kcalc,'elliptic-complement')
compare(v,R(2).sqrt()*G**2*eta*(1-m)**(QQ(1)/4),'ramanujan')
compare(v,f**2*eta*(1-m)**(QQ(1)/4),'weber')
compare(f,R(2)**(QQ(1)/4)*G,'weber-normalisation')
details.append({'r':r,'number':written[key],
'generator_accuracy_bits':int(raw[key].accuracy()),
'generator_radius':str(raw[key].rad()),
'series_radius':str(t4.rad()),'product_radius':str(product.rad()),
'stored_radius':str(v.rad())})
gamma=R(QQ(1)/4).gamma()
compare(gamma,interval_of(document('T9',fresh=stored)['Numbers']['1/4']),'Gamma-source')
compare(actual['1'],gamma/(2*R.pi())**(QQ(3)/4),'square-Gamma')
compare(interval_of(docs['T296']['1']),interval_of(document('T25',fresh=stored)['Numbers']['1/2']),'rational-K-square')
t2,t3,t4=theta_sums((-R.pi()).exp())
compare(actual['1'],t2,'square-theta2-equality')
compare(eta_product((-R.pi()).exp()),gamma/(2*R.pi()**(QQ(3)/4)),'eta-square-Gamma')
# The program snippet evaluates one named member of the same family.
snippet=C(0).jacobi_theta(C.gen(0))[3].real()
compare(actual['1'],snippet,'program-example')
result={'mode':'stored' if stored else 'pre-fill','controls':control_results,
'comparisons':COUNTS,'entries':len(actual),'measure':measure(written),
'min_generator_accuracy_bits':min(int(v.accuracy()) for v in raw.values()),
'working_bits':numberdb.bits(100,losing=96),'check_bits':512,
'versions':{'Python':sys.version,'Sage':SAGE_VERSION,'numberdb':numberdb.__version__},
'values':details,'all_passed':True}
print(json.dumps(result,indent=2))
return result
if __name__=='__main__':
if os.environ.get('NUMBERDB_KEY_FROM_STDIN')=='1':
numberdb.configure(api_key=sys.stdin.read().strip())
main('--stored' in sys.argv)