1use std::collections::{BTreeMap, BTreeSet};
20
21use crate::tensor_graph::graph::{Storage, TensorGraph};
22
23pub const VENDOR_HEADER: &str =
25 "# vendor-ingestion artifact emitted by LOGOS — not our compute path";
26pub const TEMPLATE_MARK: &str = "# TEMPLATE — SambaFlow API unverified";
28pub const QWEN38_OPS: &[&str] = &[
30 "rmsnorm",
31 "rope",
32 "attention",
33 "sigmoid",
34 "hadamard",
35 "causal conv",
36 "silu",
37 "l2norm",
38 "decay",
39 "gated delta",
40 "swiglu",
41 "matmul",
42 "argmax",
43 "embedding",
44];
45
46pub struct TorchArtifact {
47 pub model_py: String,
48 pub compile_py: String,
49 pub manifest_json: String,
50}
51
52#[derive(Clone, Debug, PartialEq)]
54pub enum TensorSentence {
55 Decl {
57 var: String,
58 rows: usize,
59 cols: usize,
60 real: bool,
61 },
62 Fill {
64 var: String,
65 index: usize,
66 value: String,
67 },
68 Op { out: String, kind: OpKind },
70 Show,
72}
73
74#[derive(Clone, Debug, PartialEq)]
75pub enum OpKind {
76 Matmul {
77 a: String,
78 b: String,
79 },
80 Plus {
81 a: String,
82 b: String,
83 },
84 Hadamard {
85 a: String,
86 b: String,
87 },
88 Rmsnorm {
89 x: String,
90 residual: Option<String>,
91 weight: Option<String>,
92 heads: Option<usize>,
93 },
94 Rope {
95 q: String,
96 rc: String,
97 rs: String,
98 heads: Option<usize>,
99 },
100 Attention {
101 q: String,
102 k: String,
103 v: String,
104 heads: usize,
105 groups: usize,
106 },
107 FullAttention {
108 q: String,
109 k: String,
110 v: String,
111 heads: usize,
112 },
113 Sigmoid {
114 x: String,
115 },
116 Silu {
117 x: String,
118 },
119 Gelu {
120 x: String,
121 },
122 Softmax {
123 x: String,
124 },
125 Transpose {
126 x: String,
127 },
128 Layernorm {
129 x: String,
130 weight: String,
131 bias: String,
132 },
133 CausalConv {
134 x: String,
135 weight: String,
136 state: String,
137 taps: usize,
138 },
139 L2norm {
140 x: String,
141 heads: Option<usize>,
142 },
143 Decay {
144 ar: String,
145 dt: String,
146 alog: String,
147 },
148 GatedDelta {
149 q: String,
150 k: String,
151 v: String,
152 gate: String,
153 beta: String,
154 decay: String,
155 state: String,
156 norm: String,
157 hv: usize,
158 hk: usize,
159 },
160 Swiglu {
161 gate: String,
162 up: String,
163 },
164 Moe {
165 x: String,
166 },
167 Argmax {
168 x: String,
169 },
170 RowOf {
172 table: String,
173 tok: String,
174 },
175 SetRow {
177 table: String,
178 pos: String,
179 row: String,
180 },
181}
182
183impl OpKind {
184 pub fn name(&self) -> &'static str {
186 match self {
187 OpKind::Matmul { .. } => "matmul",
188 OpKind::Plus { .. } => "plus",
189 OpKind::Hadamard { .. } => "hadamard",
190 OpKind::Rmsnorm { .. } => "rmsnorm",
191 OpKind::Rope { .. } => "rope",
192 OpKind::Attention { .. } => "attention",
193 OpKind::FullAttention { .. } => "full attention",
194 OpKind::Sigmoid { .. } => "sigmoid",
195 OpKind::Silu { .. } => "silu",
196 OpKind::Gelu { .. } => "gelu",
197 OpKind::Softmax { .. } => "softmax",
198 OpKind::Transpose { .. } => "transpose",
199 OpKind::Layernorm { .. } => "layernorm",
200 OpKind::CausalConv { .. } => "causal conv",
201 OpKind::L2norm { .. } => "l2norm",
202 OpKind::Decay { .. } => "decay",
203 OpKind::GatedDelta { .. } => "gated delta",
204 OpKind::Swiglu { .. } => "swiglu",
205 OpKind::Moe { .. } => "moe",
206 OpKind::Argmax { .. } => "argmax",
207 OpKind::RowOf { .. } => "embedding",
208 OpKind::SetRow { .. } => "set row",
209 }
210 }
211
212 pub fn inputs(&self) -> Vec<&str> {
214 match self {
215 OpKind::Matmul { a, b } | OpKind::Plus { a, b } | OpKind::Hadamard { a, b } => {
216 vec![a, b]
217 }
218 OpKind::Rmsnorm {
219 x,
220 residual,
221 weight,
222 ..
223 } => {
224 let mut v = vec![x.as_str()];
225 v.extend(residual.as_deref());
226 v.extend(weight.as_deref());
227 v
228 }
229 OpKind::Rope { q, rc, rs, .. } => vec![q, rc, rs],
230 OpKind::Attention { q, k, v, .. } | OpKind::FullAttention { q, k, v, .. } => {
231 vec![q, k, v]
232 }
233 OpKind::Sigmoid { x }
234 | OpKind::Silu { x }
235 | OpKind::Gelu { x }
236 | OpKind::Softmax { x }
237 | OpKind::Transpose { x }
238 | OpKind::L2norm { x, .. }
239 | OpKind::Moe { x }
240 | OpKind::Argmax { x } => vec![x],
241 OpKind::Layernorm { x, weight, bias } => vec![x, weight, bias],
242 OpKind::CausalConv {
243 x, weight, state, ..
244 } => vec![x, weight, state],
245 OpKind::Decay { ar, dt, alog } => vec![ar, dt, alog],
246 OpKind::GatedDelta {
247 q,
248 k,
249 v,
250 gate,
251 beta,
252 decay,
253 state,
254 norm,
255 ..
256 } => vec![q, k, v, gate, beta, decay, state, norm],
257 OpKind::Swiglu { gate, up } => vec![gate, up],
258 OpKind::RowOf { table, tok } => vec![table, tok],
259 OpKind::SetRow { table, pos, row } => vec![table, pos, row],
260 }
261 }
262
263 pub fn writes(&self) -> Option<&str> {
265 match self {
266 OpKind::CausalConv { state, .. } | OpKind::GatedDelta { state, .. } => Some(state),
267 OpKind::SetRow { table, .. } => Some(table),
268 _ => None,
269 }
270 }
271}
272
273fn num(w: &str, what: &str, line: &str) -> Result<usize, String> {
276 w.parse::<usize>()
277 .map_err(|_| format!("`{line}`: {what} must be an integer literal, got `{w}`"))
278}
279
280fn read_sentence(line: &str) -> Result<Option<TensorSentence>, String> {
282 let t = line.trim();
283 if t.is_empty() || t.starts_with('#') {
284 return Ok(None);
285 }
286 let Some(body) = t.strip_suffix('.') else {
287 return Err(format!("`{t}`: a sentence ends with a period"));
288 };
289 let w: Vec<&str> = body.split_whitespace().collect();
290 let s = |i: usize| w.get(i).copied().unwrap_or("");
291 let own = |i: usize| s(i).to_string();
292 let unknown = || {
293 Err(format!("`{t}`: not a tensor sentence the SambaFlow reader knows (a declaration, a fill, a tensor op, or a Show)"))
294 };
295 if s(0) == "Show" {
296 return Ok(Some(TensorSentence::Show));
297 }
298 if s(0) == "Set" && s(1) == "item" && s(3) == "of" && s(5) == "to" && w.len() == 7 {
299 return Ok(Some(TensorSentence::Fill {
300 var: own(4),
301 index: num(s(2), "the item index", t)?,
302 value: own(6),
303 }));
304 }
305 if s(0) == "Set" && s(1) == "row" && s(3) == "of" && s(5) == "to" && w.len() == 7 {
306 return Ok(Some(TensorSentence::Op {
307 out: own(4),
308 kind: OpKind::SetRow {
309 table: own(4),
310 pos: own(2),
311 row: own(6),
312 },
313 }));
314 }
315 if s(0) != "Let" || s(2) != "be" {
316 return unknown();
317 }
318 let out = own(1);
319 let rest = &w[3..];
320 let r = |i: usize| rest.get(i).copied().unwrap_or("");
321 let ro = |i: usize| r(i).to_string();
322 let n = rest.len();
323 if r(0) == "a" && r(2) == "by" && r(4) == "tensor" && r(5) == "of" && n == 7 {
325 let real = match r(6) {
326 "Real" => true,
327 "Int" => false,
328 other => return Err(format!("`{t}`: a tensor is of Int or Real, not `{other}`")),
329 };
330 return Ok(Some(TensorSentence::Decl {
331 var: out,
332 rows: num(r(1), "the row count", t)?,
333 cols: num(r(3), "the column count", t)?,
334 real,
335 }));
336 }
337 if n == 3 && matches!(r(1), "matmul" | "plus" | "hadamard") {
339 let (a, b) = (ro(0), ro(2));
340 let kind = match r(1) {
341 "matmul" => OpKind::Matmul { a, b },
342 "plus" => OpKind::Plus { a, b },
343 _ => OpKind::Hadamard { a, b },
344 };
345 return Ok(Some(TensorSentence::Op { out, kind }));
346 }
347 let heads_at = |i: usize| -> Result<Option<usize>, String> {
348 if r(i) == "across" && r(i + 2) == "heads" {
349 Ok(Some(num(r(i + 1), "the head count", t)?))
350 } else if r(i).is_empty() {
351 Ok(None)
352 } else {
353 Err(format!(
354 "`{t}`: expected `across H heads` or the end of the sentence, got `{}`",
355 r(i)
356 ))
357 }
358 };
359 let kind = match (r(0), r(1)) {
360 ("rmsnorm", "of") => {
361 let x = ro(2);
362 let mut i = 3;
363 let residual = if r(i) == "plus" {
364 i += 2;
365 Some(ro(i - 1))
366 } else {
367 None
368 };
369 let weight = if r(i) == "with" && r(i + 1) == "weight" {
370 i += 3;
371 Some(ro(i - 1))
372 } else {
373 None
374 };
375 let heads = heads_at(i)?;
376 if residual.is_some() && weight.is_none() {
377 return Err(format!("`{t}`: a fused-add rmsnorm carries a weight"));
378 }
379 OpKind::Rmsnorm {
380 x,
381 residual,
382 weight,
383 heads,
384 }
385 }
386 ("rope", "of") => {
387 if !(r(3) == "with" && r(4) == "cos" && r(6) == "and" && r(7) == "sin") {
388 return Err(format!(
389 "`{t}`: `rope of Q with cos RC and sin RS [across H heads]`"
390 ));
391 }
392 OpKind::Rope {
393 q: ro(2),
394 rc: ro(5),
395 rs: ro(8),
396 heads: heads_at(9)?,
397 }
398 }
399 ("attention", "of") => {
400 if !(r(3) == "over"
401 && r(5) == "and"
402 && r(7) == "with"
403 && r(9) == "heads"
404 && r(11) == "groups"
405 && n == 12)
406 {
407 return Err(format!(
408 "`{t}`: `attention of Q over K and V with H heads KV groups`"
409 ));
410 }
411 OpKind::Attention {
412 q: ro(2),
413 k: ro(4),
414 v: ro(6),
415 heads: num(r(8), "H", t)?,
416 groups: num(r(10), "KV", t)?,
417 }
418 }
419 ("full", "attention") => {
420 if !(r(2) == "of"
421 && r(4) == "over"
422 && r(6) == "and"
423 && r(8) == "with"
424 && r(10) == "heads"
425 && n == 11)
426 {
427 return Err(format!(
428 "`{t}`: `full attention of Q over K and V with H heads`"
429 ));
430 }
431 OpKind::FullAttention {
432 q: ro(3),
433 k: ro(5),
434 v: ro(7),
435 heads: num(r(9), "H", t)?,
436 }
437 }
438 ("sigmoid", "of") if n == 3 => OpKind::Sigmoid { x: ro(2) },
439 ("silu", "of") if n == 3 => OpKind::Silu { x: ro(2) },
440 ("gelu", "of") if n == 3 => OpKind::Gelu { x: ro(2) },
441 ("softmax", "of") if n == 3 => OpKind::Softmax { x: ro(2) },
442 ("transpose", "of") if n == 3 => OpKind::Transpose { x: ro(2) },
443 ("argmax", "of") if n == 3 => OpKind::Argmax { x: ro(2) },
444 ("row", _) if r(2) == "of" && n == 4 => OpKind::RowOf {
445 table: ro(3),
446 tok: ro(1),
447 },
448 ("layernorm", "of") => {
449 if !(r(3) == "with" && r(4) == "weight" && r(6) == "and" && r(7) == "bias" && n == 9) {
450 return Err(format!("`{t}`: `layernorm of X with weight W and bias B`"));
451 }
452 OpKind::Layernorm {
453 x: ro(2),
454 weight: ro(5),
455 bias: ro(8),
456 }
457 }
458 ("causal", "conv") => {
459 if !(r(2) == "of"
460 && r(4) == "with"
461 && r(5) == "weight"
462 && r(7) == "state"
463 && r(9) == "over"
464 && r(11) == "taps"
465 && n == 12)
466 {
467 return Err(format!(
468 "`{t}`: `causal conv of X with weight WC state CS over T taps`"
469 ));
470 }
471 OpKind::CausalConv {
472 x: ro(3),
473 weight: ro(6),
474 state: ro(8),
475 taps: num(r(10), "T", t)?,
476 }
477 }
478 ("l2norm", "of") => OpKind::L2norm {
479 x: ro(2),
480 heads: heads_at(3)?,
481 },
482 ("decay", "of") => {
483 if !(r(3) == "with" && r(4) == "bias" && r(6) == "rate" && n == 8) {
484 return Err(format!("`{t}`: `decay of Ar with bias Dt rate Alog`"));
485 }
486 OpKind::Decay {
487 ar: ro(2),
488 dt: ro(5),
489 alog: ro(7),
490 }
491 }
492 ("gated", "delta") => {
493 let shape = [
494 "gated", "delta", "of", "Q", "over", "K", "and", "V", "with", "gate", "Z", "beta",
495 "B", "decay", "A", "state", "S", "norm", "GN", "with", "HV", "heads", "HK",
496 "groups",
497 ];
498 let fixed = |i: usize| {
499 shape[i]
500 .chars()
501 .next()
502 .map(|c| c.is_ascii_lowercase())
503 .unwrap_or(false)
504 };
505 if n != shape.len() || (0..n).any(|i| fixed(i) && r(i) != shape[i]) {
506 return Err(format!("`{t}`: `gated delta of Q over K and V with gate Z beta B decay A state S norm GN with HV heads HK groups`"));
507 }
508 OpKind::GatedDelta {
509 q: ro(3),
510 k: ro(5),
511 v: ro(7),
512 gate: ro(10),
513 beta: ro(12),
514 decay: ro(14),
515 state: ro(16),
516 norm: ro(18),
517 hv: num(r(20), "HV", t)?,
518 hk: num(r(22), "HK", t)?,
519 }
520 }
521 ("swiglu", "of") => {
522 if !(r(3) == "and" && n == 5) {
523 return Err(format!("`{t}`: `swiglu of Gate and Up`"));
524 }
525 OpKind::Swiglu {
526 gate: ro(2),
527 up: ro(4),
528 }
529 }
530 ("moe", "of") => OpKind::Moe { x: ro(2) },
531 _ => return unknown(),
532 };
533 Ok(Some(TensorSentence::Op { out, kind }))
534}
535
536pub fn read_tensor_sentences(source: &str) -> Result<Vec<TensorSentence>, String> {
538 let mut out = Vec::new();
539 for line in source.lines() {
540 if let Some(s) = read_sentence(line)? {
541 out.push(s);
542 }
543 }
544 Ok(out)
545}
546
547pub fn torch_spelling(op: &str) -> Result<&'static str, String> {
551 Ok(match op {
552 "matmul" => "a @ b",
553 "plus" => "a + b",
554 "hadamard" => "a * b",
555 "rmsnorm" => "rmsnorm(x, w, nr, nc)",
556 "rope" => "rope(q, rc, rs, heads, hd, half)",
557 "attention" => "attention(q, k, v, heads, groups, hd)",
558 "full attention" => "full_attention(q, k, v, heads, hd)",
559 "sigmoid" => "torch.sigmoid(x)",
560 "silu" => "F.silu(x)",
561 "gelu" => "gelu_tanh(x)",
562 "softmax" => "softmax_rows(x)",
563 "transpose" => "x.transpose(0, 1)",
564 "layernorm" => "layernorm(x, w, b)",
565 "causal conv" => "causal_conv(x, wc, cs)",
566 "l2norm" => "l2norm(x, nr, nc)",
567 "decay" => "decay(ar, dt, alog)",
568 "gated delta" => "gated_delta(q, k, v, z, b, a, s, gn, hv, hk, d)",
569 "swiglu" => "swiglu(g, u)",
570 "argmax" => "torch.argmax(x).reshape(1, 1).to(x.dtype)",
571 "embedding" => "table[int(tok):int(tok) + 1]",
572 "set row" => "table[int(pos)] = row.reshape(-1)",
573 other => return Err(format!("`{other}` has no torch spelling in the SambaFlow emitter — the routed expert FFN (moe) and anything outside the tensor sugar are refused, never dropped")),
574 })
575}
576
577pub fn gguf_name(var: &str, attention_layers: &BTreeSet<usize>) -> Option<String> {
581 match var {
582 "on" => return Some("output_norm.weight".into()),
583 "Wout" => return Some("output.weight".into()),
584 "emb" | "Emb" => return Some("token_embd.weight".into()),
585 _ => {}
586 }
587 let digits: String = var
588 .chars()
589 .rev()
590 .take_while(|c| c.is_ascii_digit())
591 .collect::<Vec<_>>()
592 .into_iter()
593 .rev()
594 .collect();
595 if digits.is_empty() {
596 return None;
597 }
598 let stem = &var[..var.len() - digits.len()];
599 let l: usize = digits.parse().ok()?;
600 let attn = attention_layers.contains(&l);
601 let name = match (stem, attn) {
602 ("an", _) => "attn_norm.weight",
603 ("fn", _) => "post_attention_norm.weight",
604 ("Qn", _) => "attn_q_norm.weight",
605 ("Kn", _) => "attn_k_norm.weight",
606 ("Wq", true) | ("Wqg", true) => "attn_q.weight",
607 ("Wk", true) => "attn_k.weight",
608 ("Wv", true) => "attn_v.weight",
609 ("Wo", true) => "attn_output.weight",
610 ("Wq", false) | ("Wk", false) | ("Wv", false) => "attn_qkv.weight",
611 ("Wz", false) => "attn_gate.weight",
612 ("Wo", false) => "ssm_out.weight",
613 ("Wcq", _) | ("Wck", _) | ("Wcv", _) => "ssm_conv1d.weight",
614 ("Wb", _) => "ssm_beta.weight",
615 ("Wa", _) => "ssm_alpha.weight",
616 ("Dt", _) => "ssm_dt.bias",
617 ("Al", _) => "ssm_a",
618 ("Gn", _) => "ssm_norm.weight",
619 ("Wg", _) => "ffn_gate.weight",
620 ("Wu", _) => "ffn_up.weight",
621 ("Wd", _) => "ffn_down.weight",
622 _ => return None,
623 };
624 Some(format!("blk.{l}.{name}"))
625}
626
627const PY_HELPERS: &str = r#"
630EPS = 1e-6
631
632
633def rmsnorm(x, w, nr, nc):
634 """Per-row RMSNorm over an (nr, nc) view: y = x / sqrt(mean(x^2) + eps) * w."""
635 v = x.reshape(nr, nc)
636 y = v * torch.rsqrt((v * v).mean(-1, keepdim=True) + EPS) * w.reshape(1, nc)
637 return y.reshape(x.shape)
638
639
640def rope(q, rc, rs, heads, hd, half):
641 """NEOX rotary on the leading 2*half dims of each head (pairs (i, i+half)); the rest passes through."""
642 v = q.reshape(heads, hd)
643 a, b = v[:, :half], v[:, half:2 * half]
644 c, s = rc.reshape(1, half), rs.reshape(1, half)
645 return torch.cat([a * c - b * s, a * s + b * c, v[:, 2 * half:]], dim=1).reshape(q.shape)
646
647
648def attention(q, k, v, heads, groups, hd):
649 """Single-query GQA decode attention: query head h reads kv head h // (heads // groups)."""
650 L = k.shape[0]
651 qh, kh, vh = q.reshape(heads, hd), k.reshape(L, groups, hd), v.reshape(L, groups, hd)
652 out = []
653 for h in range(heads):
654 g = h // (heads // groups)
655 scores = (kh[:, g, :] @ qh[h]) / math.sqrt(hd)
656 out.append(torch.softmax(scores, dim=0) @ vh[:, g, :])
657 return torch.stack(out).reshape(1, heads * hd)
658
659
660def full_attention(q, k, v, heads, hd):
661 """Bidirectional T x T self-attention per head, no mask."""
662 T = q.shape[0]
663 qh, kh, vh = q.reshape(T, heads, hd), k.reshape(T, heads, hd), v.reshape(T, heads, hd)
664 out = []
665 for h in range(heads):
666 scores = (qh[:, h, :] @ kh[:, h, :].transpose(0, 1)) / math.sqrt(hd)
667 out.append(torch.softmax(scores, dim=1) @ vh[:, h, :])
668 return torch.stack(out, dim=1).reshape(T, heads * hd)
669
670
671def gelu_tanh(x):
672 """GELU, ggml's tanh approximation, in its one-exponential arrangement x / (1 + e^-2z)."""
673 z = math.sqrt(2.0 / math.pi) * (x + 0.044715 * x * x * x)
674 return x / (1.0 + torch.exp(-2.0 * z))
675
676
677def softmax_rows(x):
678 return torch.softmax(x, dim=1)
679
680
681def layernorm(x, w, b):
682 mean = x.mean(-1, keepdim=True)
683 var = ((x - mean) * (x - mean)).mean(-1, keepdim=True)
684 return (x - mean) / torch.sqrt(var + EPS) * w.reshape(1, -1) + b.reshape(1, -1)
685
686
687def causal_conv(x, wc, cs):
688 """Depthwise causal conv1d over the decode ring: shift the ring up, append this token, reduce over taps.
689 Returns (y, new ring)."""
690 ring = torch.cat([cs[1:], x.reshape(1, -1)], dim=0)
691 return (ring * wc).sum(dim=0, keepdim=True), ring
692
693
694def l2norm(x, nr, nc):
695 v = x.reshape(nr, nc)
696 y = v / torch.sqrt((v * v).sum(-1, keepdim=True) + EPS)
697 return y.reshape(x.shape)
698
699
700def decay(ar, dt, alog):
701 """Gated DeltaNet's forget gate: (1 + e^(ar + dt)) ^ alog, as the LOGOS desugar computes it."""
702 return torch.pow(1.0 + torch.exp(ar + dt), alog)
703
704
705def gated_delta(q, k, v, z, b, a, s, gn, hv, hk, d):
706 """One Gated DeltaNet step over hv value heads sharing hk key heads (head h reads key head h mod hk):
707 u = S k; S = alpha S + beta (v - alpha u) k^T; o = (S q) / sqrt(d); out = gn * rmsnorm(o) * silu(z).
708 Returns (out, new memory)."""
709 S = s.reshape(hv, d, d)
710 idx = torch.tensor([h % hk for h in range(hv)])
711 qh, kh = q.reshape(hk, d)[idx], k.reshape(hk, d)[idx]
712 vh, zh = v.reshape(hv, d), z.reshape(hv, d)
713 alpha, beta = a.reshape(hv, 1), b.reshape(hv, 1)
714 u = torch.einsum('hab,hb->ha', S, kh)
715 coef = beta * (vh - alpha * u)
716 S = alpha.reshape(hv, 1, 1) * S + coef.reshape(hv, d, 1) * kh.reshape(hv, 1, d)
717 o = torch.einsum('hab,hb->ha', S, qh) / math.sqrt(d)
718 den = torch.sqrt((o * o).mean(-1, keepdim=True) + EPS)
719 out = gn.reshape(1, d) * (o / den) * F.silu(zh)
720 return out.reshape(1, hv * d), S.reshape(s.shape)
721
722
723def swiglu(g, u):
724 return F.silu(g) * u
725"#;
726
727struct Program {
728 sentences: Vec<TensorSentence>,
729 shapes: BTreeMap<String, (usize, usize)>,
730 real: BTreeMap<String, bool>,
731 leaves: Vec<String>,
733 states: BTreeSet<String>,
735 attention_layers: BTreeSet<usize>,
737 ops: Vec<(String, OpKind)>,
738 output: String,
739}
740
741fn layer_of(var: &str) -> Option<usize> {
742 let digits: String = var
743 .chars()
744 .rev()
745 .take_while(|c| c.is_ascii_digit())
746 .collect::<Vec<_>>()
747 .into_iter()
748 .rev()
749 .collect();
750 digits.parse().ok()
751}
752
753fn read_program(source: &str) -> Result<Program, String> {
754 let sentences = read_tensor_sentences(source)?;
755 let mut shapes: BTreeMap<String, (usize, usize)> = BTreeMap::new();
756 let mut real: BTreeMap<String, bool> = BTreeMap::new();
757 let mut leaves = Vec::new();
758 let mut produced: BTreeSet<String> = BTreeSet::new();
759 let mut states = BTreeSet::new();
760 let mut attention_layers = BTreeSet::new();
761 let mut ops = Vec::new();
762 let shape = |shapes: &BTreeMap<String, (usize, usize)>,
763 v: &str,
764 op: &str|
765 -> Result<(usize, usize), String> {
766 shapes.get(v).copied().ok_or_else(|| {
767 format!("`{op}` reads `{v}`, which is not a declared or computed tensor")
768 })
769 };
770 for s in &sentences {
771 match s {
772 TensorSentence::Decl {
773 var,
774 rows,
775 cols,
776 real: is_real,
777 } => {
778 shapes.insert(var.clone(), (*rows, *cols));
779 real.insert(var.clone(), *is_real);
780 leaves.push(var.clone());
781 }
782 TensorSentence::Fill { var, .. } => {
783 if !shapes.contains_key(var) {
784 return Err(format!("`Set item … of {var}` fills an undeclared tensor"));
785 }
786 }
787 TensorSentence::Show => {}
788 TensorSentence::Op { out, kind } => {
789 torch_spelling(kind.name())?;
790 for input in kind.inputs() {
791 if !matches!(kind, OpKind::RowOf { .. } | OpKind::SetRow { .. })
792 || shapes.contains_key(input)
793 {
794 shape(&shapes, input, kind.name())?;
795 }
796 }
797 let out_shape = match kind {
798 OpKind::Matmul { a, b } => {
799 let ((m, ka), (kb, n)) =
800 (shape(&shapes, a, "matmul")?, shape(&shapes, b, "matmul")?);
801 if ka != kb {
802 return Err(format!(
803 "`{out} = {a} matmul {b}`: inner dimensions {ka} and {kb} disagree"
804 ));
805 }
806 (m, n)
807 }
808 OpKind::Plus { a, .. } | OpKind::Hadamard { a, .. } => {
809 shape(&shapes, a, kind.name())?
810 }
811 OpKind::Rmsnorm { x, .. }
812 | OpKind::Rope { q: x, .. }
813 | OpKind::Attention { q: x, .. }
814 | OpKind::FullAttention { q: x, .. }
815 | OpKind::Sigmoid { x }
816 | OpKind::Silu { x }
817 | OpKind::Gelu { x }
818 | OpKind::Softmax { x }
819 | OpKind::Layernorm { x, .. }
820 | OpKind::L2norm { x, .. }
821 | OpKind::Moe { x } => shape(&shapes, x, kind.name())?,
822 OpKind::Transpose { x } => {
823 let (r, c) = shape(&shapes, x, "transpose")?;
824 (c, r)
825 }
826 OpKind::CausalConv { x, .. } => shape(&shapes, x, "causal conv")?,
827 OpKind::Decay { ar, .. } => shape(&shapes, ar, "decay")?,
828 OpKind::GatedDelta { q, hv, hk, .. } => {
829 let (_, qc) = shape(&shapes, q, "gated delta")?;
830 (1, hv * (qc / hk.max(&1)))
831 }
832 OpKind::Swiglu { gate, .. } => shape(&shapes, gate, "swiglu")?,
833 OpKind::Argmax { .. } => (1, 1),
834 OpKind::RowOf { table, .. } => (1, shape(&shapes, table, "embedding")?.1),
835 OpKind::SetRow { table, .. } => shape(&shapes, table, "set row")?,
836 };
837 if let OpKind::Attention { q, .. } = kind {
838 if let Some(l) = layer_of(q) {
839 attention_layers.insert(l);
840 }
841 }
842 if let Some(w) = kind.writes() {
843 states.insert(w.to_string());
844 }
845 if !matches!(kind, OpKind::SetRow { .. }) {
846 let is_real = kind
847 .inputs()
848 .first()
849 .and_then(|i| real.get(*i).copied())
850 .unwrap_or(true);
851 shapes.insert(out.clone(), out_shape);
852 real.insert(out.clone(), is_real);
853 produced.insert(out.clone());
854 }
855 ops.push((out.clone(), kind.clone()));
856 }
857 }
858 }
859 let leaves: Vec<String> = leaves
860 .into_iter()
861 .filter(|l| !produced.contains(l))
862 .collect();
863 let output = ops
864 .iter()
865 .rev()
866 .find(|(_, k)| !matches!(k, OpKind::SetRow { .. }))
867 .map(|(o, _)| o.clone())
868 .ok_or("a tensor program with no op has nothing to emit")?;
869 Ok(Program {
870 sentences,
871 shapes,
872 real,
873 leaves,
874 states,
875 attention_layers,
876 ops,
877 output,
878 })
879}
880
881fn cross_check(source: &str, p: &Program) -> Result<(), String> {
884 crate::ui_bridge::with_parsed_program(source, |parsed, _| parsed.map(|_| ()).map_err(|e| format!("the program does not parse: {e}")))?;
885 let lifted = TensorGraph::lift_source(source, &|_| Storage::Values).map_err(|e| format!("the front end does not walk the program: {e:?}"))?;
886 let count = |kind: &str| lifted.graph.ops.iter().filter(|op| op.kind() == kind).count();
887 let counted = (count("Matmul"), count("Rope"), count("Attention"));
888 let mine = |name: &str| p.ops.iter().filter(|(_, k)| k.name() == name).count();
889 let mine_counts = (mine("matmul"), mine("rope"), mine("attention"));
890 if counted != mine_counts {
891 return Err(format!("the sentence reader and the one IR disagree on (matmul, rope, attention) counts: the IR {counted:?}, sentences {mine_counts:?}"));
892 }
893 Ok(())
894}
895
896fn fnv(s: &str) -> u64 {
897 let mut h: u64 = 0xcbf29ce484222325;
898 for b in s.bytes() {
899 h ^= b as u64;
900 h = h.wrapping_mul(0x100000001b3);
901 }
902 h
903}
904
905fn py_call(p: &Program, kind: &OpKind) -> Result<String, String> {
906 let sh = |v: &str| p.shapes.get(v).copied().unwrap_or((1, 1));
907 Ok(match kind {
908 OpKind::Matmul { a, b } => format!("{a} @ {b}"),
909 OpKind::Plus { a, b } => format!("{a} + {b}"),
910 OpKind::Hadamard { a, b } => format!("{a} * {b}"),
911 OpKind::Rmsnorm {
912 x,
913 residual,
914 weight,
915 heads,
916 } => {
917 let (r, c) = sh(x);
918 let (nr, nc) = match heads {
919 Some(h) => (*h, (r * c) / h.max(&1)),
920 None => (r, c),
921 };
922 let src = match residual {
923 Some(res) => format!("({x} + {res})"),
924 None => x.clone(),
925 };
926 match weight {
927 Some(w) => format!("rmsnorm({src}, {w}, {nr}, {nc})"),
928 None => format!("rmsnorm({src}, torch.ones({nc}, dtype={src}.dtype), {nr}, {nc})"),
929 }
930 }
931 OpKind::Rope { q, rc, rs, heads } => {
932 let (_, qc) = sh(q);
933 let (_, half) = sh(rc);
934 let hd = match heads {
935 Some(h) => qc / h.max(&1),
936 None => 2 * half,
937 };
938 let heads = if hd > 0 { qc / hd } else { 1 };
939 format!("rope({q}, {rc}, {rs}, {heads}, {hd}, {half})")
940 }
941 OpKind::Attention {
942 q,
943 k,
944 v,
945 heads,
946 groups,
947 } => {
948 let (_, qc) = sh(q);
949 format!(
950 "attention({q}, {k}, {v}, {heads}, {groups}, {})",
951 qc / heads.max(&1)
952 )
953 }
954 OpKind::FullAttention { q, k, v, heads } => {
955 let (_, qc) = sh(q);
956 format!(
957 "full_attention({q}, {k}, {v}, {heads}, {})",
958 qc / heads.max(&1)
959 )
960 }
961 OpKind::Sigmoid { x } => format!("torch.sigmoid({x})"),
962 OpKind::Silu { x } => format!("F.silu({x})"),
963 OpKind::Gelu { x } => format!("gelu_tanh({x})"),
964 OpKind::Softmax { x } => format!("softmax_rows({x})"),
965 OpKind::Transpose { x } => format!("{x}.transpose(0, 1)"),
966 OpKind::Layernorm { x, weight, bias } => format!("layernorm({x}, {weight}, {bias})"),
967 OpKind::CausalConv {
968 x, weight, state, ..
969 } => format!("causal_conv({x}, {weight}, {state})"),
970 OpKind::L2norm { x, heads } => {
971 let (r, c) = sh(x);
972 let (nr, nc) = match heads {
973 Some(h) => (*h, (r * c) / h.max(&1)),
974 None => (r, c),
975 };
976 format!("l2norm({x}, {nr}, {nc})")
977 }
978 OpKind::Decay { ar, dt, alog } => format!("decay({ar}, {dt}, {alog})"),
979 OpKind::GatedDelta {
980 q,
981 k,
982 v,
983 gate,
984 beta,
985 decay,
986 state,
987 norm,
988 hv,
989 hk,
990 } => {
991 let (_, qc) = sh(q);
992 format!("gated_delta({q}, {k}, {v}, {gate}, {beta}, {decay}, {state}, {norm}, {hv}, {hk}, {})", qc / hk.max(&1))
993 }
994 OpKind::Swiglu { gate, up } => format!("swiglu({gate}, {up})"),
995 OpKind::Argmax { x } => format!("torch.argmax({x}).reshape(1, 1).to({x}.dtype)"),
996 OpKind::RowOf { table, tok } => format!("{table}[int({tok}):int({tok}) + 1]"),
997 OpKind::SetRow { .. } => String::new(),
998 OpKind::Moe { .. } => return Err(torch_spelling("moe").unwrap_err()),
999 })
1000}
1001
1002pub fn emit_torch_module(source: &str) -> Result<TorchArtifact, String> {
1005 let p = read_program(source)?;
1006 cross_check(source, &p)?;
1007 let name = format!("logos_{:016x}", fnv(source));
1008 let leaf_role = |v: &str| -> &'static str {
1009 if p.states.contains(v) {
1010 "state"
1011 } else if gguf_name(v, &p.attention_layers).is_some() {
1012 "weight"
1013 } else {
1014 "input"
1015 }
1016 };
1017
1018 let mut m = String::new();
1020 m.push_str(VENDOR_HEADER);
1021 m.push('\n');
1022 m.push_str(&format!(
1023 "# Generated by LOGOS (logicaffeine_compile::spatial::lower::sambaflow) from the tensor program `{name}`:\n# {} tensors, {} ops, output `{}`. The forward mirrors the program one op per statement, in dataflow order;\n# every helper spells the LOGOS desugar of its op. Weights are raw little-endian files named by variable\n# (`<var>.f64` or `<var>.f32`, row-major, shape from MANIFEST) — see `load_weights`.\n",
1024 p.leaves.len(),
1025 p.ops.len(),
1026 p.output
1027 ));
1028 m.push_str("import argparse\nimport json\nimport math\nimport os\nimport sys\n\nimport numpy as np\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\n");
1029 m.push_str(PY_HELPERS);
1030 m.push_str("\n\nLEAVES = {\n");
1031 for l in &p.leaves {
1032 let (r, c) = p.shapes[l];
1033 m.push_str(&format!(
1034 " \"{l}\": (({r}, {c}), \"{}\", {}),\n",
1035 leaf_role(l),
1036 if p.real[l] { "\"real\"" } else { "\"int\"" }
1037 ));
1038 }
1039 m.push_str("}\n");
1040 m.push_str(&format!("OUTPUT = \"{}\"\n", p.output));
1041 m.push_str("\n\nclass LogosModule(nn.Module):\n");
1042 m.push_str(" \"\"\"The tensor program. Leaves present in `weights` become buffers; the rest are forward inputs.\"\"\"\n\n");
1043 m.push_str(" def __init__(self, weights):\n super().__init__()\n self.free = []\n for name, (shape, role, kind) in LEAVES.items():\n if name in weights:\n self.register_buffer(name, weights[name].reshape(shape))\n else:\n self.free.append(name)\n\n");
1044 m.push_str(" def _write_state(self, name, value, inputs):\n if name not in inputs:\n with torch.no_grad():\n getattr(self, name).copy_(value)\n\n");
1045 m.push_str(" def forward(self, **inputs):\n");
1046 m.push_str(" for name in self.free:\n if name not in inputs:\n raise KeyError(f\"forward needs `{name}` (not among the weights)\")\n");
1047 for l in &p.leaves {
1048 m.push_str(&format!(
1049 " {l} = inputs[\"{l}\"] if \"{l}\" in inputs else self.{l}\n"
1050 ));
1051 }
1052 for (i, (out, kind)) in p.ops.iter().enumerate() {
1053 m.push_str(&format!(" # stage {i}: {out} = {}\n", kind.name()));
1054 let call = py_call(&p, kind)?;
1055 match kind {
1056 OpKind::CausalConv { state, .. } | OpKind::GatedDelta { state, .. } => {
1057 m.push_str(&format!(" {out}, {state} = {call}\n"));
1058 m.push_str(&format!(
1059 " self._write_state(\"{state}\", {state}, inputs)\n"
1060 ));
1061 }
1062 OpKind::SetRow { table, pos, row } => {
1063 m.push_str(&format!(" {table} = {table}.clone()\n {table}[int({pos})] = {row}.reshape(-1)\n"));
1064 m.push_str(&format!(
1065 " self._write_state(\"{table}\", {table}, inputs)\n"
1066 ));
1067 }
1068 _ => m.push_str(&format!(" {out} = {call}\n")),
1069 }
1070 }
1071 m.push_str(&format!(" return {}\n", p.output));
1072 m.push_str("\n\ndef load_weights(directory, dtype):\n \"\"\"Every leaf with a `<var>.f64` / `<var>.f32` file in `directory`, as tensors of `dtype`.\"\"\"\n weights = {}\n for name, (shape, role, kind) in LEAVES.items():\n for ext, np_dtype in ((\".f64\", np.float64), (\".f32\", np.float32)):\n path = os.path.join(directory, name + ext)\n if os.path.exists(path):\n arr = np.fromfile(path, dtype=np_dtype).reshape(shape)\n weights[name] = torch.from_numpy(arr.copy()).to(dtype)\n break\n return weights\n");
1073 m.push_str("\n\ndef main():\n ap = argparse.ArgumentParser(description=\"run the LOGOS tensor program in torch\")\n ap.add_argument(\"--weights\", required=True, help=\"directory of <var>.f64/<var>.f32 files\")\n ap.add_argument(\"--dtype\", default=\"float32\", choices=[\"float32\", \"float64\"])\n ap.add_argument(\"--out\", default=None, help=\"write the output tensor as raw little-endian values of --dtype\")\n args = ap.parse_args()\n dtype = torch.float64 if args.dtype == \"float64\" else torch.float32\n torch.set_default_dtype(dtype)\n model = LogosModule(load_weights(args.weights, dtype))\n if model.free:\n raise SystemExit(f\"missing inputs: {model.free}\")\n with torch.no_grad():\n out = model()\n flat = out.detach().to(dtype).reshape(-1).cpu().numpy()\n if args.out:\n flat.astype(np.float64 if args.dtype == \"float64\" else np.float32).tofile(args.out)\n print(json.dumps({\"output\": OUTPUT, \"shape\": list(out.shape), \"first\": float(flat[0]), \"last\": float(flat[-1])}))\n\n\nif __name__ == \"__main__\":\n main()\n");
1074
1075 let tensors: Vec<serde_json::Value> = p
1077 .sentences
1078 .iter()
1079 .filter_map(|s| match s {
1080 TensorSentence::Decl {
1081 var,
1082 rows,
1083 cols,
1084 real,
1085 } => Some(serde_json::json!({
1086 "var": var,
1087 "shape": [rows, cols],
1088 "dtype": if *real { "Real" } else { "Int" },
1089 "role": if p.leaves.contains(var) { leaf_role(var) } else { "computed" },
1090 "gguf": gguf_name(var, &p.attention_layers),
1091 "file": format!("{var}.f64"),
1092 })),
1093 _ => None,
1094 })
1095 .collect();
1096 let ops: Vec<serde_json::Value> = p
1097 .ops
1098 .iter()
1099 .enumerate()
1100 .map(|(i, (out, kind))| serde_json::json!({ "index": i, "out": out, "op": kind.name(), "inputs": kind.inputs(), "writes": kind.writes(), "torch": torch_spelling(kind.name()).unwrap_or("") }))
1101 .collect();
1102 let manifest = serde_json::json!({
1103 "schema": "logos.sambaflow.manifest",
1104 "version": 1,
1105 "provenance": "vendor-ingestion artifact emitted by LOGOS — not our compute path",
1106 "program": name,
1107 "inputs": p.leaves.iter().filter(|l| leaf_role(l) == "input").collect::<Vec<_>>(),
1108 "states": p.leaves.iter().filter(|l| leaf_role(l) == "state").collect::<Vec<_>>(),
1109 "output": p.output,
1110 "tensors": tensors,
1111 "ops": ops,
1112 });
1113 let manifest_json = serde_json::to_string_pretty(&manifest).expect("the manifest serializes");
1114
1115 let mut c = String::new();
1117 c.push_str(VENDOR_HEADER);
1118 c.push('\n');
1119 c.push_str(&format!("{TEMPLATE_MARK}\n"));
1120 c.push_str(&format!(
1121 "# Compile `model.py` (the LOGOS tensor program `{name}`) for the RDU through SambaFlow, then run it.\n# Every call into the vendor SDK below is a TEMPLATE: the documented shape of the SambaFlow front end as\n# last publicly described, to be replaced by the actual entry points the day the SDK is in hand.\nimport argparse\nimport json\n\nimport torch\n\nfrom model import LogosModule, load_weights, LEAVES, OUTPUT\n\n\ndef main():\n ap = argparse.ArgumentParser(description=\"SambaFlow compile/run scaffold for a LOGOS tensor program\")\n ap.add_argument(\"command\", choices=[\"compile\", \"run\"])\n ap.add_argument(\"--weights\", required=True)\n ap.add_argument(\"--pef\", default=\"{name}.pef\")\n args = ap.parse_args()\n weights = load_weights(args.weights, torch.float32)\n model = LogosModule(weights)\n inputs = {{name: torch.zeros(LEAVES[name][0]) for name in model.free}}\n {TEMPLATE_MARK}\n from sambaflow import samba # noqa: E402\n {TEMPLATE_MARK}\n samba.from_torch_model_(model)\n if args.command == \"compile\":\n {TEMPLATE_MARK}\n samba.session.compile(model, inputs=list(inputs.values()), pef_name=args.pef)\n print(json.dumps({{\"pef\": args.pef, \"output\": OUTPUT}}))\n else:\n {TEMPLATE_MARK}\n out = samba.session.run(input_tensors=list(inputs.values()), output_tensors=[model(**inputs)], pef=args.pef)\n print(json.dumps({{\"output\": OUTPUT, \"shape\": list(out[0].shape)}}))\n\n\nif __name__ == \"__main__\":\n main()\n"
1122 ));
1123
1124 Ok(TorchArtifact {
1125 model_py: m,
1126 compile_py: c,
1127 manifest_json,
1128 })
1129}