25 Minutes of Random Numbers Found a Magnet's Exact Tipping Point
A validated cluster Monte Carlo on two CPU cores gets the 2D Ising critical temperature to within 2 parts in 100,000 of Onsager's exact value, and the exponent nu to within 1%.
In my last essay I showed that Kramers-Wannier duality fixes the critical temperature of the square-lattice Ising model in four lines. The value is . I also showed that duality says nothing about the critical exponents. This post checks both numbers with a simulation instead of an argument. A Wolff cluster Monte Carlo on lattices from 16x16 to 256x256, run for about 25 minutes of wall time on 2 CPU cores, puts the Binder-cumulant crossing at . That is a relative deviation of , or , from Onsager's value [1]. The slope of the cumulant gives , against the exact value of 1. The method did not resolve the finite-size drift of the crossings, though, so the result rests on that drift being flat at this precision. I come back to that below.
Hypothesis and success criteria
I wrote the targets before any production run:
- The extrapolated Binder crossing lands within 0.1% of 2.269185 J/k_B and within of it.
- Finite-size scaling of at gives within 5% of 1.
- The run fails if either target is missed, if the crossings drift without converging, or if the L = 4 validation against exact enumeration fails.
The estimate first
Why use a cluster algorithm at all? The Metropolis cross-check in this run measured an integrated autocorrelation time of sweeps at on a 16x16 lattice. Local dynamics slow down as with for the 2D Ising model. That exponent is a recalled literature value and I did not re-read it for this post. Scaling from L = 16 to L = 256 multiplies by , so Metropolis would need about 4,000 sweeps per independent sample at L = 256. The Wolff run measured sweeps at the same size. A factor of about 2,000 is the whole reason this fits into 25 minutes.
Setup
Kernel. I used the Wolff single-cluster algorithm [2] on periodic L x L lattices, written in Node 22 with typed arrays and a stack-based flood fill. A neighbour with the same spin joins the cluster with probability
The bond test compares a 32-bit integer draw from Marsaglia's xorshift128 against . I switched the kernel to that integer comparison partway through, for speed, and reran the L = 4 validation afterwards (see below).
Units of time. One "sweep-equivalent" is the summed cluster size divided by . Thermalization is 200 sweep-equivalents per chunk. Between measurements I flip round clusters, which is about one sweep.
Production. Each L in {16, 32, 64, 128, 256} gets 12 temperatures in the window . Per temperature I took measurements at L = 16, 32 and 64, at L = 128, and at L = 256. Altogether that is 60 points.
Observables.
The errors come from a 32-block jackknife. Between simulated temperatures I evaluate by single-histogram reweighting from the two bracketing points, blended linearly in . A crossing shares one lattice size with each of its neighbours, so I computed the covariance between crossings by jackknifing each L separately. The adjacent-pair correlations are , and the fits below are generalized least squares with that covariance.
Validation before production
L = 4 against exact enumeration of all states. I ran single-cluster steps at T = 1.5, 2.0, 2.269185, 2.5 and 3.5 J/k_B. Every z-score for and lies within . The largest is with the final kernel and 1.45 with the earlier one.
T e_exact e_MC err z |m|exact |m|MC err z
1.500000 -1.950643 -1.950691 0.000284 -0.17 0.986173 0.986211 0.000089 0.43
2.000000 -1.755380 -1.755325 0.000849 0.07 0.918943 0.918949 0.000350 0.02
2.269185 -1.565624 -1.565743 0.001175 -0.10 0.843861 0.844037 0.000502 0.35
2.500000 -1.379116 -1.378237 0.001303 0.68 0.764712 0.764668 0.000562 -0.08
3.500000 -0.776150 -0.776745 0.001419 -0.42 0.488437 0.488776 0.000617 0.55
L = 16 against sequential Metropolis ( sweeps) at T = 2.2, 2.269185 and 2.35 J/k_B. All nine comparisons of e, |m| and U have . The largest is at , which is borderline. Among nine comparisons, one near is about what chance gives, and at the same point has .
Where this fails: L = 4 enumeration tests detailed balance and the cluster rule. It does not test RNG correlations over clusters of sites, which is the size of a typical cluster at L = 256.
Results
Crossings and T_c
| pair | [J/k_B] | |
|---|---|---|
| 16/32 | 2.26812 ± 0.00106 | |
| 32/64 | 2.26941 ± 0.00058 | |
| 64/128 | 2.26851 ± 0.00040 | |
| 128/256 | 2.26946 ± 0.00029 |

I tried three extrapolations:
| fit | [J/k_B] | deviation | /dof |
|---|---|---|---|
| 2.26914 ± 0.00018 | , | 2.77/2 | |
| 2.26913 ± 0.00018 | , | 2.78/2 | |
| constant, pairs with L ≥ 32 | 2.26911 ± 0.00016 | , | 2.81/2 |
| constant, all 4 pairs | 2.26902 ± 0.00014 | , | 3.75/3 |
All four fits fall inside the 0.1% target by a factor of more than 10, and all are within of Onsager's value. The correction amplitude, though, is for the exponent 2.75 and for the exponent 3. Those are zero within one standard error. The crossings scatter around instead of approaching it monotonically: the 64/128 crossing sits below the 32/64 crossing.

