1use crate::cq::CqWeight;
27use crate::hadamard::hada_n;
28use crate::math;
29use crate::norm::zc_rms_norm_vec;
30use crate::ops::{sigmoid, softmax_inplace};
31use crate::rope::RopeCache;
32use crate::v2::config::V2Config;
33use crate::v2::kernels::{
34 engram_index, engram_table_order, hadamard_mlp, rms_unit, rms_unit_to, sinkhorn,
35};
36use alloc::vec;
37use alloc::vec::Vec;
38
39pub struct V2Layer {
41 pub norm_in: Vec<f32>,
43 pub q_proj: CqWeight,
44 pub k_proj: CqWeight,
45 pub v_proj: CqWeight,
46 pub gate_proj: CqWeight,
48 pub out_proj: CqWeight,
49 pub q_norm: Vec<f32>,
51 pub k_norm: Vec<f32>,
52 pub post_norm: Vec<f32>,
54 pub attn_gate: f32,
56 pub pre_hada: Vec<f32>,
58 pub d1: Vec<f32>,
60 pub d2: Vec<f32>,
61 pub d3: Vec<f32>,
62}
63
64pub struct V2Mhc {
66 pub a_pre: Vec<f32>,
68 pub a_post: Vec<f32>,
69 pub a_res: Vec<f32>,
70 pub b_pre: Vec<f32>,
72 pub b_post: Vec<f32>,
73 pub b_res: Vec<f32>,
75 pub phi_pre: CqWeight,
77 pub phi_post: CqWeight,
78 pub phi_res: CqWeight,
80}
81
82pub struct V2Engram {
84 pub tables: CqWeight,
86 pub key_proj: CqWeight,
88 pub value_proj: CqWeight,
90 pub taps: Vec<f32>,
92}
93
94pub struct V2Model {
97 pub cfg: V2Config,
98 pub embedding: CqWeight,
100 pub layers: Vec<V2Layer>,
101 pub mhc: V2Mhc,
102 pub engrams: Vec<V2Engram>,
103 pub final_norm: Vec<f32>,
104 rope: RopeCache,
105 embed_scale: f32,
106}
107
108pub struct V2State {
111 k_cache: Vec<Vec<f32>>,
123 v_cache: Vec<Vec<f32>>,
124 history: Vec<u32>,
125 pos: usize,
126 kv_heads: usize,
127 head_dim: usize,
128 cache_len: usize,
130 kv_window: usize,
134
135 engram_v: Vec<f32>,
138 ring: usize,
139
140 lanes: Vec<f32>, lanes_prev: Vec<f32>, nx: Vec<f32>, nx_prep: Vec<f32>,
145 u: Vec<f32>, bx: Vec<f32>, y: Vec<f32>, h: Vec<f32>, h_prep: Vec<f32>,
150 hpre: Vec<f32>, hpost: Vec<f32>, hres: Vec<f32>, q: Vec<f32>, k: Vec<f32>, v: Vec<f32>, gate: Vec<f32>, attn_out: Vec<f32>, attn_prep: Vec<f32>,
159 attn_result: Vec<f32>, scores: Vec<f32>, hada: Vec<f32>, mlp_out: Vec<f32>, e_buf: Vec<f32>, e_prep: Vec<f32>,
165 engram_k: Vec<f32>, engram_val: Vec<f32>, alpha: Vec<f32>, lm: Vec<f32>, lm_prep: Vec<f32>,
171 tmp_a: Vec<f32>, tmp_b: Vec<f32>, }
174
175impl V2Model {
176 pub fn new(
177 cfg: V2Config,
178 embedding: CqWeight,
179 layers: Vec<V2Layer>,
180 mhc: V2Mhc,
181 engrams: Vec<V2Engram>,
182 final_norm: Vec<f32>,
183 ) -> Result<Self, &'static str> {
184 cfg.validate()?;
185 if layers.len() != cfg.num_layers {
186 return Err("layer count does not match num_layers");
187 }
188 if engrams.len() != cfg.engram.sites.len() {
189 return Err("engram count does not match site count");
190 }
191 if final_norm.len() != cfg.d_model {
192 return Err("final_norm width does not match d_model");
193 }
194 if hada_n(cfg.d_model) != cfg.hada_n {
195 return Err("hada_n is not d_model rounded up to a power of two");
196 }
197 if embedding.out_feat != cfg.vocab_size || embedding.in_feat != cfg.d_model {
198 return Err("embedding shape does not match geometry");
199 }
200 let n = cfg.mhc_lanes;
201 let l = cfg.num_layers;
202 if mhc.a_pre.len() != l || mhc.a_post.len() != l || mhc.a_res.len() != l {
203 return Err("mhc a_* length does not match num_layers");
204 }
205 if mhc.b_pre.len() != l * n || mhc.b_post.len() != l * n || mhc.b_res.len() != l * n * n {
206 return Err("mhc b_* length does not match num_layers x lanes");
207 }
208 if mhc.phi_pre.out_feat != l * n
209 || mhc.phi_post.out_feat != l * n
210 || mhc.phi_res.out_feat != l * n * n
211 {
212 return Err("mhc phi_* row count does not match num_layers x lanes");
213 }
214 if mhc.phi_pre.in_feat != cfg.mhc_width() {
215 return Err("mhc phi_* width does not match lanes x d_model");
216 }
217 for layer in &layers {
218 if layer.d1.len() != cfg.hada_n
219 || layer.d2.len() != cfg.hada_n
220 || layer.d3.len() != cfg.hada_n
221 {
222 return Err("HadamardMLP diagonal width does not match hada_n");
223 }
224 if layer.q_norm.len() != cfg.head_dim || layer.k_norm.len() != cfg.head_dim {
225 return Err("q_norm/k_norm width does not match head_dim");
226 }
227 }
228 for e in &engrams {
229 if e.taps.len() != cfg.engram.conv_taps * cfg.d_model {
230 return Err("engram taps shape does not match conv_taps x d_model");
231 }
232 if e.tables.in_feat != cfg.engram.sub_dim {
233 return Err("engram table width does not match sub_dim");
234 }
235 }
236
237 let rope = RopeCache::new(cfg.max_seq_len, cfg.head_dim, cfg.rope_theta);
238 let embed_scale = math::sqrt(cfg.d_model as f32);
239 Ok(Self {
240 cfg,
241 embedding,
242 layers,
243 mhc,
244 engrams,
245 final_norm,
246 rope,
247 embed_scale,
248 })
249 }
250
251 pub const ENGRAM_GATE_SOURCE: &'static str = "decode.py (_forward_cached), gate on u";
260
261 pub fn make_state(&self) -> V2State {
272 let cache_len = if self.cfg.kv_window == 0 {
273 self.cfg.max_seq_len
274 } else {
275 self.cfg.kv_window.min(self.cfg.max_seq_len)
276 };
277 self.make_state_with_cache(cache_len, self.cfg.kv_window)
278 }
279
280 pub fn make_state_full_causal(&self) -> V2State {
286 self.make_state_with_cache(self.cfg.max_seq_len, 0)
287 }
288
289 fn make_state_with_cache(&self, cache_len: usize, kv_window: usize) -> V2State {
290 let cfg = &self.cfg;
291 let (d, n) = (cfg.d_model, cfg.mhc_lanes);
292 let sites = cfg.engram.sites.len();
293 let ring = (cfg.engram.window() + 1).max(1);
294 let fetched = cfg.engram.fetched_dim().max(1);
295
296 let engram_prep = self
297 .engrams
298 .first()
299 .map_or(fetched, |e| e.value_proj.prepared_len());
300
301 V2State {
302 k_cache: (0..cfg.num_layers)
303 .map(|_| vec![0.0f32; cfg.num_kv_heads * cache_len * cfg.head_dim])
304 .collect(),
305 v_cache: (0..cfg.num_layers)
306 .map(|_| vec![0.0f32; cfg.num_kv_heads * cache_len * cfg.head_dim])
307 .collect(),
308 history: vec![0u32; cfg.max_seq_len],
309 pos: 0,
310 kv_heads: cfg.num_kv_heads,
311 head_dim: cfg.head_dim,
312 cache_len,
313 kv_window,
314 engram_v: vec![0.0; sites * ring * d],
315 ring,
316 lanes: vec![0.0; n * d],
317 lanes_prev: vec![0.0; n * d],
318 nx: vec![0.0; n * d],
319 nx_prep: vec![0.0; self.mhc.phi_pre.prepared_len()],
320 u: vec![0.0; d],
321 bx: vec![0.0; d],
322 y: vec![0.0; d],
323 h: vec![0.0; d],
324 h_prep: vec![0.0; self.layers[0].q_proj.prepared_len()],
325 hpre: vec![0.0; n],
326 hpost: vec![0.0; n],
327 hres: vec![0.0; n * n],
328 q: vec![0.0; cfg.attn_dim()],
329 k: vec![0.0; cfg.kv_dim()],
330 v: vec![0.0; cfg.kv_dim()],
331 gate: vec![0.0; cfg.attn_dim()],
332 attn_out: vec![0.0; cfg.attn_dim()],
333 attn_prep: vec![0.0; self.layers[0].out_proj.prepared_len()],
334 attn_result: vec![0.0; d],
335 scores: vec![0.0; cfg.max_seq_len],
336 hada: vec![0.0; cfg.hada_n],
337 mlp_out: vec![0.0; d],
338 e_buf: vec![0.0; fetched],
339 e_prep: vec![0.0; engram_prep],
340 engram_k: vec![0.0; sites * d],
341 engram_val: vec![0.0; sites * d],
342 alpha: vec![0.0; sites.max(1)],
343 lm: vec![0.0; d],
344 lm_prep: vec![0.0; self.embedding.prepared_len()],
345 tmp_a: vec![0.0; d],
346 tmp_b: vec![0.0; d],
347 }
348 }
349
350 pub fn step(
355 &self,
356 token: u32,
357 state: &mut V2State,
358 logits: &mut [f32],
359 ) -> Result<(), &'static str> {
360 self.forward(token, state, Some(logits), &mut |_, _, _| {})
361 }
362
363 pub fn step_prefill(&self, token: u32, state: &mut V2State) -> Result<(), &'static str> {
374 self.forward(token, state, None, &mut |_, _, _| {})
375 }
376
377 pub fn step_traced<F: FnMut(&str, usize, &[f32])>(
387 &self,
388 token: u32,
389 state: &mut V2State,
390 logits: &mut [f32],
391 trace: &mut F,
392 ) -> Result<(), &'static str> {
393 self.forward(token, state, Some(logits), trace)
394 }
395
396 fn forward<F: FnMut(&str, usize, &[f32])>(
397 &self,
398 token: u32,
399 state: &mut V2State,
400 logits: Option<&mut [f32]>,
401 trace: &mut F,
402 ) -> Result<(), &'static str> {
403 const WHOLE: usize = usize::MAX;
404 let cfg = &self.cfg;
405 if state.pos >= cfg.max_seq_len {
406 return Err("sequence exceeds max_seq_len");
407 }
408 if token as usize >= cfg.vocab_size {
409 return Err("token id outside vocabulary");
410 }
411 if logits.as_ref().is_some_and(|l| l.len() != cfg.vocab_size) {
412 return Err("logits buffer is not vocab_size long");
413 }
414
415 let pos = state.pos;
416 let (d, n) = (cfg.d_model, cfg.mhc_lanes);
417 state.history[pos] = token;
418
419 self.embedding
421 .dequantize_row(token as usize, &mut state.tmp_a);
422 for v in state.tmp_a.iter_mut() {
423 *v *= self.embed_scale;
424 }
425 trace("embed", WHOLE, &state.tmp_a);
426 for lane in 0..n {
427 state.lanes[lane * d..(lane + 1) * d].copy_from_slice(&state.tmp_a);
428 }
429
430 self.engram_fill(state, pos);
432 if !cfg.engram.sites.is_empty() {
433 trace("engram_k0", WHOLE, &state.engram_k[..d]);
434 trace("engram_v0", WHOLE, &state.engram_val[..d]);
435 }
436
437 for li in 0..cfg.num_layers {
438 self.mhc_pre(state, li);
439 trace("hpre", li, &state.hpre);
440 trace("u", li, &state.u);
441 self.engram_gate(state, li);
442 trace("bx", li, &state.bx);
443 if let Some(site) = cfg.engram.site_of_layer(li) {
444 trace("alpha", li, &state.alpha[site..site + 1]);
445 }
446 self.block(state, li, pos);
447 trace("y", li, &state.y);
448 self.mhc_post(state, li);
449 trace("hpost", li, &state.hpost);
450 trace("hres", li, &state.hres);
451 trace("x", li, &state.lanes);
452 }
453
454 for c in 0..d {
456 let mut acc = 0.0f32;
457 for lane in 0..n {
458 acc += state.lanes[lane * d + c];
459 }
460 state.lm[c] = acc / n as f32;
461 }
462 trace("lane_mean", WHOLE, &state.lm);
463 zc_rms_norm_vec(&mut state.lm, &self.final_norm);
464 trace("final_norm", WHOLE, &state.lm);
465 if let Some(logits) = logits {
466 self.embedding.prepare_input(&state.lm, &mut state.lm_prep);
467 self.embedding.matvec_prepared(&state.lm_prep, logits);
468 trace("logits", WHOLE, logits);
469 }
470
471 state.pos += 1;
472 Ok(())
473 }
474
475 pub fn last_hidden(state: &V2State) -> &[f32] {
482 &state.lm
483 }
484
485 pub(crate) fn rope_apply(
488 &self,
489 x: &mut [f32],
490 seq_len: usize,
491 num_heads: usize,
492 head_dim: usize,
493 offset: usize,
494 ) {
495 self.rope.apply(x, seq_len, num_heads, head_dim, offset);
496 }
497
498 fn mhc_pre(&self, state: &mut V2State, li: usize) {
500 let cfg = &self.cfg;
501 let (d, n) = (cfg.d_model, cfg.mhc_lanes);
502
503 rms_unit_to(&state.lanes, &mut state.nx);
504 self.mhc
507 .phi_pre
508 .prepare_input(&state.nx, &mut state.nx_prep);
509
510 self.mhc
511 .phi_pre
512 .matvec_rows_prepared(&state.nx_prep, li * n, &mut state.hpre);
513 let a = self.mhc.a_pre[li];
514 for lane in 0..n {
515 let z = a * state.hpre[lane] + self.mhc.b_pre[li * n + lane] + cfg.pre_off(li, lane);
516 state.hpre[lane] = sigmoid(z);
517 }
518
519 for c in 0..d {
520 let mut acc = 0.0f32;
521 for lane in 0..n {
522 acc += state.hpre[lane] * state.lanes[lane * d + c];
523 }
524 state.u[c] = acc;
525 }
526 state.bx.copy_from_slice(&state.u);
527 }
528
529 fn engram_gate(&self, state: &mut V2State, li: usize) {
531 let Some(site) = self.cfg.engram.site_of_layer(li) else {
532 return;
533 };
534 let d = self.cfg.d_model;
535 let base = site * d;
536
537 rms_unit_to(&state.u, &mut state.tmp_a);
538 state.tmp_b.copy_from_slice(&state.engram_k[base..base + d]);
539 rms_unit(&mut state.tmp_b);
540 let mut dot = 0.0f32;
541 for c in 0..d {
542 dot += state.tmp_a[c] * state.tmp_b[c];
543 }
544 let alpha = sigmoid(dot / math::sqrt(d as f32));
545 state.alpha[site] = alpha;
546 for c in 0..d {
547 state.bx[c] = state.u[c] + alpha * state.engram_val[base + c];
548 }
549 }
550
551 fn block(&self, state: &mut V2State, li: usize, pos: usize) {
554 let layer = &self.layers[li];
555 let d = self.cfg.d_model;
556
557 state.h.copy_from_slice(&state.bx);
558 zc_rms_norm_vec(&mut state.h, &layer.norm_in);
559 self.attention(state, li, pos);
560 zc_rms_norm_vec(&mut state.attn_result, &layer.post_norm);
561 let g = sigmoid(layer.attn_gate);
562 for c in 0..d {
563 state.y[c] = state.bx[c] + g * state.attn_result[c];
564 }
565
566 state.h.copy_from_slice(&state.y);
567 zc_rms_norm_vec(&mut state.h, &layer.pre_hada);
568 hadamard_mlp(
569 &state.h,
570 &layer.d1,
571 &layer.d2,
572 &layer.d3,
573 &mut state.hada,
574 &mut state.mlp_out,
575 );
576 for c in 0..d {
577 state.y[c] += state.mlp_out[c];
578 state.y[c] -= state.u[c];
579 }
580 }
581
582 fn attention(&self, state: &mut V2State, li: usize, pos: usize) {
584 let layer = &self.layers[li];
585 let cfg = &self.cfg;
586 let (h_n, kv_n, hd) = (cfg.num_heads, cfg.num_kv_heads, cfg.head_dim);
587 let reps = cfg.kv_repeat();
588
589 layer.q_proj.prepare_input(&state.h, &mut state.h_prep);
591 layer.q_proj.matvec_prepared(&state.h_prep, &mut state.q);
592 layer.k_proj.matvec_prepared(&state.h_prep, &mut state.k);
593 layer.v_proj.matvec_prepared(&state.h_prep, &mut state.v);
594 layer
595 .gate_proj
596 .matvec_prepared(&state.h_prep, &mut state.gate);
597
598 for head in 0..h_n {
599 zc_rms_norm_vec(&mut state.q[head * hd..(head + 1) * hd], &layer.q_norm);
600 }
601 for head in 0..kv_n {
602 zc_rms_norm_vec(&mut state.k[head * hd..(head + 1) * hd], &layer.k_norm);
603 }
604 self.rope.apply(&mut state.q, 1, h_n, hd, pos);
605 self.rope.apply(&mut state.k, 1, kv_n, hd, pos);
606
607 let k_src = core::mem::take(&mut state.k);
609 let v_src = core::mem::take(&mut state.v);
610 state.write_kv(li, pos, &k_src, &v_src);
611 state.k = k_src;
612 state.v = v_src;
613
614 let lo = state.window_lo(pos);
618 let span = pos - lo + 1;
619 let scale = 1.0 / math::sqrt(hd as f32);
620
621 let (cache_len, ring_hd) = (state.cache_len, state.head_dim);
625 let slot = |kv: usize, p: usize| (kv * cache_len + p % cache_len) * ring_hd;
626
627 for head in 0..h_n {
628 let kv = head / reps;
629
630 for s in 0..span {
631 let off = slot(kv, lo + s);
632 let krow = &state.k_cache[li][off..off + hd];
633 let qrow = &state.q[head * hd..(head + 1) * hd];
634 let mut acc = 0.0f32;
635 for i in 0..hd {
636 acc += qrow[i] * krow[i];
637 }
638 state.scores[s] = acc * scale;
639 }
640 softmax_inplace(&mut state.scores[..span]);
641
642 let obase = head * hd;
643 state.attn_out[obase..obase + hd].fill(0.0);
644 for s in 0..span {
645 let w = state.scores[s];
646 let off = slot(kv, lo + s);
647 for i in 0..hd {
648 state.attn_out[obase + i] += w * state.v_cache[li][off + i];
649 }
650 }
651 }
652
653 for (o, &g) in state.attn_out.iter_mut().zip(state.gate.iter()) {
654 *o *= sigmoid(g);
655 }
656 layer
657 .out_proj
658 .prepare_input(&state.attn_out, &mut state.attn_prep);
659 layer
660 .out_proj
661 .matvec_prepared(&state.attn_prep, &mut state.attn_result);
662 }
663
664 fn mhc_post(&self, state: &mut V2State, li: usize) {
667 let cfg = &self.cfg;
668 let (d, n) = (cfg.d_model, cfg.mhc_lanes);
669
670 self.mhc
671 .phi_post
672 .matvec_rows_prepared(&state.nx_prep, li * n, &mut state.hpost);
673 let a_post = self.mhc.a_post[li];
674 for lane in 0..n {
675 let z = a_post * state.hpost[lane]
676 + self.mhc.b_post[li * n + lane]
677 + cfg.post_off(li, lane);
678 state.hpost[lane] = 2.0 * sigmoid(z);
679 }
680
681 self.mhc
682 .phi_res
683 .matvec_rows_prepared(&state.nx_prep, li * n * n, &mut state.hres);
684 let a_res = self.mhc.a_res[li];
685 for k in 0..n * n {
686 state.hres[k] = a_res * state.hres[k] + self.mhc.b_res[li * n * n + k];
687 }
688 sinkhorn(&mut state.hres, n);
689
690 state.lanes_prev.copy_from_slice(&state.lanes);
692 for i in 0..n {
693 let hp = state.hpost[i];
694 for c in 0..d {
695 let mut acc = 0.0f32;
696 for j in 0..n {
697 acc += state.hres[i * n + j] * state.lanes_prev[j * d + c];
698 }
699 state.lanes[i * d + c] = acc + hp * state.y[c];
700 }
701 }
702 }
703
704 pub(crate) fn engram_fill(&self, state: &mut V2State, pos: usize) {
710 let cfg = &self.cfg;
711 if cfg.engram.sites.is_empty() {
712 return;
713 }
714 let d = cfg.d_model;
715 let eg = &cfg.engram;
716 let heads = eg.heads();
717 let dil = eg.conv_dilation;
718 let ring = state.ring;
719
720 for site in 0..self.engrams.len() {
721 self.engram_fetch(state, pos, site, heads);
722 let weights = &self.engrams[site];
723 weights
724 .value_proj
725 .prepare_input(&state.e_buf, &mut state.e_prep);
726
727 let slot = (site * ring + pos % ring) * d;
729 weights
730 .value_proj
731 .matvec_prepared(&state.e_prep, &mut state.engram_v[slot..slot + d]);
732
733 let kbase = site * d;
734 weights
735 .key_proj
736 .matvec_prepared(&state.e_prep, &mut state.engram_k[kbase..kbase + d]);
737
738 let vbase = site * d;
741 state.engram_val[vbase..vbase + d].fill(0.0);
742 for j in 0..eg.conv_taps {
743 let shift = j * dil;
744 if shift > pos {
745 continue;
746 }
747 let src = (site * ring + (pos - shift) % ring) * d;
748 let tap = &weights.taps[j * d..(j + 1) * d];
749 let v = &state.engram_v[src..src + d];
750 for (dst, (&t, &vc)) in state.engram_val[vbase..vbase + d]
751 .iter_mut()
752 .zip(tap.iter().zip(v.iter()))
753 {
754 *dst += t * vc;
755 }
756 }
757 }
758 }
759
760 fn engram_fetch(&self, state: &mut V2State, pos: usize, site: usize, heads: usize) {
762 let (hist, e_buf) = (&state.history, &mut state.e_buf);
763 self.engram_fetch_into(pos, site, heads, hist, e_buf);
764 }
765
766 pub(crate) fn engram_fetch_into(
771 &self,
772 pos: usize,
773 site: usize,
774 heads: usize,
775 history: &[u32],
776 out: &mut [f32],
777 ) {
778 let eg = &self.cfg.engram;
779 let tables = &self.engrams[site].tables;
780 let sub = eg.sub_dim;
781
782 for table in 0..eg.num_tables {
783 let order = eg.orders[engram_table_order(table, heads)];
784 let idx = engram_index(table, order, eg.slots, |j| {
785 if j > pos {
786 0
787 } else {
788 history[pos - j]
789 }
790 });
791 let dst = &mut out[table * sub..(table + 1) * sub];
792 if order > 0 && order - 1 > pos {
793 dst.fill(0.0);
796 } else {
797 tables.dequantize_row(table * eg.slots + idx, dst);
798 }
799 }
800 }
801}
802
803impl V2State {
804 pub fn pos(&self) -> usize {
806 self.pos
807 }
808
809 pub fn history(&self) -> &[u32] {
811 &self.history[..self.pos]
812 }
813
814 #[inline]
818 pub(crate) fn window_lo(&self, pos: usize) -> usize {
819 if self.kv_window == 0 {
820 0
821 } else {
822 pos + 1 - self.kv_window.min(pos + 1)
823 }
824 }
825
826 pub fn kv_window(&self) -> usize {
828 self.kv_window
829 }
830
831 pub fn set_kv_window(
847 &mut self,
848 window: Option<usize>,
849 model: &V2Model,
850 ) -> Result<(), &'static str> {
851 let w = window.unwrap_or(model.cfg.kv_window);
852 let needed = if w == 0 { model.cfg.max_seq_len } else { w };
853 if needed > self.cache_len {
854 return Err(
855 "requested attention window exceeds this state's KV capacity; \
856 allocate with make_state_full_causal",
857 );
858 }
859 self.kv_window = w;
860 Ok(())
861 }
862
863 pub(crate) fn set_history(&mut self, pos: usize, token: u32) {
865 self.history[pos] = token;
866 }
867
868 pub(crate) fn advance(&mut self, n: usize) {
870 self.pos += n;
871 }
872
873 #[inline]
875 fn slot(&self, kv_head: usize, pos: usize) -> usize {
876 (kv_head * self.cache_len + pos % self.cache_len) * self.head_dim
877 }
878
879 pub(crate) fn write_kv(&mut self, layer: usize, pos: usize, k: &[f32], v: &[f32]) {
881 let hd = self.head_dim;
882 for head in 0..self.kv_heads {
883 let off = self.slot(head, pos);
884 self.k_cache[layer][off..off + hd].copy_from_slice(&k[head * hd..(head + 1) * hd]);
885 self.v_cache[layer][off..off + hd].copy_from_slice(&v[head * hd..(head + 1) * hd]);
886 }
887 }
888
889 #[inline]
892 pub(crate) fn k_at(&self, layer: usize, kv_head: usize, pos: usize) -> &[f32] {
893 let off = self.slot(kv_head, pos);
894 &self.k_cache[layer][off..off + self.head_dim]
895 }
896
897 #[inline]
898 pub(crate) fn v_at(&self, layer: usize, kv_head: usize, pos: usize) -> &[f32] {
899 let off = self.slot(kv_head, pos);
900 &self.v_cache[layer][off..off + self.head_dim]
901 }
902
903 pub(crate) fn ring(&self) -> usize {
905 self.ring
906 }
907
908 pub(crate) fn history_slice(&self) -> &[u32] {
909 &self.history
910 }
911
912 pub(crate) fn engram_v_slot(&mut self, slot: usize, d: usize) -> &mut [f32] {
913 &mut self.engram_v[slot..slot + d]
914 }
915
916 pub(crate) fn engram_v_row(&self, slot: usize, d: usize) -> &[f32] {
917 &self.engram_v[slot..slot + d]
918 }
919
920 pub fn cache_len(&self) -> usize {
922 self.cache_len
923 }
924
925 pub fn kv_bytes(&self) -> usize {
927 self.k_cache
928 .iter()
929 .chain(self.v_cache.iter())
930 .map(|c| c.len() * 4)
931 .sum()
932 }
933
934 pub fn reset(&mut self) {
936 self.pos = 0;
937 for c in self.k_cache.iter_mut().chain(self.v_cache.iter_mut()) {
938 c.fill(0.0);
939 }
940 self.engram_v.fill(0.0);
941 self.history.fill(0);
942 }
943}