back to table · edit · history · where entries came from · files · download
7365 bytes, as of the version from 2026-10-26 14:02 (current). Recorded here, not run.
"""Real zeros of the derivative of the Scorer function Gi -- numberdb.org/T517.
Run with SageMath (or passagemath with numberdb):
sage -pip install numberdb
sage -python generate.py # verify every stored entry
sage -python generate.py --fill # fill the existing draft
Set NUMBERDB_API_KEY for authenticated draft access. --fill sends entries;
it does not publish the table or offer it for review.
The quantity is the signed location g'_n, not the height of Gi. Index zero
selects the unique positive real zero; 1,...,12 select the negative zeros
nearest zero, in decreasing order. DLMF 9.12 fixes the normalization.
Certification uses 700-bit Arb arithmetic, 900 Maclaurin coefficients, and
geometric tails on |x| <= 16 for Gi and its first two derivatives. For a
term indexed by k, the ratios three indices later for derivative orders
0,1,2 are respectively R^3/((k+3)(k+2)), R^3/(k(k+2)), and
R^3/(k(k-1)). After the last term in each residue class the ratios decrease.
The absolute omitted tail is at most the last absolute term times q/(1-q).
To count roots, adjacent rational cells cover the path from zero to the
12th negative root and [0,1]. On a cell of half-width h and midpoint m,
Gronwall applied to (Gi,Gi') gives the uniform bound
B = (|Gi(m)| + |Gi'(m)| + h/pi) exp(16 h)
on both components, since max(1,|x|) <= 16. Thus
Gi(x) is in Gi(m) +/- h B, and Gi'(x) is in
Gi'(m) +/- h (R B + 1/pi), where R bounds |x| on the cell.
Gi''(x) = x Gi(x) - 1/pi then certifies monotonicity of Gi'.
Each cell either excludes a zero of Gi', or has a fixed sign of Gi'' and
endpoint signs that determine whether it contains exactly one zero.
Unresolved cells are subdivided. This certifies the negative-root indices;
DLMF 9.12(ix) establishes uniqueness on the entire nonnegative axis.
mpmath supplies candidates only. Every root is certified independently of
the candidate accuracy by opposite ball signs at exact rational endpoints
2*10^-120 apart and fixed-sign Gi'' throughout that bracket. Root balls
have about 120 decimal digits, leaving a measured 20-digit margin over the
100 significant digits stored. checks.py supplies independent integral and
mpmath comparisons, with controls, and can repeat them on stored values.
"""
import os
import sys
from functools import lru_cache
import numberdb.sage as numberdb
from sage.rings.rational_field import QQ
from sage.rings.real_arb import RealBallField
import mpmath as mp
PRECISION_BITS = 700
TERMS = 900
DOMAIN_RADIUS = 16
BRACKET_PLACES = 120
R = RealBallField(PRECISION_BITS)
def sign(x):
assert x.is_finite()
return 1 if x > 0 else -1 if x < 0 else 0
def widen(x, error):
return x + R(0, R(error).upper())
@lru_cache(None)
def coefficients():
c = [1 / (R(3)**(QQ(7)/6) * R(QQ(2)/3).gamma()),
1 / (R(3)**(QQ(5)/6) * R(QQ(1)/3).gamma()),
-1 / (2*R.pi())]
for k in range(TERMS-3):
c.append(c[k] / ((k+3)*(k+2)))
return c
@lru_cache(None)
def derivative_coefficients(d):
c = coefficients()
if d == 0:
return c
if d == 1:
return [k*c[k] for k in range(1, TERMS)]
if d == 2:
return [k*(k-1)*c[k] for k in range(2, TERMS)]
raise ValueError('only derivative orders zero through two are implemented')
@lru_cache(None)
def tail(d):
c = coefficients()
total = R(0)
for k in range(TERMS-3, TERMS):
factor = [1, k, k*(k-1)][d]
denominator = [(k+3)*(k+2), k*(k+2), k*(k-1)][d]
q = R(DOMAIN_RADIUS)**3 / denominator
assert 0 < q < 1
total += abs(c[k])*factor*R(DOMAIN_RADIUS)**(k-d)*q/(1-q)
assert total < R('1e-400')
return total
def evaluate(x, d=0):
x = R(x)
assert abs(x) <= DOMAIN_RADIUS
answer = R(0)
for c in reversed(derivative_coefficients(d)):
answer = answer*x+c
answer = widen(answer, tail(d))
assert answer.is_finite()
return answer
@lru_cache(None)
def point(x):
return evaluate(x), evaluate(x, 1)
def local_enclosures(a, b):
m, h = (a+b)/2, (b-a)/2
y, v = point(m)
bound = (abs(y)+abs(v)+R(h)/R.pi())*(16*R(h)).exp()
y_box = widen(y, R(h)*bound)
v_box = widen(v, R(h)*(max(abs(a),abs(b))*bound+1/R.pi()))
acceleration = R(m, h)*y_box-1/R.pi()
return v_box, acceleration
def classify(a, b, records, depth=0):
v_box, acceleration = local_enclosures(a,b)
if sign(v_box):
records.append({'a':str(a),'b':str(b),'test':'excludes-zero'})
return []
if sign(acceleration):
sa, sb = sign(point(a)[1]), sign(point(b)[1])
assert sa and sb
roots = sa != sb
records.append({'a':str(a),'b':str(b),'test':'monotone',
'left_sign':sa,'right_sign':sb,'root':roots})
return [(a,b)] if roots else []
assert depth < 20, 'interval root count did not resolve'
m = (a+b)/2
return classify(a,m,records,depth+1)+classify(m,b,records,depth+1)
@lru_cache(None)
def root_cells():
records=[]
positive=[]
for k in range(64):
positive += classify(QQ(k)/64,QQ(k+1)/64,records)
assert len(positive)==1
negative=[]
for k in range(1024):
negative += list(reversed(classify(-QQ(k+1)/64,-QQ(k)/64,records)))
if len(negative)==12:
break
assert len(negative)==12
return positive+negative, records
def mp_rational(q):
return mp.mpf(int(q.numerator()))/int(q.denominator())
@lru_cache(None)
def certified_root(n):
cells, _ = root_cells()
a,b = cells[n]
with mp.workdps(170):
z = mp.findroot(lambda x:mp.diff(mp.scorergi,x),
(mp_rational(a),mp_rational(b)))
center = QQ(int(mp.nint(z*mp.mpf(10)**BRACKET_PLACES)))/10**BRACKET_PLACES
epsilon = QQ(1)/10**BRACKET_PLACES
lo,hi = center-epsilon,center+epsilon
assert a < lo < hi < b
assert sign(evaluate(lo,1))*sign(evaluate(hi,1)) == -1
assert sign(local_enclosures(lo,hi)[1])
ball=R(center,epsilon)
assert ball.is_finite() and R(ball.rad()) < R('1.01e-120')
return ball,lo,hi
class ScorerGiPrimeZeros(numberdb.Generator):
table='T517'
parameters=('n',)
type='R'
digits=100
rigour='proven'
files=('generate.py','checks.py','check.py')
def enumerate(self):
for n in range(13):
yield {'n':str(n)}
def value(self, params, digits):
if digits > 100:
raise ValueError('increase and revalidate the certificate for more than 100 digits')
n=int(params['n'])
if not 0 <= n <= 12:
raise ValueError('the certified index range is zero through twelve')
value=certified_root(n)[0]
if n==0:
return {'number':value,'comment':'The unique positive real zero of $\\operatorname{Gi}^{\\prime}$. It is the location of a local maximum of $\\operatorname{Gi}$.'}
return value
if __name__=='__main__':
if os.environ.get('NUMBERDB_KEY_FROM_STDIN')=='1':
os.environ['NUMBERDB_API_KEY']=sys.stdin.read().strip()
generator=ScorerGiPrimeZeros()
if '--fill' in sys.argv or os.environ.get('NUMBERDB_PUBLISH')=='1':
print(generator.publish(message='Fill thirteen certified signed Gi derivative zeros'))
else:
report=generator.verify(sample=None)
print(report)
sys.exit(0 if report.ok else 1)