checks.py

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

8839 bytes, as of the version from 2026-10-24 23:04. Recorded here, not run.

"""Independent arithmetic and literature checks for numberdb.org/T472.

    sage -python checks.py             # all generated values
    sage -python checks.py --stored    # all values read from the database

An unpublished draft requires NUMBERDB_API_KEY, or NUMBERDB_KEY_FROM_STDIN=1
and the key on stdin. No request here modifies the database. A successful
check is consistency of published estimates, not a new physical measurement.
"""
import numberdb.sage as numberdb
from sage.rings.rational_field import QQ
from sage.rings.real_mpfi import RealIntervalField
from fractions import Fraction
from itertools import product
import json
import os
import sys
from generate import CriticalExponents3D, INPUTS, CLASSES, EXPONENTS
from check import exactness, measure, agrees_with, names_its_rings


def q(s):
    f = Fraction(str(s))
    return QQ(f.numerator)/QQ(f.denominator)


def ends(text):
    c,r = [q(a.strip()) for a in text.split('+/-')]
    assert r > 0
    return c-r,c+r


def interval(text):
    return RealIntervalField(384)(list(ends(text)))


def exponents(yt,yh):
    # Independent RG eigenvalue parameterisation, rather than the generator's
    # formulas in nu, eta or nu, gamma.
    return dict(alpha=2-3/yt,beta=(3-yh)/yt,gamma=(2*yh-3)/yt,
                delta=yh/(3-yh),nu=1/yt,eta=5-2*yh)


def overlap(a,b):
    return not (a[1] < b[0] or b[1] < a[0])


def scaling(row,dimension=3):
    a,b,g,d,n,e=[row[k] for k in ('alpha','beta','gamma','delta','nu','eta')]
    return {'Rushbrooke':a+2*b+g-2,'Widom':g-b*(d-1),
            'Fisher':g-n*(2-e),'Josephson':dimension*n+a-2,
            'critical-isotherm':(dimension-2+e)*d-(dimension+2-e)}


# Other determinations, not the inputs used to construct the table.
INDEPENDENT = {
 'ising': {'nu':'0.63002 +/- 0.00010','eta':'0.03627 +/- 0.00010','omega':'0.832 +/- 0.006'},
 'xy': {'alpha':'-0.0151 +/- 0.0003','beta':'0.3486 +/- 0.0001',
        'gamma':'1.3178 +/- 0.0002','delta':'4.780 +/- 0.001',
        'nu':'0.6717 +/- 0.0001','eta':'0.0381 +/- 0.0002','omega':'0.785 +/- 0.020'},
 'heisenberg': {'alpha':'-0.1336 +/- 0.0015','beta':'0.3689 +/- 0.0003',
               'gamma':'1.3960 +/- 0.0009','delta':'4.783 +/- 0.003',
               'nu':'0.7112 +/- 0.0005','eta':'0.0375 +/- 0.0005'},
 'saw': {'nu':'0.587597 +/- 0.000007','gamma':'1.1566 +/- 0.0010',
         'eta':'0.0310 +/- 0.0007'},
}


def records(stored=False):
    if stored:
        doc=numberdb.table('T472')
        rows=doc['Numbers']
        if isinstance(rows,dict):
            assert list(doc['Parameters'])==['class','exponent']
            return {(cls,exponent):{'number':entry['number'],
                    'comment':entry.get('comment','')}
                    for cls,group in rows.items() for exponent,entry in group.items()}
        assert isinstance(rows,list), 'Unexpected database entry shape'
        return {(r['params']['class'],r['params']['exponent']):
                {'number':r['number'],'comment':r.get('comment','')} for r in rows}
    g=CriticalExponents3D()
    return {(p['class'],p['exponent']):g.value(p,g.digits) for p in g.enumerate()}


