Skip to main content

logicaffeine_compile/model/
program.rs

1//! A model's one program. [`author`] writes a model's computation as a LOGOS program, and every backend lowers that
2//! program and no other: the CUDA lane compiles its decode step and prefill chunks, the D5005 engine reads its decode
3//! step, a spatial chip lowers its embedding, its layers and its head one program each. A backend never grows a model
4//! writer of its own, and takes every cut it lowers from this module.
5//!
6//! The program a backend lowers is a [`Cut`] of it — the decode step over a cache of `ctx` positions, a prefill chunk
7//! of `rows` tokens, a batch of `rows` sequences, or one layer, the embedding or the head of any of them — written by
8//! [`Cut::write`] with the placeholder inputs every backend replaces at run time (each token and position `1`, weights
9//! named by tensor alone). A cut split over chips is written by [`written_split`]. Its [`Identity`] is the cut, the
10//! chips it is split over, and the SHA-256 of the text `author` wrote for it; every artifact a backend compiles records
11//! it, so an artifact names the exact program it was lowered from, and a program another writer produced cannot pass
12//! for this one.
13
14use std::fmt;
15
16use super::author::{self, Authored, NotYet, RowState, TpAuthored};
17use super::spec::ModelSpec;
18use crate::crypto_accel::sha256;
19
20/// Which cut of a model's program a backend lowers.
21#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
22pub enum Cut {
23    /// One decode step over a cache of `ctx` positions ([`author::decode_step`]).
24    Decode { ctx: usize },
25    /// A prefill chunk of `rows` tokens of one sequence over a cache of `ctx` positions ([`author::prefill`]).
26    Prefill { rows: usize, ctx: usize },
27    /// One decode step of `rows` sequences at once, each with its own state ([`author::decode_batch`]).
28    Batch { rows: usize, ctx: usize },
29    /// The single token's forward with no history ([`author::forward`]).
30    Forward,
31    /// Layer `layer` of the forward alone ([`author::layer`]).
32    ForwardLayer { layer: usize },
33    /// The forward's final norm and head alone ([`author::head`]).
34    ForwardHead,
35    /// A decode step's embedding lookup alone ([`author::decode_embed`]).
36    DecodeEmbed,
37    /// Layer `layer` of a decode step alone ([`author::decode_layer`]).
38    DecodeLayer { layer: usize, ctx: usize },
39    /// A decode step's final norm, head and arg-max alone ([`author::decode_head`]).
40    DecodeHead,
41    /// The embedding lookups of `rows` stacked rows alone ([`author::decode_batch_embed`]).
42    BatchEmbed { rows: usize },
43    /// Layer `layer` of a batch of `rows` sequences alone ([`author::stacked_layer`], each row its own sequence).
44    BatchLayer { layer: usize, rows: usize, ctx: usize },
45    /// A batch's head alone, every sequence's next token ([`author::decode_batch_head`]).
46    BatchHead { rows: usize },
47    /// Layer `layer` of a prefill chunk of `rows` tokens alone ([`author::stacked_layer`], one sequence's rows).
48    PrefillLayer { layer: usize, rows: usize, ctx: usize },
49    /// A prefill chunk's head alone, read off its last row ([`author::prefill_head`]).
50    PrefillHead { rows: usize },
51}
52
53impl Cut {
54    /// The program `author` writes for this cut of `spec`'s computation, its weight manifest naming files under `dir`
55    /// (the text does not depend on `dir`).
56    pub fn write(self, spec: &ModelSpec, dir: &str) -> Authored {
57        match self {
58            Cut::Decode { ctx } => author::decode_step(spec, dir, ctx, 1, 1),
59            Cut::Prefill { rows, ctx } => author::prefill(spec, dir, ctx, &vec![1; rows], 1),
60            Cut::Batch { rows, ctx } => author::decode_batch(spec, dir, ctx, &vec![(1, 1); rows]),
61            Cut::Forward => author::forward(spec, dir),
62            Cut::ForwardLayer { layer } => author::layer(spec, layer, dir),
63            Cut::ForwardHead => author::head(spec, dir),
64            Cut::DecodeEmbed => author::decode_embed(spec, dir, 1),
65            Cut::DecodeLayer { layer, ctx } => author::decode_layer(spec, layer, dir, ctx, 1),
66            Cut::DecodeHead => author::decode_head(spec, dir),
67            Cut::BatchEmbed { rows } => author::decode_batch_embed(spec, dir, &vec![1; rows]),
68            Cut::BatchLayer { layer, rows, ctx } => {
69                author::stacked_layer(spec, layer, dir, ctx, &vec![1; rows], RowState::PerSequence)
70            }
71            Cut::BatchHead { rows } => author::decode_batch_head(spec, dir, rows),
72            Cut::PrefillLayer { layer, rows, ctx } => {
73                author::stacked_layer(spec, layer, dir, ctx, &(1..=rows).collect::<Vec<_>>(), RowState::Shared)
74            }
75            Cut::PrefillHead { rows } => author::prefill_head(spec, dir, rows),
76        }
77    }
78
79    /// The program `author` writes for this cut of `spec`'s computation split over `tp` chips, its manifest under `dir`:
80    /// a decode step, a batch, or one layer of either. Any other cut, or a width the model does not split over, is
81    /// refused.
82    pub fn write_split(self, spec: &ModelSpec, dir: &str, tp: usize) -> Result<TpAuthored, NotYet> {
83        match self {
84            Cut::Decode { ctx } => author::tp_decode_step(spec, dir, ctx, 1, 1, tp),
85            Cut::Batch { rows, ctx } => author::tp_decode_batch(spec, dir, ctx, &vec![(1, 1); rows], tp),
86            Cut::DecodeLayer { layer, ctx } => author::tp_decode_layer(spec, layer, dir, ctx, 1, tp),
87            Cut::BatchLayer { layer, rows, ctx } => author::tp_stacked_layer(spec, layer, dir, ctx, &vec![1; rows], tp),
88            _ => Err(NotYet("a cut other than a decode step, a batch or a layer of either, split over chips")),
89        }
90    }
91}
92
93impl fmt::Display for Cut {
94    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
95        match self {
96            Cut::Decode { ctx } => write!(f, "decode step, ctx {ctx}"),
97            Cut::Prefill { rows, ctx } => write!(f, "prefill chunk of {rows} rows, ctx {ctx}"),
98            Cut::Batch { rows, ctx } => write!(f, "batch of {rows} sequences, ctx {ctx}"),
99            Cut::Forward => write!(f, "forward"),
100            Cut::ForwardLayer { layer } => write!(f, "forward layer {layer}"),
101            Cut::ForwardHead => write!(f, "forward head"),
102            Cut::DecodeEmbed => write!(f, "decode step's embedding"),
103            Cut::DecodeLayer { layer, ctx } => write!(f, "decode step's layer {layer}, ctx {ctx}"),
104            Cut::DecodeHead => write!(f, "decode step's head"),
105            Cut::BatchEmbed { rows } => write!(f, "batch of {rows} sequences' embedding"),
106            Cut::BatchLayer { layer, rows, ctx } => write!(f, "batch of {rows} sequences' layer {layer}, ctx {ctx}"),
107            Cut::BatchHead { rows } => write!(f, "batch of {rows} sequences' head"),
108            Cut::PrefillLayer { layer, rows, ctx } => write!(f, "prefill chunk of {rows} rows' layer {layer}, ctx {ctx}"),
109            Cut::PrefillHead { rows } => write!(f, "prefill chunk of {rows} rows' head"),
110        }
111    }
112}
113
114/// The program an artifact was lowered from: the cut, the chips it is split over (1 when it is not), and the SHA-256 of
115/// the LOGOS text `author` wrote for it.
116#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
117pub struct Identity {
118    pub cut: Cut,
119    pub chips: usize,
120    pub sha256: [u8; 32],
121}
122
123impl Identity {
124    /// The identity of `source`, written as the cut `cut` on one chip.
125    pub fn of(cut: Cut, source: &str) -> Identity {
126        Identity::of_split(cut, 1, source)
127    }
128
129    /// The identity of `source`, written as the cut `cut` split over `chips` chips.
130    pub fn of_split(cut: Cut, chips: usize, source: &str) -> Identity {
131        Identity { cut, chips, sha256: sha256(source.as_bytes()) }
132    }
133
134    /// The digest as 64 lowercase hex digits.
135    pub fn hex(&self) -> String {
136        self.sha256.iter().map(|b| format!("{b:02x}")).collect()
137    }
138
139    /// The identity as the one line every artifact records it in: `LOGOS program: <cut>, sha256 <hex>`, the cut said
140    /// to be split over its chips when it is.
141    pub fn line(&self) -> String {
142        match self.chips {
143            1 => format!("LOGOS program: {}, sha256 {}", self.cut, self.hex()),
144            n => format!("LOGOS program: {} over {n} chips, sha256 {}", self.cut, self.hex()),
145        }
146    }
147}
148
149/// The cut `cut` of `spec`'s program, with its identity.
150pub fn written(spec: &ModelSpec, cut: Cut) -> (Authored, Identity) {
151    written_in(spec, cut, "")
152}
153
154/// The cut `cut` of `spec`'s program with its weight manifest under `dir`, and its identity.
155pub fn written_in(spec: &ModelSpec, cut: Cut, dir: &str) -> (Authored, Identity) {
156    let authored = cut.write(spec, dir);
157    let identity = Identity::of(cut, &authored.0);
158    (authored, identity)
159}
160
161/// [`Cut::Prefill`] at a run's own inputs rather than its placeholders: the chunk of `toks` (token ids from 1) at
162/// positions from `pos0`, its manifest under `dir` — the text a run executes. Its identity is the cut's
163/// ([`written`]), whose inputs sit at their placeholders.
164pub fn prefill_at(spec: &ModelSpec, dir: &str, ctx: usize, toks: &[usize], pos0: usize) -> Authored {
165    author::prefill(spec, dir, ctx, toks, pos0)
166}
167
168/// [`Cut::Decode`] at a run's own inputs: token `tok` (from 1) at position `pos`, its manifest under `dir`.
169pub fn decode_at(spec: &ModelSpec, dir: &str, ctx: usize, tok: usize, pos: usize) -> Authored {
170    author::decode_step(spec, dir, ctx, tok, pos)
171}
172
173/// The cut `cut` of `spec`'s program split over `tp` chips with its manifest under `dir`, and its identity.
174pub fn written_split(spec: &ModelSpec, cut: Cut, dir: &str, tp: usize) -> Result<(TpAuthored, Identity), NotYet> {
175    let authored = cut.write_split(spec, dir, tp)?;
176    let identity = Identity::of_split(cut, tp, &authored.0);
177    Ok((authored, identity))
178}
179
180#[cfg(test)]
181#[path = "../../../logicaffeine_tests/unit/logicaffeine_compile/model/program/tests.rs"]
182mod tests;