back to table · edit · history · where entries came from · files · download
4088 bytes, as of the version from 2026-10-28 23:47 (current). Recorded here, not run.
"""Specific heats of finite Ising rectangles at critical coupling -- numberdb.org/T593.
Run with SageMath:
sage -pip install numberdb
sage -python generate.py # verify every stored entry
sage -python generate.py --fill # fill existing draft with API key
The model is the simple vertex grid P_m square P_n, with open boundaries,
uniform ferromagnetic coupling, zero field, and K_c=log(1+sqrt(2))/2.
Values are C/(mn k_B), not unnormalized bond-energy variances.
All integer cut counts are recomputed; no attached numeric output is input.
See argument-selection.md for the discrete size family and finite extent.
The 96-bit guard is checked in checks.py, which records retained accuracy.
"""
import numberdb.sage as numberdb
from sage.rings.integer_ring import ZZ
from sage.rings.real_arb import RealBallField
from functools import lru_cache
import os
import sys
MAX_SIDE = 6
GUARD_BITS = 96
@lru_cache(maxsize=None)
def cut_counts(m, n):
"""Coefficients of F(b)=sum_sigma b^(number of disagreeing bonds)."""
m, n = int(m), int(n)
if m < 1 or n < 1:
raise ValueError('Side lengths must be positive integers')
states = 1 << m
inside = [sum(((s >> i) ^ (s >> (i + 1))) & 1
for i in range(m - 1)) for s in range(states)]
current = [{inside[s]: 1} for s in range(states)]
for _ in range(1, n):
following = [{} for _ in range(states)]
for s in range(states):
target = following[s]
for t in range(states):
shift = inside[s] + (s ^ t).bit_count()
for d, count in current[t].items():
target[d + shift] = target.get(d + shift, 0) + count
current = following
edges = 2*m*n-m-n
counts = [0] * (edges + 1)
for row in current:
for d, count in row.items():
counts[d] += count
return tuple(ZZ(c) for c in counts)
def heat_from_counts(counts, m, n, digits):
"""Evaluate 4 K_c^2 Var(d)/(mn), with exact inputs and Arb enclosures."""
rb = RealBallField(numberdb.bits(digits, losing=GUARD_BITS))
b = rb(2).sqrt() - 1
kc = (rb(1) + rb(2).sqrt()).log() / rb(2)
moments = [rb(0), rb(0), rb(0)]
power = rb(1)
for d, count in enumerate(counts):
weight = rb(count) * power
moments[0] += weight
moments[1] += d * weight
moments[2] += (d*d) * weight
power *= b
variance = moments[2]/moments[0] - (moments[1]/moments[0])**2
answer = rb(4)*kc**2*variance/rb(m*n)
if not answer.is_finite() or not answer > 0:
raise ArithmeticError('The heat must have a finite positive enclosure')
return answer
class SpecificHeats(numberdb.Generator):
table = 'T593'
parameters = ('m', 'n')
type = 'R'
digits = 100
rigour = 'proven'
files = ('generate.py', 'checks.py', 'argument-selection.md',
'numerical-evidence.md', 'counts.json', 'checks-result.json',
'skill-evidence.json')
def enumerate(self, max_side=MAX_SIDE):
for m in range(2, max_side + 1):
for n in range(m, max_side + 1):
yield {'m': m, 'n': n}
def value(self, params, digits):
m, n = int(params['m']), int(params['n'])
if not 2 <= m <= n:
raise ValueError('Require 2 <= m <= n')
return heat_from_counts(cut_counts(m, n), m, n, digits)
if __name__ == '__main__':
if os.environ.get('NUMBERDB_KEY_FROM_STDIN') == '1':
token = sys.stdin.read().strip()
if '=' in token and token.split('=', 1)[0].replace('_', '').isalnum():
token = token.split('=', 1)[1].strip().strip('\"\'')
os.environ['NUMBERDB_API_KEY'] = token
g = SpecificHeats()
if '--fill' in sys.argv or os.environ.get('NUMBERDB_PUBLISH') == '1':
print(g.publish(message='Fill 15 critical specific heats with certified 100-digit values',
assisted_by='codex-cli'))
else:
report = g.verify(sample=None)
print(report)
sys.exit(0 if report.ok else 1)