generate.py

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

3993 bytes, as of the version from 2026-10-26 12:36. Recorded here, not run.

"""Values of the Scorer function Hi -- numberdb.org/T515.

Run with SageMath (or passagemath with real and complex Arb support):

    sage -pip install numberdb
    sage -python generate.py             # verify every stored entry
    sage -python checks.py               # independent numerical checks
    sage -python generate.py --fill      # fill the draft using NUMBERDB_API_KEY

The --fill operation sends entries and source attachments; it does not make
this draft public. On the campaign builder, NUMBERDB_KEY_FROM_STDIN=1 reads
the key from stdin and NUMBERDB_PUBLISH=1 selects the same fill operation.

The standard Hi of DLMF 9.12.20 is used, without scaling or subtraction.
Only the nine reference arguments agreed in the family are enumerated.
The parameters describe all rational arguments, not just this selection.

For y=sum(c_k*x^k), y''-x*y=1/pi gives c_2=1/(2*pi) and
c_(k+3)=c_k/((k+3)*(k+2)). The initial c_0 and c_1 are DLMF 9.12.6-7.
All arithmetic, gamma evaluations, and positive real powers use Arb balls.
Each residue class modulo 3 is summed separately. After index k, the ratio
of absolute terms is at most q=abs(x)^3/((k+3)*(k+2)); subsequent ratios
are smaller. Once q<1, the entire omitted tail is bounded by
abs(term)*q/(1-q), which is explicitly added to the ball radius.

The working precision is digits+100 decimal digits, rounded up to bits;
each of the three tails is below 10^(-digits-50). At 100 requested digits
this is 665 bits. checks.py measures the final relative accuracy, tests
certified independent integration, and compares mpmath at 150 and 200 dps.
No claim of rigorous accuracy relies on mpmath.
"""
import os
import sys
import numberdb.sage as numberdb
from sage.rings.rational_field import QQ
from sage.rings.real_arb import RealBallField

ARGUMENTS = ('0', '1/2', '-1/2', '1', '-1', '2', '-2', '10', '-10')
WORKING_GUARD_DIGITS = 100
TAIL_GUARD_DIGITS = 50


def scorer_hi(x, digits=100):
    """Enclose Hi(x) from its entire Taylor series at exact rational x."""
    field = RealBallField(numberdb.bits(digits + WORKING_GUARD_DIGITS, losing=0))
    x = field(QQ(x))
    pi = field.pi()
    coefficients = (
        2 / (field(3)**(QQ(7)/6) * field(QQ(2)/3).gamma()),
        2 / (field(3)**(QQ(5)/6) * field(QQ(1)/3).gamma()),
        1 / (2*pi),
    )
    cutoff = field(10)**(-digits-TAIL_GUARD_DIGITS)
    total = field(0)
    for residue, coefficient in enumerate(coefficients):
        k = residue
        term = coefficient * x**k
        subtotal = term
        while True:
            denominator = (k+3)*(k+2)
            q = abs(x)**3 / denominator
            if q < 1:
                tail = abs(term)*q/(1-q)
                if tail < cutoff:
                    subtotal = subtotal.add_error(tail)
                    break
            term = term*x**3 / denominator
            subtotal += term
            k += 3
            if k > 100000:
                raise ArithmeticError('Taylor tail did not contract')
        total += subtotal
    assert total.is_finite()
    assert total > 0
    assert total.accuracy() >= numberdb.bits(digits, losing=20)
    return total


class ScorerHi(numberdb.Generator):
    table = 'T515'
    parameters = ('x',)
    type = 'R'
    digits = 100
    rigour = 'proven'
    files = ('generate.py', 'checks.py', 'check.py', 'dry_run.py', 'evidence.json')

    def enumerate(self):
        for x in ARGUMENTS:
            yield {'x': x}

    def value(self, params, digits):
        return scorer_hi(params['x'], digits)


if __name__ == '__main__':
    if os.environ.get('NUMBERDB_KEY_FROM_STDIN') == '1':
        numberdb.configure(api_key=sys.stdin.read().strip())
    generator = ScorerHi()
    if '--fill' in sys.argv or os.environ.get('NUMBERDB_PUBLISH') == '1':
        print(generator.publish(message='Fill nine Scorer Hi values with certified Taylor enclosures'))
    else:
        report = generator.verify(sample=None)
        print(report)
        sys.exit(0 if report.ok else 1)