Skip to main content

logicaffeine_compile/spatial/
streamed.rs

1//! A model as the RDU would run it: one lowered program per LAYER KIND, and a manifest.
2//!
3//! Two layers whose specs are equal ([`crate::model::spec::Layer`]: the mixer, its rotation and window, the
4//! feed-forward block, the norms) are written by the author as the same program up to their names, so they lower to
5//! the same spatial program — measured field by field, the only differences are name labels. Qwen3.8's 64-layer
6//! forward is not 64 programs; it is two (its Gated DeltaNet layers and its attention layers), Gemma 3's are its
7//! windowed and its global attention, DeepSeek-V2's its dense first layer and its expert layers: each applied layer
8//! after layer with a different set of weights bound each time, followed by the head. This module builds exactly that
9//! for any model the author writes: the kind programs, lowered and certified once each, and a manifest saying which
10//! kind runs at each layer and how that kind's tensor names bind to that layer's tensors.
11//!
12//! Lowering cost is therefore independent of depth — one layer's worth per kind, however many layers the model has —
13//! and so is the artifact a vendor ingests.
14//!
15//! ★ A WEIGHT MOVES AS IT IS STORED. The author declares every projection as a weight the deployment may store Q8_0;
16//! a product over one folds its int8 values and block scales on the chip ([`super::passes::from_q8`]) only where the
17//! checkpoint stores it so ([`Storage`]). A weight stored any other way is read dense, the host filling it through
18//! its binding.
19
20use super::ir::SpatialProgram;
21use super::pipeline::compile_program_to_spatial_tp_agreeing_stored;
22use super::pipeline::{compile_program_to_spatial_agreeing_stored, dense_tables_of};
23use super::target::SpatialTarget;
24use super::tensor_parallel::TpManifest;
25use crate::model::author::{layer_input, stacked_layer_input, TpAuthored};
26use crate::model::program::{written_in, written_split, Cut, Identity};
27use crate::model::bind::{bind, stored_as_q8_planes, Source};
28use crate::model::reference::WeightSource;
29use crate::model::spec::{Ffn, Mixer, ModelSpec};
30use std::collections::BTreeSet;
31
32/// Which of the weights the author declares Q8_0 the checkpoint stores Q8_0.
33#[derive(Clone, Copy)]
34pub enum Storage<'a> {
35    /// Every one: a built-in model's checkpoint, written as the author declares it.
36    AllQ8,
37    /// As this checkpoint stores each ([`stored_as_q8_planes`]), read from its description of the tensor.
38    Checkpoint(&'a dyn WeightSource),
39}
40
41impl Storage<'_> {
42    /// Whether `spec`'s declared weight `var` (`Wq3`) is read as it is stored, Q8_0.
43    pub fn q8(&self, spec: &ModelSpec, var: &str) -> bool {
44        match self {
45            Storage::AllQ8 => true,
46            Storage::Checkpoint(source) => match bind(spec, var) {
47                Ok(Source::Weight(b)) => stored_as_q8_planes(&b, *source).unwrap_or(false),
48                _ => false,
49            },
50        }
51    }
52}
53
54/// One lowered, certified layer program, standing for every layer of its kind.
55#[derive(Clone, Debug)]
56pub struct LayerKind {
57    /// What the kind's layers are, unique among the model's kinds: `attention`, `gated deltanet`, `attention, window
58    /// 512`, `latent attention, experts`.
59    pub name: String,
60    /// The layer this program was lowered from; its tensor names are that layer's.
61    pub representative: usize,
62    /// The cut of the model's one program the representative's text is.
63    pub identity: Identity,
64    pub program: SpatialProgram,
65    /// A tensor-parallel kind's manifest, in the representative's names: each chip-local tensor's chip and the slice
66    /// of the checkpoint it is.
67    pub tp: Option<TpManifest>,
68    /// The state a step of this kind carries to the next, in the representative's names — what a deployment allocates
69    /// once for a session. A table the step assembles whole is the step's own.
70    pub carried: BTreeSet<String>,
71}
72
73/// How one layer of the forward runs: which kind, and how the kind program's tensor names map onto this
74/// layer's tensors. Every tensor the kind program declares appears exactly once.
75#[derive(Clone, Debug, PartialEq, Eq)]
76pub struct LayerBinding {
77    pub layer: usize,
78    pub kind: usize,
79    pub tensors: Vec<(String, String)>,
80    /// The cut of the model's one program this layer's own text is.
81    pub identity: Identity,
82}
83
84/// A program of the bundle that did not lower: which one, and the whole-program front end's own refusal — the
85/// statements it could not lift, by index and binder, or the fault that kept the composition from certifying.
86#[derive(Clone, Debug, PartialEq, Eq)]
87pub struct StreamedReject {
88    /// `embed`, `layer 3 (attention)`, `head` — or `bundle` for a request no program is lowered for.
89    pub program: String,
90    pub why: String,
91}
92
93impl std::fmt::Display for StreamedReject {
94    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
95        write!(
96            f,
97            "the {} program does not lower: {}",
98            self.program, self.why
99        )
100    }
101}
102
103#[derive(Clone, Debug)]
104pub struct StreamedModel {
105    /// A decode bundle's embedding lookup (`x = row tok of Emb`); `None` for the authored forward, whose first
106    /// hidden state is an input.
107    pub embed: Option<SpatialProgram>,
108    /// The cut of the model's one program the embedding program was lowered from.
109    pub embed_identity: Option<Identity>,
110    pub kinds: Vec<LayerKind>,
111    pub head: SpatialProgram,
112    /// The cut of the model's one program the head was lowered from.
113    pub head_identity: Identity,
114    pub layers: Vec<LayerBinding>,
115    /// Where the deployment keeps every program's carried state — the SRAM or HBM/DDR — and what it reserves for the
116    /// largest program's own footprint ([`super::residency::plan_deployment`]); every state tensor carries its tier.
117    pub residency: super::residency::ResidencyPlan,
118}
119
120/// The name layer `to` gives the tensor that layer `from` calls `name`. The hidden-state input is named by
121/// position (`x` for the first layer, `xo{l-1}` after); every per-layer tensor carries its layer's index as
122/// a suffix (`Wq0`, `x2n3`); anything else — the shared RoPE tables (whose suffix is a rotation's index, never a
123/// layer's), the lowerings' shared scalers — is the same tensor in every layer.
124///
125/// ★ AND A DERIVED NAME CARRIES ITS PARENT'S INDEX IN THE MIDDLE. The blocked lowerings name their
126/// intermediates after the tensor they come from — `xn0__xsq`, `od0_u` — so the index is not at the end.
127/// Renaming only a trailing index left every such intermediate named after the REPRESENTATIVE layer in the
128/// manifest (`xn0__xsq` at layer 17), which no execution test sees because intermediates are computed, not
129/// bound. The part before the first `_` is the name that carries the index.
130pub fn rebind(name: &str, from: usize, to: usize) -> String {
131    rebind_via(name, from, to, layer_input)
132}
133
134/// [`rebind`] for a bundle whose layers name their hidden-state input with `input` — the stacked `X`/`bxo{l-1}` of a
135/// batch. A per-sequence tensor keeps its sequence suffix (`Kc3_s1` at layer 7 is `Kc7_s1`).
136pub fn rebind_via(name: &str, from: usize, to: usize, input: fn(usize) -> String) -> String {
137    if name == input(from) {
138        return input(to);
139    }
140    // A host table is every layer's that reads it, and its suffix is no layer's: a rotary table's is its rotation's index
141    // (`Rcos1`), a table of ones' its width (`Ones128`).
142    if crate::model::bind::HostTable::named(name).is_some() {
143        return name.to_string();
144    }
145    let (head, tail) = match name.find('_') {
146        Some(i) => name.split_at(i),
147        None => (name, ""),
148    };
149    let suffix = from.to_string();
150    match head.strip_suffix(suffix.as_str()) {
151        Some(stem) if !stem.is_empty() && !stem.ends_with(|c: char| c.is_ascii_digit()) => {
152            format!("{stem}{to}{tail}")
153        }
154        _ => name.to_string(),
155    }
156}
157
158/// One program of the deployment: its source, a tensor-parallel program's manifest, and the weights it declares that the
159/// checkpoint stores Q8_0 — each read as it is stored wherever the product over it folds ([`super::passes::from_q8`]).
160type Generated = (String, Option<TpManifest>, BTreeSet<String>, Identity);
161
162/// A program's tokens: each maximal run of letters, digits and `_`, and each other character but whitespace.
163fn tokens(src: &str) -> Vec<&str> {
164    let mut out = Vec::new();
165    let mut rest = src.trim_start();
166    while let Some(c) = rest.chars().next() {
167        let len = if c.is_ascii_alphanumeric() || c == '_' {
168            rest.find(|c: char| !(c.is_ascii_alphanumeric() || c == '_')).unwrap_or(rest.len())
169        } else {
170            c.len_utf8()
171        };
172        out.push(&rest[..len]);
173        rest = rest[len..].trim_start();
174    }
175    out
176}
177
178/// The names a program binds: every name a `Let` introduces. Every other word — a keyword, an operation (`plus`,
179/// `gelu`), a function — is the language's, and two programs of one kind spell it alike.
180fn binders<'a>(tokens: &[&'a str]) -> BTreeSet<&'a str> {
181    tokens.windows(2).filter(|w| w[0] == "Let").map(|w| w[1]).collect()
182}
183
184/// The digest of a program up to the names it binds: every bound name replaced by the order of its first appearance and
185/// whether the checkpoint stores it as Q8_0 planes (`stored`), every other token kept. Two programs share it exactly when
186/// one is the other with its names consistently renamed, each weight read the same way.
187fn shape_digest(src: &str, stored: &BTreeSet<String>) -> [u8; 32] {
188    let toks = tokens(src);
189    let names = binders(&toks);
190    let mut order: std::collections::HashMap<&str, usize> = std::collections::HashMap::new();
191    let mut canonical = String::with_capacity(src.len());
192    for t in toks {
193        if names.contains(t) {
194            let next = order.len();
195            canonical.push_str(&format!("\u{1}{}", order.entry(t).or_insert(next)));
196            if stored.contains(t) {
197                canonical.push('\u{2}');
198            }
199        } else {
200            canonical.push_str(t);
201        }
202        canonical.push(' ');
203    }
204    crate::crypto_accel::sha256(canonical.as_bytes())
205}
206
207/// The renaming that turns program `from` into program `to`, bound name by bound name, when they share a
208/// `shape_digest`: the tokens aligned position by position, every word but a bound name kept exactly. `None` when they
209/// are not one program up to the names they bind.
210pub fn renaming(from: &str, to: &str) -> Option<std::collections::BTreeMap<String, String>> {
211    let (a, b) = (tokens(from), tokens(to));
212    if a.len() != b.len() {
213        return None;
214    }
215    let (names_a, names_b) = (binders(&a), binders(&b));
216    let (mut forward, mut back) = (std::collections::BTreeMap::new(), std::collections::BTreeMap::new());
217    for (x, y) in a.into_iter().zip(b) {
218        if names_a.contains(x) != names_b.contains(y) {
219            return None;
220        }
221        if names_a.contains(x) {
222            if *forward.entry(x.to_string()).or_insert_with(|| y.to_string()) != y
223                || *back.entry(y.to_string()).or_insert_with(|| x.to_string()) != x
224            {
225                return None;
226            }
227        } else if x != y {
228            return None;
229        }
230    }
231    Some(forward)
232}
233
234/// The model's layer kinds, in the order their first layer appears: each kind's name and its layers. Two layers are of
235/// one kind when their authored programs (`source_of`: the text, and the weights it reads as stored Q8_0) are one
236/// program up to the names they give — the same `shape_digest` — so one lowered program serves both, bound to each
237/// layer's own tensors by the [`renaming`] between their programs.
238pub fn layer_kinds(spec: &ModelSpec, source_of: &dyn Fn(usize) -> (String, BTreeSet<String>)) -> Vec<(String, Vec<usize>)> {
239    let mut kinds: Vec<([u8; 32], Vec<usize>)> = Vec::new();
240    for l in 0..spec.layers.len() {
241        let (src, stored) = source_of(l);
242        let digest = shape_digest(&src, &stored);
243        match kinds.iter_mut().find(|(d, _)| *d == digest) {
244            Some((_, layers)) => layers.push(l),
245            None => kinds.push((digest, vec![l])),
246        }
247    }
248    let describe = |l: usize| {
249        let layer = &spec.layers[l];
250        let mut name = match &layer.mixer {
251            Mixer::Attention(a) => match a.window {
252                Some(crate::model::spec::Window::Sliding(w)) => format!("attention, window {w}"),
253                Some(crate::model::spec::Window::Chunked(c)) => format!("attention, chunks of {c}"),
254                None => "attention".to_string(),
255            },
256            Mixer::GatedDelta(_) => "gated deltanet".to_string(),
257            Mixer::Mla(_) => "latent attention".to_string(),
258        };
259        if matches!(layer.ffn, Ffn::Moe(_)) {
260            name.push_str(", experts");
261        }
262        name
263    };
264    let names: Vec<String> = kinds.iter().map(|(_, layers)| describe(layers[0])).collect();
265    kinds
266        .into_iter()
267        .zip(&names)
268        .map(|((_, layers), name)| {
269            // Kinds a description does not tell apart are told apart by their first layer.
270            let unique = names.iter().filter(|n| *n == name).count() == 1;
271            (if unique { name.clone() } else { format!("{name} (layer {})", layers[0]) }, layers)
272        })
273        .collect()
274}
275
276/// The weights a generated program stores Q8_0, from the generator's Q8_0 weight table.
277/// The chip-local slices of a tensor-parallel layer whose full weight the checkpoint stores Q8_0: each chip reads its
278/// slice as stored — a column slice always, a row slice when its rows are whole Q8_0 blocks (its scales are then whole
279/// rows of the full weight's scales).
280fn q8_slices<V>(m: &TpManifest, full: &std::collections::HashMap<String, V>, spec: &ModelSpec, storage: Storage) -> BTreeSet<String> {
281    let whole_blocks = |s: &super::tensor_parallel::TpSlice| {
282        s.axis == super::tensor_parallel::SliceAxis::Cols
283            || s.ranges.iter().all(|(start, n)| {
284                start % super::passes::from_q8::Q8_BLOCK == 0
285                    && n % super::passes::from_q8::Q8_BLOCK == 0
286            })
287    };
288    m.slices
289        .iter()
290        .filter(|(_, s)| full.contains_key(&s.of) && storage.q8(spec, &s.of) && whole_blocks(s))
291        .map(|(name, _)| name.clone())
292        .collect()
293}
294
295fn q8_names<V>(table: &std::collections::HashMap<String, V>, spec: &ModelSpec, storage: Storage) -> BTreeSet<String> {
296    table.keys().filter(|v| storage.q8(spec, v)).cloned().collect()
297}
298
299fn lower_certified(
300    src: &str,
301    target: &SpatialTarget,
302    program: &str,
303    dense: &BTreeSet<String>,
304    tp: Option<&TpManifest>,
305    q8: &BTreeSet<String>,
306) -> Result<SpatialProgram, StreamedReject> {
307    lower_certified_carrying(src, target, program, dense, tp, q8).map(|(p, _)| p)
308}
309
310/// [`lower_certified`], and the state the program carries from one step to the next.
311fn lower_certified_carrying(
312    src: &str,
313    target: &SpatialTarget,
314    program: &str,
315    dense: &BTreeSet<String>,
316    tp: Option<&TpManifest>,
317    q8: &BTreeSet<String>,
318) -> Result<(SpatialProgram, BTreeSet<String>), StreamedReject> {
319    let reject = |why: String| StreamedReject {
320        program: program.to_string(),
321        why,
322    };
323    let lowering = match tp {
324        Some(m) => compile_program_to_spatial_tp_agreeing_stored(src, target, m, dense, q8),
325        None => compile_program_to_spatial_agreeing_stored(src, target, dense, q8),
326    }
327    .map_err(|e| reject(format!("{e:?}")))?;
328    let carried = lowering.carried.iter().cloned().collect();
329    let p = lowering.into_whole().map_err(reject)?;
330    if p.certificate.is_none() {
331        return Err(reject("the lowering carries no certificate".to_string()));
332    }
333    Ok((p, carried))
334}
335
336/// Lower `spec` as one program per layer kind plus the head, and bind every layer to its kind.
337///
338/// `fill` turns a generated program into the source that is actually lowered (the weights' fills, when a
339/// caller has them); layer programs are lowered through it so a kind program carries the fills of its
340/// representative layer.
341pub fn lower_forward_streamed(
342    spec: &ModelSpec,
343    storage: Storage,
344    target: &SpatialTarget,
345    fill: impl Fn(&str) -> String,
346) -> Result<StreamedModel, StreamedReject> {
347    let ((head, _, head_q8), head_id) = written_in(spec, Cut::ForwardHead, "/w");
348    lower_streamed(
349        spec,
350        target,
351        &fill,
352        None,
353        &|l| {
354            let ((src, _, q8), id) = written_in(spec, Cut::ForwardLayer { layer: l }, "/w");
355            (src, None, q8_names(&q8, spec, storage), id)
356        },
357        (&head, &q8_names(&head_q8, spec, storage), head_id),
358        layer_input,
359    )
360}
361
362/// Lower `spec` as an SN50 DECODE deployment: the embedding lookup, one program per layer kind in which every
363/// attention layer appends to and attends over its KV cache of `ctx` positions, and a head that emits the next
364/// token. `tok` and `pos` are runtime inputs — the programs read them as data — so the bundle serves every step;
365/// the values the programs are lowered with (token 1, position 1) only seed their declarations.
366pub fn lower_decode_streamed(
367    spec: &ModelSpec,
368    storage: Storage,
369    ctx: usize,
370    target: &SpatialTarget,
371    fill: impl Fn(&str) -> String,
372) -> Result<StreamedModel, StreamedReject> {
373    if ctx == 0 {
374        return Err(StreamedReject {
375            program: "bundle".into(),
376            why: "a decode bundle with an empty KV cache".into(),
377        });
378    }
379    let ((embed, _, _), embed_id) = written_in(spec, Cut::DecodeEmbed, "/w");
380    let ((head, _, head_q8), head_id) = written_in(spec, Cut::DecodeHead, "/w");
381    lower_streamed(
382        spec,
383        target,
384        &fill,
385        Some((&embed, embed_id)),
386        &|l| {
387            let ((src, _, q8), id) = written_in(spec, Cut::DecodeLayer { layer: l, ctx }, "/w");
388            (src, None, q8_names(&q8, spec, storage), id)
389        },
390        (&head, &q8_names(&head_q8, spec, storage), head_id),
391        layer_input,
392    )
393}
394
395/// Lower `spec` as an SN50 TENSOR-PARALLEL decode deployment over `tp` chips: the embedding lookup and the head on
396/// the replicated spine, and each layer kind one program over the chips — every chip its own heads and FFN columns,
397/// the partials summed by an all-reduce twice a layer, the chips' slices of each op in one phase (see
398/// `spatial::tensor_parallel`). `tp` must be a width the model admits (`fit::admits_tp`); each kind program
399/// carries its manifest's slices, so a host knows which rows or columns of which checkpoint tensor each chip loads.
400pub fn lower_tp_decode_streamed(
401    spec: &ModelSpec,
402    storage: Storage,
403    ctx: usize,
404    tp: usize,
405    target: &SpatialTarget,
406    fill: impl Fn(&str) -> String,
407) -> Result<StreamedModel, StreamedReject> {
408    if ctx == 0 {
409        return Err(StreamedReject {
410            program: "bundle".into(),
411            why: "a decode bundle with an empty KV cache".into(),
412        });
413    }
414    if let Err(r) = super::fit::admits_tp(spec, tp) {
415        return Err(StreamedReject {
416            program: "bundle".into(),
417            why: format!("tensor parallel over {tp} chips: {}", r.why),
418        });
419    }
420    let ((embed, _, _), embed_id) = written_in(spec, Cut::DecodeEmbed, "/w");
421    let ((head, _, head_q8), head_id) = written_in(spec, Cut::DecodeHead, "/w");
422    lower_streamed(
423        spec,
424        target,
425        &fill,
426        Some((&embed, embed_id)),
427        &|l| {
428            let ((src, _, q8w, m), id) = split(spec, tp, written_split(spec, Cut::DecodeLayer { layer: l, ctx }, "/w", tp));
429            let q8 = q8_slices(&m, &q8w, spec, storage);
430            (src, Some(m), q8, id)
431        },
432        (&head, &q8_names(&head_q8, spec, storage), head_id),
433        layer_input,
434    )
435}
436
437/// Lower `spec` as an SN50 BATCHED decode deployment: `batch` sequences per step, each with its own token, position
438/// and carried state, every weight read once for all of them (the model's [`Cut::Batch`]). The same three kinds of
439/// program as [`lower_decode_streamed`] — the embedding lookups, one program per layer kind, the head — over
440/// the stacked `batch × d` hidden states; the token ids `toks` and each sequence's `pos_s{b}` are runtime inputs.
441pub fn lower_batched_decode_streamed(
442    spec: &ModelSpec,
443    storage: Storage,
444    ctx: usize,
445    batch: usize,
446    target: &SpatialTarget,
447    fill: impl Fn(&str) -> String,
448) -> Result<StreamedModel, StreamedReject> {
449    if ctx == 0 {
450        return Err(StreamedReject {
451            program: "bundle".into(),
452            why: "a decode bundle with an empty KV cache".into(),
453        });
454    }
455    if batch == 0 {
456        return Err(StreamedReject {
457            program: "bundle".into(),
458            why: "a batched decode bundle serving no sequence".into(),
459        });
460    }
461    let ((embed, _, _), embed_id) = written_in(spec, Cut::BatchEmbed { rows: batch }, "/w");
462    let ((head, _, head_q8), head_id) = written_in(spec, Cut::BatchHead { rows: batch }, "/w");
463    lower_streamed(
464        spec,
465        target,
466        &fill,
467        Some((&embed, embed_id)),
468        &|l| {
469            let ((src, _, q8), id) = written_in(spec, Cut::BatchLayer { layer: l, rows: batch, ctx }, "/w");
470            (src, None, q8_names(&q8, spec, storage), id)
471        },
472        (&head, &q8_names(&head_q8, spec, storage), head_id),
473        stacked_layer_input,
474    )
475}
476
477/// Lower `spec`'s PREFILL as an SN50 deployment: `chunk` tokens of one sequence per step, the cuts of the model's
478/// [`Cut::Prefill`] — the chunk's embedding lookups stacked, one program per layer kind taking the rows in order
479/// over the sequence's single state (the decode step's own, so a decode bundle continues from it), and a head reading the
480/// last row. Each row's token and position are runtime inputs.
481pub fn lower_prefill_streamed(
482    spec: &ModelSpec,
483    storage: Storage,
484    ctx: usize,
485    chunk: usize,
486    target: &SpatialTarget,
487    fill: impl Fn(&str) -> String,
488) -> Result<StreamedModel, StreamedReject> {
489    if ctx == 0 {
490        return Err(StreamedReject {
491            program: "bundle".into(),
492            why: "a prefill bundle with an empty KV cache".into(),
493        });
494    }
495    if chunk == 0 || chunk > ctx {
496        return Err(StreamedReject {
497            program: "bundle".into(),
498            why: format!("a prefill chunk of {chunk} tokens in a context of {ctx}"),
499        });
500    }
501    let ((embed, _, _), embed_id) = written_in(spec, Cut::BatchEmbed { rows: chunk }, "/w");
502    let ((head, _, head_q8), head_id) = written_in(spec, Cut::PrefillHead { rows: chunk }, "/w");
503    lower_streamed(
504        spec,
505        target,
506        &fill,
507        Some((&embed, embed_id)),
508        &|l| {
509            let ((src, _, q8), id) = written_in(spec, Cut::PrefillLayer { layer: l, rows: chunk, ctx }, "/w");
510            (src, None, q8_names(&q8, spec, storage), id)
511        },
512        (&head, &q8_names(&head_q8, spec, storage), head_id),
513        stacked_layer_input,
514    )
515}
516
517/// Lower `spec` as an SN50 BATCHED, TENSOR-PARALLEL decode deployment — `batch` sequences per step over `tp` chips,
518/// the serving shape: [`lower_batched_decode_streamed`]'s stacked programs with every layer kind
519/// [`lower_tp_decode_streamed`]'s program over the chips.
520pub fn lower_batched_tp_decode_streamed(
521    spec: &ModelSpec,
522    storage: Storage,
523    ctx: usize,
524    batch: usize,
525    tp: usize,
526    target: &SpatialTarget,
527    fill: impl Fn(&str) -> String,
528) -> Result<StreamedModel, StreamedReject> {
529    if ctx == 0 {
530        return Err(StreamedReject {
531            program: "bundle".into(),
532            why: "a decode bundle with an empty KV cache".into(),
533        });
534    }
535    if batch == 0 {
536        return Err(StreamedReject {
537            program: "bundle".into(),
538            why: "a batched decode bundle serving no sequence".into(),
539        });
540    }
541    if let Err(r) = super::fit::admits_tp(spec, tp) {
542        return Err(StreamedReject {
543            program: "bundle".into(),
544            why: format!("tensor parallel over {tp} chips: {}", r.why),
545        });
546    }
547    let ((embed, _, _), embed_id) = written_in(spec, Cut::BatchEmbed { rows: batch }, "/w");
548    let ((head, _, head_q8), head_id) = written_in(spec, Cut::BatchHead { rows: batch }, "/w");
549    lower_streamed(
550        spec,
551        target,
552        &fill,
553        Some((&embed, embed_id)),
554        &|l| {
555            let ((src, _, q8w, m), id) =
556                split(spec, tp, written_split(spec, Cut::BatchLayer { layer: l, rows: batch, ctx }, "/w", tp));
557            let q8 = q8_slices(&m, &q8w, spec, storage);
558            (src, Some(m), q8, id)
559        },
560        (&head, &q8_names(&head_q8, spec, storage), head_id),
561        stacked_layer_input,
562    )
563}
564
565/// A layer of `spec` split over `tp` chips, a width the bundle's request was already checked to admit.
566fn split(spec: &ModelSpec, tp: usize, form: Result<(TpAuthored, Identity), crate::model::author::NotYet>) -> (TpAuthored, Identity) {
567    form.unwrap_or_else(|e| panic!("{} does not split over {tp} chips: {e}", spec.arch))
568}
569
570fn lower_streamed(
571    spec: &ModelSpec,
572    target: &SpatialTarget,
573    fill: &dyn Fn(&str) -> String,
574    embed: Option<(&str, Identity)>,
575    layer: &dyn Fn(usize) -> Generated,
576    (head, head_q8, head_identity): (&str, &BTreeSet<String>, Identity),
577    input: fn(usize) -> String,
578) -> Result<StreamedModel, StreamedReject> {
579    let embed_identity = embed.map(|(_, id)| id);
580    let embed = embed.map(|(src, _)| src);
581    // ★ THE PROGRAMS AGREE ON EVERY TABLE THEY SHARE. The embedding program writes the first hidden state and
582    // never reads it, so on its own it cannot know that layer 0 reads that state as a matrix — a stacked batch
583    // is dense to the norm and the projections that read it, and would be flat to a writer that saw no reader.
584    // The deployment's dense tables are therefore decided over every program: the embedding's, the head's, and
585    // each layer kind's, carried to every layer of the kind under that layer's names.
586    let tables = |src: &str, program: String| {
587        dense_tables_of(src, target).map_err(|e| StreamedReject {
588            program,
589            why: format!("{e:?}"),
590        })
591    };
592    let mut dense: BTreeSet<String> = BTreeSet::new();
593    if let Some(src) = embed {
594        dense.extend(tables(src, "embed".into())?);
595    }
596    dense.extend(tables(head, "head".into())?);
597    let generated: Vec<Generated> = (0..spec.layers.len()).map(layer).collect();
598    let kind_layers = layer_kinds(spec, &|l| (generated[l].0.clone(), generated[l].2.clone()));
599    // Each layer's names, from its kind's representative's: the renaming between their programs, and for a name the
600    // lowering derives from a source name (`xn0__xsq`), the derivation of the renamed one.
601    let renames: Vec<std::collections::BTreeMap<String, String>> = (0..spec.layers.len())
602        .map(|l| {
603            let rep = kind_layers.iter().find(|(_, m)| m.contains(&l)).expect("every layer is of a kind").1[0];
604            renaming(&generated[rep].0, &generated[l].0).expect("layers of one kind are one program up to their names")
605        })
606        .collect();
607    let name_in = |l: usize, rep: usize, name: &str| -> String {
608        if let Some(to) = renames[l].get(name) {
609            return to.clone();
610        }
611        match name.split_once("__") {
612            Some((stem, derived)) if renames[l].contains_key(stem) => format!("{}__{derived}", renames[l][stem]),
613            _ => rebind_via(name, rep, l, input),
614        }
615    };
616    for (_, members) in &kind_layers {
617        let rep = members[0];
618        let kind = tables(&generated[rep].0, format!("layer {rep}"))?;
619        for &l in members {
620            dense.extend(kind.iter().map(|n| name_in(l, rep, n)));
621        }
622    }
623    let none = BTreeSet::new();
624    let embed = embed
625        .map(|src| lower_certified(&fill(src), target, "embed", &dense, None, &none))
626        .transpose()?;
627    let mut kinds: Vec<LayerKind> = Vec::new();
628    let mut layers = Vec::with_capacity(spec.layers.len());
629    for l in 0..spec.layers.len() {
630        let name = &kind_layers
631            .iter()
632            .find(|(_, members)| members.contains(&l))
633            .expect("every layer is of a kind")
634            .0;
635        let kind = match kinds.iter().position(|k| &k.name == name) {
636            Some(k) => k,
637            None => {
638                let (src, tp, q8, identity) = generated[l].clone();
639                let (program, carried) = lower_certified_carrying(
640                    &fill(&src),
641                    target,
642                    &format!("layer {l} ({name})"),
643                    &dense,
644                    tp.as_ref(),
645                    &q8,
646                )?;
647                kinds.push(LayerKind {
648                    name: name.clone(),
649                    representative: l,
650                    identity,
651                    program,
652                    tp,
653                    carried,
654                });
655                kinds.len() - 1
656            }
657        };
658        let rep = kinds[kind].representative;
659        let tensors = kinds[kind]
660            .program
661            .tensors
662            .iter()
663            .map(|t| (t.name.clone(), name_in(l, rep, &t.name)))
664            .collect();
665        layers.push(LayerBinding {
666            layer: l,
667            kind,
668            tensors,
669            identity: generated[l].3,
670        });
671    }
672    let mut head = lower_certified(&fill(head), target, "head", &dense, None, head_q8)?;
673    let mut embed = embed;
674    let instances: Vec<usize> = (0..kinds.len())
675        .map(|k| layers.iter().filter(|l| l.kind == k).count())
676        .collect();
677    let residency = {
678        let mut programs: Vec<super::residency::DeployedProgram> = Vec::new();
679        if let Some(e) = embed.as_mut() {
680            programs.push(("embed", 1, e, None));
681        }
682        for (k, n) in kinds.iter_mut().zip(&instances) {
683            let LayerKind { name, program, carried, .. } = k;
684            programs.push((name.as_str(), *n, program, Some(carried)));
685        }
686        programs.push(("head", 1, &mut head, None));
687        super::residency::plan_deployment(&mut programs, target)
688    };
689    Ok(StreamedModel {
690        embed,
691        embed_identity,
692        kinds,
693        head,
694        head_identity,
695        layers,
696        residency,
697    })
698}