def run(stored=False):
    # Known positive and negative controls run before the empirical comparisons.
    control={k:QQ(v) for k,v in dict(alpha=0,beta='1/8',gamma='7/4',
                       delta=15,nu=1,eta='1/4').items()}
    assert all(x==0 for x in scaling(control,2).values())
    assert interval('2.00 +/- 0.01').lower() < 2 < interval('2.00 +/- 0.01').upper()
    assert not overlap(ends('2.00 +/- 0.01'),ends('3.00 +/- 0.01'))
    bad=dict(control);bad['gamma']=QQ(2)
    assert scaling(bad,2)['Rushbrooke'] != 0
    result={'mode':'stored' if stored else 'generated','controls':'passed'}
    values=records(stored)
    assert set(values)==set(product(CLASSES,EXPONENTS))
    assert not exactness(values)
    assert not names_its_rings(os.path.join(os.path.dirname(__file__),'generate.py'))
    result['measurement']=measure(values)
    result['rows']={','.join(k):v['number'] for k,v in values.items()}
    result['scaling_residuals']={}; result['independent_comparisons']=[]; result['legacy_discrepancies']=[]
    result['corner_checks']=0
    for cls in CLASSES:
        row={k:interval(values[cls,k]['number']) for k in EXPONENTS}
        for k,b in row.items():
            assert not any(x.is_NaN() or x.is_infinity() for x in (b.lower(),b.upper()))
            assert b.absolute_diameter()>0 and b.absolute_diameter()<q('0.3')
        # Joint consistency: the exact input midpoint maps into every one of
        # the six stored components simultaneously, not just separate residuals.
        n0,n1=ends(INPUTS[cls]['nu']); assert n0>0
        second='gamma' if cls=='saw' else 'eta'
        s0,s1=ends(INPUTS[cls][second])
        if cls=='saw':
            assert 3*n0-s1>0 and s0>0  # monotonic derivatives have fixed signs
        else:
            assert 1+s0>0 and 2-s1>0
        for n,s in product((n0,n1,(n0+n1)/2),(s0,s1,(s0+s1)/2)):
            yt=1/n
            yh=(3+s/n)/2 if cls=='saw' else (5-s)/2
            expected=exponents(yt,yh)
            for k,v in expected.items():
                low,high=ends(values[cls,k]['number'])
                assert low<=v<=high,(cls,k,str(v),values[cls,k])
                result['corner_checks']+=1
        residues=scaling(row)
        assert all(b.contains_zero() for b in residues.values()),(cls,residues)
        result['scaling_residuals'][cls]={k:str(v) for k,v in residues.items()}
        for key,other in INDEPENDENT.get(cls,{}).items():
            if cls=='heisenberg' and key=='beta':
                assert not overlap(ends(values[cls,key]['number']),ends(other))
                result['legacy_discrepancies'].append({'entry':'heisenberg,beta',
                    'selected':values[cls,key]['number'],'legacy_Campostrini2002':other,
                    'reason':'Different numerical determinations; the published one-error ranges do not overlap. No source error bar has been widened.'})
                continue
            assert overlap(ends(values[cls,key]['number']),ends(other)),(cls,key,other)
            result['independent_comparisons'].append(cls+','+key)
    # The published XY and Heisenberg comparison sets must themselves obey
    # all five relations before being used as checks.
    for cls in ('xy','heisenberg'):
        assert all(x.contains_zero() for x in scaling({k:interval(v) for k,v in INDEPENDENT[cls].items()}).values())
    # Independent Heisenberg conformal bootstrap, Chester et al. (2021), Table 1.
    phi=interval('0.518936 +/- 0.000067')
    singlet=interval('1.59488 +/- 0.00081')
    for k,v in exponents(3-singlet,3-phi).items():
        assert v.overlaps(interval(values['heisenberg',k]['number'])),k
        result['independent_comparisons'].append('heisenberg,bootstrap,'+k)
    # Independent percolation FSS eigenvalues: Xu et al. (2014), Sec. III A.
    n=interval(values['percolation','nu']['number']); eta=interval(values['percolation','eta']['number'])
    assert (1/n).overlaps(interval('1.1413 +/- 0.0015'))
    assert ((5-eta)/2).overlaps(interval('2.52293 +/- 0.00010'))
    result['independent_comparisons']+=['percolation,yt','percolation,yh']
    # SAW correction against an earlier independent Monte Carlo simulation.
    d=interval(values['saw','nu']['number'])*interval(values['saw','omega']['number'])
    assert d.overlaps(interval('0.528 +/- 0.012'))
    result['independent_comparisons'].append('saw,Delta1')
    # Ising cross-table relations checked against the source scaling dimensions,
    # since the family's CFT companion tables are not published in the corpus.
    sig=interval('0.518148806 +/- 0.000000024'); eps=interval('1.41262528 +/- 0.00000029')
    prime=interval('3.82951 +/- 0.00061')
    expected={'nu':1/(3-eps),'eta':2*sig-1,'omega':prime-3,
              'alpha':2-3/(3-eps),'beta':sig/(3-eps),
              'gamma':(3-2*sig)/(3-eps),'delta':(3-sig)/sig}
    for k,v in expected.items():
        assert v.overlaps(interval(values['ising',k]['number'])),k
    result['ising_cft_relations']=7
    result['scaling_relations']=25
    result['independent_comparison_count']=len(result['independent_comparisons'])
    result['limitations']=['No Monte Carlo simulation or conformal bootstrap optimisation was rerun.',
      'Heisenberg and percolation omega were checked against their primary-source text, not independently recomputed.',
      'Physical uncertainty boxes are conditional estimates, not rigorous enclosures or joint confidence regions.']
    result['passed']=True
    return result


if __name__=='__main__':
    if os.environ.get('NUMBERDB_KEY_FROM_STDIN')=='1':
        os.environ['NUMBERDB_API_KEY']=sys.stdin.read().strip()
    print(json.dumps(run('--stored' in sys.argv or os.environ.get('NUMBERDB_STORED')=='1'),indent=2))