generate.py

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

3174 bytes, as of the version from 2026-10-26 21:30 (current). Recorded here, not run.

"""Wright constants for connected graph enumeration -- numberdb.org/T528.

Run with SageMath (or compatible passagemath):
    sage -pip install numberdb
    sage -python generate.py
    sage -python generate.py --fill  # NUMBERDB_API_KEY must authorize this draft

The default command verifies all stored entries. --fill sends entries and
attachments to the existing table; it does not make a draft public or offer it.

Janson, Probability Surveys 4 (2007), equations (6), (7), (20) and (36).
Only graph excess k is free. The default selection is 0 <= k <= 11;
argument-selection.md explains the convention and editorial endpoint.

All K coefficients and integer/half-integer Gamma simplifications are exact
rationals. Odd k gives an exact rational. Even k gives a rational multiple
of sqrt(2*pi), enclosed by Arb at at least 512 bits. The attached checks
measure supported precision and containment in each written decimal interval.
"""
import os
import sys
from functools import lru_cache
from math import factorial
import numberdb.sage as numberdb
from sage.rings.rational_field import QQ
from sage.rings.real_arb import RealBallField

MIN_WORKING_BITS = 512
WORKING_GUARD = 128


@lru_cache(None)
def k_coefficients(n):
    values = [QQ(-1) / 2]
    for j in range(1, n + 1):
        values.append(QQ(3*j - 4) / 4 * values[j - 1]
                      + sum((values[i] * values[j-i] for i in range(1, j)), QQ(0)))
    return tuple(values)


def rational_factor(k):
    """Return q, with rho_k=q for odd k and q*sqrt(2*pi) for even k."""
    if k < 0:
        raise ValueError('Stored entries have nonnegative excess')
    K = k_coefficients(k + 1)[k + 1]
    m = k // 2
    if k % 2 == 0:
        return QQ(2)**(1-m) * K / QQ(factorial(3*m))
    return (QQ(2)**(5*m+5) * K * QQ(factorial(3*m+2))
            / QQ(factorial(6*m+4)))


class WrightConstants(numberdb.Generator):
    table = 'T528'
    parameters = ('k',)
    type = 'R'
    digits = 100
    rigour = 'proven'
    files = ('generate.py', 'checks.py', 'check.py', 'argument-selection.md',
             'numerical-evidence.md', 'skill-manifest.json')

    def enumerate(self, max_excess=11):
        for k in range(max_excess + 1):
            yield {'k': str(k)}

    def value(self, params, digits):
        k = int(params['k'])
        q = rational_factor(k)
        if k % 2:
            return q
        R = RealBallField(max(MIN_WORKING_BITS,
                             numberdb.bits(digits, losing=WORKING_GUARD)))
        return R(q) * (2 * R.pi()).sqrt()


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].isupper():
            key = key.split('=', 1)[1].strip().strip('\'"')
        os.environ['NUMBERDB_API_KEY'] = key
    generator = WrightConstants()
    if '--fill' in sys.argv or os.environ.get('NUMBERDB_PUBLISH') == '1':
        print(generator.publish(message='Fill excesses 0 through 11 with exact rationals and certified 100-digit enclosures'))
    else:
        report = generator.verify(sample=None)
        print(report)
        sys.exit(0 if report.ok else 1)