1use crate::Engine;
6use crate::model::{EmbedHost, GpuTensor, HostExps};
7use cudarc::driver::CudaSlice;
8use memra_gguf::config::{ModelConfig, SwigluClamp};
9use memra_gguf::model_plan::{AttentionPlan, MlpPlan};
10use memra_gguf::source::{GgufSource, TensorSource};
11use memra_gguf::{GgmlType, GgufFile};
12use std::collections::HashMap;
13use std::sync::Arc;
14
15fn load_t(
18 e: &Engine,
19 src: &dyn TensorSource,
20 name: &str,
21) -> Result<GpuTensor, Box<dyn std::error::Error>> {
22 GpuTensor::load_from_source(e, src, name)
23}
24fn load_opt(
25 e: &Engine,
26 src: &dyn TensorSource,
27 name: &str,
28) -> Result<Option<GpuTensor>, Box<dyn std::error::Error>> {
29 GpuTensor::load_opt_from_source(e, src, name)
30}
31
32struct ResidencyBytes {
33 experts: HashMap<usize, usize>,
34 rest: usize,
35 saw_experts: bool,
36}
37
38fn block_index(name: &str) -> Option<usize> {
39 name.strip_prefix("blk.")?.split('.').next()?.parse().ok()
40}
41
42fn residency_bytes_by_device<'a>(
43 tensors: impl IntoIterator<Item = (&'a str, usize)>,
44 layer_devices: &[usize],
45 primary_device: usize,
46) -> ResidencyBytes {
47 let mut out = ResidencyBytes {
48 experts: HashMap::new(),
49 rest: 0,
50 saw_experts: false,
51 };
52 for (name, bytes) in tensors {
53 if name.starts_with("blk.") && name.contains("_exps.") {
54 let device = block_index(name)
55 .and_then(|il| layer_devices.get(il).copied())
56 .unwrap_or(primary_device);
57 *out.experts.entry(device).or_default() += bytes;
58 out.saw_experts = true;
59 } else {
60 out.rest += bytes;
61 }
62 }
63 out
64}
65
66pub(crate) struct ResidentPlan {
69 primary_device: usize,
70 layer_devices: Vec<usize>,
71 layer_counts: HashMap<usize, usize>,
72 exact_expert_bytes: Option<HashMap<usize, usize>>,
73 trunk_bytes: usize,
74 decisions: HashMap<usize, bool>,
75 pp: bool,
76}
77
78#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
88enum StepExpertArtifact {
89 #[default]
90 E4m3,
91 Nvfp4,
92}
93
94#[derive(Clone, Debug, Default)]
95struct StepParallelLoadConfig {
96 ep_specs: Vec<crate::tp::StepEpLayerSpec>,
97 tp_specs: Vec<crate::tp::StepTpLayerSpec>,
98 native_p2p: bool,
99 ep_device_arithmetic: bool,
100 f32_mirror: bool,
101 bulk_p2p: bool,
102 nvfp4_device_routes: bool,
103 auto_parallel: bool,
104 tp_attention_expert_overlap: bool,
105 expert_artifact: StepExpertArtifact,
106}
107
108#[derive(Default)]
109pub(crate) struct StepParallelRuntimeRegistry {
110 config: StepParallelLoadConfig,
111 runtimes: HashMap<(Vec<usize>, bool, bool, bool), Arc<crate::tp::TpE4m3HostBounce>>,
112}
113
114#[derive(Clone, Copy, Debug, PartialEq, Eq)]
115enum StepExpertLayout {
116 TensorParallel,
117 ExpertParallel,
118}
119
120#[derive(Clone, Debug, PartialEq, Eq)]
121struct StepExpertSelection {
122 spec: crate::tp::StepEpLayerSpec,
123 layout: StepExpertLayout,
124 configured_by_tp: bool,
125}
126
127fn select_step_expert_layout_inner(
128 layer: usize,
129 ep_specs: &[crate::tp::StepEpLayerSpec],
130 tp_specs: &[crate::tp::StepTpLayerSpec],
131 allow_attention_ep_overlap: bool,
132) -> Result<Option<StepExpertSelection>, String> {
133 let ep = ep_specs.iter().find(|spec| spec.layer == layer);
134 let tp = tp_specs.iter().find(|spec| spec.layer == layer);
135 Ok(match (ep, tp) {
136 (Some(spec), None) => Some(StepExpertSelection {
137 spec: spec.clone(),
138 layout: StepExpertLayout::ExpertParallel,
139 configured_by_tp: false,
140 }),
141 (None, Some(spec)) => Some(StepExpertSelection {
142 spec: spec.clone(),
143 layout: if spec.devices.len() > 2 {
144 StepExpertLayout::ExpertParallel
145 } else {
146 StepExpertLayout::TensorParallel
147 },
148 configured_by_tp: true,
149 }),
150 (None, None) => None,
151 (Some(ep), Some(tp)) => {
152 if !allow_attention_ep_overlap {
153 return Err(format!(
154 "Step layer {layer} cannot enable MEMRA_STEP_EP and MEMRA_STEP_TP together"
155 ));
156 }
157 if ep.devices.first() != tp.devices.first()
158 || tp.devices.iter().any(|device| !ep.devices.contains(device))
159 {
160 return Err(format!(
161 "automatic TP-attention/EP overlap at layer {layer} requires the attention \
162 ranks {:?} to be an owner-first subset of expert ranks {:?}",
163 tp.devices, ep.devices
164 ));
165 }
166 Some(StepExpertSelection {
167 spec: ep.clone(),
168 layout: StepExpertLayout::ExpertParallel,
169 configured_by_tp: false,
170 })
171 }
172 })
173}
174
175#[cfg(test)]
176fn select_step_expert_layout(
177 layer: usize,
178 ep_specs: &[crate::tp::StepEpLayerSpec],
179 tp_specs: &[crate::tp::StepTpLayerSpec],
180) -> Result<Option<StepExpertSelection>, String> {
181 select_step_expert_layout_inner(layer, ep_specs, tp_specs, false)
182}
183
184impl StepParallelRuntimeRegistry {
185 fn with_config(config: StepParallelLoadConfig) -> Self {
186 Self {
187 config,
188 runtimes: HashMap::new(),
189 }
190 }
191
192 fn tp_spec(&self, layer: usize) -> Option<&crate::tp::StepTpLayerSpec> {
193 self.config.tp_specs.iter().find(|spec| spec.layer == layer)
194 }
195
196 fn expert_selection(&self, layer: usize) -> Result<Option<StepExpertSelection>, String> {
197 select_step_expert_layout_inner(
198 layer,
199 &self.config.ep_specs,
200 &self.config.tp_specs,
201 self.config.tp_attention_expert_overlap,
202 )
203 }
204
205 fn runtime(
206 &mut self,
207 devices: &[usize],
208 native_p2p: bool,
209 ep_device_arithmetic: bool,
210 ) -> Result<Arc<crate::tp::TpE4m3HostBounce>, Box<dyn std::error::Error>> {
211 let bulk_p2p = self.config.bulk_p2p && native_p2p;
212 let key = (devices.to_vec(), native_p2p, ep_device_arithmetic, bulk_p2p);
213 if let Some(runtime) = self.runtimes.get(&key) {
214 return Ok(Arc::clone(runtime));
215 }
216 let runtime = Arc::new(crate::tp::TpE4m3HostBounce::new_configured(
217 devices,
218 native_p2p,
219 ep_device_arithmetic,
220 bulk_p2p,
221 )?);
222 let names = runtime.device_names()?;
223 if names
224 .iter()
225 .any(|name| !name.contains("RTX PRO 6000") || !name.contains("Blackwell"))
226 {
227 return Err(format!(
228 "Step distributed execution is qualified only on RTX PRO 6000 Blackwell, \
229 got {names:?}"
230 )
231 .into());
232 }
233 self.runtimes.insert(key, Arc::clone(&runtime));
234 Ok(runtime)
235 }
236}
237
238impl ResidentPlan {
239 fn from_layout(
240 src: &dyn TensorSource,
241 primary_device: usize,
242 layer_devices: Vec<usize>,
243 pp: bool,
244 ) -> Self {
245 let mut layer_counts = HashMap::new();
246 for &device in &layer_devices {
247 *layer_counts.entry(device).or_default() += 1;
248 }
249 let (exact_expert_bytes, trunk_bytes) = match src.gguf() {
250 Some(g) => {
251 let bytes = residency_bytes_by_device(
252 g.tensors
253 .iter()
254 .map(|t| (t.name.as_str(), t.n_bytes as usize)),
255 &layer_devices,
256 primary_device,
257 );
258 if bytes.saw_experts {
259 (Some(bytes.experts), bytes.rest)
260 } else {
261 (None, 0)
262 }
263 }
264 None => (None, 0),
265 };
266 Self {
267 primary_device,
268 layer_devices,
269 layer_counts,
270 exact_expert_bytes,
271 trunk_bytes,
272 decisions: HashMap::new(),
273 pp,
274 }
275 }
276
277 pub(crate) fn unsharded(e: &Engine, src: &dyn TensorSource, cfg: &ModelConfig) -> Self {
278 let device = e.ctx().ordinal();
279 Self::from_layout(src, device, vec![device; cfg.n_layer as usize], false)
280 }
281
282 pub(crate) fn pp(
283 e: &Engine,
284 src: &dyn TensorSource,
285 cfg: &ModelConfig,
286 n_trunk: usize,
287 ) -> Result<Self, Box<dyn std::error::Error>> {
288 let primary = e.ctx().ordinal();
289 let Some(_fence) = crate::pp::pp_cuts(n_trunk) else {
290 return Ok(Self::unsharded(e, src, cfg));
291 };
292 let mut layer_devices = vec![primary; cfg.n_layer as usize];
293 for (il, device) in layer_devices.iter_mut().take(n_trunk).enumerate() {
294 *device = crate::pp::layer_engine(e, n_trunk, il)?.ctx().ordinal();
295 }
296 Ok(Self::from_layout(src, primary, layer_devices, true))
297 }
298
299 fn exclude_distributed_expert_layers(&mut self, specs: impl IntoIterator<Item = usize>) {
303 for layer in specs {
304 let device = self
305 .layer_devices
306 .get(layer)
307 .copied()
308 .unwrap_or(self.primary_device);
309 if let Some(count) = self.layer_counts.get_mut(&device) {
310 *count = count.saturating_sub(1);
311 }
312 }
313 }
314
315 fn should_reside(&mut self, e: &Engine, il: usize, per_layer: usize) -> bool {
316 let device = self
317 .layer_devices
318 .get(il)
319 .copied()
320 .unwrap_or(self.primary_device);
321 debug_assert_eq!(e.ctx().ordinal(), device);
322 if let Some(&decision) = self.decisions.get(&device) {
323 return decision;
324 }
325 if std::env::var("MEMRA_MOE_RESIDENT").as_deref() == Ok("0") {
326 self.decisions.insert(device, false);
327 return false;
328 }
329 let (free, _total) = match e.ctx().mem_get_info() {
330 Ok(v) => v,
331 Err(_) => {
332 self.decisions.insert(device, false);
333 return false;
334 }
335 };
336 let projected = self
337 .exact_expert_bytes
338 .as_ref()
339 .map(|bytes| bytes.get(&device).copied().unwrap_or(0))
340 .unwrap_or(per_layer * self.layer_counts.get(&device).copied().unwrap_or(1));
341 let budget = std::env::var("MEMRA_MOE_RESIDENT_GB")
342 .ok()
343 .and_then(|v| v.parse::<f64>().ok())
344 .map(|gb| (gb * 1e9) as usize)
345 .unwrap_or_else(|| {
346 let reserve = std::env::var("MEMRA_MOE_RESIDENT_HEADROOM_GB")
347 .ok()
348 .and_then(|v| v.parse::<f64>().ok())
349 .map(|gb| (gb * 1e9) as usize)
350 .unwrap_or(2_000_000_000);
351 free.saturating_sub(self.trunk_bytes + reserve)
352 });
353 let ok = projected <= budget;
354 eprintln!(
355 "[moe] resident-experts decision ({}dev{}): experts {:.2}GB + trunk {:.2}GB vs free {:.2}GB (expert budget {:.2}GB) -> {}",
356 if self.pp { "PP " } else { "" },
357 device,
358 projected as f64 / 1e9,
359 self.trunk_bytes as f64 / 1e9,
360 free as f64 / 1e9,
361 budget as f64 / 1e9,
362 if ok { "RESIDENT" } else { "SLRU cache" }
363 );
364 self.decisions.insert(device, ok);
365 ok
366 }
367}
368
369fn load_mixer_kind(
371 e: &Engine,
372 src: &dyn TensorSource,
373 cfg: &ModelConfig,
374 il: u32,
375 attention: &AttentionPlan,
376 step_runtimes: &mut StepParallelRuntimeRegistry,
377) -> Result<Mixer, Box<dyn std::error::Error>> {
378 let p = |s: &str| format!("blk.{il}.{s}");
379 Ok(match attention {
380 AttentionPlan::Mla(mla) => Mixer::Mla(MlaAttnLayer::load(e, src, il, mla)?),
381 AttentionPlan::Full(full)
382 | AttentionPlan::SlidingWindow {
383 attention: full, ..
384 } => {
385 Mixer::Full(FullAttnLayer {
386 wq: load_t(e, src, &p("attn_q.weight"))?,
387 wk: load_t(e, src, &p("attn_k.weight"))?,
388 wv: match load_opt(e, src, &p("attn_v.weight"))? {
393 Some(v) => v,
394 None => load_t(e, src, &p("attn_k.weight"))?,
395 },
396 wo: load_t(e, src, &p("attn_output.weight"))?,
397 q_norm: load_t(e, src, &p("attn_q_norm.weight"))?,
398 k_norm: load_t(e, src, &p("attn_k_norm.weight"))?,
399 attn_gate: if full.output_gate
403 == memra_gguf::config::AttentionGateKind::SeparateHead
404 {
405 Some(load_t(e, src, &p("attn_gate.weight"))?)
406 } else {
407 None
408 },
409 step_tp_qkv: build_step_tp_qkv(e, src, cfg, il as usize, step_runtimes)?,
410 })
411 }
412 AttentionPlan::KimiDeltaNet(kda) => {
415 Mixer::Kda(crate::kda::KdaAttnLayer::load(e, src, il, kda)?)
416 }
417 AttentionPlan::GatedDeltaNet(geometry) => Mixer::Linear(LinearAttnLayer {
418 geometry: *geometry,
419 wqkv: load_t(e, src, &p("attn_qkv.weight"))?,
420 wqkv_gate: load_t(e, src, &p("attn_gate.weight"))?,
421 ssm_beta: load_t(e, src, &p("ssm_beta.weight"))?,
422 ssm_alpha: load_t(e, src, &p("ssm_alpha.weight"))?,
423 ssm_a: load_t(e, src, &p("ssm_a"))?,
424 ssm_dt: load_t(e, src, &p("ssm_dt.bias"))?,
425 ssm_conv1d: load_t(e, src, &p("ssm_conv1d.weight"))?,
426 ssm_norm: load_t(e, src, &p("ssm_norm.weight"))?,
427 ssm_out: load_t(e, src, &p("ssm_out.weight"))?,
428 }),
429 })
430}
431
432#[allow(clippy::too_many_arguments)] pub(crate) fn load_ffn(
440 e: &Engine,
441 src: &dyn TensorSource,
442 cfg: &ModelConfig,
443 mlp: &MlpPlan,
444 il: u32,
445 spill: Option<(&GgufFile, &mut crate::spill::SpillCtx)>,
446 resident: &mut ResidentPlan,
447 step_runtimes: &mut StepParallelRuntimeRegistry,
448) -> Result<Ffn, Box<dyn std::error::Error>> {
449 let p = |s: &str| format!("blk.{il}.{s}");
450 let artifact_dense = matches!(mlp, MlpPlan::Moe(_))
457 && !src.has(&p("ffn_gate_exps.weight"))
458 && !src.has(&p("ffn_gate_up_exps.weight"))
459 && src.has(&p("ffn_gate.weight"));
460 Ok(if artifact_dense {
461 Ffn::Dense {
462 ffn_gate: GpuTensor::load_from_source(e, src, &p("ffn_gate.weight"))?,
463 ffn_up: GpuTensor::load_from_source(e, src, &p("ffn_up.weight"))?,
464 ffn_down: GpuTensor::load_from_source(e, src, &p("ffn_down.weight"))?,
465 }
466 } else if let MlpPlan::Moe(moe) = mlp {
467 let n_expert = moe.expert_count as usize;
468 let (gate_exps, up_exps, down_exps) = match spill {
474 Some((g, ctx)) => (
475 HostExps::load_tiered(e, g, &p("ffn_gate_exps.weight"), ctx)?,
476 HostExps::load_tiered(e, g, &p("ffn_up_exps.weight"), ctx)?,
477 HostExps::load_tiered(e, g, &p("ffn_down_exps.weight"), ctx)?,
478 ),
479 None => {
480 let exps = |e: &Engine, n: &str| -> Result<HostExps, Box<dyn std::error::Error>> {
481 if src.has(n) {
482 HostExps::load_stacked_from_source(e, src, n)
483 } else {
484 HostExps::load_from_source(e, src, n, n_expert)
485 }
486 };
487 let fused = p("ffn_gate_up_exps.weight");
489 if !src.has(&p("ffn_gate_exps.weight")) && src.has(&fused) {
490 let ff = moe.expert_intermediate_size as usize;
491 (
492 HostExps::load_stacked_split_from_source(e, src, &fused, 0, ff)?,
493 HostExps::load_stacked_split_from_source(e, src, &fused, ff, 2 * ff)?,
494 exps(e, &p("ffn_down_exps.weight"))?,
495 )
496 } else {
497 (
498 exps(e, &p("ffn_gate_exps.weight"))?,
499 exps(e, &p("ffn_up_exps.weight"))?,
500 exps(e, &p("ffn_down_exps.weight"))?,
501 )
502 }
503 }
504 };
505 let (step_ep, step_tp) = build_step_distributed_exps(
506 e,
507 cfg,
508 src,
509 il as usize,
510 &gate_exps,
511 &up_exps,
512 &down_exps,
513 step_runtimes,
514 )?;
515 let dev_exps = if step_ep.is_some() || step_tp.is_some() {
521 None
522 } else {
523 build_dev_exps(e, resident, il as usize, &gate_exps, &up_exps, &down_exps)?
524 };
525 let mut macro_row = vec![1.0f32; 3 * n_expert];
527 for (slot, exps) in [(0usize, &gate_exps), (1, &up_exps), (2, &down_exps)] {
528 if let Some(ms) = exps.macros.as_ref() {
529 macro_row[slot * n_expert..(slot + 1) * n_expert].copy_from_slice(ms);
530 }
531 }
532 let has_macros = macro_row.iter().any(|&m| m != 1.0);
533 let dev_macros = e.htod(¯o_row)?;
534 let exp_probs_b = src
537 .find(&p("exp_probs_b.bias"))
538 .map(|v| memra_gguf::dequant::dequantize(v.ggml_type, &v.bytes, n_expert));
539 if exp_probs_b.is_none()
548 && matches!(
549 moe.router,
550 memra_gguf::model_plan::RouterPlan::Sigmoid {
551 selection_bias: true,
552 ..
553 } | memra_gguf::model_plan::RouterPlan::SqrtSoftplus {
554 selection_bias: true,
555 ..
556 }
557 )
558 {
559 return Err(format!(
560 "layer {il}: {} is absent, but the compiled ModelPlan declares a router with a \
561 selection bias ({:?}). Refusing to load: a zero-filled bias would route to \
562 different experts than this model does, silently. Either the checkpoint does \
563 not carry the tensor, or this arch has no `exp_probs_b.bias` entry in \
564 hf_mapping's ggml->HF map",
565 p("exp_probs_b.bias"),
566 moe.router
567 )
568 .into());
569 }
570 let active_experts = src.active_experts(il).map(<[bool]>::to_vec);
571 let route_bias = exp_probs_b.clone().unwrap_or_else(|| vec![0.0; n_expert]);
572 let active_row: Vec<u8> = active_experts
573 .as_ref()
574 .map(|mask| mask.iter().map(|&is_active| u8::from(is_active)).collect())
575 .unwrap_or_else(|| vec![1; n_expert]);
576 let exp_probs_b_dev = e.htod(&route_bias)?;
577 let active_experts_dev = e.htod_bytes(&active_row)?;
578 let gate_shexp = load_opt(e, src, &p("ffn_gate_shexp.weight"))?;
579 let up_shexp = load_opt(e, src, &p("ffn_up_shexp.weight"))?;
580 let down_shexp = load_opt(e, src, &p("ffn_down_shexp.weight"))?;
581 if moe.shared.is_some()
589 && (gate_shexp.is_none() || up_shexp.is_none() || down_shexp.is_none())
590 {
591 return Err(format!(
592 "layer {il}: the compiled ModelPlan declares an always-on shared expert, but \
593 {}{}{} could not be resolved in the checkpoint. Refusing to load: dropping the \
594 shared branch computes a different model, silently. Either the checkpoint does \
595 not carry it, or this arch's shared-expert spelling is missing from \
596 hf_mapping's ggml->HF map",
597 if gate_shexp.is_none() {
598 format!("{} ", p("ffn_gate_shexp.weight"))
599 } else {
600 String::new()
601 },
602 if up_shexp.is_none() {
603 format!("{} ", p("ffn_up_shexp.weight"))
604 } else {
605 String::new()
606 },
607 if down_shexp.is_none() {
608 p("ffn_down_shexp.weight")
609 } else {
610 String::new()
611 },
612 )
613 .into());
614 }
615 Ffn::Moe(MoeWeights {
616 gate_inp: load_t(e, src, &p("ffn_gate_inp.weight"))?,
617 gate_inp_shexp: load_opt(e, src, &p("ffn_gate_inp_shexp.weight"))?,
618 exp_probs_b,
619 exp_probs_b_dev,
620 active_experts,
621 active_experts_dev,
622 gate_exps,
623 up_exps,
624 down_exps,
625 gate_shexp,
626 up_shexp,
627 down_shexp,
628 dev_exps,
629 step_ep,
630 step_tp,
631 glm5_ep: None,
632 dev_macros,
633 has_macros,
634 w4a16_bf16_activations: matches!(
635 src.expert_activation_precision(),
636 memra_gguf::source::ExpertActivationPrecision::Bf16
637 ),
638 })
639 } else {
640 Ffn::Dense {
641 ffn_gate: load_t(e, src, &p("ffn_gate.weight"))?,
642 ffn_up: load_t(e, src, &p("ffn_up.weight"))?,
643 ffn_down: load_t(e, src, &p("ffn_down.weight"))?,
644 }
645 })
646}
647
648fn host_e4m3_bank(
649 exps: &HostExps,
650) -> Result<crate::tp::E4m3ExpertBank<'_>, Box<dyn std::error::Error>> {
651 if exps.qtype != crate::QT_F8_E4M3_BLK {
652 return Err(format!(
653 "Step EP requires native block-E4M3 expert banks, got qtype {}",
654 exps.qtype
655 )
656 .into());
657 }
658 let scales = exps
659 .fp8_blk
660 .as_ref()
661 .ok_or("Step EP native expert bank has no block-E4M3 scale plane")?;
662 Ok(crate::tp::E4m3ExpertBank {
663 codes: exps.bytes.as_bytes(),
664 scales: &scales.scales,
665 expert_count: exps.n_expert,
666 out_features: exps.out_f,
667 in_features: exps.in_f,
668 })
669}
670
671fn validate_step_expert_specs(
672 contract: &crate::parallel::ModelParallelContract,
673 flag: &str,
674 specs: &[crate::tp::StepEpLayerSpec],
675 allow_dense_attention_only: bool,
676) -> Result<(), Box<dyn std::error::Error>> {
677 for candidate in specs {
678 if candidate.layer >= contract.trunk_layers {
679 return Err(format!(
680 "{flag} layer {} is outside Step trunk layers 0..{}",
681 candidate.layer, contract.trunk_layers
682 )
683 .into());
684 }
685 if candidate.layer < contract.dense_prefix_layers {
686 if allow_dense_attention_only {
687 continue;
688 }
689 return Err(format!(
690 "{flag} layer {} is outside Step routed-expert layers {}..{}",
691 candidate.layer, contract.dense_prefix_layers, contract.trunk_layers
692 )
693 .into());
694 }
695 }
696 Ok(())
697}
698
699fn validate_step_expert_activation_layout(
700 cfg: &ModelConfig,
701 flag: &str,
702 selection: &StepExpertSelection,
703) -> Result<(), Box<dyn std::error::Error>> {
704 let _ = (cfg, flag, selection);
710 Ok(())
711}
712
713fn parse_auto_w4a16_bf16_mmv(value: Option<&str>) -> Result<bool, String> {
714 match value {
715 None => Ok(true),
716 Some("0") => Ok(false),
717 Some("1") => Ok(true),
718 Some(value) => Err(format!(
719 "MEMRA_BF16_MMV={value:?} is invalid under MEMRA_PARALLEL=auto; expected 0 or 1"
720 )),
721 }
722}
723
724fn parse_auto_parallel_tp_attention(value: Option<&str>) -> Result<bool, String> {
725 match value {
726 None | Some("") | Some("0") => Ok(false),
727 Some("1") => Ok(true),
728 Some(value) => Err(format!(
729 "MEMRA_PARALLEL_TP_ATTENTION={value:?} is invalid; expected 0 or 1"
730 )),
731 }
732}
733
734fn auto_parallel_tp_attention_enabled() -> Result<bool, String> {
735 parse_auto_parallel_tp_attention(std::env::var("MEMRA_PARALLEL_TP_ATTENTION").ok().as_deref())
736}
737
738fn parse_auto_parallel_tp_attention_ranks(value: Option<&str>) -> Result<Option<usize>, String> {
739 match value {
740 None => Ok(None),
741 Some("2") => Ok(Some(2)),
742 Some("3") => Ok(Some(3)),
743 Some("4") => Ok(Some(4)),
744 Some(value) => Err(format!(
745 "MEMRA_PARALLEL_TP_ATTENTION_RANKS={value:?} is invalid; expected 2, 3, or 4"
746 )),
747 }
748}
749
750fn auto_parallel_tp_attention_ranks() -> Result<Option<usize>, String> {
751 parse_auto_parallel_tp_attention_ranks(
752 std::env::var("MEMRA_PARALLEL_TP_ATTENTION_RANKS")
753 .ok()
754 .as_deref(),
755 )
756}
757
758fn prepare_auto_parallel(
764 src: &dyn TensorSource,
765 cfg: &ModelConfig,
766 plan: &memra_gguf::model_plan::ModelPlan,
767) -> Result<Option<crate::parallel::AutoParallelPlacement>, Box<dyn std::error::Error>> {
768 let Some(devices) = crate::tp::auto_parallel_devices()? else {
769 return Ok(None);
770 };
771 if std::env::var_os("MEMRA_PP_STAGES").is_some()
772 || std::env::var_os("MEMRA_PP_DEVICES").is_some()
773 || std::env::var_os("MEMRA_PP_SPLITS").is_some()
774 {
775 return Err(
776 "MEMRA_PARALLEL=auto cannot be combined with MEMRA_PP_STAGES, MEMRA_PP_DEVICES, or \
777 MEMRA_PP_SPLITS"
778 .into(),
779 );
780 }
781 let placement = crate::parallel::plan_auto_parallel(src, cfg, plan, &devices)?;
782 let auto_w4a16_bf16 = placement.backend == crate::parallel::AutoParallelBackend::ExpertParallel
783 && matches!(
784 src.expert_activation_precision(),
785 memra_gguf::source::ExpertActivationPrecision::Bf16
786 );
787 let bf16_nonexpert = if auto_w4a16_bf16 {
788 let explicit = match std::env::var("MEMRA_BF16_MMV") {
789 Ok(value) => Some(value),
790 Err(std::env::VarError::NotPresent) => None,
791 Err(error) => return Err(format!("cannot read MEMRA_BF16_MMV: {error}").into()),
792 };
793 let enabled = parse_auto_w4a16_bf16_mmv(explicit.as_deref())?;
794 if enabled && explicit.is_none() {
795 unsafe {
798 std::env::set_var("MEMRA_BF16_MMV", "1");
799 }
800 }
801 match (enabled, explicit.is_some()) {
802 (true, false) => "bf16-resident(auto)",
803 (true, true) => "bf16-resident(explicit)",
804 (false, true) => "f32-expanded(explicit-rollback)",
805 (false, false) => unreachable!("unset auto W4A16 defaults BF16 residency on"),
806 }
807 } else {
808 "placement-default"
809 };
810 if placement.backend == crate::parallel::AutoParallelBackend::Pipeline {
811 let stages = placement.devices.len();
812 let device_list = placement
813 .devices
814 .iter()
815 .map(usize::to_string)
816 .collect::<Vec<_>>()
817 .join(",");
818 let splits = placement
819 .pipeline_splits
820 .iter()
821 .map(usize::to_string)
822 .collect::<Vec<_>>()
823 .join(",");
824 unsafe {
827 std::env::set_var("MEMRA_PP_STAGES", stages.to_string());
828 std::env::set_var("MEMRA_PP_DEVICES", &device_list);
829 std::env::set_var("MEMRA_PP_SPLITS", &splits);
830 }
831 }
832 let family = if placement.routed_layers.is_empty() {
833 "dense-transformer"
834 } else {
835 "routed-moe"
836 };
837 eprintln!(
838 "[parallel-auto] family={family} variant={:?} devices={:?} placement={} \
839 checkpoint_peak={:.2}GB ep_root={:.2}GB ep_peer={:.2}GB reserve={:.2}GB \
840 capacity={:?} splits={:?} bf16_nonexpert={bf16_nonexpert} \
841 wavefront=off(default) performance_claim=false",
842 cfg.name,
843 placement.devices,
844 match placement.backend {
845 crate::parallel::AutoParallelBackend::Pipeline => "pipeline",
846 crate::parallel::AutoParallelBackend::ExpertParallel => "expert-parallel",
847 },
848 placement.checkpoint_peak_bytes as f64 / 1e9,
849 placement.expert_root_bytes as f64 / 1e9,
850 placement.expert_peer_bytes as f64 / 1e9,
851 placement.reserve_bytes as f64 / 1e9,
852 placement.device_capacity_bytes,
853 placement.pipeline_splits,
854 );
855 Ok(Some(placement))
856}
857
858fn prepare_step_parallel_load(
859 e: &Engine,
860 src: &dyn TensorSource,
861 cfg: &ModelConfig,
862 trunk_layers: usize,
863 auto_placement: Option<&crate::parallel::AutoParallelPlacement>,
864) -> Result<StepParallelLoadConfig, Box<dyn std::error::Error>> {
865 let mut tp_specs = crate::tp::step_tp_layer_specs()?;
866 let mut ep_specs = crate::tp::step_ep_layer_specs()?;
867 let device_arithmetic = crate::tp::step_ep_device_arithmetic_enabled()?;
868 let f32_mirror = crate::tp::step_tp_f32_mirror_enabled()?;
869 let bulk_p2p = crate::tp::step_tp_bulk_p2p_enabled()?;
870 let mut native_p2p = crate::tp::step_tp_native_p2p_enabled()?;
871 let mut nvfp4_device_routes = crate::tp::step_nvfp4_dev_routes_enabled()?;
872 let auto_tp_attention = auto_parallel_tp_attention_enabled()?;
873 let requested_attention_ranks = auto_parallel_tp_attention_ranks()?;
874 let mut auto_parallel = false;
875 let mut tp_attention_expert_overlap = false;
876 if requested_attention_ranks.is_some() && !auto_tp_attention {
877 return Err(
878 "MEMRA_PARALLEL_TP_ATTENTION_RANKS requires MEMRA_PARALLEL_TP_ATTENTION=1".into(),
879 );
880 }
881 if auto_tp_attention && auto_placement.is_none() {
882 return Err(
883 "MEMRA_PARALLEL_TP_ATTENTION=1 requires MEMRA_PARALLEL=auto; explicit per-layer \
884 recipes remain under MEMRA_STEP_TP"
885 .into(),
886 );
887 }
888 if let Some(placement) = auto_placement {
889 if !tp_specs.is_empty() || !ep_specs.is_empty() {
890 return Err(
891 "MEMRA_PARALLEL=auto cannot be combined with MEMRA_STEP_EP or MEMRA_STEP_TP".into(),
892 );
893 }
894 if placement.backend == crate::parallel::AutoParallelBackend::Pipeline {
895 if auto_tp_attention {
896 return Err(
897 "MEMRA_PARALLEL_TP_ATTENTION=1 requires automatic whole-expert EP; the \
898 selected checkpoint fits only the pipeline backend"
899 .into(),
900 );
901 }
902 return Ok(StepParallelLoadConfig::default());
903 }
904 if auto_tp_attention {
905 let contract = crate::parallel::ModelParallelContract::from_model(cfg)?;
906 if !contract.tensor_attention_supported {
907 return Err(format!(
908 "MEMRA_PARALLEL_TP_ATTENTION=1 cannot shard attention for {:?}: the \
909 compiled ModelPlan has no generic tensor-attention contract",
910 cfg.name
911 )
912 .into());
913 }
914 let attention_ranks = requested_attention_ranks.unwrap_or(placement.devices.len());
915 if attention_ranks > placement.devices.len() {
916 return Err(format!(
917 "MEMRA_PARALLEL_TP_ATTENTION_RANKS={attention_ranks} exceeds the automatic \
918 placement width {}",
919 placement.devices.len()
920 )
921 .into());
922 }
923 let attention_devices = placement.devices[..attention_ranks].to_vec();
924 tp_specs = (0..trunk_layers)
925 .map(|layer| crate::tp::StepTpLayerSpec {
926 layer,
927 devices: attention_devices.clone(),
928 })
929 .collect();
930 if attention_ranks < placement.devices.len() {
931 ep_specs = placement
932 .routed_layers
933 .iter()
934 .map(|&layer| crate::tp::StepEpLayerSpec {
935 layer,
936 devices: placement.devices.clone(),
937 })
938 .collect();
939 tp_attention_expert_overlap = true;
940 } else {
941 ep_specs.clear();
942 }
943 } else {
944 ep_specs = placement
945 .routed_layers
946 .iter()
947 .map(|&layer| crate::tp::StepEpLayerSpec {
948 layer,
949 devices: placement.devices.clone(),
950 })
951 .collect();
952 }
953 auto_parallel = true;
954 native_p2p = true;
955 nvfp4_device_routes = matches!(
956 src.expert_activation_precision(),
957 memra_gguf::source::ExpertActivationPrecision::Bf16
958 );
959 eprintln!(
960 "[parallel-auto-backend] devices={:?} routed_layers={} native_p2p=true \
961 artifact_activation={:?} attention_layout={} attention_devices={:?} \
962 expert_layout=expert-parallel expert_devices={:?} \
963 backend={} performance_claim=false",
964 placement.devices,
965 placement.routed_layers.len(),
966 src.expert_activation_precision(),
967 if auto_tp_attention {
968 "tensor-parallel"
969 } else {
970 "root-local"
971 },
972 tp_specs
973 .first()
974 .map(|spec| spec.devices.as_slice())
975 .unwrap_or(&[]),
976 ep_specs
977 .first()
978 .map(|spec| spec.devices.as_slice())
979 .unwrap_or(placement.devices.as_slice()),
980 if nvfp4_device_routes {
981 "nvfp4-w4a16"
982 } else {
983 "artifact-selected-host-oracle"
984 },
985 );
986 }
987 if tp_specs.is_empty() {
988 if auto_tp_attention {
989 return Err("MEMRA_PARALLEL_TP_ATTENTION=1 produced no tensor-parallel layers".into());
990 }
991 if device_arithmetic || f32_mirror || bulk_p2p {
992 return Err(
993 "MEMRA_STEP_EP_DEVICE_ARITHMETIC=1, MEMRA_STEP_TP_F32_MIRROR=1, or \
994 MEMRA_STEP_TP_BULK_P2P=1 requires MEMRA_STEP_TP; device arithmetic and bulk \
995 transport also require MEMRA_STEP_TP_NATIVE_P2P=1"
996 .into(),
997 );
998 }
999 if nvfp4_device_routes && ep_specs.is_empty() {
1000 return Err(
1001 "MEMRA_STEP_NVFP4_DEV_ROUTES=1 requires MEMRA_STEP_EP or MEMRA_STEP_TP".into(),
1002 );
1003 }
1004 if nvfp4_device_routes && !native_p2p {
1005 return Err("MEMRA_STEP_NVFP4_DEV_ROUTES=1 with explicit EP requires \
1006 MEMRA_STEP_TP_NATIVE_P2P=1"
1007 .into());
1008 }
1009 let expert_artifact = if ep_specs.is_empty() {
1012 StepExpertArtifact::default()
1013 } else if nvfp4_device_routes
1014 && matches!(
1015 src.expert_activation_precision(),
1016 memra_gguf::source::ExpertActivationPrecision::Bf16
1017 )
1018 {
1019 StepExpertArtifact::Nvfp4
1024 } else {
1025 let contract = crate::parallel::ModelParallelContract::from_model(cfg)?;
1026 validate_step_expert_specs(&contract, "MEMRA_STEP_EP", &ep_specs, false)?;
1027 let layer_owners = (0..trunk_layers)
1028 .map(|layer| {
1029 crate::pp::layer_engine(e, trunk_layers, layer)
1030 .map(|engine| engine.ctx().ordinal())
1031 })
1032 .collect::<Result<Vec<_>, _>>()?;
1033 let mut runtime_groups = Vec::<Vec<usize>>::new();
1034 for spec in &ep_specs {
1035 let owner = layer_owners[spec.layer];
1036 if !spec.devices.contains(&owner) {
1037 return Err(format!(
1038 "MEMRA_STEP_EP layer {} owning device {owner} is absent from {:?}",
1039 spec.layer, spec.devices
1040 )
1041 .into());
1042 }
1043 if nvfp4_device_routes && spec.devices.first().copied() != Some(owner) {
1044 return Err(format!(
1045 "MEMRA_STEP_NVFP4_DEV_ROUTES=1 requires the owning device first; \
1046 layer {} owner={owner} devices={:?}",
1047 spec.layer, spec.devices
1048 )
1049 .into());
1050 }
1051 if !runtime_groups.contains(&spec.devices) {
1052 runtime_groups.push(spec.devices.clone());
1053 }
1054 }
1055 for devices in &runtime_groups {
1056 let hardware = crate::parallel::detect_uniform_hardware(devices)?;
1057 if !contract.hardware_targets.contains(&hardware) {
1058 return Err(format!(
1059 "{} has no qualified {hardware:?} EP contract for devices {devices:?}",
1060 contract.variant
1061 )
1062 .into());
1063 }
1064 }
1065 let artifact = match crate::parallel::validate_fp8_expert_checkpoint(src, &contract) {
1066 Ok(_) => StepExpertArtifact::E4m3,
1067 Err(fp8_error) => {
1068 match crate::parallel::validate_nvfp4_expert_checkpoint(src, &contract) {
1069 Ok(_) => StepExpertArtifact::Nvfp4,
1070 Err(nvfp4_error) => {
1071 return Err(format!(
1072 "Step checkpoint qualifies as neither native expert artifact \
1073 class: [E4M3] {fp8_error} [NVFP4] {nvfp4_error}"
1074 )
1075 .into());
1076 }
1077 }
1078 }
1079 };
1080 if nvfp4_device_routes && artifact != StepExpertArtifact::Nvfp4 {
1081 return Err(
1082 "MEMRA_STEP_NVFP4_DEV_ROUTES=1 requires a native ModelOpt NVFP4 expert \
1083 artifact"
1084 .into(),
1085 );
1086 }
1087 artifact
1088 };
1089 return Ok(StepParallelLoadConfig {
1090 ep_specs,
1091 native_p2p,
1092 nvfp4_device_routes,
1093 auto_parallel,
1094 expert_artifact,
1095 ..StepParallelLoadConfig::default()
1096 });
1097 }
1098 let contract = crate::parallel::ModelParallelContract::from_model(cfg)?;
1099 validate_step_expert_specs(&contract, "MEMRA_STEP_EP", &ep_specs, false)?;
1100 validate_step_expert_specs(&contract, "MEMRA_STEP_TP", &tp_specs, true)?;
1101 for spec in &tp_specs {
1102 let selection = select_step_expert_layout_inner(
1103 spec.layer,
1104 &ep_specs,
1105 &tp_specs,
1106 tp_attention_expert_overlap,
1107 )?
1108 .ok_or("Step TP expert selection disappeared during preflight")?;
1109 validate_step_expert_activation_layout(cfg, "MEMRA_STEP_TP", &selection)?;
1110 }
1111
1112 let layer_owners = (0..trunk_layers)
1113 .map(|layer| {
1114 crate::pp::layer_engine(e, trunk_layers, layer).map(|engine| engine.ctx().ordinal())
1115 })
1116 .collect::<Result<Vec<_>, _>>()?;
1117 let plan = contract.preflight_step_tp_specs(
1118 tp_specs
1119 .iter()
1120 .map(|spec| (spec.layer, spec.devices.as_slice())),
1121 &layer_owners,
1122 )?;
1123
1124 for devices in &plan.runtime_groups {
1125 let hardware = crate::parallel::detect_uniform_hardware(devices)?;
1126 if !contract.hardware_targets.contains(&hardware) {
1127 return Err(format!(
1128 "{} has no qualified {hardware:?} TP contract for devices {devices:?}",
1129 contract.variant
1130 )
1131 .into());
1132 }
1133 }
1134
1135 if bulk_p2p && !native_p2p {
1136 return Err("MEMRA_STEP_TP_BULK_P2P=1 requires MEMRA_STEP_TP_NATIVE_P2P=1".into());
1137 }
1138 if device_arithmetic
1139 && (!ep_specs.is_empty()
1140 || !native_p2p
1141 || plan.expert_parallel_layers() == 0
1142 || plan.tensor_parallel_expert_layers() != 0)
1143 {
1144 return Err(
1145 "MEMRA_STEP_EP_DEVICE_ARITHMETIC=1 requires native-P2P TP4/TP8 \
1146 expert ownership for every selected routed-expert layer"
1147 .into(),
1148 );
1149 }
1150 let (qualified_experts, expert_artifact) =
1154 match crate::parallel::validate_fp8_expert_checkpoint(src, &contract) {
1155 Ok(qualified) => (qualified, StepExpertArtifact::E4m3),
1156 Err(fp8_error) => {
1157 match crate::parallel::validate_nvfp4_expert_checkpoint(src, &contract) {
1158 Ok(qualified) => (qualified, StepExpertArtifact::Nvfp4),
1159 Err(nvfp4_error) => {
1160 return Err(format!(
1161 "Step checkpoint qualifies as neither native expert artifact class: \
1162 [E4M3] {fp8_error} [NVFP4] {nvfp4_error}"
1163 )
1164 .into());
1165 }
1166 }
1167 }
1168 };
1169 if expert_artifact == StepExpertArtifact::Nvfp4 {
1170 if device_arithmetic {
1171 return Err(
1172 "MEMRA_STEP_EP_DEVICE_ARITHMETIC=1 is qualified for the E4M3 expert artifact \
1173 only; the NVFP4 expert program is host-canonical in this increment"
1174 .into(),
1175 );
1176 }
1177 if bulk_p2p {
1182 return Err(
1183 "MEMRA_STEP_TP_BULK_P2P=1 is qualified for the E4M3 expert artifact only; the \
1184 NVFP4 bank transport increment has not landed"
1185 .into(),
1186 );
1187 }
1188 }
1189
1190 if f32_mirror {
1191 eprintln!(
1192 "[step-tp-preflight] layers={} full_trunk={} runtime_groups={} \
1193 dense_attention_layers={} tensor_expert_layers={} expert_owner_layers={} \
1194 qualified_fp8_expert_projection_slices={} owner_first=true \
1195 hardware=rtx-pro-6000-blackwell \
1196 native_p2p={} bulk_p2p={} device_arithmetic={} bf16_residency=f32-mirror \
1197 weights_loaded=false performance_claim=false",
1198 plan.layers.len(),
1199 plan.full_trunk,
1200 plan.runtime_groups.len(),
1201 plan.dense_attention_layers(),
1202 plan.tensor_parallel_expert_layers(),
1203 plan.expert_parallel_layers(),
1204 qualified_experts,
1205 native_p2p,
1206 bulk_p2p,
1207 device_arithmetic,
1208 );
1209 } else {
1210 eprintln!(
1211 "[step-tp-preflight] layers={} full_trunk={} runtime_groups={} \
1212 dense_attention_layers={} tensor_expert_layers={} expert_owner_layers={} \
1213 qualified_fp8_expert_projection_slices={} owner_first=true \
1214 hardware=rtx-pro-6000-blackwell \
1215 native_p2p={} bulk_p2p={} device_arithmetic={} \
1216 weights_loaded=false performance_claim=false",
1217 plan.layers.len(),
1218 plan.full_trunk,
1219 plan.runtime_groups.len(),
1220 plan.dense_attention_layers(),
1221 plan.tensor_parallel_expert_layers(),
1222 plan.expert_parallel_layers(),
1223 qualified_experts,
1224 native_p2p,
1225 bulk_p2p,
1226 device_arithmetic,
1227 );
1228 }
1229 Ok(StepParallelLoadConfig {
1230 ep_specs,
1231 tp_specs,
1232 native_p2p,
1233 ep_device_arithmetic: device_arithmetic,
1234 f32_mirror,
1235 bulk_p2p,
1236 nvfp4_device_routes,
1237 auto_parallel,
1238 tp_attention_expert_overlap,
1239 expert_artifact,
1240 })
1241}
1242
1243fn nvfp4_native_expert_bank<'a>(
1245 src: &'a dyn TensorSource,
1246 layer: usize,
1247 proj: &str,
1248) -> Result<memra_gguf::source::Nvfp4StackedNative<'a>, Box<dyn std::error::Error>> {
1249 let name = format!("blk.{layer}.ffn_{proj}_exps.weight");
1250 src.find_nvfp4_stacked_native(&name)
1251 .ok_or_else(|| format!("NVFP4 expert backend is missing native bank {name}").into())
1252}
1253
1254fn nvfp4_expert_bank_view<'a>(
1256 native: &'a memra_gguf::source::Nvfp4StackedNative<'a>,
1257) -> crate::tp::Nvfp4ExpertBank<'a> {
1258 crate::tp::Nvfp4ExpertBank {
1259 codes: native.codes,
1260 scales: native.scales,
1261 macros: &native.macros,
1262 expert_count: native.n_expert,
1263 out_features: native.out_f,
1264 in_features: native.in_f,
1265 }
1266}
1267
1268#[allow(clippy::too_many_arguments)] fn build_step_distributed_exps(
1270 e: &Engine,
1271 cfg: &ModelConfig,
1272 src: &dyn TensorSource,
1273 layer: usize,
1274 gate: &HostExps,
1275 up: &HostExps,
1276 down: &HostExps,
1277 step_runtimes: &mut StepParallelRuntimeRegistry,
1278) -> Result<(Option<StepEpExps>, Option<StepTpExps>), Box<dyn std::error::Error>> {
1279 let ep_device_arithmetic = step_runtimes.config.ep_device_arithmetic;
1280 if step_runtimes.config.ep_specs.is_empty() && step_runtimes.config.tp_specs.is_empty() {
1281 if ep_device_arithmetic {
1282 return Err(
1283 "MEMRA_STEP_EP_DEVICE_ARITHMETIC=1 requires MEMRA_STEP_TP and \
1284 MEMRA_STEP_TP_NATIVE_P2P=1"
1285 .into(),
1286 );
1287 }
1288 return Ok((None, None));
1289 }
1290 let contract = crate::parallel::ModelParallelContract::from_model(cfg)?;
1291 validate_step_expert_specs(
1292 &contract,
1293 "MEMRA_STEP_EP",
1294 &step_runtimes.config.ep_specs,
1295 false,
1296 )?;
1297 validate_step_expert_specs(
1298 &contract,
1299 "MEMRA_STEP_TP",
1300 &step_runtimes.config.tp_specs,
1301 true,
1302 )?;
1303 let Some(selection) = step_runtimes.expert_selection(layer)? else {
1304 return Ok((None, None));
1305 };
1306 validate_step_expert_activation_layout(
1307 cfg,
1308 if selection.configured_by_tp {
1309 "MEMRA_STEP_TP"
1310 } else {
1311 "MEMRA_STEP_EP"
1312 },
1313 &selection,
1314 )?;
1315 let activation_limit = match cfg.clamp_exp_at(layer as u32) {
1320 None => None,
1321 Some(SwigluClamp::Post(l)) => Some(l),
1322 Some(SwigluClamp::Pre(_)) => {
1323 return Err(format!(
1324 "MEMRA_STEP_EP/TP layer {layer}: glm5_next PRE-clamped SwiGLU has no \
1325 expert-parallel arm (the banks encode step35's post-clamp form)"
1326 )
1327 .into());
1328 }
1329 };
1330 let owner = e.ctx().ordinal();
1331 if !selection.spec.devices.contains(&owner) {
1332 let flag = if selection.configured_by_tp {
1333 "MEMRA_STEP_TP"
1334 } else {
1335 "MEMRA_STEP_EP"
1336 };
1337 return Err(format!(
1338 "{flag} layer {layer} owning PP device {owner} is absent from rank devices {:?}",
1339 selection.spec.devices
1340 )
1341 .into());
1342 }
1343 let expert_parallel = selection.layout == StepExpertLayout::ExpertParallel;
1344 if selection.configured_by_tp {
1345 contract.plan(crate::parallel::TopologyRequest {
1346 pipeline: 1,
1347 tensor: selection.spec.devices.len(),
1348 expert_parallel,
1349 available_devices: selection.spec.devices.len(),
1350 hardware: crate::parallel::HardwareTarget::RtxPro6000Blackwell,
1351 })?;
1352 }
1353 let native_p2p = selection.configured_by_tp && step_runtimes.config.native_p2p;
1354 if ep_device_arithmetic
1355 && (!selection.configured_by_tp
1356 || selection.layout != StepExpertLayout::ExpertParallel
1357 || !native_p2p)
1358 {
1359 return Err(
1360 "MEMRA_STEP_EP_DEVICE_ARITHMETIC=1 requires a MEMRA_STEP_TP TP4/TP8 \
1361 expert-owner layer and MEMRA_STEP_TP_NATIVE_P2P=1"
1362 .into(),
1363 );
1364 }
1365 let expert_artifact = step_runtimes.config.expert_artifact;
1366 match selection.layout {
1367 StepExpertLayout::ExpertParallel => {
1368 if expert_artifact == StepExpertArtifact::Nvfp4 {
1369 let w4a16_device_routes = step_runtimes.config.nvfp4_device_routes;
1373 if w4a16_device_routes
1374 && !matches!(
1375 src.expert_activation_precision(),
1376 memra_gguf::source::ExpertActivationPrecision::Bf16
1377 )
1378 {
1379 return Err(
1380 "explicit-EP MEMRA_STEP_NVFP4_DEV_ROUTES=1 requires an artifact that \
1381 declares BF16 routed-expert activations; TP keeps its separately gated \
1382 quantized-activation path"
1383 .into(),
1384 );
1385 }
1386 let runtime = step_runtimes.runtime(
1391 &selection.spec.devices,
1392 step_runtimes.config.native_p2p,
1393 false,
1394 )?;
1395 let experts = runtime.upload_expert_parallel_nvfp4_normalized(gate, up, down)?;
1396 let marker = if step_runtimes.config.auto_parallel {
1397 "parallel-ep"
1398 } else {
1399 "step-ep"
1400 };
1401 eprintln!(
1402 "[{marker}] layer={layer} devices={:?} experts={} artifact=nvfp4 \
1403 expert_layout=expert-parallel expert_transport={} \
1404 macro_fold=post-kernel-once native_p2p={} w4a16_device_routes={} \
1405 performance_claim=false",
1406 selection.spec.devices,
1407 contract.expert_count,
1408 runtime.transport_label(),
1409 runtime.native_p2p(),
1410 w4a16_device_routes,
1411 );
1412 if let Some(limit) = activation_limit {
1413 eprintln!(
1414 "[step-ep-clamp] load layer={layer} routed_clamp={limit} \
1415 formula=min-silu-times-clamped-up performance_claim=false"
1416 );
1417 }
1418 return Ok((
1419 Some(StepEpExps {
1420 runtime,
1421 experts: StepEpExpertBank::Nvfp4(experts),
1422 devices: selection.spec.devices,
1423 configured_by_tp: selection.configured_by_tp,
1424 activation_limit,
1425 nvfp4_device_routes: w4a16_device_routes,
1426 grouped_decode: None,
1427 }),
1428 None,
1429 ));
1430 }
1431 let runtime =
1432 step_runtimes.runtime(&selection.spec.devices, native_p2p, ep_device_arithmetic)?;
1433 let experts = runtime.upload_expert_parallel(
1434 host_e4m3_bank(gate)?,
1435 host_e4m3_bank(up)?,
1436 host_e4m3_bank(down)?,
1437 )?;
1438 let grouped_decode = if ep_device_arithmetic {
1439 let tokens = 1;
1440 let selected = (0..contract.experts_per_token).collect::<Vec<_>>();
1441 let input = vec![0.0f32; contract.hidden_size];
1442 let route_weights = vec![1.0f32; contract.experts_per_token];
1443 let projection = runtime.prepare_step_grouped_expert_parallel_gate_with_capacity(
1444 &experts,
1445 &input,
1446 tokens,
1447 &selected,
1448 activation_limit,
1449 tokens,
1450 )?;
1451 let combine = runtime
1452 .prepare_step_grouped_expert_parallel_combine(&projection, &route_weights)?;
1453 Some(std::sync::Mutex::new(StepEpGroupedDecode {
1454 projection,
1455 combine,
1456 }))
1457 } else {
1458 None
1459 };
1460 if selection.configured_by_tp {
1461 eprintln!(
1462 "[step-tp-ep] layer={layer} devices={:?} experts={} tp={} \
1463 attention_layout=tensor-parallel expert_layout=expert-parallel \
1464 expert_transport={} tp_transport={} native_p2p={} \
1465 activation={} accumulation={} output={} \
1466 grouped_decode_prepared={} grouped_decode_capacity=1 \
1467 performance_claim=false",
1468 selection.spec.devices,
1469 contract.expert_count,
1470 selection.spec.devices.len(),
1471 runtime.transport_label(),
1472 runtime.transport_label(),
1473 runtime.native_p2p(),
1474 runtime.expert_activation_label(),
1475 runtime.expert_accumulation_label(),
1476 runtime.expert_output_label(),
1477 grouped_decode.is_some(),
1478 );
1479 } else {
1480 eprintln!(
1481 "[step-ep] layer={layer} devices={:?} experts={} \
1482 expert_layout=expert-parallel expert_transport=host-bounce \
1483 native_p2p=false performance_claim=false",
1484 selection.spec.devices, contract.expert_count
1485 );
1486 }
1487 if let Some(limit) = activation_limit {
1488 eprintln!(
1489 "[step-ep-clamp] load layer={layer} routed_clamp={limit} \
1490 formula=min-silu-times-clamped-up performance_claim=false"
1491 );
1492 }
1493 Ok((
1494 Some(StepEpExps {
1495 runtime,
1496 experts: StepEpExpertBank::E4m3(experts),
1497 devices: selection.spec.devices,
1498 configured_by_tp: selection.configured_by_tp,
1499 activation_limit,
1500 nvfp4_device_routes: false,
1501 grouped_decode,
1502 }),
1503 None,
1504 ))
1505 }
1506 StepExpertLayout::TensorParallel => {
1507 let runtime =
1508 step_runtimes.runtime(&selection.spec.devices, native_p2p, ep_device_arithmetic)?;
1509 if activation_limit.is_some() && expert_artifact == StepExpertArtifact::E4m3 {
1510 return Err(format!(
1511 "layer {layer} uses the routed SwiGLU clamp and the E4M3 TP expert \
1512 program has no clamp arm; select EP for this layer (the NVFP4 TP \
1513 program carries the clamp)"
1514 )
1515 .into());
1516 }
1517 let experts = if expert_artifact == StepExpertArtifact::Nvfp4 {
1518 let gate_native = nvfp4_native_expert_bank(src, layer, "gate")?;
1519 let up_native = nvfp4_native_expert_bank(src, layer, "up")?;
1520 let down_native = nvfp4_native_expert_bank(src, layer, "down")?;
1521 StepTpExpertBank::Nvfp4(runtime.upload_tensor_parallel_nvfp4(
1522 nvfp4_expert_bank_view(&gate_native),
1523 nvfp4_expert_bank_view(&up_native),
1524 nvfp4_expert_bank_view(&down_native),
1525 )?)
1526 } else {
1527 StepTpExpertBank::E4m3(runtime.upload_tensor_parallel(
1528 host_e4m3_bank(gate)?,
1529 host_e4m3_bank(up)?,
1530 host_e4m3_bank(down)?,
1531 )?)
1532 };
1533 eprintln!(
1534 "[step-tp] layer={layer} devices={:?} experts={} tp={} artifact={} \
1535 expert_layout=tensor-parallel transport={} native_p2p={} \
1536 performance_claim=false",
1537 selection.spec.devices,
1538 contract.expert_count,
1539 selection.spec.devices.len(),
1540 match expert_artifact {
1541 StepExpertArtifact::E4m3 => "e4m3",
1542 StepExpertArtifact::Nvfp4 => "nvfp4",
1543 },
1544 runtime.transport_label(),
1545 runtime.native_p2p(),
1546 );
1547 if let Some(limit) = activation_limit {
1548 eprintln!(
1549 "[step-tp-clamp] load layer={layer} routed_clamp={limit} \
1550 formula=min-silu-times-clamped-up performance_claim=false"
1551 );
1552 }
1553 Ok((
1554 None,
1555 Some(StepTpExps {
1556 runtime,
1557 experts,
1558 devices: selection.spec.devices,
1559 activation_limit,
1560 }),
1561 ))
1562 }
1563 }
1564}
1565
1566fn upload_step_bf16_column(
1567 runtime: &crate::tp::TpE4m3HostBounce,
1568 src: &dyn TensorSource,
1569 name: &str,
1570 expected_in: usize,
1571 expected_out: usize,
1572 f32_mirror: bool,
1573) -> Result<crate::tp::ResidentBf16ColumnParallel, Box<dyn std::error::Error>> {
1574 let tensor = src
1575 .find(name)
1576 .ok_or_else(|| format!("Step TP projection is missing {name}"))?;
1577 if tensor.ggml_type != GgmlType::BF16 {
1578 return Err(format!(
1579 "Step TP projection {name} must preserve checkpoint BF16 bytes, got {:?}",
1580 tensor.ggml_type
1581 )
1582 .into());
1583 }
1584 if tensor.ne.len() != 2 {
1585 return Err(format!(
1586 "Step TP projection {name} must be a 2-D matrix, got shape {:?}",
1587 tensor.ne
1588 )
1589 .into());
1590 }
1591 let matrix = crate::tp::Bf16Matrix {
1592 bytes: tensor.bytes.as_ref(),
1593 in_features: tensor.ne[0] as usize,
1594 out_features: tensor.ne[1] as usize,
1595 };
1596 matrix.validate()?;
1597 if matrix.in_features != expected_in || matrix.out_features != expected_out {
1598 return Err(format!(
1599 "Step TP projection {name} shape {}x{} != registered {expected_out}x{expected_in}",
1600 matrix.out_features, matrix.in_features
1601 )
1602 .into());
1603 }
1604 Ok(if f32_mirror {
1605 runtime.upload_step_bf16_column_parallel_f32_mirror(matrix)?
1606 } else {
1607 runtime.upload_step_bf16_column_parallel(matrix)?
1608 })
1609}
1610
1611fn upload_step_bf16_row(
1612 runtime: &crate::tp::TpE4m3HostBounce,
1613 src: &dyn TensorSource,
1614 name: &str,
1615 expected_in: usize,
1616 expected_out: usize,
1617 f32_mirror: bool,
1618) -> Result<crate::tp::ResidentStepBf16RowParallel, Box<dyn std::error::Error>> {
1619 let tensor = src
1620 .find(name)
1621 .ok_or_else(|| format!("Step TP projection is missing {name}"))?;
1622 if tensor.ggml_type != GgmlType::BF16 {
1623 return Err(format!(
1624 "Step TP projection {name} must preserve checkpoint BF16 bytes, got {:?}",
1625 tensor.ggml_type
1626 )
1627 .into());
1628 }
1629 if tensor.ne.len() != 2 {
1630 return Err(format!(
1631 "Step TP projection {name} must be a 2-D matrix, got shape {:?}",
1632 tensor.ne
1633 )
1634 .into());
1635 }
1636 let matrix = crate::tp::Bf16Matrix {
1637 bytes: tensor.bytes.as_ref(),
1638 in_features: tensor.ne[0] as usize,
1639 out_features: tensor.ne[1] as usize,
1640 };
1641 matrix.validate()?;
1642 if matrix.in_features != expected_in || matrix.out_features != expected_out {
1643 return Err(format!(
1644 "Step TP projection {name} shape {}x{} != registered {expected_out}x{expected_in}",
1645 matrix.out_features, matrix.in_features
1646 )
1647 .into());
1648 }
1649 Ok(if f32_mirror {
1650 runtime.upload_step_bf16_row_parallel_f32_mirror(matrix)?
1651 } else {
1652 runtime.upload_step_bf16_row_parallel(matrix)?
1653 })
1654}
1655
1656fn upload_step_tp_f32_copies(
1657 runtime: &crate::tp::TpE4m3HostBounce,
1658 src: &dyn TensorSource,
1659 name: &str,
1660 expected: usize,
1661) -> Result<Vec<CudaSlice<f32>>, Box<dyn std::error::Error>> {
1662 let tensor = src
1663 .find(name)
1664 .ok_or_else(|| format!("Step TP attention is missing {name}"))?;
1665 let values = memra_gguf::dequant::dequantize(
1666 tensor.ggml_type,
1667 &tensor.bytes,
1668 tensor.ne.iter().product::<u64>() as usize,
1669 );
1670 if values.len() != expected || values.iter().any(|value| !value.is_finite()) {
1671 return Err(format!(
1672 "Step TP attention {name} has {} finite values, expected {expected}",
1673 values.len()
1674 )
1675 .into());
1676 }
1677 let mut copies = Vec::with_capacity(runtime.devices().len());
1678 for rank in 0..runtime.devices().len() {
1679 let engine = runtime
1680 .rank_engine(rank)
1681 .ok_or_else(|| format!("Step TP attention has no engine for rank {rank}"))?;
1682 let _main = engine.gpu.enter_main()?;
1683 copies.push(engine.htod(&values)?);
1684 }
1685 Ok(copies)
1686}
1687
1688#[allow(clippy::manual_is_multiple_of)] fn upload_step_tp_f32_row_shards(
1694 runtime: &crate::tp::TpE4m3HostBounce,
1695 src: &dyn TensorSource,
1696 name: &str,
1697 rows: usize,
1698 cols: usize,
1699) -> Result<Vec<CudaSlice<f32>>, Box<dyn std::error::Error>> {
1700 let tensor = src
1701 .find(name)
1702 .ok_or_else(|| format!("Step TP attention is missing {name}"))?;
1703 let values = memra_gguf::dequant::dequantize(
1704 tensor.ggml_type,
1705 &tensor.bytes,
1706 tensor.ne.iter().product::<u64>() as usize,
1707 );
1708 let world = runtime.devices().len();
1709 if values.len() != rows * cols || rows % world != 0 || values.iter().any(|v| !v.is_finite()) {
1710 return Err(format!(
1711 "Step TP attention {name} has {} finite values, expected {rows}x{cols} \
1712 (rows divisible by world {world})",
1713 values.len()
1714 )
1715 .into());
1716 }
1717 let local_rows = rows / world;
1718 let mut shards = Vec::with_capacity(world);
1719 for rank in 0..world {
1720 let engine = runtime
1721 .rank_engine(rank)
1722 .ok_or_else(|| format!("Step TP attention has no engine for rank {rank}"))?;
1723 let _main = engine.gpu.enter_main()?;
1724 shards
1725 .push(engine.htod(&values[rank * local_rows * cols..(rank + 1) * local_rows * cols])?);
1726 }
1727 Ok(shards)
1728}
1729
1730#[allow(clippy::manual_is_multiple_of)] fn upload_step_tp_bf16_row_shards(
1733 runtime: &crate::tp::TpE4m3HostBounce,
1734 src: &dyn TensorSource,
1735 name: &str,
1736 rows: usize,
1737 cols: usize,
1738) -> Result<Vec<CudaSlice<u8>>, Box<dyn std::error::Error>> {
1739 let tensor = src
1740 .find(name)
1741 .ok_or_else(|| format!("Step TP attention is missing {name}"))?;
1742 if tensor.ggml_type != memra_gguf::GgmlType::BF16 || tensor.bytes.len() != rows * cols * 2 {
1743 return Err(format!(
1744 "Step TP attention {name} is not a bf16 [{rows}, {cols}] tensor ({} bytes, {:?})",
1745 tensor.bytes.len(),
1746 tensor.ggml_type
1747 )
1748 .into());
1749 }
1750 let world = runtime.devices().len();
1751 if rows % world != 0 {
1752 return Err(format!("{name} rows {rows} not divisible by world {world}").into());
1753 }
1754 let local = rows / world * cols * 2;
1755 let mut shards = Vec::with_capacity(world);
1756 for rank in 0..world {
1757 let engine = runtime
1758 .rank_engine(rank)
1759 .ok_or_else(|| format!("Step TP attention has no engine for rank {rank}"))?;
1760 let _main = engine.gpu.enter_main()?;
1761 shards.push(engine.htod_bytes(&tensor.bytes[rank * local..(rank + 1) * local])?);
1762 }
1763 Ok(shards)
1764}
1765
1766#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1767enum StepTpAttentionPlacement {
1768 RankLocalGlobal,
1769 RankLocalSwa,
1770 OwnerSwa,
1771 OwnerTransportFallback,
1772}
1773
1774impl StepTpAttentionPlacement {
1775 fn resolve(native_p2p: bool, window: Option<u32>) -> Self {
1776 match (native_p2p, window.is_some()) {
1777 (true, true) => Self::RankLocalSwa,
1778 (false, true) => Self::OwnerSwa,
1779 (true, false) => Self::RankLocalGlobal,
1780 (false, false) => Self::OwnerTransportFallback,
1781 }
1782 }
1783
1784 fn is_rank_local(self) -> bool {
1785 matches!(self, Self::RankLocalGlobal | Self::RankLocalSwa)
1786 }
1787
1788 fn label(self) -> &'static str {
1789 match self {
1790 Self::RankLocalGlobal => "rank-local-global",
1791 Self::RankLocalSwa => "rank-local-swa-ring",
1792 Self::OwnerSwa => "owner-swa",
1793 Self::OwnerTransportFallback => "owner-transport-fallback",
1794 }
1795 }
1796}
1797
1798fn build_step_tp_qkv(
1799 e: &Engine,
1800 src: &dyn TensorSource,
1801 cfg: &ModelConfig,
1802 layer: usize,
1803 step_runtimes: &mut StepParallelRuntimeRegistry,
1804) -> Result<Option<StepTpQkv>, Box<dyn std::error::Error>> {
1805 let Some(spec) = step_runtimes.tp_spec(layer).cloned() else {
1806 return Ok(None);
1807 };
1808 let contract = crate::parallel::ModelParallelContract::from_model(cfg)?;
1809 if layer >= contract.trunk_layers {
1810 return Err(format!(
1811 "MEMRA_STEP_TP layer {layer} is outside Step trunk layers 0..{}",
1812 contract.trunk_layers
1813 )
1814 .into());
1815 }
1816 let owner = e.ctx().ordinal();
1817 if spec.devices.first().copied() != Some(owner) {
1818 return Err(format!(
1819 "MEMRA_STEP_TP layer {layer} owning PP device {owner} must be the first QKV rank, \
1820 got {:?}",
1821 spec.devices
1822 )
1823 .into());
1824 }
1825 let plan = contract.plan(crate::parallel::TopologyRequest {
1826 pipeline: 1,
1827 tensor: spec.devices.len(),
1828 expert_parallel: spec.devices.len() > 2,
1829 available_devices: spec.devices.len(),
1830 hardware: crate::parallel::HardwareTarget::RtxPro6000Blackwell,
1831 })?;
1832 for rank in 0..spec.devices.len() {
1833 plan.query_head_range(layer, rank).ok_or_else(|| {
1834 format!("Step TP layer {layer} has no query-head range for rank {rank}")
1835 })?;
1836 plan.kv_head_range(layer, rank)
1837 .ok_or_else(|| format!("Step TP layer {layer} has no KV-head range for rank {rank}"))?;
1838 }
1839 let native_p2p = step_runtimes.config.native_p2p;
1840 let ep_device_arithmetic = step_runtimes.config.ep_device_arithmetic;
1841 let f32_mirror = step_runtimes.config.f32_mirror;
1842 if ep_device_arithmetic && (!native_p2p || !matches!(spec.devices.len(), 4 | 8)) {
1843 return Err(
1844 "MEMRA_STEP_EP_DEVICE_ARITHMETIC=1 requires a MEMRA_STEP_TP TP4/TP8 \
1845 expert-owner layer and MEMRA_STEP_TP_NATIVE_P2P=1"
1846 .into(),
1847 );
1848 }
1849 let runtime = step_runtimes.runtime(&spec.devices, native_p2p, ep_device_arithmetic)?;
1850 let p = |suffix: &str| format!("blk.{layer}.{suffix}");
1851 let q = upload_step_bf16_column(
1852 &runtime,
1853 src,
1854 &p("attn_q.weight"),
1855 contract.hidden_size,
1856 contract.query_heads[layer] * contract.head_dim,
1857 f32_mirror,
1858 )?;
1859 let k = upload_step_bf16_column(
1860 &runtime,
1861 src,
1862 &p("attn_k.weight"),
1863 contract.hidden_size,
1864 contract.kv_heads[layer] * contract.head_dim,
1865 f32_mirror,
1866 )?;
1867 let v = upload_step_bf16_column(
1868 &runtime,
1869 src,
1870 &p("attn_v.weight"),
1871 contract.hidden_size,
1872 contract.kv_heads[layer] * contract.head_dim,
1873 f32_mirror,
1874 )?;
1875 let o = upload_step_bf16_row(
1876 &runtime,
1877 src,
1878 &p("attn_output.weight"),
1879 contract.query_heads[layer] * contract.head_dim,
1880 contract.hidden_size,
1881 f32_mirror,
1882 )?;
1883 let geometry = cfg.full_attention_geometry_at(layer as u32);
1884 let attention_placement =
1885 StepTpAttentionPlacement::resolve(runtime.native_p2p(), geometry.window);
1886 let attention = if attention_placement.is_rank_local() {
1887 let decode_input = if ep_device_arithmetic || crate::tp::step_tp_decode_v2_enabled()? {
1892 Some(std::sync::Mutex::new(
1893 runtime.allocate_replicated_device_rows(1, contract.hidden_size)?,
1894 ))
1895 } else {
1896 None
1897 };
1898 let gate_fused =
1901 crate::tp::step_tp_qkv_fused_enabled()? && src.find(&p("attn_gate.weight")).is_some();
1902 let gate_shards = if gate_fused && f32_mirror {
1903 Some(upload_step_tp_f32_row_shards(
1904 &runtime,
1905 src,
1906 &p("attn_gate.weight"),
1907 contract.query_heads[layer],
1908 contract.hidden_size,
1909 )?)
1910 } else {
1911 None
1912 };
1913 let gate_shards_bf16 = if gate_fused && !f32_mirror {
1914 Some(upload_step_tp_bf16_row_shards(
1915 &runtime,
1916 src,
1917 &p("attn_gate.weight"),
1918 contract.query_heads[layer],
1919 contract.hidden_size,
1920 )?)
1921 } else {
1922 None
1923 };
1924 Some(StepTpAttention {
1925 q_norm: upload_step_tp_f32_copies(
1926 &runtime,
1927 src,
1928 &p("attn_q_norm.weight"),
1929 contract.head_dim,
1930 )?,
1931 k_norm: upload_step_tp_f32_copies(
1932 &runtime,
1933 src,
1934 &p("attn_k_norm.weight"),
1935 contract.head_dim,
1936 )?,
1937 decode_input,
1938 gate_shards,
1939 gate_shards_bf16,
1940 })
1941 } else {
1942 None
1943 };
1944 if f32_mirror {
1945 eprintln!(
1946 "[step-tp-qkv] load layer={layer} devices={:?} projections=qkv \
1947 qkv_tensor_parallel=true attention_local=true kv_local=true output_local=true \
1948 transport={} native_p2p={} bf16_residency=f32-mirror \
1949 output=root-readback performance_claim=false",
1950 spec.devices,
1951 runtime.transport_label(),
1952 runtime.native_p2p(),
1953 );
1954 } else {
1955 eprintln!(
1956 "[step-tp-qkv] load layer={layer} devices={:?} projections=qkv \
1957 qkv_tensor_parallel=true attention_local=true kv_local=true output_local=true \
1958 transport={} native_p2p={} output=root-readback performance_claim=false",
1959 spec.devices,
1960 runtime.transport_label(),
1961 runtime.native_p2p(),
1962 );
1963 }
1964 eprintln!(
1965 "[step-tp-attn-plan] load layer={layer} devices={:?} \
1966 qkv_tensor_parallel=true attention_tensor_parallel={} kv_cache_distributed={} \
1967 attention_scope={} transport={} native_p2p={} replicated_decode_input_prepared={} \
1968 performance_claim=false",
1969 spec.devices,
1970 attention_placement.is_rank_local(),
1971 attention_placement.is_rank_local(),
1972 attention_placement.label(),
1973 runtime.transport_label(),
1974 runtime.native_p2p(),
1975 attention
1976 .as_ref()
1977 .is_some_and(|attention| attention.decode_input.is_some()),
1978 );
1979 if f32_mirror {
1980 eprintln!(
1981 "[step-tp-o] load layer={layer} devices={:?} projection=o \
1982 o_tensor_parallel=true attention_local=true kv_local=true \
1983 transport={} native_p2p={} reduction=global-tp8-block-order \
1984 bf16_residency=f32-mirror output=root-readback performance_claim=false",
1985 spec.devices,
1986 runtime.transport_label(),
1987 runtime.native_p2p(),
1988 );
1989 } else {
1990 eprintln!(
1991 "[step-tp-o] load layer={layer} devices={:?} projection=o \
1992 o_tensor_parallel=true attention_local=true kv_local=true \
1993 transport={} native_p2p={} reduction=global-tp8-block-order \
1994 output=root-readback performance_claim=false",
1995 spec.devices,
1996 runtime.transport_label(),
1997 runtime.native_p2p(),
1998 );
1999 }
2000 Ok(Some(StepTpQkv {
2001 runtime,
2002 q,
2003 k,
2004 v,
2005 o,
2006 attention,
2007 devices: spec.devices,
2008 layer,
2009 }))
2010}
2011
2012fn build_dev_exps(
2025 e: &Engine,
2026 resident: &mut ResidentPlan,
2027 il: usize,
2028 gate: &HostExps,
2029 up: &HostExps,
2030 down: &HostExps,
2031) -> Result<Option<crate::hybrid::DevExps>, Box<dyn std::error::Error>> {
2032 if !gate.is_uniform_layout() || !up.is_uniform_layout() || !down.is_uniform_layout() {
2035 return Ok(None);
2036 }
2037 let fp8_host = match (&gate.fp8_blk, &up.fp8_blk, &down.fp8_blk) {
2038 (None, None, None) => None,
2039 (Some(g), Some(u), Some(d)) => Some((g, u, d)),
2040 _ => {
2041 return Err("resident expert projections disagree on block-E4M3 scale carriage".into());
2042 }
2043 };
2044 let scale_bytes = fp8_host
2045 .map(|(g, u, d)| (g.scales.len() + u.scales.len() + d.scales.len()) * size_of::<f32>())
2046 .unwrap_or(0);
2047 let per_layer = gate.bytes.as_bytes().len()
2048 + up.bytes.as_bytes().len()
2049 + down.bytes.as_bytes().len()
2050 + scale_bytes;
2051 if gate.tiers.is_some() {
2052 return Ok(None); }
2054 let fits = resident.should_reside(e, il, per_layer);
2055 if !fits {
2056 return Ok(None);
2057 }
2058 use cudarc::driver::DevicePtr;
2059 let gu_il = std::env::var("MEMRA_MOE_GU_IL").as_deref() == Ok("1")
2060 && gate.out_f == up.out_f
2061 && gate.in_f == up.in_f
2062 && fp8_host.is_none();
2063 let n_expert = gate.n_expert;
2064 let (g, u) = if gu_il {
2065 let (rbg, rbu) = (gate.row_bytes, up.row_bytes);
2067 let n_rows = gate.out_f;
2068 let gb = gate.bytes.as_bytes();
2069 let ub = up.bytes.as_bytes();
2070 let mut il = vec![0u8; n_expert * n_rows * (rbg + rbu)];
2071 for ex in 0..n_expert {
2072 for o in 0..n_rows {
2073 let dst = (ex * n_rows + o) * (rbg + rbu);
2074 let sg = ex * gate.expert_stride + o * rbg;
2075 let su = ex * up.expert_stride + o * rbu;
2076 il[dst..dst + rbg].copy_from_slice(&gb[sg..sg + rbg]);
2077 il[dst + rbg..dst + rbg + rbu].copy_from_slice(&ub[su..su + rbu]);
2078 }
2079 }
2080 let ild = e.htod_bytes_padded(&il, 8)?;
2081 (ild, e.htod_bytes(&[0u8; 16])?)
2084 } else {
2085 (
2086 e.htod_bytes_padded(gate.bytes.as_bytes(), 8)?,
2087 e.htod_bytes_padded(up.bytes.as_bytes(), 8)?,
2088 )
2089 };
2090 let d = e.htod_bytes_padded(down.bytes.as_bytes(), 144)?;
2095 let rp_want = crate::moe_expert_rp_on()
2099 && !gu_il
2100 && fp8_host.is_none()
2101 && gate.qtype == crate::QT_NVFP4
2102 && up.qtype == crate::QT_NVFP4
2103 && down.qtype == crate::QT_NVFP4
2104 && gate.in_f.is_multiple_of(256)
2105 && up.in_f.is_multiple_of(256)
2106 && down.in_f.is_multiple_of(256)
2107 && gate.row_bytes == gate.in_f / 64 * 36
2108 && up.row_bytes == up.in_f / 64 * 36
2109 && down.row_bytes == down.in_f / 64 * 36;
2110 if crate::moe_expert_rp_on() && !rp_want {
2111 static SAID: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false);
2112 if !SAID.swap(true, std::sync::atomic::Ordering::Relaxed) {
2113 eprintln!(
2114 "[moe-rp] MEMRA_MOE_EXPERT_RP=1 REFUSED for layer {il}: needs plain NVFP4 gate/up/down \
2115 slabs (qtypes {},{},{}), no MEMRA_MOE_GU_IL, no block-FP8 scales, in_f % 256 == 0 \
2116 (gate/up {}, down {}); this and any layer like it stay interleaved",
2117 gate.qtype, up.qtype, down.qtype, gate.in_f, down.in_f
2118 );
2119 }
2120 }
2121 let (g, u, d, rp) = if rp_want {
2122 let g2 = e.nvfp4_expert_split_repack(&g, n_expert, gate.out_f, gate.in_f / 64)?;
2123 let u2 = e.nvfp4_expert_split_repack(&u, n_expert, up.out_f, up.in_f / 64)?;
2124 let d2 = e.nvfp4_expert_split_repack(&d, n_expert, down.out_f, down.in_f / 64)?;
2125 e.stream().synchronize()?;
2126 drop((g, u, d));
2127 static SAID: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false);
2128 if !SAID.swap(true, std::sync::atomic::Ordering::Relaxed) {
2129 eprintln!(
2130 "[moe-rp] resident expert slabs SLOT-MAJOR (QT_NVFP4_V2) from layer {il} (MEMRA_MOE_EXPERT_RP, memra#147): \
2131 gate/up rows={} nsb64={}, down rows={} nsb64={}, n_expert={n_expert}; readers get QT_NVFP4_V2, \
2132 unwired readers refuse by name",
2133 gate.out_f,
2134 gate.in_f / 64,
2135 down.out_f,
2136 down.in_f / 64
2137 );
2138 }
2139 (g2, u2, d2, true)
2140 } else {
2141 (g, u, d, false)
2142 };
2143 let fp8_blk = match fp8_host {
2144 Some((gate, up, down)) => {
2145 if e.fp8_blk_nan_count(&g)? != 0
2146 || e.fp8_blk_nan_count(&u)? != 0
2147 || e.fp8_blk_nan_count(&d)? != 0
2148 {
2149 return Err("native stacked block-E4M3 expert bank contains NaN codes".into());
2150 }
2151 Some(DevExpertFp8BlockScales {
2152 gate: DevExpertFp8ProjectionScales::upload(e, gate, n_expert)?,
2153 up: DevExpertFp8ProjectionScales::upload(e, up, n_expert)?,
2154 down: DevExpertFp8ProjectionScales::upload(e, down, n_expert)?,
2155 })
2156 }
2157 None => None,
2158 };
2159 let mut host = vec![0u64; 3 * n_expert];
2160 let (pg, pu, pd) = {
2161 let __s_e0 = e.stream();
2162 let (pg, _e0) = g.device_ptr(&__s_e0);
2163 let __s_e1 = e.stream();
2164 let (pu, _e1) = u.device_ptr(&__s_e1);
2165 let __s_e2 = e.stream();
2166 let (pd, _e2) = d.device_ptr(&__s_e2);
2167 (pg, pu, pd)
2168 };
2169 for ex in 0..n_expert {
2170 if gu_il {
2171 let stride = gate.out_f * (gate.row_bytes + up.row_bytes);
2172 host[ex] = pg + (ex * stride) as u64;
2173 host[n_expert + ex] = pg + (ex * stride + gate.row_bytes) as u64;
2174 } else {
2175 host[ex] = pg + (ex * gate.expert_stride) as u64;
2176 host[n_expert + ex] = pu + (ex * up.expert_stride) as u64;
2177 }
2178 host[2 * n_expert + ex] = pd + (ex * down.expert_stride) as u64;
2179 }
2180 if gu_il {
2181 eprintln!("[moe] gate/up dev slab INTERLEAVED (MEMRA_MOE_GU_IL)");
2182 }
2183 let ptr_row = e.htod_u64(&host)?;
2184 Ok(Some(crate::hybrid::DevExps {
2185 gate: g,
2186 up: u,
2187 down: d,
2188 ptr_row,
2189 gu_il,
2190 rp,
2191 dev: e.ctx().ordinal(),
2192 fp8_blk,
2193 }))
2194}
2195
2196pub struct FullAttnLayer {
2197 pub wq: GpuTensor,
2198 pub wk: GpuTensor,
2199 pub wv: GpuTensor,
2200 pub wo: GpuTensor,
2201 pub q_norm: GpuTensor,
2202 pub k_norm: GpuTensor,
2203 pub attn_gate: Option<GpuTensor>,
2214 pub step_tp_qkv: Option<StepTpQkv>,
2218}
2219
2220pub struct StepTpQkv {
2221 pub runtime: Arc<crate::tp::TpE4m3HostBounce>,
2222 pub q: crate::tp::ResidentBf16ColumnParallel,
2223 pub k: crate::tp::ResidentBf16ColumnParallel,
2224 pub v: crate::tp::ResidentBf16ColumnParallel,
2225 pub o: crate::tp::ResidentStepBf16RowParallel,
2226 pub attention: Option<StepTpAttention>,
2227 pub devices: Vec<usize>,
2228 pub layer: usize,
2229}
2230
2231pub struct StepTpAttention {
2232 pub q_norm: Vec<CudaSlice<f32>>,
2233 pub k_norm: Vec<CudaSlice<f32>>,
2234 pub decode_input: Option<std::sync::Mutex<crate::tp::ResidentReplicatedDeviceRows>>,
2235 pub gate_shards: Option<Vec<CudaSlice<f32>>>,
2238 pub gate_shards_bf16: Option<Vec<CudaSlice<u8>>>,
2240}
2241
2242#[derive(Clone, Copy, Debug, PartialEq, Eq)]
2243pub struct StepTpKvDeviceAdmission {
2244 pub device: usize,
2245 pub bytes: usize,
2246}
2247
2248#[derive(Clone, Copy, Debug)]
2252pub struct MlaGeom {
2253 pub n_head: usize, pub d_nope: usize, pub d_rope: usize, pub d_v: usize, pub kv_rank: usize, pub latent_dim: usize, pub scale: f32, }
2261
2262#[derive(Clone, Copy, Debug)]
2267pub struct MlaIndexerGeom {
2268 pub heads: usize, pub head_dim: usize, pub top_k: usize, pub pool: usize, pub always_select_tail: bool,
2273}
2274
2275impl MlaIndexerGeom {
2276 pub fn select_k(&self, n_pools: usize) -> usize {
2278 (self.top_k / self.pool).min(n_pools)
2279 }
2280
2281 pub fn index_width(&self, n_pools: usize) -> usize {
2283 self.select_k(n_pools) * self.pool
2284 + if self.always_select_tail {
2285 self.pool - 1
2286 } else {
2287 0
2288 }
2289 }
2290
2291 pub fn state_width(&self) -> usize {
2293 2 * self.head_dim
2294 }
2295}
2296
2297pub struct MlaIndexer {
2301 pub wq_b: GpuTensor, pub wk: GpuTensor, pub k_norm_w: GpuTensor, pub k_norm_b: GpuTensor, pub weights_proj: GpuTensor, pub kpool_gate: GpuTensor, pub kpool_ape: GpuTensor, pub geom: MlaIndexerGeom,
2309}
2310
2311pub struct MlaAttnLayer {
2312 pub wq_a: GpuTensor, pub q_a_norm: GpuTensor, pub wq_b: GpuTensor, pub wkv_a: GpuTensor, pub kv_a_norm: GpuTensor, pub wk_b: GpuTensor, pub wv_b: GpuTensor, pub wo: GpuTensor, pub geom: MlaGeom,
2322 pub index: Option<MlaIndexer>,
2325 pub tp: Option<Box<crate::glm5_tp::Glm5TpMla>>,
2330 pub tp_shard: bool,
2337}
2338
2339impl MlaAttnLayer {
2340 pub fn load(
2352 e: &Engine,
2353 src: &dyn TensorSource,
2354 il: u32,
2355 plan: &memra_gguf::model_plan::MlaAttentionPlan,
2356 ) -> Result<Self, Box<dyn std::error::Error>> {
2357 let memra_gguf::model_plan::MlaAttentionPlan::LatentKv {
2358 query_heads,
2359 q_lora_rank,
2360 kv_lora_rank,
2361 qk_head_dim,
2362 rope_head_dim,
2363 value_head_dim,
2364 sparse_index,
2365 ..
2366 } = plan
2367 else {
2368 return Err(format!(
2369 "native MLA loader has no compressed-KV implementation for block {il}"
2370 )
2371 .into());
2372 };
2373 let d_nope = qk_head_dim
2374 .checked_sub(*rope_head_dim)
2375 .ok_or("MLA rope head width exceeds total QK head width")?;
2376 let p = |s: &str| format!("blk.{il}.{s}");
2377 let geom = MlaGeom {
2378 n_head: *query_heads as usize,
2379 d_nope: d_nope as usize,
2380 d_rope: *rope_head_dim as usize,
2381 d_v: *value_head_dim as usize,
2382 kv_rank: *kv_lora_rank as usize,
2383 latent_dim: (*kv_lora_rank + *rope_head_dim) as usize,
2384 scale: 1.0 / (*qk_head_dim as f32).sqrt(),
2385 };
2386 let wq_a = load_t(e, src, &p("attn_q_a.weight"))?;
2387 let wq_b = load_t(e, src, &p("attn_q_b.weight"))?;
2388 let wkv_a = load_t(e, src, &p("attn_kv_a_mqa.weight"))?;
2389 let wk_b = load_t(e, src, &p("attn_k_b.weight"))?;
2390 let wv_b = load_t(e, src, &p("attn_v_b.weight"))?;
2391 let wo = load_t(e, src, &p("attn_output.weight"))?;
2392 for (w, tensor) in [(&wk_b, "attn_k_b"), (&wv_b, "attn_v_b")] {
2401 if !matches!(w, GpuTensor::Float { .. }) {
2402 return Err(format!(
2403 "blk.{il}.{tensor}.weight is not f32-resident. The MLA conversion-split \
2404 operands feed f32-only absorb/decompress kernels; the checkpoint source must \
2405 dequantize them (TensorTransform::SplitMlaKv) rather than hand the engine a \
2406 quantized plane"
2407 )
2408 .into());
2409 }
2410 }
2411 let n_head = wq_b.out_features() / (geom.d_nope + geom.d_rope);
2413 assert_eq!(
2414 wq_b.out_features(),
2415 n_head * (geom.d_nope + geom.d_rope),
2416 "wq_b out {} not a multiple of qk_head_dim {}",
2417 wq_b.out_features(),
2418 geom.d_nope + geom.d_rope
2419 );
2420 assert_eq!(
2421 wq_a.in_features(),
2422 wkv_a.in_features(),
2423 "q_a/kv_a hidden mismatch"
2424 );
2425 assert_eq!(
2426 wq_b.in_features(),
2427 *q_lora_rank as usize,
2428 "wq_b in != q_lora_rank"
2429 );
2430 assert_eq!(
2431 n_head, geom.n_head,
2432 "MLA checkpoint head count != ModelPlan"
2433 );
2434 assert_eq!(
2435 wkv_a.out_features(),
2436 geom.latent_dim,
2437 "wkv_a out != kv_lora_rank + rope"
2438 );
2439 assert_eq!(
2440 wk_b.ne(),
2441 &[geom.d_nope as u64, geom.kv_rank as u64, n_head as u64],
2442 "attn_k_b must be the TRANSPOSED (nope, kv_rank, head) conversion split"
2443 );
2444 assert_eq!(
2445 wv_b.ne(),
2446 &[geom.kv_rank as u64, geom.d_v as u64, n_head as u64],
2447 "attn_v_b must be the (kv_rank, v, head) conversion split"
2448 );
2449 assert_eq!(
2450 wo.in_features(),
2451 n_head * geom.d_v,
2452 "wo in != n_head * v_head_dim"
2453 );
2454 let index = Self::load_indexer(e, src, il, sparse_index, *q_lora_rank)?;
2455 Ok(MlaAttnLayer {
2456 wq_a,
2457 q_a_norm: load_t(e, src, &p("attn_q_a_norm.weight"))?,
2458 wq_b,
2459 wkv_a,
2460 kv_a_norm: load_t(e, src, &p("attn_kv_a_norm.weight"))?,
2461 wk_b,
2462 wv_b,
2463 wo,
2464 geom,
2465 index,
2466 tp: None,
2467 tp_shard: false,
2468 })
2469 }
2470
2471 fn load_indexer(
2483 e: &Engine,
2484 src: &dyn TensorSource,
2485 il: u32,
2486 sparse_index: &memra_gguf::model_plan::SparseIndexPlan,
2487 q_lora_rank: u32,
2488 ) -> Result<Option<MlaIndexer>, Box<dyn std::error::Error>> {
2489 let memra_gguf::model_plan::SparseIndexPlan::Own {
2490 heads,
2491 head_dim,
2492 top_k,
2493 kpool: Some(kpool),
2494 } = sparse_index
2495 else {
2496 return Ok(None);
2497 };
2498 let geom = MlaIndexerGeom {
2499 heads: *heads as usize,
2500 head_dim: *head_dim as usize,
2501 top_k: *top_k as usize,
2502 pool: kpool.pool as usize,
2503 always_select_tail: kpool.always_select_tail,
2504 };
2505 if geom.heads == 0 || geom.head_dim == 0 || geom.pool == 0 || geom.top_k < geom.pool {
2506 return Err(format!(
2507 "blk.{il}: SparseIndexPlan::Own declares an unusable k-pool indexer \
2508 (heads {}, head_dim {}, pool {}, top_k {}) — heads/head_dim/pool must be \
2509 positive and top_k must admit at least one pool",
2510 geom.heads, geom.head_dim, geom.pool, geom.top_k
2511 )
2512 .into());
2513 }
2514 let need = |suffix: &str| -> Result<GpuTensor, Box<dyn std::error::Error>> {
2519 let name = format!("blk.{il}.{suffix}");
2520 if !src.has(&name) {
2521 return Err(format!(
2522 "blk.{il}: the layer's ModelPlan declares a DSA k-pool indexer but the \
2523 checkpoint has no `{name}`. This layer MUST NOT fall back to dense \
2524 attention: dense and indexed attention are the same function only below \
2525 index_topk ({}), and glm5_next serves a 1,048,576-token context",
2526 geom.top_k
2527 )
2528 .into());
2529 }
2530 load_t(e, src, &name).map_err(|source| -> Box<dyn std::error::Error> {
2531 format!("blk.{il}: DSA k-pool indexer tensor `{name}` failed to load: {source}")
2532 .into()
2533 })
2534 };
2535 let wq_b = need("indexer.attn_q_b.weight")?;
2536 let wk = need("indexer.attn_k.weight")?;
2537 let k_norm_w = need("indexer.k_norm.weight")?;
2538 let k_norm_b = need("indexer.k_norm.bias")?;
2539 let weights_proj = need("indexer.proj.weight")?;
2540 let kpool_gate = need("indexer.kpool_gate.weight")?;
2541 let kpool_ape = need("indexer.kpool_ape.weight")?;
2542 for (w, name) in [
2545 (&k_norm_w, "indexer.k_norm.weight"),
2546 (&k_norm_b, "indexer.k_norm.bias"),
2547 (&kpool_ape, "indexer.kpool_ape.weight"),
2548 ] {
2549 if !matches!(w, GpuTensor::Float { .. }) {
2550 return Err(format!(
2551 "blk.{il}.{name} is not f32-resident. The indexer's LayerNorm affine and \
2552 k-pool positional embedding feed f32-only kernels"
2553 )
2554 .into());
2555 }
2556 }
2557 assert_eq!(
2558 wq_b.in_features(),
2559 q_lora_rank as usize,
2560 "blk.{il}.indexer.attn_q_b in != q_lora_rank"
2561 );
2562 assert_eq!(
2563 wq_b.out_features(),
2564 geom.heads * geom.head_dim,
2565 "blk.{il}.indexer.attn_q_b out != index heads * head_dim"
2566 );
2567 assert_eq!(
2568 wk.out_features(),
2569 geom.head_dim,
2570 "blk.{il}.indexer.attn_k out != index head_dim"
2571 );
2572 assert_eq!(
2573 weights_proj.out_features(),
2574 geom.heads,
2575 "blk.{il}.indexer.proj out != index heads"
2576 );
2577 assert_eq!(
2578 kpool_gate.out_features(),
2579 geom.head_dim,
2580 "blk.{il}.indexer.kpool_gate out != index head_dim"
2581 );
2582 assert_eq!(
2583 kpool_ape.float_data().len(),
2584 geom.pool * geom.head_dim,
2585 "blk.{il}.indexer.kpool_ape must hold pool * head_dim elements"
2586 );
2587 Ok(Some(MlaIndexer {
2588 wq_b,
2589 wk,
2590 k_norm_w,
2591 k_norm_b,
2592 weights_proj,
2593 kpool_gate,
2594 kpool_ape,
2595 geom,
2596 }))
2597 }
2598}
2599
2600#[track_caller]
2608pub(crate) fn mla_path_unimplemented(path: &str) -> ! {
2609 panic!(
2610 "Mixer::Mla has no {path} arm — the MLA forward is wired for the stateless forward, \
2611 the stateful prime and T=1 decode only (cu/mla_attn.cu, increment 4); this path needs \
2612 its own parity gate before it may run \
2613 (research/mla-bringup-20260801/DESIGN.md §4, increment 7)"
2614 )
2615}
2616
2617#[track_caller]
2623pub(crate) fn kda_path_unimplemented(path: &str) -> ! {
2624 panic!(
2625 "Mixer::Kda has no {path} arm — glm5_next KDA is wired for the stateless forward, the \
2626 stateful prime and T=1 decode only (crates/memra-engine/src/kda.rs); this path needs \
2627 its own parity gate before it may run"
2628 )
2629}
2630
2631pub struct LinearAttnLayer {
2632 pub geometry: memra_gguf::model_plan::GatedDeltaNetPlan,
2633 pub wqkv: GpuTensor, pub wqkv_gate: GpuTensor, pub ssm_beta: GpuTensor, pub ssm_alpha: GpuTensor, pub ssm_a: GpuTensor, pub ssm_dt: GpuTensor, pub ssm_conv1d: GpuTensor, pub ssm_norm: GpuTensor, pub ssm_out: GpuTensor, }
2643
2644#[allow(clippy::large_enum_variant)] pub enum Mixer {
2646 Full(FullAttnLayer),
2647 Linear(LinearAttnLayer),
2648 Mla(MlaAttnLayer),
2650 Kda(crate::kda::KdaAttnLayer),
2652}
2653
2654pub struct MoeWeights {
2661 pub gate_inp: GpuTensor, pub gate_inp_shexp: Option<GpuTensor>, pub exp_probs_b: Option<Vec<f32>>,
2667 pub exp_probs_b_dev: CudaSlice<f32>,
2668 pub active_experts: Option<Vec<bool>>,
2672 pub active_experts_dev: CudaSlice<u8>,
2673 pub gate_exps: HostExps, pub up_exps: HostExps, pub down_exps: HostExps, pub gate_shexp: Option<GpuTensor>,
2677 pub up_shexp: Option<GpuTensor>,
2678 pub down_shexp: Option<GpuTensor>,
2679 pub dev_exps: Option<DevExps>,
2686 pub step_ep: Option<StepEpExps>,
2690 pub step_tp: Option<StepTpExps>,
2694 pub glm5_ep: Option<crate::glm5_tp::Glm5EpExps>,
2699 pub dev_macros: cudarc::driver::CudaSlice<f32>,
2705 pub has_macros: bool,
2706 pub w4a16_bf16_activations: bool,
2709}
2710
2711#[allow(clippy::large_enum_variant)] pub enum StepEpExpertBank {
2714 E4m3(crate::tp::ResidentExpertParallel),
2715 Nvfp4(crate::tp::ResidentNvfp4ExpertParallel),
2716}
2717
2718impl StepEpExpertBank {
2719 pub fn e4m3(&self) -> Result<&crate::tp::ResidentExpertParallel, String> {
2723 match self {
2724 Self::E4m3(bank) => Ok(bank),
2725 Self::Nvfp4(_) => Err(
2726 "Step grouped expert program reached an NVFP4 bank; this path is qualified \
2727 for the E4M3 artifact only"
2728 .to_string(),
2729 ),
2730 }
2731 }
2732}
2733
2734pub struct StepEpExps {
2735 pub runtime: Arc<crate::tp::TpE4m3HostBounce>,
2736 pub experts: StepEpExpertBank,
2737 pub devices: Vec<usize>,
2738 pub configured_by_tp: bool,
2739 pub activation_limit: Option<f32>,
2740 pub nvfp4_device_routes: bool,
2742 pub grouped_decode: Option<std::sync::Mutex<StepEpGroupedDecode>>,
2745}
2746
2747pub struct StepEpGroupedDecode {
2748 pub(crate) projection: crate::tp::PreparedStepGroupedExpertParallelGate,
2749 pub(crate) combine: crate::tp::PreparedPeerWeightedRouteCombine,
2750}
2751
2752#[derive(Default)]
2753pub(crate) struct StepEpGroupedPrefill {
2754 pub(crate) state: Option<StepEpGroupedPrefillState>,
2755}
2756
2757pub(crate) struct StepEpGroupedPrefillState {
2758 pub(crate) devices: Vec<usize>,
2759 pub(crate) grouped: StepEpGroupedDecode,
2760}
2761
2762#[allow(clippy::large_enum_variant)] pub enum StepTpExpertBank {
2765 E4m3(crate::tp::ResidentTensorParallel),
2766 Nvfp4(crate::tp::ResidentNvfp4TensorParallel),
2767}
2768
2769pub struct StepTpExps {
2770 pub runtime: Arc<crate::tp::TpE4m3HostBounce>,
2771 pub experts: StepTpExpertBank,
2772 pub devices: Vec<usize>,
2773 pub activation_limit: Option<f32>,
2776}
2777
2778impl MoeWeights {
2779 #[inline]
2780 pub fn has_uniform_expert_layout(&self) -> bool {
2781 self.gate_exps.is_uniform_layout()
2782 && self.up_exps.is_uniform_layout()
2783 && self.down_exps.is_uniform_layout()
2784 }
2785
2786 #[inline]
2787 pub fn active_count(&self) -> usize {
2788 self.active_experts
2789 .as_ref()
2790 .map(|mask| mask.iter().filter(|&&active| active).count())
2791 .unwrap_or(self.gate_exps.n_expert)
2792 }
2793
2794 #[allow(clippy::too_many_arguments)]
2795 pub(crate) fn qmatvec_view(
2796 &self,
2797 e: &Engine,
2798 w: &CudaSlice<u8>,
2799 range: std::ops::Range<usize>,
2800 x: &cudarc::driver::CudaView<f32>,
2801 m: usize,
2802 in_f: usize,
2803 out_f: usize,
2804 qtype: i32,
2805 row_bytes: usize,
2806 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
2807 if self.w4a16_bf16_activations && qtype == crate::QT_NVFP4 {
2808 e.qmatvec_view_bf16_activation(w, range, x, m, in_f, out_f, qtype, row_bytes)
2809 } else {
2810 e.qmatvec_view(w, range, x, m, in_f, out_f, qtype, row_bytes)
2811 }
2812 }
2813}
2814
2815pub struct DevExps {
2818 pub gate: CudaSlice<u8>,
2819 pub up: CudaSlice<u8>,
2820 pub down: CudaSlice<u8>,
2821 pub ptr_row: CudaSlice<u64>,
2823 pub dev: usize,
2831 pub gu_il: bool,
2837 pub rp: bool,
2840 pub fp8_blk: Option<DevExpertFp8BlockScales>,
2844}
2845
2846pub struct DevExpertFp8BlockScales {
2847 pub gate: DevExpertFp8ProjectionScales,
2848 pub up: DevExpertFp8ProjectionScales,
2849 pub down: DevExpertFp8ProjectionScales,
2850}
2851
2852pub struct DevExpertFp8ProjectionScales {
2853 pub scales: CudaSlice<f32>,
2854 pub rows: usize,
2855 pub cols: usize,
2856 pub expert_stride: usize,
2857}
2858
2859impl DevExpertFp8ProjectionScales {
2860 fn validate(
2861 host: &crate::model::HostExpertFp8BlockScales,
2862 n_expert: usize,
2863 ) -> Result<(), String> {
2864 if host.expert_stride == 0 {
2865 return Err("block-E4M3 expert scale stride must be nonzero".into());
2866 }
2867 if host.rows * host.cols != host.expert_stride {
2868 return Err(format!(
2869 "block-E4M3 expert scale stride mismatch: {}x{} != {}",
2870 host.rows, host.cols, host.expert_stride
2871 ));
2872 }
2873 let want = n_expert
2874 .checked_mul(host.expert_stride)
2875 .ok_or("block-E4M3 expert scale slab length overflow")?;
2876 if host.scales.len() != want {
2877 return Err(format!(
2878 "block-E4M3 scale slab length mismatch: got {}, want {n_expert}x{}={want}",
2879 host.scales.len(),
2880 host.expert_stride
2881 ));
2882 }
2883 Ok(())
2884 }
2885
2886 fn upload(
2887 e: &Engine,
2888 host: &crate::model::HostExpertFp8BlockScales,
2889 n_expert: usize,
2890 ) -> Result<Self, Box<dyn std::error::Error>> {
2891 Self::validate(host, n_expert)?;
2892 Ok(Self {
2893 scales: e.htod(&host.scales)?,
2894 rows: host.rows,
2895 cols: host.cols,
2896 expert_stride: host.expert_stride,
2897 })
2898 }
2899}
2900
2901#[allow(clippy::large_enum_variant)] pub enum Ffn {
2904 Dense {
2905 ffn_gate: GpuTensor,
2906 ffn_up: GpuTensor,
2907 ffn_down: GpuTensor,
2908 },
2909 Moe(MoeWeights),
2910}
2911
2912pub struct HybridLayer {
2913 pub attn_norm: GpuTensor,
2914 pub post_attn_norm: GpuTensor, pub mixer: Mixer,
2916 pub ffn: Ffn,
2917 pub gemma4: Option<Gemma4LayerBits>,
2918 pub hyper: Option<crate::hyper::HyperLayer>,
2923}
2924
2925pub struct Gemma4LayerBits {
2929 pub ffn_norm: GpuTensor, pub post_ffw_norm: GpuTensor, pub moe_bits: Option<Gemma4MoeBits>,
2934 pub layer_scale: f32, pub e4b: Option<Gemma4E4bLayer>,
2937}
2938
2939pub struct Gemma4E4bLayer {
2944 pub inp_gate: GpuTensor, pub proj: GpuTensor, pub post_norm: GpuTensor, pub qkv_cat: Option<GpuTensor>,
2951 pub kv_share: Option<u32>,
2955}
2956
2957pub struct Gemma4E4bModel {
2961 pub tok_tbl_gpu: std::sync::OnceLock<CudaSlice<u8>>,
2964 pub tok_embd_bytes: Vec<u8>,
2965 pub tok_embd_qt: i32,
2966 pub tok_embd_row_bytes: usize,
2967 pub model_proj: GpuTensor, pub proj_norm: GpuTensor, pub n_epl: usize,
2970}
2971
2972pub struct Gemma4MoeBits {
2973 pub post_ffw_norm_1: GpuTensor, pub pre_ffw_norm_2: GpuTensor, pub post_ffw_norm_2: GpuTensor, pub shared_gate: GpuTensor,
2977 pub shared_up: GpuTensor,
2978 pub shared_down: GpuTensor,
2979 pub router_scale_pre: CudaSlice<f32>,
2984 pub per_expert_scale: Vec<f32>, pub per_expert_scale_d: CudaSlice<f32>, }
2987
2988fn load_mtp_head_maybe_nvfp4(
3001 e: &Engine,
3002 src: &dyn TensorSource,
3003 name: &str,
3004) -> Result<Option<GpuTensor>, Box<dyn std::error::Error>> {
3005 if !{
3006 static ENV: std::sync::OnceLock<Option<bool>> = std::sync::OnceLock::new();
3007 crate::step37_door(&ENV, "MEMRA_MTP_HEAD_NVFP4")
3008 } {
3009 return load_opt(e, src, name);
3010 }
3011 let Some(v) = src.find(name) else {
3012 return Ok(None);
3013 };
3014 if !matches!(v.ggml_type, GgmlType::BF16) || v.ne[0] % 64 != 0 {
3015 return load_opt(e, src, name);
3016 }
3017 let vals: Vec<f32> = v
3018 .bytes
3019 .chunks_exact(2)
3020 .map(|c| f32::from_bits((u16::from_le_bytes([c[0], c[1]]) as u32) << 16))
3021 .collect();
3022 let blocks = memra_gguf::nvfp4_repack::f32_to_nvfp4(&vals);
3023 eprintln!(
3024 "[mtp-head] {name}: BF16 -> NVFP4 ({} MiB, was {} MiB)",
3025 blocks.len() >> 20,
3026 v.bytes.len() >> 20
3027 );
3028 Ok(Some(GpuTensor::from_quant_bytes(
3029 e,
3030 &blocks,
3031 GgmlType::NVFP4,
3032 v.ne[0],
3033 v.ne[1],
3034 1.0,
3035 )?))
3036}
3037
3038pub(crate) fn sha256_file_hex8(
3046 path: &std::path::Path,
3047) -> Result<String, Box<dyn std::error::Error>> {
3048 sha256_file_hex(path, 4)
3049}
3050
3051pub(crate) fn sha256_file_hex(
3056 path: &std::path::Path,
3057 n_bytes: usize,
3058) -> Result<String, Box<dyn std::error::Error>> {
3059 use sha2::{Digest, Sha256};
3060 let mut file = std::fs::File::open(path)?;
3061 let mut hasher = Sha256::new();
3062 std::io::copy(&mut file, &mut hasher)?;
3063 let digest = hasher.finalize();
3064 Ok(digest
3065 .iter()
3066 .take(n_bytes)
3067 .map(|byte| format!("{byte:02x}"))
3068 .collect())
3069}
3070
3071pub fn frspec_parse_ranks_txt_strict(text: &str, what: &str) -> Result<Vec<u32>, String> {
3079 let mut out: Vec<u32> = Vec::new();
3080 for (lineno, raw) in text.lines().enumerate() {
3081 let line = raw.trim();
3082 if line.is_empty() {
3083 continue;
3084 }
3085 let id = line.parse::<u32>().map_err(|_| {
3086 format!(
3087 "{what}: line {} is not a token id ({line:?}); a ranks .txt is one integer id \
3088 per line in rank order",
3089 lineno + 1
3090 )
3091 })?;
3092 out.push(id);
3093 }
3094 Ok(out)
3095}
3096
3097pub fn frspec_validate_ranks(d2t: &[u32], n_vocab: usize, what: &str) -> Result<(), String> {
3103 if d2t.is_empty() {
3104 return Err(format!(
3105 "{what}: the ranks artifact yields an EMPTY id list"
3106 ));
3107 }
3108 if d2t.len() > n_vocab {
3109 return Err(format!(
3110 "{what}: {} ranks for a {n_vocab}-row head: a ranks list wider than the vocabulary \
3111 was minted for a different model",
3112 d2t.len()
3113 ));
3114 }
3115 if let Some(&bad) = d2t.iter().find(|&&t| t as usize >= n_vocab) {
3116 return Err(format!(
3117 "{what}: token id {bad} >= head rows {n_vocab}: the ranks artifact was minted for a \
3118 different vocabulary (wrong-model file refused at boot)"
3119 ));
3120 }
3121 let mut seen = vec![false; n_vocab];
3122 for &t in d2t {
3123 if seen[t as usize] {
3124 return Err(format!(
3125 "{what}: token id {t} appears more than once: a ranks list is a set of distinct \
3126 ids in rank order"
3127 ));
3128 }
3129 seen[t as usize] = true;
3130 }
3131 Ok(())
3132}
3133
3134pub fn frspec_gather_rows(rows: &[u8], row_bytes: usize, d2t: &[u32]) -> Vec<u8> {
3139 let mut gathered = Vec::with_capacity(d2t.len() * row_bytes);
3140 for &t in d2t {
3141 let off = t as usize * row_bytes;
3142 gathered.extend_from_slice(&rows[off..off + row_bytes]);
3143 }
3144 gathered
3145}
3146
3147#[cfg(test)]
3148mod frspec_ranks_tests {
3149 use super::{frspec_gather_rows, frspec_parse_ranks_txt_strict, frspec_validate_ranks};
3150
3151 #[test]
3152 fn strict_parse_skips_blank_lines_and_refuses_anything_else() {
3153 let ok = frspec_parse_ranks_txt_strict("5\n\n 7 \n0\n", "t").unwrap();
3154 assert_eq!(ok, vec![5, 7, 0]);
3155 assert_eq!(
3157 frspec_parse_ranks_txt_strict("5\n7", "t").unwrap(),
3158 vec![5, 7]
3159 );
3160 for bad in ["id\n5\n", "5\n-1\n", "5\n7.0\n", "# ranks\n5\n", "5 7\n"] {
3162 let err = frspec_parse_ranks_txt_strict(bad, "t").unwrap_err();
3163 assert!(err.contains("is not a token id"), "{bad:?} -> {err}");
3164 }
3165 assert!(frspec_parse_ranks_txt_strict("", "t").unwrap().is_empty());
3167 }
3168
3169 #[test]
3170 fn validate_refuses_empty_oob_duplicate_and_wider_than_vocab() {
3171 assert!(frspec_validate_ranks(&[3, 1, 0], 4, "t").is_ok());
3172 assert!(frspec_validate_ranks(&[3, 1, 0, 2], 4, "t").is_ok());
3174 let e = frspec_validate_ranks(&[], 4, "t").unwrap_err();
3175 assert!(e.contains("EMPTY"), "{e}");
3176 let e = frspec_validate_ranks(&[3, 4], 4, "t").unwrap_err();
3177 assert!(e.contains("token id 4 >= head rows 4"), "{e}");
3178 let e = frspec_validate_ranks(&[3, 1, 3], 4, "t").unwrap_err();
3179 assert!(e.contains("token id 3 appears more than once"), "{e}");
3180 let e = frspec_validate_ranks(&[0, 1, 2, 3, 0], 4, "t").unwrap_err();
3181 assert!(e.contains("5 ranks for a 4-row head"), "{e}");
3182 }
3183
3184 #[test]
3185 fn gather_rows_is_the_rank_ordered_row_copy() {
3186 let rows: Vec<u8> = (0..5u8).flat_map(|t| [t, t + 10, t + 20]).collect();
3188 let g = frspec_gather_rows(&rows, 3, &[4, 0, 2]);
3189 assert_eq!(g, vec![4, 14, 24, 0, 10, 20, 2, 12, 22]);
3190 assert_ne!(g, frspec_gather_rows(&rows, 3, &[0, 4, 2]));
3192 assert_eq!(frspec_gather_rows(&rows, 3, &[0, 1, 2, 3, 4]), rows);
3194 }
3195}
3196
3197pub(crate) fn frspec_trim_own_head_name(n_trunk: usize) -> String {
3198 format!("blk.{n_trunk}.nextn.shared_head_head.weight")
3199}
3200
3201pub struct DflashTrimHead {
3212 pub head: GpuTensor,
3215 pub d2t: Vec<u32>,
3217 pub src_sha16: String,
3220}
3221
3222fn frspec_read_d2t(path: &str) -> Result<Vec<u32>, Box<dyn std::error::Error>> {
3231 Ok(if path.ends_with(".txt") {
3232 let text = std::fs::read_to_string(path)?;
3233 frspec_parse_ranks_txt_strict(&text, &format!("MEMRA_FRSPEC_TRIM={path}"))?
3234 } else {
3235 let tg = GgufFile::open(path)?;
3236 let d2t_t = tg
3237 .find("d2t")
3238 .expect("MEMRA_FRSPEC_TRIM file has no d2t tensor");
3239 let d2t_bytes = tg.tensor_data(d2t_t);
3240 match d2t_t.ggml_type {
3241 GgmlType::I32 => d2t_bytes
3242 .chunks_exact(4)
3243 .map(|c| i32::from_le_bytes(c.try_into().unwrap()) as u32)
3244 .collect(),
3245 GgmlType::I64 => d2t_bytes
3246 .chunks_exact(8)
3247 .map(|c| i64::from_le_bytes(c.try_into().unwrap()) as u32)
3248 .collect(),
3249 other => panic!("d2t must be I32/I64, got {other:?}"),
3250 }
3251 })
3252}
3253
3254#[allow(clippy::type_complexity)] fn frspec_gather_trimmed_head(
3263 e: &Engine,
3264 v: &memra_gguf::source::TensorView<'_>,
3265 d2t: &[u32],
3266 want_nvfp4_env: bool,
3267 macro_scale: f32,
3268) -> Result<(GpuTensor, Option<(usize, usize)>), Box<dyn std::error::Error>> {
3269 let out_f = v.ne[1] as usize;
3270 let row_bytes = v.bytes.len() / out_f;
3271 assert!(
3272 d2t.iter().all(|&t| (t as usize) < out_f),
3273 "d2t token id >= lm_head rows {out_f}"
3274 );
3275 let gathered = frspec_gather_rows(&v.bytes, row_bytes, d2t);
3276 let want_nvfp4 =
3277 want_nvfp4_env && matches!(v.ggml_type, GgmlType::BF16) && v.ne[0].is_multiple_of(64);
3278 if want_nvfp4 {
3279 let in_f = v.ne[0] as usize;
3280 let vals: Vec<f32> = gathered
3281 .chunks_exact(2)
3282 .map(|c| f32::from_bits((u16::from_le_bytes([c[0], c[1]]) as u32) << 16))
3283 .collect();
3284 debug_assert_eq!(vals.len(), d2t.len() * in_f);
3285 let blocks = memra_gguf::nvfp4_repack::f32_to_nvfp4(&vals);
3286 let sizes = (blocks.len(), gathered.len());
3287 let trimmed = GpuTensor::from_quant_bytes(
3288 e,
3289 &blocks,
3290 GgmlType::NVFP4,
3291 v.ne[0],
3292 d2t.len() as u64,
3293 1.0,
3294 )?;
3295 Ok((trimmed, Some(sizes)))
3296 } else {
3297 let trimmed = match v.ggml_type {
3298 GgmlType::BF16 => GpuTensor::FloatBf16 {
3299 data: e.htod_bytes(&gathered)?,
3300 ne: vec![v.ne[0], d2t.len() as u64],
3301 },
3302 GgmlType::F32 => GpuTensor::Float {
3303 data: e.htod(
3304 &gathered
3305 .chunks_exact(4)
3306 .map(|c| f32::from_le_bytes(c.try_into().unwrap()))
3307 .collect::<Vec<f32>>(),
3308 )?,
3309 ne: vec![v.ne[0], d2t.len() as u64],
3310 },
3311 _ => GpuTensor::from_quant_bytes(
3312 e,
3313 &gathered,
3314 v.ggml_type,
3315 v.ne[0],
3316 d2t.len() as u64,
3317 macro_scale,
3318 )?,
3319 };
3320 Ok((trimmed, None))
3321 }
3322}
3323
3324pub struct MtpHead {
3325 pub enorm: GpuTensor, pub hnorm: GpuTensor, pub eh_proj: GpuTensor, pub attn_norm: GpuTensor, pub post_attn_norm: GpuTensor, pub mixer: Mixer, pub ffn: Ffn, pub shared_head_norm: Option<GpuTensor>, pub shared_head_head: Option<GpuTensor>, pub d2t: Option<Vec<u32>>,
3339 pub d2t_from_target_head: bool,
3343 pub geom: Option<DraftGeom>,
3349 pub step35: Option<Step35MtpGeom>,
3354}
3355
3356#[derive(Debug, Clone)]
3370pub struct Step35MtpGeom {
3371 pub il: u32,
3373 pub n_head: usize, pub n_head_kv: usize, pub n_rot: usize, pub rope_base: f32, pub swa: bool, pub window: usize, pub clamp_shexp: Option<f32>,
3384}
3385
3386impl Step35MtpGeom {
3387 pub fn from_plan(layer: &memra_gguf::model_plan::LayerPlan) -> Result<Self, String> {
3389 use memra_gguf::model_plan::{ActivationPlan, AttentionPlan};
3390
3391 let (attention, window) = match &layer.attention {
3392 AttentionPlan::Full(attention) => (attention, None),
3393 AttentionPlan::SlidingWindow { attention, window } => (attention, Some(*window)),
3394 other => {
3395 return Err(format!(
3396 "MTP block {} has unsupported tuned attention {other:?}",
3397 layer.index
3398 ));
3399 }
3400 };
3401 if attention.output_gate != memra_gguf::config::AttentionGateKind::SeparateHead {
3402 return Err(format!(
3403 "MTP block {} does not declare a separate attention gate",
3404 layer.index
3405 ));
3406 }
3407 let activation = match &layer.mlp {
3408 MlpPlan::Dense(dense) => &dense.activation,
3409 MlpPlan::Moe(moe) => &moe.activation,
3410 };
3411 let clamp_shexp = match activation {
3412 ActivationPlan::SwiGluClamped { limit } if *limit > 0.0 => Some(*limit),
3413 _ => None,
3414 };
3415 Ok(Step35MtpGeom {
3416 il: layer.index,
3417 n_head: attention.query_heads as usize,
3418 n_head_kv: attention.kv_heads as usize,
3419 n_rot: attention.rope.dimensions as usize,
3420 rope_base: attention.rope.base,
3421 swa: window.is_some(),
3422 window: window.unwrap_or(0) as usize,
3423 clamp_shexp,
3424 })
3425 }
3426}
3427
3428pub struct DraftGeom {
3430 pub d_inner: usize, pub n_head: usize, pub n_head_kv: usize,
3433 pub out_up: GpuTensor, }
3435
3436pub fn draft_head_tensor(has: impl Fn(&str) -> bool, n: u32) -> String {
3445 let own = format!("blk.{n}.nextn.shared_head_head.weight");
3446 if has(&own) {
3447 return own;
3448 }
3449 let legacy = format!("blk.{n}.nextn.shared_head.weight");
3452 if has(&legacy) {
3453 return legacy;
3454 }
3455 "output.weight".to_string()
3457}
3458
3459impl MtpHead {
3460 pub fn load_draft(
3467 e: &Engine,
3468 g: &GgufFile,
3469 main_cfg: &ModelConfig,
3470 ) -> Result<Self, Box<dyn std::error::Error>> {
3471 let src = GgufSource(g);
3472 let dcfg = src.try_config().map_err(std::io::Error::other)?;
3473 let draft_plan = match memra_gguf::model_packs::for_config(&dcfg) {
3474 Some(pack) => pack.compile_plan(&dcfg)?,
3475 None => memra_gguf::model_plan::ModelPlan::compile(&dcfg)?,
3476 };
3477 let main_plan = match memra_gguf::model_packs::for_config(main_cfg) {
3478 Some(pack) => pack.compile_plan(main_cfg)?,
3479 None => memra_gguf::model_plan::ModelPlan::compile(main_cfg)?,
3480 };
3481 if dcfg.nextn_predict_layers == 0 {
3486 return Err(format!(
3487 "draft GGUF has no nextn_predict_layers (arch {:?}) — not a NextN/MTP regime \
3488 draft; gemma assistant drafters attach via MEMRA_DRAFT, not '+draft'",
3489 g.arch()
3490 )
3491 .into());
3492 }
3493 let n = dcfg.n_layer - dcfg.nextn_predict_layers;
3494 let draft_block = draft_plan
3495 .mtp_blocks
3496 .iter()
3497 .find(|block| block.layer.index == n)
3498 .ok_or_else(|| format!("draft ModelPlan has no MTP block {n}"))?;
3499 let p = |s: &str| format!("blk.{n}.{s}");
3500
3501 let student = src.has(&p("nextn.out_up.weight"));
3505 assert_eq!(dcfg.n_embd, main_cfg.n_embd, "draft n_embd != model n_embd");
3506 assert_eq!(
3507 dcfg.head_dim_k, main_cfg.head_dim_k,
3508 "draft head_dim != model head_dim"
3509 );
3510 let main_sliding_gated = crate::plan_backend::decode_batch_program(&main_plan)
3517 == crate::plan_backend::DecodeBatchProgram::SlidingGatedMoe;
3518 let draft_sliding_gated = crate::plan_backend::decode_batch_program(&draft_plan)
3519 == crate::plan_backend::DecodeBatchProgram::SlidingGatedMoe;
3520 let step35 = match (main_sliding_gated, draft_sliding_gated) {
3521 (true, true) => {
3522 let g = Step35MtpGeom::from_plan(&draft_block.layer)?;
3523 let out_f = |t: &str| -> Option<usize> {
3525 src.find(&p(t))
3526 .and_then(|v| v.ne.get(1).copied())
3527 .map(|x| x as usize)
3528 };
3529 let hd = dcfg.head_dim_k as usize;
3530 let wq_out =
3531 out_f("attn_q.weight").ok_or("step35 draft block has no attn_q.weight")?;
3532 assert_eq!(
3533 wq_out,
3534 g.n_head * hd,
3535 "step35 draft blk.{n}: attn_q out {wq_out} != n_head({}) * head_dim({hd}) — \
3536 the draft file's head_count array disagrees with its own tensors",
3537 g.n_head
3538 );
3539 let wg_out = out_f("attn_gate.weight")
3542 .ok_or("step35 draft block has no attn_gate.weight (head-wise gate)")?;
3543 assert_eq!(
3544 wg_out, g.n_head,
3545 "step35 draft blk.{n}: attn_gate out {wg_out} != n_head({})",
3546 g.n_head
3547 );
3548 assert_eq!(
3552 g.n_head_kv, main_cfg.n_head_kv as usize,
3553 "step35 draft blk.{n} KV heads {} != trunk n_head_kv {} — the MTP scratch \
3554 rows are sized from the trunk cfg, so a differing draft KV width would \
3555 write past the row",
3556 g.n_head_kv, main_cfg.n_head_kv
3557 );
3558 eprintln!(
3559 "[mtp-draft] step35 MTP geometry blk.{n}: n_head={} n_head_kv={} n_rot={} \
3560 rope_base={:.0} swa={} window={}",
3561 g.n_head, g.n_head_kv, g.n_rot, g.rope_base, g.swa, g.window
3562 );
3563 Some(g)
3564 }
3565 (true, false) => {
3566 return Err(format!(
3567 "MEMRA_MTP_DRAFT operations are incompatible with the model's \
3568 sliding-gated-MoE program (draft arch {:?})",
3569 g.arch()
3570 )
3571 .into());
3572 }
3573 (false, true) => {
3574 return Err(
3575 "MEMRA_MTP_DRAFT requires sliding-gated-MoE operations but the model does not"
3576 .into(),
3577 );
3578 }
3579 (false, false) => None,
3580 };
3581 if step35.is_none() && !student {
3582 assert_eq!(dcfg.n_head, main_cfg.n_head, "draft n_head != model n_head");
3585 assert_eq!(
3586 dcfg.n_head_kv, main_cfg.n_head_kv,
3587 "draft n_head_kv != model n_head_kv"
3588 );
3589 }
3590
3591 let head_name = draft_head_tensor(|t| src.has(t), n);
3618 let head = load_t(e, &src, &head_name)?;
3619 let head_norm = match load_opt(e, &src, &p("nextn.shared_head_norm.weight"))? {
3620 Some(t) => Some(t),
3621 None => load_opt(e, &src, "output_norm.weight")?,
3622 };
3623
3624 let d2t: Option<Vec<u32>> = g.find("d2t").map(|t| {
3626 let bytes = g.tensor_data(t);
3627 match t.ggml_type {
3628 GgmlType::I32 => bytes
3629 .chunks_exact(4)
3630 .map(|c| i32::from_le_bytes(c.try_into().unwrap()) as u32)
3631 .collect(),
3632 GgmlType::I64 => bytes
3633 .chunks_exact(8)
3634 .map(|c| i64::from_le_bytes(c.try_into().unwrap()) as u32)
3635 .collect(),
3636 other => panic!("d2t must be I32/I64, got {other:?}"),
3637 }
3638 });
3639 if let Some(map) = &d2t {
3640 assert_eq!(
3641 map.len(),
3642 head.out_features(),
3643 "d2t len {} != draft head rows {}",
3644 map.len(),
3645 head.out_features()
3646 );
3647 let n_vocab = main_cfg.n_vocab as u64;
3648 assert!(
3649 map.iter().all(|&t| (t as u64) < n_vocab),
3650 "d2t contains token id >= model n_vocab {n_vocab}"
3651 );
3652 }
3653 let eh_proj = load_t(e, &src, &p("nextn.eh_proj.weight"))?;
3654 assert_eq!(
3657 eh_proj.in_features(),
3658 2 * main_cfg.n_embd as usize,
3659 "eh_proj in dim != 2*n_embd"
3660 );
3661 let geom = if student {
3662 let out_up = load_t(e, &src, &p("nextn.out_up.weight"))?;
3663 let d_inner = eh_proj.out_features();
3664 assert_eq!(
3665 out_up.out_features(),
3666 main_cfg.n_embd as usize,
3667 "out_up out dim != n_embd"
3668 );
3669 assert_eq!(
3670 out_up.in_features(),
3671 d_inner,
3672 "out_up in dim != eh_proj out dim (d_inner)"
3673 );
3674 assert!(
3675 dcfg.n_head >= 1 && dcfg.n_head_kv >= 1 && dcfg.n_head % dcfg.n_head_kv == 0,
3676 "student head counts malformed ({}/{})",
3677 dcfg.n_head,
3678 dcfg.n_head_kv
3679 );
3680 Some(DraftGeom {
3681 d_inner,
3682 n_head: dcfg.n_head as usize,
3683 n_head_kv: dcfg.n_head_kv as usize,
3684 out_up,
3685 })
3686 } else {
3687 None
3688 };
3689 let blk_prefix = format!("blk.{n}.");
3693 let head_src = head_name.strip_prefix(&blk_prefix).unwrap_or(&head_name);
3694 eprintln!(
3695 "[mtp-draft] external draft head: blk.{n}, source={}, head_vocab={}{}{}",
3696 head_src,
3697 head.out_features(),
3698 if d2t.is_some() {
3699 " (trimmed, d2t map)"
3700 } else {
3701 " (full)"
3702 },
3703 match &geom {
3704 Some(g) => format!(
3705 " (student d_inner={} heads={}/{})",
3706 g.d_inner, g.n_head, g.n_head_kv
3707 ),
3708 None => String::new(),
3709 }
3710 );
3711
3712 let mut resident = ResidentPlan::unsharded(e, &src, &dcfg);
3713 let mut step_runtimes = StepParallelRuntimeRegistry::default();
3714 Ok(MtpHead {
3715 enorm: load_t(e, &src, &p("nextn.enorm.weight"))?,
3716 hnorm: load_t(e, &src, &p("nextn.hnorm.weight"))?,
3717 eh_proj,
3718 attn_norm: load_t(e, &src, &p("attn_norm.weight"))?,
3719 post_attn_norm: load_opt(e, &src, &p("post_attention_norm.weight"))?
3720 .or(load_opt(e, &src, &p("ffn_norm.weight"))?)
3721 .expect("draft NextN block needs post_attention_norm or ffn_norm"),
3722 mixer: load_mixer_kind(
3723 e,
3724 &src,
3725 &dcfg,
3726 n,
3727 &draft_block.layer.attention,
3728 &mut step_runtimes,
3729 )?,
3730 ffn: load_ffn(
3731 e,
3732 &src,
3733 &dcfg,
3734 &draft_block.layer.mlp,
3735 n,
3736 None,
3737 &mut resident,
3738 &mut step_runtimes,
3739 )?,
3740 shared_head_norm: head_norm,
3741 shared_head_head: Some(head),
3742 d2t,
3743 d2t_from_target_head: false,
3744 geom,
3745 step35,
3746 })
3747 }
3748}
3749
3750pub struct GemmaAux {
3752 pub rope_freqs: Option<Vec<(usize, CudaSlice<f32>)>>,
3755 pub ones: Vec<(usize, CudaSlice<f32>)>,
3758 pub suppress_d: Option<(CudaSlice<i32>, usize)>,
3761 pub e4b: Option<Gemma4E4bModel>,
3763}
3764
3765impl GemmaAux {
3766 pub fn rope_freqs(&self, e: &Engine) -> Option<&CudaSlice<f32>> {
3767 self.rope_freqs.as_ref().map(|copies| {
3768 let dev = e.ctx().ordinal();
3769 &copies
3770 .iter()
3771 .find(|(d, _)| *d == dev)
3772 .unwrap_or_else(|| panic!("gemma4 rope_freqs has no local copy for device {dev}"))
3773 .1
3774 })
3775 }
3776
3777 pub fn ones(&self, e: &Engine) -> &CudaSlice<f32> {
3778 let dev = e.ctx().ordinal();
3779 &self
3780 .ones
3781 .iter()
3782 .find(|(d, _)| *d == dev)
3783 .unwrap_or_else(|| panic!("gemma4 ones has no local copy for device {dev}"))
3784 .1
3785 }
3786}
3787
3788pub struct Step35Aux {
3791 pub rope_freqs: Option<Vec<(usize, CudaSlice<f32>)>>,
3797}
3798
3799impl Step35Aux {
3800 pub fn rope_freqs(&self, e: &Engine) -> Option<&CudaSlice<f32>> {
3801 self.rope_freqs.as_ref().map(|copies| {
3802 let dev = e.ctx().ordinal();
3803 &copies
3804 .iter()
3805 .find(|(d, _)| *d == dev)
3806 .unwrap_or_else(|| panic!("step35 rope_freqs has no local copy for device {dev}"))
3807 .1
3808 })
3809 }
3810}
3811
3812pub struct HybridModel {
3813 pub cfg: ModelConfig,
3814 pub plan: memra_gguf::model_plan::ModelPlan,
3815 pub rewrite_qualifications: Option<memra_gguf::execution_manifest::RewriteQualifications>,
3816 pub embd: EmbedHost,
3817 pub output_norm: GpuTensor,
3818 pub output: GpuTensor,
3819 pub layers: Vec<HybridLayer>,
3820 pub mtp: Option<MtpHead>, pub mtp_extra: Vec<MtpHead>,
3824 pub dflash_trim: Option<DflashTrimHead>,
3829 pub frspec_src_sha16: Option<String>,
3833 pub embd_gpu: std::sync::OnceLock<cudarc::driver::CudaSlice<u8>>,
3836 pub gemma4_aux: Option<GemmaAux>,
3837 pub step35_aux: Option<Step35Aux>,
3839 pub prime_slabs: std::sync::Mutex<
3847 std::collections::HashMap<
3848 usize,
3849 std::sync::Arc<std::sync::Mutex<crate::hybrid_forward::PrimeSlabs>>,
3850 >,
3851 >,
3852 pub(crate) dspark_vgraphs: std::sync::Mutex<Option<crate::spec::DsparkVerifyGraphs>>,
3865 pub(crate) step_grouped_prefill: std::sync::Mutex<StepEpGroupedPrefill>,
3871 pub(crate) step35_token_graph:
3874 std::sync::Mutex<Option<crate::hybrid_forward::Step35TokenGraphState>>,
3875 pub hyper: Option<crate::hyper::HyperTopology>,
3880 pub hyper_head: Option<crate::hyper::HyperHead>,
3883 pub glm5_dflash: Option<crate::glm_spec::Glm5DflashDrafter>,
3890 pub(crate) draft_state_bytes: std::sync::atomic::AtomicUsize,
3899 pub test_extra_devices: Vec<usize>,
3901}
3902
3903impl HybridModel {
3904 pub fn install_rewrite_bundle(
3905 &mut self,
3906 bundle: &std::path::Path,
3907 ) -> Result<(), Box<dyn std::error::Error>> {
3908 self.rewrite_qualifications = Some(
3909 memra_gguf::execution_manifest::RewriteQualifications::load(bundle, &self.plan)
3910 .map_err(|error| format!("rewrite qualification: {error}"))?,
3911 );
3912 Ok(())
3913 }
3914
3915 pub fn rewrite_allowed(&self, surface: memra_gguf::execution_manifest::RewriteSurface) -> bool {
3916 self.rewrite_qualifications
3917 .as_ref()
3918 .is_none_or(|qualifications| qualifications.allows(surface))
3919 }
3920
3921 pub fn devices(&self) -> Vec<usize> {
3924 let mut devs = std::collections::BTreeSet::new();
3925 devs.insert(self.output_norm.ordinal());
3926 devs.insert(self.output.ordinal());
3927
3928 for layer in &self.layers {
3929 devs.insert(layer.attn_norm.ordinal());
3930 devs.insert(layer.post_attn_norm.ordinal());
3931
3932 match &layer.mixer {
3933 Mixer::Full(full) => {
3934 devs.insert(full.wq.ordinal());
3935 devs.insert(full.wk.ordinal());
3936 devs.insert(full.wv.ordinal());
3937 devs.insert(full.wo.ordinal());
3938 if let Some(tp) = &full.step_tp_qkv {
3939 devs.extend(&tp.devices);
3940 devs.extend(tp.runtime.devices());
3941 }
3942 }
3943 Mixer::Linear(linear) => {
3944 devs.insert(linear.wqkv.ordinal());
3945 devs.insert(linear.ssm_out.ordinal());
3946 }
3947 Mixer::Mla(mla) => {
3948 devs.insert(mla.wo.ordinal());
3949 }
3950 Mixer::Kda(kda) => {
3951 devs.insert(kda.wo.ordinal());
3952 }
3953 }
3954
3955 match &layer.ffn {
3956 Ffn::Dense {
3957 ffn_gate,
3958 ffn_up,
3959 ffn_down,
3960 } => {
3961 devs.insert(ffn_gate.ordinal());
3962 devs.insert(ffn_up.ordinal());
3963 devs.insert(ffn_down.ordinal());
3964 }
3965 Ffn::Moe(moe) => {
3966 devs.insert(moe.gate_inp.ordinal());
3967 if let Some(step_ep) = &moe.step_ep {
3968 devs.extend(&step_ep.devices);
3969 devs.extend(step_ep.runtime.devices());
3970 }
3971 if let Some(step_tp) = &moe.step_tp {
3972 devs.extend(step_tp.runtime.devices());
3973 }
3974 if let Some(glm5_ep) = &moe.glm5_ep {
3975 devs.extend(glm5_ep.rt.devices());
3976 }
3977 }
3978 }
3979
3980 if let Some(gemma4) = &layer.gemma4 {
3981 devs.insert(gemma4.ffn_norm.ordinal());
3982 devs.insert(gemma4.post_ffw_norm.ordinal());
3983 }
3984 }
3985
3986 if let Ok(guard) = self.step_grouped_prefill.lock()
3987 && let Some(state) = &guard.state
3988 {
3989 devs.extend(&state.devices);
3990 }
3991
3992 devs.extend(&self.test_extra_devices);
3993
3994 devs.into_iter().collect()
3995 }
3996
3997 pub fn is_multi_device(&self) -> bool {
4000 self.devices().len() > 1
4001 }
4002
4003 pub fn record_draft_state_bytes(&self, observed: usize) -> Option<usize> {
4008 use std::sync::atomic::Ordering;
4009 let prev = self
4010 .draft_state_bytes
4011 .fetch_max(observed, Ordering::Relaxed);
4012 (observed > prev).then_some(observed)
4013 }
4014
4015 pub fn draft_session_admission_bytes(&self) -> usize {
4021 self.draft_state_bytes
4022 .load(std::sync::atomic::Ordering::Relaxed)
4023 }
4024
4025 pub fn step_tp_unmaterialized_kv_bytes(
4031 &self,
4032 cache: Option<&crate::cache::Cache>,
4033 capacity: usize,
4034 ) -> Result<Vec<StepTpKvDeviceAdmission>, String> {
4035 if let Some(cache) = cache
4036 && cache.tp_kv.len() < self.layers.len()
4037 {
4038 return Err(format!(
4039 "Step TP admission cache has {} layers, model trunk has {}",
4040 cache.tp_kv.len(),
4041 self.layers.len()
4042 ));
4043 }
4044
4045 let mut by_device: HashMap<usize, usize> = HashMap::new();
4046 for (layer, weights) in self.layers.iter().enumerate() {
4047 let Mixer::Full(attention) = &weights.mixer else {
4048 continue;
4049 };
4050 let Some(tp) = attention
4051 .step_tp_qkv
4052 .as_ref()
4053 .filter(|tp| tp.attention.is_some())
4054 else {
4055 continue;
4056 };
4057 if cache.is_some_and(|cache| cache.tp_kv[layer].is_some()) {
4058 continue;
4059 }
4060 let geometry = self.cfg.full_attention_geometry_at(layer as u32);
4061 let shape = crate::cache::tp_kv_rank_allocation_shape(
4062 geometry.n_head_kv as usize * geometry.head_dim_k as usize,
4063 geometry.n_head_kv as usize * geometry.head_dim_v as usize,
4064 tp.devices.len(),
4065 )?;
4066 let physical_rows = geometry
4067 .window
4068 .map(|window| crate::cache::swa_ring_rows(window as usize, capacity))
4069 .unwrap_or(capacity);
4070 let bytes = shape.allocation_bytes(physical_rows);
4071 for &device in &tp.devices {
4072 let total = by_device.entry(device).or_default();
4073 *total = total.saturating_add(bytes);
4074 }
4075 }
4076
4077 let mut out: Vec<_> = by_device
4078 .into_iter()
4079 .map(|(device, bytes)| StepTpKvDeviceAdmission { device, bytes })
4080 .collect();
4081 out.sort_unstable_by_key(|charge| charge.device);
4082 Ok(out)
4083 }
4084
4085 pub fn step_tp_rank_engine(&self, device: usize) -> Option<&Engine> {
4087 self.layers.iter().find_map(|weights| {
4088 let Mixer::Full(attention) = &weights.mixer else {
4089 return None;
4090 };
4091 let tp = attention.step_tp_qkv.as_ref()?;
4092 let rank = tp
4093 .runtime
4094 .devices()
4095 .iter()
4096 .position(|&rank| rank == device)?;
4097 tp.runtime.rank_engine(rank)
4098 })
4099 }
4100
4101 pub(crate) fn step_tp_runtime_for_layer(
4102 &self,
4103 layer: usize,
4104 ) -> Option<&crate::tp::TpE4m3HostBounce> {
4105 let Mixer::Full(attention) = &self.layers.get(layer)?.mixer else {
4106 return None;
4107 };
4108 let tp = attention.step_tp_qkv.as_ref()?;
4109 tp.attention.as_ref()?;
4110 Some(tp.runtime.as_ref())
4111 }
4112
4113 pub fn decode_batch_program(&self) -> crate::plan_backend::DecodeBatchProgram {
4114 crate::plan_backend::decode_batch_program(&self.plan)
4115 }
4116
4117 pub fn uses_gemma_program(&self) -> bool {
4118 self.decode_batch_program() == crate::plan_backend::DecodeBatchProgram::Gemma
4119 }
4120
4121 pub fn uses_sliding_gated_moe_program(&self) -> bool {
4122 self.decode_batch_program() == crate::plan_backend::DecodeBatchProgram::SlidingGatedMoe
4123 }
4124
4125 pub fn has_plan_operation(&self, operation: memra_gguf::model_plan::OperationKind) -> bool {
4126 self.plan.trunk_operations().contains(&operation)
4127 }
4128
4129 pub fn load(e: &Engine, g: &GgufFile) -> Result<Self, Box<dyn std::error::Error>> {
4131 Self::load_from_source(e, &GgufSource(g))
4132 }
4133
4134 pub fn load_without_mtp(e: &Engine, g: &GgufFile) -> Result<Self, Box<dyn std::error::Error>> {
4137 Self::load_from_source_impl(e, &GgufSource(g), false)
4138 }
4139
4140 pub fn load_from_source(
4144 e: &Engine,
4145 src: &dyn TensorSource,
4146 ) -> Result<Self, Box<dyn std::error::Error>> {
4147 Self::load_from_source_impl(e, src, true)
4148 }
4149
4150 pub fn load_from_source_without_mtp(
4152 e: &Engine,
4153 src: &dyn TensorSource,
4154 ) -> Result<Self, Box<dyn std::error::Error>> {
4155 Self::load_from_source_impl(e, src, false)
4156 }
4157
4158 fn load_from_source_impl(
4159 e: &Engine,
4160 src: &dyn TensorSource,
4161 load_mtp: bool,
4162 ) -> Result<Self, Box<dyn std::error::Error>> {
4163 let cfg = src.try_config().map_err(std::io::Error::other)?;
4164 let plan = match memra_gguf::model_packs::for_config(&cfg) {
4165 Some(pack) => pack.compile_plan(&cfg)?,
4166 None => memra_gguf::model_plan::ModelPlan::compile(&cfg)?,
4167 };
4168 let auto_parallel = prepare_auto_parallel(src, &cfg, &plan)?;
4169 let batch_program = crate::plan_backend::decode_batch_program(&plan);
4170 let gemma_program = batch_program == crate::plan_backend::DecodeBatchProgram::Gemma;
4171 let sliding_gated_moe_program =
4172 batch_program == crate::plan_backend::DecodeBatchProgram::SlidingGatedMoe;
4173 if matches!(
4174 src.expert_activation_precision(),
4175 memra_gguf::source::ExpertActivationPrecision::Bf16
4176 ) {
4177 eprintln!(
4178 "[w4a16] artifact contract accepted: expert_weights=nvfp4 \
4179 expert_activations=bf16-rounded q8_expert_program=disabled"
4180 );
4181 }
4182 if sliding_gated_moe_program {
4187 crate::arm_step37_serving_defaults();
4188 }
4189 cfg.validate_attention_gate_layout()?;
4194 if cfg.sigmoid_router().is_some() {
4201 let host_oracle = std::env::var("MEMRA_SIG_ROUTER").as_deref() == Ok("0");
4202 match crate::sigrouter_contract::verify_host_expf() {
4203 Ok(()) => {}
4204 Err(e) if host_oracle => return Err(e.into()),
4205 Err(e) => eprintln!(
4206 "[sigrouter] WARN: host expf probe mismatch ({e}); device routing is \
4207 unaffected, but host-oracle replay/comparison cells are invalid on this host"
4208 ),
4209 }
4210 }
4211 if std::env::var("MEMRA_DRAFT").is_ok() && std::env::var("MEMRA_MMQ_SK").is_err() {
4220 let force = if cfg.n_embd >= 3500 { 0i8 } else { -1i8 };
4221 crate::MMQ_SK_FORCE.store(force, std::sync::atomic::Ordering::Relaxed);
4222 }
4223 crate::KV_FP8_FORCE.store(0, std::sync::atomic::Ordering::Relaxed);
4227
4228 let n_trunk = (cfg.n_layer - cfg.nextn_predict_layers) as usize;
4233 let mtp_skip_requested = load_mtp
4243 && match std::env::var("MEMRA_MTP_SKIP").ok().as_deref() {
4244 None | Some("") | Some("0") => false,
4245 Some("1") => true,
4246 Some(other) => {
4247 return Err(format!(
4248 "MEMRA_MTP_SKIP={other:?}: expected 1 (skip the embedded MTP block) or \
4249 0/unset (load it); refusing to guess"
4250 )
4251 .into());
4252 }
4253 };
4254 if mtp_skip_requested && std::env::var("MEMRA_MTP_DRAFT").is_ok_and(|p| !p.is_empty()) {
4255 return Err(
4256 "MEMRA_MTP_SKIP=1 together with MEMRA_MTP_DRAFT is contradictory: the skip \
4257 removes the MTP head to reclaim VRAM while MEMRA_MTP_DRAFT attaches an \
4258 external MTP head for MTP spec decode; unset one"
4259 .into(),
4260 );
4261 }
4262 if mtp_skip_requested && cfg.nextn_predict_layers > 0 {
4263 let prefixes: Vec<String> = (0..cfg.nextn_predict_layers)
4268 .map(|off| format!("blk.{}.", n_trunk as u32 + off))
4269 .collect();
4270 let skipped_bytes: Option<u64> = src.gguf().map(|g| {
4271 g.tensors
4272 .iter()
4273 .filter(|t| prefixes.iter().any(|p| t.name.starts_with(p.as_str())))
4274 .map(|t| t.n_bytes)
4275 .sum()
4276 });
4277 eprintln!(
4278 "[mtp-skip] MEMRA_MTP_SKIP=1: skipping {} embedded MTP/NextN block(s) \
4279 blk.{}..=blk.{} ({}); MTP spec decode is unavailable for this model \
4280 (dspark/DFlash2 drafting keeps its trimmed head via the MEMRA_FRSPEC_TRIM stub)",
4281 cfg.nextn_predict_layers,
4282 n_trunk,
4283 n_trunk as u32 + cfg.nextn_predict_layers - 1,
4284 match skipped_bytes {
4285 Some(b) => format!("~{} MiB of weights not loaded", b >> 20),
4286 None => "size unknown: non-GGUF source".to_string(),
4287 },
4288 );
4289 }
4290 let mtp_skip_trim_d2t: Option<(Vec<u32>, String)> = if mtp_skip_requested
4305 && cfg.nextn_predict_layers > 0
4306 && !crate::model::full_prec_enabled()
4307 {
4308 match std::env::var("MEMRA_FRSPEC_TRIM") {
4309 Ok(path) if !path.is_empty() => {
4310 let path = memra_gguf::hf::resolve_arg(&path)
4311 .map_err(|err| format!("MEMRA_FRSPEC_TRIM={path:?}: {err}"))?;
4312 let own_head_name = frspec_trim_own_head_name(n_trunk);
4313 if src.has(&own_head_name) {
4314 return Err(format!(
4315 "MEMRA_MTP_SKIP=1 with MEMRA_FRSPEC_TRIM: this artifact ships its \
4316 own MTP-block lm_head ({own_head_name}), so the trimmed draft rows \
4317 live in the block being skipped; gathering trunk rows instead is \
4318 the wrong-head bug (acceptance 0/248 receipt, \
4319 frspec_trim_own_head_name). Unset MEMRA_MTP_SKIP or \
4320 MEMRA_FRSPEC_TRIM"
4321 )
4322 .into());
4323 }
4324 if !src.has("output.weight") && !src.has("token_embd.weight") {
4325 return Err("MEMRA_MTP_SKIP=1 with MEMRA_FRSPEC_TRIM: model has no \
4326 output.weight (or tied token_embd.weight) to gather trimmed draft \
4327 rows from"
4328 .into());
4329 }
4330 let d2t = frspec_read_d2t(&path)?;
4331 if d2t.is_empty() {
4332 return Err(format!(
4333 "MEMRA_MTP_SKIP=1 with MEMRA_FRSPEC_TRIM={path}: the rank artifact \
4334 yields an EMPTY d2t list, so no stub draft head can be built; fix \
4335 the artifact or unset MEMRA_MTP_SKIP"
4336 )
4337 .into());
4338 }
4339 let sha16 = sha256_file_hex(std::path::Path::new(&path), 8)?;
4340 Some((d2t, sha16))
4341 }
4342 _ => None,
4343 }
4344 } else {
4345 None
4346 };
4347 let mut frspec_src_sha16: Option<String> =
4351 mtp_skip_trim_d2t.as_ref().map(|(_, s)| s.clone());
4352 if let Some(fence) = crate::pp::pp_cuts(n_trunk) {
4353 let pipeline = crate::plan_backend::PIPELINE
4354 .trunk_capabilities(&plan)
4355 .pipeline;
4356 let qualified_gemma_pp2 = gemma_program && fence.len() == 3;
4360 if !pipeline.supported && !qualified_gemma_pp2 {
4361 return Err(format!(
4362 "pipeline placement is unsupported for plan operations {:?}; blockers={:?}",
4363 plan.trunk_operations(),
4364 pipeline.blockers,
4365 )
4366 .into());
4367 }
4368 let illegal = illegal_pipeline_cuts(&fence, &plan.partition_boundaries);
4369 if !illegal.is_empty() {
4370 return Err(format!(
4371 "pipeline placement cuts {illegal:?} split outside ModelPlan legal boundaries {:?}",
4372 plan.partition_boundaries,
4373 )
4374 .into());
4375 }
4376 }
4377 crate::pp::init_model_transport(e, &cfg, n_trunk)?;
4378 let step_parallel =
4379 prepare_step_parallel_load(e, src, &cfg, n_trunk, auto_parallel.as_ref())?;
4380 let glm5_tp = if crate::glm5_tp::glm5_tp_armed() {
4384 use memra_gguf::model_plan::{AttentionPlan, MlpPlan};
4385 let moe = cfg.moe.as_ref().ok_or(
4386 "MEMRA_GLM5_TP requires a MoE model (glm5_next); this plan carries no MoE \
4387 metadata",
4388 )?;
4389 let mut layer_class = Vec::with_capacity(n_trunk);
4390 let mut layer_is_moe = Vec::with_capacity(n_trunk);
4391 let (mut kda_heads, mut kda_head_dim, mut mla_heads) = (0usize, 0usize, 0usize);
4392 for (il, lp) in plan.layers.iter().take(n_trunk).enumerate() {
4393 match &lp.attention {
4394 AttentionPlan::KimiDeltaNet(k) => {
4395 layer_class.push(crate::glm5_tp::Glm5LayerClass::Kda);
4396 kda_heads = k.num_heads as usize;
4397 kda_head_dim = k.head_dim as usize;
4398 }
4399 AttentionPlan::Mla(memra_gguf::model_plan::MlaAttentionPlan::LatentKv {
4400 query_heads,
4401 ..
4402 }) => {
4403 layer_class.push(crate::glm5_tp::Glm5LayerClass::Mla);
4404 mla_heads = *query_heads as usize;
4405 }
4406 other => {
4407 return Err(format!(
4408 "MEMRA_GLM5_TP requires a glm5_next-class plan (KDA/MLA mixers): \
4409 trunk layer {il} declares {other:?}"
4410 )
4411 .into());
4412 }
4413 }
4414 layer_is_moe.push(matches!(&lp.mlp, MlpPlan::Moe(_)));
4415 }
4416 let view = crate::glm5_tp::Glm5TpModelView {
4417 trunk_layers: n_trunk,
4418 layer_class,
4419 layer_is_moe,
4420 kda_heads,
4421 kda_head_dim,
4422 mla_heads,
4423 n_routed_experts: moe.expert_count as usize,
4424 top_k: moe.expert_used_count as usize,
4425 };
4426 crate::glm5_tp::prepare_glm5_tp_load(e, &view)?
4427 } else {
4428 let glm5_class = plan.layers.iter().take(n_trunk).any(|lp| {
4435 matches!(
4436 lp.attention,
4437 memra_gguf::model_plan::AttentionPlan::KimiDeltaNet(_)
4438 )
4439 });
4440 let ep_map_armed = crate::ep_map::ep_map_env()?;
4441 if let Some((flag, _)) = ep_map_armed
4442 && glm5_class
4443 {
4444 return Err(format!(
4445 "{flag} is set but MEMRA_GLM5_TP is off: the map cannot \
4446 engage, and a placement that silently reverts to the even split is \
4447 refused by name (unset one of the two)"
4448 )
4449 .into());
4450 }
4451 if glm5_class {
4459 for (armed, flag) in [crate::ep_diet_armed(), crate::ep_grouped_prime_armed()] {
4460 if armed {
4461 return Err(format!(
4462 "{flag}=1 is set but MEMRA_GLM5_TP is off: the EP dispatch \
4463 diet only exists inside the TP-2 EP walk and cannot engage \
4464 (unset one of the two)"
4465 )
4466 .into());
4467 }
4468 }
4469 }
4470 None
4471 };
4472 let embd = EmbedHost::from_source(src, "token_embd.weight");
4473 let e_head = crate::pp::layer_engine(e, n_trunk, n_trunk - 1)?;
4477 let output_norm = load_t(e_head, src, "output_norm.weight")?;
4478 let mut output = if src.has("output.weight") {
4480 load_t(e_head, src, "output.weight")?
4481 } else {
4482 load_t(e_head, src, "token_embd.weight")?
4483 };
4484 let mut resident = ResidentPlan::pp(e, src, &cfg, n_trunk)?;
4485 resident.exclude_distributed_expert_layers(
4486 step_parallel
4487 .ep_specs
4488 .iter()
4489 .map(|spec| spec.layer)
4490 .chain(step_parallel.tp_specs.iter().map(|spec| spec.layer)),
4491 );
4492 let mut step_runtimes = StepParallelRuntimeRegistry::with_config(step_parallel);
4493
4494 let gguf: Option<&GgufFile> = src.gguf();
4501 let mut spill: Option<crate::spill::SpillCtx> = if cfg
4504 .moe
4505 .as_ref()
4506 .is_some_and(|m| m.expert_count > 0)
4507 && crate::spill::disk_tier_enabled()
4508 && gguf.is_some()
4509 {
4510 let budget = crate::spill::MemBudget::probe(e)?;
4511 #[allow(clippy::unnecessary_unwrap)]
4512 let ctx = crate::spill::SpillCtx::open(gguf.unwrap(), &budget)?;
4514 eprintln!(
4515 "[spill] disk tier ON: free_vram={} MiB free_pinnable_ram={} MiB (MemAvailable*resolved_frac)",
4516 budget.free_vram >> 20,
4517 budget.free_pinnable_ram >> 20
4518 );
4519 Some(ctx)
4520 } else {
4521 None
4522 };
4523
4524 let hyper = crate::hyper::HyperTopology::from_plan(&plan)?;
4531 let hyper_head = match hyper.as_ref() {
4532 Some(topology) => {
4533 crate::hyper::HyperHead::load(e_head, src, topology, cfg.n_embd as usize)?
4534 }
4535 None => None,
4536 };
4537 let mut layers = Vec::with_capacity(n_trunk);
4538 for il in 0..n_trunk as u32 {
4539 let p = |s: &str| format!("blk.{il}.{s}");
4540 let layer_plan = plan
4541 .layers
4542 .get(il as usize)
4543 .ok_or_else(|| format!("ModelPlan has no trunk layer {il}"))?;
4544 let e = crate::pp::layer_engine(e, n_trunk, il as usize)?;
4548 layers.push(HybridLayer {
4550 attn_norm: load_t(e, src, &p("attn_norm.weight"))?,
4551 post_attn_norm: load_opt(e, src, &p("post_attention_norm.weight"))?
4552 .or(load_opt(e, src, &p("ffn_norm.weight"))?)
4553 .expect("need post_attention_norm or ffn_norm"),
4554 mixer: {
4555 let g4_shared = cfg.gemma4.as_ref().map(|g| g.shared_kv_layers).unwrap_or(0);
4559 let kv_from = n_trunk as u32 - g4_shared;
4560 if g4_shared > 0
4561 && il >= kv_from
4562 && !src.has(&format!("blk.{il}.attn_k.weight"))
4563 {
4564 let g4 = cfg.gemma4.as_ref().unwrap();
4565 let swa = g4.swa_pattern.get(il as usize).copied().unwrap_or(true);
4566 let tgt = kv_from - if swa { 2 } else { 1 };
4567 let tp = |s: &str| format!("blk.{tgt}.{s}");
4568 Mixer::Full(FullAttnLayer {
4569 wq: load_t(e, src, &p("attn_q.weight"))?,
4570 wk: load_t(e, src, &tp("attn_k.weight"))?,
4571 wv: load_t(e, src, &tp("attn_v.weight"))?,
4572 wo: load_t(e, src, &p("attn_output.weight"))?,
4573 q_norm: load_t(e, src, &p("attn_q_norm.weight"))?,
4574 k_norm: load_t(e, src, &tp("attn_k_norm.weight"))?,
4575 attn_gate: None, step_tp_qkv: None,
4577 })
4578 } else {
4579 load_mixer_kind(
4580 e,
4581 src,
4582 &cfg,
4583 il,
4584 &layer_plan.attention,
4585 &mut step_runtimes,
4586 )?
4587 }
4588 },
4589 ffn: load_ffn(
4590 e,
4591 src,
4592 &cfg,
4593 &layer_plan.mlp,
4594 il,
4595 spill.as_mut().map(|c| (gguf.unwrap(), c)),
4596 &mut resident,
4597 &mut step_runtimes,
4598 )?,
4599 gemma4: if gemma_program {
4600 let scalar = |n: &str| -> f32 {
4601 let t = src.find(&p(n)).unwrap_or_else(|| panic!("missing {n}"));
4602 memra_gguf::dequant::dequantize(t.ggml_type, &t.bytes, 1)[0]
4603 };
4604 let vecf = |n: &str| -> Vec<f32> {
4605 let t = src.find(&p(n)).unwrap_or_else(|| panic!("missing {n}"));
4606 memra_gguf::dequant::dequantize(
4607 t.ggml_type,
4608 &t.bytes,
4609 t.ne.iter().product::<u64>() as usize,
4610 )
4611 };
4612 let moe_bits = if src.find(&p("ffn_gate_inp.scale")).is_some() {
4613 Some(crate::hybrid::Gemma4MoeBits {
4614 post_ffw_norm_1: load_t(e, src, &p("post_ffw_norm_1.weight"))?,
4615 pre_ffw_norm_2: load_t(e, src, &p("pre_ffw_norm_2.weight"))?,
4616 post_ffw_norm_2: load_t(e, src, &p("post_ffw_norm_2.weight"))?,
4617 shared_gate: load_t(e, src, &p("ffn_gate.weight"))?,
4618 shared_up: load_t(e, src, &p("ffn_up.weight"))?,
4619 shared_down: load_t(e, src, &p("ffn_down.weight"))?,
4620 router_scale_pre: {
4621 let inv = 1.0 / (cfg.n_embd as f32).sqrt();
4622 let v: Vec<f32> =
4623 vecf("ffn_gate_inp.scale").iter().map(|x| x * inv).collect();
4624 e.htod(&v)?
4625 },
4626 per_expert_scale: vecf("ffn_down_exps.scale"),
4627 per_expert_scale_d: e.htod(&vecf("ffn_down_exps.scale"))?,
4628 })
4629 } else {
4630 None
4631 };
4632 let e4b = if src.has(&p("inp_gate.weight")) {
4634 let g4 = cfg.gemma4.as_ref().unwrap();
4635 let kv_from = n_trunk as u32 - g4.shared_kv_layers;
4636 let kv_share = if g4.shared_kv_layers > 0 && il >= kv_from {
4637 let swa = g4.swa_pattern.get(il as usize).copied().unwrap_or(true);
4638 Some(kv_from - if swa { 2 } else { 1 })
4639 } else {
4640 None
4641 };
4642 Some(crate::hybrid::Gemma4E4bLayer {
4643 inp_gate: load_t(e, src, &p("inp_gate.weight"))?,
4644 proj: load_t(e, src, &p("proj.weight"))?,
4645 post_norm: load_t(e, src, &p("post_norm.weight"))?,
4646 kv_share,
4647 qkv_cat: None, })
4649 } else {
4650 None
4651 };
4652 Some(Gemma4LayerBits {
4653 ffn_norm: load_t(e, src, &p("ffn_norm.weight"))?,
4654 post_ffw_norm: load_t(e, src, &p("post_ffw_norm.weight"))?,
4655 moe_bits,
4656 layer_scale: scalar("layer_output_scale.weight"),
4657 e4b,
4658 })
4659 } else {
4660 None
4661 },
4662 hyper: match hyper.as_ref() {
4663 Some(topology) => Some(crate::hyper::HyperLayer::load(
4664 e,
4665 src,
4666 il,
4667 topology,
4668 cfg.n_embd as usize,
4669 )?),
4670 None => None,
4671 },
4672 });
4673 if let Some(tp_plan) = &glm5_tp
4676 && tp_plan.layers.contains(&(il as usize))
4677 {
4678 let mut layer = layers.pop().expect("layer just pushed");
4679 layer.mixer = match layer.mixer {
4680 Mixer::Kda(la) => {
4681 Mixer::Kda(crate::glm5_tp::shard_kda_layer(e, &tp_plan.rt, la)?)
4682 }
4683 Mixer::Mla(la) => {
4684 Mixer::Mla(crate::glm5_tp::shard_mla_layer(e, &tp_plan.rt, la)?)
4685 }
4686 _ => {
4687 return Err(format!(
4688 "MEMRA_GLM5_TP selected layer {il}, whose loaded mixer is not \
4689 KDA/MLA — preflight and loader disagree (wiring bug)"
4690 )
4691 .into());
4692 }
4693 };
4694 if let Ffn::Moe(m) = &mut layer.ffn {
4695 let placement = match &tp_plan.ep_map {
4699 Some(map) => Some(
4700 map.layers
4701 .get(&(il as usize))
4702 .ok_or_else(|| {
4703 format!(
4704 "glm5-tp EP: preflight-validated map lost layer {il} \
4705 (wiring bug)"
4706 )
4707 })?
4708 .as_slice(),
4709 ),
4710 None => None,
4711 };
4712 crate::glm5_tp::arm_moe_ep(e, &tp_plan.rt, m, placement)?;
4713 }
4714 layers.push(layer);
4715 }
4716 }
4717
4718 let external_mtp_requested =
4722 load_mtp && std::env::var("MEMRA_MTP_DRAFT").is_ok_and(|path| !path.is_empty());
4723 let trim_mtp_requested = load_mtp
4724 && !crate::model::full_prec_enabled()
4725 && std::env::var("MEMRA_FRSPEC_TRIM").is_ok_and(|path| !path.is_empty());
4726 let _ = trim_mtp_requested;
4737 let glm5_mtp_requested =
4747 !cfg.arch.is_glm5_next() || std::env::var("MEMRA_GLM5_MTP").as_deref() == Ok("1");
4748 let embedded_head_count =
4751 if external_mtp_requested || !glm5_mtp_requested || mtp_skip_requested {
4752 0
4753 } else {
4754 cfg.nextn_predict_layers
4755 };
4756 if cfg.arch.is_glm5_next()
4757 && glm5_mtp_requested
4758 && !mtp_skip_requested
4759 && cfg.nextn_predict_layers > 0
4760 {
4761 eprintln!("[mtp-glm5] MEMRA_GLM5_MTP=1: loading the glm5_next NextN block");
4762 }
4763 let embedded_head_count = match std::env::var("MEMRA_MTP_HEADS")
4768 .ok()
4769 .and_then(|v| v.parse::<u32>().ok())
4770 .filter(|&n| n > 0)
4771 {
4772 Some(cap) if cap < embedded_head_count => {
4773 eprintln!(
4774 "[mtp-chain] MEMRA_MTP_HEADS={cap}: capping the embedded chain from \
4775 {embedded_head_count} heads (measurement knob)"
4776 );
4777 cap
4778 }
4779 _ => embedded_head_count,
4780 };
4781 let mut embedded_mtp = Vec::new();
4782 if load_mtp && embedded_head_count > 0 {
4783 for offset in 0..embedded_head_count {
4784 let n = n_trunk as u32 + offset;
4785 let e = crate::pp::layer_engine(e, n_trunk, n as usize)?;
4791 let p = |s: &str| format!("blk.{n}.{s}");
4792 let mtp_plan = plan
4793 .mtp_blocks
4794 .iter()
4795 .find(|block| block.layer.index == n)
4796 .ok_or_else(|| format!("ModelPlan has no embedded MTP block {n}"))?;
4797 if !src.has(&p("nextn.eh_proj.weight")) {
4798 if offset == 0 {
4799 break;
4800 }
4801 return Err(format!(
4802 "embedded MTP chain declares {} heads but blk.{n} has no \
4803 nextn.eh_proj.weight",
4804 cfg.nextn_predict_layers
4805 )
4806 .into());
4807 }
4808 embedded_mtp.push(MtpHead {
4809 enorm: load_t(e, src, &p("nextn.enorm.weight"))?,
4810 hnorm: load_t(e, src, &p("nextn.hnorm.weight"))?,
4811 eh_proj: load_t(e, src, &p("nextn.eh_proj.weight"))?,
4812 attn_norm: load_t(e, src, &p("attn_norm.weight"))?,
4813 post_attn_norm: load_opt(e, src, &p("post_attention_norm.weight"))?
4814 .or(load_opt(e, src, &p("ffn_norm.weight"))?)
4815 .expect("MTP block needs post_attention_norm or ffn_norm"),
4816 mixer: load_mixer_kind(
4817 e,
4818 src,
4819 &cfg,
4820 n,
4821 &mtp_plan.layer.attention,
4822 &mut step_runtimes,
4823 )?,
4824 ffn: load_ffn(
4825 e,
4826 src,
4827 &cfg,
4828 &mtp_plan.layer.mlp,
4829 n,
4830 spill.as_mut().map(|c| (gguf.unwrap(), c)),
4831 &mut resident,
4832 &mut step_runtimes,
4833 )?,
4834 shared_head_norm: load_opt(e, src, &p("nextn.shared_head_norm.weight"))?,
4835 shared_head_head: load_mtp_head_maybe_nvfp4(
4844 e,
4845 src,
4846 &p("nextn.shared_head_head.weight"),
4847 )?
4848 .or(load_opt(e, src, &p("nextn.shared_head.weight"))?),
4849 d2t: None,
4850 d2t_from_target_head: false,
4851 geom: None,
4852 step35: if sliding_gated_moe_program {
4853 Some(Step35MtpGeom::from_plan(&mtp_plan.layer)?)
4854 } else {
4855 None
4856 },
4857 });
4858 }
4859 }
4860 let mut embedded_mtp = embedded_mtp.into_iter();
4861 let mut mtp = embedded_mtp.next();
4862 let mut mtp_extra: Vec<MtpHead> = embedded_mtp.collect();
4863
4864 mtp = if load_mtp {
4868 match std::env::var("MEMRA_MTP_DRAFT") {
4869 Ok(path) if !path.is_empty() => {
4870 eprintln!("[mtp-draft] loading external MTP draft: {path}");
4871 let dg = GgufFile::open(&path)?;
4872 mtp_extra.clear();
4873 Some(MtpHead::load_draft(e, &dg, &cfg)?)
4874 }
4875 _ => mtp,
4876 }
4877 } else {
4878 None
4879 };
4880
4881 let trim_env = if load_mtp {
4892 std::env::var("MEMRA_FRSPEC_TRIM")
4893 } else {
4894 Err(std::env::VarError::NotPresent)
4895 };
4896 if crate::model::full_prec_enabled()
4897 && trim_env.as_deref().map(|p| !p.is_empty()).unwrap_or(false)
4898 {
4899 eprintln!(
4900 "[frspec-trim] DISABLED under MEMRA_FULL_PREC — using the natural full MTP head"
4901 );
4902 }
4903 mtp = match (
4904 if crate::model::full_prec_enabled() {
4905 Err(std::env::VarError::NotPresent)
4906 } else {
4907 trim_env
4908 },
4909 mtp,
4910 ) {
4911 (Ok(path), Some(mut head)) if !path.is_empty() => {
4912 let e = crate::pp::layer_engine(e, n_trunk, n_trunk)?;
4916 let path = memra_gguf::hf::resolve_arg(&path)
4920 .map_err(|err| format!("MEMRA_FRSPEC_TRIM={path:?}: {err}"))?;
4921 let d2t: Vec<u32> = frspec_read_d2t(&path)?;
4925 frspec_src_sha16 = Some(sha256_file_hex(std::path::Path::new(&path), 8)?);
4926 let own_head_name = frspec_trim_own_head_name(n_trunk);
4935 let own_head = src.find(&own_head_name);
4936 let from_own_head = own_head.is_some();
4937 let v = own_head
4938 .or_else(|| src.find("output.weight"))
4939 .or_else(|| src.find("token_embd.weight"))
4940 .expect("model has no output.weight for FR-Spec trim");
4941 frspec_validate_ranks(
4946 &d2t,
4947 v.ne[1] as usize,
4948 &format!(
4949 "MEMRA_FRSPEC_TRIM={path} (sha16={}) on {}",
4950 frspec_src_sha16.as_deref().unwrap_or("unknown"),
4951 if from_own_head {
4952 own_head_name.as_str()
4953 } else {
4954 "main output.weight"
4955 }
4956 ),
4957 )?;
4958 let (trimmed, nvfp4_sizes) = frspec_gather_trimmed_head(
4978 e,
4979 &v,
4980 &d2t,
4981 std::env::var("MEMRA_FRSPEC_TRIM_NVFP4").as_deref() == Ok("1"),
4982 match src.find("output.scale") {
4984 Some(sv) => f32::from_le_bytes(sv.bytes[..4].try_into().unwrap()),
4985 None => 1.0,
4986 },
4987 )?;
4988 match nvfp4_sizes {
4989 Some((nvfp4_bytes, gathered_bytes)) => eprintln!(
4990 "[frspec-trim] self-trimmed head: {} rows of {} re-quantized BF16 -> NVFP4 \
4991 ({} MiB, was {} MiB)",
4992 d2t.len(),
4993 if from_own_head {
4994 own_head_name.as_str()
4995 } else {
4996 "main output.weight"
4997 },
4998 nvfp4_bytes >> 20,
4999 gathered_bytes >> 20,
5000 ),
5001 None => eprintln!(
5002 "[frspec-trim] self-trimmed head: {} rows of {} ({:?})",
5003 d2t.len(),
5004 if from_own_head {
5005 own_head_name.as_str()
5006 } else {
5007 "main output.weight"
5008 },
5009 v.ggml_type
5010 ),
5011 }
5012 head.shared_head_head = Some(trimmed);
5013 head.d2t = Some(d2t);
5014 head.d2t_from_target_head = !from_own_head;
5017 Some(head)
5018 }
5019 (_, m) => m,
5020 };
5021 let mut dflash_trim: Option<DflashTrimHead> = match mtp_skip_trim_d2t {
5032 Some((d2t, src_sha16)) => {
5033 let v = src
5034 .find("output.weight")
5035 .or_else(|| src.find("token_embd.weight"))
5036 .ok_or("model has no output.weight for FR-Spec trim")?;
5037 frspec_validate_ranks(
5038 &d2t,
5039 v.ne[1] as usize,
5040 &format!("MEMRA_MTP_SKIP=1 with MEMRA_FRSPEC_TRIM (sha16={src_sha16})"),
5041 )?;
5042 let (head, nvfp4_sizes) = frspec_gather_trimmed_head(
5043 e,
5044 &v,
5045 &d2t,
5046 std::env::var("MEMRA_FRSPEC_TRIM_NVFP4").as_deref() == Ok("1"),
5047 match src.find("output.scale") {
5048 Some(sv) => f32::from_le_bytes(sv.bytes[..4].try_into().unwrap()),
5049 None => 1.0,
5050 },
5051 )?;
5052 eprintln!(
5053 "[mtp-skip] FR-Spec stub draft head built: {} rows of main output.weight \
5054 ({}); DFlash2 trim serves without the embedded MTP block",
5055 d2t.len(),
5056 match nvfp4_sizes {
5057 Some((nvfp4_bytes, gathered_bytes)) => format!(
5058 "re-quantized BF16 -> NVFP4, {} MiB, was {} MiB",
5059 nvfp4_bytes >> 20,
5060 gathered_bytes >> 20
5061 ),
5062 None => format!("{:?}", v.ggml_type),
5063 },
5064 );
5065 Some(DflashTrimHead {
5066 head,
5067 d2t,
5068 src_sha16,
5069 })
5070 }
5071 None => None,
5072 };
5073 if let Some(d2t) = mtp.as_ref().and_then(|head| head.d2t.clone()) {
5086 let want_nvfp4_env = std::env::var("MEMRA_FRSPEC_TRIM_NVFP4").as_deref() == Ok("1");
5087 let mut kept = 0usize;
5088 let e = crate::pp::layer_engine(e, n_trunk, n_trunk)?;
5091 for (i, head) in mtp_extra.iter_mut().enumerate() {
5092 let name = frspec_trim_own_head_name(n_trunk + 1 + i);
5093 let Some(v) = src.find(&name) else { break };
5094 let out_f = v.ne[1] as usize;
5095 let row_bytes = v.bytes.len() / out_f;
5096 if d2t.iter().any(|&t| (t as usize) >= out_f) {
5097 break;
5098 }
5099 let mut gathered = Vec::with_capacity(d2t.len() * row_bytes);
5100 for &t in &d2t {
5101 let off = t as usize * row_bytes;
5102 gathered.extend_from_slice(&v.bytes[off..off + row_bytes]);
5103 }
5104 let want_nvfp4 =
5105 want_nvfp4_env && matches!(v.ggml_type, GgmlType::BF16) && v.ne[0] % 64 == 0;
5106 let trimmed = if want_nvfp4 {
5107 let vals: Vec<f32> = gathered
5108 .chunks_exact(2)
5109 .map(|c| f32::from_bits((u16::from_le_bytes([c[0], c[1]]) as u32) << 16))
5110 .collect();
5111 let blocks = memra_gguf::nvfp4_repack::f32_to_nvfp4(&vals);
5112 GpuTensor::from_quant_bytes(
5113 e,
5114 &blocks,
5115 GgmlType::NVFP4,
5116 v.ne[0],
5117 d2t.len() as u64,
5118 1.0,
5119 )?
5120 } else {
5121 match v.ggml_type {
5122 GgmlType::BF16 => GpuTensor::FloatBf16 {
5123 data: e.htod_bytes(&gathered)?,
5124 ne: vec![v.ne[0], d2t.len() as u64],
5125 },
5126 GgmlType::F32 => GpuTensor::Float {
5127 data: e.htod(
5128 &gathered
5129 .chunks_exact(4)
5130 .map(|c| f32::from_le_bytes(c.try_into().unwrap()))
5131 .collect::<Vec<f32>>(),
5132 )?,
5133 ne: vec![v.ne[0], d2t.len() as u64],
5134 },
5135 _ => GpuTensor::from_quant_bytes(
5136 e,
5137 &gathered,
5138 v.ggml_type,
5139 v.ne[0],
5140 d2t.len() as u64,
5141 1.0,
5142 )?,
5143 }
5144 };
5145 head.shared_head_head = Some(trimmed);
5146 head.d2t = Some(d2t.clone());
5147 head.d2t_from_target_head = false;
5148 kept += 1;
5149 }
5150 let dropped = mtp_extra.len() - kept;
5151 mtp_extra.truncate(kept);
5152 eprintln!(
5153 "[frspec-trim] per-head trim: {kept} extra chain head(s) gathered from their own \
5154 blocks{}",
5155 if dropped > 0 {
5156 format!(" ({dropped} dropped: no own-head tensor)")
5157 } else {
5158 String::new()
5159 }
5160 );
5161 }
5162 if !mtp_extra.is_empty() {
5163 if plan.draft_source != memra_gguf::model_plan::DraftSourcePlan::Embedded
5164 || plan.mtp_blocks.len() != 1 + mtp_extra.len()
5165 || plan
5166 .mtp_blocks
5167 .iter()
5168 .any(|block| !matches!(block.layer.mlp, MlpPlan::Dense(_)))
5169 || mtp
5170 .iter()
5171 .chain(mtp_extra.iter())
5172 .any(|head| !matches!(head.ffn, Ffn::Dense { .. }))
5173 {
5174 return Err(
5175 "multi-head MTP requires embedded dense canonical blocks and matching loaded heads"
5176 .into(),
5177 );
5178 }
5179 eprintln!(
5180 "[mtp-draft] embedded chain: heads={} blocks={}..={} scratch=per-head",
5181 1 + mtp_extra.len(),
5182 n_trunk,
5183 n_trunk + mtp_extra.len()
5184 );
5185 }
5186
5187 let glm5_dflash = match std::env::var("MEMRA_GLM5_DFLASH") {
5203 Ok(spec) if !spec.is_empty() && cfg.arch.is_glm5_next() => {
5204 let dpath = memra_gguf::hf::resolve_arg(&spec)
5205 .map_err(|err| format!("MEMRA_GLM5_DFLASH={spec:?}: {err}"))?;
5206 let de = crate::pp::layer_engine(e, n_trunk, n_trunk)?;
5207 Some(crate::dflash::load_drafter(
5208 de,
5209 std::path::Path::new(&dpath),
5210 "MEMRA_GLM5_DFLASH",
5211 n_trunk,
5212 cfg.n_embd as usize,
5213 output.out_features(),
5214 )?)
5215 }
5216 _ => None,
5217 };
5218
5219 if cfg.arch.is_glm5_next()
5248 && glm5_dflash.is_some()
5249 && dflash_trim.is_none()
5250 && !crate::model::full_prec_enabled()
5251 && !mtp
5252 .as_ref()
5253 .is_some_and(|m| m.d2t_from_target_head && m.d2t.is_some())
5254 && let Ok(spec) = std::env::var("MEMRA_FRSPEC_TRIM")
5255 && !spec.is_empty()
5256 {
5257 let what = "MEMRA_FRSPEC_TRIM on the glm5 DFlash2 draft head";
5258 let path = memra_gguf::hf::resolve_arg(&spec)
5259 .map_err(|err| format!("{what}: {spec:?}: {err}"))?;
5260 let sha16 = sha256_file_hex(std::path::Path::new(&path), 8)?;
5261 let d2t: Vec<u32> = if path.ends_with(".txt") {
5262 let text = std::fs::read_to_string(&path)
5263 .map_err(|err| format!("{what}: {path}: {err}"))?;
5264 frspec_parse_ranks_txt_strict(&text, &format!("{what} ({path}, sha16={sha16})"))?
5265 } else {
5266 frspec_read_d2t(&path)?
5267 };
5268 let n_vocab = output.out_features();
5269 frspec_validate_ranks(&d2t, n_vocab, &format!("{what} ({path}, sha16={sha16})"))?;
5270 let v = src
5271 .find("output.weight")
5272 .or_else(|| src.find("token_embd.weight"))
5273 .ok_or_else(|| {
5274 format!("{what}: model has no output.weight (or tied token_embd.weight)")
5275 })?;
5276 if v.ne[1] as usize != n_vocab {
5277 return Err(format!(
5278 "{what}: source head rows {} != loaded head rows {n_vocab}",
5279 v.ne[1]
5280 )
5281 .into());
5282 }
5283 let de = crate::pp::layer_engine(e, n_trunk, n_trunk)?;
5285 let (head, nvfp4_sizes) = frspec_gather_trimmed_head(
5286 de,
5287 &v,
5288 &d2t,
5289 std::env::var("MEMRA_FRSPEC_TRIM_NVFP4").as_deref() == Ok("1"),
5290 match src.find("output.scale") {
5291 Some(sv) => f32::from_le_bytes(sv.bytes[..4].try_into().unwrap()),
5292 None => 1.0,
5293 },
5294 )?;
5295 eprintln!(
5296 "[frspec-trim] glm5 DFlash2 draft-head slab: {} rows of {} gathered from main \
5297 output.weight ({}) src={sha16} ({path})",
5298 d2t.len(),
5299 n_vocab,
5300 match nvfp4_sizes {
5301 Some((nvfp4_bytes, gathered_bytes)) => format!(
5302 "re-quantized BF16 -> NVFP4, {} MiB, was {} MiB",
5303 nvfp4_bytes >> 20,
5304 gathered_bytes >> 20
5305 ),
5306 None => format!(
5307 "{:?}, {} MiB",
5308 v.ggml_type,
5309 (d2t.len() * (v.bytes.len() / n_vocab)) >> 20
5310 ),
5311 },
5312 );
5313 frspec_src_sha16 = Some(sha16.clone());
5314 dflash_trim = Some(DflashTrimHead {
5315 head,
5316 d2t,
5317 src_sha16: sha16,
5318 });
5319 }
5320
5321 if cfg.arch.is_glm5_next() && crate::glm_spec::glm5_spec_on() {
5329 match (glm5_dflash.as_ref(), mtp.as_ref()) {
5330 (Some(dr), head) => {
5331 let trim_note = match (
5336 head.filter(|h| h.d2t_from_target_head)
5337 .and_then(|h| h.d2t.as_ref())
5338 .filter(|m| !m.is_empty()),
5339 dflash_trim.as_ref(),
5340 ) {
5341 (Some(map), _) => format!(
5342 "draft head RANK-TRIMMED n_ranks={} src={}",
5343 map.len(),
5344 frspec_src_sha16.as_deref().unwrap_or("unknown")
5345 ),
5346 (None, Some(slab)) => format!(
5347 "draft head RANK-TRIMMED n_ranks={} src={}",
5348 slab.d2t.len(),
5349 slab.src_sha16
5350 ),
5351 (None, None) => "draft head FULL target vocab".to_string(),
5352 };
5353 eprintln!(
5354 "[glm5-spec] serve route ARMED: draft source = dflash2 @ {}; {trim_note}; \
5355 native MTP head {}",
5356 dr.sha8,
5357 if head.is_some() {
5358 "ALSO loaded (idle for drafting — dflash2 wins by selection)"
5359 } else {
5360 "NOT loaded (the q38 pattern: a full MoE trunk layer of VRAM saved)"
5361 }
5362 );
5363 }
5364 (None, Some(head)) => {
5365 match head.d2t.as_ref() {
5366 Some(map) => eprintln!(
5367 "[glm5-spec] serve route ARMED: MTP head loaded; draft head TRIMMED \
5368 to {} rows (FR-Spec d2t engaged)",
5369 map.len()
5370 ),
5371 None => eprintln!(
5372 "[glm5-spec] serve route ARMED: MTP head loaded; draft head FULL \
5373 target vocab (no FR-Spec trim)"
5374 ),
5375 }
5376 eprintln!("[glm5-spec] draft source = native-mtp");
5377 }
5378 (None, None) => eprintln!(
5379 "[glm5-spec] MEMRA_GLM5_SPEC=1 but no MTP head loaded \
5380 (set MEMRA_GLM5_MTP=1 or MEMRA_GLM5_DFLASH=<drafter>) — route stays \
5381 fail-closed, plain serving"
5382 ),
5383 }
5384 }
5385
5386 if let Some(ctx) = spill.as_ref() {
5387 eprintln!(
5388 "[spill] experts placed: {} pinned (Tier 1), {} mmap'd from disk (Tier 2, {} MiB)",
5389 ctx.n_pinned,
5390 ctx.n_mmap,
5391 ctx.mmap_bytes >> 20
5392 );
5393 }
5394
5395 if cfg.n_head_kv > 0 && cfg.n_head / cfg.n_head_kv > 8 {
5409 crate::FA_V4_MAX_DEFAULT.store(0, std::sync::atomic::Ordering::Relaxed);
5410 eprintln!(
5411 "[fa] v4 decode family disabled: gqa {} > fa_v4_smem capacity 8 (v3 lane serves)",
5412 cfg.n_head / cfg.n_head_kv
5413 );
5414 }
5415
5416 if gemma_program {
5417 crate::FA_VEC_MIN_DEFAULT.store(1, std::sync::atomic::Ordering::Relaxed);
5419 let real_moe = plan
5422 .trunk_operations()
5423 .contains(&memra_gguf::model_plan::OperationKind::MoeMlp);
5424 crate::FA_SPW_DEFAULT.store(
5425 if real_moe { 32 } else { 64 },
5426 std::sync::atomic::Ordering::Relaxed,
5427 );
5428 crate::FA_SP512_DEFAULT.store(
5430 if real_moe { 16 } else { 32 },
5431 std::sync::atomic::Ordering::Relaxed,
5432 );
5433 crate::FUSED_MR1_DEFAULT.store(!real_moe, std::sync::atomic::Ordering::Relaxed);
5443 crate::RMS_BLOCK_DEFAULT.store(1024, std::sync::atomic::Ordering::Relaxed);
5445 crate::FA_SP_GEMMA.store(true, std::sync::atomic::Ordering::Relaxed);
5447 }
5451 let force_embd_gpu = gemma_program;
5454 let gemma4_aux = if gemma_program {
5455 let rope_freqs = match src.find("rope_freqs.weight") {
5456 Some(t) => {
5457 let host = memra_gguf::dequant::dequantize(
5458 t.ggml_type,
5459 &t.bytes,
5460 t.ne.iter().product::<u64>() as usize,
5461 );
5462 let mut copies = Vec::new();
5463 if let Some(fence) = crate::pp::pp_cuts(n_trunk) {
5464 #[allow(clippy::needless_range_loop)]
5465 for s in 0..fence.len() - 1 {
5467 let owner = crate::pp::layer_engine(e, n_trunk, fence[s])?;
5468 let dev = owner.ctx().ordinal();
5469 if copies.iter().all(|(d, _)| *d != dev) {
5470 copies.push((dev, owner.htod(&host)?));
5471 }
5472 }
5473 } else {
5474 copies.push((e.ctx().ordinal(), e.htod(&host)?));
5475 }
5476 Some(copies)
5477 }
5478 None => {
5486 let g4 = cfg.gemma4.as_ref().unwrap();
5487 let n = (g4.rope_dims_global / 2) as usize;
5488 let keep =
5489 ((n as f32) * g4.partial_rotary_global.clamp(0.0, 1.0)).round() as usize;
5490 let host: Vec<f32> = (0..n)
5491 .map(|i| if i < keep { 1.0 } else { 1.0e30 })
5492 .collect();
5493 eprintln!(
5494 "[gemma4] rope_freqs.weight synthesized ({n} factors, first {keep} \
5495 rotate; source ships none — native checkpoint)"
5496 );
5497 let mut copies = Vec::new();
5498 if let Some(fence) = crate::pp::pp_cuts(n_trunk) {
5499 #[allow(clippy::needless_range_loop)]
5500 for s in 0..fence.len() - 1 {
5502 let owner = crate::pp::layer_engine(e, n_trunk, fence[s])?;
5503 let dev = owner.ctx().ordinal();
5504 if copies.iter().all(|(d, _)| *d != dev) {
5505 copies.push((dev, owner.htod(&host)?));
5506 }
5507 }
5508 } else {
5509 copies.push((e.ctx().ordinal(), e.htod(&host)?));
5510 }
5511 Some(copies)
5512 }
5513 };
5514 let e4b = match src.find("per_layer_token_embd.weight") {
5516 Some(t) => {
5517 let n_epl = cfg
5518 .gemma4
5519 .as_ref()
5520 .map(|g| g.n_embd_per_layer as usize)
5521 .unwrap_or(0);
5522 let row = t.ne[0] as usize; let row_bytes = t.bytes.len() / (t.ne[1] as usize);
5524 eprintln!(
5525 "[gemma4-e4b] per-layer-embed model detected (n_epl={n_epl}, row {row}) — \
5526 first-light forward (eager decode + prime); dc/graph/spec unwired \
5527 (HANDOVER-E4B.md)"
5528 );
5529 Some(crate::hybrid::Gemma4E4bModel {
5530 tok_tbl_gpu: std::sync::OnceLock::new(),
5531 tok_embd_bytes: t.bytes.to_vec(),
5532 tok_embd_qt: match t.ggml_type {
5533 memra_gguf::GgmlType::Q6_K => crate::QT_Q6_K,
5534 memra_gguf::GgmlType::Q8_0 => crate::QT_Q8_0,
5535 other => panic!("e4b per-layer tok embd: unhandled dtype {other:?}"),
5536 },
5537 tok_embd_row_bytes: row_bytes,
5538 model_proj: load_t(e, src, "per_layer_model_proj.weight")?,
5539 proj_norm: load_t(e, src, "per_layer_proj_norm.weight")?,
5540 n_epl,
5541 })
5542 }
5543 None => None,
5544 };
5545 let suppress_d = {
5546 let sup = &cfg.gemma4.as_ref().unwrap().suppress_tokens;
5547 if sup.is_empty() {
5548 None
5549 } else {
5550 let ids: Vec<i32> = sup.iter().map(|&x| x as i32).collect();
5551 eprintln!(
5552 "[gemma4] suppress_tokens: {} ids masked at sampling",
5553 ids.len()
5554 );
5555 Some((e.htod_i32(&ids)?, ids.len()))
5556 }
5557 };
5558 let ones_host = [1.0f32; 512];
5559 let mut ones = Vec::new();
5560 if let Some(fence) = crate::pp::pp_cuts(n_trunk) {
5561 #[allow(clippy::needless_range_loop)]
5562 for s in 0..fence.len() - 1 {
5564 let owner = crate::pp::layer_engine(e, n_trunk, fence[s])?;
5565 let dev = owner.ctx().ordinal();
5566 if ones.iter().all(|(d, _)| *d != dev) {
5567 ones.push((dev, owner.htod(&ones_host)?));
5568 }
5569 }
5570 } else {
5571 ones.push((e.ctx().ordinal(), e.htod(&ones_host)?));
5572 }
5573 Some(GemmaAux {
5574 rope_freqs,
5575 ones,
5576 suppress_d,
5577 e4b,
5578 })
5579 } else {
5580 None
5581 };
5582 let step35_aux = if sliding_gated_moe_program {
5586 let rope_freqs = match src.find("rope_freqs.weight") {
5587 Some(t) => {
5588 let host = memra_gguf::dequant::dequantize(
5589 t.ggml_type,
5590 &t.bytes,
5591 t.ne.iter().product::<u64>() as usize,
5592 );
5593 let mut copies = Vec::new();
5594 if let Some(fence) = crate::pp::pp_cuts(n_trunk) {
5595 #[allow(clippy::needless_range_loop)]
5596 for s in 0..fence.len() - 1 {
5598 let owner = crate::pp::layer_engine(e, n_trunk, fence[s])?;
5599 let dev = owner.ctx().ordinal();
5600 if copies.iter().all(|(d, _)| *d != dev) {
5601 copies.push((dev, owner.htod(&host)?));
5602 }
5603 }
5604 } else {
5605 copies.push((e.ctx().ordinal(), e.htod(&host)?));
5606 }
5607 Some(copies)
5608 }
5609 None => None,
5610 };
5611 Some(Step35Aux { rope_freqs })
5612 } else {
5613 None
5614 };
5615 let mut layers = layers;
5616 {
5623 let q8rp_on = match std::env::var("MEMRA_Q8RP").as_deref() {
5624 Ok("0") => false,
5625 Ok(_) => true,
5626 Err(_) => {
5634 cfg!(memra_hopper_mma) || {
5635 let q8b = |w: &crate::model::GpuTensor| -> usize {
5636 match w {
5637 crate::model::GpuTensor::Quant {
5638 bytes,
5639 qtype,
5640 row_bytes,
5641 ne,
5642 rp4: None,
5643 ..
5644 } if *qtype == crate::QT_Q8_0
5645 && ne.len() == 2
5646 && (ne[0] as usize).is_multiple_of(32)
5647 && *row_bytes == (ne[0] as usize / 32) * 34 =>
5648 {
5649 bytes.len()
5650 }
5651 _ => 0,
5652 }
5653 };
5654 let mut need = q8b(&output);
5655 for layer in layers.iter() {
5656 match &layer.mixer {
5657 Mixer::Full(fa) => {
5658 for w in [&fa.wq, &fa.wk, &fa.wv, &fa.wo] {
5659 need += q8b(w);
5660 }
5661 }
5662 Mixer::Linear(la) => {
5663 for w in [
5664 &la.wqkv,
5665 &la.wqkv_gate,
5666 &la.ssm_beta,
5667 &la.ssm_alpha,
5668 &la.ssm_out,
5669 ] {
5670 need += q8b(w);
5671 }
5672 }
5673 Mixer::Mla(_) => {}
5674 Mixer::Kda(_) => {} }
5676 if let Ffn::Dense {
5677 ffn_gate,
5678 ffn_up,
5679 ffn_down,
5680 } = &layer.ffn
5681 {
5682 for w in [ffn_gate, ffn_up, ffn_down] {
5683 need += q8b(w);
5684 }
5685 }
5686 }
5687 need > 0
5688 && e.ctx()
5689 .mem_get_info()
5690 .map(|(free, _)| free >= need + (8usize << 30))
5691 .unwrap_or(false)
5692 }
5693 }
5694 };
5695 let kqrp_on = crate::Engine::kqrp_enabled() || {
5705 std::env::var("MEMRA_KQRP").is_err() && {
5706 let kqb = |w: &crate::model::GpuTensor| -> usize {
5707 match w {
5708 crate::model::GpuTensor::Quant {
5709 bytes,
5710 qtype,
5711 row_bytes,
5712 ne,
5713 rp4: None,
5714 ..
5715 } if ne.len() == 2 && (ne[0] as usize).is_multiple_of(256) => {
5716 let sb = if *qtype == crate::QT_Q4_K {
5717 144
5718 } else if *qtype == crate::QT_Q6_K {
5719 210
5720 } else {
5721 return 0;
5722 };
5723 if *row_bytes == (ne[0] as usize / 256) * sb {
5724 bytes.len()
5725 } else {
5726 0
5727 }
5728 }
5729 _ => 0,
5730 }
5731 };
5732 let mut need = kqb(&output);
5733 for layer in layers.iter() {
5734 if let Mixer::Full(fa) = &layer.mixer {
5735 for w in [&fa.wq, &fa.wk, &fa.wv, &fa.wo] {
5736 need += kqb(w);
5737 }
5738 }
5739 if let Ffn::Dense {
5740 ffn_gate,
5741 ffn_up,
5742 ffn_down,
5743 } = &layer.ffn
5744 {
5745 for w in [ffn_gate, ffn_up, ffn_down] {
5746 need += kqb(w);
5747 }
5748 }
5749 }
5750 need > 0
5751 && e.ctx()
5752 .mem_get_info()
5753 .map(|(free, _)| free >= need + (8usize << 30))
5754 .unwrap_or(false)
5755 }
5756 };
5757 if q8rp_on || kqrp_on {
5758 let f16_model_ok = gemma_program
5765 || plan
5766 .trunk_operations()
5767 .contains(&memra_gguf::model_plan::OperationKind::MoeMlp)
5768 || std::env::var("MEMRA_PP_F16").as_deref() == Ok("1");
5769 let mut nmir = 0usize;
5770 let mut mir = |e_ref: &crate::Engine,
5774 w: &mut crate::model::GpuTensor|
5775 -> Result<(), Box<dyn std::error::Error>> {
5776 let before = matches!(w, crate::model::GpuTensor::Quant { rp4: Some(_), .. });
5777 if q8rp_on {
5778 e_ref.build_q8_rp4(w)?;
5779 }
5780 if kqrp_on {
5781 e_ref.build_q4k_rp4(w)?;
5782 e_ref.build_q6k_rp4(w)?;
5783 }
5784 let q6k = matches!(w, crate::model::GpuTensor::Quant { qtype, .. }
5789 if *qtype == crate::QT_Q6_K);
5790 if q8rp_on && crate::f16_ffi::pp_f16_enabled() && (f16_model_ok || q6k) {
5791 e_ref.build_q8_f16(w)?;
5792 }
5793 if !before && matches!(w, crate::model::GpuTensor::Quant { rp4: Some(_), .. }) {
5794 nmir += 1;
5795 }
5796 Ok(())
5797 };
5798 for (il, layer) in layers.iter_mut().enumerate() {
5799 let el = crate::pp::layer_engine(e, n_trunk, il)?;
5800 match &mut layer.mixer {
5801 Mixer::Full(fa) => {
5802 for w in [&mut fa.wq, &mut fa.wk, &mut fa.wv, &mut fa.wo] {
5803 mir(el, w)?;
5804 }
5805 }
5806 Mixer::Linear(la) => {
5807 for w in [
5808 &mut la.wqkv,
5809 &mut la.wqkv_gate,
5810 &mut la.ssm_beta,
5811 &mut la.ssm_alpha,
5812 &mut la.ssm_out,
5813 ] {
5814 mir(el, w)?;
5815 }
5816 }
5817 Mixer::Mla(_) => {}
5820 Mixer::Kda(_) => {} }
5822 if let Ffn::Dense {
5823 ffn_gate,
5824 ffn_up,
5825 ffn_down,
5826 } = &mut layer.ffn
5827 {
5828 for w in [ffn_gate, ffn_up, ffn_down] {
5829 mir(el, w)?;
5830 }
5831 }
5832 }
5833 mir(e_head, &mut output)?;
5834 if nmir > 0 {
5835 eprintln!("[q8rp] split-plane decode mirrors built: {nmir} tensors");
5836 }
5837 if q8rp_on && crate::f16_ffi::pp_f16_enabled() {
5852 for (want, tag) in [(crate::QT_Q4_K, "q4kf16"), (crate::QT_Q5_K, "q5kf16")] {
5853 let (mut n4, mut b4) = (0usize, 0usize);
5854 let mut mirk =
5855 |e_ref: &crate::Engine,
5856 w: &mut crate::model::GpuTensor|
5857 -> Result<(), Box<dyn std::error::Error>> {
5858 if matches!(w, crate::model::GpuTensor::Quant { qtype, f16: None, .. }
5859 if *qtype == want)
5860 {
5861 e_ref.build_q8_f16(w)?;
5862 if let crate::model::GpuTensor::Quant { f16: Some(m), .. } = w {
5863 n4 += 1;
5864 b4 += m.len();
5865 }
5866 }
5867 Ok(())
5868 };
5869 for (il, layer) in layers.iter_mut().enumerate() {
5870 let el = crate::pp::layer_engine(e, n_trunk, il)?;
5871 match &mut layer.mixer {
5872 Mixer::Full(fa) => {
5873 for w in [&mut fa.wq, &mut fa.wk, &mut fa.wv, &mut fa.wo] {
5874 mirk(el, w)?;
5875 }
5876 }
5877 Mixer::Linear(la) => {
5878 for w in [
5879 &mut la.wqkv,
5880 &mut la.wqkv_gate,
5881 &mut la.ssm_beta,
5882 &mut la.ssm_alpha,
5883 &mut la.ssm_out,
5884 ] {
5885 mirk(el, w)?;
5886 }
5887 }
5888 Mixer::Mla(_) => {} Mixer::Kda(_) => {} }
5891 if let Ffn::Dense {
5892 ffn_gate,
5893 ffn_up,
5894 ffn_down,
5895 } = &mut layer.ffn
5896 {
5897 for w in [ffn_gate, ffn_up, ffn_down] {
5898 mirk(el, w)?;
5899 }
5900 }
5901 }
5902 mirk(e_head, &mut output)?;
5903 if n4 > 0 {
5904 eprintln!(
5905 "[{tag}] prefill fp16 mirrors built: {n4} tensors \
5906 ({} MB)",
5907 b4 >> 20
5908 );
5909 }
5910 }
5911 }
5912 }
5913 }
5914 if gemma_program && crate::Engine::q4rp_enabled() {
5921 let mut nmir = 0usize;
5922 for (il, layer) in layers.iter_mut().enumerate() {
5923 let e = crate::pp::layer_engine(e, n_trunk, il)?;
5925 let is_moe26 = layer.gemma4.as_ref().is_some_and(|g| g.moe_bits.is_some());
5934 let is_e4b = layer.gemma4.as_ref().is_some_and(|g| g.e4b.is_some());
5935 if !(is_moe26 || is_e4b) {
5936 continue;
5937 }
5938 if let Mixer::Full(fa) = &mut layer.mixer {
5939 for w in [&mut fa.wq, &mut fa.wk, &mut fa.wv, &mut fa.wo] {
5940 e.build_q4_rp4(w)?;
5941 nmir += 1;
5942 }
5943 }
5944 if is_e4b {
5945 let own_kv = layer
5947 .gemma4
5948 .as_ref()
5949 .unwrap()
5950 .e4b
5951 .as_ref()
5952 .is_some_and(|e4| e4.kv_share.is_none());
5953 if own_kv
5954 && let Mixer::Full(fa) = &layer.mixer
5955 && let Some(mut cat) = e.build_q4_out_concat3(&fa.wq, &fa.wk, &fa.wv)?
5956 {
5957 e.build_q4_rp4(&mut cat)?;
5958 nmir += 1;
5959 layer.gemma4.as_mut().unwrap().e4b.as_mut().unwrap().qkv_cat = Some(cat);
5960 }
5961 if let Ffn::Dense {
5962 ffn_gate,
5963 ffn_up,
5964 ffn_down,
5965 } = &mut layer.ffn
5966 {
5967 for w in [ffn_gate, ffn_up, ffn_down] {
5968 e.build_q4_rp4(w)?;
5969 nmir += 1;
5970 }
5971 }
5972 let e4 = layer.gemma4.as_mut().unwrap().e4b.as_mut().unwrap();
5973 for w in [&mut e4.inp_gate, &mut e4.proj] {
5974 e.build_q4_rp4(w)?;
5975 nmir += 1;
5976 }
5977 }
5978 if let Some(mb) = layer.gemma4.as_mut().unwrap().moe_bits.as_mut() {
5979 for w in [&mut mb.shared_gate, &mut mb.shared_up, &mut mb.shared_down] {
5980 e.build_q4_rp4(w)?;
5981 nmir += 1;
5982 }
5983 }
5984 }
5985 if nmir > 0 {
5986 eprintln!("[q4rp] split-plane decode mirrors built: {nmir} trunk tensors");
5987 }
5988 let fast_on = std::env::var("MEMRA_FAST").as_deref() != Ok("0");
5995 if fast_on {
5996 let mut nswap = 0usize;
5997 let mut nf16 = 0usize;
5998 let q4f16_model_ok = matches!(cfg.n_embd, 3840 | 5376); if let Ok(v) = std::env::var("MEMRA_Q4F16")
6017 && v != "0"
6018 && v != "1"
6019 {
6020 return Err(format!(
6021 "MEMRA_Q4F16={v} is not 0 or 1 — this env selects the prefill \
6022 ARITHMETIC (fp16 mirrors vs int8 MMQ) and must never be guessed"
6023 )
6024 .into());
6025 }
6026 let f16_need = {
6027 let f16b = |w: &crate::model::GpuTensor| -> usize {
6028 match w {
6029 crate::model::GpuTensor::Quant {
6030 qtype,
6031 ne,
6032 f16: None,
6033 ..
6034 } if ne.len() == 2
6035 && matches!(
6036 *qtype,
6037 crate::QT_Q8_0
6038 | crate::QT_Q4_0
6039 | crate::QT_Q6_K
6040 | crate::QT_Q4_K
6041 | crate::QT_Q5_K
6042 ) =>
6043 {
6044 (ne[0] as usize) * (ne[1] as usize) * 2
6045 }
6046 _ => 0,
6047 }
6048 };
6049 let mut need = 0usize;
6050 for layer in layers.iter() {
6051 if layer.gemma4.as_ref().is_none_or(|g| g.moe_bits.is_some()) {
6052 continue;
6053 }
6054 if let Mixer::Full(fa) = &layer.mixer {
6055 for w in [&fa.wq, &fa.wk, &fa.wv, &fa.wo] {
6056 need += f16b(w);
6057 }
6058 }
6059 if let Ffn::Dense {
6060 ffn_gate,
6061 ffn_up,
6062 ffn_down,
6063 } = &layer.ffn
6064 {
6065 for w in [ffn_gate, ffn_up, ffn_down] {
6066 need += f16b(w);
6067 }
6068 }
6069 }
6070 need
6071 };
6072 let f16_free = e.ctx().mem_get_info().map(|(free, _)| free).unwrap_or(0);
6073 let f16_auto = q4f16_model_ok
6074 && std::env::var("MEMRA_Q4F16").is_err()
6075 && crate::f16_ffi::pp_f16_capacity_ok(f16_free, f16_need);
6076 let (f16_on, f16_why) = match std::env::var("MEMRA_Q4F16").as_deref() {
6082 Ok("1") => (true, "env MEMRA_Q4F16=1"),
6083 Ok("0") => (false, "env MEMRA_Q4F16=0"),
6084 _ if crate::f16_ffi::pp_f16_enabled() && q4f16_model_ok => {
6085 (true, "env MEMRA_PP_F16")
6086 }
6087 _ if f16_auto => (true, "capacity-keyed auto (UNPINNED)"),
6088 _ if !q4f16_model_ok => (false, "model geometry not eligible"),
6089 _ => (false, "capacity-keyed auto REFUSED (UNPINNED)"),
6090 };
6091 eprintln!(
6098 "[q4f16] prefill program = {} (reason: {}); free {} MiB, mirror mass {} MiB, \
6099 capacity threshold {} MiB (mass + 8192 headroom) — SELECTS PREFILL ARITHMETIC",
6100 if f16_on {
6101 "FP16 MIRRORS"
6102 } else {
6103 "INT8 MMQ (no f16 mirrors)"
6104 },
6105 f16_why,
6106 f16_free >> 20,
6107 f16_need >> 20,
6108 (f16_need + (8usize << 30)) >> 20,
6109 );
6110 for (il, layer) in layers.iter_mut().enumerate() {
6111 let e = crate::pp::layer_engine(e, n_trunk, il)?;
6113 let dense_gemma = layer.gemma4.as_ref().is_some_and(|g| g.moe_bits.is_none());
6114 if !dense_gemma {
6115 continue;
6116 }
6117 if let Mixer::Full(fa) = &mut layer.mixer {
6118 for w in [&mut fa.wq, &mut fa.wk, &mut fa.wv, &mut fa.wo] {
6119 if f16_on {
6120 e.build_q8_f16(w)?;
6121 if matches!(w, crate::model::GpuTensor::Quant { f16: Some(_), .. })
6122 {
6123 nf16 += 1;
6124 }
6125 }
6126 if e.build_q4_rp_swap(w)? {
6127 nswap += 1;
6128 }
6129 }
6130 }
6131 if let Ffn::Dense {
6132 ffn_gate,
6133 ffn_up,
6134 ffn_down,
6135 } = &mut layer.ffn
6136 {
6137 for w in [ffn_gate, ffn_up, ffn_down] {
6138 if f16_on {
6139 e.build_q8_f16(w)?;
6140 if matches!(w, crate::model::GpuTensor::Quant { f16: Some(_), .. })
6141 {
6142 nf16 += 1;
6143 }
6144 }
6145 if e.build_q4_rp_swap(w)? {
6146 nswap += 1;
6147 }
6148 }
6149 }
6150 }
6151 if nswap > 0 {
6152 eprintln!("[q4rp] split-plane IN-PLACE swap: {nswap} dense trunk tensors");
6153 }
6154 if nf16 > 0 {
6155 eprintln!("[q4f16] prefill fp16 mirrors built: {nf16} dense trunk tensors");
6156 }
6157 }
6158 }
6159 let model = HybridModel {
6160 cfg,
6161 plan,
6162 rewrite_qualifications: None,
6163 embd,
6164 output_norm,
6165 output,
6166 layers,
6167 mtp,
6168 mtp_extra,
6169 dflash_trim,
6170 frspec_src_sha16,
6171 embd_gpu: std::sync::OnceLock::new(),
6172 gemma4_aux,
6173 step35_aux,
6174 prime_slabs: std::sync::Mutex::new(std::collections::HashMap::new()),
6175 dspark_vgraphs: std::sync::Mutex::new(None),
6176 step_grouped_prefill: std::sync::Mutex::new(StepEpGroupedPrefill::default()),
6177 step35_token_graph: std::sync::Mutex::new(None),
6178 hyper,
6179 hyper_head,
6180 glm5_dflash,
6181 draft_state_bytes: std::sync::atomic::AtomicUsize::new(0),
6182 test_extra_devices: Vec::new(),
6183 };
6184 e.configure_moe_cache_layout(model.moe_cache_block_sizes());
6185 if force_embd_gpu {
6186 let _ = model
6187 .embd_gpu
6188 .get_or_init(|| e.upload_u8(&model.embd.raw).expect("embed table upload"));
6189 }
6190 crate::pp::sync_stages_after_load(e, n_trunk)?;
6196 Ok(model)
6197 }
6198
6199 pub fn ensure_embed_resident(&self, e: &Engine) -> Result<(), Box<dyn std::error::Error>> {
6209 if std::env::var("MEMRA_EMBED_DEV").as_deref() == Ok("0") {
6210 return Ok(());
6211 }
6212 if self.embd_gpu.get().is_none() {
6213 let buf = e.upload_u8(&self.embd.raw)?;
6214 let _ = self.embd_gpu.set(buf); }
6216 Ok(())
6217 }
6218
6219 pub fn embed(
6220 &self,
6221 e: &Engine,
6222 tokens: &[u32],
6223 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6224 let n_embd = self.cfg.n_embd as usize;
6225 if std::env::var("MEMRA_EMBED_DEV").as_deref() != Ok("0") {
6231 let tbl = self
6232 .embd_gpu
6233 .get_or_init(|| e.upload_u8(&self.embd.raw).expect("embed table upload"));
6234 let tok_d = e.htod_u32_v(tokens)?;
6235 let (qt, rb) = self.embd.qt_and_row_bytes(n_embd);
6236 return e.embed_gather_device_td(tbl, &tok_d, tokens.len(), n_embd, qt, rb);
6237 }
6238 let x = self.embd.try_gather(n_embd, tokens)?;
6239 e.htod(&x)
6240 }
6241}
6242
6243fn illegal_pipeline_cuts(fence: &[usize], legal_boundaries: &[usize]) -> Vec<usize> {
6244 fence
6245 .get(1..fence.len().saturating_sub(1))
6246 .unwrap_or_default()
6247 .iter()
6248 .copied()
6249 .filter(|cut| !legal_boundaries.contains(cut))
6250 .collect()
6251}
6252
6253#[cfg(test)]
6254mod pipeline_cut_tests {
6255 use super::illegal_pipeline_cuts;
6256
6257 #[test]
6258 fn manual_pipeline_cuts_cannot_bypass_model_plan_boundaries() {
6259 assert!(illegal_pipeline_cuts(&[0, 8, 16, 24], &[8, 16]).is_empty());
6260 assert_eq!(illegal_pipeline_cuts(&[0, 7, 16, 24], &[8, 16]), vec![7]);
6261 assert_eq!(
6262 illegal_pipeline_cuts(&[0, 7, 15, 24], &[8, 16]),
6263 vec![7, 15]
6264 );
6265 }
6266}
6267
6268#[cfg(test)]
6269mod auto_parallel_policy_tests {
6270 use super::{
6271 parse_auto_parallel_tp_attention, parse_auto_parallel_tp_attention_ranks,
6272 parse_auto_w4a16_bf16_mmv,
6273 };
6274
6275 #[test]
6276 fn automatic_w4a16_bf16_residency_defaults_on_with_explicit_rollback() {
6277 assert!(parse_auto_w4a16_bf16_mmv(None).unwrap());
6278 assert!(!parse_auto_w4a16_bf16_mmv(Some("0")).unwrap());
6279 assert!(parse_auto_w4a16_bf16_mmv(Some("1")).unwrap());
6280 assert!(parse_auto_w4a16_bf16_mmv(Some("true")).is_err());
6281 assert!(parse_auto_w4a16_bf16_mmv(Some("")).is_err());
6282 }
6283
6284 #[test]
6285 fn automatic_tp_attention_is_strict_and_defaults_off() {
6286 assert!(!parse_auto_parallel_tp_attention(None).unwrap());
6287 assert!(!parse_auto_parallel_tp_attention(Some("")).unwrap());
6288 assert!(!parse_auto_parallel_tp_attention(Some("0")).unwrap());
6289 assert!(parse_auto_parallel_tp_attention(Some("1")).unwrap());
6290 assert!(parse_auto_parallel_tp_attention(Some("true")).is_err());
6291 assert!(parse_auto_parallel_tp_attention(Some("2")).is_err());
6292 }
6293
6294 #[test]
6295 fn automatic_tp_attention_rank_count_is_explicit_and_bounded() {
6296 assert_eq!(parse_auto_parallel_tp_attention_ranks(None).unwrap(), None);
6297 assert_eq!(
6298 parse_auto_parallel_tp_attention_ranks(Some("2")).unwrap(),
6299 Some(2)
6300 );
6301 assert_eq!(
6302 parse_auto_parallel_tp_attention_ranks(Some("3")).unwrap(),
6303 Some(3)
6304 );
6305 assert_eq!(
6306 parse_auto_parallel_tp_attention_ranks(Some("4")).unwrap(),
6307 Some(4)
6308 );
6309 for bad in ["", "0", "1", "5", "all"] {
6310 assert!(parse_auto_parallel_tp_attention_ranks(Some(bad)).is_err());
6311 }
6312 }
6313}
6314
6315#[cfg(test)]
6316mod step_expert_selection_tests {
6317 use super::{
6318 StepExpertArtifact, StepExpertLayout, StepParallelLoadConfig, StepParallelRuntimeRegistry,
6319 StepTpAttentionPlacement, select_step_expert_layout, select_step_expert_layout_inner,
6320 };
6321 use crate::tp::StepEpLayerSpec;
6322
6323 fn spec(layer: usize, ranks: usize) -> StepEpLayerSpec {
6324 StepEpLayerSpec {
6325 layer,
6326 devices: (0..ranks).collect(),
6327 }
6328 }
6329
6330 #[test]
6331 fn tp2_keeps_projection_sharded_experts() {
6332 let selection = select_step_expert_layout(24, &[], &[spec(24, 2)])
6333 .unwrap()
6334 .unwrap();
6335 assert_eq!(selection.layout, StepExpertLayout::TensorParallel);
6336 assert!(selection.configured_by_tp);
6337 }
6338
6339 #[test]
6340 fn tp4_and_tp8_use_expert_ownership_without_a_second_flag() {
6341 for ranks in [4, 8] {
6342 let selection = select_step_expert_layout(24, &[], &[spec(24, ranks)])
6343 .unwrap()
6344 .unwrap();
6345 assert_eq!(selection.layout, StepExpertLayout::ExpertParallel);
6346 assert!(selection.configured_by_tp);
6347 assert_eq!(selection.spec.devices.len(), ranks);
6348 }
6349 }
6350
6351 #[test]
6352 fn explicit_ep_remains_expert_parallel() {
6353 let selection = select_step_expert_layout(24, &[spec(24, 2)], &[])
6354 .unwrap()
6355 .unwrap();
6356 assert_eq!(selection.layout, StepExpertLayout::ExpertParallel);
6357 assert!(!selection.configured_by_tp);
6358 }
6359
6360 #[test]
6361 fn conflicting_ep_and_tp_assignments_fail_closed() {
6362 let error = select_step_expert_layout(24, &[spec(24, 4)], &[spec(24, 4)]).unwrap_err();
6363 assert!(error.contains("cannot enable MEMRA_STEP_EP and MEMRA_STEP_TP together"));
6364 }
6365
6366 #[test]
6367 fn automatic_tp2_attention_can_overlap_ep4_expert_ownership() {
6368 let selection = select_step_expert_layout_inner(24, &[spec(24, 4)], &[spec(24, 2)], true)
6369 .unwrap()
6370 .unwrap();
6371 assert_eq!(selection.layout, StepExpertLayout::ExpertParallel);
6372 assert!(!selection.configured_by_tp);
6373 assert_eq!(selection.spec.devices, vec![0, 1, 2, 3]);
6374
6375 let error =
6376 select_step_expert_layout_inner(24, &[spec(24, 4)], &[spec(24, 2)], false).unwrap_err();
6377 assert!(error.contains("cannot enable MEMRA_STEP_EP and MEMRA_STEP_TP together"));
6378 }
6379
6380 #[test]
6381 fn runtime_registry_owns_one_immutable_load_snapshot() {
6382 let mut source_specs = vec![spec(24, 8)];
6383 let registry = StepParallelRuntimeRegistry::with_config(StepParallelLoadConfig {
6384 ep_specs: Vec::new(),
6385 tp_specs: source_specs.clone(),
6386 native_p2p: true,
6387 ep_device_arithmetic: true,
6388 f32_mirror: true,
6389 bulk_p2p: true,
6390 nvfp4_device_routes: true,
6391 auto_parallel: true,
6392 tp_attention_expert_overlap: false,
6393 expert_artifact: StepExpertArtifact::default(),
6394 });
6395 source_specs[0].devices.clear();
6396
6397 let stored = registry.tp_spec(24).unwrap();
6398 assert_eq!(stored.devices, (0..8).collect::<Vec<_>>());
6399 assert!(registry.config.native_p2p);
6400 assert!(registry.config.ep_device_arithmetic);
6401 assert!(registry.config.f32_mirror);
6402 assert!(registry.config.bulk_p2p);
6403 assert!(registry.config.nvfp4_device_routes);
6404 assert!(registry.config.auto_parallel);
6405 assert_eq!(
6406 registry.expert_selection(24).unwrap().unwrap().layout,
6407 StepExpertLayout::ExpertParallel
6408 );
6409
6410 let standalone = StepParallelRuntimeRegistry::default();
6411 assert!(standalone.tp_spec(24).is_none());
6412 assert!(!standalone.config.native_p2p);
6413 assert!(!standalone.config.ep_device_arithmetic);
6414 assert!(!standalone.config.f32_mirror);
6415 assert!(!standalone.config.bulk_p2p);
6416 }
6417
6418 #[test]
6419 fn rank_local_attention_uses_bounded_swa_rings_only_with_native_p2p() {
6420 assert_eq!(
6421 StepTpAttentionPlacement::resolve(true, None),
6422 StepTpAttentionPlacement::RankLocalGlobal
6423 );
6424 assert_eq!(
6425 StepTpAttentionPlacement::resolve(true, Some(512)),
6426 StepTpAttentionPlacement::RankLocalSwa
6427 );
6428 assert_eq!(
6429 StepTpAttentionPlacement::resolve(false, None),
6430 StepTpAttentionPlacement::OwnerTransportFallback
6431 );
6432 assert_eq!(
6433 StepTpAttentionPlacement::resolve(false, Some(512)),
6434 StepTpAttentionPlacement::OwnerSwa
6435 );
6436 }
6437}
6438
6439#[cfg(test)]
6440mod residency_tests {
6441 use super::{DevExpertFp8ProjectionScales, ResidentPlan, residency_bytes_by_device};
6442 use crate::model::HostExpertFp8BlockScales;
6443 use std::collections::HashMap;
6444
6445 #[test]
6446 fn pp_residency_counts_only_each_devices_expert_slice() {
6447 let tensors = [
6448 ("blk.0.ffn_gate_exps.weight", 10usize),
6449 ("blk.0.ffn_up_exps.weight", 20),
6450 ("blk.1.ffn_down_exps.weight", 30),
6451 ("blk.2.ffn_gate_exps.weight", 40),
6452 ("blk.3.ffn_up_exps.weight", 50),
6453 ("blk.0.attn_q.weight", 7),
6454 ("output.weight", 11),
6455 ];
6456 let bytes = residency_bytes_by_device(tensors, &[0, 0, 1, 1], 0);
6457 assert_eq!(bytes.experts.get(&0), Some(&60));
6458 assert_eq!(bytes.experts.get(&1), Some(&90));
6459 assert_eq!(bytes.rest, 18);
6460 assert!(bytes.saw_experts);
6461 }
6462
6463 #[test]
6464 fn pp_residency_combines_stages_that_share_one_device() {
6465 let tensors = [
6466 ("blk.0.ffn_gate_exps.weight", 10usize),
6467 ("blk.1.ffn_gate_exps.weight", 20),
6468 ("blk.2.ffn_gate_exps.weight", 30),
6469 ("blk.3.ffn_gate_exps.weight", 40),
6470 ];
6471 let bytes = residency_bytes_by_device(tensors, &[0, 0, 0, 0], 0);
6472 assert_eq!(bytes.experts.get(&0), Some(&100));
6473 assert_eq!(bytes.experts.len(), 1);
6474 }
6475
6476 #[test]
6477 fn distributed_trunk_layers_do_not_poison_local_mtp_residency_estimates() {
6478 let mut plan = ResidentPlan {
6479 primary_device: 0,
6480 layer_devices: vec![0; 81],
6481 layer_counts: HashMap::from([(0, 81)]),
6482 exact_expert_bytes: None,
6483 trunk_bytes: 0,
6484 decisions: HashMap::new(),
6485 pp: false,
6486 };
6487 plan.exclude_distributed_expert_layers(1..80);
6488 assert_eq!(plan.layer_counts.get(&0), Some(&2));
6489 }
6490
6491 #[test]
6492 fn resident_fp8_scale_slab_must_match_every_expert() {
6493 let valid = HostExpertFp8BlockScales {
6494 scales: vec![1.0; 12],
6495 rows: 2,
6496 cols: 3,
6497 expert_stride: 6,
6498 };
6499 DevExpertFp8ProjectionScales::validate(&valid, 2).unwrap();
6500
6501 let short = HostExpertFp8BlockScales {
6502 scales: vec![1.0; 11],
6503 ..valid
6504 };
6505 assert_eq!(
6506 DevExpertFp8ProjectionScales::validate(&short, 2).unwrap_err(),
6507 "block-E4M3 scale slab length mismatch: got 11, want 2x6=12"
6508 );
6509 }
6510
6511 #[test]
6512 fn resident_fp8_scale_stride_must_match_its_grid() {
6513 let invalid = HostExpertFp8BlockScales {
6514 scales: vec![1.0; 8],
6515 rows: 2,
6516 cols: 2,
6517 expert_stride: 0,
6518 };
6519 assert_eq!(
6520 DevExpertFp8ProjectionScales::validate(&invalid, 2).unwrap_err(),
6521 "block-E4M3 expert scale stride must be nonzero"
6522 );
6523 }
6524}
6525
6526#[cfg(test)]
6527mod draft_head_tests {
6528 use super::{draft_head_tensor, frspec_trim_own_head_name};
6529
6530 const STEP37_DRAFTER: &[&str] = &[
6537 "output.weight",
6538 "output_norm.weight",
6539 "token_embd.weight",
6540 "blk.45.nextn.shared_head_norm.weight",
6541 "blk.45.nextn.shared_head_head.weight",
6542 "blk.46.nextn.shared_head_head.weight",
6543 "blk.47.nextn.shared_head_head.weight",
6544 ];
6545
6546 fn present(names: &'static [&'static str]) -> impl Fn(&str) -> bool {
6547 move |t: &str| names.contains(&t)
6548 }
6549
6550 #[test]
6558 fn step37_drafter_prefers_the_blocks_own_nextn_head_over_file_level_output() {
6559 assert_eq!(
6560 draft_head_tensor(present(STEP37_DRAFTER), 45),
6561 "blk.45.nextn.shared_head_head.weight"
6562 );
6563 }
6564
6565 #[test]
6569 fn each_nextn_block_selects_its_own_head() {
6570 for n in 45..=47u32 {
6571 assert_eq!(
6572 draft_head_tensor(present(STEP37_DRAFTER), n),
6573 format!("blk.{n}.nextn.shared_head_head.weight")
6574 );
6575 }
6576 }
6577
6578 #[test]
6582 fn draft_without_a_nextn_head_falls_back_to_file_level_output() {
6583 let fr_spec: &[&str] = &["output.weight", "output_norm.weight", "d2t.weight"];
6584 assert_eq!(draft_head_tensor(present(fr_spec), 45), "output.weight");
6585 }
6586
6587 #[test]
6592 fn legacy_shared_head_is_probed_but_loses_to_shared_head_head() {
6593 let legacy_only: &[&str] = &["output.weight", "blk.45.nextn.shared_head.weight"];
6594 assert_eq!(
6595 draft_head_tensor(present(legacy_only), 45),
6596 "blk.45.nextn.shared_head.weight"
6597 );
6598
6599 let both: &[&str] = &[
6600 "output.weight",
6601 "blk.45.nextn.shared_head.weight",
6602 "blk.45.nextn.shared_head_head.weight",
6603 ];
6604 assert_eq!(
6605 draft_head_tensor(present(both), 45),
6606 "blk.45.nextn.shared_head_head.weight"
6607 );
6608 }
6609
6610 #[test]
6614 fn a_different_blocks_nextn_head_is_never_borrowed() {
6615 let wrong_block: &[&str] = &[
6616 "output.weight",
6617 "blk.46.nextn.shared_head_head.weight",
6618 "blk.47.nextn.shared_head_head.weight",
6619 ];
6620 assert_eq!(draft_head_tensor(present(wrong_block), 45), "output.weight");
6621 }
6622
6623 #[test]
6628 fn frspec_trim_prefers_the_nextn_blocks_own_head_name() {
6629 assert_eq!(
6630 frspec_trim_own_head_name(45),
6631 "blk.45.nextn.shared_head_head.weight"
6632 );
6633 assert_eq!(
6635 frspec_trim_own_head_name(45),
6636 format!("blk.{}.nextn.shared_head_head.weight", 45)
6637 );
6638 assert_eq!(
6639 frspec_trim_own_head_name(40),
6640 "blk.40.nextn.shared_head_head.weight"
6641 );
6642 }
6643}