back to table · edit · history · where entries came from · files · download
7912 bytes, as of the version from 2026-10-30 09:56. Recorded here, not run.
"""Independent checks for T622, run with sage -python checks.py.
Default: compute the generator locally, then check it by a cotangent sum in
512-bit Arb, Dedekind reciprocity, and weighted lattice-point enumeration.
--stored: read the table through the API and run every check on stored values.
NUMBERDB_KEY_FROM_STDIN=1 selects --stored in the campaign wrapper.
This file and the unchanged campaign check.py are attached to the draft.
The output following EVIDENCE_JSON is a complete reproducible evidence object.
"""
import numberdb.sage as numberdb
from sage.rings.rational_field import QQ
from sage.rings.real_arb import RealBallField
from sage.env import SAGE_VERSION
from fractions import Fraction
from functools import lru_cache
from math import gcd
import hashlib
import json
import os
from pathlib import Path
import sys
import generate
from check import exactness, measure, agrees_with, names_its_rings
BITS = 512
R = RealBallField(BITS)
def cotangent_eta(p, q, u):
"""Independent trigonometric representation, without sawtooth arithmetic."""
total = R(0)
for j in range(1, p):
a = R.pi()*R(j)/R(p)
b = R.pi()*R(q*j)/R(p)
c = R.pi()*R(2*u*j)/R(p)
total += a.cos()/a.sin() * b.cos()/b.sin() * c.cos()
return -total/R(p)
@lru_cache(None)
def dedekind_reciprocity(a, b):
"""Classical Dedekind sum from reciprocity, using only Python Fractions."""
if b == 1:
return Fraction(0)
a %= b
assert a > 0 and gcd(a, b) == 1
return (-Fraction(1, 4) + (Fraction(a, b)+Fraction(b, a)
+Fraction(1, a*b))/12 - dedekind_reciprocity(b, a))
def lattice_rho(p, q, u):
"""Count actual points of the Casson-Gordon triangle, including edges.
The triangle has vertices (0,0), (k,0), (k,k*q/p), where k*q=u mod p.
Interior, open-edge, nonorigin vertex weights are 1, 1/2, 1/4.
k=0 is handled separately: the trivial rho is zero.
"""
if u == 0:
return Fraction(0)
k = (u*pow(q, -1, p)) % p
weighted = Fraction(0)
for x in range(k+1):
for y in range((k*q)//p+1):
if p*y > q*x or (x == 0 and y == 0):
continue
if x == k and (y == 0 or p*y == q*k):
weighted += Fraction(1, 4)
elif y == 0 or x == k or p*y == q*x:
weighted += Fraction(1, 2)
else:
weighted += 1
area = Fraction(q*k*k, 2*p)
return 4*(weighted-area)
def independent_exact(key):
p, q, u = key
rational = -4*dedekind_reciprocity(q,p)+lattice_rho(p,q,u)
return QQ(rational.numerator) / QQ(rational.denominator)
def canonical_key(p, q, u):
inverse = pow(q, -1, p)
if inverse < q:
q, u = inverse, (u*inverse) % p
return p, q, min(u, (-u) % p)
def control_checks():
known = {(2,1,0):QQ(0), (2,1,1):QQ(0), (3,1,0):-QQ(2)/9,
(3,1,1):QQ(1)/9, (5,1,0):-QQ(4)/5, (5,1,1):-QQ(1)/5,
(5,1,2):QQ(3)/5, (3,2,1):-QQ(1)/9}
assert dedekind_reciprocity(1, 3) == Fraction(1,18)
assert lattice_rho(3,1,1) == Fraction(1,3)
assert lattice_rho(5,1,2) == Fraction(7,5)
for key, expected in known.items():
ball = cotangent_eta(*key)
assert ball.is_finite() and R(ball.rad()) < R(10)**(-130)
assert ball.contains_exact(expected), (key, ball, expected)
assert independent_exact(key) == expected, (key, independent_exact(key), expected)
return {','.join(map(str,k)):str(v) for k,v in known.items()}
def main():
controls = control_checks() # Must pass before using either checker.
generator = generate.LensEta()
expected_keys = [tuple(int(params[k]) for k in generator.parameters)
for params in generator.enumerate()]
stored_mode = '--stored' in sys.argv or os.environ.get('NUMBERDB_KEY_FROM_STDIN') == '1'
verify_report = None
if stored_mode:
client = generate.client_from_environment()
report = generator.verify(sample=None, client=client)
assert report.ok, str(report)
verify_report = str(report)
doc = numberdb.table('T622', client=client)
# The entries endpoint stores flat records; no Django database is needed.
assert isinstance(doc['Numbers'], list)
values = {tuple(int(row['params'][k]) for k in generator.parameters):
QQ(row['number']) for row in doc['Numbers']}
assert list(values) == expected_keys
else:
values = {key:generate.eta_exact(*key) for key in expected_keys}
assert len(values) == 637
assert len(set(key[:2] for key in values)) == 87
assert not exactness(values)
assert not names_its_rings(str(Path(__file__).with_name('generate.py')))
disagreements = agrees_with(values, independent_exact)
assert not disagreements, disagreements[:10]
ball_records = []
max_radius = R(0)
min_relative_bits = BITS
symmetry_count = 0
all_domain_count = 0
q1_count = 0
for key, value in values.items():
p,q,u = key
ball = cotangent_eta(p,q,u)
assert ball.is_finite() and R(ball.rad()) < R(10)**(-130), (key, ball)
assert ball.contains_exact(value), (key, ball, value)
if value:
assert ball.accuracy() >= 430, (key, ball.accuracy())
min_relative_bits = min(min_relative_bits, int(ball.accuracy()))
max_radius = max(max_radius, R(ball.rad()))
ball_records.append({'p':p,'q':q,'u':u,'exact':str(value),
'cotangent_ball':str(ball), 'radius':str(ball.rad())})
if q == 1:
formula = -QQ((p-1)*(p-2))/QQ(3*p)
if u: formula += QQ(2*u*(p-u))/QQ(p)-1
assert formula == value
q1_count += 1
# Check all omitted labels as well, using stored values for reconstruction.
for p in range(2, generate.MAX_P+1):
for q in range(1, p):
if gcd(p,q) != 1: continue
for u in range(p):
key = (p,q,u)
value = values[canonical_key(*key)]
assert generate.eta_exact(*key) == value
assert independent_exact(key) == value
assert values[canonical_key(p,q,(-u)%p)] == value
assert values[canonical_key(p,p-q,u)] == -value
r=pow(q,-1,p)
assert values[canonical_key(p,r,(u*r)%p)] == value
symmetry_count += 3
all_domain_count += 1
assert sum(values[canonical_key(p,q,u)] for u in range(p)) == 0
evidence = {'table':'T622','mode':'stored' if stored_mode else 'local',
'archived_skill_manifest':json.loads(Path(__file__).with_name('skill-manifest.json').read_text()),
'sage_version':SAGE_VERSION,'python_version':sys.version,
'working_bits':BITS,'controls':controls,'measure':measure(values),
'selected_entries':len(values),'oriented_presentations':87,
'independent_exact_comparisons':len(values),
'ball_comparisons':len(values),'max_ball_radius':str(max_radius),
'minimum_nonzero_relative_accuracy_bits':min_relative_bits,
'full_domain_reconstruction_checks':all_domain_count,
'symmetry_comparisons':symmetry_count,'homogeneous_formula_checks':q1_count,
'character_sum_zero_checked':True,'verify_report':verify_report,
'generator_files':{name:hashlib.sha256(Path(__file__).with_name(name).read_bytes()).hexdigest()
for name in ['generate.py','checks.py','check.py','argument-selection.md']},
'q_representatives':{str(p):sorted({q for pp,q,u in values if pp==p}) for p in range(2,21)},
'records':ball_records}
print('EVIDENCE_JSON')
print(json.dumps(evidence,indent=2))
if __name__ == '__main__':
main()