1use crate::math;
34use crate::norm::zc_rms_norm_vec;
35use crate::ops::{sigmoid, softmax_inplace};
36use crate::v2::kernels::{hadamard_mlp, rms_unit_to, sinkhorn};
37use crate::v2::model::{V2Model, V2State};
38use alloc::vec;
39use alloc::vec::Vec;
40
41pub const DEFAULT_CHUNK: usize = 128;
55
56pub struct V2Batch {
63 cap: usize,
64 lanes: Vec<f32>,
65 lanes_prev: Vec<f32>,
66 nx: Vec<f32>,
67 nx_prep: Vec<f32>,
68 u: Vec<f32>,
69 bx: Vec<f32>,
70 y: Vec<f32>,
71 h: Vec<f32>,
72 h_prep: Vec<f32>,
73 hpre: Vec<f32>,
74 hpost: Vec<f32>,
75 hres: Vec<f32>,
76 q: Vec<f32>,
77 k: Vec<f32>,
78 v: Vec<f32>,
79 gate: Vec<f32>,
80 attn_out: Vec<f32>,
81 attn_prep: Vec<f32>,
82 attn_result: Vec<f32>,
83 mlp_out: Vec<f32>,
84 hada: Vec<f32>,
85 engram_k: Vec<f32>,
86 engram_val: Vec<f32>,
87 e_all: Vec<f32>,
89 e_prep_all: Vec<f32>,
90 e_val_all: Vec<f32>,
92 scores: Vec<f32>,
93 acc: Vec<f32>,
95}
96
97impl V2Batch {
98 pub fn new(model: &V2Model, cap: usize) -> Self {
101 let cap = cap.max(1);
102 let cfg = &model.cfg;
103 let (d, n) = (cfg.d_model, cfg.mhc_lanes);
104 let sites = cfg.engram.sites.len();
105 let layer0 = &model.layers[0];
106 Self {
107 cap,
108 lanes: vec![0.0; cap * n * d],
109 lanes_prev: vec![0.0; cap * n * d],
110 nx: vec![0.0; cap * n * d],
111 nx_prep: vec![0.0; cap * model.mhc.phi_pre.prepared_len()],
112 u: vec![0.0; cap * d],
113 bx: vec![0.0; cap * d],
114 y: vec![0.0; cap * d],
115 h: vec![0.0; cap * d],
116 h_prep: vec![0.0; cap * layer0.q_proj.prepared_len()],
117 hpre: vec![0.0; cap * n],
118 hpost: vec![0.0; cap * n],
119 hres: vec![0.0; cap * n * n],
120 q: vec![0.0; cap * cfg.attn_dim()],
121 k: vec![0.0; cap * cfg.kv_dim()],
122 v: vec![0.0; cap * cfg.kv_dim()],
123 gate: vec![0.0; cap * cfg.attn_dim()],
124 attn_out: vec![0.0; cap * cfg.attn_dim()],
125 attn_prep: vec![0.0; cap * layer0.out_proj.prepared_len()],
126 attn_result: vec![0.0; cap * d],
127 mlp_out: vec![0.0; cap * d],
128 hada: vec![0.0; cfg.hada_n],
129 engram_k: vec![0.0; cap * sites * d],
130 engram_val: vec![0.0; cap * sites * d],
131 e_all: vec![0.0; cap * cfg.engram.fetched_dim().max(1)],
132 e_prep_all: vec![
133 0.0;
134 cap * model
135 .engrams
136 .first()
137 .map_or(1, |e| e.value_proj.prepared_len())
138 ],
139 e_val_all: vec![0.0; cap * d],
140 scores: vec![0.0; cfg.max_seq_len],
141 acc: vec![0.0; cap],
142 }
143 }
144
145 pub fn cap(&self) -> usize {
147 self.cap
148 }
149
150 pub fn bytes(&self) -> usize {
152 let f = |v: &Vec<f32>| v.len() * 4;
153 f(&self.lanes)
154 + f(&self.lanes_prev)
155 + f(&self.nx)
156 + f(&self.nx_prep)
157 + f(&self.u)
158 + f(&self.bx)
159 + f(&self.y)
160 + f(&self.h)
161 + f(&self.h_prep)
162 + f(&self.hpre)
163 + f(&self.hpost)
164 + f(&self.hres)
165 + f(&self.q)
166 + f(&self.k)
167 + f(&self.v)
168 + f(&self.gate)
169 + f(&self.attn_out)
170 + f(&self.attn_prep)
171 + f(&self.attn_result)
172 + f(&self.mlp_out)
173 + f(&self.hada)
174 + f(&self.engram_k)
175 + f(&self.engram_val)
176 + f(&self.scores)
177 + f(&self.acc)
178 }
179}
180
181impl V2Model {
182 pub fn prefill_batch(
195 &self,
196 tokens: &[u32],
197 state: &mut V2State,
198 batch: &mut V2Batch,
199 mut logits: Option<&mut [f32]>,
200 ) -> Result<(), &'static str> {
201 let cfg = &self.cfg;
202 if tokens.is_empty() {
203 return Ok(());
204 }
205 if state.pos() + tokens.len() > cfg.max_seq_len {
206 return Err("sequence exceeds max_seq_len");
207 }
208 if tokens.iter().any(|&t| t as usize >= cfg.vocab_size) {
209 return Err("token id outside vocabulary");
210 }
211 if logits.as_ref().is_some_and(|l| l.len() != cfg.vocab_size) {
212 return Err("logits buffer is not vocab_size long");
213 }
214
215 let n_chunks = tokens.len().div_ceil(batch.cap);
216 for (ci, chunk) in tokens.chunks(batch.cap).enumerate() {
217 let last_chunk = ci + 1 == n_chunks;
218 self.prefill_chunk(chunk, state, batch)?;
219 if last_chunk {
220 if let Some(l) = logits.as_deref_mut() {
221 self.finish_logits(chunk.len() - 1, batch, l);
223 }
224 }
225 }
226 Ok(())
227 }
228
229 fn finish_logits(&self, i: usize, batch: &mut V2Batch, logits: &mut [f32]) {
231 let cfg = &self.cfg;
232 let (d, n) = (cfg.d_model, cfg.mhc_lanes);
233 let lanes = &batch.lanes[i * n * d..(i + 1) * n * d];
234 let out = &mut batch.attn_result[..d];
235 for (c, slot) in out.iter_mut().enumerate() {
236 let mut acc = 0.0f32;
237 for lane in 0..n {
238 acc += lanes[lane * d + c];
239 }
240 *slot = acc / n as f32;
241 }
242 zc_rms_norm_vec(out, &self.final_norm);
243 let prep_len = self.embedding.prepared_len();
246 let prep = &mut batch.attn_prep[..prep_len];
247 self.embedding.prepare_input(out, prep);
248 self.embedding.matvec_prepared(prep, logits);
249 }
250
251 fn prefill_chunk(
252 &self,
253 chunk: &[u32],
254 state: &mut V2State,
255 batch: &mut V2Batch,
256 ) -> Result<(), &'static str> {
257 let cfg = &self.cfg;
258 let (d, n) = (cfg.d_model, cfg.mhc_lanes);
259 let k = chunk.len();
260 let p0 = state.pos();
261 let sites = cfg.engram.sites.len();
262
263 for (i, &tok) in chunk.iter().enumerate() {
265 let pos = p0 + i;
266 state.set_history(pos, tok);
267 let row = &mut batch.u[..d];
268 self.embedding.dequantize_row(tok as usize, row);
269 let scale = math::sqrt(d as f32);
270 for v in row.iter_mut() {
271 *v *= scale;
272 }
273 for lane in 0..n {
274 batch.lanes[(i * n + lane) * d..(i * n + lane + 1) * d].copy_from_slice(row);
275 }
276 }
277
278 self.batch_engram(chunk.len(), p0, state, batch);
283 let _ = sites;
284
285 for li in 0..cfg.num_layers {
286 self.batch_mhc_pre(li, k, batch);
287 self.batch_engram_gate(li, k, batch);
288 self.batch_attention(li, k, p0, state, batch);
289 self.batch_mlp(li, k, batch);
290 self.batch_mhc_post(li, k, batch);
291 }
292
293 state.advance(k);
294 Ok(())
295 }
296
297 fn batch_engram(&self, k: usize, p0: usize, state: &mut V2State, b: &mut V2Batch) {
305 let cfg = &self.cfg;
306 if cfg.engram.sites.is_empty() {
307 return;
308 }
309 let d = cfg.d_model;
310 let eg = &cfg.engram;
311 let sites = eg.sites.len();
312 let heads = eg.heads();
313 let dil = eg.conv_dilation;
314 let fetched = eg.fetched_dim();
315 let ring = state.ring();
316
317 for site in 0..self.engrams.len() {
318 let weights = &self.engrams[site];
319 let prep = weights.value_proj.prepared_len();
320
321 for i in 0..k {
323 let (e_all, hist) = (&mut b.e_all, state.history_slice());
324 self.engram_fetch_into(
325 p0 + i,
326 site,
327 heads,
328 hist,
329 &mut e_all[i * fetched..(i + 1) * fetched],
330 );
331 }
332 for i in 0..k {
333 let (e_all, e_prep_all) = (&b.e_all, &mut b.e_prep_all);
334 weights.value_proj.prepare_input(
335 &e_all[i * fetched..(i + 1) * fetched],
336 &mut e_prep_all[i * prep..(i + 1) * prep],
337 );
338 }
339 let xh = &b.e_prep_all[..k * prep];
340
341 weights.value_proj.matmul_rows_prepared(
350 xh,
351 k,
352 0,
353 d,
354 &mut b.e_val_all[..k * d],
355 &mut b.acc,
356 );
357
358 let mut keys = core::mem::take(&mut b.mlp_out);
360 weights
361 .key_proj
362 .matmul_rows_prepared(xh, k, 0, d, &mut keys[..k * d], &mut b.acc);
363 for i in 0..k {
364 let base = (i * sites + site) * d;
365 b.engram_k[base..base + d].copy_from_slice(&keys[i * d..(i + 1) * d]);
366 }
367 b.mlp_out = keys;
368
369 for i in 0..k {
372 let pos = p0 + i;
373 let vbase = (i * sites + site) * d;
374 b.engram_val[vbase..vbase + d].fill(0.0);
375 for j in 0..eg.conv_taps {
376 let shift = j * dil;
377 if shift > pos {
378 continue;
379 }
380 let src_pos = pos - shift;
381 let tap = &weights.taps[j * d..(j + 1) * d];
382 if src_pos >= p0 {
383 let off = (src_pos - p0) * d;
384 let v = &b.e_val_all[off..off + d];
385 for (c, (&t, &vc)) in tap.iter().zip(v.iter()).enumerate() {
386 b.engram_val[vbase + c] += t * vc;
387 }
388 } else {
389 let slot = (site * ring + src_pos % ring) * d;
390 let v = state.engram_v_row(slot, d);
391 for (c, (&t, &vc)) in tap.iter().zip(v.iter()).enumerate() {
392 b.engram_val[vbase + c] += t * vc;
393 }
394 }
395 }
396 }
397
398 for i in 0..k {
402 let slot = (site * ring + (p0 + i) % ring) * d;
403 state
404 .engram_v_slot(slot, d)
405 .copy_from_slice(&b.e_val_all[i * d..(i + 1) * d]);
406 }
407 }
408 }
409
410 fn batch_mhc_pre(&self, li: usize, k: usize, b: &mut V2Batch) {
411 let cfg = &self.cfg;
412 let (d, n) = (cfg.d_model, cfg.mhc_lanes);
413 let prep = self.mhc.phi_pre.prepared_len();
414
415 #[cfg(feature = "parallel")]
418 {
419 use rayon::prelude::*;
420 let phi = &self.mhc.phi_pre;
421 b.nx[..k * n * d]
422 .par_chunks_mut(n * d)
423 .zip(b.nx_prep[..k * prep].par_chunks_mut(prep))
424 .zip(b.lanes[..k * n * d].par_chunks(n * d))
425 .for_each(|((nx, nx_prep), lanes)| {
426 rms_unit_to(lanes, nx);
427 phi.prepare_input(nx, nx_prep);
428 });
429 }
430 #[cfg(not(feature = "parallel"))]
431 for i in 0..k {
432 rms_unit_to(
433 &b.lanes[i * n * d..(i + 1) * n * d],
434 &mut b.nx[i * n * d..(i + 1) * n * d],
435 );
436 let (nx, nx_prep) = (&b.nx, &mut b.nx_prep);
437 self.mhc.phi_pre.prepare_input(
438 &nx[i * n * d..(i + 1) * n * d],
439 &mut nx_prep[i * prep..(i + 1) * prep],
440 );
441 }
442
443 self.mhc.phi_pre.matmul_rows_prepared(
444 &b.nx_prep[..k * prep],
445 k,
446 li * n,
447 n,
448 &mut b.hpre[..k * n],
449 &mut b.acc,
450 );
451 let a = self.mhc.a_pre[li];
452 for i in 0..k {
453 for lane in 0..n {
454 let z = a * b.hpre[i * n + lane]
455 + self.mhc.b_pre[li * n + lane]
456 + cfg.pre_off(li, lane);
457 b.hpre[i * n + lane] = sigmoid(z);
458 }
459 for c in 0..d {
460 let mut acc = 0.0f32;
461 for lane in 0..n {
462 acc += b.hpre[i * n + lane] * b.lanes[(i * n + lane) * d + c];
463 }
464 b.u[i * d + c] = acc;
465 }
466 let (src, dst) = (i * d, i * d);
467 for c in 0..d {
468 b.bx[dst + c] = b.u[src + c];
469 }
470 }
471 }
472
473 fn batch_engram_gate(&self, li: usize, k: usize, b: &mut V2Batch) {
474 let Some(site) = self.cfg.engram.site_of_layer(li) else {
475 return;
476 };
477 let d = self.cfg.d_model;
478 let sites = self.cfg.engram.sites.len();
479 let denom = math::sqrt(d as f32);
483
484 for i in 0..k {
485 let base = (i * sites + site) * d;
486 let u = &b.u[i * d..(i + 1) * d];
488 let ek = &b.engram_k[base..base + d];
489 let u_rms = rms_inv(u);
490 let k_rms = rms_inv(ek);
491 let mut dot = 0.0f32;
492 for c in 0..d {
493 dot += (u[c] * u_rms) * (ek[c] * k_rms);
494 }
495 let alpha = sigmoid(dot / denom);
496 for c in 0..d {
497 b.bx[i * d + c] = b.u[i * d + c] + alpha * b.engram_val[base + c];
498 }
499 }
500 }
501
502 fn batch_attention(
503 &self,
504 li: usize,
505 k: usize,
506 p0: usize,
507 state: &mut V2State,
508 b: &mut V2Batch,
509 ) {
510 let layer = &self.layers[li];
511 let cfg = &self.cfg;
512 let (h_n, kv_n, hd) = (cfg.num_heads, cfg.num_kv_heads, cfg.head_dim);
513 let (d, attn, kv) = (cfg.d_model, cfg.attn_dim(), cfg.kv_dim());
514 let reps = cfg.kv_repeat();
515 let prep = layer.q_proj.prepared_len();
516
517 #[cfg(feature = "parallel")]
519 {
520 use rayon::prelude::*;
521 b.h[..k * d]
522 .par_chunks_mut(d)
523 .zip(b.h_prep[..k * prep].par_chunks_mut(prep))
524 .zip(b.bx[..k * d].par_chunks(d))
525 .for_each(|((h, h_prep), bx)| {
526 h.copy_from_slice(bx);
527 zc_rms_norm_vec(h, &layer.norm_in);
528 layer.q_proj.prepare_input(h, h_prep);
529 });
530 }
531 #[cfg(not(feature = "parallel"))]
532 for i in 0..k {
533 b.h[i * d..(i + 1) * d].copy_from_slice(&b.bx[i * d..(i + 1) * d]);
534 zc_rms_norm_vec(&mut b.h[i * d..(i + 1) * d], &layer.norm_in);
535 let (h, h_prep) = (&b.h, &mut b.h_prep);
536 layer.q_proj.prepare_input(
537 &h[i * d..(i + 1) * d],
538 &mut h_prep[i * prep..(i + 1) * prep],
539 );
540 }
541 let xh = &b.h_prep[..k * prep];
542 layer
543 .q_proj
544 .matmul_rows_prepared(xh, k, 0, attn, &mut b.q[..k * attn], &mut b.acc);
545 layer
546 .k_proj
547 .matmul_rows_prepared(xh, k, 0, kv, &mut b.k[..k * kv], &mut b.acc);
548 layer
549 .v_proj
550 .matmul_rows_prepared(xh, k, 0, kv, &mut b.v[..k * kv], &mut b.acc);
551 layer
552 .gate_proj
553 .matmul_rows_prepared(xh, k, 0, attn, &mut b.gate[..k * attn], &mut b.acc);
554
555 #[cfg(feature = "parallel")]
565 {
566 use rayon::prelude::*;
567 b.q[..k * attn]
568 .par_chunks_mut(attn)
569 .zip(b.k[..k * kv].par_chunks_mut(kv))
570 .enumerate()
571 .for_each(|(i, (q, kk))| {
572 for head in 0..h_n {
573 zc_rms_norm_vec(&mut q[head * hd..(head + 1) * hd], &layer.q_norm);
574 }
575 for head in 0..kv_n {
576 zc_rms_norm_vec(&mut kk[head * hd..(head + 1) * hd], &layer.k_norm);
577 }
578 self.rope_apply(q, 1, h_n, hd, p0 + i);
579 self.rope_apply(kk, 1, kv_n, hd, p0 + i);
580 });
581 }
582 #[cfg(not(feature = "parallel"))]
583 for i in 0..k {
584 let pos = p0 + i;
585 let q = &mut b.q[i * attn..(i + 1) * attn];
586 for head in 0..h_n {
587 zc_rms_norm_vec(&mut q[head * hd..(head + 1) * hd], &layer.q_norm);
588 }
589 let kk = &mut b.k[i * kv..(i + 1) * kv];
590 for head in 0..kv_n {
591 zc_rms_norm_vec(&mut kk[head * hd..(head + 1) * hd], &layer.k_norm);
592 }
593 self.rope_apply(q, 1, h_n, hd, pos);
594 self.rope_apply(kk, 1, kv_n, hd, pos);
595 }
596
597 let scale = 1.0 / math::sqrt(hd as f32);
598 for i in 0..k {
599 let pos = p0 + i;
600 state.write_kv(
601 li,
602 pos,
603 &b.k[i * kv..(i + 1) * kv],
604 &b.v[i * kv..(i + 1) * kv],
605 );
606
607 let lo = state.window_lo(pos);
608 let span = pos - lo + 1;
609 for head in 0..h_n {
610 let kvh = head / reps;
611 let qrow = &b.q[i * attn + head * hd..i * attn + (head + 1) * hd];
612 for s in 0..span {
613 let krow = state.k_at(li, kvh, lo + s);
614 let mut acc = 0.0f32;
615 for (t, &kt) in krow.iter().enumerate() {
616 acc += qrow[t] * kt;
617 }
618 b.scores[s] = acc * scale;
619 }
620 softmax_inplace(&mut b.scores[..span]);
621 let obase = i * attn + head * hd;
622 b.attn_out[obase..obase + hd].fill(0.0);
623 for s in 0..span {
624 let w = b.scores[s];
625 let vrow = state.v_at(li, kvh, lo + s);
626 for (t, &vt) in vrow.iter().enumerate() {
627 b.attn_out[obase + t] += w * vt;
628 }
629 }
630 }
631 }
632
633 for i in 0..k * attn {
635 b.attn_out[i] *= sigmoid(b.gate[i]);
636 }
637 let oprep = layer.out_proj.prepared_len();
638 #[cfg(feature = "parallel")]
639 {
640 use rayon::prelude::*;
641 b.attn_prep[..k * oprep]
642 .par_chunks_mut(oprep)
643 .zip(b.attn_out[..k * attn].par_chunks(attn))
644 .for_each(|(ap, ao)| layer.out_proj.prepare_input(ao, ap));
645 }
646 #[cfg(not(feature = "parallel"))]
647 for i in 0..k {
648 let (ao, ap) = (&b.attn_out, &mut b.attn_prep);
649 layer.out_proj.prepare_input(
650 &ao[i * attn..(i + 1) * attn],
651 &mut ap[i * oprep..(i + 1) * oprep],
652 );
653 }
654 layer.out_proj.matmul_rows_prepared(
655 &b.attn_prep[..k * oprep],
656 k,
657 0,
658 d,
659 &mut b.attn_result[..k * d],
660 &mut b.acc,
661 );
662 }
663
664 fn batch_mlp(&self, li: usize, k: usize, b: &mut V2Batch) {
673 let layer = &self.layers[li];
674 let d = self.cfg.d_model;
675 let g = sigmoid(layer.attn_gate);
676
677 #[cfg(feature = "parallel")]
678 {
679 use rayon::prelude::*;
680 let hada_n = self.cfg.hada_n;
681 b.y[..k * d]
682 .par_chunks_mut(d)
683 .zip(b.attn_result[..k * d].par_chunks_mut(d))
684 .zip(b.h[..k * d].par_chunks_mut(d))
685 .zip(b.mlp_out[..k * d].par_chunks_mut(d))
686 .zip(b.bx[..k * d].par_chunks(d))
687 .zip(b.u[..k * d].par_chunks(d))
688 .for_each_init(
689 || vec![0.0f32; hada_n],
690 |hada, (((((y, ar), h), mlp), bx), u)| {
691 mlp_one(layer, g, y, ar, h, mlp, bx, u, hada);
692 },
693 );
694 }
695
696 #[cfg(not(feature = "parallel"))]
697 {
698 let mut hada = core::mem::take(&mut b.hada);
699 for i in 0..k {
700 let (lo, hi) = (i * d, (i + 1) * d);
701 let (y, ar) = (
703 &mut b.y[lo..hi] as *mut [f32],
704 &mut b.attn_result[lo..hi] as *mut [f32],
705 );
706 unsafe {
708 mlp_one(
709 layer,
710 g,
711 &mut *y,
712 &mut *ar,
713 &mut b.h[lo..hi],
714 &mut b.mlp_out[lo..hi],
715 &b.bx[lo..hi],
716 &b.u[lo..hi],
717 &mut hada,
718 );
719 }
720 }
721 b.hada = hada;
722 }
723 }
724
725 fn batch_mhc_post(&self, li: usize, k: usize, b: &mut V2Batch) {
726 let cfg = &self.cfg;
727 let (d, n) = (cfg.d_model, cfg.mhc_lanes);
728 let prep = self.mhc.phi_pre.prepared_len();
729 let xh = &b.nx_prep[..k * prep];
730
731 self.mhc
732 .phi_post
733 .matmul_rows_prepared(xh, k, li * n, n, &mut b.hpost[..k * n], &mut b.acc);
734 self.mhc.phi_res.matmul_rows_prepared(
735 xh,
736 k,
737 li * n * n,
738 n * n,
739 &mut b.hres[..k * n * n],
740 &mut b.acc,
741 );
742
743 let (a_post, a_res) = (self.mhc.a_post[li], self.mhc.a_res[li]);
744 let b_post = &self.mhc.b_post[li * n..(li + 1) * n];
745 let b_res = &self.mhc.b_res[li * n * n..(li + 1) * n * n];
746 let offs: Vec<f32> = (0..n).map(|lane| cfg.post_off(li, lane)).collect();
747
748 #[cfg(feature = "parallel")]
749 {
750 use rayon::prelude::*;
751 b.lanes[..k * n * d]
752 .par_chunks_mut(n * d)
753 .zip(b.lanes_prev[..k * n * d].par_chunks_mut(n * d))
754 .zip(b.hpost[..k * n].par_chunks_mut(n))
755 .zip(b.hres[..k * n * n].par_chunks_mut(n * n))
756 .zip(b.y[..k * d].par_chunks(d))
757 .for_each(|((((lanes, prev), hpost), hres), y)| {
758 mhc_post_one(
759 n, d, a_post, a_res, b_post, b_res, &offs, lanes, prev, hpost, hres, y,
760 );
761 });
762 }
763
764 #[cfg(not(feature = "parallel"))]
765 for i in 0..k {
766 let lanes = &mut b.lanes[i * n * d..(i + 1) * n * d] as *mut [f32];
767 let prev = &mut b.lanes_prev[i * n * d..(i + 1) * n * d] as *mut [f32];
768 unsafe {
770 mhc_post_one(
771 n,
772 d,
773 a_post,
774 a_res,
775 b_post,
776 b_res,
777 &offs,
778 &mut *lanes,
779 &mut *prev,
780 &mut b.hpost[i * n..(i + 1) * n],
781 &mut b.hres[i * n * n..(i + 1) * n * n],
782 &b.y[i * d..(i + 1) * d],
783 );
784 }
785 }
786 }
787}
788
789#[allow(clippy::too_many_arguments)]
791#[inline]
792fn mlp_one(
793 layer: &crate::v2::model::V2Layer,
794 g: f32,
795 y: &mut [f32],
796 attn_result: &mut [f32],
797 h: &mut [f32],
798 mlp_out: &mut [f32],
799 bx: &[f32],
800 u: &[f32],
801 hada: &mut [f32],
802) {
803 let d = y.len();
804 zc_rms_norm_vec(attn_result, &layer.post_norm);
805 for c in 0..d {
806 y[c] = bx[c] + g * attn_result[c];
807 }
808 h.copy_from_slice(y);
809 zc_rms_norm_vec(h, &layer.pre_hada);
810 hadamard_mlp(h, &layer.d1, &layer.d2, &layer.d3, hada, mlp_out);
811 for c in 0..d {
812 y[c] += mlp_out[c];
813 y[c] -= u[c];
814 }
815}
816
817#[allow(clippy::too_many_arguments)]
819#[inline]
820fn mhc_post_one(
821 n: usize,
822 d: usize,
823 a_post: f32,
824 a_res: f32,
825 b_post: &[f32],
826 b_res: &[f32],
827 post_off: &[f32],
828 lanes: &mut [f32],
829 prev: &mut [f32],
830 hpost: &mut [f32],
831 hres: &mut [f32],
832 y: &[f32],
833) {
834 for lane in 0..n {
835 let z = a_post * hpost[lane] + b_post[lane] + post_off[lane];
836 hpost[lane] = 2.0 * sigmoid(z);
837 }
838 for (j, v) in hres.iter_mut().enumerate() {
839 *v = a_res * *v + b_res[j];
840 }
841 sinkhorn(hres, n);
842
843 prev.copy_from_slice(lanes);
844 for lane_i in 0..n {
845 let hp = hpost[lane_i];
846 for c in 0..d {
847 let mut acc = 0.0f32;
848 for lane_j in 0..n {
849 acc += hres[lane_i * n + lane_j] * prev[lane_j * d + c];
850 }
851 lanes[lane_i * d + c] = acc + hp * y[c];
852 }
853 }
854}
855
856#[inline]
858fn rms_inv(x: &[f32]) -> f32 {
859 let mean_sq = x.iter().map(|v| v * v).sum::<f32>() / x.len() as f32;
860 1.0 / math::sqrt(mean_sq + crate::v2::kernels::EPS)
861}