Why it is shaped this way
Several exports, one spec
Train and sample share weights. They should not compute the same nodes.
The same model can train on a batch and generate a token. Those two jobs share weights. They should not compute the same nodes. Train does not need the sampling head. Decode does not need the full window.
The list of values this run may read is what makes that split real.
The decision#
Observability is a license: say what you want to read, once. Everything derived — the slow run, the plan, fusion, emission — honors that list. A read off the list fails loudly. A name you asked to see, sitting inside a chain that could have been fused, blocks the fusion — unless the match names it as a result it writes back. Optimization knows what must survive and what it may drop.
What it opened#
0 Parameter [4, 3] freed after 3 1 Parameter [3] freed after 4 2 Input [2, 4] freed after 3 3 MatMul 2, 0 [2, 3] freed after 5 4 BroadcastAlong 1 axis=0 extent=2 [2, 3] freed after 5 5 Add 3, 4 [2, 3] freed after 11 6 Input [2, 3] freed after 14 7 SumAlong 6 axis=1 [2] freed after 9 8 LogSumExp 5 axis=1 [2] freed after 9 9 Mul 7, 8 [2] freed after 10 10 Sum 9 [] freed after 13 11 Mul 6, 5 [2, 3] freed after 12 12 Sum 11 [] freed after 13 13 Sub 10, 12 [] freed after 15 14 Sum 6 [] freed after 15 15 Div 13, 14 [] kept plan: forward; 16 of 18 nodes evaluated, 1 readable live volume: peak 29 elements at node 3, retain-all 64
0 Parameter [4, 3] freed after 3 1 Parameter [3] freed after 4 2 Input [2, 4] freed after 3 3 MatMul 2, 0 [2, 3] freed after 5 4 BroadcastAlong 1 axis=0 extent=2 [2, 3] freed after 5 5 Add 3, 4 [2, 3] freed after 16 16 LogSoftmax 5 axis=1 [2, 3] freed after 17 17 Exp 16 [2, 3] kept plan: forward; 8 of 18 nodes evaluated, 1 readable live volume: peak 29 elements at node 3, retain-all 53
One graph can export several functions. You record them together. Each run names which export it is.
The names you keep from recording are the ones later code may mention. They are not "compute all of these every time." The training function can skip the sampling head. The decode function can skip the full window.
Printing the plan for another compiler uses the same list, in the same order. The plan is already a closed function with those results. Emission does not invent an output convention. It writes the list down.
Recurrence as feeds is how a decode function carries last step's keys and values. This article is why decode is its own function, not a mode of the full-window one.
Spelled today#
Entry is that list: results, extra values to observe,
memory and numerics choices. network.entry([loss]) binds
it. interpret runs the slow path over just those
ancestors. lower builds the plan. Plan::results is the
list emit_stablehlo returns. Run::of panics on a name
this run did not compute. Network::forward still runs
the whole graph. That is the debug path, not training.
This section may rot; the rest must not.
Not this#
- Guessing the list from buffers that happened to survive. If you did not ask to read it, it is not readable.
- One implied function per graph.
- Treating every name you detached at record time as something this run must compute.
See Observability is a license for the rule, and Fusion raises for the barrier that list is.
topos