Annealing Code Like Steel
When you heat steel past its critical temperature and cool it slowly, the crystal structure reorganizes. Fast cooling traps defects; slow cooling lets atoms find lower-energy arrangements. Blacksmiths call this annealing.
In 1983, Kirkpatrick, Gelatt, and Vecchi realized this process solves a computational problem: how do you find a global optimum when a greedy search gets stuck in local minima? Their answer: start hot, accept bad moves probabilistically, and cool down slowly.
Here’s simulated annealing finding a minimum in JavaScript. The “temperature” controls how often we accept uphill moves:
function anneal(f, x0, T=100, coolingRate=0.95) {
let x = x0, best = x0;
while (T > 0.01) {
let candidate = x + (Math.random()-0.5) * T;
let delta = f(candidate) - f(x);
if (delta < 0 || Math.random() < Math.exp(-delta/T)) {
x = candidate;
if (f(x) < f(best)) best = x;
}
T *= coolingRate;
}
return best;
}
console.log(anneal(x => (x-3.7)**2 + Math.sin(x*5), 0));
Erlang’s pattern matching makes the acceptance logic clean:
anneal(F, X, T) when T < 0.01 -> X;
anneal(F, X, T) ->
Candidate = X + (rand:uniform() - 0.5) * T,
Delta = F(Candidate) - F(X),
Accept = (Delta < 0) orelse (rand:uniform() < math:exp(-Delta/T)),
Next = if Accept -> Candidate; true -> X end,
anneal(F, Next, T * 0.95).
The algorithm accepts worse solutions early (high temperature) to explore broadly, then becomes pickier as it cools. It’s stochastic, not deterministic—two runs yield different paths. The cooling schedule matters: too fast and you freeze in a local minimum; too slow and you waste cycles. Same trade-off the blacksmith faces at the forge.