back to table · edit · history · where entries came from · files · download
5259 bytes, as of the version from 2026-10-30 22:26 (current). Recorded here, not run.
"""Scalar Laplacian determinants on lens spaces -- numberdb.org/T632.
Run with SageMath:
sage -pip install numberdb
sage -python generate.py # verify every stored entry
sage -python generate.py --fill # fill the draft with NUMBERDB_API_KEY
The scalar eigenvalues are m^2-1, m>=2, for the radius-one quotient.
Exact SU(2) weight counts give quadratic multiplicity polynomials on residue
classes modulo 2p. The shifted factors m-1 and m+1 have no multiplicative
anomaly here, because the multiplicity Dirichlet series has no positive
even pole. See continuation.md for the proof. Finite Hurwitz zeta sums and
first derivatives are evaluated with Arb enclosures, then exponentiated.
The 180 extra working bits are measured by checks.py across the full range.
"""
import os
import sys
from collections import Counter
from functools import lru_cache
from math import gcd
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.real_arb import RealBallField
from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
WORKING_GUARD = 180
def presentations(limit=20):
for p in range(2, limit + 1):
for q in range(1, p):
if gcd(p, q) == 1:
r = pow(q, -1, p)
if q == min(q, p-q, r, p-r):
yield p, q
def cases(limit=20):
for p, q in presentations(limit):
for u in range(p//2 + 1):
yield p, q, u
@lru_cache(None)
def coefficients(p, q, u):
"""Exact (m0,c0,c1,c2) with d(m0+2p*k)=c0+c1*k+c2*k^2."""
assert p >= 2 and 0 < q < p and gcd(p, q) == 1 and 0 <= u < p
period = 2*p
rows = []
for m0 in range(2, period+2):
weights = Counter(a % period for a in range(1-m0, m0, 2))
c0 = c1 = c2 = 0
residues = range((m0-1) % 2, period, 2)
for a in residues:
for b in residues:
exponent = a*(1+q) + b*(1-q)
assert exponent % 2 == 0
if (exponent//2-u) % p == 0:
c0 += weights[a]*weights[b]
c1 += 2*(weights[a]+weights[b])
c2 += 4
rows.append((m0, ZZ(c0), ZZ(c1), ZZ(c2)))
# Residue of F(z)=sum d(m)m^(-z) at z=2 vanishes exactly.
assert sum(c1*period-2*c2*m0 for m0,c0,c1,c2 in rows) == 0
return tuple(rows)
def multiplicity(p, q, u, m):
if m == 1:
return ZZ(u == 0)
assert m >= 2
period = 2*p
m0 = 2+(m-2) % period
_, c0, c1, c2 = coefficients(p,q,u)[m0-2]
k = (m-m0)//period
return c0+c1*k+c2*k*k
@lru_cache(None)
def hurwitz_data(p, precision):
C = ComplexBallField(precision)
P = PolynomialRing(C, 's')
s = P.gen()
period = 2*p
out = {}
for numerator in range(1, period+3):
a = QQ(numerator)/period
for j in range(3):
z = (s-j)._zeta_series(2, a=C(a))
assert z[0].imag().contains_zero() and z[1].imag().contains_zero()
assert all(v.real().is_finite() and v.imag().is_finite() for v in (z[0],z[1]))
out[numerator,j] = z[0].real(), z[1].real()
return out
@lru_cache(None)
def logdet(p, q, u, digits=100):
precision = numberdb.bits(digits, losing=WORKING_GUARD)
R = RealBallField(precision)
period = 2*p
data = hurwitz_data(p, precision)
lp = R(period).log()
answer = R(0)
for m0, c0, c1, c2 in coefficients(p,q,u):
for shift in (-1, 1):
a = QQ(m0+shift)/period
alpha = (c0-c1*a+c2*a*a, c1-2*c2*a, c2)
for j in range(3):
z, dz = data[m0+shift,j]
answer += alpha[j]*(lp*z-dz)
assert answer.is_finite()
return answer
class ScalarLensDeterminants(numberdb.Generator):
table = 'T632'
parameters = ('p','q','u','convention')
type = 'R'
digits = 100
rigour = 'proven'
files = ('generate.py','checks.py','check.py','argument-selection.md',
'continuation.md','numerical-evidence.json','skill-evidence.json',
'source-evidence.json')
def enumerate(self, limit=20):
for p,q,u in cases(limit):
for convention in ('det','logdet'):
yield dict(p=str(p),q=str(q),u=str(u),convention=convention)
def value(self, params, digits):
p,q,u = (int(params[k]) for k in ('p','q','u'))
L = logdet(p,q,u,digits)
if params['convention'] == 'det':
return L.exp()
assert params['convention'] == 'logdet'
return L
if __name__ == '__main__':
if os.environ.get('NUMBERDB_KEY_FROM_STDIN') == '1':
key = sys.stdin.read().strip()
if '=' in key and key.split('=',1)[0].replace('_','').isalnum():
key = key.split('=',1)[1].strip().strip('\"\'')
numberdb.configure(api_key=key)
generator = ScalarLensDeterminants()
if '--fill' in sys.argv or os.environ.get('NUMBERDB_PUBLISH') == '1':
print(generator.publish(message='Fill scalar lens-space draft with certified 100-digit values'))
else:
report = generator.verify(sample=None)
print(report)
sys.exit(0 if report.ok else 1)