back to table · edit · history · where entries came from · files · download
10269 bytes, as of the version from 2026-11-02 06:20 (current). Recorded here, not run.
"""Independent checks for Chowla–Selberg period constants -- numberdb.org/T669.
Run `sage -python checks.py` with generate.py and check.py beside this file.
The core comparison uses no Gamma function: reduced binary quadratic forms
and a finite Dedekind eta product with a proved logarithmic tail bound.
Optional companion JSON exports T230.json, T296.json, T506.json, T177.json,
T129.json and T211.json allow checks against live NumberDB values. They can
be fetched with numberdb.table(TID); no saved numerical value defines a row.
For stored values use main(stored_path='stored-table.json'). The build wrapper
passes the authenticated draft export. This file never writes to the website.
Skill manifest: agents/runs/20261102T045202Z-build-skills/manifest.json.
Both versions: a2b28d9953264d7b0ca62663785c2952190c5be6d6d3597fd695d9a54d606278.
"""
import numberdb.sage as numberdb
from sage.rings.integer_ring import ZZ
from sage.rings.rational_field import QQ
from sage.rings.real_arb import RealBallField
from sage.rings.real_mpfi import RealIntervalField
from sage.libs.pari import pari
from numberdb._write import to_text
from generate import ChowlaSelbergPeriods, character_data, period
from check import exactness, measure, names_its_rings
from math import gcd, isqrt
from decimal import Decimal
from pathlib import Path
import json
import platform
import sage.version
PRECISION = 512
TERMS = 80
R = RealBallField(PRECISION)
I = RealIntervalField(PRECISION)
HERE = Path(__file__).parent
def reduced_forms(D):
# A reduced primitive positive definite form satisfies 3*a*a <= |D|.
out = []
for a in range(1, isqrt((-D)//3)+1):
for b in range(-a, a+1):
if (b*b-D) % (4*a):
continue
c = (b*b-D)//(4*a)
if a > c or gcd(gcd(a, b), c) != 1:
continue
if (abs(b) == a or a == c) and b < 0:
continue
out.append((a, b, c))
return out
def squarefree_trial(n):
return all(n % (p*p) for p in range(2, isqrt(n)+1))
def independent_discriminants(limit):
# Enumerate squarefree negative radicands rather than testing discriminants.
fields = []
for m in range(1, limit+1):
if squarefree_trial(m):
D = -m if (-m) % 4 == 1 else -4*m
if -D <= limit:
fields.append(D)
return sorted(fields, key=abs)
def log_F(a, b, D):
y = R(-D).sqrt()/(2*a)
theta = -R.pi()*b/a
r = (-2*R.pi()*y).exp()
total = y.log()/2 - R.pi()*y/6
rn = R(1)
for n in range(1, TERMS+1):
rn *= r
total += (1-2*rn*(n*theta).cos()+rn*rn).log()
tail = 2*r**(TERMS+1)/((1-r)*(1-r**(TERMS+1)))
assert tail.is_finite() and tail >= 0 and tail < R(10)**(-185)
return total.add_error(tail), tail
def eta_period(D, forms):
terms = [log_F(a, b, D) for a,b,c in forms]
v = ((2*R.pi()).log()+R(-D).log()/4+
sum((x[0] for x in terms),R(0))/len(forms)).exp()
return v, max(t for x,t in terms)
def narrow(x, bits=350):
assert x.is_finite() and x.accuracy() >= bits, str(x)
def agrees(x, y, bits=310):
narrow(x,bits); narrow(y,bits)
return bool(x.overlaps(y))
def rational_decimal(s):
n,d = Decimal(s).as_integer_ratio()
return QQ(n)/d
def decimal_data(s):
dec = Decimal(s)
center = rational_decimal(s)
unit = QQ(10)**int(dec.as_tuple().exponent)
return center, unit
def written_ball(s):
center,unit = decimal_data(s)
return R(I(center-unit,center+unit))
def contained(x,s):
center,unit = decimal_data(s)
interval=I(x)
lo=interval.lower().exact_rational()
hi=interval.upper().exact_rational()
return center-unit <= lo and hi <= center+unit
def num(x):
return x['number'] if isinstance(x,dict) else x
def optional(tid):
p=HERE/(tid+'.json')
return json.loads(p.read_text()) if p.exists() else None
def main(stored_path=None):
# Known-answer and rejection controls run before any family comparison.
assert reduced_forms(-3)==[(1,1,1)]
assert reduced_forms(-4)==[(1,0,1)]
assert reduced_forms(-23)==[(1,1,6),(2,-1,3),(2,1,3)]
assert len(reduced_forms(-163))==1
assert not squarefree_trial(4) and squarefree_trial(6)
assert rational_decimal('1.25')==QQ(5)/4
assert contained(R(3),'3.000')
assert not contained(R(QQ(3002)/1000),'3.000')
assert R(1).agm(R(1)) == 1
Khalf=R.pi()/(2*R(1).agm((R(1)/2).sqrt()))
square=2*R(2).sqrt()*Khalf
eta4,_=eta_period(-4,reduced_forms(-4))
assert agrees(eta4,square,450)
assert not agrees(eta4,square+R(10)**(-90),450)
assert agrees(period(-4),square,400)
b3=(R(QQ(3)/4)**(QQ(3)/4))*R(QQ(1)/3).gamma()**3/R.pi()
assert agrees(period(-3),b3,400)
b4=R(QQ(1)/4).gamma()**2/(2*R.pi()).sqrt()
assert agrees(period(-4),b4,400)
generator=ChowlaSelbergPeriods()
params=list(generator.enumerate())
Ds=[int(p['D']) for p in params]
assert Ds==independent_discriminants(1000) and len(Ds)==305
values={};records=[];etas={};forms_by_D={}; max_tail=R(0)
stored=None
if stored_path:
stored=json.loads((HERE/stored_path).read_text())['Numbers']
assert list(stored)==[str(D) for D in Ds]
for D in Ds:
forms=reduced_forms(D); forms_by_D[D]=forms
chi,h,w=character_data(D)
assert len(forms)==h==int(pari(D).qfbclassno())
# Verify exact class-number relation and character parity over the full sum.
assert sum(chi)==0
assert all(chi[a-1]==-chi[-a] for a in range(1,-D))
b=period(D);e,tail=eta_period(D,forms);etas[D]=e
narrow(b,400);narrow(e,450)
assert agrees(b,e,400),(D,b,e)
assert not agrees(b,e+R(10)**(-90),400)
text=to_text(b,100)
assert contained(b,text) and contained(e,text),(D,text)
if stored is not None:
s=num(stored[str(D)])
assert contained(b,s) and contained(e,s),(D,s)
values[str(D)]=b
max_tail=max(max_tail,tail)
records.append({'D':D,'h':h,'w':w,'forms':forms,'number':text,
'gamma_bits':int(b.accuracy()),'eta_bits':int(e.accuracy()),
'working_bits':int(b.parent().precision()),
'gamma_enclosure':str(b),'eta_enclosure':str(e)})
assert not exactness(values)
assert not names_its_rings(str(HERE/'generate.py'))
companion={}
t230=optional('T230')
if t230:
negatives=[int(D) for D in t230['Numbers'] if int(D)<0]
assert set(negatives)==set(Ds)
for D in Ds:
gammaK=written_ball(num(t230['Numbers'][str(D)]))
expected=2*R.euler_constant()+(2*R.pi()**2).log()-2*etas[D].log()
assert agrees(gammaK,expected,300),D
if stored is not None:
sb=written_ball(num(stored[str(D)]))
assert agrees(gammaK,2*R.euler_constant()+(2*R.pi()**2).log()-2*sb.log(),300)
companion['T230']=len(Ds)
for tid,transform in [('T296',lambda x:2*R(2).sqrt()*x),
('T506',lambda x:2*R.pi()*R(2).sqrt()*x**2)]:
doc=optional(tid)
if doc:
result=transform(written_ball(num(doc['Numbers']['1'])))
assert agrees(etas[-4],result,300)
if stored is not None: assert agrees(written_ball(num(stored['-4'])),result,300)
companion[tid]=1
t177=optional('T177')
if t177:
beta=written_ball(num(t177['Numbers']['1/4']['1/2']))
assert agrees(etas[-4],beta,300)
if stored is not None: assert agrees(written_ball(num(stored['-4'])),beta,300)
companion['T177']=1
t129=optional('T129');t211=optional('T211')
if t129 and t211:
# Linear Hilbert polynomials are x-j, so their constant term gives j.
from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
P=PolynomialRing(QQ,'x'); j_to_D={}
for D in Ds:
row=t129['Numbers'].get(str(D))
if row:
poly=P(num(row))
assert poly.degree()==len(forms_by_D[D])
if poly.degree()==1: j_to_D[-poly[0]]=D
found=[]
for N,models in t211['Numbers'].items():
for cc,quantities in models.items():
if 'stable' not in quantities: continue
c4,c6=map(ZZ,cc.split(','))
j=QQ(1728)*c4**3/(c4**3-c6**2)
if j not in j_to_D: continue
D=j_to_D[j]
height=R(-D).log()/4-etas[D].log()
assert agrees(height,written_ball(num(quantities['stable'])),300),(N,cc,D)
if stored is not None:
sb=written_ball(num(stored[str(D)]))
assert agrees(R(-D).log()/4-sb.log(),written_ball(num(quantities['stable'])),300)
found.append({'entry':N+','+cc+',stable','D':D})
assert found
companion['T211']=found
print(json.dumps({'table':'T669','count':len(Ds),'stored_checked':stored is not None,
'skill_manifest':'agents/runs/20261102T045202Z-build-skills/manifest.json',
'skill_sha256':'a2b28d9953264d7b0ca62663785c2952190c5be6d6d3597fd695d9a54d606278',
'versions':{'python':platform.python_version(),'sage':sage.version.version,
'numberdb':__import__('numberdb').__version__,'pari':str(pari.version())},
'gamma_min_bits':min(r['gamma_bits'] for r in records),
'eta_min_bits':min(r['eta_bits'] for r in records),
'max_log_F_tail':str(max_tail),'eta_terms':TERMS,'eta_working_bits':PRECISION,
'working_bits':sorted({r['working_bits'] for r in records}),
'measure_computed_balls':measure(values),'measure_written_values':measure({r['D']:r['number'] for r in records}),
'class_number_one':[r['D'] for r in records if r['h']==1],
'max_class_number':max(r['h'] for r in records),
'odd_discriminants':sum(D%2!=0 for D in Ds),
'even_discriminants':sum(D%2==0 for D in Ds),
'known_controls_passed':True,'perturbation_rejections':len(Ds)+1,
'eta_overlap_checks':len(Ds),'decimal_containment_checks':len(Ds),
'companion_checks':companion,'records':records},indent=2))
if __name__=='__main__':
main()