back to table · edit · history · where entries came from · files · download
4114 bytes, as of the version from 2026-10-16 10:44 (current). Recorded here, not run.
"""Lambda_g Hodge integrals -- numberdb.org/T462.
Run with SageMath (or a suitable passagemath installation):
sage -pip install numberdb
sage -python generate.py # verify every stored entry
sage -python checks.py # independent checks, no API key
sage -python generate.py --fill # write entries to the existing draft
The last command requires NUMBERDB_API_KEY for the draft's owner. It writes
numbers and attachments; it does not publish or offer the draft for review.
The table uses the Deligne-Mumford stack with labelled markings. Profiles are
sorted representatives, without division by a permutation multiplicity.
Their total degree is 2*g-3+n. The first five positive genera and at most five
markings form a complete low-genus reference rectangle. Genus zero, where
lambda_0=1, is described in prose and already covered by the psi-class table.
All arithmetic is exact in QQ. The coefficient of t^(2*g) in
(t/2)/sin(t/2) is obtained by triangular coefficient inversion, then multiplied
by a multinomial coefficient. No approximate numerical arithmetic is used.
"""
import numberdb.sage as numberdb
from sage.rings.rational_field import QQ
from functools import lru_cache
from math import comb, factorial
import os
import sys
MAX_GENUS = 5
MAX_MARKS = 5
def profiles(total, length, minimum=0):
"""All non-decreasing nonnegative profiles, including zero exponents."""
if length == 0:
if total == 0:
yield ()
return
for first in range(minimum, total // length + 1):
for rest in profiles(total-first, length-1, first):
yield (first,) + rest
@lru_cache(None)
def one_point(g):
"""Coefficient of t^(2*g) in (t/2)/sin(t/2), including b_0=1."""
if g == 0:
return QQ(1)
return -sum((QQ((-1)**k) / QQ(2**(2*k)*factorial(2*k+1)))
* one_point(g-k) for k in range(1, g+1))
def integral(g, ds):
"""A stable top-degree lambda_g integral; ds need not be sorted."""
g = int(g)
ds = tuple(int(d) for d in ds)
n = len(ds)
if g < 0 or any(d < 0 for d in ds) or 2*g-2+n <= 0:
raise ValueError('A stable pair and nonnegative exponents are required')
if sum(ds) != 2*g-3+n:
raise ValueError('The exponent profile must have degree 2*g-3+n')
coefficient, remaining = 1, sum(ds)
for d in ds:
coefficient *= comb(remaining, d)
remaining -= d
return one_point(g) * coefficient
class LambdaGHodge(numberdb.Generator):
table = 'T462'
parameters = ('g', 'n', 'd')
type = 'Q'
rigour = 'exact'
files = ('generate.py', 'checks.py', 'check.py', 'evidence.json',
'bernoulli.json', 'T458.json')
def enumerate(self, max_genus=MAX_GENUS, max_marks=MAX_MARKS):
for g in range(1, max_genus+1):
for n in range(1, max_marks+1):
assert 2*g-2+n > 0
for ds in profiles(2*g-3+n, n):
assert len(ds) == n and sum(ds) == 2*g-3+n
yield {'g': g, 'n': n, 'd': ','.join(map(str, ds))}
def value(self, params, digits=100):
g, n = int(params['g']), int(params['n'])
ds = tuple(map(int, str(params['d']).split(',')))
if len(ds) != n or tuple(sorted(ds)) != ds:
raise ValueError('The profile must be sorted and have n entries')
result = integral(g, ds)
if (g, ds) == (1, (0,)):
return {'number': result,
'comment': r'The integral $\int_{\overline{\mathcal M}_{1,1}}\lambda_1=1/24$ includes the generic elliptic involution.'}
return result
if __name__ == '__main__':
if os.environ.get('NUMBERDB_KEY_FROM_STDIN') == '1':
os.environ['NUMBERDB_API_KEY'] = sys.stdin.read().strip()
generator = LambdaGHodge()
if '--fill' in sys.argv or os.environ.get('NUMBERDB_PUBLISH') == '1':
print(generator.publish(message='Fill exact lambda_g integrals after independent checks'))
else:
report = generator.verify(sample=None)
print(report)
sys.exit(0 if report.ok else 1)