# What a Flip Would Cost

*Task 1 of 6 · [The Ising Model: Colour to Break the Race](https://gpu.rocks/learn/ising-model-1f12d841.md) · GPU.js Learn*

A magnet, stripped to almost nothing: a grid of arrows, each one either
**up** (`+1`) or **down** (`−1`), and a single
rule — neighbours would rather agree. Ernst Ising's 1925 model is that sentence and no more,
and it is still the standard proving ground for phase transitions, because it is simple
enough to be solved exactly and rich enough to have one.

Agreement is written as energy. Every neighbouring pair contributes `−s·s'`:
`−1` when the two agree, `+1` when they do not, so the lattice's total
energy falls as more spins line up. Now ask what one spin flipping would do. Its four bonds
all reverse, so the change is

```js
ΔE = 2 · s · (up + down + left + right)
```

and because every spin and every neighbour is `±1`, that has exactly five possible
values: `−8` when all four neighbours disagree with you (flipping is a bargain),
through `0`, up to `+8` when all four agree (flipping is the most expensive
move on the board). This lattice is a **torus** — the right edge is glued to the
left, the top to the bottom — so there are no boundary cells to special-case, and every one of
the 16,384 threads does the identical four reads. Pure gather, the shape Thinking in Parallel
calls the one that always parallelises.

## Figures

- **five neighbourhoods, five prices — and only two of them are ever a gamble** — Five spin neighbourhoods side by side, with four, three, two, one and zero neighbours agreeing with the centre spin, and the resulting flip costs +8, +4, 0, -4 and -8.

## Goal

**Goal:** finish the kernel so cell `[y][x]` holds
`2 · s[y][x] · (sum of its four neighbours)`, with the neighbour indices wrapping
round the lattice.

## Requirements

- Sum the four axis neighbours with wrap-around — no diagonals, no boundary special case
- Wrap by adding the size first: `(x + n - 1) % n`, never `(x - 1) % n`
- Return `2 * s[y][x] * sum`

## Hint 1 — where the 2 comes from

This spin's share of the energy is `−s · (neighbour sum)`. Flip it and the
share becomes `+s · (neighbour sum)`. The difference between those two is
`2 · s · (neighbour sum)` — the bonds do not just vanish, they reverse, so the
change is twice the share, not once.

## Hint 2 — the wrap that bites

`(x - 1) % n` is `-1` at `x = 0`, in JavaScript and in
gpu.js's GLSL alike — both keep the sign of the left operand. Reading
`s[y][-1]` gives you `undefined` on the CPU backend and a zero out of
nowhere on WebGL. Add the size before you subtract:

```js
const left  = s[y][(x + n - 1) % n];
const right = s[y][(x + 1) % n];
const up    = s[(y + n - 1) % n][x];
const down  = s[(y + 1) % n][x];
```

## Hint 3 — the whole body

```js
const sum = s[y][(x + 1) % n] + s[y][(x + n - 1) % n]
          + s[(y + 1) % n][x] + s[(y + n - 1) % n][x];
return 2 * s[y][x] * sum;
```

## Same idea elsewhere

A four-neighbour gather with periodic wrap is one of the most-written kernels there
is — CUDA and WGSL spell it the same way, and a graphics API gives you the wrap for free by
setting a texture's address mode to `repeat` instead of doing modular arithmetic
at all. The five-value structure matters too: a stencil whose result comes from a tiny
discrete set is exactly the case where production Ising codes drop the exponential and look
the answer up in a five-entry table.

## Starter code

```js
// One thread per spin. Each one works out what flipping ITSELF would cost.
const gpu = new GPU({ mode });

const flipCost = gpu.createKernel(function (s) {
  const x = this.thread.x;
  const y = this.thread.y;
  const n = this.constants.size;
  // TODO 1: sum the four neighbours, wrapping round the torus:
  //         s[y][(x + 1) % n], s[y][(x + n - 1) % n],
  //         s[(y + 1) % n][x], s[(y + n - 1) % n][x]
  // TODO 2: return 2 * (this spin) * (that sum).
  return 0;
}, { output: [128, 128], constants: { size: 128 } });

const cost = await flipCost(lattice);
console.log('the spin at [0][0] is', lattice[0][0], '— flipping it would cost', cost[0][0]);
console.log('row 0, first ten costs:', Array.from(cost[0]).slice(0, 10));
```

---

Interactive version: https://gpu.rocks/learn/ising-model-1f12d841/1

[Next task](https://gpu.rocks/learn/ising-model-1f12d841/2.md)
