#|| The two-dimensional Ising model on an L by L square lattice with periodic
#|| edges: Monte Carlo by Metropolis (checkerboard order) or by Wolff
#|| clusters, the measurements and their error bars, and the exact results
#|| the runs are checked against.
#||
#|| The energy of an arrangement of spins s_i = +1 or -1 is
#|| E = -J sum_<ij> s_i s_j - h sum_i s_i,
#|| the first sum over the 2 N neighboring pairs of N = L * L sites, and an
#|| arrangement occurs with probability proportional to exp(-E / T)
#|| (Boltzmann's constant is 1, so T is in units of J when J = 1).
#||
#|| Every random number is a Philox4x32-10 word (Salmon, Moraes, Dror and
#|| Shaw, SC11) of a counter fixed by where it is used: the sweep, the
#|| row, the sublattice and the site for Metropolis; the cluster and the
#|| draw's position in it for Wolff. The key is (seed, a tag per use). So a
#|| run does not depend on the order the engine visits anything in, and the
#|| interpreter, the native build and the exported library give the same
#|| bits.
#||
#|| Exact references: Onsager's critical temperature and energy per site of
#|| the infinite lattice, Yang's spontaneous magnetization, and Kaufman's
#|| partition function of the finite periodic lattice (Ferdinand and Fisher,
#|| Phys. Rev. 185, 832 (1969), eq. 2.1), which gives the energy and the
#|| specific heat of an L by L lattice at h = 0 with no Monte Carlo at all.
#|| `exact_moments` enumerates all 2^N states of a small lattice (N <= 20).
TWO32 = 4294967296.0
PI = 3.141592653589793
# the key's second word: which use a Philox counter belongs to
TAG_METROPOLIS: Int = 1
TAG_WOLFF: Int = 2
TAG_HOT_START: Int = 3
# the window factor of the automatic windowing for the autocorrelation time:
# sum rho(t) out to the first W with W >= WINDOW_C * tau(W) (Sokal 1997)
WINDOW_C = 6.0
#| Philox4x32-10: the four 32-bit words the counter (c0, c1, c2, c3) maps to
#| under the key (k0, k1), as Random123's philox4x32 computes them (the
#| same code as examples/options/mc.viv).
def philox4x32(c0: Int, c1: Int, c2: Int, c3: Int, k0: Int, k1: Int) {
x0 = c0
x1 = c1
x2 = c2
x3 = c3
ka = k0
kb = k1
for round to 10 {
if round > 0 {
ka = (ka + 2654435769) % 4294967296
kb = (kb + 3144134277) % 4294967296
}
p0 = wrap_mul(3528531795, x0)
l0 = p0 % 4294967296
h0 = (p0 - l0) // 4294967296
if h0 < 0 {
h0 = h0 + 4294967296
}
p1 = wrap_mul(3449720151, x2)