checks.py

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))