curated_values.py

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

5503 bytes, as of the version from 2026-10-27 01:06 (current). Recorded here, not run.

"""Intrinsic argument families for Airy, Si/Ci, Fresnel, and Gaussian values.

Real Arb enclosures propagate root, pi and square-root uncertainty. Default
enumeration never samples a rational or decimal argument mesh. Parameter x
retains its meaning; exact integer arguments keep their existing identities.
"""

import argparse
import math
from fractions import Fraction

import numberdb
from flint import arb

from scorer import as_interval, precision, working_precision


KINDS = {'T192': 'si', 'T193': 'ci', 'T197': 'erf', 'T198': 'ai',
         'T201': 'ai_prime', 'T202': 'bi', 'T203': 'bi_prime',
         'T204': 'erfc', 'T207': 'fresnel_s', 'T208': 'fresnel_c'}


def sqrt_identity(radicand):
    integer = math.isqrt(radicand)
    return str(integer) if integer ** 2 == radicand else 'sqrt(%d)' % radicand


def argument_for(identity):
    if identity.startswith('ai-prime-zero-'):
        return arb.airy_ai_zero(int(identity[len('ai-prime-zero-'):]), derivative=1)
    if identity.startswith('bi-prime-zero-'):
        return arb.airy_bi_zero(int(identity[len('bi-prime-zero-'):]), derivative=1)
    if identity.startswith('ai-zero-'):
        return arb.airy_ai_zero(int(identity[len('ai-zero-'):]))
    if identity.startswith('bi-zero-'):
        return arb.airy_bi_zero(int(identity[len('bi-zero-'):]))
    if identity.startswith('sqrt(') and identity.endswith(')'):
        return arb(int(identity[5:-1])).sqrt()
    if identity.endswith('*pi/2'):
        return arb(int(identity[:-5])) * arb.pi() / 2
    if identity.endswith('*pi'):
        return arb(int(identity[:-3])) * arb.pi()
    if identity.endswith('/sqrt(2)'):
        return arb(int(identity[:-8])) / arb(2).sqrt()
    return arb(str(Fraction(identity)))


def evaluate(kind, argument):
    if kind == 'ai':
        return argument.airy_ai()
    if kind == 'ai_prime':
        return argument.airy()[1]
    if kind == 'bi':
        return argument.airy_bi()
    if kind == 'bi_prime':
        return argument.airy()[3]
    return getattr(argument, kind)()


def display(identity):
    prefixes = {'ai-prime-zero-': r'a^{\prime}', 'bi-prime-zero-': r'b^{\prime}',
                'ai-zero-': 'a', 'bi-zero-': 'b'}
    for prefix, symbol in prefixes.items():
        if identity.startswith(prefix):
            return '$%s_{%d}$' % (symbol, int(identity[len(prefix):]))
    if identity.startswith('sqrt('):
        return r'$\sqrt{%d}$' % int(identity[5:-1])
    if identity.endswith('*pi/2'):
        return r'$%d\pi/2$' % int(identity[:-5])
    if identity.endswith('*pi'):
        return r'$%d\pi$' % int(identity[:-3])
    if identity.endswith('/sqrt(2)'):
        return r'$%d/\sqrt{2}$' % int(identity[:-8])
    return '$%s$' % identity


class CuratedValues(numberdb.Generator):
    parameters = ('x',)
    type = 'R'
    digits = 100
    rigour = 'proven'
    files = ('generate.py', 'curated_values.py', 'scorer.py', 'checks.py',
             'selection.md', 'checks-evidence.json')

    def enumerate(self, limit=None):
        kind = KINDS[self.table]
        limit = (6 if kind in ('erf', 'erfc') else 20) if limit is None else limit
        if limit < 1:
            raise ValueError('limit must be positive')
        if kind != 'ci':
            yield {'x': '0'}
        prefixes = {'ai': 'ai-prime-zero-', 'ai_prime': 'ai-zero-',
                    'bi': 'bi-prime-zero-', 'bi_prime': 'bi-zero-'}
        for index in range(1, limit + 1):
            if kind in prefixes:
                identity = prefixes[kind] + str(index)
            elif kind == 'si':
                identity = '%d*pi' % index
            elif kind == 'ci':
                identity = '%d*pi/2' % (2 * index - 1)
            elif kind == 'fresnel_s':
                identity = sqrt_identity(2 * index)
            elif kind == 'fresnel_c':
                identity = sqrt_identity(2 * index - 1)
            else:
                identity = '%d/sqrt(2)' % index
            yield {'x': identity}

    def value(self, params, digits):
        identity = str(params['x'])
        kind = KINDS[self.table]
        with precision(working_precision(digits)):
            argument = argument_for(identity)
            value = evaluate(kind, argument)
            if value.is_exact():
                if not value == 0 and not value == 1:
                    raise ArithmeticError('unexpected exact value')
                number = int(value == 1)
            else:
                if value.rel_accuracy_bits() < math.ceil((digits + 10) * math.log2(10)):
                    raise ArithmeticError('insufficient certified accuracy')
                number = as_interval(value)
            result = {'number': number}
            if identity not in ('0', '1', '2', '3', '4', '5', '6'):
                result['param-latex'] = display(identity)
            if identity != '0':
                result['comment'] = 'The argument is specified by CITE{argument-selection}.'
            return result


def run(generator):
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument('--fill', action='store_true')
    parser.add_argument('--remove-unselected', action='store_true')
    arguments = parser.parse_args()
    if arguments.fill:
        print(generator.publish(removing=arguments.remove_unselected,
                                message='Recompute intrinsic special-function arguments'))
    else:
        report = generator.verify(sample=None)
        print(report)
        raise SystemExit(0 if report.ok else 1)