1extern crate alloc;
10use alloc::vec;
11use alloc::vec::Vec;
12
13use crate::cq::CqWeight;
14use crate::kernels::rms_unit_to;
15use crate::math::{cos, sin, sqrt};
16use crate::norm::zc_rms_norm_vec;
17use crate::ops::sigmoid;
18use crate::v3::attention::{
19 attend, attend_step, causal_depthwise_conv, norm_and_rope, AttnDims, KvStore, Ring,
20};
21use crate::v3::cache::{fake_quant_vec, quantize_rows, KvPrecision, Qkv, V3Cache};
22
23pub const DEFAULT_CHUNK: usize = 64;
25use crate::v3::config::V3Config;
26use crate::v3::engram::{engram_indices, ngram_valid, value_conv, EngramDims};
27use crate::v3::heads::{ProbeHead, ProbePool};
28use crate::v3::kernels::{hadamard_mlp, HadaMlp, HadaPerms};
29use crate::v3::mhc::{mix_down, scatter_up, Gates, LaneSite, MhcLayer};
30
31pub struct V3Layer {
33 pub norm_in: Vec<f32>,
34 pub q_proj: CqWeight,
35 pub k_proj: CqWeight,
36 pub v_proj: CqWeight,
37 pub q_taps: Vec<f32>,
39 pub k_taps: Vec<f32>,
40 pub v_taps: Vec<f32>,
41 pub q_norm: Vec<f32>,
43 pub k_norm: Vec<f32>,
44 pub gate_proj: CqWeight,
45 pub out_proj: CqWeight,
46 pub post_norm: Vec<f32>,
47 pub attn_gate: f32,
49 pub pre_hada: Vec<f32>,
50 pub mlp: HadaMlp,
51}
52
53pub struct V3Mhc {
55 pub a_pre: Vec<f32>,
57 pub a_post: Vec<f32>,
58 pub a_res: Vec<f32>,
59 pub b_pre: Vec<f32>,
61 pub b_post: Vec<f32>,
62 pub b_res: Vec<f32>,
64 pub phi_pre: CqWeight,
66 pub phi_post: CqWeight,
67 pub phi_res: CqWeight,
69}
70
71pub struct V3EngramSite {
73 pub tables: CqWeight,
75 pub key_proj: CqWeight,
76 pub value_proj: CqWeight,
77 pub taps: Vec<f32>,
79}
80
81pub struct V3Model {
83 pub cfg: V3Config,
84 pub embedding: CqWeight,
86 pub layers: Vec<V3Layer>,
87 pub mhc: V3Mhc,
88 pub engrams: Vec<V3EngramSite>,
89 pub final_norm: Vec<f32>,
90 pub perms: HadaPerms,
91 embed_scale: f32,
92}
93
94fn quant_rows(x: &mut [f32], seq: usize, heads: usize, head_dim: usize) {
100 for t in 0..seq {
101 for h in 0..heads {
102 let o = (t * heads + h) * head_dim;
103 fake_quant_vec(&mut x[o..o + head_dim], 8);
104 }
105 }
106}
107
108impl V3Model {
109 pub fn new(
110 cfg: V3Config,
111 embedding: CqWeight,
112 layers: Vec<V3Layer>,
113 mhc: V3Mhc,
114 engrams: Vec<V3EngramSite>,
115 final_norm: Vec<f32>,
116 perms: HadaPerms,
117 ) -> Result<Self, &'static str> {
118 if layers.len() != cfg.num_layers {
119 return Err("layer count does not match geometry");
120 }
121 if engrams.len() != cfg.engram.sites.len() {
122 return Err("engram site count does not match geometry");
123 }
124 if embedding.out_feat != cfg.vocab_size || embedding.in_feat != cfg.d_model {
125 return Err("embedding shape does not match geometry");
126 }
127 let embed_scale = sqrt(cfg.d_model as f32);
128 Ok(Self {
129 cfg,
130 embedding,
131 layers,
132 mhc,
133 engrams,
134 final_norm,
135 perms,
136 embed_scale,
137 })
138 }
139
140 pub fn decode_step(&self, cache: &mut V3Cache, token: u32) -> Vec<f32> {
151 let cfg = &self.cfg;
152 let (d, n) = (cfg.d_model, cfg.mhc_lanes);
153 let pos = cache.pos();
154 let rows = cfg.logit_rows();
155
156 cache.push_token(token);
157
158 let e = &cfg.engram;
163 let hist = cache.token_history().to_vec();
164 let here = hist.len() - 1;
165 let idx = engram_indices(&hist, &e.orders, e.heads, e.slots as u32, e.seed_heads);
166 let num_tables = e.num_tables();
167
168 let mut ek = vec![0.0f32; e.sites.len() * d];
169 let mut ev = vec![0.0f32; e.sites.len() * d];
170 let mut fetched = vec![0.0f32; num_tables * e.sub_dim];
171 let mut row = vec![0.0f32; e.sub_dim];
172 for (s, site) in self.engrams.iter().enumerate() {
173 for t in 0..num_tables {
174 let dst = &mut fetched[t * e.sub_dim..(t + 1) * e.sub_dim];
175 if ngram_valid(pos, t, &e.orders, e.heads) {
178 let slot = idx[here * num_tables + t] as usize;
179 site.tables.dequantize_row(t * e.slots + slot, &mut row);
180 dst.copy_from_slice(&row);
181 } else {
182 dst.fill(0.0);
183 }
184 }
185 site.key_proj.matvec(&fetched, &mut ek[s * d..(s + 1) * d]);
186 site.value_proj
187 .matvec(&fetched, &mut ev[s * d..(s + 1) * d]);
188 let mut cur = ev[s * d..(s + 1) * d].to_vec();
189 cache.engram_conv_step(s, &site.taps, &mut cur);
190 ev[s * d..(s + 1) * d].copy_from_slice(&cur);
191 }
192
193 let mut lanes = vec![0.0f32; n * d];
195 let mut emb = vec![0.0f32; d];
196 self.embedding
197 .dequantize_row(self.clamp_token(token), &mut emb);
198 for x in emb.iter_mut() {
199 *x *= self.embed_scale;
200 }
201 for lane in 0..n {
202 lanes[lane * d..(lane + 1) * d].copy_from_slice(&emb);
203 }
204
205 let dims = AttnDims {
206 seq: 1,
207 num_heads: cfg.num_heads,
208 num_kv_heads: cfg.num_kv_heads,
209 qk_head_dim: cfg.qk_head_dim,
210 v_head_dim: cfg.v_head_dim,
211 };
212 let half = cfg.qk_head_dim / 2;
213 let (rc, rs) = self.rope_at(pos);
214
215 let mut nx = vec![0.0f32; n * d];
216 let mut nx_prep = vec![0.0f32; self.mhc.phi_pre.prepared_len()];
217 let mut hpre = vec![0.0f32; n];
218 let mut hpost = vec![0.0f32; n];
219 let mut hres = vec![0.0f32; n * n];
220 let mut scratch = vec![0.0f32; n * d];
221 let mut u = vec![0.0f32; d];
222 let mut q = vec![0.0f32; cfg.q_dim()];
223 let mut k = vec![0.0f32; cfg.k_dim()];
224 let mut v = vec![0.0f32; cfg.v_dim()];
225 let mut attn = vec![0.0f32; cfg.attn_out_dim()];
226 let mut gate = vec![0.0f32; cfg.attn_out_dim()];
227 let mut proj = vec![0.0f32; d];
228 let mut mlp_out = vec![0.0f32; d];
229
230 for li in 0..cfg.num_layers {
231 let layer = &self.layers[li];
232 let m = MhcLayer {
233 a_pre: self.mhc.a_pre[li],
234 a_post: self.mhc.a_post[li],
235 a_res: self.mhc.a_res[li],
236 b_pre: &self.mhc.b_pre[li * n..(li + 1) * n],
237 b_post: &self.mhc.b_post[li * n..(li + 1) * n],
238 b_res: &self.mhc.b_res[li * n * n..(li + 1) * n * n],
239 };
240
241 rms_unit_to(&lanes, &mut nx);
242 self.mhc.phi_pre.prepare_input(&nx, &mut nx_prep);
243 self.mhc
244 .phi_pre
245 .matvec_rows_prepared(&nx_prep, li * n, &mut hpre);
246 mix_down(&lanes, &mut hpre, &m, li, n, d, &mut u);
247
248 let mut bx = u.clone();
249 if let Some(site) = cfg.engram.site_of(li) {
250 crate::v3::engram::apply_site(
251 &mut bx,
252 &ek[site * d..(site + 1) * d],
253 &ev[site * d..(site + 1) * d],
254 d,
255 );
256 }
257
258 let mut h = bx.clone();
259 zc_rms_norm_vec(&mut h, &layer.norm_in);
260 layer.q_proj.matvec(&h, &mut q);
261 layer.k_proj.matvec(&h, &mut k);
262 layer.v_proj.matvec(&h, &mut v);
263
264 if cfg.qkv_conv_taps > 0 {
265 cache.conv_step(li, Qkv::Q, &layer.q_taps, &mut q);
266 cache.conv_step(li, Qkv::K, &layer.k_taps, &mut k);
267 cache.conv_step(li, Qkv::V, &layer.v_taps, &mut v);
268 }
269 norm_and_rope(
270 &mut q,
271 &layer.q_norm,
272 &rc,
273 &rs,
274 1,
275 cfg.num_heads,
276 cfg.qk_head_dim,
277 );
278 norm_and_rope(
279 &mut k,
280 &layer.k_norm,
281 &rc,
282 &rs,
283 1,
284 cfg.num_kv_heads,
285 cfg.qk_head_dim,
286 );
287 let _ = half;
288
289 if let KvPrecision::Int8 = cache.precision() {
290 quant_rows(&mut q, 1, cfg.num_heads, cfg.qk_head_dim);
293 }
294 cache.write_kv(li, pos, &k, &v);
295 let (lo, hi) = cache.span(li, pos);
296 let slots = cache.slots(li);
297 attend_step(
298 &q,
299 Ring {
300 kv: cache.kv(li),
301 slots,
302 lo,
303 hi,
304 },
305 dims,
306 &mut attn,
307 );
308
309 layer.gate_proj.matvec(&h, &mut gate);
310 for (ai, &gi) in attn.iter_mut().zip(gate.iter()) {
311 *ai *= sigmoid(gi);
312 }
313 layer.out_proj.matvec(&attn, &mut proj);
314 zc_rms_norm_vec(&mut proj, &layer.post_norm);
315 let agate = sigmoid(layer.attn_gate);
316 for (si, &pi) in bx.iter_mut().zip(proj.iter()) {
317 *si += agate * pi;
318 }
319
320 proj.copy_from_slice(&bx);
321 zc_rms_norm_vec(&mut proj, &layer.pre_hada);
322 hadamard_mlp(&proj, &layer.mlp, &self.perms, cfg.hada_n, &mut mlp_out);
323 for (si, &mi) in bx.iter_mut().zip(mlp_out.iter()) {
324 *si += mi;
325 }
326
327 let mut y = vec![0.0f32; d];
328 for c in 0..d {
329 y[c] = bx[c] - u[c];
330 }
331
332 rms_unit_to(&lanes, &mut nx);
333 self.mhc.phi_post.prepare_input(&nx, &mut nx_prep);
334 self.mhc
335 .phi_post
336 .matvec_rows_prepared(&nx_prep, li * n, &mut hpost);
337 self.mhc.phi_res.prepare_input(&nx, &mut nx_prep);
338 self.mhc
339 .phi_res
340 .matvec_rows_prepared(&nx_prep, li * n * n, &mut hres);
341 scatter_up(
342 &mut lanes,
343 Gates {
344 post: &mut hpost,
345 res: &mut hres,
346 },
347 &y,
348 &m,
349 LaneSite {
350 layer: li,
351 lanes: n,
352 d_model: d,
353 },
354 &mut scratch,
355 );
356 }
357
358 cache.advance();
359
360 let mut x = vec![0.0f32; d];
361 for (c, xc) in x.iter_mut().enumerate() {
362 let mut acc = 0.0f32;
363 for lane in 0..n {
364 acc += lanes[lane * d + c];
365 }
366 *xc = acc / n as f32;
367 }
368 zc_rms_norm_vec(&mut x, &self.final_norm);
369 let mut lm_prep = vec![0.0f32; self.embedding.prepared_len()];
370 let mut full = vec![0.0f32; cfg.vocab_size];
371 self.embedding.prepare_input(&x, &mut lm_prep);
372 self.embedding.matvec_prepared(&lm_prep, &mut full);
373 full.truncate(rows);
374 full
375 }
376
377 fn rope_at(&self, pos: usize) -> (Vec<f32>, Vec<f32>) {
379 let half = self.cfg.qk_head_dim / 2;
380 let mut c = vec![0.0f32; half];
381 let mut s = vec![0.0f32; half];
382 for i in 0..half {
383 let freq = 1.0
384 / powf_f32(
385 self.cfg.rope_theta,
386 (2 * i) as f32 / self.cfg.qk_head_dim as f32,
387 );
388 let angle = pos as f32 * freq;
389 c[i] = cos(angle);
390 s[i] = sin(angle);
391 }
392 (c, s)
393 }
394
395 fn rope(&self, seq: usize) -> (Vec<f32>, Vec<f32>) {
397 let half = self.cfg.qk_head_dim / 2;
398 let mut c = vec![0.0f32; seq * half];
399 let mut s = vec![0.0f32; seq * half];
400 for i in 0..half {
401 let freq = 1.0
402 / powf_f32(
403 self.cfg.rope_theta,
404 (2 * i) as f32 / self.cfg.qk_head_dim as f32,
405 );
406 for t in 0..seq {
407 let angle = t as f32 * freq;
408 c[t * half + i] = cos(angle);
409 s[t * half + i] = sin(angle);
410 }
411 }
412 (c, s)
413 }
414
415 fn engram_kv_with_raw(&self, tokens: &[u32], want_raw: bool) -> (Vec<f32>, Vec<f32>, Vec<f32>) {
421 let cfg = &self.cfg;
422 let seq = tokens.len();
423 let d = cfg.d_model;
424 let e = &cfg.engram;
425 let dims = EngramDims {
426 num_tables: e.num_tables(),
427 slots: e.slots,
428 sub_dim: e.sub_dim,
429 d_model: d,
430 conv_taps: e.conv_taps,
431 conv_dilation: e.conv_dilation,
432 max_order: *e.orders.iter().max().unwrap_or(&1),
433 };
434 let indices = engram_indices(tokens, &e.orders, e.heads, e.slots as u32, e.seed_heads);
435
436 let sites = e.sites.len();
437 let mut ks = vec![0.0f32; sites * seq * d];
438 let mut vs = vec![0.0f32; sites * seq * d];
439 let mut raw = if want_raw {
440 vec![0.0f32; sites * seq * d]
441 } else {
442 Vec::new()
443 };
444 let fetch_dim = dims.num_tables * dims.sub_dim;
445 let mut fetched = vec![0.0f32; fetch_dim];
446 let mut row = vec![0.0f32; dims.sub_dim];
447
448 for (s, site) in self.engrams.iter().enumerate() {
449 for p in 0..seq {
450 for t in 0..dims.num_tables {
451 let dst = &mut fetched[t * dims.sub_dim..(t + 1) * dims.sub_dim];
452 if ngram_valid(p, t, &e.orders, e.heads) {
453 let slot = indices[p * dims.num_tables + t] as usize;
454 site.tables.dequantize_row(t * dims.slots + slot, &mut row);
455 dst.copy_from_slice(&row);
456 } else {
457 dst.fill(0.0);
458 }
459 }
460 let base = (s * seq + p) * d;
461 site.key_proj.matvec(&fetched, &mut ks[base..base + d]);
462 site.value_proj.matvec(&fetched, &mut vs[base..base + d]);
463 }
464 let off = s * seq * d;
465 if want_raw {
466 raw[off..off + seq * d].copy_from_slice(&vs[off..off + seq * d]);
467 }
468 value_conv(&mut vs[off..off + seq * d], &site.taps, seq, &dims);
469 }
470 (ks, vs, raw)
471 }
472
473 pub fn forward_sequence(&self, tokens: &[u32]) -> Vec<f32> {
479 self.forward_impl(tokens, None)
480 }
481
482 pub fn forward_head(&self, tokens: &[u32], head: &ProbeHead) -> Vec<f32> {
489 let mut pool = ProbePool::new(head, self.cfg.d_model);
490 self.forward_impl_pooled(tokens, None, None, Some(&mut pool));
491 pool.finish()
492 }
493
494 pub fn forward_cells(&self, tokens: &[u32]) -> Vec<f32> {
499 let mut cells = Vec::new();
500 self.forward_impl(tokens, Some(&mut cells));
501 cells
502 }
503
504 pub fn prefill(&self, tokens: &[u32], cache: &mut V3Cache) -> Vec<f32> {
513 if tokens.is_empty() {
517 return Vec::new();
518 }
519 let rows = self.cfg.logit_rows();
520 let all = self.forward_impl_cached(tokens, None, Some(cache));
521 let last = tokens.len() - 1;
522 all[last * rows..(last + 1) * rows].to_vec()
523 }
524
525 #[inline]
533 fn clamp_token(&self, tok: u32) -> usize {
534 (tok as usize).min(self.cfg.vocab_size - 1)
535 }
536
537 fn forward_impl(&self, tokens: &[u32], cells: Option<&mut Vec<f32>>) -> Vec<f32> {
538 self.forward_impl_cached(tokens, cells, None)
539 }
540
541 fn forward_impl_cached(
542 &self,
543 tokens: &[u32],
544 cells: Option<&mut Vec<f32>>,
545 cache: Option<&mut V3Cache>,
546 ) -> Vec<f32> {
547 self.forward_impl_pooled(tokens, cells, cache, None)
548 }
549
550 fn forward_impl_pooled(
551 &self,
552 tokens: &[u32],
553 mut cells: Option<&mut Vec<f32>>,
554 mut cache: Option<&mut V3Cache>,
555 mut pool: Option<&mut ProbePool<'_>>,
556 ) -> Vec<f32> {
557 let cfg = &self.cfg;
558 let (seq, d, n) = (tokens.len(), cfg.d_model, cfg.mhc_lanes);
559 let rows = cfg.logit_rows();
560 let quantised = cache
561 .as_deref()
562 .is_some_and(|c| c.precision() == KvPrecision::Int8);
563 let (rc, rs) = self.rope(seq);
564 let (ek, ev, ev_raw) = self.engram_kv_with_raw(tokens, cache.is_some());
565
566 let l1 = cfg.num_layers + 1;
568 if let Some(c) = cells.as_deref_mut() {
569 c.clear();
570 c.resize(seq * l1 * d, 0.0);
571 }
572 let mut lanes = vec![0.0f32; seq * n * d];
573 let mut emb = vec![0.0f32; d];
574 for (t, &tok) in tokens.iter().enumerate() {
575 self.embedding
576 .dequantize_row(self.clamp_token(tok), &mut emb);
577 for e in emb.iter_mut() {
578 *e *= self.embed_scale;
579 }
580 if let Some(c) = cells.as_deref_mut() {
581 c[(t * l1) * d..(t * l1 + 1) * d].copy_from_slice(&emb);
583 }
584 if let Some(pl) = pool.as_deref_mut() {
585 pl.observe(0, &emb);
586 }
587 for lane in 0..n {
588 let off = (t * n + lane) * d;
589 lanes[off..off + d].copy_from_slice(&emb);
590 }
591 }
592
593 let dims = AttnDims {
594 seq,
595 num_heads: cfg.num_heads,
596 num_kv_heads: cfg.num_kv_heads,
597 qk_head_dim: cfg.qk_head_dim,
598 v_head_dim: cfg.v_head_dim,
599 };
600 let (q_dim, k_dim, v_dim) = (cfg.q_dim(), cfg.k_dim(), cfg.v_dim());
601 let o_dim = cfg.attn_out_dim();
602
603 let mut nx = vec![0.0f32; n * d];
604 let mut nx_prep = vec![0.0f32; self.mhc.phi_pre.prepared_len()];
605 let mut hpre = vec![0.0f32; n];
606 let mut hpost = vec![0.0f32; n];
607 let mut hres = vec![0.0f32; n * n];
608 let mut scratch = vec![0.0f32; n * d];
609
610 let mut u = vec![0.0f32; seq * d];
611 let mut bx = vec![0.0f32; seq * d];
612 let mut q = vec![0.0f32; seq * q_dim];
613 let mut k = vec![0.0f32; seq * k_dim];
614 let mut v = vec![0.0f32; seq * v_dim];
615 let mut attn = vec![0.0f32; seq * o_dim];
616 let mut gates = vec![0.0f32; seq * o_dim];
617
618 let chunk = DEFAULT_CHUNK.min(seq.max(1));
622 let prep_len = self.layers[0].q_proj.prepared_len();
623 debug_assert!(self.layers.iter().all(|l| {
624 l.k_proj.prepared_len() == prep_len
625 && l.v_proj.prepared_len() == prep_len
626 && l.gate_proj.prepared_len() == prep_len
627 }));
628 let mut xh = vec![0.0f32; chunk * prep_len];
629 let out_prep = self.layers[0].out_proj.prepared_len();
630 let mut oh = vec![0.0f32; chunk * out_prep];
631 let mut projected = vec![0.0f32; seq * d];
632 let mut acc = vec![0.0f32; chunk];
633 let mut mlp_out = vec![0.0f32; d];
634
635 for li in 0..cfg.num_layers {
636 let layer = &self.layers[li];
637 let m = MhcLayer {
638 a_pre: self.mhc.a_pre[li],
639 a_post: self.mhc.a_post[li],
640 a_res: self.mhc.a_res[li],
641 b_pre: &self.mhc.b_pre[li * n..(li + 1) * n],
642 b_post: &self.mhc.b_post[li * n..(li + 1) * n],
643 b_res: &self.mhc.b_res[li * n * n..(li + 1) * n * n],
644 };
645
646 for t in 0..seq {
648 let lane_slice = &lanes[t * n * d..(t + 1) * n * d];
649 rms_unit_to(lane_slice, &mut nx);
650 self.mhc.phi_pre.prepare_input(&nx, &mut nx_prep);
651 self.mhc
652 .phi_pre
653 .matvec_rows_prepared(&nx_prep, li * n, &mut hpre);
654 mix_down(
655 lane_slice,
656 &mut hpre,
657 &m,
658 li,
659 n,
660 d,
661 &mut u[t * d..(t + 1) * d],
662 );
663 }
664
665 bx.copy_from_slice(&u);
667 if let Some(site) = cfg.engram.site_of(li) {
668 for t in 0..seq {
669 let base = (site * seq + t) * d;
670 crate::v3::engram::apply_site(
671 &mut bx[t * d..(t + 1) * d],
672 &ek[base..base + d],
673 &ev[base..base + d],
674 d,
675 );
676 }
677 }
678
679 let mut h = bx.clone();
692 for t in 0..seq {
693 zc_rms_norm_vec(&mut h[t * d..(t + 1) * d], &layer.norm_in);
694 }
695 for c0 in (0..seq).step_by(chunk) {
696 let b = (seq - c0).min(chunk);
697 for i in 0..b {
698 layer.q_proj.prepare_input(
699 &h[(c0 + i) * d..(c0 + i + 1) * d],
700 &mut xh[i * prep_len..(i + 1) * prep_len],
701 );
702 }
703 let xb = &xh[..b * prep_len];
704 layer.q_proj.matmul_rows_prepared(
705 xb,
706 b,
707 0,
708 q_dim,
709 &mut q[c0 * q_dim..(c0 + b) * q_dim],
710 &mut acc,
711 );
712 layer.k_proj.matmul_rows_prepared(
713 xb,
714 b,
715 0,
716 k_dim,
717 &mut k[c0 * k_dim..(c0 + b) * k_dim],
718 &mut acc,
719 );
720 layer.v_proj.matmul_rows_prepared(
721 xb,
722 b,
723 0,
724 v_dim,
725 &mut v[c0 * v_dim..(c0 + b) * v_dim],
726 &mut acc,
727 );
728 layer.gate_proj.matmul_rows_prepared(
729 xb,
730 b,
731 0,
732 o_dim,
733 &mut gates[c0 * o_dim..(c0 + b) * o_dim],
734 &mut acc,
735 );
736 }
737
738 let (mut q_raw_tail, mut k_raw_tail, mut v_raw_tail) =
741 (Vec::new(), Vec::new(), Vec::new());
742 if cache.is_some() && cfg.qkv_conv_taps > 0 {
743 let n = cfg.qkv_conv_taps - 1;
744 q_raw_tail = last_rows(&q, seq, q_dim, n);
745 k_raw_tail = last_rows(&k, seq, k_dim, n);
746 v_raw_tail = last_rows(&v, seq, v_dim, n);
747 }
748 if cfg.qkv_conv_taps > 0 {
749 causal_depthwise_conv(&mut q, &layer.q_taps, seq, q_dim, cfg.qkv_conv_taps);
750 causal_depthwise_conv(&mut k, &layer.k_taps, seq, k_dim, cfg.qkv_conv_taps);
751 causal_depthwise_conv(&mut v, &layer.v_taps, seq, v_dim, cfg.qkv_conv_taps);
752 }
753 norm_and_rope(
754 &mut q,
755 &layer.q_norm,
756 &rc,
757 &rs,
758 seq,
759 cfg.num_heads,
760 cfg.qk_head_dim,
761 );
762 norm_and_rope(
763 &mut k,
764 &layer.k_norm,
765 &rc,
766 &rs,
767 seq,
768 cfg.num_kv_heads,
769 cfg.qk_head_dim,
770 );
771
772 if quantised {
780 quant_rows(&mut q, seq, cfg.num_heads, cfg.qk_head_dim);
781 quant_rows(&mut k, seq, cfg.num_kv_heads, cfg.qk_head_dim);
782 quant_rows(&mut v, seq, cfg.num_kv_heads, cfg.v_head_dim);
783 }
784
785 if let Some(c) = cache.as_deref_mut() {
786 for t in 0..seq {
792 c.write_kv(
793 li,
794 t,
795 &k[t * k_dim..(t + 1) * k_dim],
796 &v[t * v_dim..(t + 1) * v_dim],
797 );
798 }
799 if cfg.qkv_conv_taps > 0 {
800 c.seed_conv_tail(li, Qkv::Q, &q_raw_tail);
801 c.seed_conv_tail(li, Qkv::K, &k_raw_tail);
802 c.seed_conv_tail(li, Qkv::V, &v_raw_tail);
803 }
804 }
805
806 let store = if quantised {
814 let (kq, ks) = quantize_rows(&k, cfg.qk_head_dim);
815 let (vq, vs) = quantize_rows(&v, cfg.v_head_dim);
816 Some((kq, ks, vq, vs))
817 } else {
818 None
819 };
820 let kv_store = match &store {
821 Some((kq, ks, vq, vs)) => KvStore::Int8 {
822 k: kq,
823 k_scale: ks,
824 v: vq,
825 v_scale: vs,
826 },
827 None => KvStore::F32 { k: &k, v: &v },
828 };
829 attend(&q, kv_store, dims, cfg.attention_span(li), &mut attn);
830
831 let agate = sigmoid(layer.attn_gate);
832 for t in 0..seq {
833 let a = &mut attn[t * o_dim..(t + 1) * o_dim];
834 for (ai, &gi) in a.iter_mut().zip(&gates[t * o_dim..(t + 1) * o_dim]) {
835 *ai *= sigmoid(gi);
836 }
837 }
838 for c0 in (0..seq).step_by(chunk) {
842 let b = (seq - c0).min(chunk);
843 for i in 0..b {
844 layer.out_proj.prepare_input(
845 &attn[(c0 + i) * o_dim..(c0 + i + 1) * o_dim],
846 &mut oh[i * out_prep..(i + 1) * out_prep],
847 );
848 }
849 layer.out_proj.matmul_rows_prepared(
850 &oh[..b * out_prep],
851 b,
852 0,
853 d,
854 &mut projected[c0 * d..(c0 + b) * d],
855 &mut acc,
856 );
857 }
858 for t in 0..seq {
859 let proj = &mut projected[t * d..(t + 1) * d];
860 zc_rms_norm_vec(proj, &layer.post_norm);
861
862 let s1 = &mut bx[t * d..(t + 1) * d];
864 for (si, &pi) in s1.iter_mut().zip(proj.iter()) {
865 *si += agate * pi;
866 }
867 proj.copy_from_slice(s1);
868 zc_rms_norm_vec(proj, &layer.pre_hada);
869 hadamard_mlp(proj, &layer.mlp, &self.perms, cfg.hada_n, &mut mlp_out);
870 for (si, &mi) in s1.iter_mut().zip(mlp_out.iter()) {
871 *si += mi;
872 }
873 }
874
875 for t in 0..seq {
877 let lane_slice = &lanes[t * n * d..(t + 1) * n * d];
878 rms_unit_to(lane_slice, &mut nx);
879 self.mhc.phi_post.prepare_input(&nx, &mut nx_prep);
880 self.mhc
881 .phi_post
882 .matvec_rows_prepared(&nx_prep, li * n, &mut hpost);
883 self.mhc.phi_res.prepare_input(&nx, &mut nx_prep);
884 self.mhc
885 .phi_res
886 .matvec_rows_prepared(&nx_prep, li * n * n, &mut hres);
887
888 let mut y = vec![0.0f32; d];
890 for c in 0..d {
891 y[c] = bx[t * d + c] - u[t * d + c];
892 }
893 scatter_up(
894 &mut lanes[t * n * d..(t + 1) * n * d],
895 Gates {
896 post: &mut hpost,
897 res: &mut hres,
898 },
899 &y,
900 &m,
901 LaneSite {
902 layer: li,
903 lanes: n,
904 d_model: d,
905 },
906 &mut scratch,
907 );
908 }
909
910 if cells.is_some() || pool.is_some() {
911 let mut cell = vec![0.0f32; d];
912 for t in 0..seq {
913 for (cc, o) in cell.iter_mut().enumerate() {
914 let mut a = 0.0f32;
915 for lane in 0..n {
916 a += lanes[(t * n + lane) * d + cc];
917 }
918 *o = a / n as f32;
919 }
920 if let Some(c) = cells.as_deref_mut() {
921 c[(t * l1 + li + 1) * d..(t * l1 + li + 2) * d].copy_from_slice(&cell);
922 }
923 if let Some(pl) = pool.as_deref_mut() {
924 pl.observe(li + 1, &cell);
925 }
926 }
927 }
928 }
929
930 if let Some(c) = cache {
931 c.set_pos(seq);
932 c.seed_tokens(tokens);
933 let reach = cfg.engram.conv_taps.saturating_sub(1) * cfg.engram.conv_dilation;
934 if reach > 0 {
935 for s in 0..cfg.engram.sites.len() {
936 let off = s * seq * d;
937 c.seed_engram_tail(s, &last_rows(&ev_raw[off..off + seq * d], seq, d, reach));
938 }
939 }
940 }
941
942 let mut logits = vec![0.0f32; seq * rows];
945 let mut pooled = vec![0.0f32; seq * d];
946 for t in 0..seq {
947 let out = &mut pooled[t * d..(t + 1) * d];
948 for (c, o) in out.iter_mut().enumerate() {
949 let mut a = 0.0f32;
950 for lane in 0..n {
951 a += lanes[(t * n + lane) * d + c];
952 }
953 *o = a / n as f32;
954 }
955 zc_rms_norm_vec(out, &self.final_norm);
956 }
957
958 let lm_prep_len = self.embedding.prepared_len();
959 let mut lh = vec![0.0f32; chunk * lm_prep_len];
960 for c0 in (0..seq).step_by(chunk) {
961 let b = (seq - c0).min(chunk);
962 for i in 0..b {
963 self.embedding.prepare_input(
964 &pooled[(c0 + i) * d..(c0 + i + 1) * d],
965 &mut lh[i * lm_prep_len..(i + 1) * lm_prep_len],
966 );
967 }
968 self.embedding.matmul_rows_prepared(
971 &lh[..b * lm_prep_len],
972 b,
973 0,
974 rows,
975 &mut logits[c0 * rows..(c0 + b) * rows],
976 &mut acc,
977 );
978 }
979 logits
980 }
981}
982
983fn last_rows(buf: &[f32], seq: usize, dim: usize, n: usize) -> Vec<f32> {
986 let mut out = vec![0.0f32; n * dim];
987 for j in 0..n.min(seq) {
988 let src = seq - n.min(seq) + j;
990 let dst = n - n.min(seq) + j;
991 out[dst * dim..(dst + 1) * dim].copy_from_slice(&buf[src * dim..(src + 1) * dim]);
992 }
993 out
994}
995
996fn powf_f32(base: f32, exp: f32) -> f32 {
999 crate::math::powf(base, exp)
1000}