back to table · edit · history · where entries came from · files · download
6870 bytes, as of the version from 2026-11-09 10:44 (current). Recorded here, not run.
"""Fricke traces of singular moduli -- numberdb.org/T687.
Run with SageMath:
sage -pip install numberdb # once
sage -python generate.py # verify the stored draft/table
sage -python generate.py --publish # fill entries with your API key
All entries are rational Fourier coefficients of weight-2 weak Jacobi forms,
converted from fixed-residue Gamma_0(N) traces to full Atkin-Lehner traces.
Matsusaka (2017), Theorems 3.2, 3.4 and Lemma 4.2, identify these coefficients
with the CM orbit sums. No rational is reconstructed by rounding.
The ring theorem gives the basis E4^u E6^v A^a B^(N-a), 2u+3v=a+1,
where A=phi_{-2,1}, B=phi_{0,1}. Exact full-rank elimination of all singular
coefficients proves uniqueness within this complete weight/index basis.
See argument-selection.md and mathematical-method.md for selection and proof.
"""
import os
import sys
from functools import lru_cache
from math import gcd, isqrt
from pathlib import Path
import numberdb.sage as numberdb
from sage.rings.integer_ring import ZZ
from sage.rings.rational_field import QQ
from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing
LEVELS = (2, 3, 5, 6, 7, 10, 13)
ORDERS = (1, 2)
MAX_D = 200
L = LaurentPolynomialRing(QQ, 'z')
z = L.gen()
def add(f, g):
return [a+b for a,b in zip(f,g)]
def scale(f, c):
return [c*a for a in f]
def mul(f, g):
length = len(f)
out = [L.zero() for _ in f]
for i,a in enumerate(f):
if a:
for j in range(length-i):
if g[j]:
out[i+j] += a*g[j]
return out
def one(length):
return [L.one()] + [L.zero()]*(length-1)
def powers(f, bound):
out = [one(len(f))]
for _ in range(bound):
out.append(mul(out[-1], f))
return out
def sigma(n, k=1):
return sum(ZZ(d)**k for d in range(1, n+1) if n % d == 0)
def prime_divisors(n):
return [p for p in range(2,n+1) if n%p==0 and all(p%j for j in range(2,isqrt(p)+1))]
def ramification_factor(N,d):
return ZZ(2)**sum(d%p==0 for p in prime_divisors(N))
@lru_cache(None)
def jacobi_generators(length):
A = one(length)
for k in range(1,length):
factor = [L.zero() for _ in A]
# (1-z*q^k)^2 (1-z^-1*q^k)^2
numerator = [L(1),-2*(z+z**-1),z*z+z**-2+4,-2*(z+z**-1),L(1)]
# (1-q^k)^(-4), exact binomial coefficients.
for j in range((length-1)//k+1):
factor[j*k] = sum(numerator[h]*ZZ((j-h+1)*(j-h+2)*(j-h+3)//6)
for h in range(min(4,j)+1))
A=mul(A,factor)
A=scale(A,z-2+z**-1)
E2=[L(1)]+[L(-24*sigma(n)) for n in range(1,length)]
E4=[L(1)]+[L(240*sigma(n,3)) for n in range(1,length)]
E6=[L(1)]+[L(-504*sigma(n,5)) for n in range(1,length)]
B=add([L({r: (6*r*r-24*n)*c for r,c in a.dict().items()})
for n,a in enumerate(A)],scale(mul(E2,A),-5))
assert A[0]==z-2+z**-1 and B[0]==z+10+z**-1
assert A[1]==-2*z**2+8*z-12+8*z**-1-2*z**-2
assert B[1]==10*z**2-64*z+108-64*z**-1+10*z**-2
return A,B,E4,E6
def solve(rows, rhs, columns):
"""Exact rectangular elimination; require full column rank and consistency."""
a=[[QQ(x) for x in row]+[QQ(y)] for row,y in zip(rows,rhs)]
pivot=0
for col in range(columns):
k=next((i for i in range(pivot,len(a)) if a[i][col]),None)
if k is None:
raise ArithmeticError('Jacobi singular coefficients do not determine the form')
a[pivot],a[k]=a[k],a[pivot]
div=a[pivot][col]
a[pivot]=[x/div for x in a[pivot]]
for i in range(len(a)):
if i != pivot and a[i][col]:
c=a[i][col]
a[i]=[x-c*y for x,y in zip(a[i],a[pivot])]
pivot+=1
assert all(not any(row[:-1]) and not row[-1] for row in a[pivot:])
return [a[i][-1] for i in range(columns)]
def singular(N,m,d):
if d==0:
return 2*sum(e*sigma(m//e) for e in range(1,m+1) if N%e==0 and m%e==0)
k=isqrt(-d)
if k*k==-d and m%k==0:
return -k*ramification_factor(N,k)
return 0
@lru_cache(None)
def basis(N,bound=MAX_D):
length=(bound+N*N)//(4*N)+2
A,B,E4,E6=jacobi_generators(length)
ap,bp=powers(A,N),powers(B,N)
ep,fp=powers(E4,(N+1)//2),powers(E6,(N+1)//3)
monomials=[]; labels=[]
for a in range(N+1):
ab=mul(ap[a],bp[N-a])
for u in range((a+1)//2+1):
for v in range((a+1)//3+1):
if 2*u+3*v==a+1:
labels.append((a,u,v))
monomials.append(mul(ab,mul(ep[u],fp[v])))
polar=[(n,r) for r in range(N+1) for n in range(r*r//(4*N)+1)]
rows=[[f[n][r] for f in monomials] for n,r in polar]
return monomials,labels,polar,rows
@lru_cache(None)
def trace_coefficients(N,m,bound=MAX_D):
monomials,labels,polar,rows=basis(N,bound)
solution=solve(rows,[singular(N,m,4*N*n-r*r) for n,r in polar],len(labels))
f=[L.zero() for _ in monomials[0]]
for c,g in zip(solution,monomials):
f=add(f,scale(g,c))
out={}
# Check all computed elliptic translates, not just the extraction residue.
for n,a in enumerate(f):
for r in range(-N,N+1):
d=4*N*n-r*r
v=QQ(a[r])
if d<=0:
assert v==singular(N,m,d),(N,m,d,v)
if 0<d<=bound:
if d in out:
assert out[d]==v,(N,m,d,'residue-dependent coefficient')
out[d]=v
wanted={d for d in range(1,bound+1) if any((d+r*r)%(4*N)==0 for r in range(N+1))}
assert set(out)==wanted
return {d:v/ramification_factor(N,d) for d,v in out.items()}
class FrickeTraces(numberdb.Generator):
table='T687'
parameters=('N','m','d')
type='Q'
rigour='exact'
digits=100
files=('generate.py','argument-selection.md','mathematical-method.md','checks.py','check.py',
'skill-manifest.json','numerical-evidence.json','orbit-evidence.json',
'T652.json')
def enumerate(self):
for N in LEVELS:
admissible={(-r*r)%(4*N) for r in range(2*N)}
for m in ORDERS:
for d in range(1,MAX_D+1):
if d%(4*N) in admissible:
yield {'N':str(N),'m':str(m),'d':str(d)}
def value(self,params,digits):
N,m,d=(int(params[k]) for k in self.parameters)
return trace_coefficients(N,m)[d]
if __name__=='__main__':
if os.environ.get('NUMBERDB_KEY_FROM_STDIN')=='1':
numberdb.configure(api_key=sys.stdin.read().strip())
g=FrickeTraces()
if '--publish' in sys.argv or os.environ.get('NUMBERDB_PUBLISH')=='1':
print(g.publish(message='Fill exact Fricke traces after Jacobi and CM-orbit checks.'))
else:
report=g.verify(sample=None)
print(report)
sys.exit(0 if report.ok else 1)