checks.py

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

7685 bytes, as of the version from 2026-10-27 17:40 (current). Recorded here, not run.

"""Reproducible numerical checks for numberdb.org/T554.

Run: sage -python checks.py > numerical-evidence.json
Place generate.py in the same directory. If stored-document.json is present,
checks include every decimal read back from the database. These are builder
checks, not the campaign's independent critique.

Independent method: DLMF 5.12.3 with t=u^M, splitting at 1 and substituting
u=1/v in the infinite part, gives
 B(a,b)=M*integral_0^1 (u^(M*a-1)+u^(M*b-1))/(1+u^M)^(a+b) du.
The selected M makes endpoint exponents nonnegative integers. mpmath evaluates
these smooth integrals without Gamma or beta functions at two precisions.
Euclidean r=n/B(1/n,1-2/n); ideal r=B(1/2+1/n,1/2)/B(1/2-1/n,1/2).
The quadrature checks are heuristic independent confirmations. Rigorous
100-digit enclosures come from the generator's Arb arithmetic, not quadrature.
"""
import json
import hashlib
import platform
from pathlib import Path
from decimal import Decimal
from fractions import Fraction
import numberdb.sage as numberdb
from sage.rings.rational_field import QQ
from sage.rings.real_arb import RealBallField
from sage.rings.real_mpfi import RealIntervalField
from sage.env import SAGE_VERSION
from numberdb._write import to_text
import mpmath as mp
from generate import InteriorConformalRadius

HERE=Path(__file__).resolve().parent
MANIFEST='agents/runs/20261027T171124Z-build-skills/manifest.json'
SKILL_HASH='e37efb7e2ededf6c1d7134c673f152f648f26e0267c556aebdf3ec65765d8b4c'

def rational(q):
    return Fraction(int(q.numerator()), int(q.denominator()))

def bounds(ball):
    v=RealIntervalField(ball.parent().precision())(ball)
    return rational(v.lower().exact_rational()),rational(v.upper().exact_rational())

def written_bounds(s):
    d=Decimal(s); centre=Fraction(d); ulp=Fraction(10)**d.as_tuple().exponent
    return centre-ulp,centre+ulp

def as_mp(q):
    return mp.mpf(q.numerator)/q.denominator

def beta_quadrature(a,b,M):
    # a,b are Fraction, so integer endpoint exponents are checked exactly.
    pa=M*a-1; pb=M*b-1
    assert pa.denominator==pb.denominator==1 and min(pa,pb)>=0
    exponent=as_mp(a+b)
    f=lambda u:(u**int(pa)+u**int(pb))/(1+u**M)**exponent
    return M*mp.quad(f,[0,mp.mpf(1)/2,1])

def independent(geometry,n):
    a=Fraction(1,n); half=Fraction(1,2)
    if geometry=='euclidean':
        return n/beta_quadrature(a,1-2*a,n)
    return beta_quadrature(half+a,half,2*n)/beta_quadrature(half-a,half,2*n)

def safe(ball,accuracy=400):
    assert ball.is_finite() and ball>0
    lo,hi=bounds(ball)
    assert (hi-lo)/lo < Fraction(1,2**accuracy)
    return lo,hi

def agm_bracket(x,y,R):
    a=R(x); g=R(y)
    for _ in range(12):
        a,g=(a+g)/2,(a*g).sqrt()
    lo=bounds(g)[0]; hi=bounds(a)[1]
    assert hi>=lo and hi-lo < Fraction(1,10**160)
    I=RealIntervalField(R.precision())
    return R(I(QQ(lo.numerator)/lo.denominator,QQ(hi.numerator)/hi.denominator))

