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, PartialEq)]
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    /// Candidate pre-filtering source layer; `None` disables the two-level
774    /// top-k (`candidate_source_layer_id < 0` in the source config).
775    pub candidate_source: Option<usize>,
776    pub candidate_topk_blocks: usize,
777    pub candidate_block_size: usize,
778    pub kv_sources: Vec<usize>,
779    pub index_sources: Vec<usize>,
780    pub compress_ratios: Vec<usize>,
781    pub engram_layers: Vec<usize>,
782    pub engram_vocab: usize,
783    pub engram_embeddings: Vec<usize>,
784    pub engram_max_ngram: usize,
785    pub engram_heads: usize,
786    pub engram_head_dim: usize,
787    pub engram_compressed_vocab: usize,
788    pub engram_pad_id: usize,
789    pub vocab: usize,
790}
791
792impl Dsv41Cfg {
793    pub fn ratio(&self, li: usize) -> usize {
794        self.compress_ratios.get(li).copied().unwrap_or(0)
795    }
796
797    pub fn kv_source(&self, li: usize) -> Option<usize> {
798        self.kv_sources.iter().copied().filter(|&s| s <= li).max()
799    }
800
801    pub fn index_source(&self, li: usize) -> Option<usize> {
802        self.index_sources
803            .iter()
804            .copied()
805            .filter(|&s| s <= li)
806            .max()
807    }
808
809    /// Parse the preserved source `config.json` (the whole multimodal
810    /// object, or its `text_config`) into the runtime geometry. `arch` only
811    /// supplies fallbacks for keys a compact config omits. Returns the
812    /// configuration and the backbone layer count.
813    pub fn from_source(
814        source: &serde_json::Value,
815        arch: &cortiq_core::ModelArch,
816    ) -> Result<(Self, usize), String> {
817        let tc = source.get("text_config").unwrap_or(source);
818        let usize_of = |key: &str, fallback: usize| {
819            tc.get(key)
820                .and_then(|v| v.as_u64())
821                .map(|v| v as usize)
822                .unwrap_or(fallback)
823        };
824        let f32_of = |key: &str, fallback: f32| {
825            tc.get(key)
826                .and_then(|v| v.as_f64())
827                .map(|v| v as f32)
828                .unwrap_or(fallback)
829        };
830        let usize_any = |keys: &[&str], fallback: usize| {
831            keys.iter()
832                .find_map(|key| tc.get(key).and_then(|v| v.as_u64()))
833                .map(|v| v as usize)
834                .unwrap_or(fallback)
835        };
836        let f32_any = |keys: &[&str], fallback: f32| {
837            keys.iter()
838                .find_map(|key| tc.get(key).and_then(|v| v.as_f64()))
839                .map(|v| v as f32)
840                .unwrap_or(fallback)
841        };
842        let bool_of = |key: &str, fallback: bool| {
843            tc.get(key).and_then(|v| v.as_bool()).unwrap_or(fallback)
844        };
845        let array_of = |key: &str| -> Vec<usize> {
846            tc.get(key)
847                .and_then(|v| v.as_array())
848                .map(|a| {
849                    a.iter()
850                        .filter_map(|v| v.as_u64().map(|x| x as usize))
851                        .collect()
852                })
853                .unwrap_or_default()
854        };
855        let array_alias = |keys: &[&str]| -> Vec<usize> {
856            keys.iter()
857                .find_map(|key| {
858                    let values = array_of(key);
859                    (!values.is_empty()).then_some(values)
860                })
861                .unwrap_or_default()
862        };
863        let dim = usize_any(&["hidden_size", "dim"], arch.hidden_size);
864        let n_layers = usize_any(&["num_hidden_layers", "n_layers"], arch.num_layers);
865        let n_heads = usize_any(
866            &["num_attention_heads", "n_heads"],
867            arch.num_attention_heads,
868        );
869        let head_dim = usize_any(&["head_dim"], arch.head_dim);
870        let rope_head_dim = usize_any(&["rope_head_dim", "qk_rope_head_dim"], 64.min(head_dim));
871        let moe_inter = usize_any(
872            &[
873                "moe_intermediate_size",
874                "moe_inter_dim",
875                "intermediate_size",
876            ],
877            arch.intermediate_size,
878        );
879        let n_experts = usize_any(
880            &["n_routed_experts"],
881            arch.moe.as_ref().map(|m| m.num_experts).unwrap_or(384),
882        );
883        let top_k = usize_any(
884            &["num_experts_per_tok", "n_activated_experts"],
885            arch.moe.as_ref().map(|m| m.top_k).unwrap_or(6),
886        );
887        let mut ratios = array_of("compress_ratios");
888        if ratios.len() >= n_layers {
889            ratios.truncate(n_layers);
890        } else {
891            ratios = (0..n_layers)
892                .map(|li| {
893                    if (2..20).contains(&li) {
894                        2
895                    } else if (20..40).contains(&li) {
896                        1
897                    } else {
898                        0
899                    }
900                })
901                .collect();
902        }
903        let kv_sources = {
904            let a = array_alias(&["kv_source_layers", "kv_source_layer_ids"]);
905            if a.is_empty() { vec![2, 8, 14, 20] } else { a }
906        };
907        let index_sources = {
908            let a = array_alias(&["index_source_layers", "index_source_layer_ids"]);
909            if a.is_empty() {
910                vec![2, 8, 14, 20, 24, 28, 32, 36]
911            } else {
912                a
913            }
914        };
915        // The router: V4.1 scores experts with sqrt(softplus(logits)). The
916        // reference also implements softmax and sigmoid scoring; this runtime
917        // (CPU and the preweighted GPU bank) implements only sqrtsoftplus, so
918        // any other value is refused rather than routed with the wrong rule.
919        // An absent key is the release (and the reference default).
920        if let Some(func) = tc.get("scoring_func").or_else(|| tc.get("score_func")) {
921            if func.as_str() != Some("sqrtsoftplus") {
922                return Err(format!(
923                    "text_config.scoring_func = {func} is not supported; the DeepSeek-V4.1 \
924                     runtime implements only \"sqrtsoftplus\" routing (sqrt(softplus(logits)))"
925                ));
926            }
927        }
928        // `candidate_source_layer < 0 turns it off` (reference ModelArgs). An
929        // absent key keeps the release layer, like every other default here;
930        // a null one counts as absent (the other spelling is then read). An
931        // integral float (`20.0`, as some JSON writers emit) is an integer.
932        let candidate_source = match ["candidate_source_layer", "candidate_source_layer_id"]
933            .iter()
934            .find_map(|key| tc.get(*key).filter(|v| !v.is_null()).map(|v| (*key, v)))
935        {
936            None => Some(20),
937            Some((key, v)) => {
938                let layer = v.as_i64().or_else(|| {
939                    v.as_f64()
940                        .filter(|f| f.fract() == 0.0 && f.abs() < (1u64 << 53) as f64)
941                        .map(|f| f as i64)
942                });
943                match layer {
944                    Some(layer) if layer < 0 => None,
945                    Some(layer) => Some(layer as usize),
946                    None => {
947                        return Err(format!("text_config.{key} = {v} is not an integer"));
948                    }
949                }
950            }
951        };
952        let engram_layers = array_of("engram_layer_ids");
953        let engram_embeddings = array_of("engram_num_embeddings");
954        let cfg = Dsv41Cfg {
955            dim,
956            n_heads,
957            head_dim,
958            rope_head_dim: rope_head_dim.min(head_dim) & !1,
959            q_lora_rank: usize_of("q_lora_rank", 1280),
960            o_lora_rank: usize_of("o_lora_rank", 1024),
961            o_groups: usize_of("o_groups", 8),
962            hc_mult: usize_of("hc_mult", 4),
963            hc_sinkhorn_iters: usize_of("hc_sinkhorn_iters", 20),
964            hc_eps: f32_of("hc_eps", 1e-6),
965            norm_eps: f32_any(&["norm_eps", "rms_norm_eps"], arch.rms_norm_eps as f32),
966            n_routed_experts: n_experts,
967            top_k,
968            moe_inter,
969            gate_temp: f32_of("gate_temp", 1.0),
970            norm_topk_prob: bool_of("norm_topk_prob", true),
971            route_scale: f32_any(
972                &["routed_scaling_factor", "route_scale"],
973                arch.moe
974                    .as_ref()
975                    .and_then(|m| m.routed_scaling_factor)
976                    .unwrap_or(1.5),
977            ),
978            swiglu_limit: f32_of("swiglu_limit", 10.0),
979            window: usize_any(
980                &["window_size", "sliding_window"],
981                arch.sliding_window.unwrap_or(128),
982            ),
983            rope_theta: f32_any(&["rope_theta"], arch.rope_theta as f32),
984            compress_rope_theta: f32_any(&["compress_rope_theta"], 160_000.0),
985            rope_factor: f32_any(
986                &["rope_factor"],
987                tc.get("rope_scaling")
988                    .and_then(|v| v.get("factor"))
989                    .and_then(|v| v.as_f64())
990                    .map(|v| v as f32)
991                    .unwrap_or(16.0),
992            ),
993            original_seq_len: usize_any(
994                &["original_seq_len", "original_max_position_embeddings"],
995                tc.get("rope_scaling")
996                    .and_then(|v| v.get("original_max_position_embeddings"))
997                    .and_then(|v| v.as_u64())
998                    .map(|v| v as usize)
999                    .unwrap_or(65_536),
1000            ),
1001            beta_fast: f32_any(
1002                &["beta_fast"],
1003                tc.get("rope_scaling")
1004                    .and_then(|v| v.get("beta_fast"))
1005                    .and_then(|v| v.as_f64())
1006                    .map(|v| v as f32)
1007                    .unwrap_or(32.0),
1008            ),
1009            beta_slow: f32_any(
1010                &["beta_slow"],
1011                tc.get("rope_scaling")
1012                    .and_then(|v| v.get("beta_slow"))
1013                    .and_then(|v| v.as_f64())
1014                    .map(|v| v as f32)
1015                    .unwrap_or(1.0),
1016            ),
1017            index_heads: usize_any(&["index_n_heads", "indexer_n_heads"], 32),
1018            index_head_dim: usize_any(&["index_head_dim", "indexer_head_dim"], 128),
1019            index_topk: usize_any(&["index_topk", "indexer_topk"], 512),
1020            candidate_source,
1021            candidate_topk_blocks: usize_any(&["candidate_topk_blocks"], 2048),
1022            candidate_block_size: usize_any(&["candidate_block_size"], 8),
1023            kv_sources,
1024            index_sources,
1025            compress_ratios: ratios,
1026            engram_layers,
1027            engram_vocab: usize_any(&["engram_vocab_size"], 16_000_000),
1028            engram_embeddings,
1029            engram_max_ngram: usize_any(&["engram_max_ngram_size"], 4),
1030            engram_heads: usize_any(&["engram_n_heads"], 8),
1031            engram_head_dim: usize_any(&["engram_head_dim"], 256),
1032            engram_compressed_vocab: usize_any(&["engram_compressed_vocab_size"], 99_092),
1033            engram_pad_id: usize_any(&["engram_pad_id", "engram_pad_token_id"], 2),
1034            vocab: usize_any(&["vocab_size"], arch.vocab_size),
1035        };
1036        Ok((cfg, n_layers))
1037    }
1038}
1039
1040pub struct Dsv41Compressor {
1041    pub wkv: QTensor,
1042    pub wgate: Option<QTensor>,
1043    pub norm: Vec<f32>,
1044    pub ratio: usize,
1045}
1046
1047pub struct Dsv41Indexer {
1048    pub wq_b: QTensor,
1049    pub weights_proj: QTensor,
1050    pub wk: Option<QTensor>,
1051    pub k_norm: Option<Vec<f32>>,
1052}
1053
1054pub struct Dsv41Expert {
1055    pub w1: QTensor,
1056    pub w2: QTensor,
1057    pub w3: QTensor,
1058}
1059
1060pub struct Dsv41Engram {
1061    pub embed: RawFp8Rows,
1062    pub wkv: QTensor,
1063    pub q_weight: Vec<f32>,
1064    pub k_weight: Vec<f32>,
1065}
1066
1067pub struct Dsv41Layer {
1068    pub attn_norm: Vec<f32>,
1069    pub ffn_norm: Vec<f32>,
1070    pub wq_a: QTensor,
1071    pub q_norm: Vec<f32>,
1072    pub wq_b: QTensor,
1073    pub wkv: QTensor,
1074    pub kv_norm: Vec<f32>,
1075    pub wo_a: QTensor,
1076    pub wo_b: QTensor,
1077    pub attn_sink: Vec<f32>,
1078    pub compressor: Option<Dsv41Compressor>,
1079    pub indexer: Option<Dsv41Indexer>,
1080    pub hc_attn_fn: Vec<f32>,
1081    pub hc_attn_base: Vec<f32>,
1082    pub hc_attn_scale: [f32; 3],
1083    pub hc_ffn_fn: Vec<f32>,
1084    pub hc_ffn_base: Vec<f32>,
1085    pub hc_ffn_scale: [f32; 3],
1086    pub gate: QTensor,
1087    pub gate_bias: Vec<f32>,
1088    pub gate_bias_vl: Option<Vec<f32>>,
1089    pub experts: Vec<Dsv41Expert>,
1090    pub shared: Dsv41Expert,
1091    pub engram: Option<Dsv41Engram>,
1092    /// Directory triples used by the shared DSV4 dynamic MoE bank.  An
1093    /// empty table means a synthetic/F32 fixture and deliberately falls
1094    /// back to the exact host expert loop.
1095    gpu_expert_ids: Vec<(usize, usize, usize)>,
1096    gpu_shared_ids: Option<(usize, usize, usize)>,
1097}
1098
1099pub struct Dsv41Globals {
1100    pub embed: QTensor,
1101    pub norm: Vec<f32>,
1102    pub head: QTensor,
1103    pub inv_freq_compress: Vec<f32>,
1104    pub inv_freq_window: Vec<f32>,
1105}
1106
1107/// Sequence state. `compressed` and `index_k` are keyed by source index,
1108/// rather than by every consumer layer; this is the CSA2 memory bound.
1109pub struct Dsv41State {
1110    pub pos: usize,
1111    pub window: Vec<Vec<f32>>,
1112    /// Compact global main KV and indexer-K streams keyed by CSA2 source.
1113    /// Rows are decoded only into bounded caller-owned scratch.
1114    pub packed: packed_kv::Dsv41PackedKvCache,
1115    pub pending_kv: Vec<Vec<f32>>,
1116    pub pending_score: Vec<Vec<f32>>,
1117    pub topk: Vec<usize>,
1118    /// Whether an index-source layer has published the shared sparse
1119    /// positions for the current sequence. An empty published list means
1120    /// "attend no compressed rows"; it must not be confused with the
1121    /// pre-indexer state, where the reference has no list yet and consumers
1122    /// use every visible row.
1123    pub topk_ready: bool,
1124    pub candidates: Vec<bool>,
1125    pub hash: Option<EngramHash>,
1126    /// Distinct key for the persistent device attention cache. DSV4 and
1127    /// DSV4.1 share the wgpu cache implementation but use different logical
1128    /// row contracts, so their sequence ids must never collide.
1129    #[cfg(feature = "gpu")]
1130    gpu_kv_id: u64,
1131    /// Model-wide segmented expert residency survives sequence resets, as
1132    /// in the established Qwen/Dsv4 dynamic MoE path.  The cache remains
1133    /// bounded by the GPU policy and cold experts complete on the host.
1134    #[cfg(feature = "gpu")]
1135    gpu_pool: Option<crate::qwen4_exp::QwenGpuPool>,
1136    /// MoE calls the dynamic GPU expert bank served (diagnostics; survives
1137    /// [`Dsv41State::clear`] like the bank).
1138    #[cfg(feature = "gpu")]
1139    gpu_moe_calls: u64,
1140    /// GPTQ calibration capture (`cortiq quantize-moe`); `None` outside a
1141    /// capture. Survives [`Dsv41State::clear`]: [`capture_window`] owns it.
1142    pub moe_capture: Option<Dsv41MoeCapture>,
1143}
1144
1145/// What a GPTQ calibration window records at layers `lo..hi`: every
1146/// token's MoE input (the RMS-normed mHC fold that `moe` receives, the input
1147/// of the routed experts and of the shared expert) and the experts its route
1148/// selected. Routing is resolved on the host in both the CPU and the dynamic
1149/// GPU MoE paths, so the record is taken before either runs.
1150#[derive(Debug, Clone, Default)]
1151pub struct Dsv41MoeCapture {
1152    pub lo: usize,
1153    pub hi: usize,
1154    /// End each token after layer `hi - 1`. No layer reads state written by
1155    /// a later layer (KV, index and candidate sources sit at or below their
1156    /// consumers), so the captured rows are the same and the rest of the
1157    /// stack and the head are skipped.
1158    pub stop_after_hi: bool,
1159    /// Per captured layer: `dim` f16 values per token.
1160    pub x: Vec<Vec<u16>>,
1161    /// Per captured layer: `top_k` routed expert ids per token, in route
1162    /// order (`u16::MAX` pads a route cut short by non-finite scores).
1163    pub picks: Vec<Vec<u16>>,
1164    /// Per captured layer: the route weight of each pick (the factor the
1165    /// expert's SwiGLU activation is multiplied by before `w2`, as in the
1166    /// reference `Expert.forward`; 0 for a padded pick).
1167    pub w: Vec<Vec<f32>>,
1168}
1169
1170impl Dsv41State {
1171    pub fn new(cfg: &Dsv41Cfg, hash: Option<EngramHash>) -> Self {
1172        #[cfg(feature = "gpu")]
1173        let gpu_kv_id = {
1174            use std::sync::atomic::{AtomicU64, Ordering};
1175            static NEXT: AtomicU64 = AtomicU64::new(1);
1176            (0xD541u64 << 48) | NEXT.fetch_add(1, Ordering::Relaxed)
1177        };
1178        Self {
1179            pos: 0,
1180            window: vec![Vec::new(); cfg.compress_ratios.len()],
1181            packed: packed_kv::Dsv41PackedKvCache::new(
1182                cfg.kv_sources.len(),
1183                cfg.head_dim,
1184                cfg.index_head_dim,
1185            )
1186            .expect("valid V4.1 packed KV geometry"),
1187            pending_kv: vec![Vec::new(); cfg.kv_sources.len()],
1188            pending_score: vec![Vec::new(); cfg.kv_sources.len()],
1189            topk: Vec::new(),
1190            topk_ready: false,
1191            candidates: Vec::new(),
1192            hash,
1193            #[cfg(feature = "gpu")]
1194            gpu_kv_id,
1195            #[cfg(feature = "gpu")]
1196            gpu_pool: None,
1197            #[cfg(feature = "gpu")]
1198            gpu_moe_calls: 0,
1199            moe_capture: None,
1200        }
1201    }
1202
1203    /// The dynamic GPU MoE as this state saw it: how many MoE calls the
1204    /// device expert bank served, and the bank's codec (`q4tp`, `q2tp` or
1205    /// `mxfp4`; None until a bank exists). Diagnostics for tests and
1206    /// reports — counted with or without `CMF_DSV41_PROF`.
1207    #[cfg(feature = "gpu")]
1208    pub fn gpu_moe_stats(&self) -> (u64, Option<&'static str>) {
1209        (
1210            self.gpu_moe_calls,
1211            self.gpu_pool.as_ref().map(|p| p.codec.label()),
1212        )
1213    }
1214
1215    pub fn clear(&mut self) {
1216        #[cfg(feature = "gpu")]
1217        crate::gpu_wgpu::dsv4_cache_clear(self.gpu_kv_id);
1218        self.pos = 0;
1219        for v in &mut self.window {
1220            v.clear();
1221        }
1222        self.packed.clear();
1223        for v in &mut self.pending_kv {
1224            v.clear();
1225        }
1226        for v in &mut self.pending_score {
1227            v.clear();
1228        }
1229        self.topk.clear();
1230        self.topk_ready = false;
1231        self.candidates.clear();
1232        if let Some(h) = &mut self.hash {
1233            h.reset();
1234        }
1235    }
1236}
1237
1238fn source_slot(sources: &[usize], li: usize) -> Option<usize> {
1239    sources
1240        .iter()
1241        .enumerate()
1242        .filter(|&(_, &s)| s <= li)
1243        .map(|(i, _)| i)
1244        .max()
1245}
1246
1247fn rms(v: &mut [f32], w: &[f32], eps: f32) {
1248    // RMSNorm receives a BF16 tensor in the reference and returns another
1249    // BF16 tensor.  Rounding the input matters for folded residuals, while
1250    // rounding the result matters for every subsequent projection.
1251    bf16_inplace(v);
1252    crate::dsv4::rms_weighted(v, w, eps);
1253    bf16_inplace(v);
1254}
1255
1256fn matvec(t: &QTensor, x: &[f32], out: &mut [f32], pool: Option<&Pool>) {
1257    t.matvec(x, out, pool);
1258}
1259
1260#[inline]
1261fn matvec_bf16(t: &QTensor, x: &[f32], out: &mut [f32], pool: Option<&Pool>) {
1262    t.matvec(x, out, pool);
1263    bf16_inplace(out);
1264}
1265
1266/// Opt-in V4.1 query fusion. The default remains the host-materialized query
1267/// path until the parent promotes the matched GPU/CPU profile. With this flag
1268/// the DSV4 frame consumes `qr` directly and performs wq_b, BF16 materializing,
1269/// and forward RoPE in its existing single encoder/readback.
1270#[inline]
1271fn v41_fused_q_enabled() -> bool {
1272    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
1273    *ON.get_or_init(|| std::env::var("CMF_DSV41_FUSED_Q").is_ok_and(|value| value == "1"))
1274}
1275
1276/// The V4.1 tail uses the proven DSV4 frame only when the selected backend is
1277/// live. The default follows the ordinary GPU selection; an explicit zero is
1278/// useful for the CPU reference and for the paired numerical proof.
1279#[cfg(feature = "gpu")]
1280fn gpu_attention_tail_enabled() -> bool {
1281    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
1282    *ON.get_or_init(|| {
1283        let requested = std::env::var("CMF_DSV41_GPU_ATTN")
1284            .map(|v| v != "0")
1285            .unwrap_or(true);
1286        let live = requested && crate::gpu::enabled_here() && crate::gpu_wgpu::adapter_up();
1287        if requested && !live && std::env::var("CMF_DSV41_GPU_ATTN").is_ok() {
1288            tracing::debug!("V4.1 GPU attention tail unavailable; retaining the CPU path");
1289        }
1290        live
1291    })
1292}
1293
1294/// Test-only mutation of the device attention tail
1295/// (`CMF_DSV41_TEST_MUTATE`): `sink` hands the frame zero attention sinks,
1296/// `rope` shifts the position the frame's inverse RoPE uses by one (the
1297/// host has already rotated the query unless `CMF_DSV41_FUSED_Q=1`). Each is
1298/// a broken device path the GPU fixture's bound has to catch; see
1299/// `tests/dsv41_fixture_gpu.rs`. Read once per process, and only in builds
1300/// with debug assertions (`cargo test`'s default profile): a release binary
1301/// never reads the variable, so a stray one cannot corrupt its output.
1302#[cfg(feature = "gpu")]
1303fn gpu_tail_mutation() -> Option<&'static str> {
1304    if !cfg!(debug_assertions) {
1305        return None;
1306    }
1307    static M: std::sync::OnceLock<Option<String>> = std::sync::OnceLock::new();
1308    M.get_or_init(|| {
1309        let m = std::env::var("CMF_DSV41_TEST_MUTATE").ok()?;
1310        matches!(m.as_str(), "sink" | "rope").then_some(m)
1311    })
1312    .as_deref()
1313}
1314
1315/// One-shot stage capture for the V4.1 adapter repair. The frame reads the
1316/// same tap names as the generic DSV4 proof, but the adapter falls through to
1317/// the CPU path after logging the GPU value so one call carries both sides.
1318fn gpu_tail_tap() -> Option<&'static str> {
1319    static TAP: std::sync::OnceLock<Option<String>> = std::sync::OnceLock::new();
1320    TAP.get_or_init(|| {
1321        let tap = std::env::var("CMF_DSV41_TAIL_TAP").ok()?;
1322        matches!(tap.as_str(), "q" | "attn" | "mid" | "final").then_some(tap)
1323    })
1324    .as_deref()
1325}
1326
1327fn f_any(model: &CmfModel, names: &[String]) -> Result<Vec<f32>, String> {
1328    for n in names {
1329        if model.tensor(n).is_some() {
1330            return crate::loader::load_f32(model, n, &crate::loader::Overlay::None);
1331        }
1332    }
1333    Err(format!("missing tensor (tried {})", names.join(", ")))
1334}
1335
1336fn q_any(model: &Arc<CmfModel>, names: &[String]) -> Result<QTensor, String> {
1337    for n in names {
1338        if model.tensor(n).is_some() {
1339            return QTensor::from_model(model, n);
1340        }
1341    }
1342    Err(format!("missing tensor (tried {})", names.join(", ")))
1343}
1344
1345fn scale3(model: &CmfModel, names: &[String]) -> Result<[f32; 3], String> {
1346    let v = f_any(model, names)?;
1347    if v.len() < 3 {
1348        return Err(format!(
1349            "scale tensor (tried {}) expected three values",
1350            names.join(", ")
1351        ));
1352    }
1353    Ok([v[0], v[1], v[2]])
1354}
1355
1356fn load_expert(model: &Arc<CmfModel>, p: &str, e: usize) -> Result<Dsv41Expert, String> {
1357    let a = format!("{p}.experts.{e}");
1358    let w1 = q_any(
1359        model,
1360        &[format!("{a}.gate_proj.weight"), format!("{a}.w1.weight")],
1361    )?;
1362    let w2 = q_any(
1363        model,
1364        &[format!("{a}.down_proj.weight"), format!("{a}.w2.weight")],
1365    )?;
1366    let w3 = q_any(
1367        model,
1368        &[format!("{a}.up_proj.weight"), format!("{a}.w3.weight")],
1369    )?;
1370    Ok(Dsv41Expert { w1, w2, w3 })
1371}
1372
1373fn load_shared(model: &Arc<CmfModel>, p: &str) -> Result<Dsv41Expert, String> {
1374    let mut names = Vec::new();
1375    for sh in ["shared_expert", "shared_experts"] {
1376        names.push(format!("{p}.{sh}.gate_proj.weight"));
1377        names.push(format!("{p}.{sh}.w1.weight"));
1378    }
1379    let w1 = q_any(model, &names)?;
1380    let mut names2 = Vec::new();
1381    for sh in ["shared_expert", "shared_experts"] {
1382        names2.push(format!("{p}.{sh}.down_proj.weight"));
1383        names2.push(format!("{p}.{sh}.w2.weight"));
1384    }
1385    let w2 = q_any(model, &names2)?;
1386    let mut names3 = Vec::new();
1387    for sh in ["shared_expert", "shared_experts"] {
1388        names3.push(format!("{p}.{sh}.up_proj.weight"));
1389        names3.push(format!("{p}.{sh}.w3.weight"));
1390    }
1391    let w3 = q_any(model, &names3)?;
1392    Ok(Dsv41Expert { w1, w2, w3 })
1393}
1394
1395/// Load one V4.1 layer.  The loader accepts both the canonical names emitted
1396/// by the V4.1 converter and the upstream `ffn.*` spelling, which keeps toy
1397/// fixtures useful while the large source is converted in a separate job.
1398pub fn load_layer(model: &Arc<CmfModel>, cfg: &Dsv41Cfg, li: usize) -> Result<Dsv41Layer, String> {
1399    let p = format!("model.layers.{li}");
1400    let a = format!("{p}.self_attn");
1401    let q = |tail: &str| q_any(model, &[format!("{a}.{tail}"), format!("{p}.attn.{tail}")]);
1402    let attn_norm = f_any(
1403        model,
1404        &[
1405            format!("{p}.input_layernorm.weight"),
1406            format!("{p}.attn_norm.weight"),
1407        ],
1408    )?;
1409    let ffn_norm = f_any(
1410        model,
1411        &[
1412            format!("{p}.post_attention_layernorm.weight"),
1413            format!("{p}.ffn_norm.weight"),
1414        ],
1415    )?;
1416    let wq_a = q("wq_a.weight")?;
1417    let q_norm = f_any(
1418        model,
1419        &[
1420            format!("{a}.q_norm.weight"),
1421            format!("{p}.attn.q_norm.weight"),
1422        ],
1423    )?;
1424    let wq_b = q("wq_b.weight")?;
1425    let wkv = q("wkv.weight")?;
1426    let kv_norm = f_any(model, &[format!("{a}.kv_norm.weight")])?;
1427    let wo_a = q("wo_a.weight")?;
1428    let wo_b = q("wo_b.weight")?;
1429    let attn_sink = f_any(
1430        model,
1431        &[format!("{a}.attn_sink"), format!("{a}.attn_sink.weight")],
1432    )?;
1433
1434    let ratio = cfg.ratio(li);
1435    let compressor = if cfg.kv_sources.contains(&li) {
1436        let cwkv = q_any(
1437            model,
1438            &[
1439                format!("{a}.compressor.wkv.weight"),
1440                format!("{p}.attn.compressor.wkv.weight"),
1441            ],
1442        )?;
1443        let wgate = if ratio > 1 {
1444            Some(q_any(
1445                model,
1446                &[
1447                    format!("{a}.compressor.wgate.weight"),
1448                    format!("{p}.attn.compressor.wgate.weight"),
1449                ],
1450            )?)
1451        } else {
1452            None
1453        };
1454        let norm = f_any(model, &[format!("{a}.compressor.norm.weight")])?;
1455        Some(Dsv41Compressor {
1456            wkv: cwkv,
1457            wgate,
1458            norm,
1459            ratio,
1460        })
1461    } else {
1462        None
1463    };
1464
1465    let indexer = if cfg.index_sources.contains(&li) {
1466        let wq_bi = q_any(
1467            model,
1468            &[
1469                format!("{a}.indexer.wq_b.weight"),
1470                format!("{p}.attn.indexer.wq_b.weight"),
1471            ],
1472        )?;
1473        let weights_proj = q_any(
1474            model,
1475            &[
1476                format!("{a}.indexer.weights_proj.weight"),
1477                format!("{p}.attn.indexer.weights_proj.weight"),
1478            ],
1479        )?;
1480        let owns_k = cfg.kv_sources.contains(&li);
1481        let wk = if owns_k {
1482            Some(q_any(
1483                model,
1484                &[
1485                    format!("{a}.indexer.wk.weight"),
1486                    format!("{p}.attn.indexer.wk.weight"),
1487                ],
1488            )?)
1489        } else {
1490            None
1491        };
1492        let k_norm = if owns_k {
1493            Some(f_any(
1494                model,
1495                &[
1496                    format!("{a}.indexer.k_norm.weight"),
1497                    format!("{p}.attn.indexer.k_norm.weight"),
1498                ],
1499            )?)
1500        } else {
1501            None
1502        };
1503        Some(Dsv41Indexer {
1504            wq_b: wq_bi,
1505            weights_proj,
1506            wk,
1507            k_norm,
1508        })
1509    } else {
1510        None
1511    };
1512
1513    let hc_attn_fn = f_any(model, &[format!("{p}.hc_attn_fn")])?;
1514    let hc_attn_base = f_any(model, &[format!("{p}.hc_attn_base")])?;
1515    let hc_attn_scale = scale3(
1516        model,
1517        &[format!("{p}.hc_attn_scale"), format!("{p}.hc_attn.scale")],
1518    )?;
1519    let hc_ffn_fn = f_any(model, &[format!("{p}.hc_ffn_fn")])?;
1520    let hc_ffn_base = f_any(model, &[format!("{p}.hc_ffn_base")])?;
1521    let hc_ffn_scale = scale3(
1522        model,
1523        &[format!("{p}.hc_ffn_scale"), format!("{p}.hc_ffn.scale")],
1524    )?;
1525
1526    let gate = q_any(
1527        model,
1528        &[
1529            format!("{p}.mlp.gate.weight"),
1530            format!("{p}.ffn.gate.weight"),
1531        ],
1532    )?;
1533    let gate_bias = f_any(
1534        model,
1535        &[
1536            format!("{p}.mlp.expert_bias"),
1537            format!("{p}.ffn.gate.bias"),
1538            format!("{p}.mlp.gate.bias"),
1539        ],
1540    )?;
1541    let gate_bias_vl = f_any(
1542        model,
1543        &[
1544            format!("{p}.mlp.expert_bias_vl"),
1545            format!("{p}.ffn.gate.bias_vl"),
1546        ],
1547    )
1548    .ok();
1549    let mut experts = Vec::with_capacity(cfg.n_routed_experts);
1550    for e in 0..cfg.n_routed_experts {
1551        experts.push(
1552            load_expert(model, &format!("{p}.mlp"), e)
1553                .or_else(|_| load_expert(model, &format!("{p}.ffn"), e))?,
1554        );
1555    }
1556    let shared = load_shared(model, &format!("{p}.mlp"))
1557        .or_else(|_| load_shared(model, &format!("{p}.ffn")))?;
1558    let gpu_expert_ids = experts
1559        .iter()
1560        .map(|e| Some((e.w1.model_idx()?, e.w3.model_idx()?, e.w2.model_idx()?)))
1561        .collect::<Option<Vec<_>>>()
1562        .unwrap_or_default();
1563    let gpu_shared_ids = match (
1564        shared.w1.model_idx(),
1565        shared.w3.model_idx(),
1566        shared.w2.model_idx(),
1567    ) {
1568        (Some(w1), Some(w3), Some(w2)) => Some((w1, w3, w2)),
1569        _ => None,
1570    };
1571
1572    let engram = if cfg.engram_layers.contains(&li) {
1573        let ew = format!("{p}.engram.embed.weight");
1574        let es = format!("{p}.engram.embed.scale");
1575        let embed = RawFp8Rows::from_model(model, &ew, &es)?;
1576        if let Some(index) = cfg.engram_layers.iter().position(|&id| id == li) {
1577            if let Some(&expected_rows) = cfg.engram_embeddings.get(index) {
1578                if embed.rows != expected_rows {
1579                    return Err(format!(
1580                        "Engram layer {li} has {} rows, expected {expected_rows}",
1581                        embed.rows
1582                    ));
1583                }
1584            }
1585        }
1586        let wkv_e = q_any(model, &[format!("{p}.engram.wkv.weight")])?;
1587        let q_weight = f_any(model, &[format!("{p}.engram.q_weight")])?;
1588        let k_weight = f_any(model, &[format!("{p}.engram.k_weight")])?;
1589        Some(Dsv41Engram {
1590            embed,
1591            wkv: wkv_e,
1592            q_weight,
1593            k_weight,
1594        })
1595    } else {
1596        None
1597    };
1598
1599    Ok(Dsv41Layer {
1600        attn_norm,
1601        ffn_norm,
1602        wq_a,
1603        q_norm,
1604        wq_b,
1605        wkv,
1606        kv_norm,
1607        wo_a,
1608        wo_b,
1609        attn_sink,
1610        compressor,
1611        indexer,
1612        hc_attn_fn,
1613        hc_attn_base,
1614        hc_attn_scale,
1615        hc_ffn_fn,
1616        hc_ffn_base,
1617        hc_ffn_scale,
1618        gate,
1619        gate_bias,
1620        gate_bias_vl,
1621        experts,
1622        shared,
1623        engram,
1624        gpu_expert_ids,
1625        gpu_shared_ids,
1626    })
1627}
1628
1629/// Build the stack from canonical V4.1 tensors.  Frequency tables are kept
1630/// separate because compressed latents use the 160k base plus YaRN while the
1631/// raw sliding window uses the 10k base without YaRN.
1632pub fn load(
1633    model: &Arc<CmfModel>,
1634    cfg: &Dsv41Cfg,
1635    n_layers: usize,
1636    token_map: Vec<u32>,
1637) -> Result<(Dsv41Globals, Vec<Dsv41Layer>, Option<EngramHash>), String> {
1638    let q = |name: &str| QTensor::from_model(model, name);
1639    let globals = Dsv41Globals {
1640        embed: q("model.embed_tokens.weight")?,
1641        norm: crate::loader::load_f32(model, "model.norm.weight", &crate::loader::Overlay::None)?,
1642        head: q("lm_head.weight")?,
1643        inv_freq_compress: crate::attention::yarn_inv_freq(
1644            cfg.rope_head_dim,
1645            cfg.compress_rope_theta,
1646            cfg.rope_factor,
1647            cfg.original_seq_len,
1648            cfg.beta_fast,
1649            cfg.beta_slow,
1650        ),
1651        inv_freq_window: crate::attention::rope_inv_freq(cfg.rope_head_dim, cfg.rope_theta),
1652    };
1653    let mut layers = Vec::with_capacity(n_layers);
1654    for li in 0..n_layers {
1655        layers.push(load_layer(model, cfg, li)?);
1656    }
1657    // The two Engram tables are ~203 GB of FP8 rows read ~48 rows a token.
1658    // With the default read-around every row fault pulls a 128 KiB window
1659    // and the table crowds the experts out of the page cache on hosts that
1660    // cannot hold the whole file; random advice keeps a fault to its page.
1661    // `CMF_DSV41_ENGRAM_ADVISE=0` keeps the kernel default.
1662    if !cfg.engram_layers.is_empty()
1663        && std::env::var("CMF_DSV41_ENGRAM_ADVISE").as_deref() != Ok("0")
1664    {
1665        let advised = crate::expert_store::advise_random(model, |n| n.contains(".engram.embed."));
1666        tracing::debug!("deepseek_v41: MADV_RANDOM on {} MB of Engram rows", advised >> 20);
1667    }
1668    let hash = if !cfg.engram_layers.is_empty() {
1669        Some(EngramHash::new(
1670            cfg.engram_layers.clone(),
1671            cfg.engram_max_ngram,
1672            cfg.engram_heads,
1673            cfg.engram_vocab,
1674            cfg.engram_compressed_vocab,
1675            cfg.engram_pad_id,
1676            token_map,
1677        )?)
1678    } else {
1679        None
1680    };
1681    Ok((globals, layers, hash))
1682}
1683
1684/// Build the compressed token lookup used by Engram.  The loader calls this
1685/// once; the returned vector is tiny compared with the mmap-backed tables.
1686pub fn token_map_from_model(model: &CmfModel, vocab: usize) -> Vec<u32> {
1687    let tokenizer = model
1688        .vocab
1689        .as_ref()
1690        .and_then(|bytes| crate::tokenizer::Tokenizer::from_bytes(bytes).ok());
1691    let mut out = Vec::with_capacity(vocab);
1692    let mut keys = std::collections::HashMap::<String, u32>::new();
1693    for id in 0..vocab {
1694        let text = tokenizer
1695            .as_ref()
1696            .map(|t| t.decode_token_for_hash(id as u32))
1697            .unwrap_or_default();
1698        // A partial UTF-8 byte token decodes to U+FFFD in the Rust
1699        // tokenizer. The reference keys those entries by their raw backend
1700        // token, otherwise unrelated byte fragments collapse together.
1701        let key = if text.contains('\u{fffd}') {
1702            tokenizer
1703                .as_ref()
1704                .map(|t| t.raw_token_for_hash(id as u32))
1705                .unwrap_or(text)
1706        } else {
1707            normalize_hash_token(&text)
1708        };
1709        let next = keys.len() as u32;
1710        out.push(*keys.entry(key).or_insert(next));
1711    }
1712    out
1713}
1714
1715fn normalize_hash_token(text: &str) -> String {
1716    use unicode_normalization::UnicodeNormalization;
1717    // Match tokenizers' NFKC → NFD → StripAccents sequence. NFKD would
1718    // additionally decompose compatibility characters before composition,
1719    // which changes the compressed-vocabulary cardinality.
1720    let nfd: String = text.nfkc().nfd().collect();
1721    let mut out = String::with_capacity(nfd.len());
1722    let mut last_space = false;
1723    for c in nfd.chars() {
1724        if unicode_normalization::char::is_combining_mark(c) {
1725            continue;
1726        }
1727        if c == ' ' || c == '\t' || c == '\r' || c == '\n' {
1728            if !last_space {
1729                out.push(' ');
1730            }
1731            last_space = true;
1732        } else {
1733            for lower in c.to_lowercase() {
1734                out.push(lower);
1735            }
1736            last_space = false;
1737        }
1738    }
1739    let trimmed = out.trim_matches(' ');
1740    if trimmed.is_empty() && out == " " {
1741        " ".to_string()
1742    } else if trimmed.is_empty() {
1743        text.to_string()
1744    } else {
1745        trimmed.to_string()
1746    }
1747}
1748
1749fn apply_engram(
1750    e: &Dsv41Engram,
1751    h: &mut [f32],
1752    hashes: &[usize],
1753    cfg: &Dsv41Cfg,
1754    token_mask: bool,
1755    pool: Option<&Pool>,
1756) {
1757    if !token_mask {
1758        return;
1759    }
1760    let cols = hashes.len() * e.embed.cols;
1761    let mut emb = vec![0.0f32; cols];
1762    for (i, &row) in hashes.iter().enumerate() {
1763        e.embed.row_into(
1764            row.min(e.embed.rows.saturating_sub(1)),
1765            &mut emb[i * e.embed.cols..(i + 1) * e.embed.cols],
1766        );
1767    }
1768    // ParallelEngramEmbedding returns the dequantized lookup as BF16 before
1769    // the projection.  Keep the raw FP8 row representation, but materialise
1770    // this activation boundary explicitly in the f32 work buffer.
1771    bf16_inplace(&mut emb);
1772    let mut kv = vec![0.0f32; cfg.dim * (cfg.hc_mult + 1)];
1773    // Engram's projection uses the model BF16 dtype in the reference.
1774    matvec_bf16(&e.wkv, &emb, &mut kv, pool);
1775    let key = &kv[..cfg.dim * cfg.hc_mult];
1776    let value = &kv[cfg.dim * cfg.hc_mult..];
1777    for copy in 0..cfg.hc_mult {
1778        let hs = &h[copy * cfg.dim..(copy + 1) * cfg.dim];
1779        let ks = &key[copy * cfg.dim..(copy + 1) * cfg.dim];
1780        let qw = e
1781            .q_weight
1782            .get(copy * cfg.dim..(copy + 1) * cfg.dim)
1783            .unwrap_or(&[]);
1784        let kw = e
1785            .k_weight
1786            .get(copy * cfg.dim..(copy + 1) * cfg.dim)
1787            .unwrap_or(&[]);
1788        let hm = hs.iter().map(|v| v * v).sum::<f32>() / cfg.dim as f32;
1789        let km = ks.iter().map(|v| v * v).sum::<f32>() / cfg.dim as f32;
1790        let rstd = 1.0 / (hm + cfg.norm_eps).sqrt() / (km + cfg.norm_eps).sqrt();
1791        let mut dot = 0.0;
1792        for i in 0..cfg.dim {
1793            dot += hs[i]
1794                * ks[i]
1795                * qw.get(i).copied().unwrap_or(1.0)
1796                * kw.get(i).copied().unwrap_or(1.0);
1797        }
1798        dot *= rstd * (cfg.dim as f32).powf(-0.5);
1799        // torch.copysign(+sqrt(abs(dot)), dot) treats an exact zero as
1800        // positive. `signum()` would turn that case into zero and changes
1801        // the gate from sigmoid(sqrt(1e-6)) to sigmoid(0).
1802        let root = dot.abs().max(1e-6).sqrt();
1803        let gate = sigmoid(if dot.is_sign_negative() { -root } else { root });
1804        let dst = &mut h[copy * cfg.dim..(copy + 1) * cfg.dim];
1805        for i in 0..cfg.dim {
1806            dst[i] += gate * value[i];
1807        }
1808    }
1809    // The source returns `h + gate * value` cast back to the residual dtype;
1810    // leaving this f32 would make the next HC projection consume extra bits.
1811    bf16_inplace(h);
1812}
1813
1814/// Run the isolated Engram projection for the release component oracle.
1815/// Kept hidden from generated documentation; this narrow entry point lets a
1816/// fixture exercise the mmap-backed raw E4M3/E8M0 lookup without constructing
1817/// the rest of a full V4.1 checkpoint.
1818#[doc(hidden)]
1819pub fn dsv41_apply_engram_for_test(
1820    e: &Dsv41Engram,
1821    h: &mut [f32],
1822    hashes: &[usize],
1823    cfg: &Dsv41Cfg,
1824    token_mask: bool,
1825) {
1826    apply_engram(e, h, hashes, cfg, token_mask, None);
1827}
1828
1829fn compressor_step(
1830    cp: &Dsv41Compressor,
1831    x: &[f32],
1832    cfg: &Dsv41Cfg,
1833    pending_kv: &mut Vec<f32>,
1834    pending_score: &mut Vec<f32>,
1835    pool: Option<&Pool>,
1836) -> Option<Vec<f32>> {
1837    let mut kv = vec![0.0f32; cfg.head_dim];
1838    // Ratio-one compressors use the model BF16 linear.  The pooled
1839    // ratio>1 path intentionally keeps both projections in f32.
1840    if cp.ratio == 1 {
1841        matvec_bf16(&cp.wkv, x, &mut kv, pool);
1842    } else {
1843        matvec(&cp.wkv, x, &mut kv, pool);
1844    }
1845    if cp.ratio == 1 {
1846        rms(&mut kv, &cp.norm, cfg.norm_eps);
1847        return Some(kv);
1848    }
1849    let mut score = vec![0.0f32; cfg.head_dim];
1850    if let Some(wg) = &cp.wgate {
1851        matvec(wg, x, &mut score, pool);
1852    }
1853    pending_kv.extend_from_slice(&kv);
1854    pending_score.extend_from_slice(&score);
1855    if pending_kv.len() < cp.ratio * cfg.head_dim {
1856        return None;
1857    }
1858    let mut out = vec![0.0f32; cfg.head_dim];
1859    for d in 0..cfg.head_dim {
1860        let m = (0..cp.ratio)
1861            .map(|i| pending_score[i * cfg.head_dim + d])
1862            .fold(f32::NEG_INFINITY, f32::max);
1863        let mut den = 0.0;
1864        for i in 0..cp.ratio {
1865            den += (pending_score[i * cfg.head_dim + d] - m).exp();
1866        }
1867        if den > 0.0 {
1868            for i in 0..cp.ratio {
1869                out[d] += (pending_score[i * cfg.head_dim + d] - m).exp() / den
1870                    * pending_kv[i * cfg.head_dim + d];
1871            }
1872        }
1873    }
1874    pending_kv.clear();
1875    pending_score.clear();
1876    rms(&mut out, &cp.norm, cfg.norm_eps);
1877    Some(out)
1878}
1879
1880fn fp4_round(x: f32) -> f32 {
1881    let sign = if x.is_sign_negative() { -1.0 } else { 1.0 };
1882    let ax = x.abs();
1883    if !ax.is_finite() {
1884        return sign * 6.0;
1885    }
1886    const LEVELS: [f32; 8] = [0.0, 0.5, 1.0, 1.5, 2.0, 3.0, 4.0, 6.0];
1887    // E2M1 has an alternating even/odd significand at these levels.  The
1888    // hardware/Torch cast is round-to-nearest-even, so a half-way value must
1889    // choose the even level (e.g. 0.75 -> 1, 1.75 -> 2, 3.5 -> 4), rather
1890    // than the lower level selected by a stable min-by tie.
1891    const EVEN: [bool; 8] = [true, false, true, false, true, false, true, false];
1892    let mut best = 0usize;
1893    let mut best_dist = ax;
1894    for i in 1..LEVELS.len() {
1895        let dist = (ax - LEVELS[i]).abs();
1896        if dist < best_dist || (dist == best_dist && EVEN[i] && !EVEN[best]) {
1897            best = i;
1898            best_dist = dist;
1899        }
1900    }
1901    sign * LEVELS[best]
1902}
1903
1904fn fp4_inplace(v: &mut [f32], block: usize, e8m0_scale_fmt: bool) {
1905    for chunk in v.chunks_mut(block) {
1906        // Keep the same nonzero scale floors as the reference kernel.  The
1907        // E8M0 index path permits subnormal 2^-126, while compressed KV's
1908        // E4M3 scale path floors the amax at 6*2^-9 before the FP8 cast.
1909        // A single generic 1e-4 floor silently turns zero index blocks into
1910        // 2^-15 and changes every subsequent dot product.
1911        let min_amax = if e8m0_scale_fmt {
1912            6.0 * 2.0f32.powi(-126)
1913        } else {
1914            6.0 * 2.0f32.powi(-9)
1915        };
1916        let max = chunk.iter().map(|x| x.abs()).fold(min_amax, f32::max);
1917        let raw_scale = max / 6.0;
1918        let scale = if e8m0_scale_fmt {
1919            round_e8m0_scale(raw_scale)
1920        } else {
1921            // E4M3 scales use the same minimum as fp4_quant_kernel: an
1922            // all-zero block still carries 6*2^-9 before the cast.
1923            e4m3_round(raw_scale.max(6.0 * 2.0f32.powi(-9)))
1924        };
1925        if scale == 0.0 || !scale.is_finite() {
1926            chunk.fill(0.0);
1927            continue;
1928        }
1929        for x in chunk {
1930            *x = bf16_roundtrip(fp4_round(*x / scale) * scale);
1931        }
1932    }
1933}
1934
1935/// `fast_log2_ceil` of the reference kernels: the biased exponent plus one
1936/// when any mantissa bit is set. Exact for normal inputs, unlike a rounded
1937/// `log2().ceil()` just above a power of two.
1938#[inline]
1939fn fast_log2_ceil(x: f32) -> i32 {
1940    let bits = x.to_bits();
1941    ((bits >> 23) & 0xff) as i32 - 127 + i32::from(bits & 0x7f_ffff != 0)
1942}
1943
1944/// `act_quant(x, 32, "ue8m0", float8_e8m0fnu, inplace=True)`: per 32-value
1945/// block, `s = 2^ceil(log2(max(amax, 1e-4) / 448))` (the kernel multiplies by
1946/// the f32 constant 1/448), values quantized to E4M3FN (nearest-even,
1947/// saturating) and dequantized, stored as BF16.
1948fn fp8_activation_inplace(v: &mut [f32]) {
1949    const FP8_MAX_INV: f32 = 1.0 / 448.0;
1950    for chunk in v.chunks_mut(32) {
1951        let amax = chunk.iter().map(|x| x.abs()).fold(1e-4f32, f32::max);
1952        let scale = f32::from_bits(((fast_log2_ceil(amax * FP8_MAX_INV) + 127) as u32) << 23);
1953        for x in chunk {
1954            *x = bf16_roundtrip(e4m3_round((*x / scale).clamp(-448.0, 448.0)) * scale);
1955        }
1956    }
1957}
1958
1959/// `CMF_DSV41_O_FP8=1`: quantize the attention output to FP8 (E4M3, 32-value
1960/// blocks, power-of-two scales) before the grouped `wo_a`, as the reference
1961/// does since commit 4dd75a8b (`act_quant(o, ...)` before the einsum). Off
1962/// by default until a run on the released weights shows its effect. Both
1963/// tails apply it: the CPU tail here, the device frame with its
1964/// `fp8_act_quant32` kernel (bit-identical to `fp8_activation_inplace`).
1965fn v41_o_fp8_enabled() -> bool {
1966    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
1967    *ON.get_or_init(|| std::env::var("CMF_DSV41_O_FP8").is_ok_and(|value| value == "1"))
1968}
1969
1970/// The attention output boundary before `wo_a`: the reference views `o` as
1971/// `[groups, n_heads*head_dim/groups]` and quantizes the last axis in blocks
1972/// of 32, so with a 32-multiple group width the blocks are contiguous runs
1973/// of the flat output.
1974fn quantize_attention_output(attended: &mut [f32], o_groups: usize) {
1975    debug_assert!(
1976        attended.len() % o_groups.max(1) == 0 && (attended.len() / o_groups.max(1)) % 32 == 0,
1977        "act_quant(o) needs a 32-multiple group width"
1978    );
1979    fp8_activation_inplace(attended);
1980}
1981
1982struct IndexResult {
1983    picked: Vec<usize>,
1984    scores: Vec<f32>,
1985}
1986
1987fn update_index(
1988    ix: &Dsv41Indexer,
1989    x: &[f32],
1990    qr: &[f32],
1991    source_k: &packed_kv::PackedRows,
1992    cfg: &Dsv41Cfg,
1993    pos: usize,
1994    ratio: usize,
1995    inv_freq: &[f32],
1996    candidate_mask: Option<&[bool]>,
1997    pool: Option<&Pool>,
1998) -> IndexResult {
1999    let ih = cfg.index_heads;
2000    let id = cfg.index_head_dim;
2001    let mut q = vec![0.0f32; ih * id];
2002    matvec_bf16(&ix.wq_b, qr, &mut q, pool);
2003    // `q` is head-major. RoPE applies to the tail of each index head, not to
2004    // the tail of the flattened concatenation.
2005    for head in 0..ih {
2006        crate::dsv4::rope_tail(
2007            &mut q[head * id..(head + 1) * id],
2008            inv_freq,
2009            pos,
2010            cfg.rope_head_dim.min(id) & !1,
2011            false,
2012        );
2013    }
2014    // RoPE writes back into a BF16 tensor in the reference kernel.
2015    bf16_inplace(&mut q);
2016    fp4_inplace(&mut q, 32, true);
2017    let mut weights = vec![0.0f32; ih];
2018    matvec_bf16(&ix.weights_proj, x, &mut weights, pool);
2019    let sc = (id as f32).powf(-0.5) * (ih as f32).powf(-0.5);
2020    for w in &mut weights {
2021        // Torch keeps the scalar multiply in the BF16 tensor dtype.
2022        *w = bf16_roundtrip(*w * sc);
2023    }
2024    let n = source_k.rows();
2025    let mut scores = vec![f32::NEG_INFINITY; n];
2026    let mut k = vec![0.0f32; id];
2027    for t in 0..n {
2028        if candidate_mask.is_some_and(|m| !m.get(t).copied().unwrap_or(false)) {
2029            continue;
2030        }
2031        assert!(
2032            source_k.row_into(t, &mut k),
2033            "packed index-K row {t} is unavailable"
2034        );
2035        let k = &k[..];
2036        let mut s = 0.0;
2037        for head in 0..ih {
2038            // einsum emits a BF16 score; its dot-product accumulator is
2039            // wider, but the output is materialised before ReLU.
2040            let d = bf16_roundtrip(
2041                q[head * id..(head + 1) * id]
2042                    .iter()
2043                    .zip(k)
2044                    .map(|(a, b)| a * b)
2045                    .sum::<f32>(),
2046            )
2047            .max(0.0);
2048            // BF16 multiplication happens before the final head reduction.
2049            s += bf16_roundtrip(d * weights[head]);
2050        }
2051        if t >= (pos + 1) / ratio.max(1) {
2052            scores[t] = f32::NEG_INFINITY;
2053        } else {
2054            // torch.sum over the BF16 products returns a BF16 tensor.
2055            scores[t] = bf16_roundtrip(s);
2056        }
2057    }
2058    let mut picked = Vec::new();
2059    crate::dsv4::top_k_positions(&scores, cfg.index_topk, &mut picked);
2060    IndexResult { picked, scores }
2061}
2062
2063fn attention(
2064    l: &Dsv41Layer,
2065    cfg: &Dsv41Cfg,
2066    st: &mut Dsv41State,
2067    li: usize,
2068    x: &[f32],
2069    inv_freq: &[f32],
2070    pool: Option<&Pool>,
2071    out: &mut [f32],
2072) -> Vec<f32> {
2073    let attn_other_t0 = prof::start();
2074    let mut qr = vec![0.0f32; cfg.q_lora_rank];
2075    let mut kv = vec![0.0f32; cfg.head_dim];
2076    // Both projections read the same hidden state.  Keep the two output
2077    // boundaries explicit, but hand the Q4TP pair to the existing
2078    // matvec_many dispatcher so one pool job owns the shared input walk.
2079    QTensor::matvec_many([&l.wq_a, &l.wkv], x, [&mut qr, &mut kv], pool);
2080    bf16_inplace(&mut qr);
2081    trace_stats("wq_a", st.pos, Some(li), &qr);
2082    rms(&mut qr, &l.q_norm, cfg.norm_eps);
2083    trace_stats("q_norm", st.pos, Some(li), &qr);
2084    bf16_inplace(&mut kv);
2085    trace_stats("wkv", st.pos, Some(li), &kv);
2086    rms(&mut kv, &l.kv_norm, cfg.norm_eps);
2087    trace_stats("kv_norm", st.pos, Some(li), &kv);
2088    crate::dsv4::rope_tail(&mut kv, inv_freq, st.pos, cfg.rope_head_dim, false);
2089    bf16_inplace(&mut kv);
2090
2091    // A layer's `compress_ratio` controls whether it reads the shared
2092    // compressed stream at all. Layers after the last source can still have
2093    // a source slot in scope, but ratio-zero layers are window-only in the
2094    // reference and must not concatenate the old compressed cache.
2095    let source = if cfg.ratio(li) > 0 {
2096        source_slot(&cfg.kv_sources, li)
2097    } else {
2098        None
2099    };
2100    let mut latent = None;
2101    if let (Some(cp), Some(si)) = (&l.compressor, source) {
2102        latent = compressor_step(
2103            cp,
2104            x,
2105            cfg,
2106            &mut st.pending_kv[si],
2107            &mut st.pending_score[si],
2108            pool,
2109        );
2110        if let Some(v) = latent.as_ref() {
2111            trace_stats("latent", st.pos, Some(li), v);
2112            // The compressor publishes a pre-RoPE latent.  Keep that form
2113            // for the index-key owner, and write a separately rotated copy
2114            // to the attention cache.
2115            let latent_pos = st.pos + 1 - cfg.ratio(li).max(1);
2116            let mut stored = v.clone();
2117            crate::dsv4::rope_tail(&mut stored, inv_freq, latent_pos, cfg.rope_head_dim, false);
2118            bf16_inplace(&mut stored);
2119            let row_id = st
2120                .packed
2121                .push_main(si, &stored)
2122                .expect("valid main KV source slot");
2123            if std::env::var_os("CMF_DSV41_TRACE").is_some() {
2124                let mut quantized = vec![0.0f32; cfg.head_dim];
2125                assert!(st.packed.main_row_into(si, row_id, &mut quantized));
2126                trace_stats("compressed", st.pos, Some(li), &quantized);
2127            }
2128        }
2129    }
2130    // Index keys are derived from the unrotated latent, before attention's
2131    // RoPE write.  A source publishes one key; consumer indexers reuse it.
2132    if let (Some(ix), Some(si), Some(lat)) = (&l.indexer, source, latent.as_ref()) {
2133        if let (Some(wk), Some(kn)) = (&ix.wk, &ix.k_norm) {
2134            let latent_pos = st.pos + 1 - cfg.ratio(li).max(1);
2135            let mut k = vec![0.0f32; cfg.index_head_dim];
2136            matvec_bf16(wk, lat, &mut k, pool);
2137            rms(&mut k, kn, cfg.norm_eps);
2138            crate::dsv4::rope_tail(
2139                &mut k,
2140                inv_freq,
2141                latent_pos,
2142                cfg.rope_head_dim.min(cfg.index_head_dim) & !1,
2143                false,
2144            );
2145            bf16_inplace(&mut k);
2146            let row_id = st
2147                .packed
2148                .push_index(si, &k)
2149                .expect("valid index-K source slot");
2150            if std::env::var_os("CMF_DSV41_TRACE").is_some() {
2151                let mut quantized = vec![0.0f32; cfg.index_head_dim];
2152                assert!(st.packed.index_row_into(si, row_id, &mut quantized));
2153                trace_stats("index_k", st.pos, Some(li), &quantized);
2154            }
2155        }
2156        let _ = ix;
2157    }
2158    // Publish the sliding window before building the sparse position list.
2159    // Keep this mutable borrow scoped: attention below only needs immutable
2160    // slices, and can therefore read the shared compressed cache without
2161    // cloning it on every decode token.
2162    let win_len = {
2163        let w = &mut st.window[li];
2164        fp8_activation_inplace(&mut kv);
2165        // `act_quant(..., inplace=True)` stores the dequantized result as
2166        // BF16 even though its internal scale/value arithmetic is f32.
2167        bf16_inplace(&mut kv);
2168        trace_stats("kv", st.pos, Some(li), &kv);
2169        w.extend_from_slice(&kv);
2170        let cap = cfg.window * cfg.head_dim;
2171        if w.len() > cap {
2172            let drop = w.len() - cap;
2173            w.drain(..drop);
2174        }
2175        w.len() / cfg.head_dim
2176    };
2177    let mut idxs: Vec<usize> = (0..win_len).collect();
2178    if let Some(si) = source {
2179        let comp_len = st.packed.main_rows(si);
2180        if comp_len > 0 {
2181            if let Some(ix) = &l.indexer {
2182                let candidates = if cfg.candidate_source.is_some_and(|c| li > c)
2183                    && !st.candidates.is_empty()
2184                {
2185                    Some(st.candidates.as_slice())
2186                } else {
2187                    None
2188                };
2189                let result = update_index(
2190                    ix,
2191                    x,
2192                    &qr,
2193                    st.packed
2194                        .index_store(si)
2195                        .expect("index-K store for shared source"),
2196                    cfg,
2197                    st.pos,
2198                    cfg.ratio(li),
2199                    inv_freq,
2200                    candidates,
2201                    pool,
2202                );
2203                trace_indices(st.pos, li, &result.picked);
2204                if cfg.candidate_source == Some(li) {
2205                    st.candidates = candidate_blocks(
2206                        &result.scores,
2207                        cfg.candidate_topk_blocks,
2208                        cfg.candidate_block_size,
2209                    );
2210                    trace_index_scores(
2211                        st.pos,
2212                        li,
2213                        &result.scores,
2214                        &result.picked,
2215                        Some(st.candidates.as_slice()),
2216                        win_len,
2217                        comp_len,
2218                        st.pending_kv[si].len() / cfg.head_dim.max(1),
2219                    );
2220                } else {
2221                    trace_index_scores(
2222                        st.pos,
2223                        li,
2224                        &result.scores,
2225                        &result.picked,
2226                        candidates,
2227                        win_len,
2228                        comp_len,
2229                        st.pending_kv[si].len() / cfg.head_dim.max(1),
2230                    );
2231                }
2232                st.topk = result.picked;
2233                st.topk_ready = true;
2234            }
2235            let all_picks: Vec<usize> = (0..comp_len).collect();
2236            let picks: &[usize] = if cfg.index_source(li).is_some() && st.topk_ready {
2237                &st.topk
2238            } else {
2239                &all_picks
2240            };
2241            idxs.extend(
2242                picks
2243                    .iter()
2244                    .copied()
2245                    .filter(|&p| p < comp_len)
2246                    .map(|p| win_len + p),
2247            );
2248        }
2249    }
2250
2251    // V4.1 has no second per-head query RMSNorm after wq_b. The default
2252    // path materialises the final query on the host so the GPU adapter and
2253    // CPU fallback share one exact BF16/RoPE value. The opt-in fused path
2254    // delays this allocation: its frame consumes `qr` and performs wq_b,
2255    // BF16 materialisation, and forward RoPE inside the existing encoder.
2256    let fused_q = v41_fused_q_enabled();
2257    let materialize_q = || {
2258        let mut q = vec![0.0f32; cfg.n_heads * cfg.head_dim];
2259        matvec_bf16(&l.wq_b, &qr, &mut q, pool);
2260        trace_stats("wq_b", st.pos, Some(li), &q);
2261        for h in 0..cfg.n_heads {
2262            let qh = &mut q[h * cfg.head_dim..(h + 1) * cfg.head_dim];
2263            crate::dsv4::rope_tail(qh, inv_freq, st.pos, cfg.rope_head_dim, false);
2264        }
2265        bf16_inplace(&mut q);
2266        trace_stats("q", st.pos, Some(li), &q);
2267        q
2268    };
2269    let mut q = if fused_q { None } else { Some(materialize_q()) };
2270
2271    // Decode the selected global rows once. The resulting compact stream is
2272    // shared by every head and by both the device frame and CPU fallback;
2273    // no history-sized f32 materialization is needed for sparse attention.
2274    let selected_comp_positions: Vec<usize> = idxs
2275        .iter()
2276        .copied()
2277        .filter(|&p| p >= win_len)
2278        .map(|p| p - win_len)
2279        .collect();
2280    let mut selected_compressed = vec![0.0f32; selected_comp_positions.len() * cfg.head_dim];
2281    if let Some(si) = source {
2282        assert!(
2283            st.packed
2284                .gather_main_rows(si, &selected_comp_positions, &mut selected_compressed,),
2285            "packed main-K gather failed for source {si}"
2286        );
2287    }
2288    let mut packed_idxs = Vec::with_capacity(idxs.len());
2289    for &logical in &idxs {
2290        if logical < win_len {
2291            packed_idxs.push(logical);
2292        } else {
2293            let comp = logical - win_len;
2294            let selected = selected_comp_positions
2295                .iter()
2296                .position(|&row| row == comp)
2297                .expect("selected compressed row mapping");
2298            packed_idxs.push(win_len + selected);
2299        }
2300    }
2301
2302    // ── proven DSV4 attention tail on the device ──
2303    // V4.1 keeps the compressor/index publication above on the host. Once
2304    // the bounded selected rows exist, the established DSV4 frame can perform
2305    // q projection (when fused), sparse attention with the per-head sink,
2306    // inverse RoPE, grouped wo_a and wo_b in one encoder/readback.
2307    // The cache is keyed by this state's private id and receives only the
2308    // current bounded window plus the selected compressed rows; no
2309    // whole-history upload or clone is created here.
2310    #[cfg(feature = "gpu")]
2311    if gpu_attention_tail_enabled() {
2312        let n_comp = selected_comp_positions.len();
2313        let cache_cap = (cfg.window + n_comp.next_power_of_two().max(64)) * cfg.head_dim;
2314        let cache_ok =
2315            crate::gpu_wgpu::dsv4_cache_write(st.gpu_kv_id, li, 0, &st.window[li], cache_cap)
2316                && (selected_compressed.is_empty()
2317                    || crate::gpu_wgpu::dsv4_cache_write(
2318                        st.gpu_kv_id,
2319                        li,
2320                        cfg.window * cfg.head_dim,
2321                        &selected_compressed,
2322                        cache_cap,
2323                    ));
2324        let model = l.wq_b.model_arc();
2325        // The gathered cache is already in attention-list order, so logical
2326        // positions become compact row numbers. This keeps sink/index/window
2327        // semantics while bounding each upload to the rows actually used.
2328        let idx32: Vec<u32> = packed_idxs
2329            .iter()
2330            .map(|&p| {
2331                if p < win_len {
2332                    p as u32
2333                } else {
2334                    (cfg.window + p - win_len) as u32
2335                }
2336            })
2337            .collect();
2338        let diag_tap = gpu_tail_tap();
2339        let diag_len = diag_tap.map(|tap| match tap {
2340            "q" | "attn" => cfg.n_heads * cfg.head_dim,
2341            "mid" => cfg.o_groups * cfg.o_lora_rank,
2342            _ => cfg.dim,
2343        });
2344        let mut gpu_attended = vec![0.0f32; cfg.dim.max(diag_len.unwrap_or(0))];
2345        // `qn_in` is the already normalized LoRA-rank query. In fused mode
2346        // the frame owns wq_b/BF16/RoPE; otherwise retain the legacy final-q
2347        // upload. Both modes keep the same selected packed rows and output
2348        // buffer contract.
2349        let qn_in = fused_q.then_some(qr.as_slice());
2350        let q_in = q.as_deref();
2351        let mutation = gpu_tail_mutation();
2352        let sink: &[f32] = if mutation == Some("sink") {
2353            // One stable zero buffer: the frame keys constants by address.
2354            static ZERO: std::sync::OnceLock<Vec<f32>> = std::sync::OnceLock::new();
2355            let zero = ZERO.get_or_init(|| vec![0.0; 4096]);
2356            &zero[..l.attn_sink.len().min(zero.len())]
2357        } else {
2358            &l.attn_sink
2359        };
2360        let frame_pos = st.pos + usize::from(mutation == Some("rope"));
2361        let gpu_ok = cache_ok
2362            && model.is_some_and(|model| {
2363                let w = crate::gpu_wgpu::Dsv4AttnW {
2364                    wq_a: l.wq_a.model_idx().unwrap_or(usize::MAX),
2365                    wq_b: l.wq_b.model_idx().unwrap_or(usize::MAX),
2366                    wo_a: l.wo_a.model_idx().unwrap_or(usize::MAX),
2367                    wo_b: l.wo_b.model_idx().unwrap_or(usize::MAX),
2368                    q_norm: &l.q_norm,
2369                    sink,
2370                };
2371                let g = crate::gpu_wgpu::Dsv4AttnGeom {
2372                    dim: cfg.dim,
2373                    nh: cfg.n_heads,
2374                    hd: cfg.head_dim,
2375                    rd: cfg.rope_head_dim,
2376                    q_lora: cfg.q_lora_rank,
2377                    o_lora: cfg.o_lora_rank,
2378                    o_groups: cfg.o_groups,
2379                    eps: cfg.norm_eps,
2380                    scale: (cfg.head_dim as f32).powf(-0.5),
2381                    bf16: true,
2382                    q_rms: false,
2383                    o_fp8: v41_o_fp8_enabled(),
2384                };
2385                crate::gpu_wgpu::dsv4_attn_frame(
2386                    &model,
2387                    &w,
2388                    g,
2389                    &[],
2390                    qn_in,
2391                    q_in,
2392                    st.gpu_kv_id,
2393                    li,
2394                    &idx32,
2395                    inv_freq,
2396                    frame_pos,
2397                    None,
2398                    &mut gpu_attended,
2399                )
2400            });
2401        if gpu_ok {
2402            prof::note_gpu_attn();
2403            if let Some(tap) = diag_tap {
2404                let name = match tap {
2405                    "q" => "gpu_q",
2406                    "attn" => "gpu_attn",
2407                    "mid" => "gpu_mid",
2408                    _ => "gpu_tail",
2409                };
2410                trace_stats(name, st.pos, Some(li), &gpu_attended[..diag_len.unwrap()]);
2411            } else {
2412                // The GPU frame owns the entire attention body on this arm. Keep
2413                // the cumulative stage report additive with the CPU path: its
2414                // host time is the residual of the outer attention timer.
2415                prof::add(&prof::ATTN_OTHER_NS, attn_other_t0);
2416                bf16_inplace(&mut gpu_attended);
2417                out.copy_from_slice(&gpu_attended[..cfg.dim]);
2418                return qr;
2419            }
2420        }
2421        prof::note_gpu_attn_fallback();
2422    }
2423
2424    // A disabled/unavailable GPU, a cache miss, or a diagnostic tap needs the
2425    // unchanged host query for the ordinary sparse CPU fallback. Keep this
2426    // allocation after the GPU attempt in fused mode so the successful path
2427    // has no host q materialization or q upload.
2428    if q.is_none() {
2429        q = Some(materialize_q());
2430    }
2431    let q = q
2432        .as_deref()
2433        .expect("V4.1 query must be materialized before CPU attention");
2434
2435    let window = &st.window[li];
2436    let compressed = if selected_compressed.is_empty() {
2437        None
2438    } else {
2439        Some(selected_compressed.as_slice())
2440    };
2441    let mut attended = vec![0.0f32; cfg.n_heads * cfg.head_dim];
2442    prof::add(&prof::ATTN_OTHER_NS, attn_other_t0);
2443    let sparse_t0 = prof::start();
2444    for h in 0..cfg.n_heads {
2445        sparse_attend_split(
2446            &q[h * cfg.head_dim..(h + 1) * cfg.head_dim],
2447            window,
2448            compressed,
2449            &packed_idxs,
2450            l.attn_sink.get(h).copied().unwrap_or(0.0),
2451            (cfg.head_dim as f32).powf(-0.5),
2452            win_len,
2453            cfg.head_dim,
2454            &mut attended[h * cfg.head_dim..(h + 1) * cfg.head_dim],
2455        );
2456    }
2457    prof::add(&prof::SPARSE_NS, sparse_t0);
2458    let attn_other_t0 = prof::start();
2459    for h in 0..cfg.n_heads {
2460        crate::dsv4::rope_tail(
2461            &mut attended[h * cfg.head_dim..(h + 1) * cfg.head_dim],
2462            inv_freq,
2463            st.pos,
2464            cfg.rope_head_dim,
2465            true,
2466        );
2467        bf16_inplace(&mut attended[h * cfg.head_dim..(h + 1) * cfg.head_dim]);
2468    }
2469    if v41_o_fp8_enabled() {
2470        quantize_attention_output(&mut attended, cfg.o_groups);
2471    }
2472    trace_stats("attended", st.pos, Some(li), &attended);
2473    if gpu_tail_tap().is_some_and(|tap| tap == "mid") {
2474        let mut cpu_mid = vec![0.0f32; cfg.o_groups * cfg.o_lora_rank];
2475        let mut scratch = vec![0.0f32; l.wo_a.cols()];
2476        for (i, mid) in cpu_mid.iter_mut().enumerate() {
2477            let group = i / cfg.o_lora_rank;
2478            *mid = bf16_roundtrip(l.wo_a.row_dot(
2479                i,
2480                &attended[group * l.wo_a.cols()..(group + 1) * l.wo_a.cols()],
2481                &mut scratch,
2482            ));
2483        }
2484        trace_stats("cpu_mid", st.pos, Some(li), &cpu_mid);
2485    }
2486    crate::dsv4::o_project(
2487        &attended,
2488        // `wo_a` is the grouped BF16 einsum in the reference.  Its result
2489        // is materialised as BF16 before the second projection; rounding
2490        // only the final `wo_b` output leaves a wider f32 intermediate and
2491        // accumulates a measurable drift into the next block.
2492        &|r, v, scratch| bf16_roundtrip(l.wo_a.row_dot(r, v, scratch)),
2493        l.wo_a.cols(),
2494        &|m, o| l.wo_b.matvec(m, o, pool),
2495        cfg.o_groups,
2496        cfg.o_lora_rank,
2497        pool,
2498        out,
2499    );
2500    // The grouped wo_a/wo_b projection returns the model activation dtype.
2501    bf16_inplace(out);
2502    if gpu_tail_tap().is_some() {
2503        trace_stats("cpu_tail", st.pos, Some(li), out);
2504    }
2505    prof::add(&prof::ATTN_OTHER_NS, attn_other_t0);
2506    qr
2507}
2508
2509/// Attention over the concatenated logical stream `window || compressed`
2510/// without materializing that concatenation.  A decode step can have a very
2511/// large compressed history, so cloning it once per layer would turn a
2512/// bounded sparse read into an O(context) copy before the actual O(top-k)
2513/// attention.  The position list still uses the same logical indices as the
2514/// reference helper; this function only changes how the selected value rows
2515/// are addressed.
2516fn sparse_attend_split(
2517    q: &[f32],
2518    window: &[f32],
2519    compressed: Option<&[f32]>,
2520    idxs: &[usize],
2521    sink: f32,
2522    scale: f32,
2523    window_len: usize,
2524    head_dim: usize,
2525    out: &mut [f32],
2526) {
2527    let row = |logical: usize| -> Option<&[f32]> {
2528        if logical < window_len {
2529            window.get(logical * head_dim..(logical + 1) * head_dim)
2530        } else {
2531            compressed.and_then(|c| {
2532                let p = logical - window_len;
2533                c.get(p * head_dim..(p + 1) * head_dim)
2534            })
2535        }
2536    };
2537    let mut m = sink;
2538    let mut scores = Vec::with_capacity(idxs.len());
2539    for &p in idxs {
2540        let Some(k) = row(p) else {
2541            scores.push(f32::NEG_INFINITY);
2542            continue;
2543        };
2544        let dot: f32 = q.iter().zip(k).map(|(a, b)| a * b).sum::<f32>() * scale;
2545        m = m.max(dot);
2546        scores.push(dot);
2547    }
2548    let mut denom = (sink - m).exp();
2549    out.fill(0.0);
2550    for (&p, &score) in idxs.iter().zip(&scores) {
2551        let Some(v) = row(p) else {
2552            continue;
2553        };
2554        let weight = (score - m).exp();
2555        denom += weight;
2556        for (dst, &value) in out.iter_mut().zip(v) {
2557            *dst += weight * value;
2558        }
2559    }
2560    if denom > 0.0 && denom.is_finite() {
2561        let inv = 1.0 / denom;
2562        for dst in out.iter_mut() {
2563            *dst *= inv;
2564        }
2565    }
2566}
2567
2568fn candidate_blocks(scores: &[f32], top_blocks: usize, block: usize) -> Vec<bool> {
2569    let n = scores.len();
2570    let blocks = n.div_ceil(block.max(1));
2571    if blocks == 0 {
2572        return Vec::new();
2573    }
2574    let block = block.max(1);
2575    let mut order: Vec<usize> = (0..blocks).collect();
2576    // The source selects by the best reachable position in each block. A
2577    // stable index tie break keeps CPU and GPU fixtures deterministic.
2578    order.sort_by(|&a, &b| {
2579        let sa = scores[a * block..((a + 1) * block).min(n)]
2580            .iter()
2581            .copied()
2582            .fold(f32::NEG_INFINITY, f32::max);
2583        let sb = scores[b * block..((b + 1) * block).min(n)]
2584            .iter()
2585            .copied()
2586            .fold(f32::NEG_INFINITY, f32::max);
2587        sb.partial_cmp(&sa)
2588            .unwrap_or(std::cmp::Ordering::Equal)
2589            .then_with(|| a.cmp(&b))
2590    });
2591    let reachable = scores.iter().filter(|x| x.is_finite()).count();
2592    if reachable == 0 {
2593        return vec![false; n];
2594    }
2595    let last = scores
2596        .iter()
2597        .rposition(|x| x.is_finite())
2598        .map(|p| p / block)
2599        .unwrap_or(0);
2600    let mut keep = vec![false; blocks];
2601    let budget = top_blocks.min(blocks);
2602    let mut selected = 0usize;
2603    if budget > 0 {
2604        keep[last] = true;
2605        selected = 1;
2606    }
2607    for &b in &order {
2608        if selected >= budget {
2609            break;
2610        }
2611        if b == last || keep[b] {
2612            continue;
2613        }
2614        let block_score = scores[b * block..((b + 1) * block).min(n)]
2615            .iter()
2616            .copied()
2617            .fold(f32::NEG_INFINITY, f32::max);
2618        if block_score.is_finite() {
2619            keep[b] = true;
2620            selected += 1;
2621        }
2622    }
2623    let mut out = vec![false; n];
2624    for b in 0..blocks {
2625        if keep[b] {
2626            for i in b * block..((b + 1) * block).min(n) {
2627                out[i] = true;
2628            }
2629        }
2630    }
2631    out
2632}
2633
2634#[cfg(feature = "gpu")]
2635fn dsv41_expert_cpu(
2636    expert: &Dsv41Expert,
2637    cfg: &Dsv41Cfg,
2638    x: &[f32],
2639    weight: f32,
2640    pool: Option<&Pool>,
2641    out: &mut [f32],
2642) {
2643    let mut g = vec![0.0f32; cfg.moe_inter];
2644    let mut u = vec![0.0f32; cfg.moe_inter];
2645    matvec_bf16(&expert.w1, x, &mut g, pool);
2646    matvec_bf16(&expert.w3, x, &mut u, pool);
2647    for i in 0..cfg.moe_inter {
2648        if cfg.swiglu_limit > 0.0 {
2649            g[i] = g[i].min(cfg.swiglu_limit);
2650            u[i] = u[i].clamp(-cfg.swiglu_limit, cfg.swiglu_limit);
2651        }
2652        g[i] = g[i] / (1.0 + (-g[i]).exp()) * u[i] * weight;
2653    }
2654    // The official Expert converts the weighted SwiGLU activation back to
2655    // the input dtype (BF16 in V4.1) before w2. `matvec_bf16` rounds its
2656    // output, but that is a separate boundary and cannot replace this cast.
2657    bf16_inplace(&mut g);
2658    let mut tmp = vec![0.0f32; cfg.dim];
2659    matvec_bf16(&expert.w2, &g, &mut tmp, pool);
2660    for (dst, value) in out.iter_mut().zip(tmp) {
2661        *dst += value;
2662    }
2663}
2664
2665#[cfg(feature = "gpu")]
2666fn dsv41_cold_experts_cpu(
2667    layer: &Dsv41Layer,
2668    cfg: &Dsv41Cfg,
2669    x: &[f32],
2670    cold: &[(usize, f32)],
2671    pool: Option<&Pool>,
2672) -> Vec<f32> {
2673    let mut out = vec![0.0f32; cfg.dim];
2674    for &(expert, weight) in cold {
2675        if let Some(expert) = layer.experts.get(expert) {
2676            dsv41_expert_cpu(expert, cfg, x, weight, pool, &mut out);
2677        }
2678    }
2679    out
2680}
2681
2682/// `MADV_WILLNEED` over the three projections of each routed expert, issued
2683/// right after the route: on a host that cannot cache the whole file the
2684/// kernel then reads every missing expert with large asynchronous requests
2685/// while the layer runs, instead of the cold-expert matvecs and the bank
2686/// fills faulting them in page by page (measured on an NVMe RAID that reads
2687/// 7.7–12 GB/s directly: ~0.6 GB/s through faults). Advisory; resident pages
2688/// cost a page-cache walk. `CMF_DSV41_WILLNEED=0` turns it off.
2689fn will_need_experts(layer: &Dsv41Layer, picks: &[usize]) {
2690    #[cfg(any(target_os = "linux", target_os = "android", target_os = "macos"))]
2691    {
2692        static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
2693        if !*ON.get_or_init(|| std::env::var("CMF_DSV41_WILLNEED").as_deref() != Ok("0")) {
2694            return;
2695        }
2696        let page = 4096usize;
2697        for &e in picks {
2698            let Some(expert) = layer.experts.get(e) else {
2699                continue;
2700            };
2701            for t in [&expert.w1, &expert.w3, &expert.w2] {
2702                let (Some(model), Some(idx)) = (t.model_arc(), t.model_idx()) else {
2703                    continue;
2704                };
2705                let Some(entry) = model.tensors.get(idx) else {
2706                    continue;
2707                };
2708                let Some(abs) = model.entry_abs_offset(entry) else {
2709                    continue;
2710                };
2711                let bytes = model.primary_bytes();
2712                if abs + entry.nbytes as usize > bytes.len() {
2713                    continue;
2714                }
2715                let base = bytes.as_ptr() as usize;
2716                let s = (base + abs) & !(page - 1);
2717                let end = base + abs + entry.nbytes as usize;
2718                // SAFETY: a range inside the model's read-only mapping;
2719                // MADV_WILLNEED only schedules read-ahead.
2720                unsafe {
2721                    libc::madvise(s as *mut libc::c_void, end - s, libc::MADV_WILLNEED);
2722                }
2723            }
2724        }
2725    }
2726    #[cfg(not(any(target_os = "linux", target_os = "android", target_os = "macos")))]
2727    let _ = (layer, picks);
2728}
2729
2730/// Execute V4.1's host-resolved route through the shared DSV4 segmented
2731/// global expert bank.  V4.1 routing stays on the host because its VL bias
2732/// and sqrt-softplus rule differ from the generic shader route.  Forced ids
2733/// plus preweighted values preserve that route exactly while the card fuses
2734/// gate/up/SwiGLU/down for resident experts and the host completes cold ones.
2735#[cfg(feature = "gpu")]
2736fn dynamic_moe_gpu(
2737    layer: &Dsv41Layer,
2738    cfg: &Dsv41Cfg,
2739    x: &[f32],
2740    pool: Option<&Pool>,
2741    logits: &[f32],
2742    picks: &[usize],
2743    weights: &[f32],
2744    layer_index: usize,
2745    n_layers: usize,
2746    state: &mut Dsv41State,
2747    out: &mut [f32],
2748) -> bool {
2749    if !crate::gpu::enabled_here()
2750        || !crate::gpu_wgpu::dsv4_global_moe_supported()
2751        || std::env::var("CMF_DSV41_DYNAMIC_MOE").as_deref() == Ok("0")
2752        || picks.len() != cfg.top_k
2753        || weights.len() != cfg.n_routed_experts
2754        || logits.len() != cfg.n_routed_experts
2755        || layer.gpu_expert_ids.len() != cfg.n_routed_experts
2756        || layer.gpu_shared_ids.is_none()
2757    {
2758        return false;
2759    }
2760    let first = match layer.experts.first() {
2761        Some(expert) => expert,
2762        None => return false,
2763    };
2764    let model = match first.w1.model_arc() {
2765        Some(model) => model,
2766        None => return false,
2767    };
2768    // `convert --quant fp4`: gate, up and down all mxfp4 (one bank codec).
2769    let fp4 = [&first.w1, &first.w3, &first.w2]
2770        .iter()
2771        .all(|t| t.model_dtype() == Some(TensorDtype::Mxfp4));
2772    let gu_q2 = !fp4
2773        && first.w1.model_dtype() == Some(TensorDtype::Q2TiledP)
2774        && first.w3.model_dtype() == Some(TensorDtype::Q2TiledP);
2775    if !fp4 && first.w2.model_dtype() != Some(TensorDtype::Q4TiledP) {
2776        return false;
2777    }
2778    // The global bank has one gate/up layout and one down layout.  Refuse a
2779    // mixed or synthetic layer rather than reinterpreting a tensor's bytes.
2780    let (gu_dtype, dn_dtype, codec) = if fp4 {
2781        (
2782            TensorDtype::Mxfp4,
2783            TensorDtype::Mxfp4,
2784            crate::gpu_wgpu::MoeBankCodec::Mxfp4,
2785        )
2786    } else if gu_q2 {
2787        (
2788            TensorDtype::Q2TiledP,
2789            TensorDtype::Q4TiledP,
2790            crate::gpu_wgpu::MoeBankCodec::Q2tpGateUp,
2791        )
2792    } else {
2793        (
2794            TensorDtype::Q4TiledP,
2795            TensorDtype::Q4TiledP,
2796            crate::gpu_wgpu::MoeBankCodec::Q4tp,
2797        )
2798    };
2799    let same_layout = layer.experts.iter().all(|expert| {
2800        expert
2801            .w1
2802            .model_arc()
2803            .is_some_and(|m| m.uid() == model.uid())
2804            && expert
2805                .w3
2806                .model_arc()
2807                .is_some_and(|m| m.uid() == model.uid())
2808            && expert
2809                .w2
2810                .model_arc()
2811                .is_some_and(|m| m.uid() == model.uid())
2812            && expert.w1.model_dtype() == Some(gu_dtype)
2813            && expert.w3.model_dtype() == Some(gu_dtype)
2814            && expert.w2.model_dtype() == Some(dn_dtype)
2815    });
2816    let shared_layout = layer
2817        .shared
2818        .w1
2819        .model_arc()
2820        .is_some_and(|m| m.uid() == model.uid())
2821        && layer
2822            .shared
2823            .w3
2824            .model_arc()
2825            .is_some_and(|m| m.uid() == model.uid())
2826        && layer
2827            .shared
2828            .w2
2829            .model_arc()
2830            .is_some_and(|m| m.uid() == model.uid())
2831        && layer.shared.w1.model_dtype() == Some(gu_dtype)
2832        && layer.shared.w3.model_dtype() == Some(gu_dtype)
2833        && layer.shared.w2.model_dtype() == Some(dn_dtype);
2834    if !same_layout || !shared_layout || layer_index >= n_layers {
2835        return false;
2836    }
2837    if state.gpu_pool.is_none() {
2838        state.gpu_pool = crate::qwen4_exp::QwenGpuPool::create_for_dsv41_codec(
2839            &model,
2840            cfg.moe_inter,
2841            cfg.dim,
2842            n_layers,
2843            cfg.n_routed_experts,
2844            codec,
2845        );
2846    }
2847    // One bank, one codec: a layer stored differently stays on the host.
2848    if state.gpu_pool.as_ref().is_some_and(|p| p.codec != codec) {
2849        return false;
2850    }
2851    let (remap, shared_slot, segment_slots) = {
2852        let Some(gpu_pool) = state.gpu_pool.as_mut() else {
2853            return false;
2854        };
2855        let Some((remap, shared_slot)) = gpu_pool.ensure(
2856            &model,
2857            layer_index,
2858            picks,
2859            &layer.gpu_expert_ids,
2860            layer.gpu_shared_ids,
2861        ) else {
2862            return false;
2863        };
2864        (remap, shared_slot, gpu_pool.segment_slots)
2865    };
2866    let cold_ids: Vec<usize> = picks
2867        .iter()
2868        .copied()
2869        .filter(|&expert| remap.get(expert).copied() == Some(u32::MAX))
2870        .collect();
2871    let cold_jobs: Vec<(usize, f32)> = cold_ids
2872        .iter()
2873        .copied()
2874        .map(|expert| (expert, weights[expert]))
2875        .collect();
2876    let gpu_weights = crate::gpu_wgpu::Dsv4MoeW {
2877        router: &[],
2878        experts: &layer.gpu_expert_ids,
2879        logits,
2880        // In forced + preweighted mode this is V4.1's final route table;
2881        // the shader does not redo softplus, bias, or normalization.
2882        bias: Some(weights),
2883        mask: None,
2884        forced: Some(picks),
2885        remap: Some(&remap),
2886        global: Some(crate::gpu_wgpu::Dsv4GlobalMoe {
2887            pool_uid: model.uid(),
2888            shared_slot,
2889            segment_slots: segment_slots as u32,
2890        }),
2891        has_shared: true,
2892        shared_weight: 1.0,
2893        preweighted: true,
2894        qwen_softmax: false,
2895    };
2896    let geom = crate::gpu_wgpu::Dsv4MoeGeom {
2897        hidden: cfg.dim,
2898        inter: cfg.moe_inter,
2899        top_k: picks.len(),
2900        // We have already applied V4.1's route scale and denominator.
2901        route_scale: 1.0,
2902        swiglu_limit: cfg.swiglu_limit,
2903        gu_q2,
2904        bf16: true,
2905    };
2906    let mut gpu_out = vec![0.0f32; cfg.dim];
2907    let mut cold_from_gpu = Vec::new();
2908    let mut cold_x = Vec::new();
2909    let (frame_ok, cold_cpu) = std::thread::scope(|scope| {
2910        let cpu = (!cold_jobs.is_empty()).then(|| {
2911            // Cold experts deliberately stay on the host while resident
2912            // routes run on the device. Without the scope guard, QTensor's
2913            // small matvecs can re-enter the generic GPU backend and defeat
2914            // the intended overlap.
2915            scope.spawn(|| {
2916                crate::gpu::cpu_scope(|| dsv41_cold_experts_cpu(layer, cfg, x, &cold_jobs, pool))
2917            })
2918        });
2919        let ok = crate::gpu_wgpu::dsv4_moe_frame(
2920            &model,
2921            &gpu_weights,
2922            geom,
2923            x,
2924            &mut cold_from_gpu,
2925            &mut cold_x,
2926            None,
2927            None,
2928            &mut gpu_out,
2929        );
2930        let cpu_out = cpu
2931            .and_then(|job| job.join().ok())
2932            .unwrap_or_else(|| vec![0.0; cfg.dim]);
2933        (ok, cpu_out)
2934    });
2935    if !frame_ok
2936        || cold_from_gpu.len() != cold_jobs.len()
2937        || cold_from_gpu
2938            .iter()
2939            .map(|&(expert, _)| expert)
2940            .ne(cold_ids.iter().copied())
2941    {
2942        return false;
2943    }
2944    prof::note_cold(cold_jobs.len());
2945    for (dst, src) in gpu_out.iter_mut().zip(cold_cpu) {
2946        *dst += src;
2947    }
2948    // Match the source's BF16 boundary after routed and shared accumulation.
2949    bf16_inplace(&mut gpu_out);
2950    out[..cfg.dim].copy_from_slice(&gpu_out);
2951    true
2952}
2953
2954fn moe(
2955    l: &Dsv41Layer,
2956    cfg: &Dsv41Cfg,
2957    x: &[f32],
2958    pool: Option<&Pool>,
2959    image: bool,
2960    position: usize,
2961    layer: usize,
2962    n_layers: usize,
2963    state: &mut Dsv41State,
2964    out: &mut [f32],
2965) {
2966    prof::note_moe();
2967    let mut logits = vec![0.0f32; cfg.n_routed_experts];
2968    matvec(&l.gate, x, &mut logits, pool);
2969    let bias = if image {
2970        l.gate_bias_vl.as_deref().unwrap_or(&l.gate_bias)
2971    } else {
2972        &l.gate_bias
2973    };
2974    let temperature = cfg.gate_temp.max(f32::MIN_POSITIVE);
2975    let scores: Vec<f32> = logits
2976        .iter()
2977        .map(|&v| sqrt_softplus(v / temperature))
2978        .collect();
2979    let mut shifted: Vec<f32> = scores
2980        .iter()
2981        .enumerate()
2982        .map(|(i, s)| s + bias.get(i).copied().unwrap_or(0.0))
2983        .collect();
2984    let mut picks = Vec::with_capacity(cfg.top_k);
2985    for _ in 0..cfg.top_k.min(cfg.n_routed_experts) {
2986        let mut i = 0usize;
2987        let mut v = f32::NEG_INFINITY;
2988        for (j, &candidate) in shifted.iter().enumerate() {
2989            if candidate > v {
2990                i = j;
2991                v = candidate;
2992            }
2993        }
2994        if !v.is_finite() {
2995            break;
2996        }
2997        picks.push(i);
2998        shifted[i] = f32::NEG_INFINITY;
2999    }
3000    trace_moe(position, layer, image, &logits, &scores, bias, &picks);
3001    will_need_experts(l, &picks);
3002    // `weights /= weights.sum() + 1e-20` only when more than one expert is
3003    // chosen (Gate.forward); the epsilon is not norm_eps.
3004    let sum = picks.iter().map(|&i| scores[i]).sum::<f32>() + 1e-20;
3005    let mut mix_weights = vec![0.0f32; cfg.n_routed_experts];
3006    if cfg.norm_topk_prob && cfg.top_k > 1 {
3007        for &e in &picks {
3008            mix_weights[e] = scores[e] / sum * cfg.route_scale;
3009        }
3010    } else {
3011        for &e in &picks {
3012            mix_weights[e] = scores[e] * cfg.route_scale;
3013        }
3014    }
3015    if let Some(cap) = state.moe_capture.as_mut() {
3016        if (cap.lo..cap.hi).contains(&layer) {
3017            let k = layer - cap.lo;
3018            cap.x[k].extend(x[..cfg.dim].iter().map(|&v| cortiq_core::quant::f32_to_f16(v)));
3019            cap.picks[k].extend(
3020                (0..cfg.top_k).map(|j| picks.get(j).map_or(u16::MAX, |&e| e as u16)),
3021            );
3022            cap.w[k].extend((0..cfg.top_k).map(|j| picks.get(j).map_or(0.0, |&e| mix_weights[e])));
3023        }
3024    }
3025    #[cfg(feature = "gpu")]
3026    if dynamic_moe_gpu(
3027        l,
3028        cfg,
3029        x,
3030        pool,
3031        &logits,
3032        &picks,
3033        &mix_weights,
3034        layer,
3035        n_layers,
3036        state,
3037        out,
3038    ) {
3039        prof::note_gpu_moe();
3040        state.gpu_moe_calls += 1;
3041        return;
3042    }
3043    prof::note_cpu_moe();
3044    out.fill(0.0);
3045    let mut tmp = vec![0.0f32; cfg.dim];
3046    for &e in &picks {
3047        let ew = mix_weights[e];
3048        let ex = &l.experts[e];
3049        let mut g = vec![0.0f32; cfg.moe_inter];
3050        let mut u = vec![0.0f32; cfg.moe_inter];
3051        matvec_bf16(&ex.w1, x, &mut g, pool);
3052        matvec_bf16(&ex.w3, x, &mut u, pool);
3053        for i in 0..cfg.moe_inter {
3054            if cfg.swiglu_limit > 0.0 {
3055                g[i] = g[i].min(cfg.swiglu_limit);
3056                u[i] = u[i].clamp(-cfg.swiglu_limit, cfg.swiglu_limit);
3057            }
3058            g[i] = g[i] / (1.0 + (-g[i]).exp()) * u[i] * ew;
3059        }
3060        bf16_inplace(&mut g);
3061        matvec_bf16(&ex.w2, &g, &mut tmp, pool);
3062        for i in 0..cfg.dim {
3063            out[i] += tmp[i];
3064        }
3065    }
3066    let shared = &l.shared;
3067    let mut g = vec![0.0f32; cfg.moe_inter];
3068    let mut u = vec![0.0f32; cfg.moe_inter];
3069    matvec_bf16(&shared.w1, x, &mut g, pool);
3070    matvec_bf16(&shared.w3, x, &mut u, pool);
3071    for i in 0..cfg.moe_inter {
3072        if cfg.swiglu_limit > 0.0 {
3073            g[i] = g[i].min(cfg.swiglu_limit);
3074            u[i] = u[i].clamp(-cfg.swiglu_limit, cfg.swiglu_limit);
3075        }
3076        g[i] = g[i] / (1.0 + (-g[i]).exp()) * u[i];
3077    }
3078    bf16_inplace(&mut g);
3079    matvec_bf16(&shared.w2, &g, &mut tmp, pool);
3080    for i in 0..cfg.dim {
3081        out[i] += tmp[i];
3082    }
3083    // MoE returns `y.type_as(x)` after accumulating routed and shared
3084    // branches in f32.
3085    bf16_inplace(out);
3086}
3087
3088/// `F.softplus(x).sqrt()`: `log1p(exp(x))`, linear past torch's threshold
3089/// of 20. `ln_1p` keeps strongly negative logits positive; `(1 + e^x).ln()`
3090/// rounds them to an exact zero score below x ~ -17.
3091#[inline]
3092fn sqrt_softplus(v: f32) -> f32 {
3093    if v > 20.0 { v } else { v.exp().ln_1p() }.sqrt()
3094}
3095
3096fn hc_mixes(
3097    x: &[f32],
3098    fn_w: &[f32],
3099    base: &[f32],
3100    scale: &[f32; 3],
3101    cfg: &Dsv41Cfg,
3102    pool: Option<&Pool>,
3103    pre: &mut [f32],
3104    post: &mut [f32],
3105    comb: &mut [f32],
3106) {
3107    let mix_n = (2 + cfg.hc_mult) * cfg.hc_mult;
3108    let mut mixes = vec![0.0f32; mix_n];
3109    crate::dsv4::hc_mixes(x, fn_w, mix_n, cfg.norm_eps, pool, &mut mixes);
3110    crate::dsv4::hc_split_sinkhorn(
3111        &mixes,
3112        scale,
3113        base,
3114        cfg.hc_mult,
3115        cfg.hc_sinkhorn_iters,
3116        cfg.hc_eps,
3117        pre,
3118        post,
3119        comb,
3120    );
3121}
3122
3123/// Run one token through the complete V4.1 stack.  `token_id` is used for
3124/// embedding and for Engram hashes; the returned vector is the final logits.
3125pub fn forward_token(
3126    globals: &Dsv41Globals,
3127    layers: &[Dsv41Layer],
3128    cfg: &Dsv41Cfg,
3129    st: &mut Dsv41State,
3130    token_id: u32,
3131    position: usize,
3132    pool: Option<&Pool>,
3133    logits: &mut Vec<f32>,
3134) {
3135    forward_token_masked(
3136        globals, layers, cfg, st, token_id, position, true, pool, logits,
3137    )
3138}
3139
3140pub fn forward_token_masked(
3141    globals: &Dsv41Globals,
3142    layers: &[Dsv41Layer],
3143    cfg: &Dsv41Cfg,
3144    st: &mut Dsv41State,
3145    token_id: u32,
3146    position: usize,
3147    participates: bool,
3148    pool: Option<&Pool>,
3149    logits: &mut Vec<f32>,
3150) {
3151    forward_token_masked_with_embedding(
3152        globals,
3153        layers,
3154        cfg,
3155        st,
3156        token_id,
3157        position,
3158        participates,
3159        None,
3160        pool,
3161        logits,
3162        true,
3163    );
3164}
3165
3166/// V4.1 image ingress uses the same text stack but replaces the embedding
3167/// row for image markers/projector outputs. `embedding` is `None` for normal
3168/// text tokens; `want_logits=false` is used for all non-final prefill rows.
3169pub fn forward_token_masked_with_embedding(
3170    globals: &Dsv41Globals,
3171    layers: &[Dsv41Layer],
3172    cfg: &Dsv41Cfg,
3173    st: &mut Dsv41State,
3174    token_id: u32,
3175    position: usize,
3176    participates: bool,
3177    embedding: Option<&[f32]>,
3178    pool: Option<&Pool>,
3179    logits: &mut Vec<f32>,
3180    want_logits: bool,
3181) {
3182    forward_token_impl(
3183        globals,
3184        layers,
3185        cfg,
3186        st,
3187        token_id,
3188        position,
3189        participates,
3190        embedding,
3191        pool,
3192        logits,
3193        want_logits,
3194    );
3195}
3196
3197fn forward_token_impl(
3198    globals: &Dsv41Globals,
3199    layers: &[Dsv41Layer],
3200    cfg: &Dsv41Cfg,
3201    st: &mut Dsv41State,
3202    token_id: u32,
3203    position: usize,
3204    participates: bool,
3205    embedding: Option<&[f32]>,
3206    pool: Option<&Pool>,
3207    logits: &mut Vec<f32>,
3208    want_logits: bool,
3209) {
3210    let total_t0 = prof::start();
3211    prof::note_token(layers.len());
3212    st.pos = position;
3213    let hashes = st.hash.as_mut().map(|h| h.push(token_id, participates));
3214    let mut embed = vec![0.0f32; cfg.dim];
3215    if let Some(embedding) = embedding {
3216        assert_eq!(embedding.len(), cfg.dim, "V4.1 override embedding width");
3217        embed.copy_from_slice(embedding);
3218    } else {
3219        globals.embed.row_f32(
3220            (token_id as usize).min(globals.embed.rows().saturating_sub(1)),
3221            &mut embed,
3222        );
3223    }
3224    trace_stats("embed", position, None, &embed);
3225    let mut h = vec![0.0f32; cfg.hc_mult * cfg.dim];
3226    for copy in 0..cfg.hc_mult {
3227        h[copy * cfg.dim..(copy + 1) * cfg.dim].copy_from_slice(&embed);
3228    }
3229    let mut pre_mix = vec![0.0f32; cfg.hc_mult];
3230    pre_mix[0] = 1.0;
3231    for (li, l) in layers.iter().enumerate() {
3232        if let (Some(e), Some(all)) = (&l.engram, hashes.as_ref()) {
3233            if let Some(ix) = cfg.engram_layers.iter().position(|&id| id == li) {
3234                let engram_t0 = prof::start();
3235                apply_engram(e, &mut h, &all[ix], cfg, participates, pool);
3236                prof::add(&prof::ENGRAM_NS, engram_t0);
3237            }
3238        }
3239        let residual = h.clone();
3240        let mut ap = vec![0.0; cfg.hc_mult];
3241        let mut apo = vec![0.0; cfg.hc_mult];
3242        let mut ac = vec![0.0; cfg.hc_mult * cfg.hc_mult];
3243        hc_mixes(
3244            &h,
3245            &l.hc_attn_fn,
3246            &l.hc_attn_base,
3247            &l.hc_attn_scale,
3248            cfg,
3249            pool,
3250            &mut ap,
3251            &mut apo,
3252            &mut ac,
3253        );
3254        let mut folded = vec![0.0f32; cfg.dim];
3255        crate::dsv4::hc_fold(&h, &pre_mix, cfg.hc_mult, cfg.dim, &mut folded);
3256        rms(&mut folded, &l.attn_norm, cfg.norm_eps);
3257        let mut attn_out = vec![0.0f32; cfg.dim];
3258        let attn_t0 = prof::start();
3259        let _qr = attention(
3260            l,
3261            cfg,
3262            st,
3263            li,
3264            &folded,
3265            if cfg.ratio(li) > 0 {
3266                &globals.inv_freq_compress
3267            } else {
3268                &globals.inv_freq_window
3269            },
3270            pool,
3271            &mut attn_out,
3272        );
3273        prof::add(&prof::ATTN_NS, attn_t0);
3274        trace_stats("attn", position, Some(li), &attn_out);
3275        crate::dsv4::hc_expand(
3276            &attn_out,
3277            &residual,
3278            &apo,
3279            &ac,
3280            cfg.hc_mult,
3281            cfg.dim,
3282            &mut h,
3283        );
3284        bf16_inplace(&mut h);
3285        let residual2 = h.clone();
3286        let mut fp = vec![0.0; cfg.hc_mult];
3287        let mut fpo = vec![0.0; cfg.hc_mult];
3288        let mut fc = vec![0.0; cfg.hc_mult * cfg.hc_mult];
3289        hc_mixes(
3290            &h,
3291            &l.hc_ffn_fn,
3292            &l.hc_ffn_base,
3293            &l.hc_ffn_scale,
3294            cfg,
3295            pool,
3296            &mut fp,
3297            &mut fpo,
3298            &mut fc,
3299        );
3300        let mut ff = vec![0.0f32; cfg.dim];
3301        crate::dsv4::hc_fold(&h, &ap, cfg.hc_mult, cfg.dim, &mut folded);
3302        rms(&mut folded, &l.ffn_norm, cfg.norm_eps);
3303        // `participates=false` marks image span tokens.  Those tokens use
3304        // the VL routing correction bias while still bypassing Engram.
3305        let moe_t0 = prof::start();
3306        moe(
3307            l,
3308            cfg,
3309            &folded,
3310            pool,
3311            !participates,
3312            position,
3313            li,
3314            layers.len(),
3315            st,
3316            &mut ff,
3317        );
3318        prof::add(&prof::MOE_NS, moe_t0);
3319        crate::dsv4::hc_expand(&ff, &residual2, &fpo, &fc, cfg.hc_mult, cfg.dim, &mut h);
3320        bf16_inplace(&mut h);
3321        pre_mix.copy_from_slice(&fp);
3322        trace_stats("block", position, Some(li), &h);
3323        trace_stats("pre", position, Some(li), &pre_mix);
3324        if st
3325            .moe_capture
3326            .as_ref()
3327            .is_some_and(|c| c.stop_after_hi && li + 1 >= c.hi)
3328        {
3329            // Calibration capture: nothing at or above `hi` is recorded.
3330            logits.clear();
3331            prof::add(&prof::TOTAL_NS, total_t0);
3332            return;
3333        }
3334    }
3335    let head_t0 = prof::start();
3336    let mut final_h = vec![0.0f32; cfg.dim];
3337    crate::dsv4::hc_fold(&h, &pre_mix, cfg.hc_mult, cfg.dim, &mut final_h);
3338    rms(&mut final_h, &globals.norm, cfg.norm_eps);
3339    if want_logits {
3340        logits.resize(globals.head.rows(), 0.0);
3341        globals.head.matvec(&final_h, logits, pool);
3342        trace_stats("final", position, None, &final_h);
3343        trace_stats("logits", position, None, logits);
3344    } else {
3345        logits.clear();
3346    }
3347    prof::add(&prof::HEAD_NS, head_t0);
3348    prof::add(&prof::TOTAL_NS, total_t0);
3349}
3350
3351/// Print the opt-in V4.1 stage profile from the CLI's normal completion path.
3352pub fn profile_report() {
3353    prof::report();
3354}
3355
3356/// The `CMF_DSV41_PROF` path counters, cumulative for the process:
3357/// `(gpu_attn, gpu_attn_fallbacks, gpu_moe, cpu_moe)`. Zero unless the
3358/// profile is on. For tests that must prove a device path ran: a refused
3359/// device frame falls back to the host silently and still passes a numeric
3360/// check.
3361#[doc(hidden)]
3362pub fn profile_counters() -> (u64, u64, u64, u64) {
3363    use std::sync::atomic::Ordering::Relaxed;
3364    (
3365        prof::GPU_ATTN_CALLS.load(Relaxed),
3366        prof::GPU_ATTN_FALLBACKS.load(Relaxed),
3367        prof::GPU_MOE_CALLS.load(Relaxed),
3368        prof::CPU_MOE_CALLS.load(Relaxed),
3369    )
3370}
3371
3372pub fn forward_chunk(
3373    globals: &Dsv41Globals,
3374    layers: &[Dsv41Layer],
3375    cfg: &Dsv41Cfg,
3376    st: &mut Dsv41State,
3377    ids: &[u32],
3378    start: usize,
3379    pool: Option<&Pool>,
3380    logits: &mut Vec<f32>,
3381) {
3382    for (i, &id) in ids.iter().enumerate() {
3383        forward_token_masked_with_embedding(
3384            globals,
3385            layers,
3386            cfg,
3387            st,
3388            id,
3389            start + i,
3390            true,
3391            None,
3392            pool,
3393            logits,
3394            i + 1 == ids.len(),
3395        );
3396    }
3397}
3398
3399/// Prefill variant for multimodal ingress. Each optional row corresponds to
3400/// one token in `ids`; image rows are already produced by `VisionModel` and
3401/// are marked non-participating for Engram/vision routing.
3402pub fn forward_chunk_masked_with_embeddings(
3403    globals: &Dsv41Globals,
3404    layers: &[Dsv41Layer],
3405    cfg: &Dsv41Cfg,
3406    st: &mut Dsv41State,
3407    ids: &[u32],
3408    start: usize,
3409    embeddings: &[Option<Vec<f32>>],
3410    participates: &[bool],
3411    pool: Option<&Pool>,
3412    logits: &mut Vec<f32>,
3413) {
3414    assert_eq!(ids.len(), embeddings.len());
3415    assert_eq!(ids.len(), participates.len());
3416    for (i, &id) in ids.iter().enumerate() {
3417        forward_token_masked_with_embedding(
3418            globals,
3419            layers,
3420            cfg,
3421            st,
3422            id,
3423            start + i,
3424            participates[i],
3425            embeddings[i].as_deref(),
3426            pool,
3427            logits,
3428            i + 1 == ids.len(),
3429        );
3430    }
3431}
3432
3433/// GPTQ calibration capture (`cortiq quantize-moe`): `ids` as a fresh
3434/// sequence from position 0, recording the MoE input rows and routed expert
3435/// ids of layers `lo..hi` ([`Dsv41MoeCapture`]); each token stops after
3436/// layer `hi - 1`. The sequence state is cleared before and after.
3437#[allow(clippy::too_many_arguments)]
3438pub fn capture_window(
3439    globals: &Dsv41Globals,
3440    layers: &[Dsv41Layer],
3441    cfg: &Dsv41Cfg,
3442    st: &mut Dsv41State,
3443    ids: &[u32],
3444    lo: usize,
3445    hi: usize,
3446    pool: Option<&Pool>,
3447) -> Result<Dsv41MoeCapture, String> {
3448    let hi = hi.min(layers.len());
3449    if lo >= hi {
3450        return Err(format!("capture range {lo}..{hi} is empty"));
3451    }
3452    if cfg.n_routed_experts > u16::MAX as usize {
3453        return Err(format!("{} experts do not fit a u16 id", cfg.n_routed_experts));
3454    }
3455    st.clear();
3456    st.moe_capture = Some(Dsv41MoeCapture {
3457        lo,
3458        hi,
3459        stop_after_hi: true,
3460        x: (lo..hi).map(|_| Vec::with_capacity(ids.len() * cfg.dim)).collect(),
3461        picks: (lo..hi).map(|_| Vec::with_capacity(ids.len() * cfg.top_k)).collect(),
3462        w: (lo..hi).map(|_| Vec::with_capacity(ids.len() * cfg.top_k)).collect(),
3463    });
3464    let mut logits = Vec::new();
3465    for (pos, &id) in ids.iter().enumerate() {
3466        forward_token_masked_with_embedding(
3467            globals,
3468            layers,
3469            cfg,
3470            st,
3471            id,
3472            pos,
3473            true,
3474            None,
3475            pool,
3476            &mut logits,
3477            false,
3478        );
3479    }
3480    let cap = st.moe_capture.take().unwrap_or_default();
3481    st.clear();
3482    for (k, ((x, p), w)) in cap.x.iter().zip(&cap.picks).zip(&cap.w).enumerate() {
3483        if x.len() != ids.len() * cfg.dim || p.len() != ids.len() * cfg.top_k || w.len() != p.len() {
3484            return Err(format!(
3485                "layer {}: captured {} rows / {} routes for {} tokens",
3486                lo + k,
3487                x.len() / cfg.dim.max(1),
3488                p.len() / cfg.top_k.max(1),
3489                ids.len()
3490            ));
3491        }
3492    }
3493    Ok(cap)
3494}
3495
3496/// Compact, row-addressable CSA2 global KV storage.
3497///
3498/// V4.1 stores the post-RoPE main KV in MXFP4/E2M1 with one E4M3 scale
3499/// byte per 16 values.  Indexer K uses the same E2M1 nibbles with one E8M0
3500/// scale byte per 32 values.  Rows stay keyed by the shared CSA2 source;
3501/// callers can gather a selected set into one bounded scratch buffer and
3502/// reuse it for every attention head.
3503pub mod packed_kv {
3504    const FP4_LEVELS: [f32; 8] = [0.0, 0.5, 1.0, 1.5, 2.0, 3.0, 4.0, 6.0];
3505    const MAIN_BLOCK: usize = 16;
3506    const INDEX_BLOCK: usize = 32;
3507
3508    /// A dense row store with two E2M1 values per byte and one scale byte per
3509    /// block.  `values` and `scales` are row-major, so an individual row can
3510    /// be decoded without touching any other history entry.
3511    #[derive(Clone, Debug)]
3512    pub struct PackedRows {
3513        cols: usize,
3514        block: usize,
3515        e8m0_scale: bool,
3516        value_bytes: usize,
3517        scale_bytes: usize,
3518        values: Vec<u8>,
3519        scales: Vec<u8>,
3520    }
3521
3522    impl PackedRows {
3523        /// Construct a store. `cols` must be a multiple of `block` because
3524        /// the source MXFP formats carry exactly one scale for each block.
3525        pub fn new(cols: usize, block: usize, e8m0_scale: bool) -> Result<Self, String> {
3526            if cols == 0 || block == 0 || cols % block != 0 {
3527                return Err(format!(
3528                    "packed KV width {cols} must be a positive multiple of block {block}"
3529                ));
3530            }
3531            Ok(Self {
3532                cols,
3533                block,
3534                e8m0_scale,
3535                value_bytes: cols.div_ceil(2),
3536                scale_bytes: cols / block,
3537                values: Vec::new(),
3538                scales: Vec::new(),
3539            })
3540        }
3541
3542        #[inline]
3543        pub fn cols(&self) -> usize {
3544            self.cols
3545        }
3546
3547        #[inline]
3548        pub fn block(&self) -> usize {
3549            self.block
3550        }
3551
3552        #[inline]
3553        pub fn rows(&self) -> usize {
3554            self.scales.len() / self.scale_bytes
3555        }
3556
3557        #[inline]
3558        pub fn row_bytes(&self) -> usize {
3559            self.value_bytes + self.scale_bytes
3560        }
3561
3562        #[inline]
3563        pub fn bytes(&self) -> usize {
3564            self.values.len() + self.scales.len()
3565        }
3566
3567        #[inline]
3568        pub fn clear(&mut self) {
3569            self.values.clear();
3570            self.scales.clear();
3571        }
3572
3573        /// Quantise and append one source row. The returned index is the
3574        /// stable logical row id used by `row_into` and `gather_rows`.
3575        /// Quantisation is deliberately performed here rather than by a
3576        /// caller-provided f32 mirror, so the packed representation itself is
3577        /// the canonical long-context storage.
3578        pub fn push(&mut self, row: &[f32]) -> usize {
3579            assert_eq!(row.len(), self.cols);
3580            let row_id = self.rows();
3581            let value_base = row_id * self.value_bytes;
3582            let scale_base = row_id * self.scale_bytes;
3583            self.values.resize(value_base + self.value_bytes, 0);
3584            self.scales.resize(scale_base + self.scale_bytes, 0);
3585            for block_id in 0..self.scale_bytes {
3586                let begin = block_id * self.block;
3587                let end = begin + self.block;
3588                let min_amax = if self.e8m0_scale {
3589                    6.0 * 2.0f32.powi(-126)
3590                } else {
3591                    6.0 * 2.0f32.powi(-9)
3592                };
3593                let max = row[begin..end]
3594                    .iter()
3595                    .map(|x| x.abs())
3596                    .fold(min_amax, f32::max);
3597                let raw_scale = max / 6.0;
3598                let scale = if self.e8m0_scale {
3599                    super::round_e8m0_scale(raw_scale)
3600                } else {
3601                    super::e4m3_round(raw_scale.max(6.0 * 2.0f32.powi(-9)))
3602                };
3603                self.scales[scale_base + block_id] = if self.e8m0_scale {
3604                    encode_e8m0_scale(scale)
3605                } else {
3606                    encode_e4m3_scale(scale)
3607                };
3608                for i in begin..end {
3609                    let code = fp4_code(row[i] / scale);
3610                    let dst = value_base + i / 2;
3611                    if i & 1 == 0 {
3612                        self.values[dst] = (self.values[dst] & 0xf0) | code;
3613                    } else {
3614                        self.values[dst] = (self.values[dst] & 0x0f) | (code << 4);
3615                    }
3616                }
3617            }
3618            row_id
3619        }
3620
3621        /// Decode one row to the same BF16-materialised f32 values emitted by
3622        /// the source in-place quantiser. A signed E2M1 zero is retained.
3623        pub fn row_into(&self, row: usize, dst: &mut [f32]) -> bool {
3624            if row >= self.rows() || dst.len() != self.cols {
3625                return false;
3626            }
3627            let value_base = row * self.value_bytes;
3628            let scale_base = row * self.scale_bytes;
3629            for block_id in 0..self.scale_bytes {
3630                let scale_byte = self.scales[scale_base + block_id];
3631                let scale = if self.e8m0_scale {
3632                    super::e8m0_scale(scale_byte)
3633                } else {
3634                    super::fp8_e4m3(scale_byte)
3635                };
3636                let begin = block_id * self.block;
3637                let end = begin + self.block;
3638                for i in begin..end {
3639                    let code = if i & 1 == 0 {
3640                        self.values[value_base + i / 2] & 0x0f
3641                    } else {
3642                        self.values[value_base + i / 2] >> 4
3643                    };
3644                    let mag = FP4_LEVELS[(code & 0x07) as usize];
3645                    let signed = if code & 0x08 != 0 { -mag } else { mag };
3646                    // Multiplication by a positive scale does not reliably
3647                    // preserve a negative zero on every compiler path.
3648                    let mut value = signed * scale;
3649                    if mag == 0.0 && code & 0x08 != 0 {
3650                        value = -0.0;
3651                    }
3652                    dst[i] = super::bf16_roundtrip(value);
3653                }
3654            }
3655            true
3656        }
3657
3658        /// Decode the requested rows once into contiguous row-major scratch.
3659        /// The caller can pass that same scratch to all attention heads.
3660        pub fn gather_rows(&self, rows: &[usize], dst: &mut [f32]) -> bool {
3661            if dst.len() != rows.len() * self.cols {
3662                return false;
3663            }
3664            for (i, &row) in rows.iter().enumerate() {
3665                if !self.row_into(row, &mut dst[i * self.cols..(i + 1) * self.cols]) {
3666                    return false;
3667                }
3668            }
3669            true
3670        }
3671
3672        /// Decode all rows into caller-owned scratch. This is intended for
3673        /// bounded diagnostic/export paths; attention should use `gather_rows`.
3674        pub fn all_into(&self, dst: &mut [f32]) -> bool {
3675            if dst.len() != self.rows() * self.cols {
3676                return false;
3677            }
3678            self.gather_rows(&(0..self.rows()).collect::<Vec<_>>(), dst)
3679        }
3680    }
3681
3682    /// The two CSA2 global streams. Main and index rows are each keyed by
3683    /// the source slot, so cross-layer reuse never duplicates history.
3684    #[derive(Clone, Debug)]
3685    pub struct Dsv41PackedKvCache {
3686        main: Vec<PackedRows>,
3687        index: Vec<PackedRows>,
3688    }
3689
3690    impl Dsv41PackedKvCache {
3691        pub fn new(
3692            source_count: usize,
3693            main_cols: usize,
3694            index_cols: usize,
3695        ) -> Result<Self, String> {
3696            let mut main = Vec::with_capacity(source_count);
3697            let mut index = Vec::with_capacity(source_count);
3698            for _ in 0..source_count {
3699                main.push(PackedRows::new(main_cols, MAIN_BLOCK, false)?);
3700                index.push(PackedRows::new(index_cols, INDEX_BLOCK, true)?);
3701            }
3702            Ok(Self { main, index })
3703        }
3704
3705        #[inline]
3706        pub fn main_store(&self, source: usize) -> Option<&PackedRows> {
3707            self.main.get(source)
3708        }
3709
3710        #[inline]
3711        pub fn index_store(&self, source: usize) -> Option<&PackedRows> {
3712            self.index.get(source)
3713        }
3714
3715        #[inline]
3716        pub fn main_rows(&self, source: usize) -> usize {
3717            self.main_store(source).map_or(0, PackedRows::rows)
3718        }
3719
3720        #[inline]
3721        pub fn index_rows(&self, source: usize) -> usize {
3722            self.index_store(source).map_or(0, PackedRows::rows)
3723        }
3724
3725        #[inline]
3726        pub fn main_row_bytes(&self, source: usize) -> usize {
3727            self.main_store(source).map_or(0, PackedRows::row_bytes)
3728        }
3729
3730        #[inline]
3731        pub fn index_row_bytes(&self, source: usize) -> usize {
3732            self.index_store(source).map_or(0, PackedRows::row_bytes)
3733        }
3734
3735        #[inline]
3736        pub fn main_bytes(&self) -> usize {
3737            self.main.iter().map(PackedRows::bytes).sum()
3738        }
3739
3740        #[inline]
3741        pub fn index_bytes(&self) -> usize {
3742            self.index.iter().map(PackedRows::bytes).sum()
3743        }
3744
3745        #[inline]
3746        pub fn bytes(&self) -> usize {
3747            self.main_bytes() + self.index_bytes()
3748        }
3749
3750        pub fn clear(&mut self) {
3751            for store in &mut self.main {
3752                store.clear();
3753            }
3754            for store in &mut self.index {
3755                store.clear();
3756            }
3757        }
3758
3759        pub fn push_main(&mut self, source: usize, row: &[f32]) -> Option<usize> {
3760            self.main.get_mut(source).map(|store| store.push(row))
3761        }
3762
3763        pub fn push_index(&mut self, source: usize, row: &[f32]) -> Option<usize> {
3764            self.index.get_mut(source).map(|store| store.push(row))
3765        }
3766
3767        pub fn main_row_into(&self, source: usize, row: usize, dst: &mut [f32]) -> bool {
3768            self.main_store(source)
3769                .is_some_and(|store| store.row_into(row, dst))
3770        }
3771
3772        pub fn index_row_into(&self, source: usize, row: usize, dst: &mut [f32]) -> bool {
3773            self.index_store(source)
3774                .is_some_and(|store| store.row_into(row, dst))
3775        }
3776
3777        /// Narrow GPU/CPU adapter: decode selected main rows exactly once.
3778        pub fn gather_main_rows(&self, source: usize, rows: &[usize], dst: &mut [f32]) -> bool {
3779            self.main_store(source)
3780                .is_some_and(|store| store.gather_rows(rows, dst))
3781        }
3782
3783        /// Narrow indexer adapter; index scoring can stream one row at a time
3784        /// through `index_store` to avoid a history-sized f32 temporary.
3785        pub fn gather_index_rows(&self, source: usize, rows: &[usize], dst: &mut [f32]) -> bool {
3786            self.index_store(source)
3787                .is_some_and(|store| store.gather_rows(rows, dst))
3788        }
3789    }
3790
3791    fn fp4_code(value: f32) -> u8 {
3792        let q = super::fp4_round(value);
3793        let sign = if q.is_sign_negative() { 0x08 } else { 0 };
3794        let aq = q.abs();
3795        let mag = FP4_LEVELS
3796            .iter()
3797            .position(|&level| level.to_bits() == aq.to_bits())
3798            .unwrap_or(7);
3799        sign | mag as u8
3800    }
3801
3802    fn encode_e4m3_scale(scale: f32) -> u8 {
3803        // The source scale is already the result of e4m3_round. Exact lookup
3804        // avoids introducing a second tie rule in the storage adapter.
3805        (0u8..=0x7e)
3806            .find(|&byte| super::fp8_e4m3(byte).to_bits() == scale.to_bits())
3807            .unwrap_or_else(|| panic!("non-E4M3 scale {scale:?}"))
3808    }
3809
3810    fn encode_e8m0_scale(scale: f32) -> u8 {
3811        if !scale.is_finite() || scale <= 0.0 {
3812            return 0;
3813        }
3814        let exponent = scale.log2().round() as i32;
3815        (exponent + 127).clamp(0, 254) as u8
3816    }
3817}
3818#[cfg(test)]
3819mod tests {
3820    use super::*;
3821
3822    /// A fallback-only arch: every value differs from the release, so a key
3823    /// the parser fails to read from the source config shows up as a
3824    /// mismatch instead of silently agreeing.
3825    fn decoy_arch() -> cortiq_core::ModelArch {
3826        serde_json::from_value(serde_json::json!({
3827            "arch_name": "deepseek_v41",
3828            "hidden_size": 8,
3829            "intermediate_size": 16,
3830            "num_layers": 3,
3831            "num_attention_heads": 2,
3832            "num_kv_heads": 1,
3833            "head_dim": 4,
3834            "vocab_size": 32,
3835            "layer_types": ["FullAttention", "FullAttention", "FullAttention"],
3836            "rms_norm_eps": 0.5,
3837            "max_position_embeddings": 64,
3838            "linear_conv_kernel_dim": 0,
3839            "linear_num_key_heads": 0,
3840            "linear_num_value_heads": 0,
3841            "rope_theta": 7.0,
3842            "sliding_window": 3,
3843        }))
3844        .unwrap()
3845    }
3846
3847    fn release_1017_cfg() -> Dsv41Cfg {
3848        let mut compress_ratios = vec![0, 0];
3849        compress_ratios.extend(std::iter::repeat_n(2, 18));
3850        compress_ratios.extend(std::iter::repeat_n(1, 20));
3851        Dsv41Cfg {
3852            dim: 5120,
3853            n_heads: 64,
3854            head_dim: 512,
3855            rope_head_dim: 64,
3856            q_lora_rank: 1280,
3857            o_lora_rank: 1024,
3858            o_groups: 8,
3859            hc_mult: 4,
3860            hc_sinkhorn_iters: 20,
3861            hc_eps: 1e-6,
3862            norm_eps: 1e-20,
3863            n_routed_experts: 384,
3864            top_k: 6,
3865            moe_inter: 2304,
3866            gate_temp: 1.0,
3867            norm_topk_prob: true,
3868            route_scale: 1.5,
3869            swiglu_limit: 10.0,
3870            window: 128,
3871            rope_theta: 10_000.0,
3872            compress_rope_theta: 160_000.0,
3873            rope_factor: 16.0,
3874            original_seq_len: 65_536,
3875            beta_fast: 32.0,
3876            beta_slow: 1.0,
3877            index_heads: 32,
3878            index_head_dim: 128,
3879            index_topk: 512,
3880            candidate_source: Some(20),
3881            candidate_topk_blocks: 2048,
3882            candidate_block_size: 8,
3883            kv_sources: vec![2, 8, 14, 20],
3884            index_sources: vec![2, 8, 14, 20, 24, 28, 32, 36],
3885            compress_ratios,
3886            engram_layers: vec![1, 14],
3887            engram_vocab: 16_000_000,
3888            engram_embeddings: vec![384_006_168, 384_016_682],
3889            engram_max_ngram: 4,
3890            engram_heads: 8,
3891            engram_head_dim: 256,
3892            engram_compressed_vocab: 99_092,
3893            engram_pad_id: 2,
3894            vocab: 129_280,
3895        }
3896    }
3897
3898    /// The published DeepSeek-V4.1-Flash-1017 `config.json` (HF layout,
3899    /// `text_config`) and its `inference/config.json` (flat ModelArgs keys)
3900    /// both parse to the release geometry, independent of the arch
3901    /// fallbacks. The expected values were checked against the parser as it
3902    /// stood in cortiq 0.8.23.
3903    #[test]
3904    fn release_1017_configs_parse_to_release_geometry() {
3905        let hf: serde_json::Value =
3906            serde_json::from_str(include_str!("../tests/fixtures/dsv41/config_1017.json")).unwrap();
3907        let flat: serde_json::Value = serde_json::from_str(include_str!(
3908            "../tests/fixtures/dsv41/inference_config_1017.json"
3909        ))
3910        .unwrap();
3911        for (name, source) in [("config.json", &hf), ("inference/config.json", &flat)] {
3912            let (cfg, n_layers) = Dsv41Cfg::from_source(source, &decoy_arch()).unwrap();
3913            assert_eq!(n_layers, 40, "{name}");
3914            assert_eq!(cfg, release_1017_cfg(), "{name}");
3915        }
3916    }
3917
3918    fn release_text_config() -> serde_json::Value {
3919        let hf: serde_json::Value =
3920            serde_json::from_str(include_str!("../tests/fixtures/dsv41/config_1017.json")).unwrap();
3921        hf["text_config"].clone()
3922    }
3923
3924    #[test]
3925    fn scoring_func_other_than_sqrtsoftplus_is_refused() {
3926        for key in ["scoring_func", "score_func"] {
3927            for bad in [
3928                serde_json::json!("softmax"),
3929                serde_json::json!("sigmoid"),
3930                serde_json::json!(1),
3931            ] {
3932                let mut tc = release_text_config();
3933                tc.as_object_mut().unwrap().remove("scoring_func");
3934                tc[key] = bad.clone();
3935                let err = Dsv41Cfg::from_source(&tc, &decoy_arch()).unwrap_err();
3936                assert!(
3937                    err.contains("scoring_func") && err.contains("sqrtsoftplus"),
3938                    "{key}={bad}: {err}"
3939                );
3940            }
3941            let mut tc = release_text_config();
3942            tc.as_object_mut().unwrap().remove("scoring_func");
3943            tc[key] = serde_json::json!("sqrtsoftplus");
3944            assert!(Dsv41Cfg::from_source(&tc, &decoy_arch()).is_ok(), "{key}");
3945        }
3946        let mut tc = release_text_config();
3947        tc.as_object_mut().unwrap().remove("scoring_func");
3948        assert!(Dsv41Cfg::from_source(&tc, &decoy_arch()).is_ok(), "absent");
3949    }
3950
3951    #[test]
3952    fn negative_candidate_source_disables_candidates() {
3953        for key in ["candidate_source_layer_id", "candidate_source_layer"] {
3954            let mut tc = release_text_config();
3955            tc.as_object_mut()
3956                .unwrap()
3957                .remove("candidate_source_layer_id");
3958            tc[key] = serde_json::json!(-1);
3959            let (cfg, _) = Dsv41Cfg::from_source(&tc, &decoy_arch()).unwrap();
3960            assert_eq!(cfg.candidate_source, None, "{key}");
3961            tc[key] = serde_json::json!(7);
3962            let (cfg, _) = Dsv41Cfg::from_source(&tc, &decoy_arch()).unwrap();
3963            assert_eq!(cfg.candidate_source, Some(7), "{key}");
3964            tc[key] = serde_json::json!("20");
3965            assert!(Dsv41Cfg::from_source(&tc, &decoy_arch()).is_err(), "{key}");
3966        }
3967        let mut tc = release_text_config();
3968        tc.as_object_mut()
3969            .unwrap()
3970            .remove("candidate_source_layer_id");
3971        let (cfg, _) = Dsv41Cfg::from_source(&tc, &decoy_arch()).unwrap();
3972        assert_eq!(cfg.candidate_source, Some(20), "absent keeps the release layer");
3973    }
3974
3975    /// A null candidate key is skipped (the other spelling, or the release
3976    /// default, applies); an integral float is an integer; a fractional one
3977    /// or a string is refused with the key it came from.
3978    #[test]
3979    fn candidate_source_skips_null_and_accepts_integral_float() {
3980        let with = |pairs: &[(&str, serde_json::Value)]| {
3981            let mut tc = release_text_config();
3982            let obj = tc.as_object_mut().unwrap();
3983            obj.remove("candidate_source_layer_id");
3984            for (k, v) in pairs {
3985                obj.insert((*k).into(), v.clone());
3986            }
3987            Dsv41Cfg::from_source(&tc, &decoy_arch()).map(|(cfg, _)| cfg.candidate_source)
3988        };
3989        let null = serde_json::Value::Null;
3990        assert_eq!(
3991            with(&[("candidate_source_layer_id", serde_json::json!(20.0))]),
3992            Ok(Some(20))
3993        );
3994        assert_eq!(
3995            with(&[("candidate_source_layer", serde_json::json!(-1.0))]),
3996            Ok(None)
3997        );
3998        assert_eq!(
3999            with(&[
4000                ("candidate_source_layer", null.clone()),
4001                ("candidate_source_layer_id", serde_json::json!(7)),
4002            ]),
4003            Ok(Some(7)),
4004            "a null first spelling falls through to the second"
4005        );
4006        assert_eq!(
4007            with(&[("candidate_source_layer_id", null.clone())]),
4008            Ok(Some(20)),
4009            "null alone is absent"
4010        );
4011        for bad in [serde_json::json!(20.5), serde_json::json!("20")] {
4012            let err = with(&[("candidate_source_layer_id", bad.clone())]).unwrap_err();
4013            assert!(err.contains("candidate_source_layer_id"), "{bad}: {err}");
4014        }
4015        let err = with(&[("candidate_source_layer", serde_json::json!(1.5))]).unwrap_err();
4016        assert!(
4017            err.contains("text_config.candidate_source_layer ="),
4018            "{err}"
4019        );
4020    }
4021
4022    #[test]
4023    fn sqrt_softplus_keeps_negative_logits_positive() {
4024        for x in [-40.0f32, -30.0, -17.5, -3.0, 0.0, 1.5, 19.9, 20.0, 25.0] {
4025            let xd = x as f64;
4026            let want = if xd > 20.0 { xd } else { xd.exp().ln_1p() }.sqrt();
4027            let got = sqrt_softplus(x) as f64;
4028            assert!(got > 0.0, "x={x}");
4029            assert!((got - want).abs() <= want * 1e-6, "x={x}: {got} vs {want}");
4030        }
4031        // The old (1 + e^x).ln() form returned exactly zero here.
4032        assert_eq!((1.0f32 + (-30.0f32).exp()).ln(), 0.0);
4033    }
4034
4035    /// kernel.py `act_quant(..., inplace=True)` written out independently:
4036    /// f64 arithmetic, an explicit E4M3FN grid with nearest-even ties, and
4037    /// the bit-level `fast_log2_ceil`/`fast_pow2` scale.
4038    fn act_quant_reference(block: &[f32]) -> Vec<f32> {
4039        let amax = block.iter().fold(0.0f32, |m, x| m.max(x.abs())).max(1e-4);
4040        let t = amax * (1.0f32 / 448.0);
4041        let bits = t.to_bits();
4042        let e = ((bits >> 23) & 0xff) as i32 - 127 + i32::from(bits & 0x7f_ffff != 0);
4043        let s = 2f64.powi(e);
4044        block
4045            .iter()
4046            .map(|&x| {
4047                let v = (x as f64 / s).clamp(-448.0, 448.0);
4048                let a = v.abs();
4049                let q = if a == 0.0 {
4050                    0.0
4051                } else {
4052                    let quantum = if a < 2f64.powi(-6) {
4053                        2f64.powi(-9)
4054                    } else {
4055                        2f64.powi(((a.to_bits() >> 52) & 0x7ff) as i32 - 1023 - 3)
4056                    };
4057                    let n = a / quantum;
4058                    let f = n.floor();
4059                    let r = if n - f > 0.5 || (n - f == 0.5 && f % 2.0 != 0.0) {
4060                        f + 1.0
4061                    } else {
4062                        f
4063                    };
4064                    (r * quantum).min(448.0)
4065                };
4066                let y = if v < 0.0 { -q } else { q } * s;
4067                // `T.Cast(bf16, ...)`: the product is BF16-exact already.
4068                assert_eq!(y as f32 as f64, y);
4069                y as f32
4070            })
4071            .collect()
4072    }
4073
4074    /// The act_quant(o) test set: 2000 random BF16 blocks and the edge
4075    /// blocks listed inside. The last block is the nearest-even tie block.
4076    fn act_quant_test_blocks() -> Vec<Vec<f32>> {
4077        let mut state = 0x5EED_u64;
4078        let mut next = || {
4079            state = state.wrapping_mul(6364136223846793005).wrapping_add(1442695040888963407);
4080            (state >> 33) as u32
4081        };
4082        let mut blocks: Vec<Vec<f32>> = Vec::new();
4083        // Random BF16 blocks over a wide dynamic range.
4084        for _ in 0..2000 {
4085            let exp = (next() % 40) as i32 - 30;
4086            blocks.push(
4087                (0..32)
4088                    .map(|_| {
4089                        let m = (next() % 65536) as f32 / 32768.0 - 1.0;
4090                        bf16_roundtrip(m * 2f32.powi(exp))
4091                    })
4092                    .collect(),
4093            );
4094        }
4095        // Edge blocks: all zeros (the 1e-4 amax floor), a signed zero, an
4096        // exact 448*2^k maximum (scale boundary), a maximum one f32 ulp above
4097        // it (where a rounded log2 picks the smaller scale), tiny values on
4098        // the E4M3 subnormal grid, and exact nearest-even ties.
4099        blocks.push(vec![0.0; 32]);
4100        let mut b = vec![-0.0f32; 32];
4101        b[3] = 1e-6;
4102        blocks.push(b);
4103        let mut b = vec![1.0f32; 32];
4104        b[0] = 448.0 * 2f32.powi(-3);
4105        blocks.push(b);
4106        let mut b = vec![0.3f32; 32];
4107        b[5] = f32::from_bits((448.0f32 * 2f32.powi(-10)).to_bits() + 1);
4108        // 1.5 subnormal quanta at the correct scale 2^-9 (tie -> 2), but an
4109        // exact 3 quanta at the 2^-10 a rounded log2 would choose.
4110        b[7] = 3.0 * 2f32.powi(-19);
4111        blocks.push(b);
4112        let mut b: Vec<f32> = (0..32).map(|i| i as f32 * 2f32.powi(-17)).collect();
4113        b[31] = 448.0 * 2f32.powi(-8);
4114        blocks.push(b);
4115        // With s = 1 (amax 448 / 2^0): 17 lies between 16 and 18 (tie ->
4116        // 16), 19 between 18 and 20 (tie -> 20), 0.0068359375 = 3.5*2^-9.
4117        let mut b = vec![17.0f32, 19.0, -17.0, 0.0068359375, 300.0, 448.0];
4118        b.resize(32, 1.0);
4119        blocks.push(b);
4120        blocks
4121    }
4122
4123    #[test]
4124    fn fp8_activation_matches_kernel_act_quant() {
4125        let blocks = act_quant_test_blocks();
4126        for (i, block) in blocks.iter().enumerate() {
4127            let mut got = block.clone();
4128            fp8_activation_inplace(&mut got);
4129            let want = act_quant_reference(block);
4130            for (j, (g, w)) in got.iter().zip(&want).enumerate() {
4131                assert_eq!(g.to_bits(), w.to_bits(), "block {i} value {j}: {g} vs {w}");
4132            }
4133        }
4134        let tie = &blocks[blocks.len() - 1];
4135        let mut q = tie.clone();
4136        fp8_activation_inplace(&mut q);
4137        assert_eq!(&q[..4], &[16.0, 20.0, -16.0, 4.0 * 2f32.powi(-9)]);
4138    }
4139
4140    #[test]
4141    fn attention_output_quantizes_per_32_block() {
4142        // Two 64-wide groups: four independent blocks with their own scales.
4143        let mut o: Vec<f32> = (0..128)
4144            .map(|i| bf16_roundtrip(((i * 37 % 101) as f32 - 50.0) * 2f32.powi(-(i as i32 / 32) * 3)))
4145            .collect();
4146        let want: Vec<f32> = o.chunks(32).flat_map(act_quant_reference).collect();
4147        quantize_attention_output(&mut o, 2);
4148        assert_eq!(o, want);
4149    }
4150
4151    #[test]
4152    fn fp8_reference_points() {
4153        assert_eq!(fp8_e4m3(0), 0.0);
4154        assert_eq!(fp8_e4m3(0x38), 1.0);
4155        assert_eq!(fp8_e4m3(0xb8), -1.0);
4156        assert_eq!(fp8_e4m3(0x3c), 1.5);
4157        assert_eq!(fp8_e4m3(0x7e), 448.0);
4158        assert!(fp8_e4m3(0x7f).is_nan());
4159        assert_eq!(e8m0_scale(127), 1.0);
4160        assert_eq!(e8m0_scale(128), 2.0);
4161    }
4162
4163    #[test]
4164    fn fp8_rounds_finite_top_band_and_ties_even() {
4165        // Exponent field 15 has six finite mantissas.  The old scalar path
4166        // returned 448 for every value in this band, which corrupted 256..416.
4167        for (input, expected) in [
4168            (256.0, 256.0),
4169            (288.0, 288.0),
4170            (320.0, 320.0),
4171            (352.0, 352.0),
4172            (384.0, 384.0),
4173            (416.0, 416.0),
4174            (448.0, 448.0),
4175            (480.0, 448.0),
4176            (-320.0, -320.0),
4177        ] {
4178            assert_eq!(e4m3_round(input), expected, "input={input}");
4179        }
4180        // Half-way values choose the even mantissa, including the
4181        // subnormal-to-normal carry at 8 * 2^-9.
4182        let q = 2.0f32.powi(-9);
4183        assert_eq!(e4m3_round(0.5 * q), 0.0);
4184        assert_eq!(e4m3_round(1.5 * q), 2.0 * q);
4185        assert_eq!(e4m3_round(7.5 * q), 8.0 * q);
4186        assert_eq!(e4m3_round(272.0), 256.0);
4187        assert_eq!(e4m3_round(304.0), 320.0);
4188        assert_eq!(e4m3_round(432.0), 448.0);
4189    }
4190
4191    /// The device `fp8_act_quant32` kernel (the V4.1 attention frame's
4192    /// act_quant(o)) against the host quantizer, bit for bit: the reference
4193    /// test set above, plus full-mantissa f32 blocks over the whole finite
4194    /// range and the f32-underflow corner of the sign rule. The repo's GPU
4195    /// test convention: skips unless `CMF_GPU` selects wgpu, fails when it
4196    /// does and no context comes up, and with a context up a missing result
4197    /// (module rejected, readback failed) is a failure, not a skip.
4198    #[cfg(feature = "gpu")]
4199    #[test]
4200    fn fp8_act_quant32_device_matches_host_bits() {
4201        match crate::gpu_wgpu::selected_and_up() {
4202            None => {
4203                crate::gpu_wgpu::skip_or_fail(module_path!());
4204                return;
4205            }
4206            Some(false) => panic!("CMF_GPU selects wgpu but no wgpu context came up"),
4207            Some(true) => {}
4208        }
4209        let mut blocks = act_quant_test_blocks();
4210        let mut state = 0xF8A7_u64;
4211        let mut next = || {
4212            state = state
4213                .wrapping_mul(6364136223846793005)
4214                .wrapping_add(1442695040888963407);
4215            (state >> 32) as u32
4216        };
4217        for _ in 0..2000 {
4218            // A block exponent anywhere in the finite range, values up to 40
4219            // binades below it (f32 subnormals included), any mantissa.
4220            let top = (next() % 250) as i32 - 125;
4221            blocks.push(
4222                (0..32)
4223                    .map(|_| {
4224                        let e = (top - (next() % 40) as i32).clamp(-149, 126);
4225                        let m = 1.0 + (next() & 0x7f_ffff) as f32 / 8_388_608.0;
4226                        let v = m * 2f32.powi(e.max(-126)) * 2f32.powi((e + 126).min(0));
4227                        if next() & 1 == 1 { -v } else { v }
4228                    })
4229                    .collect(),
4230            );
4231        }
4232        // Scale 2^25: these negative values divide to an f32 subnormal (the
4233        // host keeps the sign of the zero they round to) or underflow to an
4234        // exact zero (the host returns +0); f32 subnormal inputs too.
4235        let mut b = vec![0.0f32; 32];
4236        b[0] = 448.0 * 2f32.powi(25);
4237        for (i, v) in [
4238            -2f32.powi(-126),
4239            -2f32.powi(-124),
4240            -3.0 * 2f32.powi(-127),
4241            -2f32.powi(-125),
4242            -1.5 * 2f32.powi(-125),
4243            -5.0 * 2f32.powi(-149),
4244            -f32::from_bits(0x007f_ffff),
4245            1e-30,
4246            -1e-30,
4247            -0.0,
4248        ]
4249        .into_iter()
4250        .enumerate()
4251        {
4252            b[i + 1] = v;
4253        }
4254        blocks.push(b);
4255        // Scale 2^-5: f32 subnormal inputs divide to a nonzero quotient.
4256        let mut b = vec![-f32::from_bits(3); 32];
4257        b[0] = 14.0;
4258        b[1] = f32::from_bits(1);
4259        blocks.push(b);
4260        let flat: Vec<f32> = blocks.iter().flatten().copied().collect();
4261        let got = crate::gpu_wgpu::fp8_act::act_quant32_for_test(&flat).expect(
4262            "wgpu is up but fp8_act_quant32 gave no result (module rejected or readback failed)",
4263        );
4264        let mut want = flat.clone();
4265        fp8_activation_inplace(&mut want);
4266        let mut bad = 0usize;
4267        for (i, ((g, w), x)) in got.iter().zip(&want).zip(&flat).enumerate() {
4268            if g.to_bits() != w.to_bits() {
4269                if bad < 8 {
4270                    eprintln!(
4271                        "block {} value {}: x {x:e} ({:#010x}) device {g:e} ({:#010x}) host {w:e} ({:#010x})",
4272                        i / 32,
4273                        i % 32,
4274                        x.to_bits(),
4275                        g.to_bits(),
4276                        w.to_bits()
4277                    );
4278                }
4279                bad += 1;
4280            }
4281        }
4282        eprintln!(
4283            "fp8_act_quant32: {} blocks, {bad} values differ from the host",
4284            blocks.len()
4285        );
4286        assert_eq!(
4287            bad, 0,
4288            "device act_quant(o) is not bit-identical to the host"
4289        );
4290    }
4291
4292    #[test]
4293    fn fp4_rounds_ties_to_even_level() {
4294        assert_eq!(fp4_round(0.25), 0.0);
4295        assert_eq!(fp4_round(0.75), 1.0);
4296        assert_eq!(fp4_round(1.25), 1.0);
4297        assert_eq!(fp4_round(1.75), 2.0);
4298        assert_eq!(fp4_round(2.5), 2.0);
4299        assert_eq!(fp4_round(3.5), 4.0);
4300        assert_eq!(fp4_round(5.0), 4.0);
4301        assert_eq!(fp4_round(-0.75), -1.0);
4302    }
4303
4304    #[test]
4305    fn release_hash_multipliers_are_stable() {
4306        assert_eq!(
4307            hash_multipliers(1, 4, 99092),
4308            [
4309                76632096046245,
4310                4839876093313,
4311                35959672319349,
4312                73987337458391
4313            ]
4314        );
4315        assert_eq!(
4316            hash_multipliers(14, 4, 99092),
4317            [
4318                67716810739261,
4319                51510806800915,
4320                30921347202721,
4321                82619226485591
4322            ]
4323        );
4324    }
4325
4326    #[test]
4327    fn release_hash_layout_is_disjoint_and_stable() {
4328        let h = EngramHash::new(
4329            vec![1, 14],
4330            4,
4331            8,
4332            16_000_000,
4333            99_092,
4334            2,
4335            (0..99_092).map(|v| v as u32).collect(),
4336        )
4337        .unwrap();
4338        assert_eq!(
4339            h.primes[0][0],
4340            [
4341                16_000_057, 16_000_079, 16_000_081, 16_000_097, 16_000_121, 16_000_129, 16_000_133,
4342                16_000_183,
4343            ]
4344        );
4345        // `offsets[n]` is the flattened start of n-gram order n.  Each
4346        // order owns eight disjoint prime ranges, so the starts are the
4347        // cumulative sums before orders 1, 2, and 3 in the oracle layout.
4348        assert_eq!(h.offsets[0], [0, 128_000_880, 256_002_934]);
4349        assert_eq!(h.primes[1][2][7], 16_000_889);
4350        assert_eq!(h.offsets[1], [0, 128_004_290, 256_009_984]);
4351    }
4352
4353    #[test]
4354    fn dead_tokens_break_ngrams() {
4355        let map = (0..16).collect::<Vec<u32>>();
4356        let mut h = EngramHash::new(vec![1], 4, 2, 16, 16, 2, map).unwrap();
4357        let first = h.push(3, true);
4358        let second = h.push(4, false);
4359        let third = h.push(5, true);
4360        assert_ne!(first, second);
4361        assert_ne!(second, third);
4362        assert_eq!(h.history.last().copied(), Some(5));
4363    }
4364
4365    #[test]
4366    fn candidate_mask_is_bounded() {
4367        let mut scores = vec![f32::NEG_INFINITY; 32];
4368        scores[1] = 1.0;
4369        scores[2] = 2.0;
4370        scores[9] = 3.0;
4371        scores[31] = 0.0;
4372        let m = candidate_blocks(&scores, 2, 8);
4373        assert_eq!(m.len(), 32);
4374        assert!(m.iter().filter(|&&x| x).count() <= 24);
4375    }
4376
4377    #[test]
4378    fn split_attention_matches_materialized_stream() {
4379        let window = vec![
4380            0.10, 0.20, 0.30, 0.40, // window row 0
4381            -0.20, 0.30, 0.50, -0.10, // window row 1
4382        ];
4383        let compressed = vec![
4384            0.70, -0.40, 0.10, 0.20, // compressed row 0
4385            -0.30, 0.60, 0.20, 0.90, // compressed row 1
4386        ];
4387        let idxs = [0, 2, 3];
4388        let mut stream = window.clone();
4389        stream.extend_from_slice(&compressed);
4390        let q = [0.25, -0.10, 0.40, 0.05];
4391        let mut expected = [0.0; 4];
4392        crate::dsv4::sparse_attend(&q, &stream, &idxs, -0.2, 0.5, 4, &mut expected);
4393        let mut actual = [0.0; 4];
4394        sparse_attend_split(
4395            &q,
4396            &window,
4397            Some(&compressed),
4398            &idxs,
4399            -0.2,
4400            0.5,
4401            2,
4402            4,
4403            &mut actual,
4404        );
4405        for (a, b) in actual.iter().zip(expected) {
4406            assert!((a - b).abs() < 1e-7, "split={a} materialized={b}");
4407        }
4408    }
4409
4410    #[test]
4411    fn packed_rows_match_source_fp4_and_preserve_signed_zero() {
4412        let mut main_input = (0..32)
4413            .map(|i| (i as f32 - 13.0) * 0.137)
4414            .collect::<Vec<_>>();
4415        main_input[0] = -0.0;
4416        main_input[1] = 0.25;
4417        main_input[16] = 2688.0;
4418        let mut main_ref = main_input.clone();
4419        fp4_inplace(&mut main_ref, 16, false);
4420        let mut main = packed_kv::PackedRows::new(32, 16, false).unwrap();
4421        assert_eq!(main.push(&main_input), 0);
4422        let mut main_decoded = vec![0.0f32; 32];
4423        assert!(main.row_into(0, &mut main_decoded));
4424        for (i, (&expected, &actual)) in main_ref.iter().zip(&main_decoded).enumerate() {
4425            assert_eq!(expected.to_bits(), actual.to_bits(), "main row value {i}");
4426        }
4427        assert!(main_decoded[0].is_sign_negative());
4428
4429        let mut index_input = (0..64)
4430            .map(|i| ((i as f32 * 0.03125).sin()) * 3.0)
4431            .collect::<Vec<_>>();
4432        index_input[32] = -0.0;
4433        let mut index_ref = index_input.clone();
4434        fp4_inplace(&mut index_ref, 32, true);
4435        let mut index = packed_kv::PackedRows::new(64, 32, true).unwrap();
4436        index.push(&index_input);
4437        let mut index_decoded = vec![0.0f32; 64];
4438        assert!(index.row_into(0, &mut index_decoded));
4439        for (i, (&expected, &actual)) in index_ref.iter().zip(&index_decoded).enumerate() {
4440            assert_eq!(expected.to_bits(), actual.to_bits(), "index row value {i}");
4441        }
4442        assert!(index_decoded[32].is_sign_negative());
4443        assert_eq!(main.row_bytes(), 18);
4444        assert_eq!(index.row_bytes(), 34);
4445    }
4446
4447    #[test]
4448    fn packed_cache_gathers_once_and_tracks_source_rows() {
4449        let mut cache = packed_kv::Dsv41PackedKvCache::new(2, 32, 64).unwrap();
4450        let a = (0..32).map(|i| i as f32 * 0.01).collect::<Vec<_>>();
4451        let b = (0..32).map(|i| -(i as f32) * 0.02).collect::<Vec<_>>();
4452        let k = (0..64)
4453            .map(|i| (i as f32 - 20.0) * 0.03)
4454            .collect::<Vec<_>>();
4455        assert_eq!(cache.push_main(1, &a), Some(0));
4456        assert_eq!(cache.push_main(1, &b), Some(1));
4457        assert_eq!(cache.push_index(1, &k), Some(0));
4458        assert_eq!(cache.main_rows(0), 0);
4459        assert_eq!(cache.main_rows(1), 2);
4460        assert_eq!(cache.index_rows(1), 1);
4461        let mut gathered = vec![0.0f32; 64];
4462        assert!(cache.gather_main_rows(1, &[1, 0], &mut gathered));
4463        let mut row = vec![0.0f32; 32];
4464        assert!(cache.main_row_into(1, 1, &mut row));
4465        assert_eq!(&gathered[..32], &row[..]);
4466        assert_eq!(cache.main_row_bytes(1), 18);
4467        assert_eq!(cache.index_row_bytes(1), 34);
4468        assert_eq!(cache.bytes(), 2 * 18 + 34);
4469    }
4470
4471    #[test]
4472    fn v41_global_row_bytes_match_890_byte_slope() {
4473        let cache = packed_kv::Dsv41PackedKvCache::new(4, 512, 128).unwrap();
4474        assert_eq!(cache.main_row_bytes(0), 512 / 2 + 512 / 16);
4475        assert_eq!(cache.index_row_bytes(0), 128 / 2 + 128 / 32);
4476        // The release source has 2.5 global source rows per token across its
4477        // four CSA2 source layers: 2.5 * (288 + 68) = 890 bytes/token.
4478        assert_eq!(
4479            2.5 * (cache.main_row_bytes(0) + cache.index_row_bytes(0)) as f32,
4480            890.0
4481        );
4482    }
4483
4484    #[test]
4485    fn fp4_scales_keep_reference_zero_floors() {
4486        assert_eq!(round_e8m0_scale(2.0f32.powi(-126)), 2.0f32.powi(-126));
4487        assert_eq!(e4m3_round(2.0f32.powi(-9)), 2.0f32.powi(-9));
4488        let mut index = vec![0.0; 32];
4489        fp4_inplace(&mut index, 32, true);
4490        // E8M0's all-zero block floor is 2^-126, rather than the generic
4491        // activation floor used by the FP8 path.
4492        assert_eq!(index, vec![0.0; 32]);
4493        let mut compressed = vec![0.0; 16];
4494        fp4_inplace(&mut compressed, 16, false);
4495        assert_eq!(compressed, vec![0.0; 16]);
4496    }
4497
4498    /// A tiny window-only V4.1 stack of f32 tensors (no compressor, indexer
4499    /// or Engram): enough for the full per-token forward on the host.
4500    fn tiny_stack(n_layers: usize) -> (Dsv41Globals, Vec<Dsv41Layer>, Dsv41Cfg) {
4501        let cfg = Dsv41Cfg {
4502            dim: 32,
4503            n_heads: 2,
4504            head_dim: 16,
4505            rope_head_dim: 4,
4506            q_lora_rank: 8,
4507            o_lora_rank: 4,
4508            o_groups: 2,
4509            hc_mult: 2,
4510            hc_sinkhorn_iters: 3,
4511            hc_eps: 1e-6,
4512            norm_eps: 1e-6,
4513            n_routed_experts: 8,
4514            top_k: 2,
4515            moe_inter: 16,
4516            gate_temp: 1.0,
4517            norm_topk_prob: true,
4518            route_scale: 1.5,
4519            swiglu_limit: 10.0,
4520            window: 8,
4521            rope_theta: 10_000.0,
4522            compress_rope_theta: 160_000.0,
4523            rope_factor: 1.0,
4524            original_seq_len: 64,
4525            beta_fast: 32.0,
4526            beta_slow: 1.0,
4527            index_heads: 1,
4528            index_head_dim: 32,
4529            index_topk: 4,
4530            candidate_source: None,
4531            candidate_topk_blocks: 2,
4532            candidate_block_size: 8,
4533            kv_sources: Vec::new(),
4534            index_sources: Vec::new(),
4535            compress_ratios: vec![0; n_layers],
4536            engram_layers: Vec::new(),
4537            engram_vocab: 0,
4538            engram_embeddings: Vec::new(),
4539            engram_max_ngram: 4,
4540            engram_heads: 1,
4541            engram_head_dim: 16,
4542            engram_compressed_vocab: 0,
4543            engram_pad_id: 2,
4544            vocab: 16,
4545        };
4546        let mut seed = 0x5eed_u64;
4547        let mut rnd = |n: usize, scale: f32| -> Vec<f32> {
4548            (0..n)
4549                .map(|_| {
4550                    seed = splitmix64(seed);
4551                    ((seed >> 40) as f32 / (1u64 << 24) as f32 - 0.5) * 2.0 * scale
4552                })
4553                .collect()
4554        };
4555        let q = |v: Vec<f32>, r: usize, c: usize| QTensor::from_f32(v, r, c);
4556        let (d, hc) = (cfg.dim, cfg.hc_mult);
4557        let mix_n = (2 + hc) * hc;
4558        let globals = Dsv41Globals {
4559            embed: q(rnd(cfg.vocab * d, 1.0), cfg.vocab, d),
4560            norm: vec![1.0; d],
4561            head: q(rnd(cfg.vocab * d, 0.3), cfg.vocab, d),
4562            inv_freq_compress: vec![1.0, 0.01],
4563            inv_freq_window: vec![1.0, 0.01],
4564        };
4565        let mut layers = Vec::new();
4566        for _ in 0..n_layers {
4567            let mut expert = |rnd: &mut dyn FnMut(usize, f32) -> Vec<f32>| Dsv41Expert {
4568                w1: q(rnd(cfg.moe_inter * d, 0.4), cfg.moe_inter, d),
4569                w2: q(rnd(d * cfg.moe_inter, 0.4), d, cfg.moe_inter),
4570                w3: q(rnd(cfg.moe_inter * d, 0.4), cfg.moe_inter, d),
4571            };
4572            let experts = (0..cfg.n_routed_experts).map(|_| expert(&mut rnd)).collect();
4573            let shared = expert(&mut rnd);
4574            let per_group = cfg.n_heads * cfg.head_dim / cfg.o_groups;
4575            layers.push(Dsv41Layer {
4576                attn_norm: vec![1.0; d],
4577                ffn_norm: vec![1.0; d],
4578                wq_a: q(rnd(cfg.q_lora_rank * d, 0.4), cfg.q_lora_rank, d),
4579                q_norm: vec![1.0; cfg.q_lora_rank],
4580                wq_b: q(rnd(cfg.n_heads * cfg.head_dim * cfg.q_lora_rank, 0.4), cfg.n_heads * cfg.head_dim, cfg.q_lora_rank),
4581                wkv: q(rnd(cfg.head_dim * d, 0.4), cfg.head_dim, d),
4582                kv_norm: vec![1.0; cfg.head_dim],
4583                wo_a: q(rnd(cfg.o_groups * cfg.o_lora_rank * per_group, 0.4), cfg.o_groups * cfg.o_lora_rank, per_group),
4584                wo_b: q(rnd(d * cfg.o_groups * cfg.o_lora_rank, 0.4), d, cfg.o_groups * cfg.o_lora_rank),
4585                attn_sink: vec![0.0; cfg.n_heads],
4586                compressor: None,
4587                indexer: None,
4588                hc_attn_fn: rnd(mix_n * hc * d, 0.1),
4589                hc_attn_base: rnd(mix_n, 0.5),
4590                hc_attn_scale: [1.0, 1.0, 1.0],
4591                hc_ffn_fn: rnd(mix_n * hc * d, 0.1),
4592                hc_ffn_base: rnd(mix_n, 0.5),
4593                hc_ffn_scale: [1.0, 1.0, 1.0],
4594                gate: q(rnd(cfg.n_routed_experts * d, 1.0), cfg.n_routed_experts, d),
4595                gate_bias: rnd(cfg.n_routed_experts, 0.2),
4596                gate_bias_vl: None,
4597                experts,
4598                shared,
4599                engram: None,
4600                gpu_expert_ids: Vec::new(),
4601                gpu_shared_ids: None,
4602            });
4603        }
4604        (globals, layers, cfg)
4605    }
4606
4607    /// The quantize-moe capture records the MoE input and the route of
4608    /// every token at the captured layers through the real forward; ending
4609    /// each token after the last captured layer leaves the record unchanged.
4610    #[test]
4611    fn moe_capture_records_inputs_and_routes() {
4612        let (g, layers, cfg) = tiny_stack(3);
4613        let ids: Vec<u32> = (0..13).map(|i| (i * 5 % 16) as u32).collect();
4614        let mut st = Dsv41State::new(&cfg, None);
4615        let cap = capture_window(&g, &layers, &cfg, &mut st, &ids, 1, 2, None).unwrap();
4616        assert_eq!((cap.lo, cap.hi), (1, 2));
4617        assert_eq!(cap.x.len(), 1);
4618        assert_eq!(cap.x[0].len(), ids.len() * cfg.dim);
4619        assert_eq!(cap.picks[0].len(), ids.len() * cfg.top_k);
4620        assert!(st.moe_capture.is_none(), "the capture is handed back, not left on the state");
4621
4622        // the same record through the whole stack (no early stop)
4623        st.clear();
4624        st.moe_capture = Some(Dsv41MoeCapture {
4625            lo: 0,
4626            hi: 2,
4627            stop_after_hi: false,
4628            x: vec![Vec::new(); 2],
4629            picks: vec![Vec::new(); 2],
4630            w: vec![Vec::new(); 2],
4631        });
4632        let mut logits = Vec::new();
4633        for (pos, &id) in ids.iter().enumerate() {
4634            forward_token_masked_with_embedding(&g, &layers, &cfg, &mut st, id, pos, true, None, None, &mut logits, true);
4635        }
4636        assert_eq!(logits.len(), cfg.vocab, "a full forward still produces logits");
4637        let full = st.moe_capture.take().unwrap();
4638        assert_eq!(full.x[1], cap.x[0], "early stop changes no captured row");
4639        assert_eq!(full.picks[1], cap.picks[0]);
4640        assert_eq!(full.w[1], cap.w[0]);
4641        assert_ne!(full.x[0], full.x[1], "layers capture their own inputs");
4642
4643        // the route is V4.1's: sqrt-softplus scores + bias, top-k, from the captured row
4644        let l = &layers[1];
4645        for t in 0..ids.len() {
4646            let x: Vec<f32> = cap.x[0][t * cfg.dim..(t + 1) * cfg.dim]
4647                .iter()
4648                .map(|&h| cortiq_core::quant::f16_to_f32(h))
4649                .collect();
4650            let mut lg = vec![0f32; cfg.n_routed_experts];
4651            l.gate.matvec(&x, &mut lg, None);
4652            let mut sh: Vec<(f32, usize)> = lg
4653                .iter()
4654                .enumerate()
4655                .map(|(e, &v)| ((1.0 + v.exp()).ln().sqrt() + l.gate_bias[e], e))
4656                .collect();
4657            sh.sort_by(|a, b| b.0.partial_cmp(&a.0).unwrap());
4658            let want: Vec<u16> = sh[..cfg.top_k].iter().map(|&(_, e)| e as u16).collect();
4659            assert_eq!(&cap.picks[0][t * cfg.top_k..(t + 1) * cfg.top_k], &want[..], "token {t}");
4660            // route weights: the picks' scores normalised to route_scale
4661            let sc: Vec<f32> = want.iter().map(|&e| (1.0 + lg[e as usize].exp()).ln().sqrt()).collect();
4662            let sum: f32 = sc.iter().sum();
4663            for (j, &v) in sc.iter().enumerate() {
4664                let got = cap.w[0][t * cfg.top_k + j];
4665                assert!((got - v / sum * cfg.route_scale).abs() <= 1e-5 * cfg.route_scale, "token {t} pick {j}: weight {got}");
4666            }
4667            let tot: f32 = cap.w[0][t * cfg.top_k..(t + 1) * cfg.top_k].iter().sum();
4668            assert!((tot - cfg.route_scale).abs() < 1e-4, "token {t}: weights sum to {tot}");
4669        }
4670    }
4671}