back to table · edit · history · where entries came from · files · download
10041 bytes, as of the version from 2026-11-02 09:34 (current). Recorded here, not run.
"""Independent checks for Thompson moonshine coefficients -- numberdb.org/T673.
Download all attachments beside this file; run `sage -python checks.py`.
Use `sage -python checks.py --stored` to repeat on the live draft (authorized
key required), or pass --document table.json for a saved API document.
The checker uses finite Euler products, unlike the generator's logarithmic
recurrence. It checks published coefficient controls, the independent
Rankin--Cohen construction, theta-split rational identities, congruences,
and companion tables. See source-notes.md for source locators and errata.
"""
import numberdb.sage as numberdb
from sage.rings.integer_ring import ZZ
from fractions import Fraction as Q
from math import isqrt
from pathlib import Path
import json
import os
import sys
import hashlib
from generate import ThompsonCoefficients, coefficients, CLASSES, MAX_GRADE
ROOT = Path(__file__).resolve().parent
CAP = MAX_GRADE + 30
def add(*xs):
out = {}
for x in xs:
for k,v in x.items():
if k <= CAP:
out[k] = out.get(k,Q(0)) + v
return {k:v for k,v in out.items() if v}
def scale(x,c):
return {k:Q(c)*v for k,v in x.items() if v}
def mul(x,y):
out = {}
for j,a in x.items():
for k,b in y.items():
if j+k <= CAP:
out[j+k] = out.get(j+k,Q(0)) + a*b
return {k:v for k,v in out.items() if v}
def inv(x):
shift = min(k for k,v in x.items() if v)
lead = Q(x[shift]); size = CAP + abs(shift)
a = [Q(x.get(k+shift,0)) for k in range(size+1)]
b = [1/lead]
for n in range(1,size+1):
b.append(-sum(a[k]*b[n-k] for k in range(1,n+1))/lead)
return {n-shift:v for n,v in enumerate(b) if v and n-shift <= CAP}
def div(x,y):
return mul(x,inv(y))
def finite_eta(exponents,bound=CAP):
shift,rem = divmod(sum(d*r for d,r in exponents),24)
assert rem == 0
limit = bound-shift
a = [1]+[0]*limit
for d,r in exponents:
for step in range(d,limit+1,d):
for _ in range(abs(r)):
if r>0:
for n in range(limit,step-1,-1):
a[n] -= a[n-step]
else:
for n in range(step,limit+1):
a[n] += a[n-step]
return {n+shift:Q(v) for n,v in enumerate(a) if v}
def poly_at(coeffs,x):
out = {}
for c in coeffs: # highest degree first
out = add(mul(out,x), {0:Q(c)})
return out
def rational_at(num,den,x):
# Evaluate in z=1/x to avoid losing coefficients to large Laurent poles.
z=inv(x)
a=poly_at(list(reversed(num)),z)
b=poly_at(list(reversed(den)),z)
assert len(num)==len(den)+1
return mul(x,div(a,b))
def equal_series(x,y,lo,hi):
for n in range(lo,hi+1):
assert x.get(n,0)==y.get(n,0), (n,x.get(n,0),y.get(n,0))
def controls():
# Known nonzero controls, then deliberate errors to test rejection.
equal_series(finite_eta(((1,24),),8),{1:1,2:-24,3:252,4:-1472,5:4830},1,5)
equal_series(mul({0:1,1:-1},inv({0:1,1:-1})),{0:1},0,15)
assert rational_at([1,2,3],[1,4],{-1:Q(1)})[-1]==1
for wrong in [{1:-1,2:-24},{2:1},{1:2}]:
try:equal_series(wrong,{1:1,2:-24},1,2)
except AssertionError:pass
else:raise AssertionError('A deliberately wrong control was accepted')
def identity_rankin_cohen(bound):
theta = {0:Q(1)}
for r in range(1,isqrt(CAP)+1):theta[r*r]=Q(2)
eisenstein={0:Q(1)}
for m in range(1,CAP//4+1):
eisenstein[4*m]=Q(-264*sum(d**9 for d in range(1,m+1) if m%d==0))
derivative=lambda s:{n:n*v for n,v in s.items() if n}
bracket=add(scale(mul(theta,derivative(eisenstein)),Q(1,2)),scale(mul(eisenstein,derivative(theta)),-10))
delta=finite_eta(((4,24),),CAP)
return add(scale(div(bracket,delta),Q(-1,10)),scale(theta,Q(936,5)))
def theta_split(values,g,through=50):
a={m:Q(values[g][4*m]) for m in range(through+1)}
b={-1:Q(2)}
b.update({m:Q(values[g][4*m+1]) for m in range(through+1)})
theta0={0:Q(1)}
for r in range(1,isqrt(CAP)+1):theta0[r*r]=Q(2)
theta1={r*(r+1):Q(2) for r in range(isqrt(CAP)+1) if r*(r+1)<=CAP}
return div(a,theta0),div(b,theta1)
def replicable_checks(values):
# GM Table A.6, every total series; individual sectors for five classes.
specs={
'1A':(((1,24),(2,-24)),[1,272,-32768],[1,0]),
'2A':(((1,24),(2,-24)),[1,16],[1]),
'3A':(((1,-1),(2,2),(9,1),(18,-2)),[1,9,-126,450,-675,405,0],[1,-4,9,-15,18,-9]),
'3B':(((1,-3),(2,3),(3,9),(6,-9)),[1,2,64],[1,0]),
'3C':(((1,-1),(2,2),(9,1),(18,-2)),[1,-9,9,171,-837,1701,-1701,729],[1,-4,9,-15,18,-9,0]),
'4A':(((1,8),(4,-8)),[1,16],[1]),
'4B':(((2,12),(4,-12)),[1,0],[1]),
'5A':(((1,3),(2,-1),(5,1),(10,-3)),[1,10,36,80],[1,9,20]),
}
old_cap=globals()['CAP'];globals()['CAP']=70
counts=0
for g,(exps,num,den) in specs.items():
even,odd=theta_split(values,g)
expected=rational_at(num,den,finite_eta(exps,70))
equal_series(add(even,odd),expected,-1,50);counts+=52
if g=='1A':
x=finite_eta(((1,8),(4,-8)),70);z=inv(x)
odd_ref=add(x,{0:Q(8)},scale(z,-86016),scale(mul(z,z),-3407872),scale(mul(mul(z,z),z),-33554432))
elif g=='2A':odd_ref=add(finite_eta(((1,-8),(2,24),(4,-16)),70),{0:Q(-8)})
elif g=='3B':
x=finite_eta(((1,-1),(3,3),(4,1),(12,-3)),70);z=inv(x)
odd_ref=add(x,{0:Q(-1)},scale(z,24),scale(mul(z,z),32),scale(mul(mul(z,z),z),-128))
elif g=='4A':odd_ref=add(finite_eta(((1,8),(4,-8)),70),{0:Q(8)})
elif g=='4B':odd_ref=finite_eta(((2,12),(4,-12)),70)
else:continue
equal_series(odd,odd_ref,-1,50);counts+=52
globals()['CAP']=old_cap
return counts
def congruences(values):
count=0
for n in range(MAX_GRADE+1):
if n%4 not in (0,1):continue
a,b,c,d,e,f,h,j=[values[g][n] for g in CLASSES]
theta4=2 if n>0 and n%4==0 and isqrt(n//4)**2==n//4 else (1 if n==0 else 0)
theta9=2 if n>0 and n%9==0 and isqrt(n//9)**2==n//9 else (1 if n==0 else 0)
tests=[(a-j,25),(a-c+9*theta9,27),(28*a-27*c-d,3**7),
(53*a-27*c-53*d+27*e+729*theta9,3**8),
(a-b-256*theta4,2**12),(a+15*b-16*f,2**13),
(11*a+5*b-144*f+128*h-1536*theta4,2**16)]
for residual,modulus in tests:
assert residual%modulus==0,(n,modulus,residual)
count+=1
assert a==0 or (a>0 if n%4==0 else a<0)
assert abs(b)<=abs(a) and all(abs(x)<=abs(a) for x in [c,d,e,f,h,j])
return count
def run(values,companions=None):
controls()
source=json.loads((ROOT/'source-controls.json').read_text())['classes']
checks={}
checks['published_supplement_coefficients']=0
for g in CLASSES:
for n,a in values[g].items():
assert a==int(source[g][str(n)]),(g,n,a,source[g][str(n)])
checks['published_supplement_coefficients']+=1
independent=identity_rankin_cohen(MAX_GRADE)
for n,a in values['1A'].items():assert a==independent.get(n,0),(n,a,independent.get(n,0))
checks['rankin_cohen_coefficients']=len(values['1A'])
checks['replicable_series_positions']=replicable_checks(values)
checks['congruences']=congruences(values)
if companions:
twisted=companions['T671']['Numbers']['1'];count=0
for d,v in twisted.items():
if int(d)<=MAX_GRADE and '3' in v:
a=v['3']['coefficient'];a=a['number'] if isinstance(a,dict) else a
assert values['1A'][int(d)]==2*int(a),(d,a)
count+=1
checks['independent_twisted_CM_traces']=count
degrees=companions['T255']['Numbers']['Th']
def deg(i):
a=degrees[str(i)];return int(a['number'] if isinstance(a,dict) else a)
for n,sign,inds in [(0,1,[2]),(4,1,[4,5]),(5,-1,[9,10]),(8,1,[17,18]),(9,-1,[22,23]),(12,1,[40,40])]:
assert values['1A'][n]==sign*sum(deg(i) for i in inds),(n,inds)
checks['irreducible_degree_decompositions']=6
return checks
def table_values(document):
# The g,n table is nested by the declared groups.
out={}
for g,block in document['Numbers'].items():
out[g]={int(n):int(v['number'] if isinstance(v,dict) else v) for n,v in block.items()}
assert tuple(out)==CLASSES
wanted={n for n in range(MAX_GRADE+1) if n%4 in (0,1)}
assert all(set(v)==wanted for v in out.values())
return out
if __name__=='__main__':
if os.environ.get('NUMBERDB_KEY_FROM_STDIN')=='1':
token=sys.stdin.read().strip()
if '=' in token and token.split('=',1)[0].isupper():token=token.split('=',1)[1].strip().strip('\'"')
numberdb.configure(api_key=token)
if '--stored' in sys.argv or os.environ.get('THOMPSON_STORED')=='1':
values=table_values(numberdb.table('T673'))
companions={tid:numberdb.table(tid) for tid in ['T671','T255']}
mode='stored'
elif '--document' in sys.argv:
values=table_values(json.loads(Path(sys.argv[sys.argv.index('--document')+1]).read_text()))
companions={tid:json.loads((ROOT/(tid+'.json')).read_text()) for tid in ['T671','T255']}
mode='saved_document'
else:
values={g:{n:int(v) for n,v in s.items() if n>=0 and n%4 in (0,1)} for g,s in coefficients().items()}
companions={tid:json.loads((ROOT/(tid+'.json')).read_text()) for tid in ['T671','T255']} if (ROOT/'T671.json').exists() else {tid:numberdb.table(tid) for tid in ['T671','T255']}
mode='computed'
result=run(values,companions)
result.update({'mode':mode,'table':'T673','entries':sum(map(len,values.values())),
'longest_integer_characters':max(len(str(a)) for s in values.values() for a in s.values()),
'skill_manifest':'agents/runs/20261102T085118Z-build-skills/manifest.json',
'canonical_skill_sha256':'a2b28d9953264d7b0ca62663785c2952190c5be6d6d3597fd695d9a54d606278'})
print(json.dumps(result,indent=2))