def main():
    gen=InteriorConformalRadius(); balls={}; records=[]
    for p in gen.enumerate():
        key=(p['geometry'],int(p['n'])); b=gen.value(p,100); lo,hi=safe(b)
        s=to_text(b,100); sl,sh=written_bounds(s)
        assert sl<=lo<=hi<=sh
        assert len(s.replace('.','').lstrip('0'))==100
        balls[key]=b
        records.append({'params':p,'number':s,'ball':str(b),'lower':str(lo),'upper':str(hi),'relative_accuracy_bits':int(b.accuracy()),'serialization_contains_ball':True})
    R=RealBallField(600); pi=R.pi()
    agm_control=agm_bracket(1,1,R); assert bounds(agm_control)[0]<=1<=bounds(agm_control)[1]
    M=agm_bracket(1,R(2).sqrt()/2,R); K=pi/(2*M); safe(K,500)
    square_checks={'euclidean':R(2).sqrt()/K,'ideal-hyperbolic':pi/(2*K*K)}
    for geometry,expected in square_checks.items():
        safe(expected,500); assert expected.overlaps(balls[(geometry,4)])
    for n in range(3,31):
        a=QQ(1)/n
        area=R(n)/2*(2*pi/n).sin()
        ratio=R(1-a).gamma()*R(QQ(1)/2+a).gamma()/(R(1+a).gamma()*R(QQ(1)/2-a).gamma())
        expected=(R(2)**(4*a)*area*ratio/pi).sqrt();safe(expected,500)
        assert expected.overlaps(balls[('euclidean',n)])
        assert balls[('ideal-hyperbolic',n)] < balls[('euclidean',n)] < 1
    controls=[];quadrature=[];quad_values={}
    for dps in [130,170]:
        with mp.workdps(dps):
            cases=[(Fraction(1),Fraction(1),1,mp.mpf(1)),(Fraction(1,2),Fraction(1,2),2,mp.pi),(Fraction(1,3),Fraction(2,3),3,2*mp.pi/mp.sqrt(3))]
            for a,b,M,known in cases:
                actual=beta_quadrature(a,b,M);err=abs(actual-known)
                assert mp.isfinite(actual) and err<mp.mpf(10)**(-dps+10)
                controls.append({'dps':dps,'a':str(a),'b':str(b),'answer':mp.nstr(actual,30),'absolute_error':mp.nstr(err,8)})
            for record in records:
                p=record['params'];key=(p['geometry'],p['n'])
                v=independent(*key);lo,hi=bounds(balls[key]);mid=(lo+hi)/2
                err=abs(v-as_mp(mid))/abs(v)
                assert mp.isfinite(v) and 0<v<1 and err<mp.mpf('1e-120')
                sl,sh=written_bounds(record['number']);assert as_mp(sl)<v<as_mp(sh)
                quadrature.append({'geometry':key[0],'n':key[1],'dps':dps,'value':mp.nstr(v,110),'relative_difference_from_arb_midpoint':mp.nstr(err,8)})
                if dps==170:quad_values[key]=mp.nstr(v,170)
    stored_path=HERE/'stored-document.json'; stored_report=None
    if stored_path.exists():
        doc=json.loads(stored_path.read_text()); nums=doc['Numbers'];seen=[];stored_balls={}
        for geometry,group in nums.items():
            for n,entry in group.items():
                key=(geometry,int(n));s=entry['number'] if isinstance(entry,dict) else entry
                sl,sh=written_bounds(s);lo,hi=bounds(balls[key]);assert sl<=lo<=hi<=sh
                I=RealIntervalField(600)
                sb=R(I(QQ(sl.numerator)/sl.denominator,QQ(sh.numerator)/sh.denominator))
                safe(sb,320);stored_balls[key]=sb
                with mp.workdps(180):
                    v=mp.mpf(quad_values[key]);assert as_mp(sl)<v<as_mp(sh)
                seen.append(key)
        assert seen==list(balls)
        for n in range(3,31):
            area=R(n)/2*(2*pi/n).sin()
            rhs=R(2)**(QQ(4)/n)*area*stored_balls[('ideal-hyperbolic',n)]/pi
            lo,hi=bounds(stored_balls[('euclidean',n)]**2-rhs); assert lo<=0<=hi
        for geometry,expected in square_checks.items():
            assert expected.overlaps(stored_balls[(geometry,4)])
        stored_report={'entries':len(seen),'ordered_enumeration_equal':True,'all_serialized_intervals_contain_arb_balls':True,'all_quadrature_values_inside_intervals':True,'cross_geometry_identity_count':28,'square_agm_checks':2,'document_sha256':hashlib.sha256(stored_path.read_bytes()).hexdigest()}
    result={'table':'T554','instruction_manifest':MANIFEST,'skill_sha256':{'local':SKILL_HASH,'live':SKILL_HASH},'versions':{'sage':SAGE_VERSION,'numberdb':numberdb.__version__,'mpmath':mp.__version__,'python':platform.python_version()},'working_bits':numberdb.bits(100,losing=128),'digits':100,'entry_count':len(records),'minimum_relative_accuracy_bits':min(r['relative_accuracy_bits'] for r in records),'longest_serialized_value':max(len(r['number']) for r in records),'serialized_entries_bytes':len(json.dumps([{'params':r['params'],'number':r['number']} for r in records]).encode()),'arb_serialization_checks':56,'BHS_equation_1_5_checks':28,'strict_geometry_order_checks':28,'square_agm_checks':2,'agm_control':'AGM(1,1)=1','quadrature_controls':controls,'quadrature':quadrature,'records':records,'stored_checks':stored_report}
    print(json.dumps(result,indent=2))

if __name__=='__main__':main()