generate.py

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

3372 bytes, as of the version from 2026-10-28 00:06 (current). Recorded here, not run.

"""Morris integrals for circular ensembles with a coalescing charge -- numberdb.org/T565.

Run with SageMath and the numberdb package:
    sage -pip install numberdb
    sage -python generate.py                 # verify stored entries
    sage -python checks.py                   # independent integral checks

To fill the existing draft, set NUMBERDB_API_KEY and NUMBERDB_FILL_DRAFT=1.
Filling entries does not publish or offer the draft for review.
The default selection is beta=1,2,4; n=1,...,10; k=0,...,4.
Every Gamma argument is integral or half-integral. Exact rational reduction
produces q*pi**p. Values with p=0 are returned exactly; other values use
100-digit real balls with 96 guard bits. See argument-selection.md and
numerical-evidence.json for the selection and measured precision.
"""
import os
import sys
from math import factorial
from pathlib import Path
import numberdb.sage as numberdb
from sage.rings.rational_field import QQ
from sage.rings.real_arb import RealBallField

WORKING_GUARD = 96


def gamma_half_integer(twice_argument):
    """Return q,e with Gamma(twice_argument/2) = q*sqrt(pi)**e."""
    a = int(twice_argument)
    assert a >= 2
    if a % 2 == 0:
        return QQ(factorial(a // 2 - 1)), 0
    m = (a - 1) // 2
    return QQ(factorial(2 * m)) / QQ(4**m * factorial(m)), 1


def exact_form(beta, n, k):
    """Morris's product in exact Q times an integral power of pi."""
    beta, n, k = int(beta), int(n), int(k)
    assert beta in (1, 2, 4) and n >= 1 and k >= 0
    q, sqrt_power = QQ(1), 0
    for j in range(n):
        for a, sign in ((2 + 2*k*beta + j*beta, 1),
                        (2 + (j+1)*beta, 1),
                        (2 + k*beta + j*beta, -2),
                        (2 + beta, -1)):
            coefficient, exponent = gamma_half_integer(a)
            q *= coefficient**sign
            sqrt_power += sign*exponent
    assert sqrt_power % 2 == 0
    return q, sqrt_power // 2


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

    def enumerate(self):
        for beta in (1, 2, 4):
            for n in range(1, 11):
                for k in range(5):
                    yield {'beta': str(beta), 'n': str(n), 'k': str(k)}

    def value(self, params, digits):
        q, p = exact_form(*(params[name] for name in self.parameters))
        if p == 0:
            return q
        field = RealBallField(numberdb.bits(digits, losing=WORKING_GUARD))
        return field(q) * field.pi()**p


if __name__ == '__main__':
    if os.environ.get('NUMBERDB_KEY_FROM_STDIN') == '1':
        key = sys.stdin.read().strip()
        if '=' in key:
            key = key.split('=', 1)[1].strip().strip('\"\'')
        os.environ['NUMBERDB_API_KEY'] = key
    generator = MorrisIntegrals()
    if os.environ.get('NUMBERDB_FILL_DRAFT') == '1' or os.environ.get('NUMBERDB_PUBLISH') == '1':
        result = generator.publish(message='Fill 150 Morris integrals checked by independent angular moment matrices')
        print(result)
    else:
        result = generator.verify(sample=None)
        print(result)
        sys.exit(0 if result.ok else 1)