back to table · edit · history · where entries came from · files · download
8939 bytes, as of the version from 2026-10-26 23:52 (current). Recorded here, not run.
"""Independent checks for Airy derivative zeta values -- numberdb.org/T532.
Run: sage -python checks.py. Add --stored to check database values (the
NUMBERDB_CHECK_STORED=1 environment variable is equivalent). Download
check.py and generate.py beside this file. Credentials for a private draft
may be supplied on stdin with NUMBERDB_KEY_FROM_STDIN=1.
The independent computation extracts coefficients of Ai(z)/Ai'(z) by a
512-point Cauchy sum on |z|=1/2 at 600 bits. It shares no recurrence or
Gamma initialization with generate.py. Its alias bound is 2/(2^512-1).
Why that bound holds: DLMF 9.9(i) puts all Ai' zeros on the negative real
axis. On [-1,0], the two alternating Airy power series give Ai(-x)>0:
the initial-value-normalized components are >=1-x^3/6 and
x*(1-x^3/12), respectively, for 0<=x<=1. Therefore Ai''(z)=z Ai(z)<=0
there. The rigorously negative evaluation Ai'(-1) proves no derivative
zero lies on [-1,0], so every positive eigenvalue exceeds 1. By Voros
(43.2), Z_N(2)=-Ai(0)/Ai'(0)<2, verified here in balls. Hence
|[z^m](Ai/Ai')|=Z_N(m+2)<2. Discrete Fourier extraction at order m aliases
only m+j*512 (j>=1), bounded by 2*sum(2^(-j*512)). The alias error is
added to each extracted ball. The finite sum's roundoff is propagated by
Arb. The polynomial 3+2z+z^5 is the control, tested before the Airy sum.
Read numerical-evidence.md and skill-manifest.json for instruction hashes,
measurement results, sources and limitations. These are build checks, not
the separate independent critique or its argument-review sidecar.
"""
import json
import os
import sys
import time
from decimal import Decimal
from fractions import Fraction
import numberdb.sage as numberdb
from numberdb._write import to_text
from sage.rings.rational_field import QQ
from sage.rings.real_arb import RealBallField
from sage.rings.complex_arb import ComplexBallField
from sage.env import SAGE_VERSION
from check import exactness, measure, names_its_rings, agrees_with
from generate import AiryDerivativeZeta, polynomials, read_key_if_requested
COUNT = 512
CHECK_BITS = 600
B = RealBallField(CHECK_BITS)
C = ComplexBallField(CHECK_BITS)
def finite_narrow(v, bound=QQ(1)/10**120):
if hasattr(v, 'real') and hasattr(v, 'imag'):
assert v.real().is_finite() and v.imag().is_finite(), 'Nonfinite check ball'
assert v.real().rad() < bound and v.imag().rad() < bound
else:
assert v.is_finite() and v.rad() < bound
def cauchy_values():
phases = [(C(0, 2)*B.pi()*QQ(k)/QQ(COUNT)).exp() for k in range(COUNT)]
points = [w/2 for w in phases]
def extract(samples):
sums = [C(0) for _ in range(19)]
for w, sample in zip(phases, samples):
factor = C(1)
ratio = 2/w
for m in range(19):
sums[m] += sample*factor
factor *= ratio
return [s/COUNT for s in sums]
# A nonzero control prevents a coercion or Fourier-sign bug passing silently.
control = extract([3+2*z+z**5 for z in points])
for m, got in enumerate(control):
expected = {0: 3, 1: 2, 5: 1}.get(m, 0)
finite_narrow(got)
assert (expected in got.real()) and got.imag().contains_zero()
print('Cauchy control: all 19 coefficients of 3+2z+z^5 passed', flush=True)
assert C(-1).airy_ai_prime().real() < 0
t = -C(0).airy_ai()/C(0).airy_ai_prime()
finite_narrow(t)
assert 1 < t.real() < 2
# Independent evaluations at nonzero complex arguments.
samples = []
for z in points:
denominator = z.airy_ai_prime()
assert (0 not in denominator)
v = z.airy_ai()/denominator
finite_narrow(v)
samples.append(v)
alias = QQ(2)/QQ(2**COUNT-1)
result = {}
for m, got in enumerate(extract(samples)):
finite_narrow(got)
assert got.imag().contains_zero()
result[m+2] = ((-1)**(m+1)*got.real()).add_error(B(alias))
finite_narrow(result[m+2])
return result, alias
def interval_from_text(text):
text = str(text)
if '.' not in text and 'e' not in text.lower():
q = QQ(text)
return B(q), q, q
value = Decimal(text)
center = QQ(Fraction(value))
radius = QQ(10)**int(value.as_tuple().exponent)
return B(center, radius), center-radius, center+radius
def check_identities(values):
for n in range(2, 21):
assert values[n] > 0
for n in range(2, 20):
assert values[n] > values[n+1]
for n in range(3, 20):
assert values[n-1]*values[n+1] > values[n]**2
for n in range(4, 21):
residual = (n-2)*values[n] - sum((values[j]*values[n-j] for j in range(2,n-1)), B(0))
assert residual.contains_zero(), ('recurrence',n)
# Published identities with the reflection-form Gamma expression for kappa.
t_reflect = B(3)**(QQ(1)/6)*B(QQ(1)/3).gamma()**2/(2*B.pi())
assert values[2].overlaps(t_reflect)
assert (1 in values[3])
low = {4: t_reflect**2/2, 5: 2*t_reflect/3, 6: (t_reflect**3+1)/4}
for n,v in low.items():
assert values[n].overlaps(v)
def main():
started = time.monotonic()
read_key_if_requested()
if os.environ.get('NUMBERDB_API_KEY'):
numberdb.configure(api_key=os.environ['NUMBERDB_API_KEY'])
g=AiryDerivativeZeta()
params=list(g.enumerate())
assert params == [{'n':str(n)} for n in range(2,21)]
produced={int(p['n']):g.value(p,100) for p in params}
values={n:(v['number'] if isinstance(v,dict) else v) for n,v in produced.items()}
assert not exactness(produced)
assert not exactness({n:polynomials(20)[n] for n in range(2,21)})
assert not names_its_rings(os.path.join(os.path.dirname(__file__),'generate.py'))
assert not agrees_with({3:values[3]}, lambda n:QQ(1))
text={n:to_text(v,digits=100) for n,v in values.items()}
high={n:g.value({'n':str(n)},160) for n in range(2,21)}
for n,v in high.items():
v=v['number'] if isinstance(v,dict) else v
if n!=3:
finite_narrow(v)
assert values[n].overlaps(v)
independent, alias=cauchy_values()
for n,v in values.items():
if n==3:
assert v==1 and text[n]=='1' and (1 in independent[n])
else:
finite_narrow(v)
assert v.overlaps(independent[n]), ('Cauchy comparison',n)
_,lo,hi=interval_from_text(text[n])
assert QQ(independent[n].lower()) >= lo and QQ(independent[n].upper()) <= hi
check_identities({n:B(v) for n,v in values.items()})
print('All 19 values: higher precision, Cauchy enclosures, stored spelling and identities passed')
result={'sage':SAGE_VERSION,'numberdb':numberdb.__version__,
'working_bits':numberdb.bits(100,losing=96),
'higher_bits':numberdb.bits(160,losing=96),
'check_bits':CHECK_BITS,'cauchy_points':COUNT,
'alias_bound':str(alias),
'min_accuracy_bits':min(int(v.accuracy()) for n,v in values.items() if n!=3),
'worst_relative_radius':max(float(v.rad()/abs(v.center())) for n,v in values.items() if n!=3),
'measure':measure(text),
'values':{str(n):text[n] for n in range(2,21)},
'independent_balls':{str(n):str(v) for n,v in independent.items()}}
if '--stored' in sys.argv or os.environ.get('NUMBERDB_CHECK_STORED')=='1':
document=numberdb.table('T532')
records=document['Numbers']
if isinstance(records,dict):
records=[{'params':{'n':k}, **(v if isinstance(v,dict) else {'number':v})} for k,v in records.items()]
written={int(r['params']['n']):str(r['number']) for r in records}
assert list(written)==list(range(2,21))
stored_balls={}
for n,t in written.items():
ball,lo,hi=interval_from_text(t); stored_balls[n]=ball
if n==3:
assert t=='1' and (1 in independent[n])
else:
assert QQ(independent[n].lower()) >= lo and QQ(independent[n].upper()) <= hi
check_identities(stored_balls)
# Test the actual published program, not a remembered copy.
import contextlib
import io
namespace = {}
with contextlib.redirect_stdout(io.StringIO()):
exec(document['Programs']['program-sage']['code'], namespace)
assert list(namespace['Z']) == list(range(2,21))
for n, v in namespace['Z'].items():
assert B(v).overlaps(independent[n]), ('program', n)
assert namespace['Z'][3] == QQ(1)
report=g.verify(sample=None); assert report.ok,report
result['stored_check']={'entries':len(written),'verify':str(report),'all_independent_enclosures_contained':True}
print('Read-back checks: all 19 independent enclosures, recurrence and inequalities passed')
result['seconds']=time.monotonic()-started
print('EVIDENCE_JSON '+json.dumps(result,sort_keys=True))
if __name__=='__main__':
main()