generate.py

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

4007 bytes, as of the version from 2026-11-11 03:31 (current). Recorded here, not run.

"""T715: Central binomial sums, U_p(v).

Install numberdb in SageMath (or a suitable modular passagemath environment).
    sage -pip install numberdb
    sage -python generate.py                  # verify all stored entries
    sage -python generate.py --publish        # add/update the selected entries
The second command requires the draft owner's configured NumberDB API key.
It fills the target; it does not publish or offer the private draft.

Parameters are ordered (p,v), with p>=1 integral and -1/4<=v<=1/4 rational.
The default selection is p=1..9 at v=-1/4,-1/16,1/16,1/4. Values include n=0.
No normalization factors are applied. All computations use rigorous real balls.
See computation.md for the beta coefficient identity and certified tails.
At v=1/4 use finite coefficients of the beta generating function; at -1/4
use the Euler transform of a Hausdorff moment sequence, with tail <=2^-N.
Interior points use the defining series and a geometric absolute tail.
The Euler difference computation uses extra precision to absorb cancellation.
Session/model attribution is supplied externally, never fixed by this file.
"""
import argparse
import os
import sys
from functools import lru_cache

import numberdb.sage as numberdb
from sage.rings.integer_ring import ZZ
from sage.rings.rational_field import QQ
from sage.rings.real_arb import RealBallField

ARGUMENTS = ('-1/4', '-1/16', '1/16', '1/4')


def selection(max_order=9):
    for p in range(1, int(max_order) + 1):
        for v in ARGUMENTS:
            yield {'p': p, 'v': v}


@lru_cache(maxsize=256)
def central_sum(p, v, digits):
    p, v, digits = ZZ(p), QQ(v), int(digits)
    if p < 1 or abs(v) > QQ(1)/4:
        raise ValueError('Require integer p>=1 and rational |v|<=1/4')
    target = numberdb.bits(digits + 25)
    work_bits = 2*target + 80
    R = RealBallField(work_bits)
    if v == 0:
        return R(1)
    if v == QQ(1)/4:
        # sum U_(m+1)(1/4) h^m = pi/2 exp(sum a_k h^k).
        a = [R(0), R(2).log()]
        a.extend((1 - R(2)**(1-k))*R(k).zeta()/k for k in range(2, int(p)))
        e = [R(1)]
        for m in range(1, int(p)):
            e.append(sum(k*a[k]*e[m-k] for k in range(1, m+1))/m)
        result = R.pi()*e[int(p)-1]/2
    elif v == -QQ(1)/4:
        N = int(target)
        c = QQ(1)
        differences = []
        for n in range(N):
            differences.append(R(c)/(2*n+1)**p)
            c *= QQ(2*n+1)/(2*n+2)
        result = R(0)
        for k in range(N):
            result += differences[0]*R(2)**(-k-1)
            differences = [differences[j]-differences[j+1]
                           for j in range(len(differences)-1)]
        result = result.add_error(R(2)**(-N))
    else:
        q = 4*abs(v)
        # Choose N by an exact geometric inequality, without float logs.
        N = 1
        while q**N/(1-q) > QQ(1)/ZZ(2)**target:
            N += 1
        coefficient = R(1)
        result = R(0)
        for n in range(N):
            result += coefficient/(2*n+1)**p
            coefficient *= R(v)*2*(2*n+1)/(n+1)
        tail = R(q)**N/((1-R(q))*(2*N+1)**p)
        result = result.add_error(tail)
    assert result.is_finite()
    assert R(result.rad()) < R(10)**(-digits-15)
    return result


class CentralBinomialSums(numberdb.Generator):
    table = 'T715'
    parameters = ('p', 'v')
    type = 'R'
    digits = 100
    rigour = 'proven'

    def enumerate(self, max_order=9):
        yield from selection(max_order)

    def value(self, params, digits):
        return central_sum(params['p'], params['v'], digits)


if __name__ == '__main__':
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument('--publish', action='store_true')
    args = parser.parse_args()
    if os.environ.get('NUMBERDB_KEY_FROM_STDIN') == '1':
        numberdb.configure(api_key=sys.stdin.read().strip())
    generator = CentralBinomialSums()
    if args.publish:
        print(generator.publish())
    else:
        print(generator.verify(sample=None))