generate.py

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

3243 bytes, as of the version from 2026-10-30 12:25 (current). Recorded here, not run.

"""Volumes of real and complex Grassmann manifolds -- numberdb.org/T625.

Run with SageMath:
    sage -pip install numberdb
    sage -python generate.py             # verify all stored entries
    sage -python generate.py --fill      # write entries to this draft

Filling requires NUMBERDB_API_KEY; it does not publish or offer the table.
The campaign wrapper may instead pipe the key and set NUMBERDB_KEY_FROM_STDIN=1
and NUMBERDB_PUBLISH=1. This file has no selection or numerical dependencies.

The metric is ds^2 = Re tr(dP^2)/2 on rank-k orthogonal projectors.
Both fields and 2 <= n <= 16, 1 <= k <= floor(n/2), give 128 entries.
See argument-selection.md for the discrete family and practical finite bound.
Integer and half-integer Gamma factors are reduced exactly before evaluating
one integer power of pi with Arb. A 64-bit guard is measured by check_values.py.
"""
import os
import sys
from math import factorial
import numberdb.sage as numberdb
from sage.rings.rational_field import QQ
from sage.rings.real_arb import RealBallField

WORKING_GUARD = 64
MAX_N = 16
FIELDS = ('real', 'complex')


def gamma_factor(twice_argument):
    """Return exact (r, e) for Gamma(t/2) = r*pi^(e/2), t positive."""
    t = int(twice_argument)
    if t < 1:
        raise ValueError('Gamma argument must be positive')
    if t % 2 == 0:
        return QQ(factorial(t//2-1)), 0
    m = (t-1)//2
    return QQ(factorial(2*m))/QQ(4**m*factorial(m)), 1


def exact_form(field, n, k):
    """Return (c, e) such that G = c*pi^e, with c rational and e integer."""
    if field not in FIELDS or n < 2 or not 1 <= k < n:
        raise ValueError('need real or complex, n >= 2, 1 <= k < n')
    beta = 1 if field == 'real' else 2
    coefficient = QQ(1)
    twice_exponent = beta*k*(n-k)
    for i in range(1, k+1):
        a, ea = gamma_factor(beta*i)
        b, eb = gamma_factor(beta*(n-k+i))
        coefficient *= a/b
        twice_exponent += ea-eb
    assert twice_exponent % 2 == 0
    return coefficient, twice_exponent//2


class GrassmannVolumes(numberdb.Generator):
    table = 'T625'
    parameters = ('field', 'n', 'k')
    type = 'R'
    digits = 100
    rigour = 'proven'

    def enumerate(self, max_n=MAX_N):
        for field in FIELDS:
            for n in range(2, max_n+1):
                for k in range(1, n//2+1):
                    yield {'field':field, 'n':n, 'k':k}

    def value(self, params, digits):
        c, e = exact_form(params['field'], int(params['n']), int(params['k']))
        if e == 0:
            return c
        R = RealBallField(numberdb.bits(digits, losing=WORKING_GUARD))
        return R(c)*R.pi()**e


if __name__ == '__main__':
    if os.environ.get('NUMBERDB_KEY_FROM_STDIN') == '1':
        key = sys.stdin.read().strip()
        if key.startswith('NUMBERDB_'):
            key = key.split('=', 1)[1].strip().strip('\"\'')
        numberdb.configure(api_key=key)
    generator = GrassmannVolumes()
    if '--fill' in sys.argv or os.environ.get('NUMBERDB_PUBLISH') == '1':
        print(generator.publish(message='Compute 128 Grassmann volumes with certified 100-digit enclosures'))
    else:
        report = generator.verify(sample=None)
        print(report)
        sys.exit(0 if report.ok else 1)