back to table · edit · history · where entries came from · files · download
7793 bytes, as of the version from 2026-10-31 20:22 (current). Recorded here, not run.
"""Independent checks for Bettin–Conrey cotangent sums, numberdb.org/T639.
sage -python checks.py
sage -python checks.py --stored # NUMBERDB_API_KEY required for draft
The exact check continues the Estermann series through Hurwitz zeta values
zeta(0,a/k)=1/2-a/k. No cotangent occurs in that computation. A second
numerical check uses Arb's digamma implementation and the reflection formula.
Controls are tested before the full range. Every compared enclosure must be
finite and narrow. Stored decimal intervals are checked for containment of
higher precision independent balls, not just agreement of their midpoints.
"""
import json
import sys
from functools import lru_cache
from math import gcd, lcm
import numberdb.sage as numberdb
from numberdb._write import to_text
from numberdb._generate import _stored_entries
from sage.rings.number_field.number_field import CyclotomicField
from sage.rings.rational_field import QQ
from sage.rings.complex_arb import ComplexBallField
from sage.rings.real_arb import RealBallField
from generate import BettinConrey, algebraic_sum, cotangent_ball
PRECISION = 550
C = ComplexBallField(PRECISION)
B = RealBallField(PRECISION)
def plain(value):
return value['number'] if isinstance(value, dict) else value
def narrow(value, relative=False):
assert value.is_finite(), 'Nonfinite comparison'
if relative and not value.contains_zero():
assert value.rad() < abs(value).lower() * (QQ(1) / 10**120)
else:
assert value.rad() < QQ(1) / 10**120
return value
@lru_cache(maxsize=None)
def estermann_exact(k, h):
n = lcm(k, 4)
field = CyclotomicField(n)
z = field.gen()
i = z**(n//4)
root = z**(n//k)
coefficients = [0] * k
for a in range(1, k+1):
for b in range(1, k+1):
coefficients[(h*a*b) % k] += (k-2*a) * (k-2*b)
D = sum(QQ(c) * root**j for j,c in enumerate(coefficients)) / (4*k*k)
# The conjugate here is the exact cyclotomic automorphism z -> z^-1.
conjugate = field.hom([1/z])(D)
assert D + conjugate == QQ(1)/2
return (D - conjugate)/i, D
def exact_ball(value):
embedded = C(value)
narrow(embedded.real())
narrow(embedded.imag())
assert embedded.imag().contains_zero()
return embedded.real()
@lru_cache(maxsize=None)
def digammas(k):
return [None]+[C(QQ(r)/k).psi().real() for r in range(1,k)]
def digamma_value(k,h):
psi=digammas(k)
value=sum(B(QQ(m)/k)*(psi[(m*h)%k]-psi[k-(m*h)%k])
for m in range(1,k))/B.pi()
return narrow(value)
def vasyunin(k,h):
value=B(0)
for m in range(1,k):
if 2*m==k:
continue
value += B(QQ((m*h)%k)/k) * (B.pi()*B(QQ(m)/k)).cot()
return narrow(value)
def decimal_rational(text):
from decimal import Decimal
tup=Decimal(text).as_tuple()
integer=int(''.join(map(str,tup.digits))) * (-1 if tup.sign else 1)
return QQ(integer)*QQ(10)**int(tup.exponent)
def stored_interval(text):
text=str(text).strip()
if '.' not in text and 'e' not in text.lower():
return QQ(text),QQ(text)
from decimal import Decimal
centre=decimal_rational(text)
unit=QQ(10)**int(Decimal(text).as_tuple().exponent)
return centre-unit,centre+unit
def main(stored_document=None):
# Nonzero and zero controls, evaluated before any comparison is accepted.
psi_control=C(QQ(1)/2).psi().real()
expected=-B.euler_constant()-2*B(2).log()
narrow(psi_control);narrow(expected)
assert psi_control.overlaps(expected) and psi_control < -1
e2,D2=estermann_exact(2,1)
e4,D4=estermann_exact(4,1)
assert e2==0 and D2==QQ(1)/4
assert e4==QQ(1)/2 and D4==QQ(1)/4+D4.parent().gen()*QQ(1)/4
assert (QQ(1)/2) in digamma_value(4,1)
assert (-QQ(1)/2) in vasyunin(4,1)
control3=exact_ball(estermann_exact(3,1)[0])
assert control3.overlaps(1/(3*B(3).sqrt()))
assert not control3.overlaps(-control3)
assert not control3.overlaps(control3+B(QQ(1)/10**100))
assert exact_ball(estermann_exact(6,1)[0]).overlaps(7/(3*B(3).sqrt()))
generator=BettinConrey()
selected=list(generator.enumerate())
numbers={};exact_rationals={};max_radius=B(0);min_accuracy=10000
for params in selected:
k,h=int(params['k']),int(params['h'])
value=plain(generator.value(params,100))
exact,D=estermann_exact(k,h)
assert exact == algebraic_sum(k,h), (k,h,'exact Estermann disagreement')
embed=exact_ball(exact)
digamma=digamma_value(k,h)
assert embed.overlaps(digamma),(k,h,'digamma disagreement')
if exact in QQ:
assert value==QQ(exact)
exact_rationals[f'{k},{h}']=str(QQ(exact))
else:
narrow(value,relative=True)
assert value.overlaps(embed)
max_radius=max(max_radius,B(value.rad()))
min_accuracy=min(min_accuracy,int(value.accuracy()))
assert narrow(cotangent_ball(k,h+k,120)).overlaps(embed)
numbers[k,h]=B(value)
for k,h in numbers:
assert (numbers[k,h]+numbers[k,k-h]).contains_zero()
assert (numbers[k,pow(h,-1,k)]+vasyunin(k,h)).contains_zero()
records=[{'params':p,'number':to_text(plain(generator.value(p,100)),digits=100)} for p in selected]
stored_count=0
if stored_document is not None:
assert tuple(stored_document['Parameters']) == ('k','h')
live=[{'params':dict(zip(('k','h'),identity.split(','))), 'number':value}
for identity,value in _stored_entries(stored_document).items()]
assert [tuple(int(r['params'][p]) for p in ('k','h')) for r in live] == list(numbers)
intervals={}
for record in live:
k,h=(int(record['params'][p]) for p in ('k','h'))
lo,hi=stored_interval(record['number'])
independent=exact_ball(estermann_exact(k,h)[0])
if lo==hi:
assert estermann_exact(k,h)[0]==lo
else:
assert lo<=independent.lower() and independent.upper()<=hi,(k,h,'not contained')
intervals[k,h]=(lo,hi)
stored_count+=1
for (k,h),(lo,hi) in intervals.items():
rlo,rhi=intervals[k,k-h]
assert lo+rlo<=0<=hi+rhi
ilo,ihi=intervals[k,pow(h,-1,k)]
V=vasyunin(k,h)
assert ilo<=-V.upper() and -V.lower()<=ihi if ilo!=ihi else (-ilo) in V
from sage.version import version as sage_version
import platform
result={'versions':{'sage':sage_version,'numberdb':numberdb.__version__,'python':platform.python_version()},'table':'T639','entries':len(selected),'exact_rationals':exact_rationals,
'exact_estermann_checks':len(selected),'digamma_checks':len(selected),
'periodicity_checks':len(selected),'reflection_checks':len(numbers),
'vasyunin_checks':len(numbers),'controls':'known zero, nonzero, sign-flip and 1e-100 perturbation controls passed before range checks',
'minimum_accuracy_bits':min_accuracy,'maximum_absolute_ball_radius':str(max_radius),
'maximum_stored_characters':max(len(r['number']) for r in records),
'stored_independent_containment_checks':stored_count,
'precision_bits_independent':PRECISION,
'skill_manifest':'agents/runs/20261031T200151Z-build-skills/manifest.json',
'skill_local_sha256':'a2b28d9953264d7b0ca62663785c2952190c5be6d6d3597fd695d9a54d606278',
'skill_live_sha256':'a2b28d9953264d7b0ca62663785c2952190c5be6d6d3597fd695d9a54d606278'}
print('EVIDENCE_JSON '+json.dumps(result,sort_keys=True))
print('NUMBERS_JSON '+json.dumps(records))
return result
if __name__=='__main__':
document=numberdb.table('T639') if '--stored' in sys.argv else None
main(document)