generate.py

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

3552 bytes, as of the version from 2026-09-15 09:28. Recorded here, not run.

"""https://numberdb.org/T228
Generate T228, the values of the derivative zeta'(s) of the Riemann zeta
function at rational arguments.

Run it with SageMath:

    $ sage -pip install numberdb
    $ sage -python generate.py
    $ sage -python generate.py --publish

Under the repository's agent runner, pipe the API key on stdin:

    $ cat "$NUMBERDB_KEY_FILE" | NUMBERDB_KEY_FROM_STDIN=1 \
        agents/sage.sh generators/zeta-derivative-rational-values/generate.py

Set NUMBERDB_PUBLISH=preview to preview the write, or NUMBERDB_PUBLISH=1 to
send the entries and attach this file.
"""

import os
import sys

import numberdb.sage as numberdb
from sage.rings.complex_arb import ComplexBallField
from sage.rings.rational_field import QQ


WORKING_GUARD = 96


def configure_key_from_stdin():
    if os.environ.get('NUMBERDB_KEY_FROM_STDIN') != '1':
        return
    token = sys.stdin.read().strip()
    if '=' in token and token.split('=', 1)[0].isupper():
        token = token.split('=', 1)[1].strip().strip('\'"')
    if token:
        numberdb.configure(api_key=token)


def rational_arguments(denominator=4, lower=-20, upper=20):
    for b in range(1, denominator + 1):
        for n in range(0, upper * b + 1):
            numerators = [n] if n == 0 else [n, -n]
            for a in numerators:
                s = QQ(a) / QQ(b)
                if s.denominator() != b or s < lower or s > upper or s == 1:
                    continue
                yield s


def finite_real(value, label):
    if not value.real().is_finite() or not value.imag().is_finite():
        raise ArithmeticError('computed a non-finite ball for %s' % (label,))
    if not value.imag().contains_zero():
        raise ArithmeticError('expected a real value for %s' % (label,))
    return value.real()


def derivative_comment(s):
    text = str(s)
    if text == '0':
        return '$-\\frac12\\log(2\\pi)$.'
    if text == '-1':
        return (
            '$\\frac1{12}-\\log A$, where $A$ is '
            'HREF{T227#1,A}[the Glaisher-Kinkelin constant] '
            'CITE{DLMFBarnes}.'
        )
    if text == '-2':
        return '$-\\zeta(3)/(4\\pi^2)$.'
    if text == '-4':
        return '$3\\zeta(5)/(4\\pi^4)$.'
    if text == '2':
        return (
            '$\\frac{\\pi^2}{6}(\\gamma+\\log(2\\pi)-12\\log A)$ '
            'CITE{DLMFBarnes}.'
        )
    return ''


class RiemannZetaDerivativeAtRationals(numberdb.Generator):
    table = 'T228'
    parameters = ('s',)
    type = 'R'
    digits = 100
    rigour = 'proven'

    def enumerate(self):
        for s in rational_arguments():
            yield {'s': str(s)}

    def value(self, params, digits):
        field = ComplexBallField(numberdb.bits(digits, losing=WORKING_GUARD))
        rational_s = QQ(params['s'])
        s = field(rational_s)
        number = finite_real(s.zetaderiv(1), "zeta'(%s)" % (rational_s,))
        comment = derivative_comment(rational_s)
        if comment:
            return {'number': number, 'comment': comment}
        return number


def main():
    configure_key_from_stdin()
    generator = RiemannZetaDerivativeAtRationals()
    mode = os.environ.get('NUMBERDB_PUBLISH')
    if '--publish' in sys.argv or mode == '1':
        print(generator.publish(message='split T228 derivative entries'))
        return
    if '--preview' in sys.argv or mode == 'preview':
        print(generator.preview())
        return
    report = generator.verify(sample=None)
    print(report)
    sys.exit(0 if report.ok else 1)


if __name__ == '__main__':
    main()