prepare.py

back to table · edit · history · where entries came from · files · download

2762 bytes, as of the version from 2026-11-02 18:57 (current). Recorded here, not run.

"""Identify every selected orbit exactly through its Sturm bound, for T683."""
import numberdb.sage as numberdb
from sage.libs.pari import pari
from sage.rings.rational_field import QQ
from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
import json
from pathlib import Path
w=Path(__file__).parent
catalogue=json.loads((w/'catalogue.json').read_text())
hecke={r['label']:r for r in json.loads((w/'hecke-nf.json').read_text())['data']}
P=PolynomialRing(QQ,'x');cache={}
def poly(a):
 return P(str(pari.lift(a)).replace('y','x'))
def coeffs(r):
 h=hecke[r['label']];d=r['dim'];F=P(r['field_poly'])
 if h['hecke_ring_power_basis']:
  basis=[P.gen()**j for j in range(d)]
 else:
  basis=[P(v)/QQ(b) for v,b in zip(h['hecke_ring_numerators'],h['hecke_ring_denominators'])]
 result=[P(0)]
 for vec in h['an']:
  if isinstance(vec,int): vec=[vec]
  result.append(sum((QQ(v)*b for v,b in zip(vec,basis)),P(0))%F)
 return F,result
for entry in catalogue:
 r=entry['catalogue'];label=r['label'];N=r['level'];k=r['weight'];char=-N if entry['group']=='C' else 1
 spec=(N,k,char)
 if spec not in cache:
  mf=pari.mfinit(list(spec),0);fs=list(pari.mfeigenbasis(mf));B=int(pari.mfsturm([N,k]));cache[spec]=(mf,fs,B)
 mf,fs,B=cache[spec];F,cs=coeffs(r);assert len(cs)>B
 target_traces=[pari.Mod(str(c),str(F)).trace() for c in cs[:B+1]]
 candidates=[]
 for pos,f in enumerate(fs):
  fp=pari.mfparams(f)[3];d=int(fp.poldegree())
  if d!=r['dim']: continue
  fc=list(pari.mfcoefs(f,B))
  traces=[a.trace() if a.type()=='t_POLMOD' else d*a for a in fc]
  if traces!=target_traces: continue
  maps=[P(0)] if d==1 else [P(str(x)) for x in pari.nfisincl(fp,str(F))]
  for h in maps:
   if all(poly(a)(h)%F==c for a,c in zip(fc,cs)):
    candidates.append((pos,f,fp,h))
 assert len(candidates)==1,(label,len(candidates))
 pos,f,fp,h=candidates[0]
 assert all(poly(a)(h)%F==c for a,c in zip(list(pari.mfcoefs(f,len(cs)-1)),cs)),label
 fricke=None
 if char==1:
  A=pari.mfatkininit(mf,N,precision=128); assert A[2]==1
  af=pari.mfatkin(A,f);ac=list(pari.mfcoefs(af,B));fc=list(pari.mfcoefs(f,B));sign=ac[1]
  assert sign in [-1,1] and all(a==sign*b for a,b in zip(ac,fc))
  fricke=int(pari.lift(sign))
  assert fricke==r['fricke_eigenval']
 out={'label':label,'group':entry['group'],'level':N,'weight':k,'character_discriminant':char,'conrey_index':r['conrey_index'],'degree':r['dim'],'field_poly':r['field_poly'],'sturm_bound':B,'an_power_basis':[[str(c) for c in a.list()] for a in cs[:B+1]],'pari_orbit_index':pos,'pari_field_poly':str(fp),'pari_generator_in_catalogue_field':[str(c) for c in h.list()],'fricke_eigenvalue':fricke,'exact_coefficients_checked':len(cs)-1}
 print('ROSTER '+json.dumps(out),flush=True)
print('PREPARED',len(catalogue),flush=True)