generate.py

back to table · edit · history · where entries came from · files · download

4882 bytes, as of the version from 2026-11-09 12:11 (current). Recorded here, not run.

"""Genus-zero GW invariants of projective Calabi–Yau complete intersections -- numberdb.org/T688.

Run with SageMath (or passagemath-symbolics) and the numberdb package:
    sage -pip install numberdb                # once, inside SageMath
    sage -python generate.py                  # verify every stored entry
    sage -python checks.py                    # offline independent checks
    sage -python generate.py --submit         # fill the existing draft

The last command requires NUMBERDB_API_KEY and submits entries only. It cannot
publish or offer a draft. NUMBERDB_KEY_FROM_STDIN=1 reads the key from stdin;
NUMBERDB_PUBLISH=1 is the build-wrapper equivalent of --submit.

All series coefficients are Sage rationals. Arrays contain coefficients through
an inclusive degree bound, with explicit convolution, reciprocal, exponential,
and composition. The mirror inverse is computed by x=q exp(-h(x)), gaining an
order each iteration. K(x)=kappa/((1-C*x)*F(x)^2*(1+x*h'(x))^3), h=G/F.
Its q^d coefficient divided by d^3 is the unmarked, connected genus-zero GW
invariant. No instanton/GV values are submitted. Default enumeration is all five
nonlinear ordinary-projective types and every curve degree 1 through 20.
See argument-selection.md and numerical-evidence.md for scope and checks.
"""
import os
import sys
from functools import lru_cache
from math import factorial, prod
import numberdb.sage as numberdb
from sage.rings.rational_field import QQ

TARGETS = ((5,), (2, 4), (3, 3), (2, 2, 3), (2, 2, 2, 2))
MAX_DEGREE = 20
EXTRA_ORDERS = 4


def multiply(a, b):
    n = len(a)
    assert n == len(b)
    return [sum((a[j] * b[k-j] for j in range(k+1)), QQ(0))
            for k in range(n)]


def reciprocal(a):
    assert a[0]
    b = [QQ(1) / a[0]]
    for k in range(1, len(a)):
        b.append(-sum((a[j] * b[k-j] for j in range(1, k+1)), QQ(0)) / a[0])
    return b


def exponential(a):
    assert a[0] == 0
    b = [QQ(1)]
    for k in range(1, len(a)):
        b.append(sum((QQ(j) * a[j] * b[k-j] for j in range(1, k+1)), QQ(0)) / QQ(k))
    return b


def compose(a, b):
    assert len(a) == len(b) and b[0] == 0
    out = [QQ(0)] * len(a)
    for coefficient in reversed(a):
        out = multiply(out, b)
        out[0] += coefficient
    return out


def factorial_periods(ell, order):
    harmonics = [QQ(0)]
    for j in range(1, max(ell) * order + 1):
        harmonics.append(harmonics[-1] + QQ(1) / QQ(j))
    f, g = [], []
    for m in range(order + 1):
        a = QQ(prod(factorial(l*m) for l in ell)) / QQ(factorial(m)**(len(ell)+4))
        b = sum((QQ(l) * harmonics[l*m] for l in ell), QQ(0)) - QQ(len(ell)+4)*harmonics[m]
        f.append(a)
        g.append(a*b)
    return f, g


@lru_cache(None)
def yukawa_series(ell, order):
    assert ell in TARGETS
    f, g = factorial_periods(ell, order)
    h = multiply(g, reciprocal(f))
    u = [QQ(1)] + [QQ(m)*h[m] for m in range(1, order+1)]
    inv_u = reciprocal(u)
    inv_f = reciprocal(f)
    c = prod(l**l for l in ell)
    k_x = [QQ(prod(ell)) * QQ(c)**m for m in range(order+1)]
    for factor in (inv_f, inv_f, inv_u, inv_u, inv_u):
        k_x = multiply(k_x, factor)
    x = [QQ(0), QQ(1)] + [QQ(0)] * (order-1)
    for _ in range(order):
        correction = exponential([-v for v in compose(h, x)])
        new_x = [QQ(0)] + correction[:-1]
        if new_x == x:
            break
        x = new_x
    q_x = [QQ(0)] + exponential(h)[:-1]
    assert compose(q_x, x) == [QQ(0), QQ(1)] + [QQ(0)]*(order-1)
    return tuple(compose(k_x, x))


class ProjectiveCalabiYauGW(numberdb.Generator):
    table = 'T688'
    parameters = ('multidegree', 'd')
    type = 'Q'
    rigour = 'exact'
    files = ('generate.py', 'checks.py', 'check.py', 'argument-selection.md',
             'numerical-evidence.md', 'localization-controls.json',
             'skill-manifest.json', 'source-evidence.md')

    def enumerate(self, max_degree=MAX_DEGREE):
        for ell in TARGETS:
            for d in range(1, max_degree+1):
                yield {'multidegree': ','.join(map(str, ell)), 'd': str(d)}

    def value(self, params, digits):
        ell = tuple(map(int, params['multidegree'].split(',')))
        d = int(params['d'])
        if d < 1:
            raise ValueError('Curve degree must be positive')
        return yukawa_series(ell, max(MAX_DEGREE, d)+EXTRA_ORDERS)[d] / QQ(d)**3


if __name__ == '__main__':
    if os.environ.get('NUMBERDB_KEY_FROM_STDIN') == '1':
        os.environ['NUMBERDB_API_KEY'] = sys.stdin.read().strip()
    generator = ProjectiveCalabiYauGW()
    if '--submit' in sys.argv or os.environ.get('NUMBERDB_PUBLISH') == '1':
        print(generator.publish(message='Fill exact genus-zero Calabi-Yau GW invariants after independent checks'))
    else:
        report = generator.verify(sample=None)
        print(report)
        sys.exit(0 if report.ok else 1)