checks.py

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

5354 bytes, as of the version from 2026-10-27 19:05. Recorded here, not run.

"""Independent exact checks for numberdb.org/T557.

    sage -python checks.py
    sage -python checks.py --stored

For a private draft use NUMBERDB_API_KEY or NUMBERDB_KEY_FROM_STDIN=1.
NUMBERDB_CHECK_STORED=1 is the wrapper equivalent of --stored.
The independent polynomial construction uses the closed Hermite coefficient
sum and the differential recurrence in n; it imports no arithmetic helpers
from generate.py. The final driver imports the generator only to obtain the
values being tested and to compare their enumeration.
"""
import json
import os
import sys
import time
from functools import lru_cache
from math import factorial

import numberdb.sage as numberdb
from sage.rings.rational_field import QQ
from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
from sage.env import SAGE_VERSION
from check import exactness, agrees_with, measure, names_its_rings

S = PolynomialRing(QQ, 'x')
z = S.gen()


@lru_cache(None)
def independent(m,n):
    """Monic form of Buckingham (2020), equation (3), second recurrence."""
    if n == 0 or m == 0:
        return S.one()
    if n == 1:
        return sum((QQ((-1)**j*factorial(m))/QQ(4**j*factorial(j)*factorial(m-2*j)))*z**(m-2*j) for j in range(m//2+1))
    p=independent(m,n-1)
    numerator=p*p + (p.derivative()**2-p*p.derivative(2))*(QQ(1)/QQ(2*(n-1)))
    q,r=numerator.quo_rem(independent(m,n-2))
    assert r==0, ('independent recurrence division',m,n)
    return q


def fourth_order_residual(p,m,n):
    # Clarkson (2008), equation (5.9); invariant under constant normalization.
    p1,p2,p3,p4=[p.derivative(j) for j in range(1,5)]
    return p*p4-4*p1*p3+3*p2**2+4*z*p*p1-8*m*n*p*p-4*(z*z+2*n-2*m)*(p*p2-p1*p1)


def painleve_residual(p,q,m,n):
    # w=(log(q/p))', alpha=2m+n+1, beta=-2*n^2.
    # p=Hhat_{m,n}, q=Hhat_{m+1,n}.
    a=q.derivative()*p-q*p.derivative()
    b=p*q
    c=a.derivative()*b-a*b.derivative()
    d=c.derivative()*b-2*c*b.derivative()
    alpha=2*m+n+1
    beta=-2*n*n
    return 2*a*d-c*c-3*a**4-8*z*a**3*b-4*(z*z-alpha)*a*a*b*b-2*beta*b**4


def controls():
    assert independent(2,1)==z*z-QQ(1)/2
    assert independent(1,2)==z*z+QQ(1)/2
    assert independent(2,2)==z**4+QQ(3)/4
    assert fourth_order_residual(z,1,1)==0
    assert fourth_order_residual(z+1,1,1)!=0
    assert painleve_residual(S.one(),z,0,1)==0  # w=1/x.
    assert painleve_residual(S.one(),z+1,0,1)!=0
    assert agrees_with({(2,2):z**4+QQ(3)/4},lambda key:independent(*key))==[]
    assert agrees_with({(2,2):z**4+QQ(1)/4},lambda key:independent(*key))


def check_values(values):
    controls()
    assert not exactness(values)
    assert not agrees_with(values,lambda key:independent(*key))
    counts={k:0 for k in ['coefficient_comparisons','degree_monic_parity','transpose_symmetry','recurrence_m','recurrence_n','bilinear_ode','painleve_iv','hermite_specialization']}
    for (m,n),p in values.items():
        counts['coefficient_comparisons']+=1
        assert p.degree()==m*n and p.leading_coefficient()==1
        assert p(-z)==(-1)**(m*n)*p
        counts['degree_monic_parity']+=1
        transpose=values[(n,m)]
        expected=S([coefficient*(-1)**((m*n-k)//2) if (m*n-k)%2==0 else 0 for k,coefficient in enumerate(p.list())])
        assert transpose==expected
        counts['transpose_symmetry']+=1
        delta=p*p.derivative(2)-p.derivative()**2
        assert independent(m+1,n)*independent(m-1,n)==p*p+delta*(QQ(1)/QQ(2*m))
        counts['recurrence_m']+=1
        assert independent(m,n+1)*independent(m,n-1)==p*p-delta*(QQ(1)/QQ(2*n))
        counts['recurrence_n']+=1
        assert fourth_order_residual(p,m,n)==0
        counts['bilinear_ode']+=1
        assert painleve_residual(p,independent(m+1,n),m,n)==0
        counts['painleve_iv']+=1
        if n==1:
            assert p==independent(m,1)
            counts['hermite_specialization']+=1
    return counts


def stored_values():
    document=numberdb.table('T557')
    records=document['Numbers']
    assert isinstance(records,list), 'Expected flat entries'
    return {(int(r['params']['m']),int(r['params']['n'])):S(r['number']) for r in records}


if __name__=='__main__':
    start=time.monotonic()
    from generate import GeneralizedHermite, authenticate
    authenticate()
    g=GeneralizedHermite()
    params=list(g.enumerate())
    expected_keys=[(p['m'],p['n']) for p in params]
    # Independent complete set and independent ordering.
    assert expected_keys==sorted([(m,n) for m in range(1,51) for n in range(1,51) if m*n<=50],key=lambda pair:(pair[0]*pair[1],*pair))
    assert not names_its_rings(os.path.join(os.path.dirname(__file__),'generate.py'))
    readback='--stored' in sys.argv or os.environ.get('NUMBERDB_CHECK_STORED')=='1'
    values=stored_values() if readback else {(p['m'],p['n']):S(g.value(p,100)) for p in params}
    assert list(values)==expected_keys
    counts=check_values(values)
    report={'table':'T557','mode':'stored' if readback else 'local','checks':counts,'controls':'known values and deliberately altered values passed','measurement':measure(values),'sage_version':SAGE_VERSION,'numberdb_version':numberdb.__version__,'elapsed_seconds':time.monotonic()-start}
    print('CHECKS_JSON',json.dumps(report,default=str))
    print('VALUES_JSON',json.dumps([{'params':{'m':m,'n':n},'number':str(p)} for (m,n),p in values.items()]))