checks.py

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

8744 bytes, as of the version from 2026-11-09 23:15 (current). Recorded here, not run.

"""Independent checks for inverse binomial sums -- numberdb.org/T698.

Run: sage -python checks.py
Set NUMBERDB_API_KEY to read the private draft and companion T697.
This script writes nothing to the website. It emits EVIDENCE_JSON for archiving.
The generator certifies values; the independent elementary-integral quadrature
at 150 and 200 digits is only a check. Companion decimal intervals and closed
forms are checked both before filling and on the values read from T698.
Instruction provenance: skill-manifest.json, archived at
agents/runs/20261109T224707Z-build-skills/manifest.json.
"""
import os,sys,json,hashlib,time,platform
from pathlib import Path
from math import factorial
from decimal import Decimal
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.rings.complex_arb import ComplexBallField
from sage.env import SAGE_VERSION
import mpmath as mp
from generate import InverseBinomial,evaluate,WORKING_GUARD_BITS,endpoint_workspace
from check import exactness,measure,names_its_rings
from numberdb._write import to_text

R=RealBallField(800)
I=RealIntervalField(800)

def decimal_rational(text):
    d=Decimal(text)
    e=int(d.as_tuple().exponent)
    center=QQ(int(''.join(str(x) for x in d.as_tuple().digits))) * QQ(10)**e
    if d.is_signed(): center=-center
    return center,QQ(10)**e

def decimal_interval(text):
    center,ulp=decimal_rational(text)
    return I(center-ulp,center+ulp)

def rows(document):
    # API documents here use ordered nested Numbers or the flat record schema.
    block=document.get('Numbers',[])
    names=list(document.get('Parameters',{}))
    if isinstance(block,list):
        return {tuple(str(e['params'][n]) for n in names):e['number'] for e in block}
    out={}
    def walk(node,keys):
        if isinstance(node,dict) and 'number' in node:
            out[tuple(keys)]=node['number']; return
        if len(keys)==len(names) and isinstance(node,(str,int)):
            out[tuple(keys)]=str(node); return
        for k,v in node.items(): walk(v,keys+[str(k)])
    if block: walk(block,[])
    return out

def f2(u):
    if u>=0: return 2*mp.asin(mp.sqrt(u)/2)**2
    return -2*mp.asinh(mp.sqrt(-u)/2)**2

def integral(s,u,dps):
    with mp.workdps(dps):
        if s==2: return mp.nstr(f2(mp.mpf(u)),dps)
        def f(y):
            return 2*y**(2*s-5)*f2(mp.mpf(u)*mp.exp(-y*y))/mp.factorial(s-3)
        return mp.nstr(mp.quad(f,[0,1,2,4,8,16,mp.inf]),dps)

def assert_overlap(a,b,label):
    a,b=I(a),I(b)
    assert not a.is_NaN() and not b.is_NaN(),label
    assert I(a.absolute_diameter())<I('1e-88') and I(b.absolute_diameter())<I('1e-88'),label
    assert a.overlaps(b),label

def closed_forms(values):
    B=RealBallField(700); pi=B.pi()
    for u in ['1','2','3','4']:
        expected=2*(B(QQ(u)).sqrt()/2).arcsin()**2
        assert_overlap(values[('2',u)],expected,('F2',u))
    assert_overlap(values[('2','-1')],-2*(B(1)/2).arcsinh()**2,'F2(-1)')
    assert_overlap(values[('3','-1')],-B(2)*B(3).zeta()/5,'Apery')
    assert_overlap(values[('4','1')],B(17)*B(4).zeta()/36,'F4(1)')
    C=ComplexBallField(700)
    z=(C.gen(0)*C.pi()/3).exp()
    assert_overlap(values[('3','1')],2*pi*z.polylog(2).imag()/3-B(4)*B(3).zeta()/3,'F3(1) Clausen')
    assert_overlap(values[('5','1')],2*pi*z.polylog(4).imag()-B(19)*B(5).zeta()/3+B(2)*B(3).zeta()*B(2).zeta()/3,'F5(1) Clausen')
    assert_overlap(values[('3','4')],pi**2*B(2).log()-B(7)*B(3).zeta()/2,'F3(4)')
    return 10

def companion_checks(values,documents):
    ls=rows(documents['T697']); zeta=rows(documents['T14']); cl=rows(documents['T175'])
    angle={'1':'1/3','2':'1/2','3':'2/3','4':'1'}
    count=0
    for s in range(2,11):
        for u,t in angle.items():
            theta=I(R.pi()*R(QQ(t)))
            logu=I(R(int(u)).log())
            expected=I(0)
            for i in range(s-1):
                lsvalue=-theta**2/2 if i==0 else decimal_interval(ls[(str(i+2),'1',t)])
                expected-=I((-2)**i)*logu**(s-2-i)*lsvalue/(factorial(i)*factorial(s-2-i))
            assert_overlap(values[(str(s),u)],expected,('T697 triangular identity',s,u))
            count+=1
    assert_overlap(values[('3','-1')],-I(2)*decimal_interval(zeta[('3',)])/5,'stored zeta3')
    assert_overlap(values[('4','1')],I(17)*decimal_interval(zeta[('4',)])/36,'stored zeta4')
    assert_overlap(values[('3','1')],2*I(R.pi())*decimal_interval(cl[('2','1/3')])/3-I(4)*decimal_interval(zeta[('3',)])/3,'stored Clausen2')
    assert_overlap(values[('5','1')],2*I(R.pi())*decimal_interval(cl[('4','1/3')])-I(19)*decimal_interval(zeta[('5',)])/3+I(2)*decimal_interval(zeta[('3',)])*decimal_interval(zeta[('2',)])/3,'stored Clausen4')
    return count+4

