back to table · edit · history · where entries came from · files · download
8618 bytes, as of the version from 2026-10-25 05:25 (current). Recorded here, not run.
"""Numerical checks for Scaling dimensions of the 3D Ising CFT -- numberdb.org/T473.
sage -python checks.py # local generator, no network/key needed
sage -python checks.py --stored # database, with a draft-readable key
Uses exact rational interval arithmetic, known 2D Ising controls, an intentionally
wrong control, source transcriptions, and independent published computations.
This does not rerun either conformal-bootstrap optimisation or Monte Carlo.
"""
import json
import re
import sys
from fractions import Fraction as F
from pathlib import Path
import numberdb.sage as numberdb
from sage.rings.integer_ring import ZZ
from generate import IsingScalingDimensions, configure_key
from check import exactness, measure, names_its_rings
class Interval:
def __init__(self, lo, hi=None):
self.lo = F(str(lo))
self.hi = self.lo if hi is None else F(str(hi))
assert self.lo <= self.hi
def __add__(self, other):
other = interval(other)
return Interval(self.lo + other.lo, self.hi + other.hi)
__radd__ = __add__
def __neg__(self):
return Interval(-self.hi, -self.lo)
def __sub__(self, other):
return self + -interval(other)
def __rsub__(self, other):
return interval(other) + -self
def __mul__(self, other):
other = interval(other)
corners = [a*b for a in (self.lo, self.hi)
for b in (other.lo, other.hi)]
return Interval(min(corners), max(corners))
__rmul__ = __mul__
def __truediv__(self, other):
other = interval(other)
assert not other.contains(0), 'division interval includes zero'
return self * Interval(1/other.hi, 1/other.lo)
def __rtruediv__(self, other):
return interval(other) / self
def contains(self, value):
return self.lo <= value <= self.hi
def overlaps(self, other):
other = interval(other)
return max(self.lo, other.lo) <= min(self.hi, other.hi)
def endpoints(self):
return [str(self.lo), str(self.hi)]
def interval(value):
return value if isinstance(value, Interval) else Interval(value)
def published(text):
"""Parse the source's parentheses, independently of the generator strings."""
m = re.fullmatch(r'(\d+\.\d+)\((\d+)\)', text)
assert m, text
c = F(m[1]); r = F(int(m[2]), 10 ** len(m[1].split('.')[1]))
return Interval(c-r, c+r)
def written(value):
text = str(value)
if ' +/- ' in text:
c, r = map(F, text.split(' +/- '))
assert r > 0
return Interval(c-r, c+r)
assert '.' not in text and 'e' not in text.lower(), text
return Interval(F(text))
def exponents(sigma, epsilon, d=3):
nu = 1/(d-epsilon)
return {'nu': nu, 'eta': 2*sigma-d+2,
'alpha': 2-d*nu, 'beta': sigma*nu,
'gamma': (d-2*sigma)*nu, 'delta': (d-sigma)/sigma}
def residuals(e, d=3):
a,b,g,de,n,eta = (e[k] for k in ('alpha','beta','gamma','delta','nu','eta'))
return {'Rushbrooke': a+2*b+g-2,
'Widom': g-b*(de-1),
'Fisher': g-n*(2-eta),
'Josephson': 2-a-d*n,
'Griffiths': a+b*(de+1)-2}
def controls():
assert (Interval(-2,3)*Interval(-4,5)).endpoints() == ['-12','15']
assert (Interval(1,2)/Interval(2,4)).endpoints() == ['1/4','1']
assert published('0.518148806(24)').endpoints() == ['259074391/500000000','51814883/100000000']
control = exponents(F(1,8), F(1), d=2)
known = {'alpha':F(0),'beta':F(1,8),'gamma':F(7,4),
'delta':F(15),'nu':F(1),'eta':F(1,4)}
assert control == known
assert all(x == 0 for x in residuals(control, d=2).values())
bad = {k: Interval(v) for k,v in known.items()}
bad['gamma'] = Interval(2)
assert not residuals(bad, d=2)['Fisher'].contains(0)
assert exactness({'bad':1.0}), 'shared check must reject float control'
assert exactness({'good':ZZ(3)}) == []
def normalize_numbers(numbers):
if isinstance(numbers, dict):
out = {k: dict(v) if isinstance(v,dict) else {'number':v}
for k,v in numbers.items()}
else:
out = {v['params']['operator']:dict(v) for v in numbers}
# The document API returns wire strings even for exact integers. Decode
# that exact spelling before invoking the shared Sage-object type check.
# A decimal such as '3.0' deliberately does not qualify.
for entry in out.values():
text = entry['number']
if isinstance(text,str) and re.fullmatch(r'-?\d+',text):
entry['number'] = ZZ(text)
return out
def run(values, companion=None):
controls()
assert names_its_rings(str(Path(__file__).with_name('generate.py'))) == []
assert exactness(values) == []
assert list(values) == ['sigma','epsilon','T','epsilon-prime','sigma-prime']
# Independently read from the two cited primary papers.
expected = {'sigma':'0.518148806(24)','epsilon':'1.41262528(29)',
'epsilon-prime':'3.82951(61)','sigma-prime':'5.262(89)'}
got = {k:written(v['number']) for k,v in values.items()}
for key, text in expected.items():
assert got[key].endpoints() == published(text).endpoints(), key
assert 0 < got[key].hi-got[key].lo <= F(1,5), key
assert got[key].lo >= F(1,2), '3D scalar unitarity bound'
assert str(values['T']['number']) == '3'
assert got['T'].endpoints() == ['3','3']
assert got['sigma'].hi < got['epsilon'].lo < 3
assert 3 < got['epsilon-prime'].lo < got['sigma-prime'].lo
assert got['sigma-prime'].hi < 6
# Check source consistency: Chang2025 Table 1 versus Table 2.
exps = exponents(got['sigma'],got['epsilon'])
exps['omega'] = got['epsilon-prime']-3
for key,text in {'eta':'0.036297612(48)','nu':'0.62997097(12)'}.items():
assert exps[key].overlaps(published(text)), key
for key,res in residuals(exps).items():
assert res.contains(0), key
assert res.hi-res.lo < F(1,1000), 'uninformative residual interval'
# All source error-box corners satisfy the algebraic identities exactly.
for s in (got['sigma'].lo,got['sigma'].hi):
for e in (got['epsilon'].lo,got['epsilon'].hi):
assert all(r == 0 for r in residuals(exponents(s,e)).values())
# Independent finite-size Monte Carlo, Hasenbusch2010 abstract.
mc = {'nu':'0.63002(10)','eta':'0.03627(10)','omega':'0.832(6)'}
for key,text in mc.items():
assert exps[key].overlaps(published(text)), ('Monte Carlo',key)
# A distinct bootstrap extraction, not a rigorous second error bound.
efm = {'epsilon-prime':'3.82968(23)','sigma-prime':'5.2906(11)'}
for key,text in efm.items():
assert got[key].overlaps(published(text)), ('EFM',key)
companion_checks = []
if companion is not None:
# T472 shares some input literature, so this is consistency, not independence.
ising = companion['Numbers']['ising']
for key,x in exps.items():
assert x.overlaps(written(ising[key]['number'])), ('T472',key)
companion_checks.append(key)
stored_exps = {k:written(ising[k]['number']) for k in exps}
assert all(r.contains(0) for r in residuals(stored_exps).values())
centers = {k:(x.lo+x.hi)/2 for k,x in got.items()}
center_exps = exponents(centers['sigma'],centers['epsilon'])
center_exps['omega'] = centers['epsilon-prime']-3
assert all(stored_exps[k].contains(x) for k,x in center_exps.items())
return {'ok':True,'entries':len(values),'controls':'exact 2D Ising and inconsistent Fisher control passed',
'source_transcriptions':4,'exact_stress_tensor':True,
'scalar_unitarity':4,'scaling_relations':list(residuals(exps)),
'source_exponents':['eta','nu'], 'independent_Monte_Carlo':list(mc),
'independent_EFM':list(efm),'companion_T472':companion_checks,
'size':measure(values),'stored_intervals':{k:x.endpoints() for k,x in got.items()},
'derived_exponent_intervals':{k:x.endpoints() for k,x in exps.items()}}
if __name__ == '__main__':
configure_key()
if '--stored' in sys.argv:
values = normalize_numbers(numberdb.table('T473')['Numbers'])
companion = numberdb.table('T472')
else:
gen = IsingScalingDimensions()
values = {p['operator']:gen.value(p,gen.digits) for p in gen.enumerate()}
path = Path(__file__).with_name('T472.json')
companion = json.loads(path.read_text()) if path.exists() else None
print(json.dumps(run(values,companion),indent=2))