back to table · edit · history · where entries came from · files · download
8303 bytes, as of the version from 2026-10-28 23:31. Recorded here, not run.
"""Independent exact and interval checks for T593.
sage -python checks.py
sage -python checks.py stored-document.json
No write requests. The independent polynomial comes from enumeration of the
cycle space, not the row transfer in generate.py. Direct spin enumeration
checks all rectangles with at most 16 sites. All 15 rectangles are checked.
The JSON report printed on stdout is an artifact, not a generator input.
"""
import numberdb.sage as numberdb
from sage.rings.integer_ring import ZZ
from sage.rings.rational_field import QQ
from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
from sage.rings.number_field.number_field import QuadraticField
from sage.rings.real_arb import RealBallField
from collections import Counter
from itertools import product
from pathlib import Path
import json
import sys
import time
from generate import SpecificHeats, cut_counts, heat_from_counts
def grid_edges(m, n):
return [(i*n+j, (i+1)*n+j) for i in range(m-1) for j in range(n)] + [
(i*n+j, i*n+j+1) for i in range(m) for j in range(n-1)]
def even_counts(m, n):
"""Enumerate all Eulerian edge subsets as XORs of bounded face cycles."""
edges = grid_edges(m, n)
index = {tuple(sorted(e)): i for i, e in enumerate(edges)}
faces = []
for i in range(m-1):
for j in range(n-1):
a, b, c, d = i*n+j, i*n+j+1, (i+1)*n+j+1, (i+1)*n+j
faces.append(sum(1 << index[tuple(sorted(e))]
for e in ((a,b),(b,c),(c,d),(d,a))))
hist = [0]*(len(edges)+1)
mask = 0
hist[0] = 1
for k in range(1, 1 << len(faces)):
mask ^= faces[(k & -k).bit_length()-1]
hist[mask.bit_count()] += 1
return hist
def polynomial_from_even(m, n, even):
ring = PolynomialRing(QQ, 'b')
b = ring.gen()
e = 2*m*n-m-n
return QQ(2)**(m*n-e) * sum((ZZ(c)*(1-b)**j*(1+b)**(e-j)
for j,c in enumerate(even) if c), ring.zero())
def brute_spin_counts(m, n):
edges = grid_edges(m, n)
out = Counter()
for spins in range(1 << (m*n)):
d = sum(((spins >> a) ^ (spins >> b)) & 1 for a,b in edges)
out[(spins.bit_count(), d)] += 1
return dict(out)
def bivariate_transfer(m, n):
"""Separate full P(z,b) transfer for the family polynomial identities."""
choices = list(product((0,1), repeat=m))
states = {}
for row in choices:
states[row] = {(sum(row), sum(a != b for a,b in zip(row,row[1:]))): 1}
for _ in range(n-1):
new = {}
for row in choices:
target = Counter()
inside = sum(a != b for a,b in zip(row,row[1:]))
for prev, coeffs in states.items():
extra = inside + sum(a != b for a,b in zip(row,prev))
for (k,d), count in coeffs.items():
target[(k+sum(row),d+extra)] += count
new[row] = dict(target)
states = new
total = Counter()
for coeffs in states.values():
total.update(coeffs)
return dict(total)
def independent_heat(poly, m, n, precision=600):
"""Exact Q(sqrt(2)) moments in B=E-2d, then a single ball log."""
field = QuadraticField(2, 's')
s = field.gen()
b = s-1
e = 2*m*n-m-n
moments = [sum((field(c)*(e-2*d)**r*b**d for d,c in enumerate(poly)),field(0))
for r in range(3)]
variance = moments[2]/moments[0] - (moments[1]/moments[0])**2
rb = RealBallField(precision)
coefficients = list(variance)
v = rb(coefficients[0]) + rb(coefficients[1])*rb(2).sqrt()
k = (rb(2).sqrt()+1).log()/rb(2)
result = k*k*v/rb(m*n)
assert result.is_finite() and result > 0
assert result.accuracy() > 500
return result, [str(x) for x in coefficients]
def known_controls():
assert even_counts(2,2) == [1,0,0,0,1]
assert cut_counts(2,2) == (2,0,12,0,2)
assert brute_spin_counts(2,2) == {(0,0):1,(1,2):4,(2,2):4,(2,4):2,(3,2):4,(4,0):1}
assert list(polynomial_from_even(2,2,even_counts(2,2))) == [2,0,12,0,2]
# One bond: independent thermodynamics gives K^2 sech(K)^2/2.
rb = RealBallField(600)
k = (rb(2).sqrt()+1).log()/rb(2)
bond = k*k/(2*k.cosh()**2)
observed = heat_from_counts((ZZ(2),ZZ(2)),1,2,100)
assert observed.is_finite() and observed.accuracy() > 380
assert observed.overlaps(bond)
# At K=0 the independent spin measure gives Var(B)=E and <B>=0.
def stored_records(document):
block = document.get('Numbers', [])
if isinstance(block,list):
return {(int(x['params']['m']),int(x['params']['n'])): x['number'] for x in block}
result={}
for m, row in block.items():
if not isinstance(row,dict): continue
for n, entry in row.items():
if isinstance(entry,dict) and 'number' in entry:
result[(int(m),int(n))] = entry['number']
return result
def decimal_bounds(text):
# Decimal text means one unit of uncertainty in the last written place.
s=text.strip().lower(); mant,exponent=(s.split('e')+[None])[:2] if 'e' in s else (s,'0')
exponent=int(exponent or 0)
if '.' in mant:
whole,frac=mant.split('.'); scale=QQ(10)**(exponent-len(frac))
mid=ZZ(whole+frac)*scale
else:
scale=QQ(10)**exponent; mid=ZZ(mant)*scale
return mid-scale,mid+scale
def main():
started=time.monotonic()
known_controls()
gen=SpecificHeats()
stored=stored_records(json.loads(Path(sys.argv[1]).read_text())) if len(sys.argv)>1 else None
expected=[(p['m'],p['n']) for p in gen.enumerate()]
if stored is not None:
assert list(stored)==expected
rows=[]
from numberdb._write import to_text
for p in gen.enumerate():
m,n=p['m'],p['n']; N=m*n; E=2*N-m-n
counts=cut_counts(m,n)
even=even_counts(m,n)
poly=polynomial_from_even(m,n,even)
assert list(poly)==list(counts)
assert sum(even)==2**((m-1)*(n-1))
assert sum(counts)==2**N and counts[0]==counts[-1]==2
assert list(counts)==list(reversed(counts))
assert sum(c*(E-2*d) for d,c in enumerate(counts))==0
assert sum(c*(E-2*d)**2 for d,c in enumerate(counts))==E*2**N
full=bivariate_transfer(m,n)
assert [sum(c for (k,d),c in full.items() if d==j) for j in range(E+1)]==list(counts)
assert all(full.get((N-k,d),0)==c for (k,d),c in full.items())
assert {k:c for (k,d),c in full.items() if d==0}=={0:1,N:1}
assert [sum(c for (k,d),c in full.items() if k==j) for j in range(N+1)]==[int(ZZ(N).binomial(j)) for j in range(N+1)]
if N<=16: assert full==brute_spin_counts(m,n)
actual=gen.value(p,100)
independent,variance=independent_heat(poly,m,n)
assert actual.is_finite() and actual.accuracy()>370
assert actual.overlaps(independent)
# The derivative formula in the document must give the same enclosure.
rb=RealBallField(600); b=rb(2).sqrt()-1; k=(rb(2).sqrt()+1).log()/2
formula=4*k*k/(m*n)*((b*poly.derivative()(b)+b*b*poly.derivative(2)(b))/poly(b)-(b*poly.derivative()(b)/poly(b))**2)
assert formula.is_finite() and formula.accuracy()>500
assert formula.overlaps(independent)
text=stored[(m,n)] if stored is not None else to_text(actual,100)
lo,hi=decimal_bounds(text)
# Strict containment, not merely overlap with a rounded midpoint.
assert independent.lower()>lo and independent.upper()<hi
# Rotation is checked against the generator in the other direction.
assert counts==cut_counts(n,m)
rows.append({'m':m,'n':n,'number':text,'accuracy_bits':int(actual.accuracy()),
'variance_B_exact_coefficients':variance,'cut_counts':[int(c) for c in counts],
'even_counts':even,'direct_spin_enumeration':N<=16,
'independent_enclosure_contained_in_written_interval':True})
print('passed %s x %s'%(m,n),file=sys.stderr,flush=True)
print(json.dumps({'table':'T593','all_passed':True,'entries':len(rows),'stored_readback':stored is not None,
'minimum_accuracy_bits':min(r['accuracy_bits'] for r in rows),'seconds':time.monotonic()-started,
'rows':rows},indent=2))
if __name__=='__main__': main()