back to table · edit · history · where entries came from · files · download
4341 bytes, as of the version from 2026-10-27 01:11 (current). Recorded here, not run.
"""Checks for the Struve function table -- numberdb.org/T492.
Run with SageMath and numberdb installed:
sage -python check_t492_repair.py < /path/to/numberdb-key
For a public table stdin may be empty. A saved T492-document.json beside
this script is used if present. This script writes no API data. Its JSON
output records independent decimal comparisons and the negative-order
substitution used to derive the table's Bessel-integral formula.
DLMF 10.22.2 at nu=0 gives pi*x/2*(J0*H_minus1-J_minus1*H0).
Since J_minus1=-J1 and H_minus1=2/pi-H1, substitution gives the
formula printed in T492. The antiderivative tends to zero at the origin.
The Struve recurrence at nu=0 gives H_minus1+H1=1/(sqrt(pi)*Gamma(3/2));
Gamma(3/2)=sqrt(pi)/2 reduces that constant to 2/pi.
"""
import sys
import json
import pathlib
import urllib.request
from decimal import Decimal
import importlib.metadata
import math
import numberdb.sage as numberdb
from sage.rings.complex_arb import ComplexBallField
from sage.rings.rational_field import QQ
import mpmath as mp
p = pathlib.Path(__file__).parent / 'T492-document.json'
if p.exists():
doc = json.loads(p.read_text())
else:
key = sys.stdin.read().strip()
req = urllib.request.Request('https://numberdb.org/api/table?id=T492',
headers={'Authorization': 'Bearer ' + key} if key else {})
with urllib.request.urlopen(req, timeout=60) as response:
doc = json.load(response)
mp.mp.dps = 170
C = ComplexBallField(560)
def mpq(s):
q = QQ(s)
return mp.mpf(str(q.numerator())) / mp.mpf(str(q.denominator()))
def struve_ball(nu, x):
nu, x, a = C(QQ(nu)), C(QQ(x)), C(QQ(3)/2)
v = (x/2)**(nu+1) * (-(x*x)/4).hypergeometric([1], [a,nu+a]) / (a.gamma()*(nu+a).gamma())
assert math.isfinite(float(v.real().mid())) and math.isfinite(float(v.imag().mid()))
assert v.imag().contains_zero()
r = v.real()
assert math.isfinite(float(r.rad())) and float(r.rad()) < 1e-140, 'enclosure too wide'
return r
assert abs(mp.quad(lambda t:t,[0,1])-mp.mpf('0.5')) < mp.mpf('1e-160')
assert abs(mp.quad(lambda t:mp.sin(t),[0,mp.pi/2])-1) < mp.mpf('1e-160')
count=0
worst=mp.mpf(0)
for nu, rows in doc['Numbers'].items():
for x, written in rows.items():
assert isinstance(written,str)
ulp=mp.power(10,int(Decimal(written).as_tuple().exponent))
err=abs(mp.struveh(mpq(nu),mpq(x))-mp.mpf(written))/ulp
assert err < 1, (nu,x,str(err))
worst=max(worst,err); count+=1
constant=1/(C.pi().sqrt()*C(QQ(3)/2).gamma())
assert (constant-2/C.pi()).contains_zero()
args=list(doc['Numbers']['0'])
max_radius=0
for x in args:
hneg=struve_ball(-1,x); hpos=struve_ball(1,x)
residual=hneg+hpos-(2/C.pi()).real()
assert math.isfinite(float(residual.mid())) and residual.contains_zero()
assert math.isfinite(float(residual.rad())) and float(residual.rad()) < 1e-140
max_radius=max(max_radius,float(residual.rad()))
max_integral_error=mp.mpf(0)
selected=['1/4','1','13/3','5','10','20']
for s in selected:
x=mpq(s)
rhs=mp.pi*x/2*(mp.besselj(0,x)*(2/mp.pi-mp.struveh(1,x))+mp.besselj(1,x)*mp.struveh(0,x))
intermediate=mp.pi*x/2*(mp.besselj(0,x)*mp.struveh(-1,x)-mp.besselj(-1,x)*mp.struveh(0,x))
integral=mp.quad(lambda t:mp.besselj(0,t),[0,x/4,x/2,3*x/4,x])
err=max(abs(rhs-integral),abs(rhs-intermediate))
assert err < mp.mpf('1e-155'), (s,str(err))
max_integral_error=max(max_integral_error,err)
print(json.dumps({
'tid':'T492', 'all_checks_passed':True,
'versions':{n:importlib.metadata.version(n) for n in ['numberdb','mpmath']},
'sage_version': __import__('sage.version',fromlist=['version']).version,
'mpmath_decimal_precision':mp.mp.dps, 'ball_precision_bits':560,
'stored_decimal_intervals_checked':count,
'maximum_error_in_last_written_place':mp.nstr(worst,20),
'negative_order_identity_ball_checks':len(args),
'maximum_identity_residual_radius':max_radius,
'bessel_integral_arguments':selected,
'maximum_bessel_integral_absolute_error':mp.nstr(max_integral_error,12),
'controls':['integral t on [0,1] = 1/2','integral sin(t) on [0,pi/2] = 1'],
'source':'https://dlmf.nist.gov/10.22#E2',
'certification_note':'Mpmath interval comparisons are agreement checks; ball checks enclose the negative-order identity residual.'
},indent=2))