back to table · edit · history · where entries came from · files · download
10062 bytes, as of the version from 2026-11-02 11:34 (current). Recorded here, not run.
"""Independent checks for numberdb.org/T674.
sage -python checks.py # compute and check offline
sage -python checks.py --stored # check every stored entry
Requires generate.py and the attached campaign helper check.py. All writes are
stdout only. No publication, review or table-edit request is made.
The principal independent check implements Beukers–Cohen–Mellit (BCM),
Finite hypergeometric functions, Theorem 1.3 (arXiv:1505.02900v2): explicit
finite-field Gauss sums in 384-bit complex balls. It shares no HGM or p-adic
code with the generator. Integrality of the trace plus a unique integer in the
ball determines the exact answer. PARI is another implementation check, with
t=1/z. A quadratic-character convolution and literal elliptic point counts
supply controls outside the Gross–Koblitz implementation.
"""
import json
import os
import sys
from functools import lru_cache
from math import gcd
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.complex_arb import ComplexBallField
from sage.rings.finite_rings.finite_field_constructor import GF
from sage.libs.pari import pari
from sage.env import SAGE_VERSION
from generate import (CalabiYauFrobenius, TEMPLATES, FIBRES, R, T, motive,
traces, trace_precision)
from check import exactness, measure, agrees_with, names_its_rings
CB = ComplexBallField(384)
# Derive gamma from cyclotomic indices, independently of Sage's HGM object.
def mobius(n):
result, p = 1, 2
while p*p <= n:
if n % p == 0:
n //= p
result = -result
if n % p == 0:
return 0
p += 1
return -result if n > 1 else result
@lru_cache(None)
def gamma_vectors(template):
gamma = {1:-4}
for d in map(int,template.split('.')):
for e in range(1,d+1):
if d % e == 0:
gamma[e] = gamma.get(e,0) + mobius(d//e)
plus = [n for n,c in gamma.items() for _ in range(max(c,0))]
minus = [n for n,c in gamma.items() for _ in range(max(-c,0))]
M = QQ(1)
for n in plus: M *= ZZ(n)**n
for n in minus: M /= ZZ(n)**n
assert sum(plus) == sum(minus)
return plus, minus, M
@lru_cache(None)
def gauss_data(p,f):
q = p**f
field = GF(q,'a')
g = field.multiplicative_generator()
xs = [field.one()]
for _ in range(q-2): xs.append(xs[-1]*g)
assert len(set(xs)) == q-1 and xs[-1]*g == 1
logs = {x:k for k,x in enumerate(xs)}
omega = [CB(QQ(2*k)/(q-1)).exppii()
for k in range(q-1)]
psi = [CB(QQ(2*int(x.trace()))/p).exppii() for x in xs]
gauss = [sum((omega[(m*k)%(q-1)]*psi[k] for k in range(q-1)),CB(0))
for m in range(q-1)]
# Nonzero controls: trivial Gauss sum=-1; every other Gauss norm=q.
assert (-1 in gauss[0].real()) and (0 in gauss[0].imag())
for m,h in enumerate(gauss):
assert (h.real().is_finite() and h.imag().is_finite()) and h.real().rad() < QQ(1)/10**90
norm = h*h.conjugate()
assert ((1 if m == 0 else q) in norm.real())
assert (0 in norm.imag()) and norm.real().rad() < QQ(1)/10**85
return field, logs, omega, gauss
@lru_cache(None)
def bcm_trace(template,z_string,p,f):
plus, minus, M = gamma_vectors(template)
q, n = p**f, p**f-1
F, logs, omega, G = gauss_data(p,f)
arg = F(((-1)**sum(minus))*QQ(z_string)/M)
l = logs[arg]
s0 = min(len(plus),len(minus))
total = CB(0)
for m in range(n):
sm = min(sum((k*m)%n == 0 for k in plus),
sum((k*m)%n == 0 for k in minus))
term = CB(QQ(q)**(sm-s0)) * omega[(l*m)%n]
for k in plus: term *= G[(k*m)%n]
for k in minus: term *= G[(-k*m)%n]
total += term
total *= QQ((-1)**(len(plus)+len(minus)))/QQ(1-q)
assert (total.real().is_finite() and total.imag().is_finite()) and (0 in total.imag())
assert total.real().rad() < QQ(1)/10**75
assert total.imag().rad() < QQ(1)/10**75
value = ZZ(total.real())
assert value in total.real()
assert value*value <= 16*ZZ(p)**(3*f)
return ZZ(value)
def bcm_polynomial(key):
template,z,p = key
a = bcm_trace(template,z,p,1)
c = bcm_trace(template,z,p,2)
assert (a*a-c)%2 == 0
return R([1,-a,(a*a-c)//2,-ZZ(p)**3*a,ZZ(p)**6])
@lru_cache(None)
def pari_template(key):
return pari('hgminit([%s])'%key.replace('.',','))
def pari_polynomial(key):
template,z,p = key
poly = pari.hgmeulerfactor(pari_template(template),pari(QQ(1)/QQ(z)),p)
return R([ZZ(poly.polcoef(i)) for i in range(5)])
def quadratic_controls():
results = []
for p in [3,5,7]:
for f in [1,2]:
F = GF(p**f,'b')
chi = lambda x: 0 if not x else (1 if x.is_square() else -1)
assert sum(chi(x*x-F(1)) for x in F) == -1
# Finite Euler convolution, normalized by J(phi,phi)^3.
# J(phi,phi)=-phi(-1), from the elementary quadratic Jacobi sum.
jacobi = sum(chi(x*(1-x)) for x in F)
assert jacobi == -chi(F(-1))
for z_string in FIBRES:
z = F(QQ(z_string))
nonzero = [x for x in F if x and x != 1]
pairs = {}
for x in nonzero:
for y in nonzero:
k=x*y
pairs[k]=pairs.get(k,0)+chi(x*(1-x)*y*(1-y))
S=sum(v*chi(w*(1-w)*(1-z*k*w)) for k,v in pairs.items()
for w in nonzero)
a=ZZ(jacobi**3*S)
assert a == bcm_trace('2.2.2.2',z_string,p,f)
# Outside rank four: BCM Theorem 1.2, literal point count.
count=1+sum(1+chi(x*(x-1)*(x-z)) for x in F)
h2=pari.hgmeulerfactor(pari('hgminit([2,2])'),
pari(QQ(1)/QQ(z_string)),p)
ap=-ZZ(h2.polcoef(1))
predicted=ap if f==1 else ap*ap-2*p
assert chi(F(-1))*(p**f+1-count) == predicted
if p==5 and f==1 and z_string=='2': assert count==8
results.append({'p':p,'f':f,'z':z_string,'trace4':int(a),
'legendre_points':int(count)})
return results
def load_values(stored=False):
gen=CalabiYauFrobenius()
parameters=list(gen.enumerate())
if stored:
from numberdb._generate import _stored_entries
doc=numberdb.table('T674')
entries=_stored_entries(doc)
expected={','.join(str(p[k]) for k in gen.parameters) for p in parameters}
assert set(entries)==expected, (len(entries),len(expected))
return {(p['template'],p['z'],int(p['p'])):
R(entries[','.join(str(p[k]) for k in gen.parameters)])
for p in parameters}
return {(p['template'],p['z'],int(p['p'])):gen.value(p,100) for p in parameters}
def main(stored=False):
print('VERSIONS',json.dumps({'sage':SAGE_VERSION,'pari':str(pari('version()')),
'numberdb':numberdb.__version__ if hasattr(numberdb,'__version__') else 'client-source'}),flush=True)
print('MODE','stored' if stored else 'offline',flush=True)
# Published, nonzero full-polynomial controls precede all comparisons.
assert bcm_polynomial(('5','-1',3)) == R([1,5,45,135,729])
assert bcm_polynomial(('2.2.2.2','-1',5)) == R([1,4,-130,500,15625])
assert pari_polynomial(('5','-1',3)) == R([1,5,45,135,729])
assert pari_polynomial(('5','1/2',7)) == R([1,-35,805,-12005,117649])
assert pari_polynomial(('5','2',7)) == R([1,10,420,3430,117649])
print('CONTROLS known polynomials and asymmetric coordinate passed',flush=True)
values=load_values(stored)
assert len(values)==390
assert not exactness(values)
assert not names_its_rings(str(Path(__file__).with_name('generate.py')))
print('MEASURE',json.dumps(measure(values),default=str),flush=True)
assert not agrees_with(values,pari_polynomial)
print('PARI all 390 complete polynomials agree at reciprocal coordinate',flush=True)
for template in TEMPLATES:
subset={k:v for k,v in values.items() if k[0]==template}
failures=agrees_with(subset,bcm_polynomial)
assert not failures, failures[:3]
print('BCM',template,len(subset),'polynomials pass',flush=True)
for key,P in values.items():
template,z,p=key
a=-P[1]
assert P.degree()==4 and P[0]==1 and P[4]==p**6
assert P[3]==p**3*P[1] and P[2]%p==0
assert 2*P[2]==a*a-bcm_trace(template,z,p,2)
assert a*a<=16*p**3 and abs(P[2])<=6*p**3
# Exact weight-three Weil test: X^-2 Q(X)=U²-aU+(b-2p³),
# U=X+p³/X. Both real U roots must lie in [-2sqrt(p³),2sqrt(p³)].
disc=a*a-4*(P[2]-2*p**3)
assert disc>=0
gap=16*p**3-a*a-disc
assert gap>=0 and gap*gap>=4*a*a*disc
assert P==motive(template).euler_factor(QQ(z),ZZ(p))
for f in (1,2):
n=trace_precision(ZZ(p),f)
assert bcm_trace(template,z,p,f)==motive(template).padic_H_value(
ZZ(p),f,QQ(z),prec=n+3,cache_p=False)
print('IDENTITIES shape, Newton reconstruction, exact Weil test, Hodge bound, Sage standard factors and extra-precision traces passed',flush=True)
controls=quadratic_controls()
print('QUADRATIC_CONTROLS',json.dumps(controls),flush=True)
print('VALUES_JSON',json.dumps([{'params':{'template':k[0],'z':k[1],'p':str(k[2])},
'number':str(v),'coefficients':list(map(int,v.list()))}
for k,v in values.items()]),flush=True)
print('PASS',len(values),'polynomials; 780 explicit Gauss-sum trace enclosures; 18 quadratic convolution and elliptic point-count controls',flush=True)
if __name__ == '__main__':
if os.environ.get('NUMBERDB_KEY_FROM_STDIN')=='1':
os.environ['NUMBERDB_API_KEY']=sys.stdin.read().strip()
main('--stored' in sys.argv)