StoryLM-0 decode slice. Not a Phase 1 exit-criteria replacement.
Not a new primitive. Uses the storylm0_forward.kina algebra
at a static T = 36, Section 17 argmax, and Section 18
Gumbel-max. Tokenization stays on the host.
1. What this section commits to
Companion 47’s first generation acceptance test asks for a
fixed tiny checkpoint, a prompt of token ids, 32 greedy
tokens, a second seeded sample run, and the causal-leak
check. Sections 20–22 stop at three generated tokens and
T_max = 5. Polymorphic S and in-place writes are still
unspecified. This section locks that test without those
features.
The instance is a host loop around one static forward:
T = 36(P = 4prompt tokens plus 32 generated).- The host loads a versioned fixture (NPZ weights + JSON vocab) and holds the length-36 token buffer.
- Each step calls the same compiled forward, reads the last
live logit row, and applies
argmaxor Section 18 Gumbel-max. - Unused suffix ids are the pad class
0. Under the additive causal mask they are never keys for an earlier query.
There is no generate keyword, no in-graph PRNG, and no
write into a T_max cache.
2. Why not a primitive
gather, rank-3 / rank-4 matmul, softmax, broadcast,
argmax, log, and neg are already specified. Thirty-two
static-S decode clones would repeat Section 20 without a
polymorphic calling convention. A fused rollout op would
invent a token clock and a checkpoint ABI.
Re-forwarding the existing causal graph at a compile-time
T is the functional encoding of “generate 32 tokens from a
frozen tiny checkpoint.” The host mutates a NumPy id buffer;
Kina functions stay pure.
3. Operands (numeric instance)
Widths match storylm0_forward.kina except the time axis:
B = 1, T = 36, H = 2, Dh = 2, D = 4, V = 8.
Host tensors. Prompt [1, 4, 2, 6] (same ids as the
forward numeric check). Pad id 0.
| Name | Type | Role |
|---|---|---|
token_ids |
Tensor[I64, [1, 36], @host] |
prompt ++ pad / generated |
token_embedding |
Tensor[F32, [8, 4], @host] |
fixture embed |
causal_mask |
Tensor[F32, [1, 1, 36, 36], @host] |
additive causal mask |
scale |
Tensor[F32, [1, 1, 1, 1], @host] |
1/sqrt(Dh) |
wq wk wv wo |
Tensor[F32, [4, 4], @host] |
fixture projections |
lm_head |
Tensor[F32, [4, 8], @host] |
fixture vocabulary |
Forward result: logits : Tensor[F32, [1, 36, 8], @host].
Row decode (one live site):
| Name | Type | Role |
|---|---|---|
row |
Tensor[F32, [1, 1, 8], @host] |
host-sliced last live logits |
u |
Tensor[F32, [1, 1, 8], @host] |
host Uniform(ε, 1-ε) |
temperature |
Tensor[F32, [1, 1], @host] |
T = 0.8 |
greedy_row is Section 17 argmax on axis 2:
Tensor[I64, [1, 1], @host]. sample_row is Section 18
Gumbel-max on that row.
Weight broadcast is [D, D] → [1, D, D] and
[D, V] → [1, D, V], same as Section 5. Batched
[1, 36, 4] @ [1, 4, 4] is existing rank-3 matmul. Do
not broadcast weights to [1, 36, 4]; T ≠ D.
4. Operational meaning
tokens = prompt ++ [pad] * 32 # length 36
for s in 0 .. 31:
logits = storylm0_rollout(tokens, …) # [1, 36, 8]
row = host slice logits[:, 3+s, :] # [1, 1, 8]
t = greedy_row(row) # or sample_row
tokens[4 + s] = t
sequence = tokens # 36 class ids
The first step reads the last prompt site (index 3) and
writes index 4. Later steps read the token just written.
Pad ids sit only in the causal future of the live prefix, so
logits[:, 0:4, :] on the initial padded buffer must match
storylm0_forward on [1, 4, 2, 6].
Changing a future id must not alter earlier logit rows. Two greedy runs on the same fixture must match. Two sampled runs that reuse the same Uniform stream must match.
Weights are the Section 5 patterned recipe, stored as a host fixture. They are not trained.
5. Typing (derived)
Each line is existing gather / broadcast / matmul / reshape /
transpose / softmax / argmax / log / neg / div /
add typing. T = 36 is static. The token buffer’s type
does not change across host steps.
6. Surface instance
storylm0_rollout.kina binds greedy_row, sample_row,
and storylm0_rollout. @main calls the forward with host
placeholders. The numeric check loads those fns from the
same .so.
Fixture (host-owned, not a compiler ABI):
tests/compiler/fixtures/storylm0/
manifest.json format storylm0-checkpoint-v0
checkpoint.npz embed, wq, wk, wv, wo, lm_head, scale
vocab.json eight class names; index 0 is <pad>
No new tokens. The loop sits outside any reverseMode
function. @mesh does not appear.
7. Tests
storylm0_rollout.kina— §6 program; golden accepted bymlir-opt;storylm0_rolloutresult istensor<1x36x8xf32>.scripts/validate_rollout.pyloads the fixture, compiles the example to a.so, and checks (a) the padded prompt prefix matches theT = 4forward, (b) 32 greedy tokens, twice, match NumPy / Kinaargmax, (c) 32 sampled tokens, twice, match Section 18 on aRandomState(42)Uniform stream atT = 0.8, and (d) the causal non-leak on a changed last id (NumPy; the same algebra in PyTorch when present).
8. Out of scope
- Growing
Sinsidewhile. - In-place write, scatter-assign, or a write index.
- Thirty-two static-S KV decode clones, or polymorphic
S. - A production checkpoint / tokenizer ABI. The trained toy fixture is Section 27.
- Trained weights or a real corpus beyond Section 27’s v1.
- Top-k / top-p.
- Device forward or device
argmax. - Differentiating through the loop.
- Phase 1 exit criteria.
9. Open issues
- Prefill-
Tfamily. One cache-returning forward per prompt length is still the Section 21 stance. - In-place write / polymorphic
S. Still unspecified. - Top-k / top-p. Need a specified sort or prefix selection.
- Trained fixture. Specified in Section 27. The patterned v0 fixture remains this section’s regression input.