back to table · edit · history · where entries came from · files · download
3621 bytes, as of the version from 2026-10-30 10:01 (current). Recorded here, not run.
"""Atiyah–Patodi–Singer eta invariants of lens spaces -- numberdb.org/T622.
Run with SageMath (or passagemath with numberdb installed):
sage -pip install numberdb
sage -python generate.py # verify every stored entry
sage -python checks.py # independent local checks
The exact values are -4 times Toffoli's shifted Dedekind sum s_{0,u/p}(q,p),
Theorem 3.3.27 of his 2019 thesis. The sawtooth is zero at integers.
The operator acts on even-degree complex forms, with the sign fixed by the
negative untwisted cotangent sum. No kernel term or reduction modulo Z is used.
Default selection: 2 <= p <= 20; q is the smaller of q and its inverse
modulo p; 0 <= u <= floor(p/2). See argument-selection.md for the rationale.
Changing q to its inverse also changes u to u*q^(-1) modulo p. Negating q
reverses orientation, so it is not used to remove presentations.
Authorized contributors may send entries to this existing table with
NUMBERDB_API_KEY=... sage -python generate.py --fill
This writes entries and source attachments; it does not offer, review or
publish the website draft. Authentication can instead arrive on stdin with
NUMBERDB_KEY_FROM_STDIN=1. NUMBERDB_PUBLISH=1 is the campaign wrapper's
entries-only switch. The generator never creates a table.
"""
import numberdb.sage as numberdb
from sage.rings.rational_field import QQ
from math import gcd
import os
from pathlib import Path
import sys
MAX_P = 20
def sawtooth_residue(n, p):
"""The exact sawtooth ((n/p)), including zero at integral arguments."""
residue = n % p
return QQ(0) if residue == 0 else QQ(residue) / QQ(p) - QQ(1) / QQ(2)
def eta_exact(p, q, u):
"""Unreduced eta for the unit round quotient and the complex line chi_u."""
p, q, u = int(p), int(q), int(u)
if p < 2 or not 1 <= q < p or gcd(p, q) != 1 or not 0 <= u < p:
raise ValueError('Require p >= 2, 1 <= q < p, gcd(p,q)=1, 0 <= u < p')
return -4 * sum((sawtooth_residue(j, p) * sawtooth_residue(q*j+u, p)
for j in range(p)), QQ(0))
class LensEta(numberdb.Generator):
table = 'T622'
parameters = ('p', 'q', 'u')
type = 'Q'
rigour = 'exact'
digits = 100
files = ('generate.py', 'checks.py', 'argument-selection.md',
'numerical-evidence.json', 'skill-manifest.json', 'sources.md',
'check.py')
def enumerate(self, max_p=MAX_P):
for p in range(2, int(max_p)+1):
for q in range(1, p):
if gcd(p, q) == 1 and q <= pow(q, -1, p):
for u in range(p//2+1):
yield {'p': str(p), 'q': str(q), 'u': str(u)}
def value(self, params, digits):
return eta_exact(*(params[key] for key in self.parameters))
def client_from_environment():
if os.environ.get('NUMBERDB_KEY_FROM_STDIN') == '1':
key = sys.stdin.read().strip()
if '=' in key and key.split('=', 1)[0].isidentifier():
key = key.split('=', 1)[1].strip().strip('\"\'')
return numberdb.Client(api_key=key)
return numberdb.Client()
if __name__ == '__main__':
client = client_from_environment()
generator = LensEta()
if '--fill' in sys.argv or os.environ.get('NUMBERDB_PUBLISH') == '1':
print(generator.publish(client=client, assisted_by=os.environ.get(
'NUMBERDB_ASSISTED_BY', ''), message='Fill exact lens-space eta invariants after independent arithmetic and ball checks'))
else:
report = generator.verify(sample=None, client=client)
print(report)
sys.exit(0 if report.ok else 1)