1use std::collections::HashMap;
14
15use super::bind::{rotation_of, rotations, HostTable, RotaryTable};
16use super::spec::{Act, Attention, Ffn, GateActivation, GatedDelta, Gating, Mixer, Mla, ModelSpec};
17
18pub type Authored = (
20 String,
21 HashMap<String, (String, usize)>,
22 HashMap<String, String>,
23);
24
25#[derive(Clone, Debug, PartialEq, Eq)]
27pub struct NotYet(pub &'static str);
28
29impl std::fmt::Display for NotYet {
30 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
31 write!(f, "the language cannot write {} yet", self.0)
32 }
33}
34
35pub fn check(spec: &ModelSpec) -> Result<(), NotYet> {
40 if !(spec.rms_eps.is_finite() && spec.rms_eps > 0.0) {
41 return Err(NotYet("an RMSNorm epsilon that is not a positive number"));
42 }
43 if spec.final_logit_softcap.is_some_and(|cap| !(cap.is_finite() && cap > 0.0)) {
44 return Err(NotYet("a final logit softcap that is not a positive number"));
45 }
46 if spec.hyper.is_some() {
48 return Err(NotYet("hyper-connections in place of the norms"));
49 }
50 if spec.ple.is_some() {
51 return Err(NotYet("a per-layer n-gram embedding"));
52 }
53 if spec.indexer.as_ref().is_some_and(|ix| !ix.layers.is_empty()) {
54 return Err(NotYet("a sparse selection of the attended positions (QSA)"));
55 }
56 for layer in &spec.layers {
57 if let Mixer::Attention(a) = &layer.mixer {
58 if a.output_gate && (a.fused_qkv || a.value_dim != a.head_dim) {
59 return Err(NotYet("an output gate on a fused projection or over values unlike the keys in width"));
60 }
61 if a.softcap.is_some_and(|cap| !(cap.is_finite() && cap > 0.0)) {
62 return Err(NotYet("an attention softcap that is not a positive number"));
63 }
64 if a.temperature.is_some() && a.head_dim % 2 != 0 {
65 return Err(NotYet("an attention temperature over an odd head width"));
66 }
67 }
68 match &layer.ffn {
69 Ffn::Dense { act, .. } if !matches!(act, Act::Silu | Act::GeluTanh) => {
70 return Err(NotYet("a gated activation other than SiLU or GeLU"))
71 }
72 Ffn::Moe(m) if !matches!(m.act, Act::Silu | Act::SwigluOai { .. }) => {
73 return Err(NotYet("an expert mixture other than top-k experts gated by a SwiGLU"))
74 }
75 Ffn::Moe(m) if !(m.weights_scale.is_finite() && m.weights_scale >= 0.0) => {
76 return Err(NotYet("a routed scale that is not a positive number"))
77 }
78 Ffn::Moe(m) if m.shared_experts > 0 && m.act != Act::Silu => {
79 return Err(NotYet("shared experts gated other than by a SwiGLU"))
80 }
81 Ffn::Moe(m) if m.gating == Gating::SoftmaxOfTopK && m.groups.is_some() => {
84 return Err(NotYet("a group limit over a softmax of the kept logits"))
85 }
86 Ffn::Moe(m)
87 if m.groups.is_some_and(|(count, used)| {
88 count == 0 || m.experts % count != 0 || !(1..=count).contains(&used) || used * (m.experts / count) < m.top_k
89 }) =>
90 {
91 return Err(NotYet("a group limit that does not split the experts evenly or keeps fewer than it chooses"))
92 }
93 _ => {}
94 }
95 }
96 Ok(())
97}
98
99fn with_epsilon(src: &str, eps: f64) -> String {
103 if eps == 1e-6 {
104 return src.to_string();
105 }
106 src.lines()
107 .map(|line| {
108 let norm = [" be rmsnorm of ", " be l2norm of ", " be gated delta of "].iter().any(|n| line.contains(n));
109 match line.strip_suffix('.') {
110 Some(stmt) if norm && line.starts_with("Let ") => format!("{stmt} with epsilon {eps:?}.\n"),
111 _ => format!("{line}\n"),
112 }
113 })
114 .collect()
115}
116
117#[derive(Clone, Copy, Debug, PartialEq, Eq)]
119pub enum AttnForm {
120 Current,
122 Cached { ctx: usize },
125}
126
127#[derive(Clone, Copy, Debug, PartialEq, Eq)]
129pub enum RowState {
130 PerSequence,
132 Shared,
135}
136
137impl RowState {
138 fn state(self, base: &str, b: usize) -> String {
140 match self {
141 RowState::PerSequence => format!("{base}_s{b}"),
142 RowState::Shared => base.to_string(),
143 }
144 }
145
146 fn holders(self, bsz: usize) -> std::ops::Range<usize> {
148 match self {
149 RowState::PerSequence => 0..bsz,
150 RowState::Shared => 0..1,
151 }
152 }
153}
154
155struct Program<'a> {
157 spec: &'a ModelSpec,
158 dir: &'a str,
159 s: String,
160 f32w: HashMap<String, (String, usize)>,
161 q8w: HashMap<String, String>,
162}
163
164struct Dims {
167 d: usize,
168 aq: usize,
169 akv: usize,
170 avd: usize,
171 ao: usize,
172 hd: usize,
173 h: usize,
174 kv: usize,
175}
176
177fn attn_dims(spec: &ModelSpec, a: &Attention) -> Dims {
178 Dims {
179 d: spec.d,
180 aq: a.heads * a.head_dim,
181 akv: a.kv_heads * a.head_dim,
182 avd: a.kv_heads * a.value_dim,
183 ao: a.heads * a.value_dim,
184 hd: a.head_dim,
185 h: a.heads,
186 kv: a.kv_heads,
187 }
188}
189
190impl<'a> Program<'a> {
191 fn new(spec: &'a ModelSpec, dir: &'a str) -> Self {
192 Program {
193 spec,
194 dir,
195 s: String::from("## Main\n"),
196 f32w: HashMap::new(),
197 q8w: HashMap::new(),
198 }
199 }
200
201 fn finish(self) -> Authored {
202 (with_epsilon(&self.s, self.spec.rms_eps), self.f32w, self.q8w)
203 }
204
205 fn decl_f32(&mut self, var: &str, r: usize, c: usize) {
206 self.s.push_str(&format!("Let {var} be a {r} by {c} tensor of Real.\n"));
207 self.f32w
208 .insert(var.to_string(), (format!("{}/{var}.f", self.dir), r * c));
209 }
210
211 fn decl_q8(&mut self, var: &str, r: usize, c: usize) {
212 self.s.push_str(&format!("Let {var} be a {r} by {c} tensor of Real.\n"));
213 self.q8w.insert(var.to_string(), format!("{}/{var}", self.dir));
214 }
215
216 fn rotary_tables(&mut self, only: Option<usize>, cached: bool, ctx: usize) {
219 let spec = self.spec;
220 for (rotation, rope) in rotations(spec).into_iter().enumerate() {
221 if only.is_some_and(|l| rotation_of(spec, l) != Some(rotation)) {
222 continue;
223 }
224 for cos in [true, false] {
225 let name = RotaryTable { rotation, cached, cos }.name();
226 self.decl_f32(&name, if cached { ctx } else { 1 }, rope.dims / 2);
227 }
228 }
229 }
230
231 fn host_table(&mut self, table: HostTable, rows: usize, cols: usize) -> String {
233 let name = table.name();
234 if !self.f32w.contains_key(&name) {
235 self.decl_f32(&name, rows, cols);
236 }
237 name
238 }
239
240 fn llama4_qk(
246 &mut self,
247 a: &Attention,
248 (q, k): (String, String),
249 norm_heads: (usize, usize),
250 rope_heads: usize,
251 from: Option<(String, usize)>,
252 name: &dyn Fn(&str) -> String,
253 ) -> (String, String) {
254 let (mut q, mut k) = (q, k);
255 if a.qk_unit_norm {
256 let ones = self.host_table(HostTable::Ones(a.head_dim), 1, a.head_dim);
257 let (qu, ku, (hq, hkv)) = (name("qu"), name("ku"), norm_heads);
258 self.s.push_str(&format!(
259 "Let {qu} be rmsnorm of {q} with weight {ones} across {hq} heads.\n\
260 Let {ku} be rmsnorm of {k} with weight {ones} across {hkv} heads.\n"
261 ));
262 (q, k) = (qu, ku);
263 }
264 if a.temperature.is_some() {
265 let half = a.head_dim / 2;
266 let (cached, rows) = from.as_ref().map_or((false, 1), |(_, ctx)| (true, *ctx));
267 let cos = self.host_table(HostTable::Temperature { cached, cos: true, half }, rows, half);
268 let sin = self.host_table(HostTable::Temperature { cached, cos: false, half }, rows, half);
269 let at = from.as_ref().map_or(String::new(), |(pos, _)| format!(" from position {pos}"));
270 let qt = name("qt");
271 self.s.push_str(&format!("Let {qt} be rope of {q} with cos {cos} and sin {sin}{at} across {rope_heads} heads.\n"));
272 q = qt;
273 }
274 (q, k)
275 }
276
277 fn angles(&self, l: usize, form: AttnForm, pos: &str) -> String {
280 let rotation = rotation_of(self.spec, l).expect("a layer that rotates");
281 let cached = matches!(form, AttnForm::Cached { .. });
282 let (cos, sin) = (RotaryTable { rotation, cached, cos: true }.name(), RotaryTable { rotation, cached, cos: false }.name());
283 match form {
284 AttnForm::Current => format!("cos {cos} and sin {sin}"),
285 AttnForm::Cached { .. } => format!("cos {cos} and sin {sin} from position {pos}"),
286 }
287 }
288
289 fn glu(&mut self, l: usize, gate: &str, up: &str, out: &str) -> Vec<String> {
292 match &self.spec.layers[l].ffn {
293 Ffn::Dense { act: Act::GeluTanh, .. } => {
294 let act = format!("{out}_a");
295 self.s.push_str(&format!("Let {act} be gelu of {gate}.\nLet {out} be {act} hadamard {up}.\n"));
296 vec![act, out.to_string()]
297 }
298 _ => {
299 self.s.push_str(&format!("Let {out} be swiglu of {gate} and {up}.\n"));
300 vec![out.to_string()]
301 }
302 }
303 }
304
305 fn post_norm(&mut self, l: usize, x: &str, after_mixer: bool) -> String {
308 let layer = &self.spec.layers[l];
309 let (on, weight) = if after_mixer { (layer.post_mixer_norm, "pan") } else { (layer.post_ffn_norm, "pfn") };
310 if !on {
311 return x.to_string();
312 }
313 let w = format!("{weight}{l}");
314 if !self.f32w.contains_key(&w) {
315 self.decl_f32(&w, 1, self.spec.d);
316 }
317 self.s.push_str(&format!("Let {x}n be rmsnorm of {x} with weight {w}.\n"));
318 format!("{x}n")
319 }
320
321 fn project(&mut self, out: &str, x: &str, w: &str, bias: Option<String>, width: usize) {
324 match bias {
325 Some(b) => {
326 if !self.f32w.contains_key(&b) {
327 self.decl_f32(&b, 1, width);
328 }
329 self.s.push_str(&format!("Let {out}_r be {x} matmul {w}.\nLet {out} be {out}_r plus {b}.\n"));
330 }
331 None => self.s.push_str(&format!("Let {out} be {x} matmul {w}.\n")),
332 }
333 }
334
335 fn sinks(&mut self, l: usize, a: &Attention) -> String {
337 if !a.sinks {
338 return String::new();
339 }
340 let sk = format!("Sk{l}");
341 if !self.f32w.contains_key(&sk) {
342 self.decl_f32(&sk, 1, a.heads);
343 }
344 format!(" with sinks {sk}")
345 }
346
347 fn embedded(&mut self, rows: &str, out: &str) {
350 match self.spec.embedding_scale {
351 Some(scale) => self.s.push_str(&format!("Let {out}e be {rows}.\nLet {out} be {out}e scaled by {scale:?}.\n")),
352 None => self.s.push_str(&format!("Let {out} be {rows}.\n")),
353 }
354 }
355
356 fn layer(&mut self, l: usize, cur: &str, form: AttnForm) {
358 let spec = self.spec;
359 self.decl_f32(&format!("an{l}"), 1, spec.d);
360 match &spec.layers[l].mixer {
361 Mixer::Attention(a) => self.attention(l, a, cur, form),
362 Mixer::GatedDelta(g) => self.gated_delta(l, g, cur),
363 Mixer::Mla(m) => self.mla(l, m, cur, form),
364 }
365 self.ffn(l, cur, None);
366 }
367
368 fn mla_weights(&mut self, l: usize, m: &Mla, ctx: Option<(usize, usize, RowState)>) {
371 let d = self.spec.d;
372 let (h, qk, lora, kw) = (m.heads, m.qk_nope + m.qk_rope, m.kv_lora_rank, m.kv_lora_rank + m.qk_rope);
373 match m.q_lora_rank {
374 None => self.decl_q8(&format!("Wq{l}"), d, h * qk),
375 Some(r) => {
376 self.decl_q8(&format!("Wqa{l}"), d, r);
377 self.decl_f32(&format!("Qan{l}"), 1, r);
378 self.decl_q8(&format!("Wqb{l}"), r, h * qk);
379 }
380 }
381 self.decl_q8(&format!("Wkva{l}"), d, kw);
382 self.decl_f32(&format!("Kan{l}"), 1, lora);
383 self.decl_q8(&format!("Wkb{l}"), h * m.qk_nope, lora);
384 self.decl_q8(&format!("Wvb{l}"), h * lora, m.v_dim);
385 self.decl_q8(&format!("Wo{l}"), h * m.v_dim, d);
386 if let Some((ctx, bsz, rows)) = ctx {
387 for b in rows.holders(bsz) {
388 self.s.push_str(&format!("Let {} be a {ctx} by {kw} tensor of Real.\n", rows.state(&format!("Kc{l}"), b)));
389 }
390 }
391 }
392
393 fn mla_projections(&mut self, l: usize, m: &Mla, p: &str) {
398 let (h, nope, rope, lora) = (m.heads, m.qk_nope, m.qk_rope, m.kv_lora_rank);
399 let (qk, kw) = (nope + rope, lora + rope);
400 match m.q_lora_rank {
401 None => self.s.push_str(&format!("Let {p}qr{l} be {p}xn{l} matmul Wq{l}.\n")),
402 Some(_) => self.s.push_str(&format!(
403 "Let {p}qx{l} be {p}xn{l} matmul Wqa{l}.\n\
404 Let {p}qxn{l} be rmsnorm of {p}qx{l} with weight Qan{l}.\n\
405 Let {p}qr{l} be {p}qxn{l} matmul Wqb{l}.\n"
406 )),
407 }
408 self.s.push_str(&format!(
409 "Let {p}kva{l} be {p}xn{l} matmul Wkva{l}.\n\
410 Let {p}kvl{l} be the first {lora} of every {kw} of {p}kva{l}.\n\
411 Let {p}kpe{l} be the last {rope} of every {kw} of {p}kva{l}.\n\
412 Let {p}kvn{l} be rmsnorm of {p}kvl{l} with weight Kan{l}.\n\
413 Let {p}qn{l} be the first {nope} of every {qk} of {p}qr{l}.\n\
414 Let {p}qe{l} be the last {rope} of every {qk} of {p}qr{l}.\n\
415 Let {p}ql{l} be {p}qn{l} matmul Wkb{l} across {h} heads.\n"
416 ));
417 }
418
419 fn mla_turned(&mut self, m: &Mla, x: &str, t: &str, angles: &str) {
423 let h = m.heads;
424 self.s.push_str(&format!(
425 "Let {x}kpp{t} be rope of {x}kpe{t} with {angles} across 1 heads.\n\
426 Let {x}kc{t} be {x}kvn{t} joined with {x}kpp{t}.\n\
427 Let {x}qpp{t} be rope of {x}qe{t} with {angles} across {h} heads.\n\
428 Let {x}qa{t} be {x}ql{t} joined with {x}qpp{t} across {h} heads.\n"
429 ));
430 }
431
432 fn mla(&mut self, l: usize, m: &Mla, cur: &str, form: AttnForm) {
439 let (d, h, lora, kw, scale) = (self.spec.d, m.heads, m.kv_lora_rank, m.kv_lora_rank + m.qk_rope, m.scale);
440 let ctx = match form {
441 AttnForm::Cached { ctx } => Some((ctx, 1, RowState::Shared)),
442 AttnForm::Current => None,
443 };
444 self.mla_weights(l, m, ctx);
445 self.s.push_str(&format!("Let xn{l} be rmsnorm of {cur} with weight an{l}.\n"));
446 let angles = self.angles(l, form, "pos");
447 self.mla_projections(l, m, "");
448 self.mla_turned(m, "", &l.to_string(), &angles);
449 match form {
450 AttnForm::Current => self.s.push_str(&format!(
451 "Let Vl{l} be the first {lora} of every {kw} of kc{l}.\n\
452 Let ol{l} be attention of qa{l} over kc{l} and Vl{l} with {h} heads 1 groups scaled by {scale:?}.\n"
453 )),
454 AttnForm::Cached { .. } => self.s.push_str(&format!(
455 "Set row pos of Kc{l} to kc{l}.\n\
456 Let Vl{l} be the first {lora} of every {kw} of Kc{l}.\n\
457 Let ol{l} be attention of qa{l} over Kc{l} and Vl{l} with {h} heads 1 groups up to pos scaled by {scale:?}.\n"
458 )),
459 }
460 self.s.push_str(&format!("Let ct{l} be ol{l} matmul Wvb{l} across {h} heads.\n"));
461 self.project(&format!("ao{l}"), &format!("ct{l}"), &format!("Wo{l}"), None, d);
462 }
463
464 fn stacked_mla(&mut self, l: usize, m: &Mla, cur: &str, ctx: usize, bsz: usize, rows: RowState) {
468 let (d, h, lora, kw, scale) = (self.spec.d, m.heads, m.kv_lora_rank, m.kv_lora_rank + m.qk_rope, m.scale);
469 self.mla_weights(l, m, Some((ctx, bsz, rows)));
470 self.s.push_str(&format!("Let bxn{l} be rmsnorm of {cur} with weight an{l}.\n"));
471 self.mla_projections(l, m, "b");
472 match rows {
473 RowState::Shared => {
474 let angles = self.angles(l, AttnForm::Cached { ctx }, "pos_s0");
475 self.mla_turned(m, "b", &l.to_string(), &angles);
476 let kc = rows.state(&format!("Kc{l}"), 0);
477 self.s.push_str(&format!(
478 "Set rows from pos_s0 of {kc} to bkc{l}.\n\
479 Let Vl{l} be the first {lora} of every {kw} of {kc}.\n\
480 Let bol{l} be causal attention of bqa{l} over {kc} and Vl{l} with {h} heads 1 groups from pos_s0 scaled by {scale:?}.\n"
481 ));
482 }
483 RowState::PerSequence => {
485 self.s.push_str(&format!("Let bol{l} be a {bsz} by {} tensor of Real.\n", h * lora));
486 for b in 0..bsz {
487 let (r, t) = (b + 1, format!("{l}_s{b}"));
488 let angles = self.angles(l, AttnForm::Cached { ctx }, &format!("pos_s{b}"));
489 for part in ["kvn", "kpe", "ql", "qe"] {
490 self.s.push_str(&format!("Let {part}{t} be row {r} of b{part}{l}.\n"));
491 }
492 self.mla_turned(m, "", &t, &angles);
493 let kc = rows.state(&format!("Kc{l}"), b);
494 self.s.push_str(&format!(
495 "Set row pos_s{b} of {kc} to kc{t}.\n\
496 Let Vl{t} be the first {lora} of every {kw} of {kc}.\n\
497 Let ol{t} be attention of qa{t} over {kc} and Vl{t} with {h} heads 1 groups up to pos_s{b} scaled by {scale:?}.\n\
498 Set row {r} of bol{l} to ol{t}.\n"
499 ));
500 }
501 }
502 }
503 self.s.push_str(&format!("Let bct{l} be bol{l} matmul Wvb{l} across {h} heads.\n"));
504 self.project(&format!("bao{l}"), &format!("bct{l}"), &format!("Wo{l}"), None, d);
505 }
506
507 fn attention(&mut self, l: usize, a: &Attention, cur: &str, form: AttnForm) {
508 let Dims { d, aq, akv, avd, ao, h, kv, .. } = attn_dims(self.spec, a);
509 self.decl_q8(&format!("Wq{l}"), d, aq);
510 if a.output_gate {
511 self.decl_q8(&format!("Wqg{l}"), d, aq);
512 }
513 self.decl_q8(&format!("Wk{l}"), d, akv);
514 self.decl_q8(&format!("Wv{l}"), d, avd);
515 let ((nq, nk), (wq, wk)) = qk_norm_segments(a);
516 if a.qk_norm {
517 self.decl_f32(&format!("Qn{l}"), 1, wq);
518 self.decl_f32(&format!("Kn{l}"), 1, wk);
519 }
520 self.decl_q8(&format!("Wo{l}"), ao, d);
521 if let AttnForm::Cached { ctx } = form {
522 self.s.push_str(&format!(
523 "Let Kc{l} be a {ctx} by {akv} tensor of Real.\nLet Vc{l} be a {ctx} by {avd} tensor of Real.\n"
524 ));
525 }
526 let bias = |b: &str| a.bias.then(|| format!("{b}{l}"));
527 self.s.push_str(&format!("Let xn{l} be rmsnorm of {cur} with weight an{l}.\n"));
528 self.project(&format!("qr{l}"), &format!("xn{l}"), &format!("Wq{l}"), bias("Bq"), aq);
529 if a.output_gate {
530 self.s.push_str(&format!("Let gr{l} be xn{l} matmul Wqg{l}.\n"));
531 }
532 self.project(&format!("kr{l}"), &format!("xn{l}"), &format!("Wk{l}"), bias("Bk"), akv);
533 self.project(&format!("vr{l}"), &format!("xn{l}"), &format!("Wv{l}"), bias("Bv"), avd);
534 let (mut q, mut k) = (format!("qr{l}"), format!("kr{l}"));
535 if a.qk_norm {
536 self.s.push_str(&format!(
537 "Let qm{l} be rmsnorm of qr{l} with weight Qn{l} across {nq} heads.\n\
538 Let km{l} be rmsnorm of kr{l} with weight Kn{l} across {nk} heads.\n"
539 ));
540 (q, k) = (format!("qm{l}"), format!("km{l}"));
541 }
542 if a.rope.is_some() {
543 let angles = self.angles(l, form, "pos");
544 self.s.push_str(&format!(
545 "Let qp{l} be rope of {q} with {angles} across {h} heads.\n\
546 Let kp{l} be rope of {k} with {angles} across {kv} heads.\n"
547 ));
548 (q, k) = (format!("qp{l}"), format!("kp{l}"));
549 }
550 let from = match form {
551 AttnForm::Current => None,
552 AttnForm::Cached { ctx } => Some(("pos".to_string(), ctx)),
553 };
554 (q, k) = self.llama4_qk(a, (q, k), (h, kv), h, from, &|base| format!("{base}{l}"));
555 let (w, sk) = (score_clauses(a), self.sinks(l, a));
556 match form {
557 AttnForm::Current => self.s.push_str(&format!(
558 "Let ct{l} be attention of {q} over {k} and vr{l} with {h} heads {kv} groups{w}{sk}.\n"
559 )),
560 AttnForm::Cached { .. } => self.s.push_str(&format!(
561 "Set row pos of Kc{l} to {k}.\n\
562 Set row pos of Vc{l} to vr{l}.\n\
563 Let ct{l} be attention of {q} over Kc{l} and Vc{l} with {h} heads {kv} groups up to pos{w}{sk}.\n"
564 )),
565 }
566 let gated = if a.output_gate {
567 self.s.push_str(&format!("Let gs{l} be sigmoid of gr{l}.\nLet cg{l} be ct{l} hadamard gs{l}.\n"));
568 format!("cg{l}")
569 } else {
570 format!("ct{l}")
571 };
572 self.project(&format!("ao{l}"), &gated, &format!("Wo{l}"), a.output_bias.then(|| format!("Bo{l}")), d);
573 }
574
575 fn gated_delta(&mut self, l: usize, g: &GatedDelta, cur: &str) {
576 let gate = gate_clause(g);
577 let d = self.spec.d;
578 let (qk, vw, taps) = (g.k_heads * g.head_dim, g.v_heads * g.head_dim, g.conv_taps);
579 let (nk, nv, ghd) = (g.k_heads, g.v_heads, g.head_dim);
580 self.decl_q8(&format!("Wq{l}"), d, qk);
581 self.decl_q8(&format!("Wk{l}"), d, qk);
582 self.decl_q8(&format!("Wv{l}"), d, vw);
583 self.decl_q8(&format!("Wz{l}"), d, vw);
584 self.decl_f32(&format!("Wcq{l}"), taps, qk);
585 self.decl_f32(&format!("Wck{l}"), taps, qk);
586 self.decl_f32(&format!("Wcv{l}"), taps, vw);
587 self.decl_f32(&format!("Csq{l}"), taps, qk);
588 self.decl_f32(&format!("Csk{l}"), taps, qk);
589 self.decl_f32(&format!("Csv{l}"), taps, vw);
590 self.decl_q8(&format!("Wb{l}"), d, nv);
591 self.decl_q8(&format!("Wa{l}"), d, nv);
592 self.decl_f32(&format!("Dt{l}"), 1, nv);
593 self.decl_f32(&format!("Al{l}"), 1, nv);
594 self.decl_f32(&format!("St{l}"), vw, ghd);
595 self.decl_f32(&format!("Gn{l}"), 1, ghd);
596 self.decl_q8(&format!("Wo{l}"), vw, d);
597 self.s.push_str(&format!(
598 "Let xn{l} be rmsnorm of {cur} with weight an{l}.\n\
599 Let qr{l} be xn{l} matmul Wq{l}.\n\
600 Let kr{l} be xn{l} matmul Wk{l}.\n\
601 Let vr{l} be xn{l} matmul Wv{l}.\n\
602 Let zz{l} be xn{l} matmul Wz{l}.\n\
603 Let qc{l} be causal conv of qr{l} with weight Wcq{l} state Csq{l} over {taps} taps.\n\
604 Let kc{l} be causal conv of kr{l} with weight Wck{l} state Csk{l} over {taps} taps.\n\
605 Let vc{l} be causal conv of vr{l} with weight Wcv{l} state Csv{l} over {taps} taps.\n\
606 Let qs{l} be silu of qc{l}.\n\
607 Let ks{l} be silu of kc{l}.\n\
608 Let vs{l} be silu of vc{l}.\n\
609 Let qn{l} be l2norm of qs{l} across {nk} heads.\n\
610 Let kn{l} be l2norm of ks{l} across {nk} heads.\n\
611 Let br{l} be xn{l} matmul Wb{l}.\n\
612 Let bt{l} be sigmoid of br{l}.\n\
613 Let ar{l} be xn{l} matmul Wa{l}.\n\
614 Let dy{l} be decay of ar{l} with bias Dt{l} rate Al{l}.\n\
615 Let od{l} be gated delta of qn{l} over kn{l} and vs{l} with {gate} zz{l} beta bt{l} decay dy{l} state St{l} norm Gn{l} with {nv} heads {nk} groups.\n\
616 Let ao{l} be od{l} matmul Wo{l}.\n",
617 ));
618 }
619
620 fn ffn(&mut self, l: usize, cur: &str, rows: Option<usize>) {
623 let d = self.spec.d;
624 let p = if rows.is_some() { "b" } else { "" };
625 self.decl_f32(&format!("fn{l}"), 1, d);
626 match self.spec.layers[l].ffn.clone() {
627 Ffn::Dense { dff, .. } => {
628 self.decl_q8(&format!("Wg{l}"), d, dff);
629 self.decl_q8(&format!("Wu{l}"), d, dff);
630 self.decl_q8(&format!("Wd{l}"), dff, d);
631 let ao = self.post_norm(l, &format!("{p}ao{l}"), true);
632 self.s.push_str(&format!(
633 "Let {p}xr{l} be {cur} plus {ao}.\n\
634 Let {p}x2n{l} be rmsnorm of {p}xr{l} with weight fn{l}.\n\
635 Let {p}gt{l} be {p}x2n{l} matmul Wg{l}.\n\
636 Let {p}up{l} be {p}x2n{l} matmul Wu{l}.\n"
637 ));
638 self.glu(l, &format!("{p}gt{l}"), &format!("{p}up{l}"), &format!("{p}hh{l}"));
639 self.s.push_str(&format!("Let {p}dn{l} be {p}hh{l} matmul Wd{l}.\n"));
640 let dn = self.post_norm(l, &format!("{p}dn{l}"), false);
641 self.s.push_str(&format!("Let {p}xo{l} be {p}xr{l} plus {dn}.\n"));
642 }
643 Ffn::Moe(m) => {
644 let (e, k, dff) = (m.experts, m.top_k, m.expert_dff);
645 self.decl_q8(&format!("Wr{l}"), d, e);
646 self.decl_q8(&format!("Wg{l}"), e * dff, d);
647 self.decl_q8(&format!("Wu{l}"), e * dff, d);
648 self.decl_q8(&format!("Wd{l}"), e * d, dff);
649 let ao = self.post_norm(l, &format!("{p}ao{l}"), true);
650 self.s.push_str(&format!(
651 "Let {p}xr{l} be {cur} plus {ao}.\n\
652 Let {p}x2n{l} be rmsnorm of {p}xr{l} with weight fn{l}.\n"
653 ));
654 if m.biases {
655 self.decl_f32(&format!("Br{l}"), 1, e);
656 self.decl_f32(&format!("Bg{l}"), e, dff);
657 self.decl_f32(&format!("Bu{l}"), e, dff);
658 self.decl_f32(&format!("Bd{l}"), e, d);
659 }
660 if m.gating == (Gating::Sigmoid { bias: true }) {
661 self.decl_f32(&format!("Bs{l}"), 1, e);
662 }
663 let plus = |b: &str| if m.biases { format!(" plus {b}{l}") } else { String::new() };
664 let (br, bg, bu, bd) = (plus("Br"), plus("Bg"), plus("Bu"), plus("Bd"));
665 let act = match m.act {
666 Act::SwigluOai { alpha, limit } => format!(" using a swiglu clamped at {limit:?} with slope {alpha:?}"),
667 _ => String::new(),
668 };
669 let score = match m.gating {
673 Gating::Sigmoid { bias: true } => format!(" by sigmoid biased by Bs{l}"),
674 Gating::Sigmoid { .. } | Gating::SigmoidScaledInput => " by sigmoid".to_string(),
675 _ => String::new(),
676 };
677 let groups = m.groups.map_or(String::new(), |(count, used)| format!(" within the top {used} of {count} groups"));
678 let norm = match !matches!(m.gating, Gating::SoftmaxOfTopK) && !m.norm_top_k {
679 true => " without renormalizing",
680 false => "",
681 };
682 let scale = match m.weights_scale {
683 s if s == 0.0 || s == 1.0 => String::new(),
684 s => format!(" scaled by {s:?}"),
685 };
686 let inputs = match m.gating {
687 Gating::SigmoidScaledInput => " weighting the inputs",
688 _ => "",
689 };
690 let route = |x: &str, out: &str| {
691 format!(
692 "Let {out} be moe of {x} with router Wr{l}{br} gate Wg{l}{bg} up Wu{l}{bu} down Wd{l}{bd} choosing {k} of {e}{score}{groups}{norm}{scale}{inputs}{act}.\n"
693 )
694 };
695 match rows {
696 None => self.s.push_str(&route(&format!("x2n{l}"), &format!("mo{l}"))),
697 Some(bsz) => {
699 self.s.push_str(&format!("Let bmo{l} be a {bsz} by {d} tensor of Real.\n"));
700 for b in 0..bsz {
701 let r = b + 1;
702 self.s.push_str(&format!("Let x2n{l}_s{b} be row {r} of bx2n{l}.\n"));
703 self.s.push_str(&route(&format!("x2n{l}_s{b}"), &format!("mo{l}_s{b}")));
704 self.s.push_str(&format!("Set row {r} of bmo{l} to mo{l}_s{b}.\n"));
705 }
706 }
707 }
708 let routed = match m.shared_experts {
710 0 => format!("{p}mo{l}"),
711 _ => {
712 let sdff = m.shared_dff;
713 self.decl_q8(&format!("Wsg{l}"), d, sdff);
714 self.decl_q8(&format!("Wsu{l}"), d, sdff);
715 self.decl_q8(&format!("Wsd{l}"), sdff, d);
716 self.s.push_str(&format!(
717 "Let {p}sg{l} be {p}x2n{l} matmul Wsg{l}.\n\
718 Let {p}su{l} be {p}x2n{l} matmul Wsu{l}.\n\
719 Let {p}sh{l} be swiglu of {p}sg{l} and {p}su{l}.\n\
720 Let {p}sd{l} be {p}sh{l} matmul Wsd{l}.\n"
721 ));
722 let shared = if m.shared_gate {
725 self.decl_f32(&format!("Wsgi{l}"), d, 1);
726 let ones = self.host_table(HostTable::Ones(d), 1, d);
727 self.s.push_str(&format!(
728 "Let {p}sgi{l} be {p}x2n{l} matmul Wsgi{l}.\n\
729 Let {p}sgw{l} be sigmoid of {p}sgi{l}.\n\
730 Let {p}sgr{l} be {p}sgw{l} matmul {ones}.\n\
731 Let {p}sdg{l} be {p}sd{l} hadamard {p}sgr{l}.\n"
732 ));
733 format!("{p}sdg{l}")
734 } else {
735 format!("{p}sd{l}")
736 };
737 self.s.push_str(&format!("Let {p}me{l} be {p}mo{l} plus {shared}.\n"));
738 format!("{p}me{l}")
739 }
740 };
741 let mo = self.post_norm(l, &routed, false);
742 self.s.push_str(&format!("Let {p}xo{l} be {p}xr{l} plus {mo}.\n"));
743 }
744 }
745 }
746
747 fn head(&mut self, cur: &str) {
749 let (d, vocab) = (self.spec.d, self.spec.vocab);
750 self.decl_f32("on", 1, d);
751 self.decl_q8("Wout", d, vocab);
752 self.s.push_str(&format!("Let xf be rmsnorm of {cur} with weight on.\n"));
753 self.s.push_str(&capped_logits("xf matmul Wout", "logits", self.spec.final_logit_softcap));
754 }
755
756 fn embed(&mut self) {
758 let (vocab, d) = (self.spec.vocab, self.spec.d);
759 self.decl_q8("Emb", vocab, d);
760 self.embedded("row tok of Emb", "x");
761 }
762
763 fn next_token(&mut self) {
765 self.s.push_str("Let next be argmax of logits.\n");
766 }
767
768 fn rope_cache(&mut self, ctx: usize) {
771 self.rotary_tables(None, true, ctx);
772 }
773
774 fn batched_embed(&mut self, toks: &[usize]) {
776 let (vocab, d) = (self.spec.vocab, self.spec.d);
777 self.decl_q8("Emb", vocab, d);
778 self.s.push_str(&format!("Let toks be a 1 by {} tensor of Int.\n", toks.len()));
779 for (k, tok) in toks.iter().enumerate() {
780 self.s.push_str(&format!("Set item {} of toks to {tok}.\n", k + 1));
781 }
782 self.embedded("rows toks of Emb", "X");
783 }
784
785 fn batched_head(&mut self, cur: &str, bsz: usize) {
787 let (d, vocab) = (self.spec.d, self.spec.vocab);
788 self.decl_f32("on", 1, d);
789 self.decl_q8("Wout", d, vocab);
790 self.s.push_str(&format!("Let bxf be rmsnorm of {cur} with weight on.\n"));
791 self.s.push_str(&capped_logits("bxf matmul Wout", "blogits", self.spec.final_logit_softcap));
792 for b in 0..bsz {
793 self.s.push_str(&format!(
794 "Let logits_s{b} be row {} of blogits.\nLet next_s{b} be argmax of logits_s{b}.\n",
795 b + 1
796 ));
797 }
798 }
799
800 fn last_row_head(&mut self, cur: &str, rows: usize) {
802 self.s.push_str(&format!("Let xl be row {rows} of {cur}.\n"));
803 self.head("xl");
804 self.next_token();
805 }
806
807 fn stacked_layer(&mut self, l: usize, cur: &str, ctx: usize, bsz: usize, rows: RowState) {
810 let spec = self.spec;
811 self.decl_f32(&format!("an{l}"), 1, spec.d);
812 match &spec.layers[l].mixer {
813 Mixer::Attention(a) => self.stacked_attention(l, a, cur, ctx, bsz, rows),
814 Mixer::GatedDelta(g) => self.stacked_gated_delta(l, g, cur, bsz, rows),
815 Mixer::Mla(m) => self.stacked_mla(l, m, cur, ctx, bsz, rows),
816 }
817 self.ffn(l, cur, Some(bsz));
818 }
819
820 fn stacked_attention(&mut self, l: usize, a: &Attention, cur: &str, ctx: usize, bsz: usize, rows: RowState) {
821 let st = |base: String, b: usize| rows.state(&base, b);
822 let Dims { d, aq, akv, avd, ao, h, kv, .. } = attn_dims(self.spec, a);
823 self.decl_q8(&format!("Wq{l}"), d, aq);
824 if a.output_gate {
825 self.decl_q8(&format!("Wqg{l}"), d, aq);
826 }
827 self.decl_q8(&format!("Wk{l}"), d, akv);
828 self.decl_q8(&format!("Wv{l}"), d, avd);
829 let ((nq, nk), (wq, wk)) = qk_norm_segments(a);
830 if a.qk_norm {
831 self.decl_f32(&format!("Qn{l}"), 1, wq);
832 self.decl_f32(&format!("Kn{l}"), 1, wk);
833 }
834 self.decl_q8(&format!("Wo{l}"), ao, d);
835 for b in rows.holders(bsz) {
836 self.s.push_str(&format!(
837 "Let {} be a {ctx} by {akv} tensor of Real.\nLet {} be a {ctx} by {avd} tensor of Real.\n",
838 st(format!("Kc{l}"), b),
839 st(format!("Vc{l}"), b)
840 ));
841 }
842 let bias = |b: &str| a.bias.then(|| format!("{b}{l}"));
843 self.s.push_str(&format!("Let bxn{l} be rmsnorm of {cur} with weight an{l}.\n"));
844 self.project(&format!("bqr{l}"), &format!("bxn{l}"), &format!("Wq{l}"), bias("Bq"), aq);
845 if a.output_gate {
846 self.s.push_str(&format!("Let bgr{l} be bxn{l} matmul Wqg{l}.\n"));
847 }
848 self.project(&format!("bkr{l}"), &format!("bxn{l}"), &format!("Wk{l}"), bias("Bk"), akv);
849 self.project(&format!("bvr{l}"), &format!("bxn{l}"), &format!("Wv{l}"), bias("Bv"), avd);
850 match rows {
851 RowState::Shared => {
856 let (kc, vc) = (st(format!("Kc{l}"), 0), st(format!("Vc{l}"), 0));
857 let (mut q, mut k) = (format!("bqr{l}"), format!("bkr{l}"));
858 if a.qk_norm {
859 let (qh, kh) = (bsz * nq, bsz * nk);
860 self.s.push_str(&format!(
861 "Let bqm{l} be rmsnorm of bqr{l} with weight Qn{l} across {qh} heads.\n\
862 Let bkm{l} be rmsnorm of bkr{l} with weight Kn{l} across {kh} heads.\n"
863 ));
864 (q, k) = (format!("bqm{l}"), format!("bkm{l}"));
865 }
866 if a.rope.is_some() {
867 let angles = self.angles(l, AttnForm::Cached { ctx }, "pos_s0");
868 self.s.push_str(&format!(
869 "Let bqp{l} be rope of {q} with {angles} across {h} heads.\n\
870 Let bkp{l} be rope of {k} with {angles} across {kv} heads.\n"
871 ));
872 (q, k) = (format!("bqp{l}"), format!("bkp{l}"));
873 }
874 (q, k) = self.llama4_qk(a, (q, k), (bsz * h, bsz * kv), h, Some(("pos_s0".to_string(), ctx)), &|base| format!("b{base}{l}"));
875 let (w, sk) = (score_clauses(a), self.sinks(l, a));
876 self.s.push_str(&format!(
877 "Set rows from pos_s0 of {kc} to {k}.\n\
878 Set rows from pos_s0 of {vc} to bvr{l}.\n\
879 Let bct{l} be causal attention of {q} over {kc} and {vc} with {h} heads {kv} groups from pos_s0{w}{sk}.\n"
880 ));
881 }
882 RowState::PerSequence => {
884 self.s.push_str(&format!("Let bct{l} be a {bsz} by {ao} tensor of Real.\n"));
885 for b in 0..bsz {
886 let r = b + 1;
887 let (kc, vc) = (st(format!("Kc{l}"), b), st(format!("Vc{l}"), b));
888 self.s.push_str(&format!(
889 "Let qr{l}_s{b} be row {r} of bqr{l}.\n\
890 Let kr{l}_s{b} be row {r} of bkr{l}.\n\
891 Let vr{l}_s{b} be row {r} of bvr{l}.\n"
892 ));
893 let (mut q, mut k) = (format!("qr{l}_s{b}"), format!("kr{l}_s{b}"));
894 if a.qk_norm {
895 self.s.push_str(&format!(
896 "Let qm{l}_s{b} be rmsnorm of qr{l}_s{b} with weight Qn{l} across {nq} heads.\n\
897 Let km{l}_s{b} be rmsnorm of kr{l}_s{b} with weight Kn{l} across {nk} heads.\n"
898 ));
899 (q, k) = (format!("qm{l}_s{b}"), format!("km{l}_s{b}"));
900 }
901 if a.rope.is_some() {
902 let angles = self.angles(l, AttnForm::Cached { ctx }, &format!("pos_s{b}"));
903 self.s.push_str(&format!(
904 "Let qp{l}_s{b} be rope of {q} with {angles} across {h} heads.\n\
905 Let kp{l}_s{b} be rope of {k} with {angles} across {kv} heads.\n"
906 ));
907 (q, k) = (format!("qp{l}_s{b}"), format!("kp{l}_s{b}"));
908 }
909 (q, k) = self.llama4_qk(a, (q, k), (h, kv), h, Some((format!("pos_s{b}"), ctx)), &|base| format!("{base}{l}_s{b}"));
910 let (w, sk) = (score_clauses(a), self.sinks(l, a));
911 self.s.push_str(&format!(
912 "Set row pos_s{b} of {kc} to {k}.\n\
913 Set row pos_s{b} of {vc} to vr{l}_s{b}.\n\
914 Let ct{l}_s{b} be attention of {q} over {kc} and {vc} with {h} heads {kv} groups up to pos_s{b}{w}{sk}.\n\
915 Set row {r} of bct{l} to ct{l}_s{b}.\n"
916 ));
917 }
918 }
919 }
920 let gated = if a.output_gate {
921 self.s.push_str(&format!("Let bgs{l} be sigmoid of bgr{l}.\nLet bcg{l} be bct{l} hadamard bgs{l}.\n"));
922 format!("bcg{l}")
923 } else {
924 format!("bct{l}")
925 };
926 self.project(&format!("bao{l}"), &gated, &format!("Wo{l}"), a.output_bias.then(|| format!("Bo{l}")), d);
927 }
928
929 fn stacked_gated_delta(&mut self, l: usize, g: &GatedDelta, cur: &str, bsz: usize, rows: RowState) {
930 let gate = gate_clause(g);
931 let st = |base: String, b: usize| rows.state(&base, b);
932 let d = self.spec.d;
933 let (qk, vw, taps) = (g.k_heads * g.head_dim, g.v_heads * g.head_dim, g.conv_taps);
934 let (nk, nv, ghd) = (g.k_heads, g.v_heads, g.head_dim);
935 self.decl_q8(&format!("Wq{l}"), d, qk);
936 self.decl_q8(&format!("Wk{l}"), d, qk);
937 self.decl_q8(&format!("Wv{l}"), d, vw);
938 self.decl_q8(&format!("Wz{l}"), d, vw);
939 self.decl_f32(&format!("Wcq{l}"), taps, qk);
940 self.decl_f32(&format!("Wck{l}"), taps, qk);
941 self.decl_f32(&format!("Wcv{l}"), taps, vw);
942 for b in rows.holders(bsz) {
943 self.decl_f32(&st(format!("Csq{l}"), b), taps, qk);
944 self.decl_f32(&st(format!("Csk{l}"), b), taps, qk);
945 self.decl_f32(&st(format!("Csv{l}"), b), taps, vw);
946 self.decl_f32(&st(format!("St{l}"), b), vw, ghd);
947 }
948 self.decl_q8(&format!("Wb{l}"), d, nv);
949 self.decl_q8(&format!("Wa{l}"), d, nv);
950 self.decl_f32(&format!("Dt{l}"), 1, nv);
951 self.decl_f32(&format!("Al{l}"), 1, nv);
952 self.decl_f32(&format!("Gn{l}"), 1, ghd);
953 self.decl_q8(&format!("Wo{l}"), vw, d);
954 self.s.push_str(&format!(
955 "Let bxn{l} be rmsnorm of {cur} with weight an{l}.\n\
956 Let bqr{l} be bxn{l} matmul Wq{l}.\n\
957 Let bkr{l} be bxn{l} matmul Wk{l}.\n\
958 Let bvr{l} be bxn{l} matmul Wv{l}.\n\
959 Let bzz{l} be bxn{l} matmul Wz{l}.\n\
960 Let bbr{l} be bxn{l} matmul Wb{l}.\n\
961 Let bar{l} be bxn{l} matmul Wa{l}.\n"
962 ));
963 match rows {
964 RowState::Shared => {
968 let bnk = bsz * nk;
969 let (csq, csk, csv, stt) = (
970 st(format!("Csq{l}"), 0),
971 st(format!("Csk{l}"), 0),
972 st(format!("Csv{l}"), 0),
973 st(format!("St{l}"), 0),
974 );
975 self.s.push_str(&format!(
976 "Let bqc{l} be causal conv of bqr{l} with weight Wcq{l} state {csq} over {taps} taps.\n\
977 Let bkc{l} be causal conv of bkr{l} with weight Wck{l} state {csk} over {taps} taps.\n\
978 Let bvc{l} be causal conv of bvr{l} with weight Wcv{l} state {csv} over {taps} taps.\n\
979 Let bqs{l} be silu of bqc{l}.\n\
980 Let bks{l} be silu of bkc{l}.\n\
981 Let bvs{l} be silu of bvc{l}.\n\
982 Let bqn{l} be l2norm of bqs{l} across {bnk} heads.\n\
983 Let bkn{l} be l2norm of bks{l} across {bnk} heads.\n\
984 Let bbt{l} be sigmoid of bbr{l}.\n\
985 Let bdy{l} be decay of bar{l} with bias Dt{l} rate Al{l}.\n\
986 Let bod{l} be gated delta of bqn{l} over bkn{l} and bvs{l} with {gate} bzz{l} beta bbt{l} decay bdy{l} state {stt} norm Gn{l} with {nv} heads {nk} groups.\n\
987 Let bao{l} be bod{l} matmul Wo{l}.\n"
988 ));
989 }
990 RowState::PerSequence => {
991 self.s.push_str(&format!("Let bod{l} be a {bsz} by {vw} tensor of Real.\n"));
992 for b in 0..bsz {
993 let r = b + 1;
994 let (csq, csk, csv, stt) = (
995 st(format!("Csq{l}"), b),
996 st(format!("Csk{l}"), b),
997 st(format!("Csv{l}"), b),
998 st(format!("St{l}"), b),
999 );
1000 self.s.push_str(&format!(
1001 "Let qr{l}_s{b} be row {r} of bqr{l}.\n\
1002 Let kr{l}_s{b} be row {r} of bkr{l}.\n\
1003 Let vr{l}_s{b} be row {r} of bvr{l}.\n\
1004 Let zz{l}_s{b} be row {r} of bzz{l}.\n\
1005 Let br{l}_s{b} be row {r} of bbr{l}.\n\
1006 Let ar{l}_s{b} be row {r} of bar{l}.\n\
1007 Let qc{l}_s{b} be causal conv of qr{l}_s{b} with weight Wcq{l} state {csq} over {taps} taps.\n\
1008 Let kc{l}_s{b} be causal conv of kr{l}_s{b} with weight Wck{l} state {csk} over {taps} taps.\n\
1009 Let vc{l}_s{b} be causal conv of vr{l}_s{b} with weight Wcv{l} state {csv} over {taps} taps.\n\
1010 Let qs{l}_s{b} be silu of qc{l}_s{b}.\n\
1011 Let ks{l}_s{b} be silu of kc{l}_s{b}.\n\
1012 Let vs{l}_s{b} be silu of vc{l}_s{b}.\n\
1013 Let qn{l}_s{b} be l2norm of qs{l}_s{b} across {nk} heads.\n\
1014 Let kn{l}_s{b} be l2norm of ks{l}_s{b} across {nk} heads.\n\
1015 Let bt{l}_s{b} be sigmoid of br{l}_s{b}.\n\
1016 Let dy{l}_s{b} be decay of ar{l}_s{b} with bias Dt{l} rate Al{l}.\n\
1017 Let od{l}_s{b} be gated delta of qn{l}_s{b} over kn{l}_s{b} and vs{l}_s{b} with {gate} zz{l}_s{b} beta bt{l}_s{b} decay dy{l}_s{b} state {stt} norm Gn{l} with {nv} heads {nk} groups.\n\
1018 Set row {r} of bod{l} to od{l}_s{b}.\n"
1019 ));
1020 }
1021 self.s.push_str(&format!("Let bao{l} be bod{l} matmul Wo{l}.\n"));
1022 }
1023 }
1024 }
1025}
1026
1027fn score_clauses(a: &Attention) -> String {
1031 let mut clauses = a.window.map_or_else(String::new, |w| w.clause());
1032 if (a.scale - 1.0 / (a.head_dim as f64).sqrt()).abs() > 1e-15 {
1033 clauses.push_str(&format!(" scaled by {:?}", a.scale));
1034 }
1035 if let Some(cap) = a.softcap {
1036 clauses.push_str(&format!(" capped at {cap:?}"));
1037 }
1038 clauses
1039}
1040
1041fn capped_logits(product: &str, logits: &str, cap: Option<f64>) -> String {
1044 match cap {
1045 Some(cap) => format!("Let {logits}_raw be {product}.\nLet {logits} be {logits}_raw capped at {cap:?}.\n"),
1046 None => format!("Let {logits} be {product}.\n"),
1047 }
1048}
1049
1050fn qk_norm_segments(a: &Attention) -> ((usize, usize), (usize, usize)) {
1053 if a.qk_norm_whole {
1054 ((1, 1), (a.heads * a.head_dim, a.kv_heads * a.head_dim))
1055 } else {
1056 ((a.heads, a.kv_heads), (a.head_dim, a.head_dim))
1057 }
1058}
1059
1060pub fn layer_input(l: usize) -> String {
1062 if l == 0 {
1063 "x".to_string()
1064 } else {
1065 format!("xo{}", l - 1)
1066 }
1067}
1068
1069pub fn stacked_layer_input(l: usize) -> String {
1071 if l == 0 {
1072 "X".to_string()
1073 } else {
1074 format!("bxo{}", l - 1)
1075 }
1076}
1077
1078pub fn forward(spec: &ModelSpec, dir: &str) -> Authored {
1080 let mut p = Program::new(spec, dir);
1081 p.s.push_str(&format!("Let x be a 1 by {} tensor of Real.\n", spec.d));
1082 p.rotary_tables(None, false, 1);
1083 let mut cur = "x".to_string();
1084 for l in 0..spec.layers.len() {
1085 p.layer(l, &cur, AttnForm::Current);
1086 cur = format!("xo{l}");
1087 }
1088 p.head(&cur);
1089 p.finish()
1090}
1091
1092pub fn decode_step(spec: &ModelSpec, dir: &str, ctx: usize, tok: usize, pos: usize) -> Authored {
1096 let mut p = Program::new(spec, dir);
1097 p.s.push_str(&format!("Let tok be {tok}.\nLet pos be {pos}.\n"));
1098 p.embed();
1099 p.rope_cache(ctx);
1100 let mut cur = "x".to_string();
1101 for l in 0..spec.layers.len() {
1102 p.layer(l, &cur, AttnForm::Cached { ctx });
1103 cur = format!("xo{l}");
1104 }
1105 p.head(&cur);
1106 p.next_token();
1107 p.finish()
1108}
1109
1110pub fn prefill(spec: &ModelSpec, dir: &str, ctx: usize, toks: &[usize], pos0: usize) -> Authored {
1116 let t = toks.len();
1117 assert!(
1118 t > 0 && pos0 >= 1 && pos0 + t - 1 <= ctx,
1119 "a chunk of {t} tokens from position {pos0} fits a context of {ctx}"
1120 );
1121 let mut p = Program::new(spec, dir);
1122 p.s.push_str(&format!("Let pos_s0 be {pos0}.\n"));
1123 p.batched_embed(toks);
1124 p.rope_cache(ctx);
1125 let mut cur = stacked_layer_input(0);
1126 for l in 0..spec.layers.len() {
1127 p.stacked_layer(l, &cur, ctx, t, RowState::Shared);
1128 cur = stacked_layer_input(l + 1);
1129 }
1130 p.last_row_head(&cur, t);
1131 p.finish()
1132}
1133
1134pub fn decode_batch(spec: &ModelSpec, dir: &str, ctx: usize, seqs: &[(usize, usize)]) -> Authored {
1139 let bsz = seqs.len();
1140 let mut p = Program::new(spec, dir);
1141 for (b, (_, pos)) in seqs.iter().enumerate() {
1142 p.s.push_str(&format!("Let pos_s{b} be {pos}.\n"));
1143 }
1144 p.batched_embed(&seqs.iter().map(|(tok, _)| *tok).collect::<Vec<_>>());
1145 p.rope_cache(ctx);
1146 let mut cur = stacked_layer_input(0);
1147 for l in 0..spec.layers.len() {
1148 p.stacked_layer(l, &cur, ctx, bsz, RowState::PerSequence);
1149 cur = stacked_layer_input(l + 1);
1150 }
1151 p.batched_head(&cur, bsz);
1152 p.finish()
1153}
1154
1155pub fn layer(spec: &ModelSpec, l: usize, dir: &str) -> Authored {
1158 let mut p = Program::new(spec, dir);
1159 let input = layer_input(l);
1160 p.s.push_str(&format!("Let {input} be a 1 by {} tensor of Real.\n", spec.d));
1161 p.rotary_tables(Some(l), false, 1);
1162 p.layer(l, &input, AttnForm::Current);
1163 p.finish()
1164}
1165
1166pub fn decode_layer(spec: &ModelSpec, l: usize, dir: &str, ctx: usize, pos: usize) -> Authored {
1168 let mut p = Program::new(spec, dir);
1169 let input = layer_input(l);
1170 p.s.push_str(&format!("Let pos be {pos}.\n"));
1171 p.s.push_str(&format!("Let {input} be a 1 by {} tensor of Real.\n", spec.d));
1172 p.rotary_tables(Some(l), true, ctx);
1173 p.layer(l, &input, AttnForm::Cached { ctx });
1174 p.finish()
1175}
1176
1177pub fn head(spec: &ModelSpec, dir: &str) -> Authored {
1179 let mut p = Program::new(spec, dir);
1180 let input = layer_input(spec.layers.len());
1181 p.s.push_str(&format!("Let {input} be a 1 by {} tensor of Real.\n", spec.d));
1182 p.head(&input);
1183 p.finish()
1184}
1185
1186pub fn decode_embed(spec: &ModelSpec, dir: &str, tok: usize) -> Authored {
1188 let mut p = Program::new(spec, dir);
1189 p.s.push_str(&format!("Let tok be {tok}.\n"));
1190 p.embed();
1191 p.finish()
1192}
1193
1194pub fn decode_head(spec: &ModelSpec, dir: &str) -> Authored {
1196 let mut p = Program::new(spec, dir);
1197 let input = layer_input(spec.layers.len());
1198 p.s.push_str(&format!("Let {input} be a 1 by {} tensor of Real.\n", spec.d));
1199 p.head(&input);
1200 p.next_token();
1201 p.finish()
1202}
1203
1204pub fn decode_batch_embed(spec: &ModelSpec, dir: &str, toks: &[usize]) -> Authored {
1206 let mut p = Program::new(spec, dir);
1207 p.batched_embed(toks);
1208 p.finish()
1209}
1210
1211pub fn stacked_layer(spec: &ModelSpec, l: usize, dir: &str, ctx: usize, poss: &[usize], rows: RowState) -> Authored {
1214 let bsz = poss.len();
1215 let mut p = Program::new(spec, dir);
1216 let read = if rows == RowState::Shared { &poss[..1] } else { poss };
1218 for (b, pos) in read.iter().enumerate() {
1219 p.s.push_str(&format!("Let pos_s{b} be {pos}.\n"));
1220 }
1221 let input = stacked_layer_input(l);
1222 p.s.push_str(&format!("Let {input} be a {bsz} by {} tensor of Real.\n", spec.d));
1223 p.rotary_tables(Some(l), true, ctx);
1224 p.stacked_layer(l, &input, ctx, bsz, rows);
1225 p.finish()
1226}
1227
1228pub fn prefill_head(spec: &ModelSpec, dir: &str, chunk: usize) -> Authored {
1230 let mut p = Program::new(spec, dir);
1231 let input = stacked_layer_input(spec.layers.len());
1232 p.s.push_str(&format!("Let {input} be a {chunk} by {} tensor of Real.\n", spec.d));
1233 p.last_row_head(&input, chunk);
1234 p.finish()
1235}
1236
1237pub fn decode_batch_head(spec: &ModelSpec, dir: &str, bsz: usize) -> Authored {
1239 let mut p = Program::new(spec, dir);
1240 let input = stacked_layer_input(spec.layers.len());
1241 p.s.push_str(&format!("Let {input} be a {bsz} by {} tensor of Real.\n", spec.d));
1242 p.batched_head(&input, bsz);
1243 p.finish()
1244}
1245
1246use crate::spatial::tensor_parallel::{SliceAxis, TpCollective, TpManifest, TpSlice};
1259
1260pub type TpAuthored = (
1262 String,
1263 HashMap<String, (String, usize)>,
1264 HashMap<String, String>,
1265 TpManifest,
1266);
1267
1268pub fn check_tp(spec: &ModelSpec, tp: usize) -> Result<(), NotYet> {
1271 check(spec)?;
1272 if tp == 0 {
1273 return Err(NotYet("a tensor-parallel width of zero chips"));
1274 }
1275 for layer in &spec.layers {
1276 match &layer.mixer {
1277 Mixer::Attention(a) if a.bias || a.output_bias || a.sinks => {
1278 return Err(NotYet("a tensor-parallel attention with biases or sinks"))
1279 }
1280 Mixer::Attention(a) if a.qk_norm_whole => return Err(NotYet("a tensor-parallel QK norm across every head")),
1282 Mixer::Mla(_) => return Err(NotYet("a tensor-parallel latent attention")),
1283 _ => {}
1284 }
1285 if let Ffn::Moe(_) = &layer.ffn {
1286 return Err(NotYet("a tensor-parallel expert mixture"));
1287 }
1288 }
1289 Ok(())
1290}
1291
1292fn run_of(n: usize, tp: usize, c: usize) -> (usize, usize) {
1295 let (lo, hi) = (c * n / tp, (c + 1) * n / tp);
1296 (lo, hi - lo)
1297}
1298
1299fn share(units: usize, parts: usize, tp: usize, c: usize) -> ((usize, usize), (usize, usize)) {
1309 if tp <= units {
1310 return (run_of(units, tp, c), (0, parts));
1311 }
1312 let u = c * units / tp;
1313 let first = (u * tp).div_ceil(units);
1314 let holders = ((u + 1) * tp).div_ceil(units) - first;
1315 let (p0, pn) = run_of(parts, holders, c - first);
1316 ((u, usize::from(pn > 0)), (p0, pn))
1317}
1318
1319fn attn_heads_of_chip(a: &Attention, tp: usize, c: usize) -> ((usize, usize), (usize, usize)) {
1327 let g = a.heads / a.kv_heads;
1328 let ((k0, kn), (p0, pn)) = share(a.kv_heads, g, tp, c);
1329 ((k0 * g + p0, kn * pn), (k0, kn))
1330}
1331
1332fn gate_clause(g: &GatedDelta) -> &'static str {
1341 match g.gate {
1342 GateActivation::Silu => "gate",
1343 GateActivation::Sigmoid => "sigmoid gate",
1344 }
1345}
1346
1347fn gdn_heads_of_chip(g: &GatedDelta, tp: usize, c: usize) -> (Vec<(usize, usize)>, Vec<(usize, usize)>) {
1348 let (nk, ghd) = (g.k_heads, g.head_dim);
1349 let ((k0, kn), (r0, rn)) = share(nk, g.v_heads / nk, tp, c);
1350 (vec![(k0 * ghd, kn * ghd)], (r0..r0 + rn).map(|r| (r * nk + k0, kn)).collect())
1351}
1352
1353fn heads_in(blocks: &[(usize, usize)]) -> usize {
1355 blocks.iter().map(|(_, n)| n).sum()
1356}
1357
1358fn head_cols(heads: &[(usize, usize)], width: usize) -> Vec<(usize, usize)> {
1360 heads.iter().map(|(h, n)| (h * width, n * width)).collect()
1361}
1362
1363impl Program<'_> {
1364 #[allow(clippy::too_many_arguments)]
1366 fn decl_slice(
1367 &mut self,
1368 m: &mut TpManifest,
1369 var: &str,
1370 c: usize,
1371 of: &str,
1372 of_shape: (usize, usize),
1373 axis: SliceAxis,
1374 ranges: Vec<(usize, usize)>,
1375 ) {
1376 let slice = TpSlice {
1377 of: of.to_string(),
1378 of_shape,
1379 axis,
1380 ranges,
1381 };
1382 let (r, cols) = slice.shape();
1383 self.s.push_str(&format!("Let {var} be a {r} by {cols} tensor of Real.\n"));
1384 m.place(var, c, Some(slice));
1385 }
1386
1387 #[allow(clippy::too_many_arguments)]
1389 fn decl_q8_slice(
1390 &mut self,
1391 m: &mut TpManifest,
1392 var: &str,
1393 c: usize,
1394 of: &str,
1395 of_shape: (usize, usize),
1396 axis: SliceAxis,
1397 ranges: Vec<(usize, usize)>,
1398 ) {
1399 self.q8w.insert(of.to_string(), format!("{}/{of}", self.dir));
1400 self.decl_slice(m, var, c, of, of_shape, axis, ranges);
1401 }
1402
1403 fn fold(&mut self, m: &mut TpManifest, out: &str, partials: Vec<String>) {
1406 let mut folds = Vec::new();
1407 let mut acc = partials[0].clone();
1408 for (i, p) in partials.iter().enumerate().skip(1) {
1409 let binder = if i + 1 == partials.len() {
1410 out.to_string()
1411 } else {
1412 format!("{out}_f{i}")
1413 };
1414 self.s.push_str(&format!("Let {binder} be {acc} plus {p}.\n"));
1415 folds.push(binder.clone());
1416 acc = binder;
1417 }
1418 m.collectives.push(TpCollective {
1419 out: out.to_string(),
1420 partials,
1421 folds,
1422 });
1423 }
1424
1425 fn zero_partial(&mut self, m: &mut TpManifest, out: &str, like: &str, c: usize) -> String {
1428 self.s.push_str(&format!("Let {out} be {like} scaled by 0.0.\n"));
1429 m.place(out, c, None);
1430 out.to_string()
1431 }
1432
1433 fn decl_attn_weight_slices(&mut self, m: &mut TpManifest, a: &Attention, l: usize, c: usize, tp: usize) {
1436 let Dims { d, aq, akv, avd, ao, hd, .. } = attn_dims(self.spec, a);
1437 let vd = a.value_dim;
1438 let ((q0, hq), (kv0, hkv)) = attn_heads_of_chip(a, tp, c);
1439 let (q, k) = (vec![(q0 * hd, hq * hd)], vec![(kv0 * hd, hkv * hd)]);
1440 let (o, v) = (vec![(q0 * vd, hq * vd)], vec![(kv0 * vd, hkv * vd)]);
1441 self.decl_q8_slice(m, &format!("Wq{l}_c{c}"), c, &format!("Wq{l}"), (d, aq), SliceAxis::Cols, q.clone());
1442 if a.output_gate {
1443 self.decl_q8_slice(m, &format!("Wqg{l}_c{c}"), c, &format!("Wqg{l}"), (d, aq), SliceAxis::Cols, q.clone());
1444 }
1445 self.decl_q8_slice(m, &format!("Wk{l}_c{c}"), c, &format!("Wk{l}"), (d, akv), SliceAxis::Cols, k.clone());
1446 self.decl_q8_slice(m, &format!("Wv{l}_c{c}"), c, &format!("Wv{l}"), (d, avd), SliceAxis::Cols, v);
1447 self.decl_q8_slice(m, &format!("Wo{l}_c{c}"), c, &format!("Wo{l}"), (ao, d), SliceAxis::Rows, o);
1448 }
1449
1450 #[allow(clippy::too_many_arguments)]
1452 fn decl_attn_cache_slices(
1453 &mut self,
1454 m: &mut TpManifest,
1455 a: &Attention,
1456 l: usize,
1457 c: usize,
1458 tp: usize,
1459 ctx: usize,
1460 sfx: &str,
1461 ) {
1462 let Dims { akv, avd, hd, .. } = attn_dims(self.spec, a);
1463 let (_, (kv0, hkv)) = attn_heads_of_chip(a, tp, c);
1464 let (k, v) = (vec![(kv0 * hd, hkv * hd)], vec![(kv0 * a.value_dim, hkv * a.value_dim)]);
1465 self.decl_slice(m, &format!("Kc{l}{sfx}_c{c}"), c, &format!("Kc{l}{sfx}"), (ctx, akv), SliceAxis::Cols, k);
1466 self.decl_slice(m, &format!("Vc{l}{sfx}_c{c}"), c, &format!("Vc{l}{sfx}"), (ctx, avd), SliceAxis::Cols, v);
1467 }
1468
1469 fn decl_gdn_weight_slices(&mut self, m: &mut TpManifest, g: &GatedDelta, l: usize, c: usize, tp: usize) {
1472 let d = self.spec.d;
1473 let (taps, ghd, nv) = (g.conv_taps, g.head_dim, g.v_heads);
1474 let (qk, vw) = (g.k_heads * ghd, nv * ghd);
1475 let (k, heads) = gdn_heads_of_chip(g, tp, c);
1476 let v = head_cols(&heads, ghd);
1477 let n = |base: &str| format!("{base}{l}_c{c}");
1478 let f = |base: &str| format!("{base}{l}");
1479 self.decl_q8_slice(m, &n("Wq"), c, &f("Wq"), (d, qk), SliceAxis::Cols, k.clone());
1480 self.decl_q8_slice(m, &n("Wk"), c, &f("Wk"), (d, qk), SliceAxis::Cols, k.clone());
1481 self.decl_q8_slice(m, &n("Wv"), c, &f("Wv"), (d, vw), SliceAxis::Cols, v.clone());
1482 self.decl_q8_slice(m, &n("Wz"), c, &f("Wz"), (d, vw), SliceAxis::Cols, v.clone());
1483 self.decl_slice(m, &n("Wcq"), c, &f("Wcq"), (taps, qk), SliceAxis::Cols, k.clone());
1484 self.decl_slice(m, &n("Wck"), c, &f("Wck"), (taps, qk), SliceAxis::Cols, k);
1485 self.decl_slice(m, &n("Wcv"), c, &f("Wcv"), (taps, vw), SliceAxis::Cols, v.clone());
1486 self.decl_q8_slice(m, &n("Wb"), c, &f("Wb"), (d, nv), SliceAxis::Cols, heads.clone());
1487 self.decl_q8_slice(m, &n("Wa"), c, &f("Wa"), (d, nv), SliceAxis::Cols, heads.clone());
1488 self.decl_slice(m, &n("Dt"), c, &f("Dt"), (1, nv), SliceAxis::Cols, heads.clone());
1489 self.decl_slice(m, &n("Al"), c, &f("Al"), (1, nv), SliceAxis::Cols, heads);
1490 self.decl_q8_slice(m, &n("Wo"), c, &f("Wo"), (vw, d), SliceAxis::Rows, v);
1491 }
1492
1493 #[allow(clippy::too_many_arguments)]
1496 fn decl_gdn_state_slices(&mut self, m: &mut TpManifest, g: &GatedDelta, l: usize, c: usize, tp: usize, sfx: &str) {
1497 let (taps, ghd) = (g.conv_taps, g.head_dim);
1498 let (qk, vw) = (g.k_heads * ghd, g.v_heads * ghd);
1499 let (k, heads) = gdn_heads_of_chip(g, tp, c);
1500 let v = head_cols(&heads, ghd);
1501 self.decl_slice(m, &format!("Csq{l}{sfx}_c{c}"), c, &format!("Csq{l}{sfx}"), (taps, qk), SliceAxis::Cols, k.clone());
1502 self.decl_slice(m, &format!("Csk{l}{sfx}_c{c}"), c, &format!("Csk{l}{sfx}"), (taps, qk), SliceAxis::Cols, k);
1503 self.decl_slice(m, &format!("Csv{l}{sfx}_c{c}"), c, &format!("Csv{l}{sfx}"), (taps, vw), SliceAxis::Cols, v.clone());
1504 self.decl_slice(m, &format!("St{l}{sfx}_c{c}"), c, &format!("St{l}{sfx}"), (vw, ghd), SliceAxis::Rows, v);
1505 }
1506
1507 fn decl_ffn_slices(&mut self, m: &mut TpManifest, l: usize, dff: usize, c: usize, tp: usize) {
1509 let d = self.spec.d;
1510 let f = vec![run_of(dff, tp, c)];
1511 self.decl_q8_slice(m, &format!("Wg{l}_c{c}"), c, &format!("Wg{l}"), (d, dff), SliceAxis::Cols, f.clone());
1512 self.decl_q8_slice(m, &format!("Wu{l}_c{c}"), c, &format!("Wu{l}"), (d, dff), SliceAxis::Cols, f.clone());
1513 self.decl_q8_slice(m, &format!("Wd{l}_c{c}"), c, &format!("Wd{l}"), (dff, d), SliceAxis::Rows, f);
1514 }
1515
1516 fn tp_layer(&mut self, m: &mut TpManifest, l: usize, cur: &str, tp: usize, ctx: usize) {
1518 let spec = self.spec;
1519 let d = spec.d;
1520 self.decl_f32(&format!("an{l}"), 1, d);
1521 self.s.push_str(&format!("Let xn{l} be rmsnorm of {cur} with weight an{l}.\n"));
1522 let mut ao = Vec::new();
1523 match &spec.layers[l].mixer {
1524 Mixer::Attention(a) => {
1525 if a.qk_norm {
1526 self.decl_f32(&format!("Qn{l}"), 1, a.head_dim);
1527 self.decl_f32(&format!("Kn{l}"), 1, a.head_dim);
1528 }
1529 for c in 0..tp {
1530 let ((_, hq), (_, hkv)) = attn_heads_of_chip(a, tp, c);
1531 if hq == 0 {
1532 ao.push(self.zero_partial(m, &format!("ao{l}_c{c}"), &format!("xn{l}"), c));
1533 continue;
1534 }
1535 self.decl_attn_weight_slices(m, a, l, c, tp);
1536 self.decl_attn_cache_slices(m, a, l, c, tp, ctx, "");
1537 let mut bases = vec!["qr", "kr", "vr", "ct", "ao"];
1538 if a.output_gate {
1539 bases.extend(["gr", "gs", "cg"]);
1540 }
1541 if a.qk_norm {
1542 bases.extend(["qm", "km"]);
1543 }
1544 if a.rope.is_some() {
1545 bases.extend(["qp", "kp"]);
1546 }
1547 if a.qk_unit_norm {
1548 bases.extend(["qu", "ku"]);
1549 }
1550 if a.temperature.is_some() {
1551 bases.push("qt");
1552 }
1553 for base in bases {
1554 m.place(&format!("{base}{l}_c{c}"), c, None);
1555 }
1556 self.s.push_str(&format!("Let qr{l}_c{c} be xn{l} matmul Wq{l}_c{c}.\n"));
1557 if a.output_gate {
1558 self.s.push_str(&format!("Let gr{l}_c{c} be xn{l} matmul Wqg{l}_c{c}.\n"));
1559 }
1560 self.s.push_str(&format!(
1561 "Let kr{l}_c{c} be xn{l} matmul Wk{l}_c{c}.\nLet vr{l}_c{c} be xn{l} matmul Wv{l}_c{c}.\n"
1562 ));
1563 let (mut q, mut k) = (format!("qr{l}_c{c}"), format!("kr{l}_c{c}"));
1564 if a.qk_norm {
1565 self.s.push_str(&format!(
1566 "Let qm{l}_c{c} be rmsnorm of qr{l}_c{c} with weight Qn{l} across {hq} heads.\n\
1567 Let km{l}_c{c} be rmsnorm of kr{l}_c{c} with weight Kn{l} across {hkv} heads.\n"
1568 ));
1569 (q, k) = (format!("qm{l}_c{c}"), format!("km{l}_c{c}"));
1570 }
1571 if a.rope.is_some() {
1572 let angles = self.angles(l, AttnForm::Cached { ctx }, "pos");
1573 self.s.push_str(&format!(
1574 "Let qp{l}_c{c} be rope of {q} with {angles} across {hq} heads.\n\
1575 Let kp{l}_c{c} be rope of {k} with {angles} across {hkv} heads.\n"
1576 ));
1577 (q, k) = (format!("qp{l}_c{c}"), format!("kp{l}_c{c}"));
1578 }
1579 (q, k) = self.llama4_qk(a, (q, k), (hq, hkv), hq, Some(("pos".to_string(), ctx)), &|base| format!("{base}{l}_c{c}"));
1580 let w = score_clauses(a);
1581 self.s.push_str(&format!(
1582 "Set row pos of Kc{l}_c{c} to {k}.\n\
1583 Set row pos of Vc{l}_c{c} to vr{l}_c{c}.\n\
1584 Let ct{l}_c{c} be attention of {q} over Kc{l}_c{c} and Vc{l}_c{c} with {hq} heads {hkv} groups up to pos{w}.\n"
1585 ));
1586 if a.output_gate {
1587 self.s.push_str(&format!(
1588 "Let gs{l}_c{c} be sigmoid of gr{l}_c{c}.\n\
1589 Let cg{l}_c{c} be ct{l}_c{c} hadamard gs{l}_c{c}.\n\
1590 Let ao{l}_c{c} be cg{l}_c{c} matmul Wo{l}_c{c}.\n"
1591 ));
1592 } else {
1593 self.s.push_str(&format!("Let ao{l}_c{c} be ct{l}_c{c} matmul Wo{l}_c{c}.\n"));
1594 }
1595 ao.push(format!("ao{l}_c{c}"));
1596 }
1597 }
1598 Mixer::GatedDelta(g) => {
1599 let gate = gate_clause(g);
1600 let (taps, ghd) = (g.conv_taps, g.head_dim);
1601 self.decl_f32(&format!("Gn{l}"), 1, ghd);
1602 for c in 0..tp {
1603 let (k, heads) = gdn_heads_of_chip(g, tp, c);
1604 let (nk_c, nv_c) = (k[0].1 / ghd, heads_in(&heads));
1605 if nv_c == 0 {
1606 ao.push(self.zero_partial(m, &format!("ao{l}_c{c}"), &format!("xn{l}"), c));
1607 continue;
1608 }
1609 self.decl_gdn_weight_slices(m, g, l, c, tp);
1610 self.decl_gdn_state_slices(m, g, l, c, tp, "");
1611 for base in [
1612 "qr", "kr", "vr", "zz", "qc", "kc", "vc", "qs", "ks", "vs", "qn", "kn", "br", "bt", "ar",
1613 "dy", "od", "ao",
1614 ] {
1615 m.place(&format!("{base}{l}_c{c}"), c, None);
1616 }
1617 self.s.push_str(&format!(
1618 "Let qr{l}_c{c} be xn{l} matmul Wq{l}_c{c}.\n\
1619 Let kr{l}_c{c} be xn{l} matmul Wk{l}_c{c}.\n\
1620 Let vr{l}_c{c} be xn{l} matmul Wv{l}_c{c}.\n\
1621 Let zz{l}_c{c} be xn{l} matmul Wz{l}_c{c}.\n\
1622 Let qc{l}_c{c} be causal conv of qr{l}_c{c} with weight Wcq{l}_c{c} state Csq{l}_c{c} over {taps} taps.\n\
1623 Let kc{l}_c{c} be causal conv of kr{l}_c{c} with weight Wck{l}_c{c} state Csk{l}_c{c} over {taps} taps.\n\
1624 Let vc{l}_c{c} be causal conv of vr{l}_c{c} with weight Wcv{l}_c{c} state Csv{l}_c{c} over {taps} taps.\n\
1625 Let qs{l}_c{c} be silu of qc{l}_c{c}.\n\
1626 Let ks{l}_c{c} be silu of kc{l}_c{c}.\n\
1627 Let vs{l}_c{c} be silu of vc{l}_c{c}.\n\
1628 Let qn{l}_c{c} be l2norm of qs{l}_c{c} across {nk_c} heads.\n\
1629 Let kn{l}_c{c} be l2norm of ks{l}_c{c} across {nk_c} heads.\n\
1630 Let br{l}_c{c} be xn{l} matmul Wb{l}_c{c}.\n\
1631 Let bt{l}_c{c} be sigmoid of br{l}_c{c}.\n\
1632 Let ar{l}_c{c} be xn{l} matmul Wa{l}_c{c}.\n\
1633 Let dy{l}_c{c} be decay of ar{l}_c{c} with bias Dt{l}_c{c} rate Al{l}_c{c}.\n\
1634 Let od{l}_c{c} be gated delta of qn{l}_c{c} over kn{l}_c{c} and vs{l}_c{c} with {gate} zz{l}_c{c} beta bt{l}_c{c} decay dy{l}_c{c} state St{l}_c{c} norm Gn{l} with {nv_c} heads {nk_c} groups.\n\
1635 Let ao{l}_c{c} be od{l}_c{c} matmul Wo{l}_c{c}.\n"
1636 ));
1637 ao.push(format!("ao{l}_c{c}"));
1638 }
1639 }
1640 Mixer::Mla(_) => unreachable!("author::check_tp refuses latent attention"),
1641 }
1642 self.fold(m, &format!("ao{l}"), ao);
1643 self.decl_f32(&format!("fn{l}"), 1, d);
1644 let ao = self.post_norm(l, &format!("ao{l}"), true);
1645 self.s.push_str(&format!(
1646 "Let xr{l} be {cur} plus {ao}.\nLet x2n{l} be rmsnorm of xr{l} with weight fn{l}.\n"
1647 ));
1648 let Ffn::Dense { dff, .. } = spec.layers[l].ffn else {
1649 unreachable!("check_tp refuses a tensor-parallel expert mixture")
1650 };
1651 let mut dn = Vec::new();
1652 for c in 0..tp {
1653 if run_of(dff, tp, c).1 == 0 {
1654 dn.push(self.zero_partial(m, &format!("dn{l}_c{c}"), &format!("x2n{l}"), c));
1655 continue;
1656 }
1657 self.decl_ffn_slices(m, l, dff, c, tp);
1658 self.s.push_str(&format!(
1659 "Let gt{l}_c{c} be x2n{l} matmul Wg{l}_c{c}.\n\
1660 Let up{l}_c{c} be x2n{l} matmul Wu{l}_c{c}.\n"
1661 ));
1662 let bound = self.glu(l, &format!("gt{l}_c{c}"), &format!("up{l}_c{c}"), &format!("hh{l}_c{c}"));
1663 self.s.push_str(&format!("Let dn{l}_c{c} be hh{l}_c{c} matmul Wd{l}_c{c}.\n"));
1664 for name in [format!("gt{l}_c{c}"), format!("up{l}_c{c}")].into_iter().chain(bound).chain([format!("dn{l}_c{c}")]) {
1665 m.place(&name, c, None);
1666 }
1667 dn.push(format!("dn{l}_c{c}"));
1668 }
1669 self.fold(m, &format!("dn{l}"), dn);
1670 let dn = self.post_norm(l, &format!("dn{l}"), false);
1671 self.s.push_str(&format!("Let xo{l} be xr{l} plus {dn}.\n"));
1672 }
1673
1674 #[allow(clippy::too_many_arguments)]
1679 fn stacked_tp_layer(&mut self, m: &mut TpManifest, l: usize, cur: &str, tp: usize, ctx: usize, bsz: usize) {
1680 let spec = self.spec;
1681 let d = spec.d;
1682 self.decl_f32(&format!("an{l}"), 1, d);
1683 self.s.push_str(&format!("Let bxn{l} be rmsnorm of {cur} with weight an{l}.\n"));
1684 let mut ao = Vec::new();
1685 match &spec.layers[l].mixer {
1686 Mixer::Attention(a) => {
1687 let hd = a.head_dim;
1688 if a.qk_norm {
1689 self.decl_f32(&format!("Qn{l}"), 1, hd);
1690 self.decl_f32(&format!("Kn{l}"), 1, hd);
1691 }
1692 for c in 0..tp {
1693 let ((_, hq), (_, hkv)) = attn_heads_of_chip(a, tp, c);
1694 if hq == 0 {
1695 ao.push(self.zero_partial(m, &format!("bao{l}_c{c}"), &format!("bxn{l}"), c));
1696 continue;
1697 }
1698 self.decl_attn_weight_slices(m, a, l, c, tp);
1699 for b in 0..bsz {
1700 self.decl_attn_cache_slices(m, a, l, c, tp, ctx, &format!("_s{b}"));
1701 }
1702 let mut bases = vec!["bqr", "bkr", "bvr", "bct", "bao"];
1703 if a.output_gate {
1704 bases.extend(["bgr", "bgs", "bcg"]);
1705 }
1706 for base in bases {
1707 m.place(&format!("{base}{l}_c{c}"), c, None);
1708 }
1709 self.s.push_str(&format!("Let bqr{l}_c{c} be bxn{l} matmul Wq{l}_c{c}.\n"));
1710 if a.output_gate {
1711 self.s.push_str(&format!("Let bgr{l}_c{c} be bxn{l} matmul Wqg{l}_c{c}.\n"));
1712 }
1713 self.s.push_str(&format!(
1714 "Let bkr{l}_c{c} be bxn{l} matmul Wk{l}_c{c}.\n\
1715 Let bvr{l}_c{c} be bxn{l} matmul Wv{l}_c{c}.\n\
1716 Let bct{l}_c{c} be a {bsz} by {} tensor of Real.\n",
1717 hq * a.value_dim
1718 ));
1719 for b in 0..bsz {
1720 let r = b + 1;
1721 let mut bases = vec!["qr", "kr", "vr", "ct"];
1722 if a.qk_norm {
1723 bases.extend(["qm", "km"]);
1724 }
1725 if a.rope.is_some() {
1726 bases.extend(["qp", "kp"]);
1727 }
1728 if a.qk_unit_norm {
1729 bases.extend(["qu", "ku"]);
1730 }
1731 if a.temperature.is_some() {
1732 bases.push("qt");
1733 }
1734 for base in bases {
1735 m.place(&format!("{base}{l}_s{b}_c{c}"), c, None);
1736 }
1737 self.s.push_str(&format!(
1738 "Let qr{l}_s{b}_c{c} be row {r} of bqr{l}_c{c}.\n\
1739 Let kr{l}_s{b}_c{c} be row {r} of bkr{l}_c{c}.\n\
1740 Let vr{l}_s{b}_c{c} be row {r} of bvr{l}_c{c}.\n"
1741 ));
1742 let (mut q, mut k) = (format!("qr{l}_s{b}_c{c}"), format!("kr{l}_s{b}_c{c}"));
1743 if a.qk_norm {
1744 self.s.push_str(&format!(
1745 "Let qm{l}_s{b}_c{c} be rmsnorm of qr{l}_s{b}_c{c} with weight Qn{l} across {hq} heads.\n\
1746 Let km{l}_s{b}_c{c} be rmsnorm of kr{l}_s{b}_c{c} with weight Kn{l} across {hkv} heads.\n"
1747 ));
1748 (q, k) = (format!("qm{l}_s{b}_c{c}"), format!("km{l}_s{b}_c{c}"));
1749 }
1750 if a.rope.is_some() {
1751 let angles = self.angles(l, AttnForm::Cached { ctx }, &format!("pos_s{b}"));
1752 self.s.push_str(&format!(
1753 "Let qp{l}_s{b}_c{c} be rope of {q} with {angles} across {hq} heads.\n\
1754 Let kp{l}_s{b}_c{c} be rope of {k} with {angles} across {hkv} heads.\n"
1755 ));
1756 (q, k) = (format!("qp{l}_s{b}_c{c}"), format!("kp{l}_s{b}_c{c}"));
1757 }
1758 (q, k) = self.llama4_qk(a, (q, k), (hq, hkv), hq, Some((format!("pos_s{b}"), ctx)), &|base| format!("{base}{l}_s{b}_c{c}"));
1759 let w = score_clauses(a);
1760 self.s.push_str(&format!(
1761 "Set row pos_s{b} of Kc{l}_s{b}_c{c} to {k}.\n\
1762 Set row pos_s{b} of Vc{l}_s{b}_c{c} to vr{l}_s{b}_c{c}.\n\
1763 Let ct{l}_s{b}_c{c} be attention of {q} over Kc{l}_s{b}_c{c} and Vc{l}_s{b}_c{c} with {hq} heads {hkv} groups up to pos_s{b}{w}.\n\
1764 Set row {r} of bct{l}_c{c} to ct{l}_s{b}_c{c}.\n"
1765 ));
1766 }
1767 if a.output_gate {
1768 self.s.push_str(&format!(
1769 "Let bgs{l}_c{c} be sigmoid of bgr{l}_c{c}.\n\
1770 Let bcg{l}_c{c} be bct{l}_c{c} hadamard bgs{l}_c{c}.\n\
1771 Let bao{l}_c{c} be bcg{l}_c{c} matmul Wo{l}_c{c}.\n"
1772 ));
1773 } else {
1774 self.s.push_str(&format!("Let bao{l}_c{c} be bct{l}_c{c} matmul Wo{l}_c{c}.\n"));
1775 }
1776 ao.push(format!("bao{l}_c{c}"));
1777 }
1778 }
1779 Mixer::GatedDelta(g) => {
1780 let gate = gate_clause(g);
1781 let (taps, ghd) = (g.conv_taps, g.head_dim);
1782 self.decl_f32(&format!("Gn{l}"), 1, ghd);
1783 for c in 0..tp {
1784 let (k, heads) = gdn_heads_of_chip(g, tp, c);
1785 let (nk_c, nv_c) = (k[0].1 / ghd, heads_in(&heads));
1786 if nv_c == 0 {
1787 ao.push(self.zero_partial(m, &format!("bao{l}_c{c}"), &format!("bxn{l}"), c));
1788 continue;
1789 }
1790 self.decl_gdn_weight_slices(m, g, l, c, tp);
1791 for b in 0..bsz {
1792 self.decl_gdn_state_slices(m, g, l, c, tp, &format!("_s{b}"));
1793 }
1794 for base in ["bqr", "bkr", "bvr", "bzz", "bbr", "bar", "bod", "bao"] {
1795 m.place(&format!("{base}{l}_c{c}"), c, None);
1796 }
1797 self.s.push_str(&format!(
1798 "Let bqr{l}_c{c} be bxn{l} matmul Wq{l}_c{c}.\n\
1799 Let bkr{l}_c{c} be bxn{l} matmul Wk{l}_c{c}.\n\
1800 Let bvr{l}_c{c} be bxn{l} matmul Wv{l}_c{c}.\n\
1801 Let bzz{l}_c{c} be bxn{l} matmul Wz{l}_c{c}.\n\
1802 Let bbr{l}_c{c} be bxn{l} matmul Wb{l}_c{c}.\n\
1803 Let bar{l}_c{c} be bxn{l} matmul Wa{l}_c{c}.\n\
1804 Let bod{l}_c{c} be a {bsz} by {} tensor of Real.\n",
1805 nv_c * ghd
1806 ));
1807 for b in 0..bsz {
1808 let r = b + 1;
1809 for base in [
1810 "qr", "kr", "vr", "zz", "br", "ar", "qc", "kc", "vc", "qs", "ks", "vs", "qn", "kn", "bt",
1811 "dy", "od",
1812 ] {
1813 m.place(&format!("{base}{l}_s{b}_c{c}"), c, None);
1814 }
1815 self.s.push_str(&format!(
1816 "Let qr{l}_s{b}_c{c} be row {r} of bqr{l}_c{c}.\n\
1817 Let kr{l}_s{b}_c{c} be row {r} of bkr{l}_c{c}.\n\
1818 Let vr{l}_s{b}_c{c} be row {r} of bvr{l}_c{c}.\n\
1819 Let zz{l}_s{b}_c{c} be row {r} of bzz{l}_c{c}.\n\
1820 Let br{l}_s{b}_c{c} be row {r} of bbr{l}_c{c}.\n\
1821 Let ar{l}_s{b}_c{c} be row {r} of bar{l}_c{c}.\n\
1822 Let qc{l}_s{b}_c{c} be causal conv of qr{l}_s{b}_c{c} with weight Wcq{l}_c{c} state Csq{l}_s{b}_c{c} over {taps} taps.\n\
1823 Let kc{l}_s{b}_c{c} be causal conv of kr{l}_s{b}_c{c} with weight Wck{l}_c{c} state Csk{l}_s{b}_c{c} over {taps} taps.\n\
1824 Let vc{l}_s{b}_c{c} be causal conv of vr{l}_s{b}_c{c} with weight Wcv{l}_c{c} state Csv{l}_s{b}_c{c} over {taps} taps.\n\
1825 Let qs{l}_s{b}_c{c} be silu of qc{l}_s{b}_c{c}.\n\
1826 Let ks{l}_s{b}_c{c} be silu of kc{l}_s{b}_c{c}.\n\
1827 Let vs{l}_s{b}_c{c} be silu of vc{l}_s{b}_c{c}.\n\
1828 Let qn{l}_s{b}_c{c} be l2norm of qs{l}_s{b}_c{c} across {nk_c} heads.\n\
1829 Let kn{l}_s{b}_c{c} be l2norm of ks{l}_s{b}_c{c} across {nk_c} heads.\n\
1830 Let bt{l}_s{b}_c{c} be sigmoid of br{l}_s{b}_c{c}.\n\
1831 Let dy{l}_s{b}_c{c} be decay of ar{l}_s{b}_c{c} with bias Dt{l}_c{c} rate Al{l}_c{c}.\n\
1832 Let od{l}_s{b}_c{c} be gated delta of qn{l}_s{b}_c{c} over kn{l}_s{b}_c{c} and vs{l}_s{b}_c{c} with {gate} zz{l}_s{b}_c{c} beta bt{l}_s{b}_c{c} decay dy{l}_s{b}_c{c} state St{l}_s{b}_c{c} norm Gn{l} with {nv_c} heads {nk_c} groups.\n\
1833 Set row {r} of bod{l}_c{c} to od{l}_s{b}_c{c}.\n"
1834 ));
1835 }
1836 self.s.push_str(&format!("Let bao{l}_c{c} be bod{l}_c{c} matmul Wo{l}_c{c}.\n"));
1837 ao.push(format!("bao{l}_c{c}"));
1838 }
1839 }
1840 Mixer::Mla(_) => unreachable!("author::check_tp refuses latent attention"),
1841 }
1842 self.fold(m, &format!("bao{l}"), ao);
1843 self.decl_f32(&format!("fn{l}"), 1, d);
1844 let ao = self.post_norm(l, &format!("bao{l}"), true);
1845 self.s.push_str(&format!(
1846 "Let bxr{l} be {cur} plus {ao}.\nLet bx2n{l} be rmsnorm of bxr{l} with weight fn{l}.\n"
1847 ));
1848 let Ffn::Dense { dff, .. } = spec.layers[l].ffn else {
1849 unreachable!("check_tp refuses a tensor-parallel expert mixture")
1850 };
1851 let mut dn = Vec::new();
1852 for c in 0..tp {
1853 if run_of(dff, tp, c).1 == 0 {
1854 dn.push(self.zero_partial(m, &format!("bdn{l}_c{c}"), &format!("bx2n{l}"), c));
1855 continue;
1856 }
1857 self.decl_ffn_slices(m, l, dff, c, tp);
1858 self.s.push_str(&format!(
1859 "Let bgt{l}_c{c} be bx2n{l} matmul Wg{l}_c{c}.\n\
1860 Let bup{l}_c{c} be bx2n{l} matmul Wu{l}_c{c}.\n"
1861 ));
1862 let bound = self.glu(l, &format!("bgt{l}_c{c}"), &format!("bup{l}_c{c}"), &format!("bhh{l}_c{c}"));
1863 self.s.push_str(&format!("Let bdn{l}_c{c} be bhh{l}_c{c} matmul Wd{l}_c{c}.\n"));
1864 for name in [format!("bgt{l}_c{c}"), format!("bup{l}_c{c}")].into_iter().chain(bound).chain([format!("bdn{l}_c{c}")]) {
1865 m.place(&name, c, None);
1866 }
1867 dn.push(format!("bdn{l}_c{c}"));
1868 }
1869 self.fold(m, &format!("bdn{l}"), dn);
1870 let dn = self.post_norm(l, &format!("bdn{l}"), false);
1871 self.s.push_str(&format!("Let bxo{l} be bxr{l} plus {dn}.\n"));
1872 }
1873
1874 fn finish_tp(self, m: TpManifest) -> TpAuthored {
1875 let (s, f, q) = self.finish();
1876 (s, f, q, m)
1877 }
1878}
1879
1880pub fn tp_decode_step(spec: &ModelSpec, dir: &str, ctx: usize, tok: usize, pos: usize, tp: usize) -> Result<TpAuthored, NotYet> {
1882 check_tp(spec, tp)?;
1883 let mut p = Program::new(spec, dir);
1884 let mut m = TpManifest::new(tp);
1885 p.s.push_str(&format!("Let tok be {tok}.\nLet pos be {pos}.\n"));
1886 p.embed();
1887 p.rope_cache(ctx);
1888 let mut cur = "x".to_string();
1889 for l in 0..spec.layers.len() {
1890 p.tp_layer(&mut m, l, &cur, tp, ctx);
1891 cur = format!("xo{l}");
1892 }
1893 p.head(&cur);
1894 p.next_token();
1895 Ok(p.finish_tp(m))
1896}
1897
1898pub fn tp_decode_batch(spec: &ModelSpec, dir: &str, ctx: usize, seqs: &[(usize, usize)], tp: usize) -> Result<TpAuthored, NotYet> {
1901 check_tp(spec, tp)?;
1902 let bsz = seqs.len();
1903 let mut p = Program::new(spec, dir);
1904 let mut m = TpManifest::new(tp);
1905 for (b, (_, pos)) in seqs.iter().enumerate() {
1906 p.s.push_str(&format!("Let pos_s{b} be {pos}.\n"));
1907 }
1908 p.batched_embed(&seqs.iter().map(|(tok, _)| *tok).collect::<Vec<_>>());
1909 p.rope_cache(ctx);
1910 let mut cur = stacked_layer_input(0);
1911 for l in 0..spec.layers.len() {
1912 p.stacked_tp_layer(&mut m, l, &cur, tp, ctx, bsz);
1913 cur = stacked_layer_input(l + 1);
1914 }
1915 p.batched_head(&cur, bsz);
1916 Ok(p.finish_tp(m))
1917}
1918
1919pub fn tp_decode_layer(spec: &ModelSpec, l: usize, dir: &str, ctx: usize, pos: usize, tp: usize) -> Result<TpAuthored, NotYet> {
1922 check_tp(spec, tp)?;
1923 let mut p = Program::new(spec, dir);
1924 let mut m = TpManifest::new(tp);
1925 let input = layer_input(l);
1926 p.s.push_str(&format!("Let pos be {pos}.\n"));
1927 p.s.push_str(&format!("Let {input} be a 1 by {} tensor of Real.\n", spec.d));
1928 p.rotary_tables(Some(l), true, ctx);
1929 p.tp_layer(&mut m, l, &input, tp, ctx);
1930 Ok(p.finish_tp(m))
1931}
1932
1933pub fn tp_stacked_layer(spec: &ModelSpec, l: usize, dir: &str, ctx: usize, poss: &[usize], tp: usize) -> Result<TpAuthored, NotYet> {
1935 check_tp(spec, tp)?;
1936 let bsz = poss.len();
1937 let mut p = Program::new(spec, dir);
1938 let mut m = TpManifest::new(tp);
1939 for (b, pos) in poss.iter().enumerate() {
1940 p.s.push_str(&format!("Let pos_s{b} be {pos}.\n"));
1941 }
1942 let input = stacked_layer_input(l);
1943 p.s.push_str(&format!("Let {input} be a {bsz} by {} tensor of Real.\n", spec.d));
1944 p.rotary_tables(Some(l), true, ctx);
1945 p.stacked_tp_layer(&mut m, l, &input, tp, ctx, bsz);
1946 Ok(p.finish_tp(m))
1947}