Every rule so far had one knob. Real rules have many: a house price responds to floor area and distance to the station; a pixel’s class responds to every other pixel. This chapter grows the guess from one number to three — and the lesson is how little that changes. The loop of chapter 3 survives untouched; what grows is the shape of the storage, and Mica keeps that shape in the type, where the compiler can check it before anything runs.
The working file is
examples/ManyKnobs
— run it with make -C examples/ManyKnobs run.
A rule with three knobs
The observations now come from y = 1.5·x₁ + 0.25·x₂ + 2: two
influences with a weight each, plus a bias — the base level the rule
answers when every influence is zero. The guess is a weight vector and
a bias scalar:
w := (0.0, 0.0); { the literal fills the tracked vector's value half }
b := 0.0;and one verb pairs every weight with its influence in a single stroke — the dot product:
x = (x₁, x₂) the data: one observation
w = (w₁, w₂) the guess: one weight per influence
Dot(w, x) + b = w₁·x₁ + w₂·x₂ + b the prediction, one number againDot is the workhorse of everything that follows — a layer of a neural
network is dot products in bulk, and attention is dot products asking
questions. Its shape law is Mica’s own: the vector lengths live in the
types, so a dot product over mismatched lengths is refused at build,
not discovered mid-training. The vectors and matrices
chapter owns that story in full.
The window grows, the loop does not
Four observations, four recorded misses, one loss — chapter 3’s window
with Dot inside it:
tape
e1 := Dot(w, x1) + b - 4.0;
e2 := Dot(w, x2) + b - 5.0;
e3 := Dot(w, x3) + b - 3.0;
e4 := Dot(w, x4) + b - 5.5;
loss := e1 * e1 + e2 * e2 + e3 * e3 + e4 * e4;
end;
Backward(loss);
w := w - Rate * Gradient(w);
b := b - Rate * Gradient(b);Gradient(w) now answers a whole vector — one blame per weight,
from the same single backward walk; the update subtracts it in one line
of vector arithmetic. Notice what fan-out did quietly: w appears in
all four recorded predictions, so each weight’s gradient already sums
its blame across the whole batch. Nothing needed to be said.
The run
the descent
round 1: loss 80.250000
round 2: loss 47.894531
round 10: loss 2.754853
round 100: loss 0.041051
round 2000: loss 0.000000
the learned rule: y = 1.500000·x1 + 0.250000·x2 + 1.999999 (made by 1.5, 0.25, 2)The machine recovers the rule that made its observations — two weights exact to six places, the bias one step of rounding away. It is worth pausing on the rounds column: three knobs took two thousand rounds where one knob took six. Knobs interact — stepping one changes what the others should be — and the plain gradient step handles that interaction slowly. The optimization chapter returns to exactly this, and makes it fast.
What to take forward
The learning loop is now complete and final: a tracked guess of
whatever shape, a recorded loss, Backward, a step. From here to the
GPT, that loop never changes again — every remaining chapter only makes
the forward story richer. The next enrichment is forced by an honest
wall: some patterns no weighted sum can express, no matter how its knobs
are set. The machine needs a bend — the next chapter’s
subject.