Task 3 of 6
Now the dynamics. The Metropolis rule is two lines: work out what
flipping this spin would cost; if the cost is zero or negative, flip it; if it is positive,
flip it anyway with probability exp(−ΔE / T). That exponential is the whole of
temperature — near T = 0 an expensive flip essentially never happens and the
lattice freezes into agreement; at large T almost everything is accepted and the
lattice is noise. You have both halves already: task 1's cost and task 2's u.
The obvious GPU move is to do all 16,384 at once, and it looks watertight. Every thread reads the old lattice and writes only its own cell, so there is no memory race — nothing is overwritten while something else is reading it. The race is in the physics. Each spin computed its cost on the assumption that its neighbours would hold still, and they did not. Two neighbours that agree share one bond; each one separately prices the cost of breaking it; both flip; the bond is not broken at all, and both of them paid for a change that never happened.
Don't take it on trust — measure it. The energy per spin is
−½ · mean(s · neighbour sum), the same five reads as task 1 assembled differently,
with the ½ because each bond is counted once from each end. It runs from
−2 (perfectly aligned) through 0 (random) to +2 (a
perfect checkerboard, every neighbour disagreeing). Metropolis at T = 1.5 should
walk downhill from a random start. Run it all-at-once and watch which way it actually goes.
bondEnergy kernel and the
meanOf helper, then read the energy the prewired all-at-once loop prints.-0.5 * s[y][x] * (neighbour sum), wrapping as in task 1meanOf(grid) averages all 128 × 128 cellsnaiveSweep kernel and its 30-sweep loop as they areThe bond between two neighbours belongs to both of them. If every cell claimed the
full −s · (neighbour sum), summing over the lattice would count each bond
twice, and an aligned lattice would report −4 per spin instead of
−2. Halving each cell's claim fixes it exactly.
Identical neighbour sum, different assembly: the flip cost multiplies it by
2 · s, the energy by −½ · s.
return -0.5 * s[y][x] * sum;bondEnergy(s) hands back an ordinary 2D grid of numbers, so this is
plain JavaScript:
let total = 0;
for (let y = 0; y < 128; y++) {
for (let x = 0; x < 128; x++) total += grid[y][x];
}
return total / (128 * 128);
Totalling a grid on the GPU is the halving ladder Reductions builds; at 16,384 cells the read-back is cheaper than the ladder, so this one stays in JavaScript.
This page is an interactive exercise — the editor, the GPU runner and your saved progress need JavaScript. The text above is the full brief.