Skip to main content

cortiq_engine/
dsv41.rs

1//! DeepSeek-V4.1 runtime.
2//!
3//! V4.1 is close enough to V4 to share the small numerical kernels in
4//! [`crate::dsv4`], but its cache contract is different: compressors and
5//! index keys are published by source layers and consumed by later layers,
6//! Engram tables are raw E4M3/E8M0 rows, and the mHC fold for attention uses
7//! the previous sub-block's `pre` coefficients.  Keeping the implementation
8//! here avoids making the V4 executor guess those rules from tensor names.
9//!
10//! The hot weights remain `QTensor` handles.  Experts therefore stay in the
11//! CMF mmap and are touched only for the routes selected for the current
12//! token.  The Engram tables deliberately do not go through `QTensor`: U8 is
13//! the on-disk byte representation, and a full dequantisation would exceed
14//! the host memory budget by hundreds of gigabytes.
15
16use crate::pool::Pool;
17use crate::qtensor::QTensor;
18use cortiq_core::{CmfModel, TensorDtype};
19use std::sync::Arc;
20
21const DEAD: i64 = -1;
22const FP8_NAN: u8 = 0x7f;
23const E8M0_NAN: u8 = 0xff;
24
25/// `CMF_DSV41_PROF=1` enables one compact, cumulative timing report for the
26/// V4.1 path.  The timers are deliberately optional and token scoped: there
27/// is no per-layer logging and the disabled path only tests the cached flag.
28pub(crate) mod prof {
29    use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
30    use std::time::Instant;
31
32    pub static TOKENS: AtomicU64 = AtomicU64::new(0);
33    pub static LAYER_VISITS: AtomicU64 = AtomicU64::new(0);
34    pub static ATTN_NS: AtomicU64 = AtomicU64::new(0);
35    pub static SPARSE_NS: AtomicU64 = AtomicU64::new(0);
36    pub static ATTN_OTHER_NS: AtomicU64 = AtomicU64::new(0);
37    pub static MOE_NS: AtomicU64 = AtomicU64::new(0);
38    pub static ENGRAM_NS: AtomicU64 = AtomicU64::new(0);
39    pub static HEAD_NS: AtomicU64 = AtomicU64::new(0);
40    pub static TOTAL_NS: AtomicU64 = AtomicU64::new(0);
41    pub static MOE_CALLS: AtomicU64 = AtomicU64::new(0);
42    pub static GPU_MOE_CALLS: AtomicU64 = AtomicU64::new(0);
43    pub static CPU_MOE_CALLS: AtomicU64 = AtomicU64::new(0);
44    pub static COLD_EXPERTS: AtomicU64 = AtomicU64::new(0);
45    pub static GPU_ATTN_CALLS: AtomicU64 = AtomicU64::new(0);
46    pub static GPU_ATTN_FALLBACKS: AtomicU64 = AtomicU64::new(0);
47    static REPORTED: AtomicBool = AtomicBool::new(false);
48
49    #[inline]
50    pub fn on() -> bool {
51        static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
52        *ON.get_or_init(|| std::env::var("CMF_DSV41_PROF").is_ok_and(|v| v != "0"))
53    }
54
55    #[inline]
56    pub fn start() -> Option<Instant> {
57        on().then(Instant::now)
58    }
59
60    #[inline]
61    pub fn add(dst: &AtomicU64, started: Option<Instant>) {
62        if let Some(started) = started {
63            dst.fetch_add(started.elapsed().as_nanos() as u64, Ordering::Relaxed);
64        }
65    }
66
67    #[inline]
68    pub fn note_token(layers: usize) {
69        if on() {
70            TOKENS.fetch_add(1, Ordering::Relaxed);
71            LAYER_VISITS.fetch_add(layers as u64, Ordering::Relaxed);
72        }
73    }
74
75    #[inline]
76    pub fn note_moe() {
77        if on() {
78            MOE_CALLS.fetch_add(1, Ordering::Relaxed);
79        }
80    }
81
82    #[inline]
83    pub fn note_gpu_moe() {
84        if on() {
85            GPU_MOE_CALLS.fetch_add(1, Ordering::Relaxed);
86        }
87    }
88
89    #[inline]
90    pub fn note_cpu_moe() {
91        if on() {
92            CPU_MOE_CALLS.fetch_add(1, Ordering::Relaxed);
93        }
94    }
95
96    #[inline]
97    pub fn note_cold(count: usize) {
98        if on() {
99            COLD_EXPERTS.fetch_add(count as u64, Ordering::Relaxed);
100        }
101    }
102
103    pub fn note_gpu_attn() {
104        if on() {
105            GPU_ATTN_CALLS.fetch_add(1, Ordering::Relaxed);
106        }
107    }
108
109    pub fn note_gpu_attn_fallback() {
110        if on() {
111            GPU_ATTN_FALLBACKS.fetch_add(1, Ordering::Relaxed);
112        }
113    }
114
115    /// Print once at the normal CLI report point.  The dense number is the
116    /// residual of the whole token after the explicitly timed attention,
117    /// MoE, Engram, and final head stages, so it includes embeddings,
118    /// hyper-connections, norms, routing, and other host work without
119    /// double-counting nested operations.
120    pub fn report() {
121        if !on() || REPORTED.swap(true, Ordering::Relaxed) {
122            return;
123        }
124        let tokens = TOKENS.load(Ordering::Relaxed).max(1) as f64;
125        let layers = LAYER_VISITS.load(Ordering::Relaxed);
126        let ns = |counter: &AtomicU64| counter.load(Ordering::Relaxed) as f64;
127        let attn = ns(&ATTN_NS);
128        let sparse = ns(&SPARSE_NS);
129        let attn_other = ns(&ATTN_OTHER_NS);
130        let moe = ns(&MOE_NS);
131        let engram = ns(&ENGRAM_NS);
132        let head = ns(&HEAD_NS);
133        let total = ns(&TOTAL_NS);
134        let dense = (total - attn - moe - engram - head).max(0.0);
135        eprintln!(
136            "[dsv41-profile] tokens={} layer_visits={} per_token_ms: attention={:.2} sparse_attend={:.2} attention_other={:.2} moe={:.2} engram={:.2} head={:.2} dense_other={:.2} total={:.2}",
137            tokens as u64,
138            layers,
139            attn / 1e6 / tokens,
140            sparse / 1e6 / tokens,
141            attn_other / 1e6 / tokens,
142            moe / 1e6 / tokens,
143            engram / 1e6 / tokens,
144            head / 1e6 / tokens,
145            dense / 1e6 / tokens,
146            total / 1e6 / tokens,
147        );
148        eprintln!(
149            "[dsv41-profile] moe_calls={} gpu_moe_calls={} cpu_moe_calls={} cold_experts={} gpu_attn_calls={} gpu_attn_fallbacks={}",
150            MOE_CALLS.load(Ordering::Relaxed),
151            GPU_MOE_CALLS.load(Ordering::Relaxed),
152            CPU_MOE_CALLS.load(Ordering::Relaxed),
153            COLD_EXPERTS.load(Ordering::Relaxed),
154            GPU_ATTN_CALLS.load(Ordering::Relaxed),
155            GPU_ATTN_FALLBACKS.load(Ordering::Relaxed),
156        );
157        #[cfg(feature = "gpu")]
158        {
159            let fills = crate::gpu_wgpu::DSV4_FILLS.load(Ordering::Relaxed);
160            let fill_bytes = crate::gpu_wgpu::DSV4_FILL_BYTES.load(Ordering::Relaxed);
161            let fill_ns = crate::gpu_wgpu::DSV4_FILL_NS.load(Ordering::Relaxed);
162            let submits = crate::gpu_wgpu::SUBMITS.load(Ordering::Relaxed);
163            let passes = crate::gpu_wgpu::PASSES.load(Ordering::Relaxed);
164            let upload_bytes = crate::gpu_wgpu::UPLOAD_BYTES.load(Ordering::Relaxed);
165            let upload_ns = crate::gpu_wgpu::UPLOAD_NS.load(Ordering::Relaxed);
166            eprintln!(
167                "[dsv41-profile] gpu_counters fills={} fill_bytes={} submits={} passes={} upload_bytes={} upload_ms={:.2} refill_ms={:.2} resident_bytes={} vram_budget={}",
168                fills,
169                fill_bytes,
170                submits,
171                passes,
172                upload_bytes,
173                upload_ns as f64 / 1e6,
174                fill_ns as f64 / 1e6,
175                crate::gpu_wgpu::resident_bytes(),
176                crate::gpu_wgpu::device_vram_budget(),
177            );
178            eprintln!(
179                "[dsv41-profile] gpu_moe_host_encode_ms={:.2} gpu_moe_wait_ms={:.2} chain_encode_ms={:.2} chain_wait_ms={:.2} chain_layers={} chain_runs={}",
180                crate::gpu_wgpu::MOE_ENC_NS.load(Ordering::Relaxed) as f64 / 1e6,
181                crate::gpu_wgpu::MOE_WAIT_NS.load(Ordering::Relaxed) as f64 / 1e6,
182                crate::gpu_wgpu::CHAIN_ENC_NS.load(Ordering::Relaxed) as f64 / 1e6,
183                crate::gpu_wgpu::CHAIN_WAIT_NS.load(Ordering::Relaxed) as f64 / 1e6,
184                crate::gpu_wgpu::CHAIN_LAYERS.load(Ordering::Relaxed),
185                crate::gpu_wgpu::CHAIN_RUNS.load(Ordering::Relaxed),
186            );
187            eprintln!(
188                "[dsv41-profile] gpu_attn_frame_encode_ms={:.2} gpu_attn_frame_wait_ms={:.2}",
189                crate::gpu_wgpu::ATT_ENC_NS.load(Ordering::Relaxed) as f64 / 1e6,
190                crate::gpu_wgpu::ATT_WAIT_NS.load(Ordering::Relaxed) as f64 / 1e6,
191            );
192        }
193    }
194}
195
196/// Round an f32 through the BF16 format used by the reference model's
197/// activation tensors.  The engine keeps working buffers in f32, so every
198/// boundary where the reference writes a BF16 tensor has to make this
199/// rounding explicit.  The add-and-mask form is round-to-nearest-even and
200/// also handles negative values without a float conversion in the hot loop.
201#[inline]
202fn bf16_roundtrip(value: f32) -> f32 {
203    let bits = value.to_bits();
204    let round = 0x7fff + ((bits >> 16) & 1);
205    f32::from_bits(bits.wrapping_add(round) & 0xffff_0000)
206}
207
208#[inline]
209fn bf16_inplace(values: &mut [f32]) {
210    for value in values {
211        *value = bf16_roundtrip(*value);
212    }
213}
214
215#[inline]
216fn trace_stats(stage: &str, position: usize, layer: Option<usize>, v: &[f32]) {
217    if std::env::var_os("CMF_DSV41_TRACE").is_none() {
218        return;
219    }
220    let mean = v.iter().copied().sum::<f32>() / v.len().max(1) as f32;
221    let sum = v.iter().copied().sum::<f32>();
222    let min = v.iter().copied().fold(f32::INFINITY, f32::min);
223    let max = v.iter().copied().fold(f32::NEG_INFINITY, f32::max);
224    let l2 = v.iter().map(|x| x * x).sum::<f32>().sqrt();
225    let full = std::env::var_os("CMF_DSV41_TRACE_FULL").is_some()
226        && std::env::var("CMF_DSV41_TRACE_POS")
227            .ok()
228            .and_then(|p| p.parse::<usize>().ok())
229            .is_some_and(|p| p == position);
230    let head: Vec<f32> = if full {
231        v.to_vec()
232    } else {
233        v.iter().take(8).copied().collect()
234    };
235    match layer {
236        Some(layer) => eprintln!(
237            "dsv41_trace stage={stage} position={position} layer={layer} mean={mean:.9e} sum={sum:.9e} min={min:.9e} max={max:.9e} l2={l2:.9e} head={head:?}"
238        ),
239        None => eprintln!(
240            "dsv41_trace stage={stage} position={position} mean={mean:.9e} sum={sum:.9e} min={min:.9e} max={max:.9e} l2={l2:.9e} head={head:?}"
241        ),
242    }
243}
244
245#[inline]
246fn trace_indices(position: usize, layer: usize, values: &[usize]) {
247    if std::env::var_os("CMF_DSV41_TRACE").is_none() {
248        return;
249    }
250    eprintln!("dsv41_trace stage=idxs position={position} layer={layer} values={values:?}");
251}
252
253#[inline]
254fn trace_index_scores(
255    position: usize,
256    layer: usize,
257    scores: &[f32],
258    picked: &[usize],
259    candidate: Option<&[bool]>,
260    window_len: usize,
261    compressed_len: usize,
262    pending_len: usize,
263) {
264    if std::env::var_os("CMF_DSV41_TRACE_INDEX").is_none() {
265        return;
266    }
267    if let Some(filter) = std::env::var("CMF_DSV41_TRACE_POS")
268        .ok()
269        .and_then(|p| p.parse::<usize>().ok())
270    {
271        if filter != position {
272            return;
273        }
274    }
275    let mut order: Vec<usize> = (0..scores.len()).collect();
276    order.sort_by(|&a, &b| scores[b].total_cmp(&scores[a]).then_with(|| a.cmp(&b)));
277    let finite_ties: Vec<(usize, usize)> = (0..scores.len())
278        .flat_map(|a| ((a + 1)..scores.len()).map(move |b| (a, b)))
279        .filter(|&(a, b)| scores[a].is_finite() && scores[a].to_bits() == scores[b].to_bits())
280        .collect();
281    let top: Vec<(usize, f32)> = order.iter().take(12).map(|&i| (i, scores[i])).collect();
282    let candidate_ids: Vec<usize> = candidate
283        .map(|mask| {
284            mask.iter()
285                .enumerate()
286                .filter_map(|(i, &keep)| keep.then_some(i))
287                .collect()
288        })
289        .unwrap_or_default();
290    eprintln!(
291        "dsv41_trace stage=index_scores position={position} layer={layer} window_len={window_len} compressed_len={compressed_len} pending_len={pending_len} picked={picked:?} top={top:?} ties={finite_ties:?} candidate_ids={candidate_ids:?}"
292    );
293}
294
295/// Emit one compact routing snapshot when diagnosing a discrete top-k
296/// divergence against the reference.  This is deliberately opt-in and
297/// position-filtered so it cannot add work to normal generation.  The full
298/// shifted scores and the unbiased scores are included: ties in the biased
299/// choice can therefore be distinguished from a genuinely different score
300/// ordering.
301#[inline]
302fn trace_moe(
303    position: usize,
304    layer: usize,
305    image: bool,
306    logits: &[f32],
307    scores: &[f32],
308    bias: &[f32],
309    picks: &[usize],
310) {
311    if std::env::var_os("CMF_DSV41_TRACE_MOE").is_none() {
312        return;
313    }
314    if let Some(filter) = std::env::var("CMF_DSV41_TRACE_POS")
315        .ok()
316        .and_then(|p| p.parse::<usize>().ok())
317    {
318        if filter != position {
319            return;
320        }
321    }
322    let shifted: Vec<f32> = scores
323        .iter()
324        .enumerate()
325        .map(|(i, &s)| s + bias.get(i).copied().unwrap_or(0.0))
326        .collect();
327    let mut order: Vec<usize> = (0..shifted.len()).collect();
328    order.sort_by(|&a, &b| {
329        shifted[b]
330            .partial_cmp(&shifted[a])
331            .unwrap_or(std::cmp::Ordering::Equal)
332            .then_with(|| a.cmp(&b))
333    });
334    let ties: Vec<(usize, usize)> = (0..shifted.len())
335        .flat_map(|a| ((a + 1)..shifted.len()).map(move |b| (a, b)))
336        .filter(|&(a, b)| shifted[a].to_bits() == shifted[b].to_bits())
337        .collect();
338    eprintln!(
339        "dsv41_trace stage=moe position={position} layer={layer} image={image} logits={logits:?} scores={scores:?} bias={bias:?} shifted={shifted:?} order={order:?} picks={picks:?} ties={ties:?}"
340    );
341}
342
343#[inline]
344fn sigmoid(x: f32) -> f32 {
345    1.0 / (1.0 + (-x).exp())
346}
347
348/// Decode an OCP E4M3FN value.  The Engram payload stores these bytes as
349/// U8, rather than as a generic CMF quantisation codec.
350#[inline]
351pub fn fp8_e4m3(byte: u8) -> f32 {
352    if (byte & 0x7f) == FP8_NAN {
353        return f32::NAN;
354    }
355    let sign = if byte & 0x80 != 0 { -1.0 } else { 1.0 };
356    let exp = (byte >> 3) & 0x0f;
357    let mant = byte & 0x07;
358    if exp == 0 {
359        sign * (mant as f32) * 2.0f32.powi(-9)
360    } else {
361        sign * (1.0 + mant as f32 / 8.0) * 2.0f32.powi(exp as i32 - 7)
362    }
363}
364
365/// Decode an E8M0 scale.  E8M0 has no sign or mantissa: the byte is a biased
366/// exponent, with 127 representing one.  Zero is a finite subnormal scale
367/// in the upstream implementation and is retained as 2^-127.
368#[inline]
369pub fn e8m0_scale(byte: u8) -> f32 {
370    if byte == E8M0_NAN {
371        f32::NAN
372    } else {
373        2.0f32.powi(byte as i32 - 127)
374    }
375}
376
377/// Round a finite value to the representable E4M3FN value and decode it
378/// again.  Activation quantisation in the reference is fused quantise plus
379/// dequantise, so keeping this small scalar path here gives the CPU fallback
380/// the same values that the GPU kernel writes back in-place.
381#[inline]
382fn round_ties_even(x: f32) -> i32 {
383    let lo = x.floor() as i32;
384    let frac = x - lo as f32;
385    if frac > 0.5 || (frac == 0.5 && (lo & 1) != 0) {
386        lo + 1
387    } else {
388        lo
389    }
390}
391
392fn e4m3_round(x: f32) -> f32 {
393    if !x.is_finite() {
394        return if x.is_sign_negative() { -448.0 } else { 448.0 };
395    }
396    let sign = if x.is_sign_negative() { 0x80 } else { 0 };
397    let ax = x.abs().min(448.0);
398    if ax == 0.0 {
399        return 0.0;
400    }
401    let byte = if ax < 2.0f32.powi(-6) {
402        // Subnormals use a 2^-9 quantum.  A rounded mantissa of eight
403        // carries into the smallest normal (0x08); clamping it to seven
404        // loses that carry and creates a discontinuity at 2^-6.
405        let mant = round_ties_even(ax * 2.0f32.powi(9));
406        if mant >= 8 {
407            sign | 0x08
408        } else {
409            sign | mant.clamp(0, 7) as u8
410        }
411    } else {
412        // Read the unbiased binary exponent instead of relying on log2 at
413        // exact powers of two.  The source cast is round-to-nearest-even;
414        // f32::round is ties-away and changes half-way E4M3 codes.
415        let exp = ((ax.to_bits() >> 23) & 0xff) as i32 - 127;
416        let mut mant = round_ties_even((ax / 2.0f32.powi(exp) - 1.0) * 8.0);
417        let mut exp = exp;
418        if mant >= 8 {
419            exp += 1;
420            mant = 0;
421        }
422        let exp_field = exp + 7;
423        if exp_field > 15 {
424            // 0x7f/0xff is NaN in E4M3FN; 0x7e/0xfe is the largest finite
425            // code and decodes to 448.  Values entering this helper are
426            // clamped above, but retain saturation here for callers that
427            // use the scalar rounder directly.
428            sign | (15 << 3) | 6
429        } else {
430            // E4M3FN reserves mantissa seven at exponent 15 for NaN.  The
431            // input clamp means a normal value reaches at most mantissa six;
432            // keep the clamp as a defensive guard for direct callers.
433            let mant = if exp_field == 15 { mant.min(6) } else { mant };
434            sign | ((exp_field.clamp(1, 15) as u8) << 3) | (mant.clamp(0, 7) as u8)
435        }
436    };
437    fp8_e4m3(byte)
438}
439
440fn round_e8m0_scale(raw: f32) -> f32 {
441    if !raw.is_finite() || raw <= 0.0 {
442        return 2.0f32.powi(-127);
443    }
444    // The upstream kernel uses fast_log2_ceil(amax / max), not nearest
445    // rounding.  Ceil keeps every value representable after the scale is
446    // quantised and is part of the MXFP contract.
447    let exponent = raw.log2().ceil().clamp(-127.0, 127.0);
448    2.0f32.powf(exponent)
449}
450
451/// A row-addressable E4M3/E8M0 tensor pair.  Only the rows selected by the
452/// hash state are decoded, so the resident memory is O(hash columns), not
453/// O(table rows).
454#[derive(Clone)]
455pub struct RawFp8Rows {
456    model: Arc<CmfModel>,
457    weight_idx: usize,
458    scale_idx: usize,
459    pub rows: usize,
460    pub cols: usize,
461}
462
463impl RawFp8Rows {
464    pub fn from_model(model: &Arc<CmfModel>, weight: &str, scale: &str) -> Result<Self, String> {
465        let wi = model
466            .tensor_index(weight)
467            .ok_or_else(|| format!("Engram tensor '{weight}' not found"))?;
468        let si = model
469            .tensor_index(scale)
470            .ok_or_else(|| format!("Engram tensor '{scale}' not found"))?;
471        let we = &model.tensors[wi];
472        let se = &model.tensors[si];
473        if we.dtype != TensorDtype::U8 || se.dtype != TensorDtype::U8 {
474            return Err(format!(
475                "Engram rows must be U8, got {} and {}",
476                we.dtype.name(),
477                se.dtype.name()
478            ));
479        }
480        if we.shape.len() != 2 || se.shape.len() != 2 {
481            return Err("Engram rows must be two-dimensional".into());
482        }
483        let (rows, cols) = (we.shape[0], we.shape[1]);
484        if cols == 0 || cols % 32 != 0 {
485            return Err(format!(
486                "Engram row width {cols} is not a positive multiple of 32"
487            ));
488        }
489        if se.shape != vec![rows, cols / 32] {
490            return Err(format!(
491                "Engram scale shape {:?}, expected [{rows}, {}]",
492                se.shape,
493                cols / 32
494            ));
495        }
496        if we.nbytes as usize != rows * cols || se.nbytes as usize != rows * cols / 32 {
497            return Err(format!(
498                "Engram U8 payload size mismatch for {weight}/{scale}"
499            ));
500        }
501        Ok(Self {
502            model: model.clone(),
503            weight_idx: wi,
504            scale_idx: si,
505            rows,
506            cols,
507        })
508    }
509
510    /// Decode one row into `dst`.  Invalid NaN payloads are treated as zero
511    /// at the edge of the runtime: an accidental poisoned row cannot turn a
512    /// token's whole hidden state into NaNs, while the converter still keeps
513    /// the raw bytes and the loader reports the shape/type contract.
514    pub fn row_into(&self, row: usize, dst: &mut [f32]) {
515        assert!(row < self.rows);
516        assert_eq!(dst.len(), self.cols);
517        let wb = self.model.entry_bytes(&self.model.tensors[self.weight_idx]);
518        let sb = self.model.entry_bytes(&self.model.tensors[self.scale_idx]);
519        for g in 0..self.cols / 32 {
520            let scale = e8m0_scale(sb[row * (self.cols / 32) + g]);
521            let scale = if scale.is_finite() { scale } else { 0.0 };
522            let src = &wb[row * self.cols + g * 32..row * self.cols + (g + 1) * 32];
523            for (out, &byte) in dst[g * 32..(g + 1) * 32].iter_mut().zip(src) {
524                let v = fp8_e4m3(byte);
525                *out = if v.is_finite() { v * scale } else { 0.0 };
526            }
527        }
528    }
529}
530
531/// The exact source layout of the V4.1 n-gram tables.  The two configured
532/// Engram layers have disjoint prime ranges, even though both tables use the
533/// same nominal 16M vocabulary.
534#[derive(Clone)]
535pub struct EngramHash {
536    pub layer_ids: Vec<usize>,
537    pub max_ngram: usize,
538    pub n_heads: usize,
539    pub compressed_vocab: usize,
540    pub pad_id: i64,
541    pub primes: Vec<Vec<Vec<u64>>>,
542    pub offsets: Vec<Vec<u64>>,
543    pub token_map: Vec<u32>,
544    pub multipliers: Vec<[u64; 4]>,
545    history: Vec<i64>,
546}
547
548impl EngramHash {
549    pub fn new(
550        layer_ids: Vec<usize>,
551        max_ngram: usize,
552        n_heads: usize,
553        table_vocab: usize,
554        compressed_vocab: usize,
555        pad_token: usize,
556        token_map: Vec<u32>,
557    ) -> Result<Self, String> {
558        if max_ngram < 2 || n_heads == 0 || layer_ids.is_empty() {
559            return Err("invalid Engram hash geometry".into());
560        }
561        let mapped_vocab = token_map
562            .iter()
563            .copied()
564            .max()
565            .map(|m| m as usize + 1)
566            .unwrap_or(0);
567        if mapped_vocab != compressed_vocab {
568            return Err(format!(
569                "Engram compressed vocabulary mismatch: tokenizer={mapped_vocab}, config={compressed_vocab}"
570            ));
571        }
572        let mut seen = Vec::<u64>::new();
573        let mut primes = Vec::with_capacity(layer_ids.len());
574        let mut offsets = Vec::with_capacity(layer_ids.len());
575        for _ in &layer_ids {
576            let mut per_layer = Vec::with_capacity(max_ngram - 1);
577            let mut per_offsets = Vec::with_capacity(max_ngram - 1);
578            let mut offset = 0u64;
579            for _ in 0..max_ngram - 1 {
580                let mut per_n = Vec::with_capacity(n_heads);
581                per_offsets.push(offset);
582                let mut current = table_vocab.saturating_sub(1) as u64;
583                for _ in 0..n_heads {
584                    loop {
585                        current = current.saturating_add(1);
586                        if is_prime(current) && !seen.contains(&current) {
587                            break;
588                        }
589                    }
590                    seen.push(current);
591                    per_n.push(current);
592                    offset = offset.saturating_add(current);
593                }
594                per_layer.push(per_n);
595            }
596            primes.push(per_layer);
597            offsets.push(per_offsets);
598        }
599        let pad_id = token_map.get(pad_token).copied().unwrap_or(2) as i64;
600        let multipliers = layer_ids
601            .iter()
602            .map(|&id| hash_multipliers(id, max_ngram, compressed_vocab))
603            .collect();
604        Ok(Self {
605            layer_ids,
606            max_ngram,
607            n_heads,
608            compressed_vocab,
609            pad_id,
610            primes,
611            offsets,
612            token_map,
613            multipliers,
614            history: Vec::new(),
615        })
616    }
617
618    pub fn reset(&mut self) {
619        self.history.clear();
620    }
621
622    /// Append one token and return one flattened hash column vector per
623    /// configured layer. `participates=false` is used for image span tokens;
624    /// dead tokens break every n-gram lookback just like the reference.
625    pub fn push(&mut self, token: u32, participates: bool) -> Vec<Vec<usize>> {
626        let mapped = self.token_map.get(token as usize).copied().unwrap_or(0) as i64;
627        self.history.push(if participates { mapped } else { DEAD });
628        let mut all = Vec::with_capacity(self.layer_ids.len());
629        for (li, _) in self.layer_ids.iter().enumerate() {
630            let mut cols = Vec::with_capacity((self.max_ngram - 1) * self.n_heads);
631            for n in 1..self.max_ngram {
632                let mut rolling = 0u64;
633                let mut blocked = false;
634                // `products[..., 0]` is the current token; each additional
635                // lookback is XORed once.  The same rolling hash is then
636                // placed in one disjoint prime range per head.
637                for shift in 0..=n {
638                    let (source, dead) = self.history_value(shift);
639                    // The reference carries a blocked bit across the
640                    // lookback loop.  Once a dead/image token or sequence
641                    // boundary is encountered, every older term is the pad
642                    // id too; replacing only the dead term would create a
643                    // cross-image n-gram.
644                    blocked |= dead;
645                    let source = if blocked { self.pad_id } else { source };
646                    rolling ^= (source as u64).wrapping_mul(self.multipliers[li][shift.min(3)]);
647                }
648                for h in 0..self.n_heads {
649                    let p = self.primes[li][n - 1][h];
650                    let off =
651                        self.offsets[li][n - 1] + self.primes[li][n - 1][..h].iter().sum::<u64>();
652                    cols.push((rolling % p + off) as usize);
653                }
654            }
655            all.push(cols);
656        }
657        all
658    }
659
660    fn history_value(&self, shift: usize) -> (i64, bool) {
661        if shift >= self.history.len() {
662            (self.pad_id, true)
663        } else {
664            let v = self.history[self.history.len() - 1 - shift];
665            if v == DEAD {
666                (self.pad_id, true)
667            } else {
668                (v, false)
669            }
670        }
671    }
672}
673
674fn is_prime(v: u64) -> bool {
675    if v < 2 {
676        return false;
677    }
678    if v % 2 == 0 {
679        return v == 2;
680    }
681    let mut d = 3u64;
682    while d <= v / d {
683        if v % d == 0 {
684            return false;
685        }
686        d += 2;
687    }
688    true
689}
690
691/// Multipliers emitted by NumPy's PCG64 for the release's two Engram
692/// layers.  These are constants in the upstream model contract.  For a
693/// custom layer id, a SplitMix fallback keeps the runtime deterministic;
694/// converted V4.1 files always use ids 1 and 14.
695fn hash_multipliers(layer: usize, n: usize, vocab: usize) -> [u64; 4] {
696    let known = match layer {
697        1 => [
698            76632096046245,
699            4839876093313,
700            35959672319349,
701            73987337458391,
702        ],
703        14 => [
704            67716810739261,
705            51510806800915,
706            30921347202721,
707            82619226485591,
708        ],
709        _ => [0; 4],
710    };
711    if n <= 4 && known[0] != 0 && vocab == 99092 {
712        return known;
713    }
714    let bound = ((i64::MAX as u128 / vocab.max(1) as u128) / 2) as u64;
715    let mut x = 10007u64
716        .wrapping_mul(layer as u64)
717        .wrapping_add(0x9e3779b97f4a7c15);
718    let mut out = [0u64; 4];
719    for i in 0..n.min(4) {
720        x = splitmix64(x);
721        out[i] = (x % bound.max(1)) * 2 + 1;
722    }
723    out
724}
725
726#[inline]
727fn splitmix64(mut x: u64) -> u64 {
728    x = x.wrapping_add(0x9e3779b97f4a7c15);
729    let mut z = x;
730    z = (z ^ (z >> 30)).wrapping_mul(0xbf58476d1ce4e5b9);
731    z = (z ^ (z >> 27)).wrapping_mul(0x94d049bb133111eb);
732    z ^ (z >> 31)
733}
734
735/// V4.1 architecture geometry. Arrays are deliberately kept in this cfg,
736/// because the source config is part of the converted model metadata and
737/// there is no safe way to infer sharing from a missing tensor alone.
738#[derive(Debug, Clone)]
739pub struct Dsv41Cfg {
740    pub dim: usize,
741    pub n_heads: usize,
742    pub head_dim: usize,
743    pub rope_head_dim: usize,
744    pub q_lora_rank: usize,
745    pub o_lora_rank: usize,
746    pub o_groups: usize,
747    pub hc_mult: usize,
748    pub hc_sinkhorn_iters: usize,
749    pub hc_eps: f32,
750    pub norm_eps: f32,
751    pub n_routed_experts: usize,
752    pub top_k: usize,
753    pub moe_inter: usize,
754    /// Router temperature and whether selected scores are renormalized.
755    /// V4.1 ships sqrt-softplus, temperature 1 and normalized top-k, but
756    /// keeping these source fields here makes small fixtures and future
757    /// checkpoints follow their preserved config instead of silently using
758    /// release defaults.
759    pub gate_temp: f32,
760    pub norm_topk_prob: bool,
761    pub route_scale: f32,
762    pub swiglu_limit: f32,
763    pub window: usize,
764    pub rope_theta: f32,
765    pub compress_rope_theta: f32,
766    pub rope_factor: f32,
767    pub original_seq_len: usize,
768    pub beta_fast: f32,
769    pub beta_slow: f32,
770    pub index_heads: usize,
771    pub index_head_dim: usize,
772    pub index_topk: usize,
773    pub candidate_source: usize,
774    pub candidate_topk_blocks: usize,
775    pub candidate_block_size: usize,
776    pub kv_sources: Vec<usize>,
777    pub index_sources: Vec<usize>,
778    pub compress_ratios: Vec<usize>,
779    pub engram_layers: Vec<usize>,
780    pub engram_vocab: usize,
781    pub engram_embeddings: Vec<usize>,
782    pub engram_max_ngram: usize,
783    pub engram_heads: usize,
784    pub engram_head_dim: usize,
785    pub engram_compressed_vocab: usize,
786    pub engram_pad_id: usize,
787    pub vocab: usize,
788}
789
790impl Dsv41Cfg {
791    pub fn ratio(&self, li: usize) -> usize {
792        self.compress_ratios.get(li).copied().unwrap_or(0)
793    }
794
795    pub fn kv_source(&self, li: usize) -> Option<usize> {
796        self.kv_sources.iter().copied().filter(|&s| s <= li).max()
797    }
798
799    pub fn index_source(&self, li: usize) -> Option<usize> {
800        self.index_sources
801            .iter()
802            .copied()
803            .filter(|&s| s <= li)
804            .max()
805    }
806}
807
808pub struct Dsv41Compressor {
809    pub wkv: QTensor,
810    pub wgate: Option<QTensor>,
811    pub norm: Vec<f32>,
812    pub ratio: usize,
813}
814
815pub struct Dsv41Indexer {
816    pub wq_b: QTensor,
817    pub weights_proj: QTensor,
818    pub wk: Option<QTensor>,
819    pub k_norm: Option<Vec<f32>>,
820}
821
822pub struct Dsv41Expert {
823    pub w1: QTensor,
824    pub w2: QTensor,
825    pub w3: QTensor,
826}
827
828pub struct Dsv41Engram {
829    pub embed: RawFp8Rows,
830    pub wkv: QTensor,
831    pub q_weight: Vec<f32>,
832    pub k_weight: Vec<f32>,
833}
834
835pub struct Dsv41Layer {
836    pub attn_norm: Vec<f32>,
837    pub ffn_norm: Vec<f32>,
838    pub wq_a: QTensor,
839    pub q_norm: Vec<f32>,
840    pub wq_b: QTensor,
841    pub wkv: QTensor,
842    pub kv_norm: Vec<f32>,
843    pub wo_a: QTensor,
844    pub wo_b: QTensor,
845    pub attn_sink: Vec<f32>,
846    pub compressor: Option<Dsv41Compressor>,
847    pub indexer: Option<Dsv41Indexer>,
848    pub hc_attn_fn: Vec<f32>,
849    pub hc_attn_base: Vec<f32>,
850    pub hc_attn_scale: [f32; 3],
851    pub hc_ffn_fn: Vec<f32>,
852    pub hc_ffn_base: Vec<f32>,
853    pub hc_ffn_scale: [f32; 3],
854    pub gate: QTensor,
855    pub gate_bias: Vec<f32>,
856    pub gate_bias_vl: Option<Vec<f32>>,
857    pub experts: Vec<Dsv41Expert>,
858    pub shared: Dsv41Expert,
859    pub engram: Option<Dsv41Engram>,
860    /// Directory triples used by the shared DSV4 dynamic MoE bank.  An
861    /// empty table means a synthetic/F32 fixture and deliberately falls
862    /// back to the exact host expert loop.
863    gpu_expert_ids: Vec<(usize, usize, usize)>,
864    gpu_shared_ids: Option<(usize, usize, usize)>,
865}
866
867pub struct Dsv41Globals {
868    pub embed: QTensor,
869    pub norm: Vec<f32>,
870    pub head: QTensor,
871    pub inv_freq_compress: Vec<f32>,
872    pub inv_freq_window: Vec<f32>,
873}
874
875/// Sequence state. `compressed` and `index_k` are keyed by source index,
876/// rather than by every consumer layer; this is the CSA2 memory bound.
877pub struct Dsv41State {
878    pub pos: usize,
879    pub window: Vec<Vec<f32>>,
880    /// Compact global main KV and indexer-K streams keyed by CSA2 source.
881    /// Rows are decoded only into bounded caller-owned scratch.
882    pub packed: packed_kv::Dsv41PackedKvCache,
883    pub pending_kv: Vec<Vec<f32>>,
884    pub pending_score: Vec<Vec<f32>>,
885    pub topk: Vec<usize>,
886    /// Whether an index-source layer has published the shared sparse
887    /// positions for the current sequence. An empty published list means
888    /// "attend no compressed rows"; it must not be confused with the
889    /// pre-indexer state, where the reference has no list yet and consumers
890    /// use every visible row.
891    pub topk_ready: bool,
892    pub candidates: Vec<bool>,
893    pub hash: Option<EngramHash>,
894    /// Distinct key for the persistent device attention cache. DSV4 and
895    /// DSV4.1 share the wgpu cache implementation but use different logical
896    /// row contracts, so their sequence ids must never collide.
897    #[cfg(feature = "gpu")]
898    gpu_kv_id: u64,
899    /// Model-wide segmented expert residency survives sequence resets, as
900    /// in the established Qwen/Dsv4 dynamic MoE path.  The cache remains
901    /// bounded by the GPU policy and cold experts complete on the host.
902    #[cfg(feature = "gpu")]
903    gpu_pool: Option<crate::qwen4_exp::QwenGpuPool>,
904}
905
906impl Dsv41State {
907    pub fn new(cfg: &Dsv41Cfg, hash: Option<EngramHash>) -> Self {
908        #[cfg(feature = "gpu")]
909        let gpu_kv_id = {
910            use std::sync::atomic::{AtomicU64, Ordering};
911            static NEXT: AtomicU64 = AtomicU64::new(1);
912            (0xD541u64 << 48) | NEXT.fetch_add(1, Ordering::Relaxed)
913        };
914        Self {
915            pos: 0,
916            window: vec![Vec::new(); cfg.compress_ratios.len()],
917            packed: packed_kv::Dsv41PackedKvCache::new(
918                cfg.kv_sources.len(),
919                cfg.head_dim,
920                cfg.index_head_dim,
921            )
922            .expect("valid V4.1 packed KV geometry"),
923            pending_kv: vec![Vec::new(); cfg.kv_sources.len()],
924            pending_score: vec![Vec::new(); cfg.kv_sources.len()],
925            topk: Vec::new(),
926            topk_ready: false,
927            candidates: Vec::new(),
928            hash,
929            #[cfg(feature = "gpu")]
930            gpu_kv_id,
931            #[cfg(feature = "gpu")]
932            gpu_pool: None,
933        }
934    }
935
936    pub fn clear(&mut self) {
937        #[cfg(feature = "gpu")]
938        crate::gpu_wgpu::dsv4_cache_clear(self.gpu_kv_id);
939        self.pos = 0;
940        for v in &mut self.window {
941            v.clear();
942        }
943        self.packed.clear();
944        for v in &mut self.pending_kv {
945            v.clear();
946        }
947        for v in &mut self.pending_score {
948            v.clear();
949        }
950        self.topk.clear();
951        self.topk_ready = false;
952        self.candidates.clear();
953        if let Some(h) = &mut self.hash {
954            h.reset();
955        }
956    }
957}
958
959fn source_slot(sources: &[usize], li: usize) -> Option<usize> {
960    sources
961        .iter()
962        .enumerate()
963        .filter(|&(_, &s)| s <= li)
964        .map(|(i, _)| i)
965        .max()
966}
967
968fn rms(v: &mut [f32], w: &[f32], eps: f32) {
969    // RMSNorm receives a BF16 tensor in the reference and returns another
970    // BF16 tensor.  Rounding the input matters for folded residuals, while
971    // rounding the result matters for every subsequent projection.
972    bf16_inplace(v);
973    crate::dsv4::rms_weighted(v, w, eps);
974    bf16_inplace(v);
975}
976
977fn matvec(t: &QTensor, x: &[f32], out: &mut [f32], pool: Option<&Pool>) {
978    t.matvec(x, out, pool);
979}
980
981#[inline]
982fn matvec_bf16(t: &QTensor, x: &[f32], out: &mut [f32], pool: Option<&Pool>) {
983    t.matvec(x, out, pool);
984    bf16_inplace(out);
985}
986
987/// Opt-in V4.1 query fusion. The default remains the host-materialized query
988/// path until the parent promotes the matched GPU/CPU profile. With this flag
989/// the DSV4 frame consumes `qr` directly and performs wq_b, BF16 materializing,
990/// and forward RoPE in its existing single encoder/readback.
991#[inline]
992fn v41_fused_q_enabled() -> bool {
993    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
994    *ON.get_or_init(|| std::env::var("CMF_DSV41_FUSED_Q").is_ok_and(|value| value == "1"))
995}
996
997/// The V4.1 tail uses the proven DSV4 frame only when the selected backend is
998/// live. The default follows the ordinary GPU selection; an explicit zero is
999/// useful for the CPU reference and for the paired numerical proof.
1000#[cfg(feature = "gpu")]
1001fn gpu_attention_tail_enabled() -> bool {
1002    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
1003    *ON.get_or_init(|| {
1004        let requested = std::env::var("CMF_DSV41_GPU_ATTN")
1005            .map(|v| v != "0")
1006            .unwrap_or(true);
1007        let live = requested && crate::gpu::enabled_here() && crate::gpu_wgpu::adapter_up();
1008        if requested && !live && std::env::var("CMF_DSV41_GPU_ATTN").is_ok() {
1009            tracing::debug!("V4.1 GPU attention tail unavailable; retaining the CPU path");
1010        }
1011        live
1012    })
1013}
1014
1015/// One-shot stage capture for the V4.1 adapter repair. The frame reads the
1016/// same tap names as the generic DSV4 proof, but the adapter falls through to
1017/// the CPU path after logging the GPU value so one call carries both sides.
1018fn gpu_tail_tap() -> Option<&'static str> {
1019    static TAP: std::sync::OnceLock<Option<String>> = std::sync::OnceLock::new();
1020    TAP.get_or_init(|| {
1021        let tap = std::env::var("CMF_DSV41_TAIL_TAP").ok()?;
1022        matches!(tap.as_str(), "q" | "attn" | "mid" | "final").then_some(tap)
1023    })
1024    .as_deref()
1025}
1026
1027fn f_any(model: &CmfModel, names: &[String]) -> Result<Vec<f32>, String> {
1028    for n in names {
1029        if model.tensor(n).is_some() {
1030            return crate::loader::load_f32(model, n, &crate::loader::Overlay::None);
1031        }
1032    }
1033    Err(format!("missing tensor (tried {})", names.join(", ")))
1034}
1035
1036fn q_any(model: &Arc<CmfModel>, names: &[String]) -> Result<QTensor, String> {
1037    for n in names {
1038        if model.tensor(n).is_some() {
1039            return QTensor::from_model(model, n);
1040        }
1041    }
1042    Err(format!("missing tensor (tried {})", names.join(", ")))
1043}
1044
1045fn scale3(model: &CmfModel, names: &[String]) -> Result<[f32; 3], String> {
1046    let v = f_any(model, names)?;
1047    if v.len() < 3 {
1048        return Err(format!(
1049            "scale tensor (tried {}) expected three values",
1050            names.join(", ")
1051        ));
1052    }
1053    Ok([v[0], v[1], v[2]])
1054}
1055
1056fn load_expert(model: &Arc<CmfModel>, p: &str, e: usize) -> Result<Dsv41Expert, String> {
1057    let a = format!("{p}.experts.{e}");
1058    let w1 = q_any(
1059        model,
1060        &[format!("{a}.gate_proj.weight"), format!("{a}.w1.weight")],
1061    )?;
1062    let w2 = q_any(
1063        model,
1064        &[format!("{a}.down_proj.weight"), format!("{a}.w2.weight")],
1065    )?;
1066    let w3 = q_any(
1067        model,
1068        &[format!("{a}.up_proj.weight"), format!("{a}.w3.weight")],
1069    )?;
1070    Ok(Dsv41Expert { w1, w2, w3 })
1071}
1072
1073fn load_shared(model: &Arc<CmfModel>, p: &str) -> Result<Dsv41Expert, String> {
1074    let mut names = Vec::new();
1075    for sh in ["shared_expert", "shared_experts"] {
1076        names.push(format!("{p}.{sh}.gate_proj.weight"));
1077        names.push(format!("{p}.{sh}.w1.weight"));
1078    }
1079    let w1 = q_any(model, &names)?;
1080    let mut names2 = Vec::new();
1081    for sh in ["shared_expert", "shared_experts"] {
1082        names2.push(format!("{p}.{sh}.down_proj.weight"));
1083        names2.push(format!("{p}.{sh}.w2.weight"));
1084    }
1085    let w2 = q_any(model, &names2)?;
1086    let mut names3 = Vec::new();
1087    for sh in ["shared_expert", "shared_experts"] {
1088        names3.push(format!("{p}.{sh}.up_proj.weight"));
1089        names3.push(format!("{p}.{sh}.w3.weight"));
1090    }
1091    let w3 = q_any(model, &names3)?;
1092    Ok(Dsv41Expert { w1, w2, w3 })
1093}
1094
1095/// Load one V4.1 layer.  The loader accepts both the canonical names emitted
1096/// by the V4.1 converter and the upstream `ffn.*` spelling, which keeps toy
1097/// fixtures useful while the large source is converted in a separate job.
1098pub fn load_layer(model: &Arc<CmfModel>, cfg: &Dsv41Cfg, li: usize) -> Result<Dsv41Layer, String> {
1099    let p = format!("model.layers.{li}");
1100    let a = format!("{p}.self_attn");
1101    let q = |tail: &str| q_any(model, &[format!("{a}.{tail}"), format!("{p}.attn.{tail}")]);
1102    let attn_norm = f_any(
1103        model,
1104        &[
1105            format!("{p}.input_layernorm.weight"),
1106            format!("{p}.attn_norm.weight"),
1107        ],
1108    )?;
1109    let ffn_norm = f_any(
1110        model,
1111        &[
1112            format!("{p}.post_attention_layernorm.weight"),
1113            format!("{p}.ffn_norm.weight"),
1114        ],
1115    )?;
1116    let wq_a = q("wq_a.weight")?;
1117    let q_norm = f_any(
1118        model,
1119        &[
1120            format!("{a}.q_norm.weight"),
1121            format!("{p}.attn.q_norm.weight"),
1122        ],
1123    )?;
1124    let wq_b = q("wq_b.weight")?;
1125    let wkv = q("wkv.weight")?;
1126    let kv_norm = f_any(model, &[format!("{a}.kv_norm.weight")])?;
1127    let wo_a = q("wo_a.weight")?;
1128    let wo_b = q("wo_b.weight")?;
1129    let attn_sink = f_any(
1130        model,
1131        &[format!("{a}.attn_sink"), format!("{a}.attn_sink.weight")],
1132    )?;
1133
1134    let ratio = cfg.ratio(li);
1135    let compressor = if cfg.kv_sources.contains(&li) {
1136        let cwkv = q_any(
1137            model,
1138            &[
1139                format!("{a}.compressor.wkv.weight"),
1140                format!("{p}.attn.compressor.wkv.weight"),
1141            ],
1142        )?;
1143        let wgate = if ratio > 1 {
1144            Some(q_any(
1145                model,
1146                &[
1147                    format!("{a}.compressor.wgate.weight"),
1148                    format!("{p}.attn.compressor.wgate.weight"),
1149                ],
1150            )?)
1151        } else {
1152            None
1153        };
1154        let norm = f_any(model, &[format!("{a}.compressor.norm.weight")])?;
1155        Some(Dsv41Compressor {
1156            wkv: cwkv,
1157            wgate,
1158            norm,
1159            ratio,
1160        })
1161    } else {
1162        None
1163    };
1164
1165    let indexer = if cfg.index_sources.contains(&li) {
1166        let wq_bi = q_any(
1167            model,
1168            &[
1169                format!("{a}.indexer.wq_b.weight"),
1170                format!("{p}.attn.indexer.wq_b.weight"),
1171            ],
1172        )?;
1173        let weights_proj = q_any(
1174            model,
1175            &[
1176                format!("{a}.indexer.weights_proj.weight"),
1177                format!("{p}.attn.indexer.weights_proj.weight"),
1178            ],
1179        )?;
1180        let owns_k = cfg.kv_sources.contains(&li);
1181        let wk = if owns_k {
1182            Some(q_any(
1183                model,
1184                &[
1185                    format!("{a}.indexer.wk.weight"),
1186                    format!("{p}.attn.indexer.wk.weight"),
1187                ],
1188            )?)
1189        } else {
1190            None
1191        };
1192        let k_norm = if owns_k {
1193            Some(f_any(
1194                model,
1195                &[
1196                    format!("{a}.indexer.k_norm.weight"),
1197                    format!("{p}.attn.indexer.k_norm.weight"),
1198                ],
1199            )?)
1200        } else {
1201            None
1202        };
1203        Some(Dsv41Indexer {
1204            wq_b: wq_bi,
1205            weights_proj,
1206            wk,
1207            k_norm,
1208        })
1209    } else {
1210        None
1211    };
1212
1213    let hc_attn_fn = f_any(model, &[format!("{p}.hc_attn_fn")])?;
1214    let hc_attn_base = f_any(model, &[format!("{p}.hc_attn_base")])?;
1215    let hc_attn_scale = scale3(
1216        model,
1217        &[format!("{p}.hc_attn_scale"), format!("{p}.hc_attn.scale")],
1218    )?;
1219    let hc_ffn_fn = f_any(model, &[format!("{p}.hc_ffn_fn")])?;
1220    let hc_ffn_base = f_any(model, &[format!("{p}.hc_ffn_base")])?;
1221    let hc_ffn_scale = scale3(
1222        model,
1223        &[format!("{p}.hc_ffn_scale"), format!("{p}.hc_ffn.scale")],
1224    )?;
1225
1226    let gate = q_any(
1227        model,
1228        &[
1229            format!("{p}.mlp.gate.weight"),
1230            format!("{p}.ffn.gate.weight"),
1231        ],
1232    )?;
1233    let gate_bias = f_any(
1234        model,
1235        &[
1236            format!("{p}.mlp.expert_bias"),
1237            format!("{p}.ffn.gate.bias"),
1238            format!("{p}.mlp.gate.bias"),
1239        ],
1240    )?;
1241    let gate_bias_vl = f_any(
1242        model,
1243        &[
1244            format!("{p}.mlp.expert_bias_vl"),
1245            format!("{p}.ffn.gate.bias_vl"),
1246        ],
1247    )
1248    .ok();
1249    let mut experts = Vec::with_capacity(cfg.n_routed_experts);
1250    for e in 0..cfg.n_routed_experts {
1251        experts.push(
1252            load_expert(model, &format!("{p}.mlp"), e)
1253                .or_else(|_| load_expert(model, &format!("{p}.ffn"), e))?,
1254        );
1255    }
1256    let shared = load_shared(model, &format!("{p}.mlp"))
1257        .or_else(|_| load_shared(model, &format!("{p}.ffn")))?;
1258    let gpu_expert_ids = experts
1259        .iter()
1260        .map(|e| Some((e.w1.model_idx()?, e.w3.model_idx()?, e.w2.model_idx()?)))
1261        .collect::<Option<Vec<_>>>()
1262        .unwrap_or_default();
1263    let gpu_shared_ids = match (
1264        shared.w1.model_idx(),
1265        shared.w3.model_idx(),
1266        shared.w2.model_idx(),
1267    ) {
1268        (Some(w1), Some(w3), Some(w2)) => Some((w1, w3, w2)),
1269        _ => None,
1270    };
1271
1272    let engram = if cfg.engram_layers.contains(&li) {
1273        let ew = format!("{p}.engram.embed.weight");
1274        let es = format!("{p}.engram.embed.scale");
1275        let embed = RawFp8Rows::from_model(model, &ew, &es)?;
1276        if let Some(index) = cfg.engram_layers.iter().position(|&id| id == li) {
1277            if let Some(&expected_rows) = cfg.engram_embeddings.get(index) {
1278                if embed.rows != expected_rows {
1279                    return Err(format!(
1280                        "Engram layer {li} has {} rows, expected {expected_rows}",
1281                        embed.rows
1282                    ));
1283                }
1284            }
1285        }
1286        let wkv_e = q_any(model, &[format!("{p}.engram.wkv.weight")])?;
1287        let q_weight = f_any(model, &[format!("{p}.engram.q_weight")])?;
1288        let k_weight = f_any(model, &[format!("{p}.engram.k_weight")])?;
1289        Some(Dsv41Engram {
1290            embed,
1291            wkv: wkv_e,
1292            q_weight,
1293            k_weight,
1294        })
1295    } else {
1296        None
1297    };
1298
1299    Ok(Dsv41Layer {
1300        attn_norm,
1301        ffn_norm,
1302        wq_a,
1303        q_norm,
1304        wq_b,
1305        wkv,
1306        kv_norm,
1307        wo_a,
1308        wo_b,
1309        attn_sink,
1310        compressor,
1311        indexer,
1312        hc_attn_fn,
1313        hc_attn_base,
1314        hc_attn_scale,
1315        hc_ffn_fn,
1316        hc_ffn_base,
1317        hc_ffn_scale,
1318        gate,
1319        gate_bias,
1320        gate_bias_vl,
1321        experts,
1322        shared,
1323        engram,
1324        gpu_expert_ids,
1325        gpu_shared_ids,
1326    })
1327}
1328
1329/// Build the stack from canonical V4.1 tensors.  Frequency tables are kept
1330/// separate because compressed latents use the 160k base plus YaRN while the
1331/// raw sliding window uses the 10k base without YaRN.
1332pub fn load(
1333    model: &Arc<CmfModel>,
1334    cfg: &Dsv41Cfg,
1335    n_layers: usize,
1336    token_map: Vec<u32>,
1337) -> Result<(Dsv41Globals, Vec<Dsv41Layer>, Option<EngramHash>), String> {
1338    let q = |name: &str| QTensor::from_model(model, name);
1339    let globals = Dsv41Globals {
1340        embed: q("model.embed_tokens.weight")?,
1341        norm: crate::loader::load_f32(model, "model.norm.weight", &crate::loader::Overlay::None)?,
1342        head: q("lm_head.weight")?,
1343        inv_freq_compress: crate::attention::yarn_inv_freq(
1344            cfg.rope_head_dim,
1345            cfg.compress_rope_theta,
1346            cfg.rope_factor,
1347            cfg.original_seq_len,
1348            cfg.beta_fast,
1349            cfg.beta_slow,
1350        ),
1351        inv_freq_window: crate::attention::rope_inv_freq(cfg.rope_head_dim, cfg.rope_theta),
1352    };
1353    let mut layers = Vec::with_capacity(n_layers);
1354    for li in 0..n_layers {
1355        layers.push(load_layer(model, cfg, li)?);
1356    }
1357    let hash = if !cfg.engram_layers.is_empty() {
1358        Some(EngramHash::new(
1359            cfg.engram_layers.clone(),
1360            cfg.engram_max_ngram,
1361            cfg.engram_heads,
1362            cfg.engram_vocab,
1363            cfg.engram_compressed_vocab,
1364            cfg.engram_pad_id,
1365            token_map,
1366        )?)
1367    } else {
1368        None
1369    };
1370    Ok((globals, layers, hash))
1371}
1372
1373/// Build the compressed token lookup used by Engram.  The loader calls this
1374/// once; the returned vector is tiny compared with the mmap-backed tables.
1375pub fn token_map_from_model(model: &CmfModel, vocab: usize) -> Vec<u32> {
1376    let tokenizer = model
1377        .vocab
1378        .as_ref()
1379        .and_then(|bytes| crate::tokenizer::Tokenizer::from_bytes(bytes).ok());
1380    let mut out = Vec::with_capacity(vocab);
1381    let mut keys = std::collections::HashMap::<String, u32>::new();
1382    for id in 0..vocab {
1383        let text = tokenizer
1384            .as_ref()
1385            .map(|t| t.decode_token_for_hash(id as u32))
1386            .unwrap_or_default();
1387        // A partial UTF-8 byte token decodes to U+FFFD in the Rust
1388        // tokenizer. The reference keys those entries by their raw backend
1389        // token, otherwise unrelated byte fragments collapse together.
1390        let key = if text.contains('\u{fffd}') {
1391            tokenizer
1392                .as_ref()
1393                .map(|t| t.raw_token_for_hash(id as u32))
1394                .unwrap_or(text)
1395        } else {
1396            normalize_hash_token(&text)
1397        };
1398        let next = keys.len() as u32;
1399        out.push(*keys.entry(key).or_insert(next));
1400    }
1401    out
1402}
1403
1404fn normalize_hash_token(text: &str) -> String {
1405    use unicode_normalization::UnicodeNormalization;
1406    // Match tokenizers' NFKC → NFD → StripAccents sequence. NFKD would
1407    // additionally decompose compatibility characters before composition,
1408    // which changes the compressed-vocabulary cardinality.
1409    let nfd: String = text.nfkc().nfd().collect();
1410    let mut out = String::with_capacity(nfd.len());
1411    let mut last_space = false;
1412    for c in nfd.chars() {
1413        if unicode_normalization::char::is_combining_mark(c) {
1414            continue;
1415        }
1416        if c == ' ' || c == '\t' || c == '\r' || c == '\n' {
1417            if !last_space {
1418                out.push(' ');
1419            }
1420            last_space = true;
1421        } else {
1422            for lower in c.to_lowercase() {
1423                out.push(lower);
1424            }
1425            last_space = false;
1426        }
1427    }
1428    let trimmed = out.trim_matches(' ');
1429    if trimmed.is_empty() && out == " " {
1430        " ".to_string()
1431    } else if trimmed.is_empty() {
1432        text.to_string()
1433    } else {
1434        trimmed.to_string()
1435    }
1436}
1437
1438fn apply_engram(
1439    e: &Dsv41Engram,
1440    h: &mut [f32],
1441    hashes: &[usize],
1442    cfg: &Dsv41Cfg,
1443    token_mask: bool,
1444    pool: Option<&Pool>,
1445) {
1446    if !token_mask {
1447        return;
1448    }
1449    let cols = hashes.len() * e.embed.cols;
1450    let mut emb = vec![0.0f32; cols];
1451    for (i, &row) in hashes.iter().enumerate() {
1452        e.embed.row_into(
1453            row.min(e.embed.rows.saturating_sub(1)),
1454            &mut emb[i * e.embed.cols..(i + 1) * e.embed.cols],
1455        );
1456    }
1457    // ParallelEngramEmbedding returns the dequantized lookup as BF16 before
1458    // the projection.  Keep the raw FP8 row representation, but materialise
1459    // this activation boundary explicitly in the f32 work buffer.
1460    bf16_inplace(&mut emb);
1461    let mut kv = vec![0.0f32; cfg.dim * (cfg.hc_mult + 1)];
1462    // Engram's projection uses the model BF16 dtype in the reference.
1463    matvec_bf16(&e.wkv, &emb, &mut kv, pool);
1464    let key = &kv[..cfg.dim * cfg.hc_mult];
1465    let value = &kv[cfg.dim * cfg.hc_mult..];
1466    for copy in 0..cfg.hc_mult {
1467        let hs = &h[copy * cfg.dim..(copy + 1) * cfg.dim];
1468        let ks = &key[copy * cfg.dim..(copy + 1) * cfg.dim];
1469        let qw = e
1470            .q_weight
1471            .get(copy * cfg.dim..(copy + 1) * cfg.dim)
1472            .unwrap_or(&[]);
1473        let kw = e
1474            .k_weight
1475            .get(copy * cfg.dim..(copy + 1) * cfg.dim)
1476            .unwrap_or(&[]);
1477        let hm = hs.iter().map(|v| v * v).sum::<f32>() / cfg.dim as f32;
1478        let km = ks.iter().map(|v| v * v).sum::<f32>() / cfg.dim as f32;
1479        let rstd = 1.0 / (hm + cfg.norm_eps).sqrt() / (km + cfg.norm_eps).sqrt();
1480        let mut dot = 0.0;
1481        for i in 0..cfg.dim {
1482            dot += hs[i]
1483                * ks[i]
1484                * qw.get(i).copied().unwrap_or(1.0)
1485                * kw.get(i).copied().unwrap_or(1.0);
1486        }
1487        dot *= rstd * (cfg.dim as f32).powf(-0.5);
1488        // torch.copysign(+sqrt(abs(dot)), dot) treats an exact zero as
1489        // positive. `signum()` would turn that case into zero and changes
1490        // the gate from sigmoid(sqrt(1e-6)) to sigmoid(0).
1491        let root = dot.abs().max(1e-6).sqrt();
1492        let gate = sigmoid(if dot.is_sign_negative() { -root } else { root });
1493        let dst = &mut h[copy * cfg.dim..(copy + 1) * cfg.dim];
1494        for i in 0..cfg.dim {
1495            dst[i] += gate * value[i];
1496        }
1497    }
1498    // The source returns `h + gate * value` cast back to the residual dtype;
1499    // leaving this f32 would make the next HC projection consume extra bits.
1500    bf16_inplace(h);
1501}
1502
1503/// Run the isolated Engram projection for the release component oracle.
1504/// Kept hidden from generated documentation; this narrow entry point lets a
1505/// fixture exercise the mmap-backed raw E4M3/E8M0 lookup without constructing
1506/// the rest of a full V4.1 checkpoint.
1507#[doc(hidden)]
1508pub fn dsv41_apply_engram_for_test(
1509    e: &Dsv41Engram,
1510    h: &mut [f32],
1511    hashes: &[usize],
1512    cfg: &Dsv41Cfg,
1513    token_mask: bool,
1514) {
1515    apply_engram(e, h, hashes, cfg, token_mask, None);
1516}
1517
1518fn compressor_step(
1519    cp: &Dsv41Compressor,
1520    x: &[f32],
1521    cfg: &Dsv41Cfg,
1522    pending_kv: &mut Vec<f32>,
1523    pending_score: &mut Vec<f32>,
1524    pool: Option<&Pool>,
1525) -> Option<Vec<f32>> {
1526    let mut kv = vec![0.0f32; cfg.head_dim];
1527    // Ratio-one compressors use the model BF16 linear.  The pooled
1528    // ratio>1 path intentionally keeps both projections in f32.
1529    if cp.ratio == 1 {
1530        matvec_bf16(&cp.wkv, x, &mut kv, pool);
1531    } else {
1532        matvec(&cp.wkv, x, &mut kv, pool);
1533    }
1534    if cp.ratio == 1 {
1535        rms(&mut kv, &cp.norm, cfg.norm_eps);
1536        return Some(kv);
1537    }
1538    let mut score = vec![0.0f32; cfg.head_dim];
1539    if let Some(wg) = &cp.wgate {
1540        matvec(wg, x, &mut score, pool);
1541    }
1542    pending_kv.extend_from_slice(&kv);
1543    pending_score.extend_from_slice(&score);
1544    if pending_kv.len() < cp.ratio * cfg.head_dim {
1545        return None;
1546    }
1547    let mut out = vec![0.0f32; cfg.head_dim];
1548    for d in 0..cfg.head_dim {
1549        let m = (0..cp.ratio)
1550            .map(|i| pending_score[i * cfg.head_dim + d])
1551            .fold(f32::NEG_INFINITY, f32::max);
1552        let mut den = 0.0;
1553        for i in 0..cp.ratio {
1554            den += (pending_score[i * cfg.head_dim + d] - m).exp();
1555        }
1556        if den > 0.0 {
1557            for i in 0..cp.ratio {
1558                out[d] += (pending_score[i * cfg.head_dim + d] - m).exp() / den
1559                    * pending_kv[i * cfg.head_dim + d];
1560            }
1561        }
1562    }
1563    pending_kv.clear();
1564    pending_score.clear();
1565    rms(&mut out, &cp.norm, cfg.norm_eps);
1566    Some(out)
1567}
1568
1569fn fp4_round(x: f32) -> f32 {
1570    let sign = if x.is_sign_negative() { -1.0 } else { 1.0 };
1571    let ax = x.abs();
1572    if !ax.is_finite() {
1573        return sign * 6.0;
1574    }
1575    const LEVELS: [f32; 8] = [0.0, 0.5, 1.0, 1.5, 2.0, 3.0, 4.0, 6.0];
1576    // E2M1 has an alternating even/odd significand at these levels.  The
1577    // hardware/Torch cast is round-to-nearest-even, so a half-way value must
1578    // choose the even level (e.g. 0.75 -> 1, 1.75 -> 2, 3.5 -> 4), rather
1579    // than the lower level selected by a stable min-by tie.
1580    const EVEN: [bool; 8] = [true, false, true, false, true, false, true, false];
1581    let mut best = 0usize;
1582    let mut best_dist = ax;
1583    for i in 1..LEVELS.len() {
1584        let dist = (ax - LEVELS[i]).abs();
1585        if dist < best_dist || (dist == best_dist && EVEN[i] && !EVEN[best]) {
1586            best = i;
1587            best_dist = dist;
1588        }
1589    }
1590    sign * LEVELS[best]
1591}
1592
1593fn fp4_inplace(v: &mut [f32], block: usize, e8m0_scale_fmt: bool) {
1594    for chunk in v.chunks_mut(block) {
1595        // Keep the same nonzero scale floors as the reference kernel.  The
1596        // E8M0 index path permits subnormal 2^-126, while compressed KV's
1597        // E4M3 scale path floors the amax at 6*2^-9 before the FP8 cast.
1598        // A single generic 1e-4 floor silently turns zero index blocks into
1599        // 2^-15 and changes every subsequent dot product.
1600        let min_amax = if e8m0_scale_fmt {
1601            6.0 * 2.0f32.powi(-126)
1602        } else {
1603            6.0 * 2.0f32.powi(-9)
1604        };
1605        let max = chunk.iter().map(|x| x.abs()).fold(min_amax, f32::max);
1606        let raw_scale = max / 6.0;
1607        let scale = if e8m0_scale_fmt {
1608            round_e8m0_scale(raw_scale)
1609        } else {
1610            // E4M3 scales use the same minimum as fp4_quant_kernel: an
1611            // all-zero block still carries 6*2^-9 before the cast.
1612            e4m3_round(raw_scale.max(6.0 * 2.0f32.powi(-9)))
1613        };
1614        if scale == 0.0 || !scale.is_finite() {
1615            chunk.fill(0.0);
1616            continue;
1617        }
1618        for x in chunk {
1619            *x = bf16_roundtrip(fp4_round(*x / scale) * scale);
1620        }
1621    }
1622}
1623
1624fn fp8_activation_inplace(v: &mut [f32]) {
1625    for chunk in v.chunks_mut(32) {
1626        let max = chunk.iter().map(|x| x.abs()).fold(1e-4f32, f32::max);
1627        let scale = 2.0f32.powf((max / 448.0).log2().ceil());
1628        for x in chunk {
1629            *x = bf16_roundtrip(e4m3_round((*x / scale).clamp(-448.0, 448.0)) * scale);
1630        }
1631    }
1632}
1633
1634struct IndexResult {
1635    picked: Vec<usize>,
1636    scores: Vec<f32>,
1637}
1638
1639fn update_index(
1640    ix: &Dsv41Indexer,
1641    x: &[f32],
1642    qr: &[f32],
1643    source_k: &packed_kv::PackedRows,
1644    cfg: &Dsv41Cfg,
1645    pos: usize,
1646    ratio: usize,
1647    inv_freq: &[f32],
1648    candidate_mask: Option<&[bool]>,
1649    pool: Option<&Pool>,
1650) -> IndexResult {
1651    let ih = cfg.index_heads;
1652    let id = cfg.index_head_dim;
1653    let mut q = vec![0.0f32; ih * id];
1654    matvec_bf16(&ix.wq_b, qr, &mut q, pool);
1655    // `q` is head-major. RoPE applies to the tail of each index head, not to
1656    // the tail of the flattened concatenation.
1657    for head in 0..ih {
1658        crate::dsv4::rope_tail(
1659            &mut q[head * id..(head + 1) * id],
1660            inv_freq,
1661            pos,
1662            cfg.rope_head_dim.min(id) & !1,
1663            false,
1664        );
1665    }
1666    // RoPE writes back into a BF16 tensor in the reference kernel.
1667    bf16_inplace(&mut q);
1668    fp4_inplace(&mut q, 32, true);
1669    let mut weights = vec![0.0f32; ih];
1670    matvec_bf16(&ix.weights_proj, x, &mut weights, pool);
1671    let sc = (id as f32).powf(-0.5) * (ih as f32).powf(-0.5);
1672    for w in &mut weights {
1673        // Torch keeps the scalar multiply in the BF16 tensor dtype.
1674        *w = bf16_roundtrip(*w * sc);
1675    }
1676    let n = source_k.rows();
1677    let mut scores = vec![f32::NEG_INFINITY; n];
1678    let mut k = vec![0.0f32; id];
1679    for t in 0..n {
1680        if candidate_mask.is_some_and(|m| !m.get(t).copied().unwrap_or(false)) {
1681            continue;
1682        }
1683        assert!(
1684            source_k.row_into(t, &mut k),
1685            "packed index-K row {t} is unavailable"
1686        );
1687        let k = &k[..];
1688        let mut s = 0.0;
1689        for head in 0..ih {
1690            // einsum emits a BF16 score; its dot-product accumulator is
1691            // wider, but the output is materialised before ReLU.
1692            let d = bf16_roundtrip(
1693                q[head * id..(head + 1) * id]
1694                    .iter()
1695                    .zip(k)
1696                    .map(|(a, b)| a * b)
1697                    .sum::<f32>(),
1698            )
1699            .max(0.0);
1700            // BF16 multiplication happens before the final head reduction.
1701            s += bf16_roundtrip(d * weights[head]);
1702        }
1703        if t >= (pos + 1) / ratio.max(1) {
1704            scores[t] = f32::NEG_INFINITY;
1705        } else {
1706            // torch.sum over the BF16 products returns a BF16 tensor.
1707            scores[t] = bf16_roundtrip(s);
1708        }
1709    }
1710    let mut picked = Vec::new();
1711    crate::dsv4::top_k_positions(&scores, cfg.index_topk, &mut picked);
1712    IndexResult { picked, scores }
1713}
1714
1715fn attention(
1716    l: &Dsv41Layer,
1717    cfg: &Dsv41Cfg,
1718    st: &mut Dsv41State,
1719    li: usize,
1720    x: &[f32],
1721    inv_freq: &[f32],
1722    pool: Option<&Pool>,
1723    out: &mut [f32],
1724) -> Vec<f32> {
1725    let attn_other_t0 = prof::start();
1726    let mut qr = vec![0.0f32; cfg.q_lora_rank];
1727    let mut kv = vec![0.0f32; cfg.head_dim];
1728    // Both projections read the same hidden state.  Keep the two output
1729    // boundaries explicit, but hand the Q4TP pair to the existing
1730    // matvec_many dispatcher so one pool job owns the shared input walk.
1731    QTensor::matvec_many([&l.wq_a, &l.wkv], x, [&mut qr, &mut kv], pool);
1732    bf16_inplace(&mut qr);
1733    trace_stats("wq_a", st.pos, Some(li), &qr);
1734    rms(&mut qr, &l.q_norm, cfg.norm_eps);
1735    trace_stats("q_norm", st.pos, Some(li), &qr);
1736    bf16_inplace(&mut kv);
1737    trace_stats("wkv", st.pos, Some(li), &kv);
1738    rms(&mut kv, &l.kv_norm, cfg.norm_eps);
1739    trace_stats("kv_norm", st.pos, Some(li), &kv);
1740    crate::dsv4::rope_tail(&mut kv, inv_freq, st.pos, cfg.rope_head_dim, false);
1741    bf16_inplace(&mut kv);
1742
1743    // A layer's `compress_ratio` controls whether it reads the shared
1744    // compressed stream at all. Layers after the last source can still have
1745    // a source slot in scope, but ratio-zero layers are window-only in the
1746    // reference and must not concatenate the old compressed cache.
1747    let source = if cfg.ratio(li) > 0 {
1748        source_slot(&cfg.kv_sources, li)
1749    } else {
1750        None
1751    };
1752    let mut latent = None;
1753    if let (Some(cp), Some(si)) = (&l.compressor, source) {
1754        latent = compressor_step(
1755            cp,
1756            x,
1757            cfg,
1758            &mut st.pending_kv[si],
1759            &mut st.pending_score[si],
1760            pool,
1761        );
1762        if let Some(v) = latent.as_ref() {
1763            trace_stats("latent", st.pos, Some(li), v);
1764            // The compressor publishes a pre-RoPE latent.  Keep that form
1765            // for the index-key owner, and write a separately rotated copy
1766            // to the attention cache.
1767            let latent_pos = st.pos + 1 - cfg.ratio(li).max(1);
1768            let mut stored = v.clone();
1769            crate::dsv4::rope_tail(&mut stored, inv_freq, latent_pos, cfg.rope_head_dim, false);
1770            bf16_inplace(&mut stored);
1771            let row_id = st
1772                .packed
1773                .push_main(si, &stored)
1774                .expect("valid main KV source slot");
1775            if std::env::var_os("CMF_DSV41_TRACE").is_some() {
1776                let mut quantized = vec![0.0f32; cfg.head_dim];
1777                assert!(st.packed.main_row_into(si, row_id, &mut quantized));
1778                trace_stats("compressed", st.pos, Some(li), &quantized);
1779            }
1780        }
1781    }
1782    // Index keys are derived from the unrotated latent, before attention's
1783    // RoPE write.  A source publishes one key; consumer indexers reuse it.
1784    if let (Some(ix), Some(si), Some(lat)) = (&l.indexer, source, latent.as_ref()) {
1785        if let (Some(wk), Some(kn)) = (&ix.wk, &ix.k_norm) {
1786            let latent_pos = st.pos + 1 - cfg.ratio(li).max(1);
1787            let mut k = vec![0.0f32; cfg.index_head_dim];
1788            matvec_bf16(wk, lat, &mut k, pool);
1789            rms(&mut k, kn, cfg.norm_eps);
1790            crate::dsv4::rope_tail(
1791                &mut k,
1792                inv_freq,
1793                latent_pos,
1794                cfg.rope_head_dim.min(cfg.index_head_dim) & !1,
1795                false,
1796            );
1797            bf16_inplace(&mut k);
1798            let row_id = st
1799                .packed
1800                .push_index(si, &k)
1801                .expect("valid index-K source slot");
1802            if std::env::var_os("CMF_DSV41_TRACE").is_some() {
1803                let mut quantized = vec![0.0f32; cfg.index_head_dim];
1804                assert!(st.packed.index_row_into(si, row_id, &mut quantized));
1805                trace_stats("index_k", st.pos, Some(li), &quantized);
1806            }
1807        }
1808        let _ = ix;
1809    }
1810    // Publish the sliding window before building the sparse position list.
1811    // Keep this mutable borrow scoped: attention below only needs immutable
1812    // slices, and can therefore read the shared compressed cache without
1813    // cloning it on every decode token.
1814    let win_len = {
1815        let w = &mut st.window[li];
1816        fp8_activation_inplace(&mut kv);
1817        // `act_quant(..., inplace=True)` stores the dequantized result as
1818        // BF16 even though its internal scale/value arithmetic is f32.
1819        bf16_inplace(&mut kv);
1820        trace_stats("kv", st.pos, Some(li), &kv);
1821        w.extend_from_slice(&kv);
1822        let cap = cfg.window * cfg.head_dim;
1823        if w.len() > cap {
1824            let drop = w.len() - cap;
1825            w.drain(..drop);
1826        }
1827        w.len() / cfg.head_dim
1828    };
1829    let mut idxs: Vec<usize> = (0..win_len).collect();
1830    if let Some(si) = source {
1831        let comp_len = st.packed.main_rows(si);
1832        if comp_len > 0 {
1833            if let Some(ix) = &l.indexer {
1834                let candidates = if li > cfg.candidate_source && !st.candidates.is_empty() {
1835                    Some(st.candidates.as_slice())
1836                } else {
1837                    None
1838                };
1839                let result = update_index(
1840                    ix,
1841                    x,
1842                    &qr,
1843                    st.packed
1844                        .index_store(si)
1845                        .expect("index-K store for shared source"),
1846                    cfg,
1847                    st.pos,
1848                    cfg.ratio(li),
1849                    inv_freq,
1850                    candidates,
1851                    pool,
1852                );
1853                trace_indices(st.pos, li, &result.picked);
1854                if cfg.candidate_source == li {
1855                    st.candidates = candidate_blocks(
1856                        &result.scores,
1857                        cfg.candidate_topk_blocks,
1858                        cfg.candidate_block_size,
1859                    );
1860                    trace_index_scores(
1861                        st.pos,
1862                        li,
1863                        &result.scores,
1864                        &result.picked,
1865                        Some(st.candidates.as_slice()),
1866                        win_len,
1867                        comp_len,
1868                        st.pending_kv[si].len() / cfg.head_dim.max(1),
1869                    );
1870                } else {
1871                    trace_index_scores(
1872                        st.pos,
1873                        li,
1874                        &result.scores,
1875                        &result.picked,
1876                        candidates,
1877                        win_len,
1878                        comp_len,
1879                        st.pending_kv[si].len() / cfg.head_dim.max(1),
1880                    );
1881                }
1882                st.topk = result.picked;
1883                st.topk_ready = true;
1884            }
1885            let all_picks: Vec<usize> = (0..comp_len).collect();
1886            let picks: &[usize] = if cfg.index_source(li).is_some() && st.topk_ready {
1887                &st.topk
1888            } else {
1889                &all_picks
1890            };
1891            idxs.extend(
1892                picks
1893                    .iter()
1894                    .copied()
1895                    .filter(|&p| p < comp_len)
1896                    .map(|p| win_len + p),
1897            );
1898        }
1899    }
1900
1901    // V4.1 has no second per-head query RMSNorm after wq_b. The default
1902    // path materialises the final query on the host so the GPU adapter and
1903    // CPU fallback share one exact BF16/RoPE value. The opt-in fused path
1904    // delays this allocation: its frame consumes `qr` and performs wq_b,
1905    // BF16 materialisation, and forward RoPE inside the existing encoder.
1906    let fused_q = v41_fused_q_enabled();
1907    let materialize_q = || {
1908        let mut q = vec![0.0f32; cfg.n_heads * cfg.head_dim];
1909        matvec_bf16(&l.wq_b, &qr, &mut q, pool);
1910        trace_stats("wq_b", st.pos, Some(li), &q);
1911        for h in 0..cfg.n_heads {
1912            let qh = &mut q[h * cfg.head_dim..(h + 1) * cfg.head_dim];
1913            crate::dsv4::rope_tail(qh, inv_freq, st.pos, cfg.rope_head_dim, false);
1914        }
1915        bf16_inplace(&mut q);
1916        trace_stats("q", st.pos, Some(li), &q);
1917        q
1918    };
1919    let mut q = if fused_q { None } else { Some(materialize_q()) };
1920
1921    // Decode the selected global rows once. The resulting compact stream is
1922    // shared by every head and by both the device frame and CPU fallback;
1923    // no history-sized f32 materialization is needed for sparse attention.
1924    let selected_comp_positions: Vec<usize> = idxs
1925        .iter()
1926        .copied()
1927        .filter(|&p| p >= win_len)
1928        .map(|p| p - win_len)
1929        .collect();
1930    let mut selected_compressed = vec![0.0f32; selected_comp_positions.len() * cfg.head_dim];
1931    if let Some(si) = source {
1932        assert!(
1933            st.packed
1934                .gather_main_rows(si, &selected_comp_positions, &mut selected_compressed,),
1935            "packed main-K gather failed for source {si}"
1936        );
1937    }
1938    let mut packed_idxs = Vec::with_capacity(idxs.len());
1939    for &logical in &idxs {
1940        if logical < win_len {
1941            packed_idxs.push(logical);
1942        } else {
1943            let comp = logical - win_len;
1944            let selected = selected_comp_positions
1945                .iter()
1946                .position(|&row| row == comp)
1947                .expect("selected compressed row mapping");
1948            packed_idxs.push(win_len + selected);
1949        }
1950    }
1951
1952    // ── proven DSV4 attention tail on the device ──
1953    // V4.1 keeps the compressor/index publication above on the host. Once
1954    // the bounded selected rows exist, the established DSV4 frame can perform
1955    // q projection (when fused), sparse attention with the per-head sink,
1956    // inverse RoPE, grouped wo_a and wo_b in one encoder/readback.
1957    // The cache is keyed by this state's private id and receives only the
1958    // current bounded window plus the selected compressed rows; no
1959    // whole-history upload or clone is created here.
1960    #[cfg(feature = "gpu")]
1961    if gpu_attention_tail_enabled() {
1962        let n_comp = selected_comp_positions.len();
1963        let cache_cap = (cfg.window + n_comp.next_power_of_two().max(64)) * cfg.head_dim;
1964        let cache_ok =
1965            crate::gpu_wgpu::dsv4_cache_write(st.gpu_kv_id, li, 0, &st.window[li], cache_cap)
1966                && (selected_compressed.is_empty()
1967                    || crate::gpu_wgpu::dsv4_cache_write(
1968                        st.gpu_kv_id,
1969                        li,
1970                        cfg.window * cfg.head_dim,
1971                        &selected_compressed,
1972                        cache_cap,
1973                    ));
1974        let model = l.wq_b.model_arc();
1975        // The gathered cache is already in attention-list order, so logical
1976        // positions become compact row numbers. This keeps sink/index/window
1977        // semantics while bounding each upload to the rows actually used.
1978        let idx32: Vec<u32> = packed_idxs
1979            .iter()
1980            .map(|&p| {
1981                if p < win_len {
1982                    p as u32
1983                } else {
1984                    (cfg.window + p - win_len) as u32
1985                }
1986            })
1987            .collect();
1988        let diag_tap = gpu_tail_tap();
1989        let diag_len = diag_tap.map(|tap| match tap {
1990            "q" | "attn" => cfg.n_heads * cfg.head_dim,
1991            "mid" => cfg.o_groups * cfg.o_lora_rank,
1992            _ => cfg.dim,
1993        });
1994        let mut gpu_attended = vec![0.0f32; cfg.dim.max(diag_len.unwrap_or(0))];
1995        // `qn_in` is the already normalized LoRA-rank query. In fused mode
1996        // the frame owns wq_b/BF16/RoPE; otherwise retain the legacy final-q
1997        // upload. Both modes keep the same selected packed rows and output
1998        // buffer contract.
1999        let qn_in = fused_q.then_some(qr.as_slice());
2000        let q_in = q.as_deref();
2001        let gpu_ok = cache_ok
2002            && model.is_some_and(|model| {
2003                let w = crate::gpu_wgpu::Dsv4AttnW {
2004                    wq_a: l.wq_a.model_idx().unwrap_or(usize::MAX),
2005                    wq_b: l.wq_b.model_idx().unwrap_or(usize::MAX),
2006                    wo_a: l.wo_a.model_idx().unwrap_or(usize::MAX),
2007                    wo_b: l.wo_b.model_idx().unwrap_or(usize::MAX),
2008                    q_norm: &l.q_norm,
2009                    sink: &l.attn_sink,
2010                };
2011                let g = crate::gpu_wgpu::Dsv4AttnGeom {
2012                    dim: cfg.dim,
2013                    nh: cfg.n_heads,
2014                    hd: cfg.head_dim,
2015                    rd: cfg.rope_head_dim,
2016                    q_lora: cfg.q_lora_rank,
2017                    o_lora: cfg.o_lora_rank,
2018                    o_groups: cfg.o_groups,
2019                    eps: cfg.norm_eps,
2020                    scale: (cfg.head_dim as f32).powf(-0.5),
2021                    bf16: true,
2022                    q_rms: false,
2023                };
2024                crate::gpu_wgpu::dsv4_attn_frame(
2025                    &model,
2026                    &w,
2027                    g,
2028                    &[],
2029                    qn_in,
2030                    q_in,
2031                    st.gpu_kv_id,
2032                    li,
2033                    &idx32,
2034                    inv_freq,
2035                    st.pos,
2036                    None,
2037                    &mut gpu_attended,
2038                )
2039            });
2040        if gpu_ok {
2041            prof::note_gpu_attn();
2042            if let Some(tap) = diag_tap {
2043                let name = match tap {
2044                    "q" => "gpu_q",
2045                    "attn" => "gpu_attn",
2046                    "mid" => "gpu_mid",
2047                    _ => "gpu_tail",
2048                };
2049                trace_stats(name, st.pos, Some(li), &gpu_attended[..diag_len.unwrap()]);
2050            } else {
2051                // The GPU frame owns the entire attention body on this arm. Keep
2052                // the cumulative stage report additive with the CPU path: its
2053                // host time is the residual of the outer attention timer.
2054                prof::add(&prof::ATTN_OTHER_NS, attn_other_t0);
2055                bf16_inplace(&mut gpu_attended);
2056                out.copy_from_slice(&gpu_attended[..cfg.dim]);
2057                return qr;
2058            }
2059        }
2060        prof::note_gpu_attn_fallback();
2061    }
2062
2063    // A disabled/unavailable GPU, a cache miss, or a diagnostic tap needs the
2064    // unchanged host query for the ordinary sparse CPU fallback. Keep this
2065    // allocation after the GPU attempt in fused mode so the successful path
2066    // has no host q materialization or q upload.
2067    if q.is_none() {
2068        q = Some(materialize_q());
2069    }
2070    let q = q
2071        .as_deref()
2072        .expect("V4.1 query must be materialized before CPU attention");
2073
2074    let window = &st.window[li];
2075    let compressed = if selected_compressed.is_empty() {
2076        None
2077    } else {
2078        Some(selected_compressed.as_slice())
2079    };
2080    let mut attended = vec![0.0f32; cfg.n_heads * cfg.head_dim];
2081    prof::add(&prof::ATTN_OTHER_NS, attn_other_t0);
2082    let sparse_t0 = prof::start();
2083    for h in 0..cfg.n_heads {
2084        sparse_attend_split(
2085            &q[h * cfg.head_dim..(h + 1) * cfg.head_dim],
2086            window,
2087            compressed,
2088            &packed_idxs,
2089            l.attn_sink.get(h).copied().unwrap_or(0.0),
2090            (cfg.head_dim as f32).powf(-0.5),
2091            win_len,
2092            cfg.head_dim,
2093            &mut attended[h * cfg.head_dim..(h + 1) * cfg.head_dim],
2094        );
2095    }
2096    prof::add(&prof::SPARSE_NS, sparse_t0);
2097    let attn_other_t0 = prof::start();
2098    for h in 0..cfg.n_heads {
2099        crate::dsv4::rope_tail(
2100            &mut attended[h * cfg.head_dim..(h + 1) * cfg.head_dim],
2101            inv_freq,
2102            st.pos,
2103            cfg.rope_head_dim,
2104            true,
2105        );
2106        bf16_inplace(&mut attended[h * cfg.head_dim..(h + 1) * cfg.head_dim]);
2107    }
2108    trace_stats("attended", st.pos, Some(li), &attended);
2109    if gpu_tail_tap().is_some_and(|tap| tap == "mid") {
2110        let mut cpu_mid = vec![0.0f32; cfg.o_groups * cfg.o_lora_rank];
2111        let mut scratch = vec![0.0f32; l.wo_a.cols()];
2112        for (i, mid) in cpu_mid.iter_mut().enumerate() {
2113            let group = i / cfg.o_lora_rank;
2114            *mid = bf16_roundtrip(l.wo_a.row_dot(
2115                i,
2116                &attended[group * l.wo_a.cols()..(group + 1) * l.wo_a.cols()],
2117                &mut scratch,
2118            ));
2119        }
2120        trace_stats("cpu_mid", st.pos, Some(li), &cpu_mid);
2121    }
2122    crate::dsv4::o_project(
2123        &attended,
2124        // `wo_a` is the grouped BF16 einsum in the reference.  Its result
2125        // is materialised as BF16 before the second projection; rounding
2126        // only the final `wo_b` output leaves a wider f32 intermediate and
2127        // accumulates a measurable drift into the next block.
2128        &|r, v, scratch| bf16_roundtrip(l.wo_a.row_dot(r, v, scratch)),
2129        l.wo_a.cols(),
2130        &|m, o| l.wo_b.matvec(m, o, pool),
2131        cfg.o_groups,
2132        cfg.o_lora_rank,
2133        pool,
2134        out,
2135    );
2136    // The grouped wo_a/wo_b projection returns the model activation dtype.
2137    bf16_inplace(out);
2138    if gpu_tail_tap().is_some() {
2139        trace_stats("cpu_tail", st.pos, Some(li), out);
2140    }
2141    prof::add(&prof::ATTN_OTHER_NS, attn_other_t0);
2142    qr
2143}
2144
2145/// Attention over the concatenated logical stream `window || compressed`
2146/// without materializing that concatenation.  A decode step can have a very
2147/// large compressed history, so cloning it once per layer would turn a
2148/// bounded sparse read into an O(context) copy before the actual O(top-k)
2149/// attention.  The position list still uses the same logical indices as the
2150/// reference helper; this function only changes how the selected value rows
2151/// are addressed.
2152fn sparse_attend_split(
2153    q: &[f32],
2154    window: &[f32],
2155    compressed: Option<&[f32]>,
2156    idxs: &[usize],
2157    sink: f32,
2158    scale: f32,
2159    window_len: usize,
2160    head_dim: usize,
2161    out: &mut [f32],
2162) {
2163    let row = |logical: usize| -> Option<&[f32]> {
2164        if logical < window_len {
2165            window.get(logical * head_dim..(logical + 1) * head_dim)
2166        } else {
2167            compressed.and_then(|c| {
2168                let p = logical - window_len;
2169                c.get(p * head_dim..(p + 1) * head_dim)
2170            })
2171        }
2172    };
2173    let mut m = sink;
2174    let mut scores = Vec::with_capacity(idxs.len());
2175    for &p in idxs {
2176        let Some(k) = row(p) else {
2177            scores.push(f32::NEG_INFINITY);
2178            continue;
2179        };
2180        let dot: f32 = q.iter().zip(k).map(|(a, b)| a * b).sum::<f32>() * scale;
2181        m = m.max(dot);
2182        scores.push(dot);
2183    }
2184    let mut denom = (sink - m).exp();
2185    out.fill(0.0);
2186    for (&p, &score) in idxs.iter().zip(&scores) {
2187        let Some(v) = row(p) else {
2188            continue;
2189        };
2190        let weight = (score - m).exp();
2191        denom += weight;
2192        for (dst, &value) in out.iter_mut().zip(v) {
2193            *dst += weight * value;
2194        }
2195    }
2196    if denom > 0.0 && denom.is_finite() {
2197        let inv = 1.0 / denom;
2198        for dst in out.iter_mut() {
2199            *dst *= inv;
2200        }
2201    }
2202}
2203
2204fn candidate_blocks(scores: &[f32], top_blocks: usize, block: usize) -> Vec<bool> {
2205    let n = scores.len();
2206    let blocks = n.div_ceil(block.max(1));
2207    if blocks == 0 {
2208        return Vec::new();
2209    }
2210    let block = block.max(1);
2211    let mut order: Vec<usize> = (0..blocks).collect();
2212    // The source selects by the best reachable position in each block. A
2213    // stable index tie break keeps CPU and GPU fixtures deterministic.
2214    order.sort_by(|&a, &b| {
2215        let sa = scores[a * block..((a + 1) * block).min(n)]
2216            .iter()
2217            .copied()
2218            .fold(f32::NEG_INFINITY, f32::max);
2219        let sb = scores[b * block..((b + 1) * block).min(n)]
2220            .iter()
2221            .copied()
2222            .fold(f32::NEG_INFINITY, f32::max);
2223        sb.partial_cmp(&sa)
2224            .unwrap_or(std::cmp::Ordering::Equal)
2225            .then_with(|| a.cmp(&b))
2226    });
2227    let reachable = scores.iter().filter(|x| x.is_finite()).count();
2228    if reachable == 0 {
2229        return vec![false; n];
2230    }
2231    let last = scores
2232        .iter()
2233        .rposition(|x| x.is_finite())
2234        .map(|p| p / block)
2235        .unwrap_or(0);
2236    let mut keep = vec![false; blocks];
2237    let budget = top_blocks.min(blocks);
2238    let mut selected = 0usize;
2239    if budget > 0 {
2240        keep[last] = true;
2241        selected = 1;
2242    }
2243    for &b in &order {
2244        if selected >= budget {
2245            break;
2246        }
2247        if b == last || keep[b] {
2248            continue;
2249        }
2250        let block_score = scores[b * block..((b + 1) * block).min(n)]
2251            .iter()
2252            .copied()
2253            .fold(f32::NEG_INFINITY, f32::max);
2254        if block_score.is_finite() {
2255            keep[b] = true;
2256            selected += 1;
2257        }
2258    }
2259    let mut out = vec![false; n];
2260    for b in 0..blocks {
2261        if keep[b] {
2262            for i in b * block..((b + 1) * block).min(n) {
2263                out[i] = true;
2264            }
2265        }
2266    }
2267    out
2268}
2269
2270#[cfg(feature = "gpu")]
2271fn dsv41_expert_cpu(
2272    expert: &Dsv41Expert,
2273    cfg: &Dsv41Cfg,
2274    x: &[f32],
2275    weight: f32,
2276    pool: Option<&Pool>,
2277    out: &mut [f32],
2278) {
2279    let mut g = vec![0.0f32; cfg.moe_inter];
2280    let mut u = vec![0.0f32; cfg.moe_inter];
2281    matvec_bf16(&expert.w1, x, &mut g, pool);
2282    matvec_bf16(&expert.w3, x, &mut u, pool);
2283    for i in 0..cfg.moe_inter {
2284        if cfg.swiglu_limit > 0.0 {
2285            g[i] = g[i].min(cfg.swiglu_limit);
2286            u[i] = u[i].clamp(-cfg.swiglu_limit, cfg.swiglu_limit);
2287        }
2288        g[i] = g[i] / (1.0 + (-g[i]).exp()) * u[i] * weight;
2289    }
2290    // The official Expert converts the weighted SwiGLU activation back to
2291    // the input dtype (BF16 in V4.1) before w2. `matvec_bf16` rounds its
2292    // output, but that is a separate boundary and cannot replace this cast.
2293    bf16_inplace(&mut g);
2294    let mut tmp = vec![0.0f32; cfg.dim];
2295    matvec_bf16(&expert.w2, &g, &mut tmp, pool);
2296    for (dst, value) in out.iter_mut().zip(tmp) {
2297        *dst += value;
2298    }
2299}
2300
2301#[cfg(feature = "gpu")]
2302fn dsv41_cold_experts_cpu(
2303    layer: &Dsv41Layer,
2304    cfg: &Dsv41Cfg,
2305    x: &[f32],
2306    cold: &[(usize, f32)],
2307    pool: Option<&Pool>,
2308) -> Vec<f32> {
2309    let mut out = vec![0.0f32; cfg.dim];
2310    for &(expert, weight) in cold {
2311        if let Some(expert) = layer.experts.get(expert) {
2312            dsv41_expert_cpu(expert, cfg, x, weight, pool, &mut out);
2313        }
2314    }
2315    out
2316}
2317
2318/// Execute V4.1's host-resolved route through the shared DSV4 segmented
2319/// global expert bank.  V4.1 routing stays on the host because its VL bias
2320/// and sqrt-softplus rule differ from the generic shader route.  Forced ids
2321/// plus preweighted values preserve that route exactly while the card fuses
2322/// gate/up/SwiGLU/down for resident experts and the host completes cold ones.
2323#[cfg(feature = "gpu")]
2324fn dynamic_moe_gpu(
2325    layer: &Dsv41Layer,
2326    cfg: &Dsv41Cfg,
2327    x: &[f32],
2328    pool: Option<&Pool>,
2329    logits: &[f32],
2330    picks: &[usize],
2331    weights: &[f32],
2332    layer_index: usize,
2333    n_layers: usize,
2334    state: &mut Dsv41State,
2335    out: &mut [f32],
2336) -> bool {
2337    if !crate::gpu::enabled_here()
2338        || !crate::gpu_wgpu::dsv4_global_moe_supported()
2339        || std::env::var("CMF_DSV41_DYNAMIC_MOE").as_deref() == Ok("0")
2340        || picks.len() != cfg.top_k
2341        || weights.len() != cfg.n_routed_experts
2342        || logits.len() != cfg.n_routed_experts
2343        || layer.gpu_expert_ids.len() != cfg.n_routed_experts
2344        || layer.gpu_shared_ids.is_none()
2345    {
2346        return false;
2347    }
2348    let first = match layer.experts.first() {
2349        Some(expert) => expert,
2350        None => return false,
2351    };
2352    let model = match first.w1.model_arc() {
2353        Some(model) => model,
2354        None => return false,
2355    };
2356    let gu_q2 = first.w1.model_dtype() == Some(TensorDtype::Q2TiledP)
2357        && first.w3.model_dtype() == Some(TensorDtype::Q2TiledP);
2358    if first.w2.model_dtype() != Some(TensorDtype::Q4TiledP) {
2359        return false;
2360    }
2361    // The global bank has one gate/up layout and one down layout.  Refuse a
2362    // mixed or synthetic layer rather than reinterpreting a tensor's bytes.
2363    let gu_dtype = if gu_q2 {
2364        TensorDtype::Q2TiledP
2365    } else {
2366        TensorDtype::Q4TiledP
2367    };
2368    let same_layout = layer.experts.iter().all(|expert| {
2369        expert
2370            .w1
2371            .model_arc()
2372            .is_some_and(|m| m.uid() == model.uid())
2373            && expert
2374                .w3
2375                .model_arc()
2376                .is_some_and(|m| m.uid() == model.uid())
2377            && expert
2378                .w2
2379                .model_arc()
2380                .is_some_and(|m| m.uid() == model.uid())
2381            && expert.w1.model_dtype() == Some(gu_dtype)
2382            && expert.w3.model_dtype() == Some(gu_dtype)
2383            && expert.w2.model_dtype() == Some(TensorDtype::Q4TiledP)
2384    });
2385    let shared_layout = layer
2386        .shared
2387        .w1
2388        .model_arc()
2389        .is_some_and(|m| m.uid() == model.uid())
2390        && layer
2391            .shared
2392            .w3
2393            .model_arc()
2394            .is_some_and(|m| m.uid() == model.uid())
2395        && layer
2396            .shared
2397            .w2
2398            .model_arc()
2399            .is_some_and(|m| m.uid() == model.uid())
2400        && layer.shared.w1.model_dtype() == Some(gu_dtype)
2401        && layer.shared.w3.model_dtype() == Some(gu_dtype)
2402        && layer.shared.w2.model_dtype() == Some(TensorDtype::Q4TiledP);
2403    if !same_layout || !shared_layout || layer_index >= n_layers {
2404        return false;
2405    }
2406    if state.gpu_pool.is_none() {
2407        state.gpu_pool = crate::qwen4_exp::QwenGpuPool::create_for_dsv41(
2408            &model,
2409            cfg.moe_inter,
2410            cfg.dim,
2411            n_layers,
2412            cfg.n_routed_experts,
2413            gu_q2,
2414        );
2415    }
2416    let (remap, shared_slot, segment_slots) = {
2417        let Some(gpu_pool) = state.gpu_pool.as_mut() else {
2418            return false;
2419        };
2420        let Some((remap, shared_slot)) = gpu_pool.ensure(
2421            &model,
2422            layer_index,
2423            picks,
2424            &layer.gpu_expert_ids,
2425            layer.gpu_shared_ids,
2426        ) else {
2427            return false;
2428        };
2429        (remap, shared_slot, gpu_pool.segment_slots)
2430    };
2431    let cold_ids: Vec<usize> = picks
2432        .iter()
2433        .copied()
2434        .filter(|&expert| remap.get(expert).copied() == Some(u32::MAX))
2435        .collect();
2436    let cold_jobs: Vec<(usize, f32)> = cold_ids
2437        .iter()
2438        .copied()
2439        .map(|expert| (expert, weights[expert]))
2440        .collect();
2441    let gpu_weights = crate::gpu_wgpu::Dsv4MoeW {
2442        router: &[],
2443        experts: &layer.gpu_expert_ids,
2444        logits,
2445        // In forced + preweighted mode this is V4.1's final route table;
2446        // the shader does not redo softplus, bias, or normalization.
2447        bias: Some(weights),
2448        mask: None,
2449        forced: Some(picks),
2450        remap: Some(&remap),
2451        global: Some(crate::gpu_wgpu::Dsv4GlobalMoe {
2452            pool_uid: model.uid(),
2453            shared_slot,
2454            segment_slots: segment_slots as u32,
2455        }),
2456        has_shared: true,
2457        shared_weight: 1.0,
2458        preweighted: true,
2459        qwen_softmax: false,
2460    };
2461    let geom = crate::gpu_wgpu::Dsv4MoeGeom {
2462        hidden: cfg.dim,
2463        inter: cfg.moe_inter,
2464        top_k: picks.len(),
2465        // We have already applied V4.1's route scale and denominator.
2466        route_scale: 1.0,
2467        swiglu_limit: cfg.swiglu_limit,
2468        gu_q2,
2469        bf16: true,
2470    };
2471    let mut gpu_out = vec![0.0f32; cfg.dim];
2472    let mut cold_from_gpu = Vec::new();
2473    let mut cold_x = Vec::new();
2474    let (frame_ok, cold_cpu) = std::thread::scope(|scope| {
2475        let cpu = (!cold_jobs.is_empty()).then(|| {
2476            // Cold experts deliberately stay on the host while resident
2477            // routes run on the device. Without the scope guard, QTensor's
2478            // small matvecs can re-enter the generic GPU backend and defeat
2479            // the intended overlap.
2480            scope.spawn(|| {
2481                crate::gpu::cpu_scope(|| dsv41_cold_experts_cpu(layer, cfg, x, &cold_jobs, pool))
2482            })
2483        });
2484        let ok = crate::gpu_wgpu::dsv4_moe_frame(
2485            &model,
2486            &gpu_weights,
2487            geom,
2488            x,
2489            &mut cold_from_gpu,
2490            &mut cold_x,
2491            None,
2492            None,
2493            &mut gpu_out,
2494        );
2495        let cpu_out = cpu
2496            .and_then(|job| job.join().ok())
2497            .unwrap_or_else(|| vec![0.0; cfg.dim]);
2498        (ok, cpu_out)
2499    });
2500    if !frame_ok
2501        || cold_from_gpu.len() != cold_jobs.len()
2502        || cold_from_gpu
2503            .iter()
2504            .map(|&(expert, _)| expert)
2505            .ne(cold_ids.iter().copied())
2506    {
2507        return false;
2508    }
2509    prof::note_cold(cold_jobs.len());
2510    for (dst, src) in gpu_out.iter_mut().zip(cold_cpu) {
2511        *dst += src;
2512    }
2513    // Match the source's BF16 boundary after routed and shared accumulation.
2514    bf16_inplace(&mut gpu_out);
2515    out[..cfg.dim].copy_from_slice(&gpu_out);
2516    true
2517}
2518
2519fn moe(
2520    l: &Dsv41Layer,
2521    cfg: &Dsv41Cfg,
2522    x: &[f32],
2523    pool: Option<&Pool>,
2524    image: bool,
2525    position: usize,
2526    layer: usize,
2527    n_layers: usize,
2528    state: &mut Dsv41State,
2529    out: &mut [f32],
2530) {
2531    prof::note_moe();
2532    let mut logits = vec![0.0f32; cfg.n_routed_experts];
2533    matvec(&l.gate, x, &mut logits, pool);
2534    let bias = if image {
2535        l.gate_bias_vl.as_deref().unwrap_or(&l.gate_bias)
2536    } else {
2537        &l.gate_bias
2538    };
2539    let mut scores = Vec::with_capacity(logits.len());
2540    let temperature = cfg.gate_temp.max(f32::MIN_POSITIVE);
2541    for &v in &logits {
2542        let v = v / temperature;
2543        scores.push(if v > 20.0 { v } else { (1.0 + v.exp()).ln() }.sqrt());
2544    }
2545    let mut shifted: Vec<f32> = scores
2546        .iter()
2547        .enumerate()
2548        .map(|(i, s)| s + bias.get(i).copied().unwrap_or(0.0))
2549        .collect();
2550    let mut picks = Vec::with_capacity(cfg.top_k);
2551    for _ in 0..cfg.top_k.min(cfg.n_routed_experts) {
2552        let mut i = 0usize;
2553        let mut v = f32::NEG_INFINITY;
2554        for (j, &candidate) in shifted.iter().enumerate() {
2555            if candidate > v {
2556                i = j;
2557                v = candidate;
2558            }
2559        }
2560        if !v.is_finite() {
2561            break;
2562        }
2563        picks.push(i);
2564        shifted[i] = f32::NEG_INFINITY;
2565    }
2566    trace_moe(position, layer, image, &logits, &scores, bias, &picks);
2567    let sum = picks.iter().map(|&i| scores[i]).sum::<f32>().max(1e-20);
2568    let mut mix_weights = vec![0.0f32; cfg.n_routed_experts];
2569    if cfg.norm_topk_prob {
2570        for &e in &picks {
2571            mix_weights[e] = scores[e] / sum * cfg.route_scale;
2572        }
2573    } else {
2574        for &e in &picks {
2575            mix_weights[e] = scores[e] * cfg.route_scale;
2576        }
2577    }
2578    #[cfg(feature = "gpu")]
2579    if dynamic_moe_gpu(
2580        l,
2581        cfg,
2582        x,
2583        pool,
2584        &logits,
2585        &picks,
2586        &mix_weights,
2587        layer,
2588        n_layers,
2589        state,
2590        out,
2591    ) {
2592        prof::note_gpu_moe();
2593        return;
2594    }
2595    prof::note_cpu_moe();
2596    out.fill(0.0);
2597    let mut tmp = vec![0.0f32; cfg.dim];
2598    for &e in &picks {
2599        let ew = mix_weights[e];
2600        let ex = &l.experts[e];
2601        let mut g = vec![0.0f32; cfg.moe_inter];
2602        let mut u = vec![0.0f32; cfg.moe_inter];
2603        matvec_bf16(&ex.w1, x, &mut g, pool);
2604        matvec_bf16(&ex.w3, x, &mut u, pool);
2605        for i in 0..cfg.moe_inter {
2606            if cfg.swiglu_limit > 0.0 {
2607                g[i] = g[i].min(cfg.swiglu_limit);
2608                u[i] = u[i].clamp(-cfg.swiglu_limit, cfg.swiglu_limit);
2609            }
2610            g[i] = g[i] / (1.0 + (-g[i]).exp()) * u[i] * ew;
2611        }
2612        bf16_inplace(&mut g);
2613        matvec_bf16(&ex.w2, &g, &mut tmp, pool);
2614        for i in 0..cfg.dim {
2615            out[i] += tmp[i];
2616        }
2617    }
2618    let shared = &l.shared;
2619    let mut g = vec![0.0f32; cfg.moe_inter];
2620    let mut u = vec![0.0f32; cfg.moe_inter];
2621    matvec_bf16(&shared.w1, x, &mut g, pool);
2622    matvec_bf16(&shared.w3, x, &mut u, pool);
2623    for i in 0..cfg.moe_inter {
2624        if cfg.swiglu_limit > 0.0 {
2625            g[i] = g[i].min(cfg.swiglu_limit);
2626            u[i] = u[i].clamp(-cfg.swiglu_limit, cfg.swiglu_limit);
2627        }
2628        g[i] = g[i] / (1.0 + (-g[i]).exp()) * u[i];
2629    }
2630    bf16_inplace(&mut g);
2631    matvec_bf16(&shared.w2, &g, &mut tmp, pool);
2632    for i in 0..cfg.dim {
2633        out[i] += tmp[i];
2634    }
2635    // MoE returns `y.type_as(x)` after accumulating routed and shared
2636    // branches in f32.
2637    bf16_inplace(out);
2638}
2639
2640fn hc_mixes(
2641    x: &[f32],
2642    fn_w: &[f32],
2643    base: &[f32],
2644    scale: &[f32; 3],
2645    cfg: &Dsv41Cfg,
2646    pool: Option<&Pool>,
2647    pre: &mut [f32],
2648    post: &mut [f32],
2649    comb: &mut [f32],
2650) {
2651    let mix_n = (2 + cfg.hc_mult) * cfg.hc_mult;
2652    let mut mixes = vec![0.0f32; mix_n];
2653    crate::dsv4::hc_mixes(x, fn_w, mix_n, cfg.norm_eps, pool, &mut mixes);
2654    crate::dsv4::hc_split_sinkhorn(
2655        &mixes,
2656        scale,
2657        base,
2658        cfg.hc_mult,
2659        cfg.hc_sinkhorn_iters,
2660        cfg.hc_eps,
2661        pre,
2662        post,
2663        comb,
2664    );
2665}
2666
2667/// Run one token through the complete V4.1 stack.  `token_id` is used for
2668/// embedding and for Engram hashes; the returned vector is the final logits.
2669pub fn forward_token(
2670    globals: &Dsv41Globals,
2671    layers: &[Dsv41Layer],
2672    cfg: &Dsv41Cfg,
2673    st: &mut Dsv41State,
2674    token_id: u32,
2675    position: usize,
2676    pool: Option<&Pool>,
2677    logits: &mut Vec<f32>,
2678) {
2679    forward_token_masked(
2680        globals, layers, cfg, st, token_id, position, true, pool, logits,
2681    )
2682}
2683
2684pub fn forward_token_masked(
2685    globals: &Dsv41Globals,
2686    layers: &[Dsv41Layer],
2687    cfg: &Dsv41Cfg,
2688    st: &mut Dsv41State,
2689    token_id: u32,
2690    position: usize,
2691    participates: bool,
2692    pool: Option<&Pool>,
2693    logits: &mut Vec<f32>,
2694) {
2695    forward_token_masked_with_embedding(
2696        globals,
2697        layers,
2698        cfg,
2699        st,
2700        token_id,
2701        position,
2702        participates,
2703        None,
2704        pool,
2705        logits,
2706        true,
2707    );
2708}
2709
2710/// V4.1 image ingress uses the same text stack but replaces the embedding
2711/// row for image markers/projector outputs. `embedding` is `None` for normal
2712/// text tokens; `want_logits=false` is used for all non-final prefill rows.
2713pub fn forward_token_masked_with_embedding(
2714    globals: &Dsv41Globals,
2715    layers: &[Dsv41Layer],
2716    cfg: &Dsv41Cfg,
2717    st: &mut Dsv41State,
2718    token_id: u32,
2719    position: usize,
2720    participates: bool,
2721    embedding: Option<&[f32]>,
2722    pool: Option<&Pool>,
2723    logits: &mut Vec<f32>,
2724    want_logits: bool,
2725) {
2726    forward_token_impl(
2727        globals,
2728        layers,
2729        cfg,
2730        st,
2731        token_id,
2732        position,
2733        participates,
2734        embedding,
2735        pool,
2736        logits,
2737        want_logits,
2738    );
2739}
2740
2741fn forward_token_impl(
2742    globals: &Dsv41Globals,
2743    layers: &[Dsv41Layer],
2744    cfg: &Dsv41Cfg,
2745    st: &mut Dsv41State,
2746    token_id: u32,
2747    position: usize,
2748    participates: bool,
2749    embedding: Option<&[f32]>,
2750    pool: Option<&Pool>,
2751    logits: &mut Vec<f32>,
2752    want_logits: bool,
2753) {
2754    let total_t0 = prof::start();
2755    prof::note_token(layers.len());
2756    st.pos = position;
2757    let hashes = st.hash.as_mut().map(|h| h.push(token_id, participates));
2758    let mut embed = vec![0.0f32; cfg.dim];
2759    if let Some(embedding) = embedding {
2760        assert_eq!(embedding.len(), cfg.dim, "V4.1 override embedding width");
2761        embed.copy_from_slice(embedding);
2762    } else {
2763        globals.embed.row_f32(
2764            (token_id as usize).min(globals.embed.rows().saturating_sub(1)),
2765            &mut embed,
2766        );
2767    }
2768    trace_stats("embed", position, None, &embed);
2769    let mut h = vec![0.0f32; cfg.hc_mult * cfg.dim];
2770    for copy in 0..cfg.hc_mult {
2771        h[copy * cfg.dim..(copy + 1) * cfg.dim].copy_from_slice(&embed);
2772    }
2773    let mut pre_mix = vec![0.0f32; cfg.hc_mult];
2774    pre_mix[0] = 1.0;
2775    for (li, l) in layers.iter().enumerate() {
2776        if let (Some(e), Some(all)) = (&l.engram, hashes.as_ref()) {
2777            if let Some(ix) = cfg.engram_layers.iter().position(|&id| id == li) {
2778                let engram_t0 = prof::start();
2779                apply_engram(e, &mut h, &all[ix], cfg, participates, pool);
2780                prof::add(&prof::ENGRAM_NS, engram_t0);
2781            }
2782        }
2783        let residual = h.clone();
2784        let mut ap = vec![0.0; cfg.hc_mult];
2785        let mut apo = vec![0.0; cfg.hc_mult];
2786        let mut ac = vec![0.0; cfg.hc_mult * cfg.hc_mult];
2787        hc_mixes(
2788            &h,
2789            &l.hc_attn_fn,
2790            &l.hc_attn_base,
2791            &l.hc_attn_scale,
2792            cfg,
2793            pool,
2794            &mut ap,
2795            &mut apo,
2796            &mut ac,
2797        );
2798        let mut folded = vec![0.0f32; cfg.dim];
2799        crate::dsv4::hc_fold(&h, &pre_mix, cfg.hc_mult, cfg.dim, &mut folded);
2800        rms(&mut folded, &l.attn_norm, cfg.norm_eps);
2801        let mut attn_out = vec![0.0f32; cfg.dim];
2802        let attn_t0 = prof::start();
2803        let _qr = attention(
2804            l,
2805            cfg,
2806            st,
2807            li,
2808            &folded,
2809            if cfg.ratio(li) > 0 {
2810                &globals.inv_freq_compress
2811            } else {
2812                &globals.inv_freq_window
2813            },
2814            pool,
2815            &mut attn_out,
2816        );
2817        prof::add(&prof::ATTN_NS, attn_t0);
2818        trace_stats("attn", position, Some(li), &attn_out);
2819        crate::dsv4::hc_expand(
2820            &attn_out,
2821            &residual,
2822            &apo,
2823            &ac,
2824            cfg.hc_mult,
2825            cfg.dim,
2826            &mut h,
2827        );
2828        bf16_inplace(&mut h);
2829        let residual2 = h.clone();
2830        let mut fp = vec![0.0; cfg.hc_mult];
2831        let mut fpo = vec![0.0; cfg.hc_mult];
2832        let mut fc = vec![0.0; cfg.hc_mult * cfg.hc_mult];
2833        hc_mixes(
2834            &h,
2835            &l.hc_ffn_fn,
2836            &l.hc_ffn_base,
2837            &l.hc_ffn_scale,
2838            cfg,
2839            pool,
2840            &mut fp,
2841            &mut fpo,
2842            &mut fc,
2843        );
2844        let mut ff = vec![0.0f32; cfg.dim];
2845        crate::dsv4::hc_fold(&h, &ap, cfg.hc_mult, cfg.dim, &mut folded);
2846        rms(&mut folded, &l.ffn_norm, cfg.norm_eps);
2847        // `participates=false` marks image span tokens.  Those tokens use
2848        // the VL routing correction bias while still bypassing Engram.
2849        let moe_t0 = prof::start();
2850        moe(
2851            l,
2852            cfg,
2853            &folded,
2854            pool,
2855            !participates,
2856            position,
2857            li,
2858            layers.len(),
2859            st,
2860            &mut ff,
2861        );
2862        prof::add(&prof::MOE_NS, moe_t0);
2863        crate::dsv4::hc_expand(&ff, &residual2, &fpo, &fc, cfg.hc_mult, cfg.dim, &mut h);
2864        bf16_inplace(&mut h);
2865        pre_mix.copy_from_slice(&fp);
2866        trace_stats("block", position, Some(li), &h);
2867        trace_stats("pre", position, Some(li), &pre_mix);
2868    }
2869    let head_t0 = prof::start();
2870    let mut final_h = vec![0.0f32; cfg.dim];
2871    crate::dsv4::hc_fold(&h, &pre_mix, cfg.hc_mult, cfg.dim, &mut final_h);
2872    rms(&mut final_h, &globals.norm, cfg.norm_eps);
2873    if want_logits {
2874        logits.resize(globals.head.rows(), 0.0);
2875        globals.head.matvec(&final_h, logits, pool);
2876        trace_stats("final", position, None, &final_h);
2877        trace_stats("logits", position, None, logits);
2878    } else {
2879        logits.clear();
2880    }
2881    prof::add(&prof::HEAD_NS, head_t0);
2882    prof::add(&prof::TOTAL_NS, total_t0);
2883}
2884
2885/// Print the opt-in V4.1 stage profile from the CLI's normal completion path.
2886pub fn profile_report() {
2887    prof::report();
2888}
2889
2890pub fn forward_chunk(
2891    globals: &Dsv41Globals,
2892    layers: &[Dsv41Layer],
2893    cfg: &Dsv41Cfg,
2894    st: &mut Dsv41State,
2895    ids: &[u32],
2896    start: usize,
2897    pool: Option<&Pool>,
2898    logits: &mut Vec<f32>,
2899) {
2900    for (i, &id) in ids.iter().enumerate() {
2901        forward_token_masked_with_embedding(
2902            globals,
2903            layers,
2904            cfg,
2905            st,
2906            id,
2907            start + i,
2908            true,
2909            None,
2910            pool,
2911            logits,
2912            i + 1 == ids.len(),
2913        );
2914    }
2915}
2916
2917/// Prefill variant for multimodal ingress. Each optional row corresponds to
2918/// one token in `ids`; image rows are already produced by `VisionModel` and
2919/// are marked non-participating for Engram/vision routing.
2920pub fn forward_chunk_masked_with_embeddings(
2921    globals: &Dsv41Globals,
2922    layers: &[Dsv41Layer],
2923    cfg: &Dsv41Cfg,
2924    st: &mut Dsv41State,
2925    ids: &[u32],
2926    start: usize,
2927    embeddings: &[Option<Vec<f32>>],
2928    participates: &[bool],
2929    pool: Option<&Pool>,
2930    logits: &mut Vec<f32>,
2931) {
2932    assert_eq!(ids.len(), embeddings.len());
2933    assert_eq!(ids.len(), participates.len());
2934    for (i, &id) in ids.iter().enumerate() {
2935        forward_token_masked_with_embedding(
2936            globals,
2937            layers,
2938            cfg,
2939            st,
2940            id,
2941            start + i,
2942            participates[i],
2943            embeddings[i].as_deref(),
2944            pool,
2945            logits,
2946            i + 1 == ids.len(),
2947        );
2948    }
2949}
2950
2951/// Compact, row-addressable CSA2 global KV storage.
2952///
2953/// V4.1 stores the post-RoPE main KV in MXFP4/E2M1 with one E4M3 scale
2954/// byte per 16 values.  Indexer K uses the same E2M1 nibbles with one E8M0
2955/// scale byte per 32 values.  Rows stay keyed by the shared CSA2 source;
2956/// callers can gather a selected set into one bounded scratch buffer and
2957/// reuse it for every attention head.
2958pub mod packed_kv {
2959    const FP4_LEVELS: [f32; 8] = [0.0, 0.5, 1.0, 1.5, 2.0, 3.0, 4.0, 6.0];
2960    const MAIN_BLOCK: usize = 16;
2961    const INDEX_BLOCK: usize = 32;
2962
2963    /// A dense row store with two E2M1 values per byte and one scale byte per
2964    /// block.  `values` and `scales` are row-major, so an individual row can
2965    /// be decoded without touching any other history entry.
2966    #[derive(Clone, Debug)]
2967    pub struct PackedRows {
2968        cols: usize,
2969        block: usize,
2970        e8m0_scale: bool,
2971        value_bytes: usize,
2972        scale_bytes: usize,
2973        values: Vec<u8>,
2974        scales: Vec<u8>,
2975    }
2976
2977    impl PackedRows {
2978        /// Construct a store. `cols` must be a multiple of `block` because
2979        /// the source MXFP formats carry exactly one scale for each block.
2980        pub fn new(cols: usize, block: usize, e8m0_scale: bool) -> Result<Self, String> {
2981            if cols == 0 || block == 0 || cols % block != 0 {
2982                return Err(format!(
2983                    "packed KV width {cols} must be a positive multiple of block {block}"
2984                ));
2985            }
2986            Ok(Self {
2987                cols,
2988                block,
2989                e8m0_scale,
2990                value_bytes: cols.div_ceil(2),
2991                scale_bytes: cols / block,
2992                values: Vec::new(),
2993                scales: Vec::new(),
2994            })
2995        }
2996
2997        #[inline]
2998        pub fn cols(&self) -> usize {
2999            self.cols
3000        }
3001
3002        #[inline]
3003        pub fn block(&self) -> usize {
3004            self.block
3005        }
3006
3007        #[inline]
3008        pub fn rows(&self) -> usize {
3009            self.scales.len() / self.scale_bytes
3010        }
3011
3012        #[inline]
3013        pub fn row_bytes(&self) -> usize {
3014            self.value_bytes + self.scale_bytes
3015        }
3016
3017        #[inline]
3018        pub fn bytes(&self) -> usize {
3019            self.values.len() + self.scales.len()
3020        }
3021
3022        #[inline]
3023        pub fn clear(&mut self) {
3024            self.values.clear();
3025            self.scales.clear();
3026        }
3027
3028        /// Quantise and append one source row. The returned index is the
3029        /// stable logical row id used by `row_into` and `gather_rows`.
3030        /// Quantisation is deliberately performed here rather than by a
3031        /// caller-provided f32 mirror, so the packed representation itself is
3032        /// the canonical long-context storage.
3033        pub fn push(&mut self, row: &[f32]) -> usize {
3034            assert_eq!(row.len(), self.cols);
3035            let row_id = self.rows();
3036            let value_base = row_id * self.value_bytes;
3037            let scale_base = row_id * self.scale_bytes;
3038            self.values.resize(value_base + self.value_bytes, 0);
3039            self.scales.resize(scale_base + self.scale_bytes, 0);
3040            for block_id in 0..self.scale_bytes {
3041                let begin = block_id * self.block;
3042                let end = begin + self.block;
3043                let min_amax = if self.e8m0_scale {
3044                    6.0 * 2.0f32.powi(-126)
3045                } else {
3046                    6.0 * 2.0f32.powi(-9)
3047                };
3048                let max = row[begin..end]
3049                    .iter()
3050                    .map(|x| x.abs())
3051                    .fold(min_amax, f32::max);
3052                let raw_scale = max / 6.0;
3053                let scale = if self.e8m0_scale {
3054                    super::round_e8m0_scale(raw_scale)
3055                } else {
3056                    super::e4m3_round(raw_scale.max(6.0 * 2.0f32.powi(-9)))
3057                };
3058                self.scales[scale_base + block_id] = if self.e8m0_scale {
3059                    encode_e8m0_scale(scale)
3060                } else {
3061                    encode_e4m3_scale(scale)
3062                };
3063                for i in begin..end {
3064                    let code = fp4_code(row[i] / scale);
3065                    let dst = value_base + i / 2;
3066                    if i & 1 == 0 {
3067                        self.values[dst] = (self.values[dst] & 0xf0) | code;
3068                    } else {
3069                        self.values[dst] = (self.values[dst] & 0x0f) | (code << 4);
3070                    }
3071                }
3072            }
3073            row_id
3074        }
3075
3076        /// Decode one row to the same BF16-materialised f32 values emitted by
3077        /// the source in-place quantiser. A signed E2M1 zero is retained.
3078        pub fn row_into(&self, row: usize, dst: &mut [f32]) -> bool {
3079            if row >= self.rows() || dst.len() != self.cols {
3080                return false;
3081            }
3082            let value_base = row * self.value_bytes;
3083            let scale_base = row * self.scale_bytes;
3084            for block_id in 0..self.scale_bytes {
3085                let scale_byte = self.scales[scale_base + block_id];
3086                let scale = if self.e8m0_scale {
3087                    super::e8m0_scale(scale_byte)
3088                } else {
3089                    super::fp8_e4m3(scale_byte)
3090                };
3091                let begin = block_id * self.block;
3092                let end = begin + self.block;
3093                for i in begin..end {
3094                    let code = if i & 1 == 0 {
3095                        self.values[value_base + i / 2] & 0x0f
3096                    } else {
3097                        self.values[value_base + i / 2] >> 4
3098                    };
3099                    let mag = FP4_LEVELS[(code & 0x07) as usize];
3100                    let signed = if code & 0x08 != 0 { -mag } else { mag };
3101                    // Multiplication by a positive scale does not reliably
3102                    // preserve a negative zero on every compiler path.
3103                    let mut value = signed * scale;
3104                    if mag == 0.0 && code & 0x08 != 0 {
3105                        value = -0.0;
3106                    }
3107                    dst[i] = super::bf16_roundtrip(value);
3108                }
3109            }
3110            true
3111        }
3112
3113        /// Decode the requested rows once into contiguous row-major scratch.
3114        /// The caller can pass that same scratch to all attention heads.
3115        pub fn gather_rows(&self, rows: &[usize], dst: &mut [f32]) -> bool {
3116            if dst.len() != rows.len() * self.cols {
3117                return false;
3118            }
3119            for (i, &row) in rows.iter().enumerate() {
3120                if !self.row_into(row, &mut dst[i * self.cols..(i + 1) * self.cols]) {
3121                    return false;
3122                }
3123            }
3124            true
3125        }
3126
3127        /// Decode all rows into caller-owned scratch. This is intended for
3128        /// bounded diagnostic/export paths; attention should use `gather_rows`.
3129        pub fn all_into(&self, dst: &mut [f32]) -> bool {
3130            if dst.len() != self.rows() * self.cols {
3131                return false;
3132            }
3133            self.gather_rows(&(0..self.rows()).collect::<Vec<_>>(), dst)
3134        }
3135    }
3136
3137    /// The two CSA2 global streams. Main and index rows are each keyed by
3138    /// the source slot, so cross-layer reuse never duplicates history.
3139    #[derive(Clone, Debug)]
3140    pub struct Dsv41PackedKvCache {
3141        main: Vec<PackedRows>,
3142        index: Vec<PackedRows>,
3143    }
3144
3145    impl Dsv41PackedKvCache {
3146        pub fn new(
3147            source_count: usize,
3148            main_cols: usize,
3149            index_cols: usize,
3150        ) -> Result<Self, String> {
3151            let mut main = Vec::with_capacity(source_count);
3152            let mut index = Vec::with_capacity(source_count);
3153            for _ in 0..source_count {
3154                main.push(PackedRows::new(main_cols, MAIN_BLOCK, false)?);
3155                index.push(PackedRows::new(index_cols, INDEX_BLOCK, true)?);
3156            }
3157            Ok(Self { main, index })
3158        }
3159
3160        #[inline]
3161        pub fn main_store(&self, source: usize) -> Option<&PackedRows> {
3162            self.main.get(source)
3163        }
3164
3165        #[inline]
3166        pub fn index_store(&self, source: usize) -> Option<&PackedRows> {
3167            self.index.get(source)
3168        }
3169
3170        #[inline]
3171        pub fn main_rows(&self, source: usize) -> usize {
3172            self.main_store(source).map_or(0, PackedRows::rows)
3173        }
3174
3175        #[inline]
3176        pub fn index_rows(&self, source: usize) -> usize {
3177            self.index_store(source).map_or(0, PackedRows::rows)
3178        }
3179
3180        #[inline]
3181        pub fn main_row_bytes(&self, source: usize) -> usize {
3182            self.main_store(source).map_or(0, PackedRows::row_bytes)
3183        }
3184
3185        #[inline]
3186        pub fn index_row_bytes(&self, source: usize) -> usize {
3187            self.index_store(source).map_or(0, PackedRows::row_bytes)
3188        }
3189
3190        #[inline]
3191        pub fn main_bytes(&self) -> usize {
3192            self.main.iter().map(PackedRows::bytes).sum()
3193        }
3194
3195        #[inline]
3196        pub fn index_bytes(&self) -> usize {
3197            self.index.iter().map(PackedRows::bytes).sum()
3198        }
3199
3200        #[inline]
3201        pub fn bytes(&self) -> usize {
3202            self.main_bytes() + self.index_bytes()
3203        }
3204
3205        pub fn clear(&mut self) {
3206            for store in &mut self.main {
3207                store.clear();
3208            }
3209            for store in &mut self.index {
3210                store.clear();
3211            }
3212        }
3213
3214        pub fn push_main(&mut self, source: usize, row: &[f32]) -> Option<usize> {
3215            self.main.get_mut(source).map(|store| store.push(row))
3216        }
3217
3218        pub fn push_index(&mut self, source: usize, row: &[f32]) -> Option<usize> {
3219            self.index.get_mut(source).map(|store| store.push(row))
3220        }
3221
3222        pub fn main_row_into(&self, source: usize, row: usize, dst: &mut [f32]) -> bool {
3223            self.main_store(source)
3224                .is_some_and(|store| store.row_into(row, dst))
3225        }
3226
3227        pub fn index_row_into(&self, source: usize, row: usize, dst: &mut [f32]) -> bool {
3228            self.index_store(source)
3229                .is_some_and(|store| store.row_into(row, dst))
3230        }
3231
3232        /// Narrow GPU/CPU adapter: decode selected main rows exactly once.
3233        pub fn gather_main_rows(&self, source: usize, rows: &[usize], dst: &mut [f32]) -> bool {
3234            self.main_store(source)
3235                .is_some_and(|store| store.gather_rows(rows, dst))
3236        }
3237
3238        /// Narrow indexer adapter; index scoring can stream one row at a time
3239        /// through `index_store` to avoid a history-sized f32 temporary.
3240        pub fn gather_index_rows(&self, source: usize, rows: &[usize], dst: &mut [f32]) -> bool {
3241            self.index_store(source)
3242                .is_some_and(|store| store.gather_rows(rows, dst))
3243        }
3244    }
3245
3246    fn fp4_code(value: f32) -> u8 {
3247        let q = super::fp4_round(value);
3248        let sign = if q.is_sign_negative() { 0x08 } else { 0 };
3249        let aq = q.abs();
3250        let mag = FP4_LEVELS
3251            .iter()
3252            .position(|&level| level.to_bits() == aq.to_bits())
3253            .unwrap_or(7);
3254        sign | mag as u8
3255    }
3256
3257    fn encode_e4m3_scale(scale: f32) -> u8 {
3258        // The source scale is already the result of e4m3_round. Exact lookup
3259        // avoids introducing a second tie rule in the storage adapter.
3260        (0u8..=0x7e)
3261            .find(|&byte| super::fp8_e4m3(byte).to_bits() == scale.to_bits())
3262            .unwrap_or_else(|| panic!("non-E4M3 scale {scale:?}"))
3263    }
3264
3265    fn encode_e8m0_scale(scale: f32) -> u8 {
3266        if !scale.is_finite() || scale <= 0.0 {
3267            return 0;
3268        }
3269        let exponent = scale.log2().round() as i32;
3270        (exponent + 127).clamp(0, 254) as u8
3271    }
3272}
3273#[cfg(test)]
3274mod tests {
3275    use super::*;
3276
3277    #[test]
3278    fn fp8_reference_points() {
3279        assert_eq!(fp8_e4m3(0), 0.0);
3280        assert_eq!(fp8_e4m3(0x38), 1.0);
3281        assert_eq!(fp8_e4m3(0xb8), -1.0);
3282        assert_eq!(fp8_e4m3(0x3c), 1.5);
3283        assert_eq!(fp8_e4m3(0x7e), 448.0);
3284        assert!(fp8_e4m3(0x7f).is_nan());
3285        assert_eq!(e8m0_scale(127), 1.0);
3286        assert_eq!(e8m0_scale(128), 2.0);
3287    }
3288
3289    #[test]
3290    fn fp8_rounds_finite_top_band_and_ties_even() {
3291        // Exponent field 15 has six finite mantissas.  The old scalar path
3292        // returned 448 for every value in this band, which corrupted 256..416.
3293        for (input, expected) in [
3294            (256.0, 256.0),
3295            (288.0, 288.0),
3296            (320.0, 320.0),
3297            (352.0, 352.0),
3298            (384.0, 384.0),
3299            (416.0, 416.0),
3300            (448.0, 448.0),
3301            (480.0, 448.0),
3302            (-320.0, -320.0),
3303        ] {
3304            assert_eq!(e4m3_round(input), expected, "input={input}");
3305        }
3306        // Half-way values choose the even mantissa, including the
3307        // subnormal-to-normal carry at 8 * 2^-9.
3308        let q = 2.0f32.powi(-9);
3309        assert_eq!(e4m3_round(0.5 * q), 0.0);
3310        assert_eq!(e4m3_round(1.5 * q), 2.0 * q);
3311        assert_eq!(e4m3_round(7.5 * q), 8.0 * q);
3312        assert_eq!(e4m3_round(272.0), 256.0);
3313        assert_eq!(e4m3_round(304.0), 320.0);
3314        assert_eq!(e4m3_round(432.0), 448.0);
3315    }
3316
3317    #[test]
3318    fn fp4_rounds_ties_to_even_level() {
3319        assert_eq!(fp4_round(0.25), 0.0);
3320        assert_eq!(fp4_round(0.75), 1.0);
3321        assert_eq!(fp4_round(1.25), 1.0);
3322        assert_eq!(fp4_round(1.75), 2.0);
3323        assert_eq!(fp4_round(2.5), 2.0);
3324        assert_eq!(fp4_round(3.5), 4.0);
3325        assert_eq!(fp4_round(5.0), 4.0);
3326        assert_eq!(fp4_round(-0.75), -1.0);
3327    }
3328
3329    #[test]
3330    fn release_hash_multipliers_are_stable() {
3331        assert_eq!(
3332            hash_multipliers(1, 4, 99092),
3333            [
3334                76632096046245,
3335                4839876093313,
3336                35959672319349,
3337                73987337458391
3338            ]
3339        );
3340        assert_eq!(
3341            hash_multipliers(14, 4, 99092),
3342            [
3343                67716810739261,
3344                51510806800915,
3345                30921347202721,
3346                82619226485591
3347            ]
3348        );
3349    }
3350
3351    #[test]
3352    fn release_hash_layout_is_disjoint_and_stable() {
3353        let h = EngramHash::new(
3354            vec![1, 14],
3355            4,
3356            8,
3357            16_000_000,
3358            99_092,
3359            2,
3360            (0..99_092).map(|v| v as u32).collect(),
3361        )
3362        .unwrap();
3363        assert_eq!(
3364            h.primes[0][0],
3365            [
3366                16_000_057, 16_000_079, 16_000_081, 16_000_097, 16_000_121, 16_000_129, 16_000_133,
3367                16_000_183,
3368            ]
3369        );
3370        // `offsets[n]` is the flattened start of n-gram order n.  Each
3371        // order owns eight disjoint prime ranges, so the starts are the
3372        // cumulative sums before orders 1, 2, and 3 in the oracle layout.
3373        assert_eq!(h.offsets[0], [0, 128_000_880, 256_002_934]);
3374        assert_eq!(h.primes[1][2][7], 16_000_889);
3375        assert_eq!(h.offsets[1], [0, 128_004_290, 256_009_984]);
3376    }
3377
3378    #[test]
3379    fn dead_tokens_break_ngrams() {
3380        let map = (0..16).collect::<Vec<u32>>();
3381        let mut h = EngramHash::new(vec![1], 4, 2, 16, 16, 2, map).unwrap();
3382        let first = h.push(3, true);
3383        let second = h.push(4, false);
3384        let third = h.push(5, true);
3385        assert_ne!(first, second);
3386        assert_ne!(second, third);
3387        assert_eq!(h.history.last().copied(), Some(5));
3388    }
3389
3390    #[test]
3391    fn candidate_mask_is_bounded() {
3392        let mut scores = vec![f32::NEG_INFINITY; 32];
3393        scores[1] = 1.0;
3394        scores[2] = 2.0;
3395        scores[9] = 3.0;
3396        scores[31] = 0.0;
3397        let m = candidate_blocks(&scores, 2, 8);
3398        assert_eq!(m.len(), 32);
3399        assert!(m.iter().filter(|&&x| x).count() <= 24);
3400    }
3401
3402    #[test]
3403    fn split_attention_matches_materialized_stream() {
3404        let window = vec![
3405            0.10, 0.20, 0.30, 0.40, // window row 0
3406            -0.20, 0.30, 0.50, -0.10, // window row 1
3407        ];
3408        let compressed = vec![
3409            0.70, -0.40, 0.10, 0.20, // compressed row 0
3410            -0.30, 0.60, 0.20, 0.90, // compressed row 1
3411        ];
3412        let idxs = [0, 2, 3];
3413        let mut stream = window.clone();
3414        stream.extend_from_slice(&compressed);
3415        let q = [0.25, -0.10, 0.40, 0.05];
3416        let mut expected = [0.0; 4];
3417        crate::dsv4::sparse_attend(&q, &stream, &idxs, -0.2, 0.5, 4, &mut expected);
3418        let mut actual = [0.0; 4];
3419        sparse_attend_split(
3420            &q,
3421            &window,
3422            Some(&compressed),
3423            &idxs,
3424            -0.2,
3425            0.5,
3426            2,
3427            4,
3428            &mut actual,
3429        );
3430        for (a, b) in actual.iter().zip(expected) {
3431            assert!((a - b).abs() < 1e-7, "split={a} materialized={b}");
3432        }
3433    }
3434
3435    #[test]
3436    fn packed_rows_match_source_fp4_and_preserve_signed_zero() {
3437        let mut main_input = (0..32)
3438            .map(|i| (i as f32 - 13.0) * 0.137)
3439            .collect::<Vec<_>>();
3440        main_input[0] = -0.0;
3441        main_input[1] = 0.25;
3442        main_input[16] = 2688.0;
3443        let mut main_ref = main_input.clone();
3444        fp4_inplace(&mut main_ref, 16, false);
3445        let mut main = packed_kv::PackedRows::new(32, 16, false).unwrap();
3446        assert_eq!(main.push(&main_input), 0);
3447        let mut main_decoded = vec![0.0f32; 32];
3448        assert!(main.row_into(0, &mut main_decoded));
3449        for (i, (&expected, &actual)) in main_ref.iter().zip(&main_decoded).enumerate() {
3450            assert_eq!(expected.to_bits(), actual.to_bits(), "main row value {i}");
3451        }
3452        assert!(main_decoded[0].is_sign_negative());
3453
3454        let mut index_input = (0..64)
3455            .map(|i| ((i as f32 * 0.03125).sin()) * 3.0)
3456            .collect::<Vec<_>>();
3457        index_input[32] = -0.0;
3458        let mut index_ref = index_input.clone();
3459        fp4_inplace(&mut index_ref, 32, true);
3460        let mut index = packed_kv::PackedRows::new(64, 32, true).unwrap();
3461        index.push(&index_input);
3462        let mut index_decoded = vec![0.0f32; 64];
3463        assert!(index.row_into(0, &mut index_decoded));
3464        for (i, (&expected, &actual)) in index_ref.iter().zip(&index_decoded).enumerate() {
3465            assert_eq!(expected.to_bits(), actual.to_bits(), "index row value {i}");
3466        }
3467        assert!(index_decoded[32].is_sign_negative());
3468        assert_eq!(main.row_bytes(), 18);
3469        assert_eq!(index.row_bytes(), 34);
3470    }
3471
3472    #[test]
3473    fn packed_cache_gathers_once_and_tracks_source_rows() {
3474        let mut cache = packed_kv::Dsv41PackedKvCache::new(2, 32, 64).unwrap();
3475        let a = (0..32).map(|i| i as f32 * 0.01).collect::<Vec<_>>();
3476        let b = (0..32).map(|i| -(i as f32) * 0.02).collect::<Vec<_>>();
3477        let k = (0..64)
3478            .map(|i| (i as f32 - 20.0) * 0.03)
3479            .collect::<Vec<_>>();
3480        assert_eq!(cache.push_main(1, &a), Some(0));
3481        assert_eq!(cache.push_main(1, &b), Some(1));
3482        assert_eq!(cache.push_index(1, &k), Some(0));
3483        assert_eq!(cache.main_rows(0), 0);
3484        assert_eq!(cache.main_rows(1), 2);
3485        assert_eq!(cache.index_rows(1), 1);
3486        let mut gathered = vec![0.0f32; 64];
3487        assert!(cache.gather_main_rows(1, &[1, 0], &mut gathered));
3488        let mut row = vec![0.0f32; 32];
3489        assert!(cache.main_row_into(1, 1, &mut row));
3490        assert_eq!(&gathered[..32], &row[..]);
3491        assert_eq!(cache.main_row_bytes(1), 18);
3492        assert_eq!(cache.index_row_bytes(1), 34);
3493        assert_eq!(cache.bytes(), 2 * 18 + 34);
3494    }
3495
3496    #[test]
3497    fn v41_global_row_bytes_match_890_byte_slope() {
3498        let cache = packed_kv::Dsv41PackedKvCache::new(4, 512, 128).unwrap();
3499        assert_eq!(cache.main_row_bytes(0), 512 / 2 + 512 / 16);
3500        assert_eq!(cache.index_row_bytes(0), 128 / 2 + 128 / 32);
3501        // The release source has 2.5 global source rows per token across its
3502        // four CSA2 source layers: 2.5 * (288 + 68) = 890 bytes/token.
3503        assert_eq!(
3504            2.5 * (cache.main_row_bytes(0) + cache.index_row_bytes(0)) as f32,
3505            890.0
3506        );
3507    }
3508
3509    #[test]
3510    fn fp4_scales_keep_reference_zero_floors() {
3511        assert_eq!(round_e8m0_scale(2.0f32.powi(-126)), 2.0f32.powi(-126));
3512        assert_eq!(e4m3_round(2.0f32.powi(-9)), 2.0f32.powi(-9));
3513        let mut index = vec![0.0; 32];
3514        fp4_inplace(&mut index, 32, true);
3515        // E8M0's all-zero block floor is 2^-126, rather than the generic
3516        // activation floor used by the FP8 path.
3517        assert_eq!(index, vec![0.0; 32]);
3518        let mut compressed = vec![0.0; 16];
3519        fp4_inplace(&mut compressed, 16, false);
3520        assert_eq!(compressed, vec![0.0; 16]);
3521    }
3522}