generate.py

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

4074 bytes, as of the version from 2026-11-01 17:38 (current). Recorded here, not run.

"""Minimum point of the Ellenberg–Gijswijt bound -- numberdb.org/T656.

Run with SageMath (or a modular Sage installation):
    sage -pip install numberdb
    sage -python generate.py                 # verify all stored entries
    sage -python generate.py --fill          # fill this draft, with NUMBERDB_API_KEY
    sage -python checks.py                   # independent checks of local values
    sage -python checks.py --stored          # check values read from the draft

The finite family is n=2,...,32 and the two source conventions rho and gamma.
Read argument-selection.md for the justification and skill-manifest.json for
this run's coordinated instruction hashes. No extra continuous parameter is
sampled. The table stores a minimizer, not the minimum objective value.

For n>=2, strict convexity of log F_n(exp(s)) follows from a positive variance.
Its derivative changes sign from negative to positive, so P_n has precisely
one positive root, between zero and one. Exact rational bisection therefore
certifies that root. A RealBallField conversion encloses both rational
endpoints, and reciprocation propagates their uncertainty. The 96 extra
working bits and 24 additional bisection steps are measured in checks.py;
the resulting balls comfortably support 100 significant decimal digits.
Dimension two returns the exactly known rationals 1/2 and 2.
"""
import os
import sys
from functools import lru_cache
import numberdb.sage as numberdb
from sage.rings.rational_field import QQ
from sage.rings.real_arb import RealBallField

WORKING_GUARD = 96
BISECTION_GUARD = 24
MAX_DIMENSION = 32


def stationary(n, t):
    """Horner evaluation of P_n over the coefficient's exact parent."""
    result = QQ(0)
    for j in range(n - 1, -1, -1):
        result = result * t + (3 * j - n + 1)
    return result


@lru_cache(maxsize=None)
def root_bounds(n, steps):
    if n < 2:
        raise ValueError('The algebra dimension must be at least two.')
    lo, hi = QQ(0), QQ(1)
    assert stationary(n, lo) < 0 < stationary(n, hi)
    for _ in range(steps):
        mid = (lo + hi) / QQ(2)
        sign = stationary(n, mid)
        if sign == 0:
            return mid, mid
        if sign < 0:
            lo = mid
        else:
            hi = mid
    assert stationary(n, lo) < 0 < stationary(n, hi)
    return lo, hi


class MinimumPoint(numberdb.Generator):
    table = 'T656'
    parameters = ('n', 'convention')
    type = 'R'
    digits = 100
    rigour = 'proven'
    files = ('generate.py', 'checks.py', 'argument-selection.md',
             'skill-manifest.json', 'source-evidence.md', 'numerical-evidence.json')

    def enumerate(self, maximum=MAX_DIMENSION):
        for n in range(2, maximum + 1):
            for convention in ('rho', 'gamma'):
                yield {'n': n, 'convention': convention}

    def value(self, params, digits):
        n = int(params['n'])
        convention = params['convention']
        if convention not in ('rho', 'gamma'):
            raise ValueError('Use rho or gamma.')
        bits = numberdb.bits(digits, losing=WORKING_GUARD)
        lo, hi = root_bounds(n, bits + BISECTION_GUARD)
        if lo == hi:
            return lo if convention == 'rho' else QQ(1) / lo
        R = RealBallField(bits)
        rho = R((lo + hi) / QQ(2)).add_error(R((hi - lo) / QQ(2)))
        result = rho if convention == 'rho' else QQ(1) / rho
        assert result.is_finite() and result > 0
        return result


def configured_client():
    key = None
    if os.environ.get('NUMBERDB_KEY_FROM_STDIN') == '1':
        key = sys.stdin.read().strip()
        if '=' in key:
            key = key.split('=', 1)[1].strip().strip('\"\'')
    return numberdb.Client(api_key=key)


if __name__ == '__main__':
    client = configured_client()
    generator = MinimumPoint()
    if '--fill' in sys.argv:
        print(generator.publish(client=client, message='Fill the certified minimizing arguments'))
    else:
        report = generator.verify(sample=None, client=client)
        print(report)
        sys.exit(0 if report.ok else 1)