def main():
    if os.environ.get('NUMBERDB_KEY_FROM_STDIN')=='1':
        os.environ['NUMBERDB_API_KEY']=sys.stdin.read().strip()
    started=time.time()
    # Nonzero known-answer and deliberately false controls precede numerical use.
    with mp.workdps(150):
        for s in [3,10]:
            control=mp.quad(lambda y:2*y**(2*s-5)*mp.exp(-y*y)/mp.factorial(s-3),[0,1,2,4,8,16,mp.inf])
            assert abs(control-1)<mp.mpf('1e-140')
            assert abs(control-mp.mpf('1.01'))>mp.mpf('0.009')
    print('Known-answer quadrature and wrong-answer controls passed.',flush=True)
    generator=InverseBinomial(); params=list(generator.enumerate())
    assert params==[{'s':str(s),'u':str(u)} for s in range(2,11) for u in [1,-1,2,3,4]]
    assert not names_its_rings(Path(__file__).with_name('generate.py'))
    values={}; records=[]; min_accuracy=10000
    for p in params:
        key=(p['s'],p['u'])
        v=evaluate(*key,100); higher=evaluate(*key,140)
        assert v.overlaps(higher)
        min_accuracy=min(min_accuracy,int(v.accuracy()))
        text=to_text(v,100)
        interval=decimal_interval(text)
        assert I(higher) in interval
        if not records:
            ulp=interval.absolute_diameter()/2
            assert not interval.overlaps(I(higher)+10*I(ulp))
        a=integral(int(p['s']),p['u'],150)
        b=integral(int(p['s']),p['u'],200)
        with mp.workdps(210):
            discrepancy=abs(mp.mpf(a)-mp.mpf(b))
            assert discrepancy < mp.mpf('1e-135')*max(1,abs(mp.mpf(b))),key
        assert I(decimal_rational(b)[0]) in interval,key
        values[key]=I(v)
        records.append({'params':p,'number':text,'accuracy_bits':int(v.accuracy()),'integral_150':a,'integral_200':b,'integral_difference':str(discrepancy)})
        print('ENTRY_JSON '+json.dumps(records[-1]),flush=True)
        print('Checked',p,'accuracy',int(v.accuracy()),'elapsed',round(time.time()-started,1),flush=True)
    assert not exactness({k:evaluate(*k) for k in values})
    forms=closed_forms(values)
    docs={tid:numberdb.table(tid) for tid in ['T697','T14','T175']}
    companions=companion_checks(values,docs)
    document=numberdb.table('T698'); stored=rows(document)
    readback=0
    if stored:
        assert list(stored)==list(values)
        read_values={key:decimal_interval(text) for key,text in stored.items()}
        for record in records:
            key=tuple(record['params'][n] for n in ['s','u'])
            assert I(evaluate(*key,140)) in read_values[key],key
            assert I(decimal_rational(record['integral_200'])[0]) in read_values[key],key
            readback+=1
        closed_forms(read_values)
        companion_checks(read_values,docs)
        report=generator.verify(sample=None)
        assert report.ok,str(report)
        print(report,flush=True)
    manifest=json.loads(Path(__file__).with_name('skill-manifest.json').read_text())
    evidence={'table':'T698','skill_manifest_path':'agents/runs/20261109T224707Z-build-skills/manifest.json','skill_manifest':manifest,
              'versions':{'python':platform.python_version(),'sage':SAGE_VERSION,'numberdb':numberdb.__version__,'mpmath':mp.__version__},
              'generator_sha256':hashlib.sha256(Path(__file__).with_name('generate.py').read_bytes()).hexdigest(),
              'selection_count':len(records),'working_bits':numberdb.bits(100,losing=WORKING_GUARD_BITS),'endpoint_degree':endpoint_workspace(100)[1],
              'minimum_accuracy_bits':min_accuracy,'size':measure({str(k):to_text(evaluate(*k),100) for k in values}),
              'known_answer_controls':2,'wrong_answer_control':True,'ten_ulp_perturbation_rejected':True,
              'closed_form_checks':forms,'companion_checks':companions,'stored_entries_checked':readback,'entries':records,'elapsed_seconds':time.time()-started}
    print('EVIDENCE_JSON '+json.dumps(evidence,default=str),flush=True)

if __name__=='__main__': main()