1use 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
25pub(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 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#[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#[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#[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#[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#[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 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 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 sign | (15 << 3) | 6
429 } else {
430 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 let exponent = raw.log2().ceil().clamp(-127.0, 127.0);
448 2.0f32.powf(exponent)
449}
450
451#[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 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#[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(¤t) {
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 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 for shift in 0..=n {
638 let (source, dead) = self.history_value(shift);
639 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
691fn 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#[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 pub gate_temp: f32,
760 pub norm_topk_prob: bool,
761 pub route_scale: f32,
762 pub swiglu_limit: f32,
763 pub window: usize,
764 pub rope_theta: f32,
765 pub compress_rope_theta: f32,
766 pub rope_factor: f32,
767 pub original_seq_len: usize,
768 pub beta_fast: f32,
769 pub beta_slow: f32,
770 pub index_heads: usize,
771 pub index_head_dim: usize,
772 pub index_topk: usize,
773 pub candidate_source: 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 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 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 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 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
1107pub struct Dsv41State {
1110 pub pos: usize,
1111 pub window: Vec<Vec<f32>>,
1112 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 pub topk_ready: bool,
1124 pub candidates: Vec<bool>,
1125 pub hash: Option<EngramHash>,
1126 #[cfg(feature = "gpu")]
1130 gpu_kv_id: u64,
1131 #[cfg(feature = "gpu")]
1135 gpu_pool: Option<crate::qwen4_exp::QwenGpuPool>,
1136 #[cfg(feature = "gpu")]
1139 gpu_moe_calls: u64,
1140 pub moe_capture: Option<Dsv41MoeCapture>,
1143}
1144
1145#[derive(Debug, Clone, Default)]
1151pub struct Dsv41MoeCapture {
1152 pub lo: usize,
1153 pub hi: usize,
1154 pub stop_after_hi: bool,
1159 pub x: Vec<Vec<u16>>,
1161 pub picks: Vec<Vec<u16>>,
1164 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 #[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 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#[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#[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#[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
1315fn 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
1395pub 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
1629pub 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 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
1684pub 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 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 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 bf16_inplace(&mut emb);
1772 let mut kv = vec![0.0f32; cfg.dim * (cfg.hc_mult + 1)];
1773 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 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 bf16_inplace(h);
1812}
1813
1814#[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 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 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 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_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#[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
1944fn 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
1959fn 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
1970fn 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 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 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 *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 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 s += bf16_roundtrip(d * weights[head]);
2050 }
2051 if t >= (pos + 1) / ratio.max(1) {
2052 scores[t] = f32::NEG_INFINITY;
2053 } else {
2054 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 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 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 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 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 let win_len = {
2163 let w = &mut st.window[li];
2164 fp8_activation_inplace(&mut kv);
2165 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 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 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 #[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 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 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 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 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 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 &|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 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
2509fn 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 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 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
2682fn 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 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#[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 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 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 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 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 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 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 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 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 bf16_inplace(out);
3086}
3087
3088#[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
3123pub 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
3166pub 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 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 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
3351pub fn profile_report() {
3353 prof::report();
3354}
3355
3356#[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
3399pub 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#[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
3496pub 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 #[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 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 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 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 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 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 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 #[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 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 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 (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 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 #[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 #[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 assert_eq!((1.0f32 + (-30.0f32).exp()).ln(), 0.0);
4033 }
4034
4035 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 assert_eq!(y as f32 as f64, y);
4069 y as f32
4070 })
4071 .collect()
4072 }
4073
4074 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 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 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 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 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 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 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 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 #[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 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 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 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 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, -0.20, 0.30, 0.50, -0.10, ];
4383 let compressed = vec![
4384 0.70, -0.40, 0.10, 0.20, -0.30, 0.60, 0.20, 0.90, ];
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 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 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 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 #[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 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 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 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}