back to table · edit · history · where entries came from · files · download
5275 bytes, as of the version from 2026-10-16 09:08 (current). Recorded here, not run.
"""Weil--Petersson volumes of hyperbolic moduli spaces -- numberdb.org/T459.
Run with SageMath after installing the client (sage -pip install numberdb):
sage -python generate.py # verify every stored entry
sage -python checks.py # independent exact and interval checks
sage -python generate.py --fill # fill the existing draft
For a private draft, set NUMBERDB_API_KEY or pipe the key on stdin with
NUMBERDB_KEY_FROM_STDIN=1. Filling writes numbers and attachments only.
It does not publish or offer the table for review.
The rational kappa_1 integrals use Theorem 1.1 and equation (3) of Liu-Xu,
arXiv:0708.0565v2. All arithmetic before pi uses QQ. The volume is
(2*pi^2)^m/m! times the integral, m=3*g-3+n. The orbifold normalisation
includes generic automorphisms, and all n punctures are labelled.
A 64-bit guard beyond bits(100) is checked by measuring the relative
accuracy of every output ball. The independent checker uses a partition
transform to pure psi integrals and MPFI for the final pi evaluation.
"""
import os
import sys
from functools import lru_cache
from math import factorial, comb
from collections import Counter
from itertools import product
import numberdb.sage as numberdb
from sage.rings.rational_field import QQ
from sage.rings.integer_ring import ZZ
from sage.rings.real_arb import RealBallField
WORKING_GUARD = 64
MAX_GENUS = 3
MAX_PUNCTURES = 8
@lru_cache(None)
def odd_factorial(k):
if k <= 0:
return ZZ(1)
return ZZ(k)*odd_factorial(k-2)
@lru_cache(None)
def splits(ds):
"""All labelled subsets, compressed by equal exponents."""
counts = sorted(Counter(ds).items())
out = []
for chosen in product(*(range(c+1) for _, c in counts)):
left, right, ways = [], [], 1
for (d,c), j in zip(counts,chosen):
left.extend([d]*j)
right.extend([d]*(c-j))
ways *= comb(c,j)
out.append((tuple(left),tuple(right),ways))
return tuple(out)
def mixed(g,k,ds):
return _mixed(int(g),int(k),tuple(sorted(ds)))
@lru_cache(None)
def _mixed(g,k,ds):
"""Integral of kappa_1^k times the psi powers ds."""
n=len(ds)
if g<0 or k<0 or any(d<0 for d in ds) or 2*g-2+n<=0:
return QQ(0)
if k+sum(ds)!=3*g-3+n:
return QQ(0)
if (g,k,ds)==(0,0,(0,0,0)):
return QQ(1)
if g==1 and n==1 and k+ds[0]==1:
return QQ(1)/24
if n==0:
# Liu-Xu equation (3), adding a marked point.
return sum((QQ((-1)**j*comb(k,j))*mixed(g,k-j,(j+1,))
for j in range(k+1)),QQ(0))/(2*g-2)
d=ds[-1]
tail=ds[:-1]
total=QQ(0)
# Isolate the L=0 term on the left of Liu-Xu Theorem 1.1.
for j in range(1,k+1):
total -= (QQ((-1)**j*comb(k,j))*odd_factorial(2*d+2*j+1)
/odd_factorial(2*j+1)*mixed(g,k-j,tail+(d+j,)))
for i,e in enumerate(tail):
total += (odd_factorial(2*(d+e)-1)/odd_factorial(2*e-1)
*mixed(g,k,tail[:i]+tail[i+1:]+(d+e-1,)))
for a in range(d-1):
b=d-2-a
term=mixed(g-1,k,tail+(a,b))
for left,right,ways in splits(tail):
for h in range(g+1):
# Dimension forces the kappa power on the first component.
ka=3*h-3+len(left)+1-a-sum(left)
if 0<=ka<=k:
term += (ways*comb(k,ka)*mixed(h,ka,left+(a,))
*mixed(g-h,k-ka,right+(b,)))
total += odd_factorial(2*a+1)*odd_factorial(2*b+1)*term/2
return total/odd_factorial(2*d+1)
def coefficient(g,n):
"""Exact coefficient of pi^(2m) in V_{g,n}."""
if g<0 or n<0 or 2*g-2+n<=0:
raise ValueError('requires stable nonnegative genus and punctures')
m=3*g-3+n
return QQ(2**m)*mixed(g,m,(0,)*n)/factorial(m)
def volume(g,n,digits=100):
c=coefficient(g,n)
m=3*g-3+n
if m==0:
return c
field=RealBallField(numberdb.bits(digits,losing=WORKING_GUARD))
ans=field(c)*field.pi()**(2*m)
assert ans.is_finite() and ans>0 and ans.rad()>0
return ans
class WeilPeterssonVolumes(numberdb.Generator):
table='T459'
parameters=('g','n')
type='R'
digits=100
rigour='proven'
files=('generate.py','checks.py','check.py','numerical-evidence.json')
def enumerate(self,max_genus=MAX_GENUS,max_punctures=MAX_PUNCTURES):
for g in range(max_genus+1):
for n in range(max_punctures+1):
if 2*g-2+n>0:
yield {'g':g,'n':n}
def value(self,params,digits):
g,n=int(params['g']),int(params['n'])
v=volume(g,n,digits)
if (g,n)==(0,3):
return {'number':v,'comment':'The thrice-punctured sphere has a unique hyperbolic structure, and its moduli space has volume $1$.'}
return v
if __name__=='__main__':
if os.environ.get('NUMBERDB_KEY_FROM_STDIN')=='1':
numberdb.configure(api_key=sys.stdin.read().strip())
gen=WeilPeterssonVolumes()
if '--fill' in sys.argv or os.environ.get('NUMBERDB_PUBLISH')=='1':
print(gen.publish(message='add orbifold Weil-Petersson volumes with exact coefficient checks'))
else:
report=gen.verify(sample=None)
print(report)
sys.exit(0 if report.ok else 1)