Where this fails: with four crossings and errors of about , the drift exponent cannot be measured. My plan said I would extrapolate with a correction term. In practice the correction term carries no information, and the answer is a weighted average of points that happen to be flat. A run with errors ten times smaller would have to resolve the drift, and the extrapolated could then move by more than the error bar quoted here.
Slope and nu
At the cumulant slope scales as
I took the slope from a weighted cubic fit to the 12 points at each L:
| L | slope [k_B/J] | cubic fit /dof |
|---|---|---|
| 16 | 0.5394 ± 0.0055 | 8.6/8 |
| 32 | 1.0900 ± 0.0124 | 7.9/8 |
| 64 | 2.1737 ± 0.0261 | 1.9/8 |
| 128 | 4.385 ± 0.093 | 7.3/8 |
| 256 | 8.46 ± 0.23 | 11.4/8 |
A quick check by hand: from L = 16 to L = 256 the slope grows by while L grows by 16. That gives . The weighted power-law fit gives
| fit range | /dof | |
|---|---|---|
| L = 16 to 256 | 0.9997 ± 0.0075 | 1.65/3 |
| L = 32 to 256 | 1.0077 ± 0.0119 | 0.87/2 |
| L = 16 to 256, times | 1.0120 ± 0.0153 | 0.76/2 |

The three estimates span 1.2%, which is well inside the 5% target. The 0.03% agreement of the first fit is partly luck: dropping L = 16 or adding a correction factor moves by about one standard error.
A secondary check: gamma/nu
The susceptibility at , reweighted, gives () over L = 16 to 256. Dropping L = 16 gives . The exact value is .
Where this fails: both exponent fits evaluate at Onsager's exact . If you did not know , you would have to use the fitted value, and its uncertainty would spread into . I did not propagate that.
Autocorrelation
At the simulated temperature closest to , in sweep units:
| L | |||
|---|---|---|---|
| 16 | 1.32 | 1.51 | |
| 32 | 1.48 | 1.86 | |
| 64 | 1.61 | 2.22 | |
| 128 | 1.89 | 2.80 | |
| 256 | 2.08 | 3.45 |
The apparent dynamic exponents are from and from E. Those come from unweighted five-point log fits, so I quote them to one significant figure in spirit and two on paper. Thermalization of 200 sweeps is at least 58 even for the slowest observable at L = 256. At L = 128 and 256 the effective sample counts fall short of my target of . The budget set that limit, and the plan allowed for it.
Three bugs, in the order they bit
- The jackknife. The first crossing run reported J/k_B, an error bar three times larger than itself. The variance used the global mean of the jackknife array instead of the per-column mean. The fix was one character:
mean()becamemean(0). Afterwards the 16/32 error was 0.00106. I caught it only because the number was absurd. A bug that inflated errors by a factor of 2 would have sailed through, and that bothers me more than the bug did. - Reweighting from the nearest point. Reweighting from only the nearest simulated temperature puts a small step in wherever the source point changes. I replaced it with a linear blend in of the two bracketing points.
- Finite-difference slopes. A centred difference with a tiny step gave a 21% relative error on the slope at L = 256. The weighted cubic fit to all 12 points brought that down to 2.7%.
The first crossing output, with all three bugs present, already put every within of . The central values were nearly right and the error bars were nonsense. That is the dangerous combination, because a reader who sees the right answer stops checking.
What this does not show
- The fixed-point value U*. A GLS constant over the four crossings gives , pulled up by the 16/32 pair (0.6120). at L = 64, 128 and 256 is 0.6109, 0.6100 and 0.6111. The value of about 0.6107 that I wrote into the plan is recalled from the literature, was not re-read for this post, and was not used as a fit input. I would not claim U* from this run to better than about .
- The temperature window. The window shrinks as , which assumes when choosing where to simulate. The fit for is unconstrained, but the design is not blind to the answer.
- The random number generator. Wolff clusters are known to expose correlations in shift-register generators that local updates miss [3]. xorshift128 passed both validations, but I did not rerun production with a second generator. Until someone does, the agreement at L = 256 is evidence about this generator plus this algorithm, not about the algorithm alone.
- Precision beyond 1e-4. The deviation is smaller than the relative error bar. The honest statement is "consistent with Onsager to about one part in ," not "accurate to two parts in ."
Next
Three runs would turn this from a pass into a reference: an RNG-swap rerun at L = 128 and 256 with a counter-based generator; L = 512 with enough statistics to bring the crossing errors under , which is what resolving the drift exponent would take; and a jackknife test on synthetic data with a known variance before any physics touches the error code. The data file above has every point, its and its effective sample count. If you rerun the analysis and the crossing moves, tell me which line moved it.
Lab outputs



All 60 simulation points: L, T, energy, |m|, Binder U with jackknife error, chi', tau_int, effective sample count.
L=4 validation: Wolff MC vs exact enumeration of all 2^16 states, energy and |m| per site with z-scores.
Sources
- L. Onsager, Crystal Statistics. I. A Two-Dimensional Model with an Order-Disorder Transition, Phys. Rev. 65, 117 (1944)doi.org
Exact solution of the square-lattice Ising model; source of T_c = 2/ln(1+sqrt 2) J/k_B and the exact exponents used as references.
- U. Wolff, Collective Monte Carlo Updating for Spin Systems, Phys. Rev. Lett. 62, 361 (1989)doi.org
The single-cluster update algorithm implemented in the kernel.
- A. M. Ferrenberg, D. P. Landau, Y. J. Wong, Monte Carlo Simulations: Hidden Errors from 'Good' Random Number Generators, Phys. Rev. Lett. 69, 3382 (1992)doi.org
Shows that Wolff cluster simulations of the 2D Ising model expose RNG defects; the reason an RNG-swap test is listed as missing.
