back to table · edit · history · where entries came from · files · download
10475 bytes, as of the version from 2026-11-01 17:54 (current). Recorded here, not run.
"""Independent numerical and identity checks for numberdb.org/T655.
sage -python checks.py > numerical-evidence.json
sage -python checks.py --stored > stored-evidence.json
The second command reads the private draft using NUMBERDB_API_KEY, or stdin
when NUMBERDB_KEY_FROM_STDIN=1. It writes nothing to NumberDB.
The independent computation bisects the reciprocal source equation exactly,
then evaluates Shannon entropy with Arb. It does not call the generator's
stationary polynomial, root-bracketing code, or objective evaluation.
The checks also use the source's reciprocal expression, closed forms at n=2,3,
and its published five-decimal values for n=2..10. Every claimed decimal
interval must contain the whole independent enclosure, not just its midpoint.
"""
import json
import sys
from fractions import Fraction
from pathlib import Path
import platform
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.polynomial.polynomial_ring_constructor import PolynomialRing
from sage.version import version as sage_version
from numberdb._write import to_text
CHECK_BITS = 560
CHECK_STEPS = 520
R = RealBallField(CHECK_BITS)
I = RealIntervalField(CHECK_BITS)
def qtext(text):
value = Fraction(str(text))
return QQ(value.numerator) / QQ(value.denominator)
def written_bounds(text):
"""A NumberDB decimal denotes its midpoint plus/minus one final place."""
text = str(text).lower()
coefficient, exponent = (text.split('e') + ['0'])[:2] if 'e' in text else (text, '0')
if '.' not in coefficient and 'e' not in text:
center = qtext(text)
return center, center
places = len(coefficient.split('.')[1]) if '.' in coefficient else 0
unit = QQ(10) ** (int(exponent) - places)
center = qtext(text)
return center - unit, center + unit
def finite_narrow(value, digits=130):
assert value.is_finite() and value > 0
interval = I(value)
lo, hi = QQ(interval.lower()), QQ(interval.upper())
assert (hi-lo) / lo < QQ(1) / (10**digits)
return lo, hi
def contained(text, value):
outer_lo, outer_hi = written_bounds(text)
inner_lo, inner_hi = finite_narrow(value)
assert outer_lo <= inner_lo <= inner_hi <= outer_hi, text
def independent(n):
"""Use CVZ (31)'s gamma equation, then KSS Lemma 5's entropy."""
def equation(g):
return QQ(1)/(g-1) - QQ(n)/(g**n-1) - QQ(n-1)/QQ(3)
if n == 2:
lower = upper = QQ(2)
assert equation(lower) == 0
else:
lower, upper = QQ(1), QQ(2)
assert equation(upper) < 0
for unused in range(CHECK_STEPS):
middle = (lower + upper)/QQ(2)
residual = equation(middle)
if residual == 0:
lower = upper = middle
break
if residual > 0:
lower = middle
else:
upper = middle
assert lower > 1
assert equation(lower) >= 0 >= equation(upper)
gamma = R(I(lower, upper))
weights = [gamma**(-j) for j in range(n)]
normalizer = sum(weights)
probability = [w/normalizer for w in weights]
entropy = -sum(p*p.log() for p in probability)
value = entropy.exp()
reciprocal_value = ((gamma**n-1)/(gamma-1) *
(-R(QQ(2*(n-1))/QQ(3))*gamma.log()).exp())
finite_narrow(value); finite_narrow(reciprocal_value)
assert (value-reciprocal_value).contains_zero()
assert (sum(probability)-1).contains_zero()
assert (sum(j*probability[j] for j in range(n))-R(QQ(n-1)/QQ(3))).contains_zero()
variance = sum((R(j)-R(QQ(n-1)/QQ(3)))**2*probability[j] for j in range(n))
assert variance > 0
return value, reciprocal_value, {'n':n,'gamma_lower':str(lower),'gamma_upper':str(upper),
'endpoint_signs_certified':True,
'entropy_ball':str(value),'reciprocal_ball':str(reciprocal_value)}
def records_of(document):
# The website exports the two ordered parameters as a nested mapping.
numbers=document['Numbers']
if isinstance(numbers,list):
return numbers
result=[]
for n, conventions in numbers.items():
for convention, entry in conventions.items():
if isinstance(entry,str): entry={'number':entry}
result.append({'params':{'n':str(n),'convention':convention},**entry})
return result
def run(document=None):
from generate import EllenbergGijswijt, constant, root_bracket
gen=EllenbergGijswijt()
# Controls run before any family comparison.
assert written_bounds('3.14') == (QQ(313)/100, QQ(315)/100)
control=R(QQ(1)/QQ(3))
control_entropy=-control*control.log()-(1-control)*(1-control).log()
closed2=R(3)/R(2)**(R(2)/3)
assert (control_entropy.exp()-closed2).contains_zero()
assert R(1) < closed2 < R(2)
finite_narrow(closed2)
independent2,other2,unused=independent(2)
assert (independent2-closed2).contains_zero()
assert not (independent2-R(2)).contains_zero()
closed3=R(3)*(R(207)+33*R(33).sqrt())**(R(1)/3)/8
finite_narrow(closed3)
if document is None:
records=[]
for params in gen.enumerate():
entry=dict(gen.value(params,100))
entry['number']=to_text(entry['number'],digits=100)
records.append({'params':params,**entry})
else:
records=records_of(document)
expected=[(n,c) for n in range(2,33) for c in ('subrank','per-dimension')]
actual=[(int(r['params']['n']),r['params']['convention']) for r in records]
assert actual == expected
written={key:r['number'] for key,r in zip(actual,records)}
poly=PolynomialRing(QQ,'x'); x=poly.gen()
certificates=[]; accuracies=[]; largest_width=QQ(0)
rounded={2:'1.88988',3:'2.75510',4:'3.61072',5:'4.46158',6:'5.30973',
7:'6.15620',8:'7.00155',9:'7.84612',10:'8.69012'}
prev_j=None
for n in range(2,33):
c, reciprocal, certificate=independent(n)
# Exact coefficient identity, independent of numerical root residuals.
s=sum(x**j for j in range(n))
p=3*x*s.derivative()-(n-1)*s
assert p == sum((3*j-n+1)*x**j for j in range(n))
reverse=sum((3*j-n+1)*x**(n-1-j) for j in range(n))
assert (x-1)**2*reverse == 3*(x**n-1)-3*n*(x-1)-(n-1)*(x-1)*(x**n-1)
lo,hi=root_bracket(n,numberdb.bits(100,losing=128)+32)
if lo==hi: assert p(lo)==0
else: assert p(lo)<0<p(hi)
certificate.update(rho_lower=str(lo),rho_upper=str(hi))
generated=constant(n,100)
finite_narrow(generated)
assert (generated-c).contains_zero()
assert generated > 1 and generated < n
glo, ghi = finite_narrow(generated)
relative_width = (ghi-glo)/glo
retained_bits = 0
while relative_width < QQ(1)/(2**(retained_bits+1)):
retained_bits += 1
accuracies.append(retained_bits)
for conv,value in [('subrank',c),('per-dimension',c/n)]:
contained(written[n,conv],value)
contained(written[n,conv],reciprocal if conv=='subrank' else reciprocal/n)
inner=generated if conv=='subrank' else generated/n
contained(written[n,conv],inner)
lower,upper=finite_narrow(inner)
largest_width=max(largest_width,(upper-lower)/lower)
assert (R(I(*written_bounds(written[n,'subrank']))) -
n*R(I(*written_bounds(written[n,'per-dimension'])))).contains_zero()
if prev_j is not None: assert prev_j > c/n
prev_j=c/n
if n in rounded:
contained(rounded[n],c)
# Source calls these rounded, so also test half a final place.
assert abs(c-R(qtext(rounded[n]))) < R(QQ(1)/200000)
if n==2: contained(written[n,'subrank'],closed2)
if n==3:
contained(written[n,'subrank'],closed3)
assert (c-closed3).contains_zero()
certificates.append(certificate)
local=Path(__file__).parent
companion=json.loads((local/'entropy-reference.json').read_text()) if (local/'entropy-reference.json').exists() else numberdb.table('T339')
bernoulli=companion['Numbers']['bernoulli']['1/3']
for unit in ('nats','bits'):
text=bernoulli[unit]
if isinstance(text,dict): text=text['number']
value=control_entropy if unit=='nats' else control_entropy/R(2).log()
contained(text,value)
evidence={
'table':'T655','mode':'stored' if document is not None else 'preflight',
'skill_manifest':'agents/runs/20261101T164559Z-build-skills/manifest.json',
'skill_sha256':'a2b28d9953264d7b0ca62663785c2952190c5be6d6d3597fd695d9a54d606278',
'versions':{'sage':sage_version,'python':platform.python_version(),'numberdb':numberdb.__version__},
'entries':len(records),
'serialized_longest_characters':max(len(r['number']) for r in records),
'serialized_records_json_bytes':len(json.dumps(records).encode('utf8')),
'dimensions':31,'conventions':['subrank','per-dimension'],
'digits':100,'minimum_generator_accuracy_bits':min(accuracies),
'maximum_generator_relative_interval_width':str(R(largest_width)),
'independent_precision_bits':CHECK_BITS,'independent_bisection_steps':CHECK_STEPS,
'checks':{'all_decimal_intervals_contain_both_computations':True,
'independent_entropy_and_reciprocal_expressions_agree':True,
'positive_finite_narrow_enclosures':True,'closed_forms_2_3':True,
'source_rounded_values_2_through_10':True,
'stationary_and_reciprocal_polynomial_identities_all_dimensions':True,
'normalization_conversion_all_dimensions':True,
'probability_sum_mean_and_positive_variance_all_dimensions':True,
'J_decreases_over_selected_dimensions':True,
'T339_bernoulli_one_third_nats_and_bits':True,
'control_rejects_incorrect_value_two':True},
'root_certificates':certificates,'records':records}
return evidence
if __name__=='__main__':
from generate import configure_from_stdin
configure_from_stdin()
document=numberdb.table('T655') if '--stored' in sys.argv else None
print(json.dumps(run(document),indent=2,ensure_ascii=False))