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 fp8_blk = match fp8_host {
2096 Some((gate, up, down)) => {
2097 if e.fp8_blk_nan_count(&g)? != 0
2098 || e.fp8_blk_nan_count(&u)? != 0
2099 || e.fp8_blk_nan_count(&d)? != 0
2100 {
2101 return Err("native stacked block-E4M3 expert bank contains NaN codes".into());
2102 }
2103 Some(DevExpertFp8BlockScales {
2104 gate: DevExpertFp8ProjectionScales::upload(e, gate, n_expert)?,
2105 up: DevExpertFp8ProjectionScales::upload(e, up, n_expert)?,
2106 down: DevExpertFp8ProjectionScales::upload(e, down, n_expert)?,
2107 })
2108 }
2109 None => None,
2110 };
2111 let mut host = vec![0u64; 3 * n_expert];
2112 let (pg, pu, pd) = {
2113 let __s_e0 = e.stream();
2114 let (pg, _e0) = g.device_ptr(&__s_e0);
2115 let __s_e1 = e.stream();
2116 let (pu, _e1) = u.device_ptr(&__s_e1);
2117 let __s_e2 = e.stream();
2118 let (pd, _e2) = d.device_ptr(&__s_e2);
2119 (pg, pu, pd)
2120 };
2121 for ex in 0..n_expert {
2122 if gu_il {
2123 let stride = gate.out_f * (gate.row_bytes + up.row_bytes);
2124 host[ex] = pg + (ex * stride) as u64;
2125 host[n_expert + ex] = pg + (ex * stride + gate.row_bytes) as u64;
2126 } else {
2127 host[ex] = pg + (ex * gate.expert_stride) as u64;
2128 host[n_expert + ex] = pu + (ex * up.expert_stride) as u64;
2129 }
2130 host[2 * n_expert + ex] = pd + (ex * down.expert_stride) as u64;
2131 }
2132 if gu_il {
2133 eprintln!("[moe] gate/up dev slab INTERLEAVED (MEMRA_MOE_GU_IL)");
2134 }
2135 let ptr_row = e.htod_u64(&host)?;
2136 Ok(Some(crate::hybrid::DevExps {
2137 gate: g,
2138 up: u,
2139 down: d,
2140 ptr_row,
2141 gu_il,
2142 dev: e.ctx().ordinal(),
2143 fp8_blk,
2144 }))
2145}
2146
2147pub struct FullAttnLayer {
2148 pub wq: GpuTensor,
2149 pub wk: GpuTensor,
2150 pub wv: GpuTensor,
2151 pub wo: GpuTensor,
2152 pub q_norm: GpuTensor,
2153 pub k_norm: GpuTensor,
2154 pub attn_gate: Option<GpuTensor>,
2165 pub step_tp_qkv: Option<StepTpQkv>,
2169}
2170
2171pub struct StepTpQkv {
2172 pub runtime: Arc<crate::tp::TpE4m3HostBounce>,
2173 pub q: crate::tp::ResidentBf16ColumnParallel,
2174 pub k: crate::tp::ResidentBf16ColumnParallel,
2175 pub v: crate::tp::ResidentBf16ColumnParallel,
2176 pub o: crate::tp::ResidentStepBf16RowParallel,
2177 pub attention: Option<StepTpAttention>,
2178 pub devices: Vec<usize>,
2179 pub layer: usize,
2180}
2181
2182pub struct StepTpAttention {
2183 pub q_norm: Vec<CudaSlice<f32>>,
2184 pub k_norm: Vec<CudaSlice<f32>>,
2185 pub decode_input: Option<std::sync::Mutex<crate::tp::ResidentReplicatedDeviceRows>>,
2186 pub gate_shards: Option<Vec<CudaSlice<f32>>>,
2189 pub gate_shards_bf16: Option<Vec<CudaSlice<u8>>>,
2191}
2192
2193#[derive(Clone, Copy, Debug, PartialEq, Eq)]
2194pub struct StepTpKvDeviceAdmission {
2195 pub device: usize,
2196 pub bytes: usize,
2197}
2198
2199#[derive(Clone, Copy, Debug)]
2203pub struct MlaGeom {
2204 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, }
2212
2213#[derive(Clone, Copy, Debug)]
2218pub struct MlaIndexerGeom {
2219 pub heads: usize, pub head_dim: usize, pub top_k: usize, pub pool: usize, pub always_select_tail: bool,
2224}
2225
2226impl MlaIndexerGeom {
2227 pub fn select_k(&self, n_pools: usize) -> usize {
2229 (self.top_k / self.pool).min(n_pools)
2230 }
2231
2232 pub fn index_width(&self, n_pools: usize) -> usize {
2234 self.select_k(n_pools) * self.pool
2235 + if self.always_select_tail {
2236 self.pool - 1
2237 } else {
2238 0
2239 }
2240 }
2241
2242 pub fn state_width(&self) -> usize {
2244 2 * self.head_dim
2245 }
2246}
2247
2248pub struct MlaIndexer {
2252 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,
2260}
2261
2262pub struct MlaAttnLayer {
2263 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,
2273 pub index: Option<MlaIndexer>,
2276 pub tp: Option<Box<crate::glm5_tp::Glm5TpMla>>,
2281 pub tp_shard: bool,
2288}
2289
2290impl MlaAttnLayer {
2291 pub fn load(
2303 e: &Engine,
2304 src: &dyn TensorSource,
2305 il: u32,
2306 plan: &memra_gguf::model_plan::MlaAttentionPlan,
2307 ) -> Result<Self, Box<dyn std::error::Error>> {
2308 let memra_gguf::model_plan::MlaAttentionPlan::LatentKv {
2309 query_heads,
2310 q_lora_rank,
2311 kv_lora_rank,
2312 qk_head_dim,
2313 rope_head_dim,
2314 value_head_dim,
2315 sparse_index,
2316 ..
2317 } = plan
2318 else {
2319 return Err(format!(
2320 "native MLA loader has no compressed-KV implementation for block {il}"
2321 )
2322 .into());
2323 };
2324 let d_nope = qk_head_dim
2325 .checked_sub(*rope_head_dim)
2326 .ok_or("MLA rope head width exceeds total QK head width")?;
2327 let p = |s: &str| format!("blk.{il}.{s}");
2328 let geom = MlaGeom {
2329 n_head: *query_heads as usize,
2330 d_nope: d_nope as usize,
2331 d_rope: *rope_head_dim as usize,
2332 d_v: *value_head_dim as usize,
2333 kv_rank: *kv_lora_rank as usize,
2334 latent_dim: (*kv_lora_rank + *rope_head_dim) as usize,
2335 scale: 1.0 / (*qk_head_dim as f32).sqrt(),
2336 };
2337 let wq_a = load_t(e, src, &p("attn_q_a.weight"))?;
2338 let wq_b = load_t(e, src, &p("attn_q_b.weight"))?;
2339 let wkv_a = load_t(e, src, &p("attn_kv_a_mqa.weight"))?;
2340 let wk_b = load_t(e, src, &p("attn_k_b.weight"))?;
2341 let wv_b = load_t(e, src, &p("attn_v_b.weight"))?;
2342 let wo = load_t(e, src, &p("attn_output.weight"))?;
2343 for (w, tensor) in [(&wk_b, "attn_k_b"), (&wv_b, "attn_v_b")] {
2352 if !matches!(w, GpuTensor::Float { .. }) {
2353 return Err(format!(
2354 "blk.{il}.{tensor}.weight is not f32-resident. The MLA conversion-split \
2355 operands feed f32-only absorb/decompress kernels; the checkpoint source must \
2356 dequantize them (TensorTransform::SplitMlaKv) rather than hand the engine a \
2357 quantized plane"
2358 )
2359 .into());
2360 }
2361 }
2362 let n_head = wq_b.out_features() / (geom.d_nope + geom.d_rope);
2364 assert_eq!(
2365 wq_b.out_features(),
2366 n_head * (geom.d_nope + geom.d_rope),
2367 "wq_b out {} not a multiple of qk_head_dim {}",
2368 wq_b.out_features(),
2369 geom.d_nope + geom.d_rope
2370 );
2371 assert_eq!(
2372 wq_a.in_features(),
2373 wkv_a.in_features(),
2374 "q_a/kv_a hidden mismatch"
2375 );
2376 assert_eq!(
2377 wq_b.in_features(),
2378 *q_lora_rank as usize,
2379 "wq_b in != q_lora_rank"
2380 );
2381 assert_eq!(
2382 n_head, geom.n_head,
2383 "MLA checkpoint head count != ModelPlan"
2384 );
2385 assert_eq!(
2386 wkv_a.out_features(),
2387 geom.latent_dim,
2388 "wkv_a out != kv_lora_rank + rope"
2389 );
2390 assert_eq!(
2391 wk_b.ne(),
2392 &[geom.d_nope as u64, geom.kv_rank as u64, n_head as u64],
2393 "attn_k_b must be the TRANSPOSED (nope, kv_rank, head) conversion split"
2394 );
2395 assert_eq!(
2396 wv_b.ne(),
2397 &[geom.kv_rank as u64, geom.d_v as u64, n_head as u64],
2398 "attn_v_b must be the (kv_rank, v, head) conversion split"
2399 );
2400 assert_eq!(
2401 wo.in_features(),
2402 n_head * geom.d_v,
2403 "wo in != n_head * v_head_dim"
2404 );
2405 let index = Self::load_indexer(e, src, il, sparse_index, *q_lora_rank)?;
2406 Ok(MlaAttnLayer {
2407 wq_a,
2408 q_a_norm: load_t(e, src, &p("attn_q_a_norm.weight"))?,
2409 wq_b,
2410 wkv_a,
2411 kv_a_norm: load_t(e, src, &p("attn_kv_a_norm.weight"))?,
2412 wk_b,
2413 wv_b,
2414 wo,
2415 geom,
2416 index,
2417 tp: None,
2418 tp_shard: false,
2419 })
2420 }
2421
2422 fn load_indexer(
2434 e: &Engine,
2435 src: &dyn TensorSource,
2436 il: u32,
2437 sparse_index: &memra_gguf::model_plan::SparseIndexPlan,
2438 q_lora_rank: u32,
2439 ) -> Result<Option<MlaIndexer>, Box<dyn std::error::Error>> {
2440 let memra_gguf::model_plan::SparseIndexPlan::Own {
2441 heads,
2442 head_dim,
2443 top_k,
2444 kpool: Some(kpool),
2445 } = sparse_index
2446 else {
2447 return Ok(None);
2448 };
2449 let geom = MlaIndexerGeom {
2450 heads: *heads as usize,
2451 head_dim: *head_dim as usize,
2452 top_k: *top_k as usize,
2453 pool: kpool.pool as usize,
2454 always_select_tail: kpool.always_select_tail,
2455 };
2456 if geom.heads == 0 || geom.head_dim == 0 || geom.pool == 0 || geom.top_k < geom.pool {
2457 return Err(format!(
2458 "blk.{il}: SparseIndexPlan::Own declares an unusable k-pool indexer \
2459 (heads {}, head_dim {}, pool {}, top_k {}) — heads/head_dim/pool must be \
2460 positive and top_k must admit at least one pool",
2461 geom.heads, geom.head_dim, geom.pool, geom.top_k
2462 )
2463 .into());
2464 }
2465 let need = |suffix: &str| -> Result<GpuTensor, Box<dyn std::error::Error>> {
2470 let name = format!("blk.{il}.{suffix}");
2471 if !src.has(&name) {
2472 return Err(format!(
2473 "blk.{il}: the layer's ModelPlan declares a DSA k-pool indexer but the \
2474 checkpoint has no `{name}`. This layer MUST NOT fall back to dense \
2475 attention: dense and indexed attention are the same function only below \
2476 index_topk ({}), and glm5_next serves a 1,048,576-token context",
2477 geom.top_k
2478 )
2479 .into());
2480 }
2481 load_t(e, src, &name).map_err(|source| -> Box<dyn std::error::Error> {
2482 format!("blk.{il}: DSA k-pool indexer tensor `{name}` failed to load: {source}")
2483 .into()
2484 })
2485 };
2486 let wq_b = need("indexer.attn_q_b.weight")?;
2487 let wk = need("indexer.attn_k.weight")?;
2488 let k_norm_w = need("indexer.k_norm.weight")?;
2489 let k_norm_b = need("indexer.k_norm.bias")?;
2490 let weights_proj = need("indexer.proj.weight")?;
2491 let kpool_gate = need("indexer.kpool_gate.weight")?;
2492 let kpool_ape = need("indexer.kpool_ape.weight")?;
2493 for (w, name) in [
2496 (&k_norm_w, "indexer.k_norm.weight"),
2497 (&k_norm_b, "indexer.k_norm.bias"),
2498 (&kpool_ape, "indexer.kpool_ape.weight"),
2499 ] {
2500 if !matches!(w, GpuTensor::Float { .. }) {
2501 return Err(format!(
2502 "blk.{il}.{name} is not f32-resident. The indexer's LayerNorm affine and \
2503 k-pool positional embedding feed f32-only kernels"
2504 )
2505 .into());
2506 }
2507 }
2508 assert_eq!(
2509 wq_b.in_features(),
2510 q_lora_rank as usize,
2511 "blk.{il}.indexer.attn_q_b in != q_lora_rank"
2512 );
2513 assert_eq!(
2514 wq_b.out_features(),
2515 geom.heads * geom.head_dim,
2516 "blk.{il}.indexer.attn_q_b out != index heads * head_dim"
2517 );
2518 assert_eq!(
2519 wk.out_features(),
2520 geom.head_dim,
2521 "blk.{il}.indexer.attn_k out != index head_dim"
2522 );
2523 assert_eq!(
2524 weights_proj.out_features(),
2525 geom.heads,
2526 "blk.{il}.indexer.proj out != index heads"
2527 );
2528 assert_eq!(
2529 kpool_gate.out_features(),
2530 geom.head_dim,
2531 "blk.{il}.indexer.kpool_gate out != index head_dim"
2532 );
2533 assert_eq!(
2534 kpool_ape.float_data().len(),
2535 geom.pool * geom.head_dim,
2536 "blk.{il}.indexer.kpool_ape must hold pool * head_dim elements"
2537 );
2538 Ok(Some(MlaIndexer {
2539 wq_b,
2540 wk,
2541 k_norm_w,
2542 k_norm_b,
2543 weights_proj,
2544 kpool_gate,
2545 kpool_ape,
2546 geom,
2547 }))
2548 }
2549}
2550
2551#[track_caller]
2559pub(crate) fn mla_path_unimplemented(path: &str) -> ! {
2560 panic!(
2561 "Mixer::Mla has no {path} arm — the MLA forward is wired for the stateless forward, \
2562 the stateful prime and T=1 decode only (cu/mla_attn.cu, increment 4); this path needs \
2563 its own parity gate before it may run \
2564 (research/mla-bringup-20260801/DESIGN.md §4, increment 7)"
2565 )
2566}
2567
2568#[track_caller]
2574pub(crate) fn kda_path_unimplemented(path: &str) -> ! {
2575 panic!(
2576 "Mixer::Kda has no {path} arm — glm5_next KDA is wired for the stateless forward, the \
2577 stateful prime and T=1 decode only (crates/memra-engine/src/kda.rs); this path needs \
2578 its own parity gate before it may run"
2579 )
2580}
2581
2582pub struct LinearAttnLayer {
2583 pub geometry: memra_gguf::model_plan::GatedDeltaNetPlan,
2584 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, }
2594
2595#[allow(clippy::large_enum_variant)] pub enum Mixer {
2597 Full(FullAttnLayer),
2598 Linear(LinearAttnLayer),
2599 Mla(MlaAttnLayer),
2601 Kda(crate::kda::KdaAttnLayer),
2603}
2604
2605pub struct MoeWeights {
2612 pub gate_inp: GpuTensor, pub gate_inp_shexp: Option<GpuTensor>, pub exp_probs_b: Option<Vec<f32>>,
2618 pub exp_probs_b_dev: CudaSlice<f32>,
2619 pub active_experts: Option<Vec<bool>>,
2623 pub active_experts_dev: CudaSlice<u8>,
2624 pub gate_exps: HostExps, pub up_exps: HostExps, pub down_exps: HostExps, pub gate_shexp: Option<GpuTensor>,
2628 pub up_shexp: Option<GpuTensor>,
2629 pub down_shexp: Option<GpuTensor>,
2630 pub dev_exps: Option<DevExps>,
2637 pub step_ep: Option<StepEpExps>,
2641 pub step_tp: Option<StepTpExps>,
2645 pub glm5_ep: Option<crate::glm5_tp::Glm5EpExps>,
2650 pub dev_macros: cudarc::driver::CudaSlice<f32>,
2656 pub has_macros: bool,
2657 pub w4a16_bf16_activations: bool,
2660}
2661
2662#[allow(clippy::large_enum_variant)] pub enum StepEpExpertBank {
2665 E4m3(crate::tp::ResidentExpertParallel),
2666 Nvfp4(crate::tp::ResidentNvfp4ExpertParallel),
2667}
2668
2669impl StepEpExpertBank {
2670 pub fn e4m3(&self) -> Result<&crate::tp::ResidentExpertParallel, String> {
2674 match self {
2675 Self::E4m3(bank) => Ok(bank),
2676 Self::Nvfp4(_) => Err(
2677 "Step grouped expert program reached an NVFP4 bank; this path is qualified \
2678 for the E4M3 artifact only"
2679 .to_string(),
2680 ),
2681 }
2682 }
2683}
2684
2685pub struct StepEpExps {
2686 pub runtime: Arc<crate::tp::TpE4m3HostBounce>,
2687 pub experts: StepEpExpertBank,
2688 pub devices: Vec<usize>,
2689 pub configured_by_tp: bool,
2690 pub activation_limit: Option<f32>,
2691 pub nvfp4_device_routes: bool,
2693 pub grouped_decode: Option<std::sync::Mutex<StepEpGroupedDecode>>,
2696}
2697
2698pub struct StepEpGroupedDecode {
2699 pub(crate) projection: crate::tp::PreparedStepGroupedExpertParallelGate,
2700 pub(crate) combine: crate::tp::PreparedPeerWeightedRouteCombine,
2701}
2702
2703#[derive(Default)]
2704pub(crate) struct StepEpGroupedPrefill {
2705 pub(crate) state: Option<StepEpGroupedPrefillState>,
2706}
2707
2708pub(crate) struct StepEpGroupedPrefillState {
2709 pub(crate) devices: Vec<usize>,
2710 pub(crate) grouped: StepEpGroupedDecode,
2711}
2712
2713#[allow(clippy::large_enum_variant)] pub enum StepTpExpertBank {
2716 E4m3(crate::tp::ResidentTensorParallel),
2717 Nvfp4(crate::tp::ResidentNvfp4TensorParallel),
2718}
2719
2720pub struct StepTpExps {
2721 pub runtime: Arc<crate::tp::TpE4m3HostBounce>,
2722 pub experts: StepTpExpertBank,
2723 pub devices: Vec<usize>,
2724 pub activation_limit: Option<f32>,
2727}
2728
2729impl MoeWeights {
2730 #[inline]
2731 pub fn has_uniform_expert_layout(&self) -> bool {
2732 self.gate_exps.is_uniform_layout()
2733 && self.up_exps.is_uniform_layout()
2734 && self.down_exps.is_uniform_layout()
2735 }
2736
2737 #[inline]
2738 pub fn active_count(&self) -> usize {
2739 self.active_experts
2740 .as_ref()
2741 .map(|mask| mask.iter().filter(|&&active| active).count())
2742 .unwrap_or(self.gate_exps.n_expert)
2743 }
2744
2745 #[allow(clippy::too_many_arguments)]
2746 pub(crate) fn qmatvec_view(
2747 &self,
2748 e: &Engine,
2749 w: &CudaSlice<u8>,
2750 range: std::ops::Range<usize>,
2751 x: &cudarc::driver::CudaView<f32>,
2752 m: usize,
2753 in_f: usize,
2754 out_f: usize,
2755 qtype: i32,
2756 row_bytes: usize,
2757 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
2758 if self.w4a16_bf16_activations && qtype == crate::QT_NVFP4 {
2759 e.qmatvec_view_bf16_activation(w, range, x, m, in_f, out_f, qtype, row_bytes)
2760 } else {
2761 e.qmatvec_view(w, range, x, m, in_f, out_f, qtype, row_bytes)
2762 }
2763 }
2764}
2765
2766pub struct DevExps {
2769 pub gate: CudaSlice<u8>,
2770 pub up: CudaSlice<u8>,
2771 pub down: CudaSlice<u8>,
2772 pub ptr_row: CudaSlice<u64>,
2774 pub dev: usize,
2782 pub gu_il: bool,
2788 pub fp8_blk: Option<DevExpertFp8BlockScales>,
2792}
2793
2794pub struct DevExpertFp8BlockScales {
2795 pub gate: DevExpertFp8ProjectionScales,
2796 pub up: DevExpertFp8ProjectionScales,
2797 pub down: DevExpertFp8ProjectionScales,
2798}
2799
2800pub struct DevExpertFp8ProjectionScales {
2801 pub scales: CudaSlice<f32>,
2802 pub rows: usize,
2803 pub cols: usize,
2804 pub expert_stride: usize,
2805}
2806
2807impl DevExpertFp8ProjectionScales {
2808 fn validate(
2809 host: &crate::model::HostExpertFp8BlockScales,
2810 n_expert: usize,
2811 ) -> Result<(), String> {
2812 if host.expert_stride == 0 {
2813 return Err("block-E4M3 expert scale stride must be nonzero".into());
2814 }
2815 if host.rows * host.cols != host.expert_stride {
2816 return Err(format!(
2817 "block-E4M3 expert scale stride mismatch: {}x{} != {}",
2818 host.rows, host.cols, host.expert_stride
2819 ));
2820 }
2821 let want = n_expert
2822 .checked_mul(host.expert_stride)
2823 .ok_or("block-E4M3 expert scale slab length overflow")?;
2824 if host.scales.len() != want {
2825 return Err(format!(
2826 "block-E4M3 scale slab length mismatch: got {}, want {n_expert}x{}={want}",
2827 host.scales.len(),
2828 host.expert_stride
2829 ));
2830 }
2831 Ok(())
2832 }
2833
2834 fn upload(
2835 e: &Engine,
2836 host: &crate::model::HostExpertFp8BlockScales,
2837 n_expert: usize,
2838 ) -> Result<Self, Box<dyn std::error::Error>> {
2839 Self::validate(host, n_expert)?;
2840 Ok(Self {
2841 scales: e.htod(&host.scales)?,
2842 rows: host.rows,
2843 cols: host.cols,
2844 expert_stride: host.expert_stride,
2845 })
2846 }
2847}
2848
2849#[allow(clippy::large_enum_variant)] pub enum Ffn {
2852 Dense {
2853 ffn_gate: GpuTensor,
2854 ffn_up: GpuTensor,
2855 ffn_down: GpuTensor,
2856 },
2857 Moe(MoeWeights),
2858}
2859
2860pub struct HybridLayer {
2861 pub attn_norm: GpuTensor,
2862 pub post_attn_norm: GpuTensor, pub mixer: Mixer,
2864 pub ffn: Ffn,
2865 pub gemma4: Option<Gemma4LayerBits>,
2866 pub hyper: Option<crate::hyper::HyperLayer>,
2871}
2872
2873pub struct Gemma4LayerBits {
2877 pub ffn_norm: GpuTensor, pub post_ffw_norm: GpuTensor, pub moe_bits: Option<Gemma4MoeBits>,
2882 pub layer_scale: f32, pub e4b: Option<Gemma4E4bLayer>,
2885}
2886
2887pub struct Gemma4E4bLayer {
2892 pub inp_gate: GpuTensor, pub proj: GpuTensor, pub post_norm: GpuTensor, pub qkv_cat: Option<GpuTensor>,
2899 pub kv_share: Option<u32>,
2903}
2904
2905pub struct Gemma4E4bModel {
2909 pub tok_tbl_gpu: std::sync::OnceLock<CudaSlice<u8>>,
2912 pub tok_embd_bytes: Vec<u8>,
2913 pub tok_embd_qt: i32,
2914 pub tok_embd_row_bytes: usize,
2915 pub model_proj: GpuTensor, pub proj_norm: GpuTensor, pub n_epl: usize,
2918}
2919
2920pub struct Gemma4MoeBits {
2921 pub post_ffw_norm_1: GpuTensor, pub pre_ffw_norm_2: GpuTensor, pub post_ffw_norm_2: GpuTensor, pub shared_gate: GpuTensor,
2925 pub shared_up: GpuTensor,
2926 pub shared_down: GpuTensor,
2927 pub router_scale_pre: CudaSlice<f32>,
2932 pub per_expert_scale: Vec<f32>, pub per_expert_scale_d: CudaSlice<f32>, }
2935
2936fn load_mtp_head_maybe_nvfp4(
2949 e: &Engine,
2950 src: &dyn TensorSource,
2951 name: &str,
2952) -> Result<Option<GpuTensor>, Box<dyn std::error::Error>> {
2953 if !{
2954 static ENV: std::sync::OnceLock<Option<bool>> = std::sync::OnceLock::new();
2955 crate::step37_door(&ENV, "MEMRA_MTP_HEAD_NVFP4")
2956 } {
2957 return load_opt(e, src, name);
2958 }
2959 let Some(v) = src.find(name) else {
2960 return Ok(None);
2961 };
2962 if !matches!(v.ggml_type, GgmlType::BF16) || v.ne[0] % 64 != 0 {
2963 return load_opt(e, src, name);
2964 }
2965 let vals: Vec<f32> = v
2966 .bytes
2967 .chunks_exact(2)
2968 .map(|c| f32::from_bits((u16::from_le_bytes([c[0], c[1]]) as u32) << 16))
2969 .collect();
2970 let blocks = memra_gguf::nvfp4_repack::f32_to_nvfp4(&vals);
2971 eprintln!(
2972 "[mtp-head] {name}: BF16 -> NVFP4 ({} MiB, was {} MiB)",
2973 blocks.len() >> 20,
2974 v.bytes.len() >> 20
2975 );
2976 Ok(Some(GpuTensor::from_quant_bytes(
2977 e,
2978 &blocks,
2979 GgmlType::NVFP4,
2980 v.ne[0],
2981 v.ne[1],
2982 1.0,
2983 )?))
2984}
2985
2986pub(crate) fn sha256_file_hex8(
2994 path: &std::path::Path,
2995) -> Result<String, Box<dyn std::error::Error>> {
2996 sha256_file_hex(path, 4)
2997}
2998
2999pub(crate) fn sha256_file_hex(
3004 path: &std::path::Path,
3005 n_bytes: usize,
3006) -> Result<String, Box<dyn std::error::Error>> {
3007 use sha2::{Digest, Sha256};
3008 let mut file = std::fs::File::open(path)?;
3009 let mut hasher = Sha256::new();
3010 std::io::copy(&mut file, &mut hasher)?;
3011 let digest = hasher.finalize();
3012 Ok(digest
3013 .iter()
3014 .take(n_bytes)
3015 .map(|byte| format!("{byte:02x}"))
3016 .collect())
3017}
3018
3019pub fn frspec_parse_ranks_txt_strict(text: &str, what: &str) -> Result<Vec<u32>, String> {
3027 let mut out: Vec<u32> = Vec::new();
3028 for (lineno, raw) in text.lines().enumerate() {
3029 let line = raw.trim();
3030 if line.is_empty() {
3031 continue;
3032 }
3033 let id = line.parse::<u32>().map_err(|_| {
3034 format!(
3035 "{what}: line {} is not a token id ({line:?}); a ranks .txt is one integer id \
3036 per line in rank order",
3037 lineno + 1
3038 )
3039 })?;
3040 out.push(id);
3041 }
3042 Ok(out)
3043}
3044
3045pub fn frspec_validate_ranks(d2t: &[u32], n_vocab: usize, what: &str) -> Result<(), String> {
3051 if d2t.is_empty() {
3052 return Err(format!(
3053 "{what}: the ranks artifact yields an EMPTY id list"
3054 ));
3055 }
3056 if d2t.len() > n_vocab {
3057 return Err(format!(
3058 "{what}: {} ranks for a {n_vocab}-row head: a ranks list wider than the vocabulary \
3059 was minted for a different model",
3060 d2t.len()
3061 ));
3062 }
3063 if let Some(&bad) = d2t.iter().find(|&&t| t as usize >= n_vocab) {
3064 return Err(format!(
3065 "{what}: token id {bad} >= head rows {n_vocab}: the ranks artifact was minted for a \
3066 different vocabulary (wrong-model file refused at boot)"
3067 ));
3068 }
3069 let mut seen = vec![false; n_vocab];
3070 for &t in d2t {
3071 if seen[t as usize] {
3072 return Err(format!(
3073 "{what}: token id {t} appears more than once: a ranks list is a set of distinct \
3074 ids in rank order"
3075 ));
3076 }
3077 seen[t as usize] = true;
3078 }
3079 Ok(())
3080}
3081
3082pub fn frspec_gather_rows(rows: &[u8], row_bytes: usize, d2t: &[u32]) -> Vec<u8> {
3087 let mut gathered = Vec::with_capacity(d2t.len() * row_bytes);
3088 for &t in d2t {
3089 let off = t as usize * row_bytes;
3090 gathered.extend_from_slice(&rows[off..off + row_bytes]);
3091 }
3092 gathered
3093}
3094
3095#[cfg(test)]
3096mod frspec_ranks_tests {
3097 use super::{frspec_gather_rows, frspec_parse_ranks_txt_strict, frspec_validate_ranks};
3098
3099 #[test]
3100 fn strict_parse_skips_blank_lines_and_refuses_anything_else() {
3101 let ok = frspec_parse_ranks_txt_strict("5\n\n 7 \n0\n", "t").unwrap();
3102 assert_eq!(ok, vec![5, 7, 0]);
3103 assert_eq!(
3105 frspec_parse_ranks_txt_strict("5\n7", "t").unwrap(),
3106 vec![5, 7]
3107 );
3108 for bad in ["id\n5\n", "5\n-1\n", "5\n7.0\n", "# ranks\n5\n", "5 7\n"] {
3110 let err = frspec_parse_ranks_txt_strict(bad, "t").unwrap_err();
3111 assert!(err.contains("is not a token id"), "{bad:?} -> {err}");
3112 }
3113 assert!(frspec_parse_ranks_txt_strict("", "t").unwrap().is_empty());
3115 }
3116
3117 #[test]
3118 fn validate_refuses_empty_oob_duplicate_and_wider_than_vocab() {
3119 assert!(frspec_validate_ranks(&[3, 1, 0], 4, "t").is_ok());
3120 assert!(frspec_validate_ranks(&[3, 1, 0, 2], 4, "t").is_ok());
3122 let e = frspec_validate_ranks(&[], 4, "t").unwrap_err();
3123 assert!(e.contains("EMPTY"), "{e}");
3124 let e = frspec_validate_ranks(&[3, 4], 4, "t").unwrap_err();
3125 assert!(e.contains("token id 4 >= head rows 4"), "{e}");
3126 let e = frspec_validate_ranks(&[3, 1, 3], 4, "t").unwrap_err();
3127 assert!(e.contains("token id 3 appears more than once"), "{e}");
3128 let e = frspec_validate_ranks(&[0, 1, 2, 3, 0], 4, "t").unwrap_err();
3129 assert!(e.contains("5 ranks for a 4-row head"), "{e}");
3130 }
3131
3132 #[test]
3133 fn gather_rows_is_the_rank_ordered_row_copy() {
3134 let rows: Vec<u8> = (0..5u8).flat_map(|t| [t, t + 10, t + 20]).collect();
3136 let g = frspec_gather_rows(&rows, 3, &[4, 0, 2]);
3137 assert_eq!(g, vec![4, 14, 24, 0, 10, 20, 2, 12, 22]);
3138 assert_ne!(g, frspec_gather_rows(&rows, 3, &[0, 4, 2]));
3140 assert_eq!(frspec_gather_rows(&rows, 3, &[0, 1, 2, 3, 4]), rows);
3142 }
3143}
3144
3145pub(crate) fn frspec_trim_own_head_name(n_trunk: usize) -> String {
3146 format!("blk.{n_trunk}.nextn.shared_head_head.weight")
3147}
3148
3149pub struct DflashTrimHead {
3160 pub head: GpuTensor,
3163 pub d2t: Vec<u32>,
3165 pub src_sha16: String,
3168}
3169
3170fn frspec_read_d2t(path: &str) -> Result<Vec<u32>, Box<dyn std::error::Error>> {
3179 Ok(if path.ends_with(".txt") {
3180 let text = std::fs::read_to_string(path)?;
3181 frspec_parse_ranks_txt_strict(&text, &format!("MEMRA_FRSPEC_TRIM={path}"))?
3182 } else {
3183 let tg = GgufFile::open(path)?;
3184 let d2t_t = tg
3185 .find("d2t")
3186 .expect("MEMRA_FRSPEC_TRIM file has no d2t tensor");
3187 let d2t_bytes = tg.tensor_data(d2t_t);
3188 match d2t_t.ggml_type {
3189 GgmlType::I32 => d2t_bytes
3190 .chunks_exact(4)
3191 .map(|c| i32::from_le_bytes(c.try_into().unwrap()) as u32)
3192 .collect(),
3193 GgmlType::I64 => d2t_bytes
3194 .chunks_exact(8)
3195 .map(|c| i64::from_le_bytes(c.try_into().unwrap()) as u32)
3196 .collect(),
3197 other => panic!("d2t must be I32/I64, got {other:?}"),
3198 }
3199 })
3200}
3201
3202#[allow(clippy::type_complexity)] fn frspec_gather_trimmed_head(
3211 e: &Engine,
3212 v: &memra_gguf::source::TensorView<'_>,
3213 d2t: &[u32],
3214 want_nvfp4_env: bool,
3215 macro_scale: f32,
3216) -> Result<(GpuTensor, Option<(usize, usize)>), Box<dyn std::error::Error>> {
3217 let out_f = v.ne[1] as usize;
3218 let row_bytes = v.bytes.len() / out_f;
3219 assert!(
3220 d2t.iter().all(|&t| (t as usize) < out_f),
3221 "d2t token id >= lm_head rows {out_f}"
3222 );
3223 let gathered = frspec_gather_rows(&v.bytes, row_bytes, d2t);
3224 let want_nvfp4 =
3225 want_nvfp4_env && matches!(v.ggml_type, GgmlType::BF16) && v.ne[0].is_multiple_of(64);
3226 if want_nvfp4 {
3227 let in_f = v.ne[0] as usize;
3228 let vals: Vec<f32> = gathered
3229 .chunks_exact(2)
3230 .map(|c| f32::from_bits((u16::from_le_bytes([c[0], c[1]]) as u32) << 16))
3231 .collect();
3232 debug_assert_eq!(vals.len(), d2t.len() * in_f);
3233 let blocks = memra_gguf::nvfp4_repack::f32_to_nvfp4(&vals);
3234 let sizes = (blocks.len(), gathered.len());
3235 let trimmed = GpuTensor::from_quant_bytes(
3236 e,
3237 &blocks,
3238 GgmlType::NVFP4,
3239 v.ne[0],
3240 d2t.len() as u64,
3241 1.0,
3242 )?;
3243 Ok((trimmed, Some(sizes)))
3244 } else {
3245 let trimmed = match v.ggml_type {
3246 GgmlType::BF16 => GpuTensor::FloatBf16 {
3247 data: e.htod_bytes(&gathered)?,
3248 ne: vec![v.ne[0], d2t.len() as u64],
3249 },
3250 GgmlType::F32 => GpuTensor::Float {
3251 data: e.htod(
3252 &gathered
3253 .chunks_exact(4)
3254 .map(|c| f32::from_le_bytes(c.try_into().unwrap()))
3255 .collect::<Vec<f32>>(),
3256 )?,
3257 ne: vec![v.ne[0], d2t.len() as u64],
3258 },
3259 _ => GpuTensor::from_quant_bytes(
3260 e,
3261 &gathered,
3262 v.ggml_type,
3263 v.ne[0],
3264 d2t.len() as u64,
3265 macro_scale,
3266 )?,
3267 };
3268 Ok((trimmed, None))
3269 }
3270}
3271
3272pub struct MtpHead {
3273 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>>,
3287 pub d2t_from_target_head: bool,
3291 pub geom: Option<DraftGeom>,
3297 pub step35: Option<Step35MtpGeom>,
3302}
3303
3304#[derive(Debug, Clone)]
3318pub struct Step35MtpGeom {
3319 pub il: u32,
3321 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>,
3332}
3333
3334impl Step35MtpGeom {
3335 pub fn from_plan(layer: &memra_gguf::model_plan::LayerPlan) -> Result<Self, String> {
3337 use memra_gguf::model_plan::{ActivationPlan, AttentionPlan};
3338
3339 let (attention, window) = match &layer.attention {
3340 AttentionPlan::Full(attention) => (attention, None),
3341 AttentionPlan::SlidingWindow { attention, window } => (attention, Some(*window)),
3342 other => {
3343 return Err(format!(
3344 "MTP block {} has unsupported tuned attention {other:?}",
3345 layer.index
3346 ));
3347 }
3348 };
3349 if attention.output_gate != memra_gguf::config::AttentionGateKind::SeparateHead {
3350 return Err(format!(
3351 "MTP block {} does not declare a separate attention gate",
3352 layer.index
3353 ));
3354 }
3355 let activation = match &layer.mlp {
3356 MlpPlan::Dense(dense) => &dense.activation,
3357 MlpPlan::Moe(moe) => &moe.activation,
3358 };
3359 let clamp_shexp = match activation {
3360 ActivationPlan::SwiGluClamped { limit } if *limit > 0.0 => Some(*limit),
3361 _ => None,
3362 };
3363 Ok(Step35MtpGeom {
3364 il: layer.index,
3365 n_head: attention.query_heads as usize,
3366 n_head_kv: attention.kv_heads as usize,
3367 n_rot: attention.rope.dimensions as usize,
3368 rope_base: attention.rope.base,
3369 swa: window.is_some(),
3370 window: window.unwrap_or(0) as usize,
3371 clamp_shexp,
3372 })
3373 }
3374}
3375
3376pub struct DraftGeom {
3378 pub d_inner: usize, pub n_head: usize, pub n_head_kv: usize,
3381 pub out_up: GpuTensor, }
3383
3384pub fn draft_head_tensor(has: impl Fn(&str) -> bool, n: u32) -> String {
3393 let own = format!("blk.{n}.nextn.shared_head_head.weight");
3394 if has(&own) {
3395 return own;
3396 }
3397 let legacy = format!("blk.{n}.nextn.shared_head.weight");
3400 if has(&legacy) {
3401 return legacy;
3402 }
3403 "output.weight".to_string()
3405}
3406
3407impl MtpHead {
3408 pub fn load_draft(
3415 e: &Engine,
3416 g: &GgufFile,
3417 main_cfg: &ModelConfig,
3418 ) -> Result<Self, Box<dyn std::error::Error>> {
3419 let src = GgufSource(g);
3420 let dcfg = src.try_config().map_err(std::io::Error::other)?;
3421 let draft_plan = match memra_gguf::model_packs::for_config(&dcfg) {
3422 Some(pack) => pack.compile_plan(&dcfg)?,
3423 None => memra_gguf::model_plan::ModelPlan::compile(&dcfg)?,
3424 };
3425 let main_plan = match memra_gguf::model_packs::for_config(main_cfg) {
3426 Some(pack) => pack.compile_plan(main_cfg)?,
3427 None => memra_gguf::model_plan::ModelPlan::compile(main_cfg)?,
3428 };
3429 if dcfg.nextn_predict_layers == 0 {
3434 return Err(format!(
3435 "draft GGUF has no nextn_predict_layers (arch {:?}) — not a NextN/MTP regime \
3436 draft; gemma assistant drafters attach via MEMRA_DRAFT, not '+draft'",
3437 g.arch()
3438 )
3439 .into());
3440 }
3441 let n = dcfg.n_layer - dcfg.nextn_predict_layers;
3442 let draft_block = draft_plan
3443 .mtp_blocks
3444 .iter()
3445 .find(|block| block.layer.index == n)
3446 .ok_or_else(|| format!("draft ModelPlan has no MTP block {n}"))?;
3447 let p = |s: &str| format!("blk.{n}.{s}");
3448
3449 let student = src.has(&p("nextn.out_up.weight"));
3453 assert_eq!(dcfg.n_embd, main_cfg.n_embd, "draft n_embd != model n_embd");
3454 assert_eq!(
3455 dcfg.head_dim_k, main_cfg.head_dim_k,
3456 "draft head_dim != model head_dim"
3457 );
3458 let main_sliding_gated = crate::plan_backend::decode_batch_program(&main_plan)
3465 == crate::plan_backend::DecodeBatchProgram::SlidingGatedMoe;
3466 let draft_sliding_gated = crate::plan_backend::decode_batch_program(&draft_plan)
3467 == crate::plan_backend::DecodeBatchProgram::SlidingGatedMoe;
3468 let step35 = match (main_sliding_gated, draft_sliding_gated) {
3469 (true, true) => {
3470 let g = Step35MtpGeom::from_plan(&draft_block.layer)?;
3471 let out_f = |t: &str| -> Option<usize> {
3473 src.find(&p(t))
3474 .and_then(|v| v.ne.get(1).copied())
3475 .map(|x| x as usize)
3476 };
3477 let hd = dcfg.head_dim_k as usize;
3478 let wq_out =
3479 out_f("attn_q.weight").ok_or("step35 draft block has no attn_q.weight")?;
3480 assert_eq!(
3481 wq_out,
3482 g.n_head * hd,
3483 "step35 draft blk.{n}: attn_q out {wq_out} != n_head({}) * head_dim({hd}) — \
3484 the draft file's head_count array disagrees with its own tensors",
3485 g.n_head
3486 );
3487 let wg_out = out_f("attn_gate.weight")
3490 .ok_or("step35 draft block has no attn_gate.weight (head-wise gate)")?;
3491 assert_eq!(
3492 wg_out, g.n_head,
3493 "step35 draft blk.{n}: attn_gate out {wg_out} != n_head({})",
3494 g.n_head
3495 );
3496 assert_eq!(
3500 g.n_head_kv, main_cfg.n_head_kv as usize,
3501 "step35 draft blk.{n} KV heads {} != trunk n_head_kv {} — the MTP scratch \
3502 rows are sized from the trunk cfg, so a differing draft KV width would \
3503 write past the row",
3504 g.n_head_kv, main_cfg.n_head_kv
3505 );
3506 eprintln!(
3507 "[mtp-draft] step35 MTP geometry blk.{n}: n_head={} n_head_kv={} n_rot={} \
3508 rope_base={:.0} swa={} window={}",
3509 g.n_head, g.n_head_kv, g.n_rot, g.rope_base, g.swa, g.window
3510 );
3511 Some(g)
3512 }
3513 (true, false) => {
3514 return Err(format!(
3515 "MEMRA_MTP_DRAFT operations are incompatible with the model's \
3516 sliding-gated-MoE program (draft arch {:?})",
3517 g.arch()
3518 )
3519 .into());
3520 }
3521 (false, true) => {
3522 return Err(
3523 "MEMRA_MTP_DRAFT requires sliding-gated-MoE operations but the model does not"
3524 .into(),
3525 );
3526 }
3527 (false, false) => None,
3528 };
3529 if step35.is_none() && !student {
3530 assert_eq!(dcfg.n_head, main_cfg.n_head, "draft n_head != model n_head");
3533 assert_eq!(
3534 dcfg.n_head_kv, main_cfg.n_head_kv,
3535 "draft n_head_kv != model n_head_kv"
3536 );
3537 }
3538
3539 let head_name = draft_head_tensor(|t| src.has(t), n);
3566 let head = load_t(e, &src, &head_name)?;
3567 let head_norm = match load_opt(e, &src, &p("nextn.shared_head_norm.weight"))? {
3568 Some(t) => Some(t),
3569 None => load_opt(e, &src, "output_norm.weight")?,
3570 };
3571
3572 let d2t: Option<Vec<u32>> = g.find("d2t").map(|t| {
3574 let bytes = g.tensor_data(t);
3575 match t.ggml_type {
3576 GgmlType::I32 => bytes
3577 .chunks_exact(4)
3578 .map(|c| i32::from_le_bytes(c.try_into().unwrap()) as u32)
3579 .collect(),
3580 GgmlType::I64 => bytes
3581 .chunks_exact(8)
3582 .map(|c| i64::from_le_bytes(c.try_into().unwrap()) as u32)
3583 .collect(),
3584 other => panic!("d2t must be I32/I64, got {other:?}"),
3585 }
3586 });
3587 if let Some(map) = &d2t {
3588 assert_eq!(
3589 map.len(),
3590 head.out_features(),
3591 "d2t len {} != draft head rows {}",
3592 map.len(),
3593 head.out_features()
3594 );
3595 let n_vocab = main_cfg.n_vocab as u64;
3596 assert!(
3597 map.iter().all(|&t| (t as u64) < n_vocab),
3598 "d2t contains token id >= model n_vocab {n_vocab}"
3599 );
3600 }
3601 let eh_proj = load_t(e, &src, &p("nextn.eh_proj.weight"))?;
3602 assert_eq!(
3605 eh_proj.in_features(),
3606 2 * main_cfg.n_embd as usize,
3607 "eh_proj in dim != 2*n_embd"
3608 );
3609 let geom = if student {
3610 let out_up = load_t(e, &src, &p("nextn.out_up.weight"))?;
3611 let d_inner = eh_proj.out_features();
3612 assert_eq!(
3613 out_up.out_features(),
3614 main_cfg.n_embd as usize,
3615 "out_up out dim != n_embd"
3616 );
3617 assert_eq!(
3618 out_up.in_features(),
3619 d_inner,
3620 "out_up in dim != eh_proj out dim (d_inner)"
3621 );
3622 assert!(
3623 dcfg.n_head >= 1 && dcfg.n_head_kv >= 1 && dcfg.n_head % dcfg.n_head_kv == 0,
3624 "student head counts malformed ({}/{})",
3625 dcfg.n_head,
3626 dcfg.n_head_kv
3627 );
3628 Some(DraftGeom {
3629 d_inner,
3630 n_head: dcfg.n_head as usize,
3631 n_head_kv: dcfg.n_head_kv as usize,
3632 out_up,
3633 })
3634 } else {
3635 None
3636 };
3637 let blk_prefix = format!("blk.{n}.");
3641 let head_src = head_name.strip_prefix(&blk_prefix).unwrap_or(&head_name);
3642 eprintln!(
3643 "[mtp-draft] external draft head: blk.{n}, source={}, head_vocab={}{}{}",
3644 head_src,
3645 head.out_features(),
3646 if d2t.is_some() {
3647 " (trimmed, d2t map)"
3648 } else {
3649 " (full)"
3650 },
3651 match &geom {
3652 Some(g) => format!(
3653 " (student d_inner={} heads={}/{})",
3654 g.d_inner, g.n_head, g.n_head_kv
3655 ),
3656 None => String::new(),
3657 }
3658 );
3659
3660 let mut resident = ResidentPlan::unsharded(e, &src, &dcfg);
3661 let mut step_runtimes = StepParallelRuntimeRegistry::default();
3662 Ok(MtpHead {
3663 enorm: load_t(e, &src, &p("nextn.enorm.weight"))?,
3664 hnorm: load_t(e, &src, &p("nextn.hnorm.weight"))?,
3665 eh_proj,
3666 attn_norm: load_t(e, &src, &p("attn_norm.weight"))?,
3667 post_attn_norm: load_opt(e, &src, &p("post_attention_norm.weight"))?
3668 .or(load_opt(e, &src, &p("ffn_norm.weight"))?)
3669 .expect("draft NextN block needs post_attention_norm or ffn_norm"),
3670 mixer: load_mixer_kind(
3671 e,
3672 &src,
3673 &dcfg,
3674 n,
3675 &draft_block.layer.attention,
3676 &mut step_runtimes,
3677 )?,
3678 ffn: load_ffn(
3679 e,
3680 &src,
3681 &dcfg,
3682 &draft_block.layer.mlp,
3683 n,
3684 None,
3685 &mut resident,
3686 &mut step_runtimes,
3687 )?,
3688 shared_head_norm: head_norm,
3689 shared_head_head: Some(head),
3690 d2t,
3691 d2t_from_target_head: false,
3692 geom,
3693 step35,
3694 })
3695 }
3696}
3697
3698pub struct GemmaAux {
3700 pub rope_freqs: Option<Vec<(usize, CudaSlice<f32>)>>,
3703 pub ones: Vec<(usize, CudaSlice<f32>)>,
3706 pub suppress_d: Option<(CudaSlice<i32>, usize)>,
3709 pub e4b: Option<Gemma4E4bModel>,
3711}
3712
3713impl GemmaAux {
3714 pub fn rope_freqs(&self, e: &Engine) -> Option<&CudaSlice<f32>> {
3715 self.rope_freqs.as_ref().map(|copies| {
3716 let dev = e.ctx().ordinal();
3717 &copies
3718 .iter()
3719 .find(|(d, _)| *d == dev)
3720 .unwrap_or_else(|| panic!("gemma4 rope_freqs has no local copy for device {dev}"))
3721 .1
3722 })
3723 }
3724
3725 pub fn ones(&self, e: &Engine) -> &CudaSlice<f32> {
3726 let dev = e.ctx().ordinal();
3727 &self
3728 .ones
3729 .iter()
3730 .find(|(d, _)| *d == dev)
3731 .unwrap_or_else(|| panic!("gemma4 ones has no local copy for device {dev}"))
3732 .1
3733 }
3734}
3735
3736pub struct Step35Aux {
3739 pub rope_freqs: Option<Vec<(usize, CudaSlice<f32>)>>,
3745}
3746
3747impl Step35Aux {
3748 pub fn rope_freqs(&self, e: &Engine) -> Option<&CudaSlice<f32>> {
3749 self.rope_freqs.as_ref().map(|copies| {
3750 let dev = e.ctx().ordinal();
3751 &copies
3752 .iter()
3753 .find(|(d, _)| *d == dev)
3754 .unwrap_or_else(|| panic!("step35 rope_freqs has no local copy for device {dev}"))
3755 .1
3756 })
3757 }
3758}
3759
3760pub struct HybridModel {
3761 pub cfg: ModelConfig,
3762 pub plan: memra_gguf::model_plan::ModelPlan,
3763 pub rewrite_qualifications: Option<memra_gguf::execution_manifest::RewriteQualifications>,
3764 pub embd: EmbedHost,
3765 pub output_norm: GpuTensor,
3766 pub output: GpuTensor,
3767 pub layers: Vec<HybridLayer>,
3768 pub mtp: Option<MtpHead>, pub mtp_extra: Vec<MtpHead>,
3772 pub dflash_trim: Option<DflashTrimHead>,
3777 pub frspec_src_sha16: Option<String>,
3781 pub embd_gpu: std::sync::OnceLock<cudarc::driver::CudaSlice<u8>>,
3784 pub gemma4_aux: Option<GemmaAux>,
3785 pub step35_aux: Option<Step35Aux>,
3787 pub prime_slabs: std::sync::Mutex<
3795 std::collections::HashMap<
3796 usize,
3797 std::sync::Arc<std::sync::Mutex<crate::hybrid_forward::PrimeSlabs>>,
3798 >,
3799 >,
3800 pub(crate) dspark_vgraphs: std::sync::Mutex<Option<crate::spec::DsparkVerifyGraphs>>,
3813 pub(crate) step_grouped_prefill: std::sync::Mutex<StepEpGroupedPrefill>,
3819 pub(crate) step35_token_graph:
3822 std::sync::Mutex<Option<crate::hybrid_forward::Step35TokenGraphState>>,
3823 pub hyper: Option<crate::hyper::HyperTopology>,
3828 pub hyper_head: Option<crate::hyper::HyperHead>,
3831 pub glm5_dflash: Option<crate::glm_spec::Glm5DflashDrafter>,
3838 pub(crate) draft_state_bytes: std::sync::atomic::AtomicUsize,
3847}
3848
3849impl HybridModel {
3850 pub fn install_rewrite_bundle(
3851 &mut self,
3852 bundle: &std::path::Path,
3853 ) -> Result<(), Box<dyn std::error::Error>> {
3854 self.rewrite_qualifications = Some(
3855 memra_gguf::execution_manifest::RewriteQualifications::load(bundle, &self.plan)
3856 .map_err(|error| format!("rewrite qualification: {error}"))?,
3857 );
3858 Ok(())
3859 }
3860
3861 pub fn rewrite_allowed(&self, surface: memra_gguf::execution_manifest::RewriteSurface) -> bool {
3862 self.rewrite_qualifications
3863 .as_ref()
3864 .is_none_or(|qualifications| qualifications.allows(surface))
3865 }
3866
3867 pub fn record_draft_state_bytes(&self, observed: usize) -> Option<usize> {
3872 use std::sync::atomic::Ordering;
3873 let prev = self
3874 .draft_state_bytes
3875 .fetch_max(observed, Ordering::Relaxed);
3876 (observed > prev).then_some(observed)
3877 }
3878
3879 pub fn draft_session_admission_bytes(&self) -> usize {
3885 self.draft_state_bytes
3886 .load(std::sync::atomic::Ordering::Relaxed)
3887 }
3888
3889 pub fn step_tp_unmaterialized_kv_bytes(
3895 &self,
3896 cache: Option<&crate::cache::Cache>,
3897 capacity: usize,
3898 ) -> Result<Vec<StepTpKvDeviceAdmission>, String> {
3899 if let Some(cache) = cache
3900 && cache.tp_kv.len() < self.layers.len()
3901 {
3902 return Err(format!(
3903 "Step TP admission cache has {} layers, model trunk has {}",
3904 cache.tp_kv.len(),
3905 self.layers.len()
3906 ));
3907 }
3908
3909 let mut by_device: HashMap<usize, usize> = HashMap::new();
3910 for (layer, weights) in self.layers.iter().enumerate() {
3911 let Mixer::Full(attention) = &weights.mixer else {
3912 continue;
3913 };
3914 let Some(tp) = attention
3915 .step_tp_qkv
3916 .as_ref()
3917 .filter(|tp| tp.attention.is_some())
3918 else {
3919 continue;
3920 };
3921 if cache.is_some_and(|cache| cache.tp_kv[layer].is_some()) {
3922 continue;
3923 }
3924 let geometry = self.cfg.full_attention_geometry_at(layer as u32);
3925 let shape = crate::cache::tp_kv_rank_allocation_shape(
3926 geometry.n_head_kv as usize * geometry.head_dim_k as usize,
3927 geometry.n_head_kv as usize * geometry.head_dim_v as usize,
3928 tp.devices.len(),
3929 )?;
3930 let physical_rows = geometry
3931 .window
3932 .map(|window| crate::cache::swa_ring_rows(window as usize, capacity))
3933 .unwrap_or(capacity);
3934 let bytes = shape.allocation_bytes(physical_rows);
3935 for &device in &tp.devices {
3936 let total = by_device.entry(device).or_default();
3937 *total = total.saturating_add(bytes);
3938 }
3939 }
3940
3941 let mut out: Vec<_> = by_device
3942 .into_iter()
3943 .map(|(device, bytes)| StepTpKvDeviceAdmission { device, bytes })
3944 .collect();
3945 out.sort_unstable_by_key(|charge| charge.device);
3946 Ok(out)
3947 }
3948
3949 pub fn step_tp_rank_engine(&self, device: usize) -> Option<&Engine> {
3951 self.layers.iter().find_map(|weights| {
3952 let Mixer::Full(attention) = &weights.mixer else {
3953 return None;
3954 };
3955 let tp = attention.step_tp_qkv.as_ref()?;
3956 let rank = tp
3957 .runtime
3958 .devices()
3959 .iter()
3960 .position(|&rank| rank == device)?;
3961 tp.runtime.rank_engine(rank)
3962 })
3963 }
3964
3965 pub(crate) fn step_tp_runtime_for_layer(
3966 &self,
3967 layer: usize,
3968 ) -> Option<&crate::tp::TpE4m3HostBounce> {
3969 let Mixer::Full(attention) = &self.layers.get(layer)?.mixer else {
3970 return None;
3971 };
3972 let tp = attention.step_tp_qkv.as_ref()?;
3973 tp.attention.as_ref()?;
3974 Some(tp.runtime.as_ref())
3975 }
3976
3977 pub fn decode_batch_program(&self) -> crate::plan_backend::DecodeBatchProgram {
3978 crate::plan_backend::decode_batch_program(&self.plan)
3979 }
3980
3981 pub fn uses_gemma_program(&self) -> bool {
3982 self.decode_batch_program() == crate::plan_backend::DecodeBatchProgram::Gemma
3983 }
3984
3985 pub fn uses_sliding_gated_moe_program(&self) -> bool {
3986 self.decode_batch_program() == crate::plan_backend::DecodeBatchProgram::SlidingGatedMoe
3987 }
3988
3989 pub fn has_plan_operation(&self, operation: memra_gguf::model_plan::OperationKind) -> bool {
3990 self.plan.trunk_operations().contains(&operation)
3991 }
3992
3993 pub fn load(e: &Engine, g: &GgufFile) -> Result<Self, Box<dyn std::error::Error>> {
3995 Self::load_from_source(e, &GgufSource(g))
3996 }
3997
3998 pub fn load_without_mtp(e: &Engine, g: &GgufFile) -> Result<Self, Box<dyn std::error::Error>> {
4001 Self::load_from_source_impl(e, &GgufSource(g), false)
4002 }
4003
4004 pub fn load_from_source(
4008 e: &Engine,
4009 src: &dyn TensorSource,
4010 ) -> Result<Self, Box<dyn std::error::Error>> {
4011 Self::load_from_source_impl(e, src, true)
4012 }
4013
4014 pub fn load_from_source_without_mtp(
4016 e: &Engine,
4017 src: &dyn TensorSource,
4018 ) -> Result<Self, Box<dyn std::error::Error>> {
4019 Self::load_from_source_impl(e, src, false)
4020 }
4021
4022 fn load_from_source_impl(
4023 e: &Engine,
4024 src: &dyn TensorSource,
4025 load_mtp: bool,
4026 ) -> Result<Self, Box<dyn std::error::Error>> {
4027 let cfg = src.try_config().map_err(std::io::Error::other)?;
4028 let plan = match memra_gguf::model_packs::for_config(&cfg) {
4029 Some(pack) => pack.compile_plan(&cfg)?,
4030 None => memra_gguf::model_plan::ModelPlan::compile(&cfg)?,
4031 };
4032 let auto_parallel = prepare_auto_parallel(src, &cfg, &plan)?;
4033 let batch_program = crate::plan_backend::decode_batch_program(&plan);
4034 let gemma_program = batch_program == crate::plan_backend::DecodeBatchProgram::Gemma;
4035 let sliding_gated_moe_program =
4036 batch_program == crate::plan_backend::DecodeBatchProgram::SlidingGatedMoe;
4037 if matches!(
4038 src.expert_activation_precision(),
4039 memra_gguf::source::ExpertActivationPrecision::Bf16
4040 ) {
4041 eprintln!(
4042 "[w4a16] artifact contract accepted: expert_weights=nvfp4 \
4043 expert_activations=bf16-rounded q8_expert_program=disabled"
4044 );
4045 }
4046 if sliding_gated_moe_program {
4051 crate::arm_step37_serving_defaults();
4052 }
4053 cfg.validate_attention_gate_layout()?;
4058 if cfg.sigmoid_router().is_some() {
4065 let host_oracle = std::env::var("MEMRA_SIG_ROUTER").as_deref() == Ok("0");
4066 match crate::sigrouter_contract::verify_host_expf() {
4067 Ok(()) => {}
4068 Err(e) if host_oracle => return Err(e.into()),
4069 Err(e) => eprintln!(
4070 "[sigrouter] WARN: host expf probe mismatch ({e}); device routing is \
4071 unaffected, but host-oracle replay/comparison cells are invalid on this host"
4072 ),
4073 }
4074 }
4075 if std::env::var("MEMRA_DRAFT").is_ok() && std::env::var("MEMRA_MMQ_SK").is_err() {
4084 let force = if cfg.n_embd >= 3500 { 0i8 } else { -1i8 };
4085 crate::MMQ_SK_FORCE.store(force, std::sync::atomic::Ordering::Relaxed);
4086 }
4087 crate::KV_FP8_FORCE.store(0, std::sync::atomic::Ordering::Relaxed);
4091
4092 let n_trunk = (cfg.n_layer - cfg.nextn_predict_layers) as usize;
4097 let mtp_skip_requested = load_mtp
4107 && match std::env::var("MEMRA_MTP_SKIP").ok().as_deref() {
4108 None | Some("") | Some("0") => false,
4109 Some("1") => true,
4110 Some(other) => {
4111 return Err(format!(
4112 "MEMRA_MTP_SKIP={other:?}: expected 1 (skip the embedded MTP block) or \
4113 0/unset (load it); refusing to guess"
4114 )
4115 .into());
4116 }
4117 };
4118 if mtp_skip_requested && std::env::var("MEMRA_MTP_DRAFT").is_ok_and(|p| !p.is_empty()) {
4119 return Err(
4120 "MEMRA_MTP_SKIP=1 together with MEMRA_MTP_DRAFT is contradictory: the skip \
4121 removes the MTP head to reclaim VRAM while MEMRA_MTP_DRAFT attaches an \
4122 external MTP head for MTP spec decode; unset one"
4123 .into(),
4124 );
4125 }
4126 if mtp_skip_requested && cfg.nextn_predict_layers > 0 {
4127 let prefixes: Vec<String> = (0..cfg.nextn_predict_layers)
4132 .map(|off| format!("blk.{}.", n_trunk as u32 + off))
4133 .collect();
4134 let skipped_bytes: Option<u64> = src.gguf().map(|g| {
4135 g.tensors
4136 .iter()
4137 .filter(|t| prefixes.iter().any(|p| t.name.starts_with(p.as_str())))
4138 .map(|t| t.n_bytes)
4139 .sum()
4140 });
4141 eprintln!(
4142 "[mtp-skip] MEMRA_MTP_SKIP=1: skipping {} embedded MTP/NextN block(s) \
4143 blk.{}..=blk.{} ({}); MTP spec decode is unavailable for this model \
4144 (dspark/DFlash2 drafting keeps its trimmed head via the MEMRA_FRSPEC_TRIM stub)",
4145 cfg.nextn_predict_layers,
4146 n_trunk,
4147 n_trunk as u32 + cfg.nextn_predict_layers - 1,
4148 match skipped_bytes {
4149 Some(b) => format!("~{} MiB of weights not loaded", b >> 20),
4150 None => "size unknown: non-GGUF source".to_string(),
4151 },
4152 );
4153 }
4154 let mtp_skip_trim_d2t: Option<(Vec<u32>, String)> = if mtp_skip_requested
4169 && cfg.nextn_predict_layers > 0
4170 && !crate::model::full_prec_enabled()
4171 {
4172 match std::env::var("MEMRA_FRSPEC_TRIM") {
4173 Ok(path) if !path.is_empty() => {
4174 let path = memra_gguf::hf::resolve_arg(&path)
4175 .map_err(|err| format!("MEMRA_FRSPEC_TRIM={path:?}: {err}"))?;
4176 let own_head_name = frspec_trim_own_head_name(n_trunk);
4177 if src.has(&own_head_name) {
4178 return Err(format!(
4179 "MEMRA_MTP_SKIP=1 with MEMRA_FRSPEC_TRIM: this artifact ships its \
4180 own MTP-block lm_head ({own_head_name}), so the trimmed draft rows \
4181 live in the block being skipped; gathering trunk rows instead is \
4182 the wrong-head bug (acceptance 0/248 receipt, \
4183 frspec_trim_own_head_name). Unset MEMRA_MTP_SKIP or \
4184 MEMRA_FRSPEC_TRIM"
4185 )
4186 .into());
4187 }
4188 if !src.has("output.weight") && !src.has("token_embd.weight") {
4189 return Err("MEMRA_MTP_SKIP=1 with MEMRA_FRSPEC_TRIM: model has no \
4190 output.weight (or tied token_embd.weight) to gather trimmed draft \
4191 rows from"
4192 .into());
4193 }
4194 let d2t = frspec_read_d2t(&path)?;
4195 if d2t.is_empty() {
4196 return Err(format!(
4197 "MEMRA_MTP_SKIP=1 with MEMRA_FRSPEC_TRIM={path}: the rank artifact \
4198 yields an EMPTY d2t list, so no stub draft head can be built; fix \
4199 the artifact or unset MEMRA_MTP_SKIP"
4200 )
4201 .into());
4202 }
4203 let sha16 = sha256_file_hex(std::path::Path::new(&path), 8)?;
4204 Some((d2t, sha16))
4205 }
4206 _ => None,
4207 }
4208 } else {
4209 None
4210 };
4211 let mut frspec_src_sha16: Option<String> =
4215 mtp_skip_trim_d2t.as_ref().map(|(_, s)| s.clone());
4216 if let Some(fence) = crate::pp::pp_cuts(n_trunk) {
4217 let pipeline = crate::plan_backend::PIPELINE
4218 .trunk_capabilities(&plan)
4219 .pipeline;
4220 let qualified_gemma_pp2 = gemma_program && fence.len() == 3;
4224 if !pipeline.supported && !qualified_gemma_pp2 {
4225 return Err(format!(
4226 "pipeline placement is unsupported for plan operations {:?}; blockers={:?}",
4227 plan.trunk_operations(),
4228 pipeline.blockers,
4229 )
4230 .into());
4231 }
4232 let illegal = illegal_pipeline_cuts(&fence, &plan.partition_boundaries);
4233 if !illegal.is_empty() {
4234 return Err(format!(
4235 "pipeline placement cuts {illegal:?} split outside ModelPlan legal boundaries {:?}",
4236 plan.partition_boundaries,
4237 )
4238 .into());
4239 }
4240 }
4241 crate::pp::init_model_transport(e, &cfg, n_trunk)?;
4242 let step_parallel =
4243 prepare_step_parallel_load(e, src, &cfg, n_trunk, auto_parallel.as_ref())?;
4244 let glm5_tp = if crate::glm5_tp::glm5_tp_armed() {
4248 use memra_gguf::model_plan::{AttentionPlan, MlpPlan};
4249 let moe = cfg.moe.as_ref().ok_or(
4250 "MEMRA_GLM5_TP requires a MoE model (glm5_next); this plan carries no MoE \
4251 metadata",
4252 )?;
4253 let mut layer_class = Vec::with_capacity(n_trunk);
4254 let mut layer_is_moe = Vec::with_capacity(n_trunk);
4255 let (mut kda_heads, mut kda_head_dim, mut mla_heads) = (0usize, 0usize, 0usize);
4256 for (il, lp) in plan.layers.iter().take(n_trunk).enumerate() {
4257 match &lp.attention {
4258 AttentionPlan::KimiDeltaNet(k) => {
4259 layer_class.push(crate::glm5_tp::Glm5LayerClass::Kda);
4260 kda_heads = k.num_heads as usize;
4261 kda_head_dim = k.head_dim as usize;
4262 }
4263 AttentionPlan::Mla(memra_gguf::model_plan::MlaAttentionPlan::LatentKv {
4264 query_heads,
4265 ..
4266 }) => {
4267 layer_class.push(crate::glm5_tp::Glm5LayerClass::Mla);
4268 mla_heads = *query_heads as usize;
4269 }
4270 other => {
4271 return Err(format!(
4272 "MEMRA_GLM5_TP requires a glm5_next-class plan (KDA/MLA mixers): \
4273 trunk layer {il} declares {other:?}"
4274 )
4275 .into());
4276 }
4277 }
4278 layer_is_moe.push(matches!(&lp.mlp, MlpPlan::Moe(_)));
4279 }
4280 let view = crate::glm5_tp::Glm5TpModelView {
4281 trunk_layers: n_trunk,
4282 layer_class,
4283 layer_is_moe,
4284 kda_heads,
4285 kda_head_dim,
4286 mla_heads,
4287 n_routed_experts: moe.expert_count as usize,
4288 top_k: moe.expert_used_count as usize,
4289 };
4290 crate::glm5_tp::prepare_glm5_tp_load(e, &view)?
4291 } else {
4292 let glm5_class = plan.layers.iter().take(n_trunk).any(|lp| {
4299 matches!(
4300 lp.attention,
4301 memra_gguf::model_plan::AttentionPlan::KimiDeltaNet(_)
4302 )
4303 });
4304 let ep_map_armed = crate::ep_map::ep_map_env()?;
4305 if let Some((flag, _)) = ep_map_armed
4306 && glm5_class
4307 {
4308 return Err(format!(
4309 "{flag} is set but MEMRA_GLM5_TP is off: the map cannot \
4310 engage, and a placement that silently reverts to the even split is \
4311 refused by name (unset one of the two)"
4312 )
4313 .into());
4314 }
4315 if glm5_class {
4323 for (armed, flag) in [crate::ep_diet_armed(), crate::ep_grouped_prime_armed()] {
4324 if armed {
4325 return Err(format!(
4326 "{flag}=1 is set but MEMRA_GLM5_TP is off: the EP dispatch \
4327 diet only exists inside the TP-2 EP walk and cannot engage \
4328 (unset one of the two)"
4329 )
4330 .into());
4331 }
4332 }
4333 }
4334 None
4335 };
4336 let embd = EmbedHost::from_source(src, "token_embd.weight");
4337 let e_head = crate::pp::layer_engine(e, n_trunk, n_trunk - 1)?;
4341 let output_norm = load_t(e_head, src, "output_norm.weight")?;
4342 let mut output = if src.has("output.weight") {
4344 load_t(e_head, src, "output.weight")?
4345 } else {
4346 load_t(e_head, src, "token_embd.weight")?
4347 };
4348 let mut resident = ResidentPlan::pp(e, src, &cfg, n_trunk)?;
4349 resident.exclude_distributed_expert_layers(
4350 step_parallel
4351 .ep_specs
4352 .iter()
4353 .map(|spec| spec.layer)
4354 .chain(step_parallel.tp_specs.iter().map(|spec| spec.layer)),
4355 );
4356 let mut step_runtimes = StepParallelRuntimeRegistry::with_config(step_parallel);
4357
4358 let gguf: Option<&GgufFile> = src.gguf();
4365 let mut spill: Option<crate::spill::SpillCtx> = if cfg
4368 .moe
4369 .as_ref()
4370 .is_some_and(|m| m.expert_count > 0)
4371 && crate::spill::disk_tier_enabled()
4372 && gguf.is_some()
4373 {
4374 let budget = crate::spill::MemBudget::probe(e)?;
4375 #[allow(clippy::unnecessary_unwrap)]
4376 let ctx = crate::spill::SpillCtx::open(gguf.unwrap(), &budget)?;
4378 eprintln!(
4379 "[spill] disk tier ON: free_vram={} MiB free_pinnable_ram={} MiB (MemAvailable*resolved_frac)",
4380 budget.free_vram >> 20,
4381 budget.free_pinnable_ram >> 20
4382 );
4383 Some(ctx)
4384 } else {
4385 None
4386 };
4387
4388 let hyper = crate::hyper::HyperTopology::from_plan(&plan)?;
4395 let hyper_head = match hyper.as_ref() {
4396 Some(topology) => {
4397 crate::hyper::HyperHead::load(e_head, src, topology, cfg.n_embd as usize)?
4398 }
4399 None => None,
4400 };
4401 let mut layers = Vec::with_capacity(n_trunk);
4402 for il in 0..n_trunk as u32 {
4403 let p = |s: &str| format!("blk.{il}.{s}");
4404 let layer_plan = plan
4405 .layers
4406 .get(il as usize)
4407 .ok_or_else(|| format!("ModelPlan has no trunk layer {il}"))?;
4408 let e = crate::pp::layer_engine(e, n_trunk, il as usize)?;
4412 layers.push(HybridLayer {
4414 attn_norm: load_t(e, src, &p("attn_norm.weight"))?,
4415 post_attn_norm: load_opt(e, src, &p("post_attention_norm.weight"))?
4416 .or(load_opt(e, src, &p("ffn_norm.weight"))?)
4417 .expect("need post_attention_norm or ffn_norm"),
4418 mixer: {
4419 let g4_shared = cfg.gemma4.as_ref().map(|g| g.shared_kv_layers).unwrap_or(0);
4423 let kv_from = n_trunk as u32 - g4_shared;
4424 if g4_shared > 0
4425 && il >= kv_from
4426 && !src.has(&format!("blk.{il}.attn_k.weight"))
4427 {
4428 let g4 = cfg.gemma4.as_ref().unwrap();
4429 let swa = g4.swa_pattern.get(il as usize).copied().unwrap_or(true);
4430 let tgt = kv_from - if swa { 2 } else { 1 };
4431 let tp = |s: &str| format!("blk.{tgt}.{s}");
4432 Mixer::Full(FullAttnLayer {
4433 wq: load_t(e, src, &p("attn_q.weight"))?,
4434 wk: load_t(e, src, &tp("attn_k.weight"))?,
4435 wv: load_t(e, src, &tp("attn_v.weight"))?,
4436 wo: load_t(e, src, &p("attn_output.weight"))?,
4437 q_norm: load_t(e, src, &p("attn_q_norm.weight"))?,
4438 k_norm: load_t(e, src, &tp("attn_k_norm.weight"))?,
4439 attn_gate: None, step_tp_qkv: None,
4441 })
4442 } else {
4443 load_mixer_kind(
4444 e,
4445 src,
4446 &cfg,
4447 il,
4448 &layer_plan.attention,
4449 &mut step_runtimes,
4450 )?
4451 }
4452 },
4453 ffn: load_ffn(
4454 e,
4455 src,
4456 &cfg,
4457 &layer_plan.mlp,
4458 il,
4459 spill.as_mut().map(|c| (gguf.unwrap(), c)),
4460 &mut resident,
4461 &mut step_runtimes,
4462 )?,
4463 gemma4: if gemma_program {
4464 let scalar = |n: &str| -> f32 {
4465 let t = src.find(&p(n)).unwrap_or_else(|| panic!("missing {n}"));
4466 memra_gguf::dequant::dequantize(t.ggml_type, &t.bytes, 1)[0]
4467 };
4468 let vecf = |n: &str| -> Vec<f32> {
4469 let t = src.find(&p(n)).unwrap_or_else(|| panic!("missing {n}"));
4470 memra_gguf::dequant::dequantize(
4471 t.ggml_type,
4472 &t.bytes,
4473 t.ne.iter().product::<u64>() as usize,
4474 )
4475 };
4476 let moe_bits = if src.find(&p("ffn_gate_inp.scale")).is_some() {
4477 Some(crate::hybrid::Gemma4MoeBits {
4478 post_ffw_norm_1: load_t(e, src, &p("post_ffw_norm_1.weight"))?,
4479 pre_ffw_norm_2: load_t(e, src, &p("pre_ffw_norm_2.weight"))?,
4480 post_ffw_norm_2: load_t(e, src, &p("post_ffw_norm_2.weight"))?,
4481 shared_gate: load_t(e, src, &p("ffn_gate.weight"))?,
4482 shared_up: load_t(e, src, &p("ffn_up.weight"))?,
4483 shared_down: load_t(e, src, &p("ffn_down.weight"))?,
4484 router_scale_pre: {
4485 let inv = 1.0 / (cfg.n_embd as f32).sqrt();
4486 let v: Vec<f32> =
4487 vecf("ffn_gate_inp.scale").iter().map(|x| x * inv).collect();
4488 e.htod(&v)?
4489 },
4490 per_expert_scale: vecf("ffn_down_exps.scale"),
4491 per_expert_scale_d: e.htod(&vecf("ffn_down_exps.scale"))?,
4492 })
4493 } else {
4494 None
4495 };
4496 let e4b = if src.has(&p("inp_gate.weight")) {
4498 let g4 = cfg.gemma4.as_ref().unwrap();
4499 let kv_from = n_trunk as u32 - g4.shared_kv_layers;
4500 let kv_share = if g4.shared_kv_layers > 0 && il >= kv_from {
4501 let swa = g4.swa_pattern.get(il as usize).copied().unwrap_or(true);
4502 Some(kv_from - if swa { 2 } else { 1 })
4503 } else {
4504 None
4505 };
4506 Some(crate::hybrid::Gemma4E4bLayer {
4507 inp_gate: load_t(e, src, &p("inp_gate.weight"))?,
4508 proj: load_t(e, src, &p("proj.weight"))?,
4509 post_norm: load_t(e, src, &p("post_norm.weight"))?,
4510 kv_share,
4511 qkv_cat: None, })
4513 } else {
4514 None
4515 };
4516 Some(Gemma4LayerBits {
4517 ffn_norm: load_t(e, src, &p("ffn_norm.weight"))?,
4518 post_ffw_norm: load_t(e, src, &p("post_ffw_norm.weight"))?,
4519 moe_bits,
4520 layer_scale: scalar("layer_output_scale.weight"),
4521 e4b,
4522 })
4523 } else {
4524 None
4525 },
4526 hyper: match hyper.as_ref() {
4527 Some(topology) => Some(crate::hyper::HyperLayer::load(
4528 e,
4529 src,
4530 il,
4531 topology,
4532 cfg.n_embd as usize,
4533 )?),
4534 None => None,
4535 },
4536 });
4537 if let Some(tp_plan) = &glm5_tp
4540 && tp_plan.layers.contains(&(il as usize))
4541 {
4542 let mut layer = layers.pop().expect("layer just pushed");
4543 layer.mixer = match layer.mixer {
4544 Mixer::Kda(la) => {
4545 Mixer::Kda(crate::glm5_tp::shard_kda_layer(e, &tp_plan.rt, la)?)
4546 }
4547 Mixer::Mla(la) => {
4548 Mixer::Mla(crate::glm5_tp::shard_mla_layer(e, &tp_plan.rt, la)?)
4549 }
4550 _ => {
4551 return Err(format!(
4552 "MEMRA_GLM5_TP selected layer {il}, whose loaded mixer is not \
4553 KDA/MLA — preflight and loader disagree (wiring bug)"
4554 )
4555 .into());
4556 }
4557 };
4558 if let Ffn::Moe(m) = &mut layer.ffn {
4559 let placement = match &tp_plan.ep_map {
4563 Some(map) => Some(
4564 map.layers
4565 .get(&(il as usize))
4566 .ok_or_else(|| {
4567 format!(
4568 "glm5-tp EP: preflight-validated map lost layer {il} \
4569 (wiring bug)"
4570 )
4571 })?
4572 .as_slice(),
4573 ),
4574 None => None,
4575 };
4576 crate::glm5_tp::arm_moe_ep(e, &tp_plan.rt, m, placement)?;
4577 }
4578 layers.push(layer);
4579 }
4580 }
4581
4582 let external_mtp_requested =
4586 load_mtp && std::env::var("MEMRA_MTP_DRAFT").is_ok_and(|path| !path.is_empty());
4587 let trim_mtp_requested = load_mtp
4588 && !crate::model::full_prec_enabled()
4589 && std::env::var("MEMRA_FRSPEC_TRIM").is_ok_and(|path| !path.is_empty());
4590 let _ = trim_mtp_requested;
4601 let glm5_mtp_requested =
4611 !cfg.arch.is_glm5_next() || std::env::var("MEMRA_GLM5_MTP").as_deref() == Ok("1");
4612 let embedded_head_count =
4615 if external_mtp_requested || !glm5_mtp_requested || mtp_skip_requested {
4616 0
4617 } else {
4618 cfg.nextn_predict_layers
4619 };
4620 if cfg.arch.is_glm5_next()
4621 && glm5_mtp_requested
4622 && !mtp_skip_requested
4623 && cfg.nextn_predict_layers > 0
4624 {
4625 eprintln!("[mtp-glm5] MEMRA_GLM5_MTP=1: loading the glm5_next NextN block");
4626 }
4627 let embedded_head_count = match std::env::var("MEMRA_MTP_HEADS")
4632 .ok()
4633 .and_then(|v| v.parse::<u32>().ok())
4634 .filter(|&n| n > 0)
4635 {
4636 Some(cap) if cap < embedded_head_count => {
4637 eprintln!(
4638 "[mtp-chain] MEMRA_MTP_HEADS={cap}: capping the embedded chain from \
4639 {embedded_head_count} heads (measurement knob)"
4640 );
4641 cap
4642 }
4643 _ => embedded_head_count,
4644 };
4645 let mut embedded_mtp = Vec::new();
4646 if load_mtp && embedded_head_count > 0 {
4647 for offset in 0..embedded_head_count {
4648 let n = n_trunk as u32 + offset;
4649 let e = crate::pp::layer_engine(e, n_trunk, n as usize)?;
4655 let p = |s: &str| format!("blk.{n}.{s}");
4656 let mtp_plan = plan
4657 .mtp_blocks
4658 .iter()
4659 .find(|block| block.layer.index == n)
4660 .ok_or_else(|| format!("ModelPlan has no embedded MTP block {n}"))?;
4661 if !src.has(&p("nextn.eh_proj.weight")) {
4662 if offset == 0 {
4663 break;
4664 }
4665 return Err(format!(
4666 "embedded MTP chain declares {} heads but blk.{n} has no \
4667 nextn.eh_proj.weight",
4668 cfg.nextn_predict_layers
4669 )
4670 .into());
4671 }
4672 embedded_mtp.push(MtpHead {
4673 enorm: load_t(e, src, &p("nextn.enorm.weight"))?,
4674 hnorm: load_t(e, src, &p("nextn.hnorm.weight"))?,
4675 eh_proj: load_t(e, src, &p("nextn.eh_proj.weight"))?,
4676 attn_norm: load_t(e, src, &p("attn_norm.weight"))?,
4677 post_attn_norm: load_opt(e, src, &p("post_attention_norm.weight"))?
4678 .or(load_opt(e, src, &p("ffn_norm.weight"))?)
4679 .expect("MTP block needs post_attention_norm or ffn_norm"),
4680 mixer: load_mixer_kind(
4681 e,
4682 src,
4683 &cfg,
4684 n,
4685 &mtp_plan.layer.attention,
4686 &mut step_runtimes,
4687 )?,
4688 ffn: load_ffn(
4689 e,
4690 src,
4691 &cfg,
4692 &mtp_plan.layer.mlp,
4693 n,
4694 spill.as_mut().map(|c| (gguf.unwrap(), c)),
4695 &mut resident,
4696 &mut step_runtimes,
4697 )?,
4698 shared_head_norm: load_opt(e, src, &p("nextn.shared_head_norm.weight"))?,
4699 shared_head_head: load_mtp_head_maybe_nvfp4(
4708 e,
4709 src,
4710 &p("nextn.shared_head_head.weight"),
4711 )?
4712 .or(load_opt(e, src, &p("nextn.shared_head.weight"))?),
4713 d2t: None,
4714 d2t_from_target_head: false,
4715 geom: None,
4716 step35: if sliding_gated_moe_program {
4717 Some(Step35MtpGeom::from_plan(&mtp_plan.layer)?)
4718 } else {
4719 None
4720 },
4721 });
4722 }
4723 }
4724 let mut embedded_mtp = embedded_mtp.into_iter();
4725 let mut mtp = embedded_mtp.next();
4726 let mut mtp_extra: Vec<MtpHead> = embedded_mtp.collect();
4727
4728 mtp = if load_mtp {
4732 match std::env::var("MEMRA_MTP_DRAFT") {
4733 Ok(path) if !path.is_empty() => {
4734 eprintln!("[mtp-draft] loading external MTP draft: {path}");
4735 let dg = GgufFile::open(&path)?;
4736 mtp_extra.clear();
4737 Some(MtpHead::load_draft(e, &dg, &cfg)?)
4738 }
4739 _ => mtp,
4740 }
4741 } else {
4742 None
4743 };
4744
4745 let trim_env = if load_mtp {
4756 std::env::var("MEMRA_FRSPEC_TRIM")
4757 } else {
4758 Err(std::env::VarError::NotPresent)
4759 };
4760 if crate::model::full_prec_enabled()
4761 && trim_env.as_deref().map(|p| !p.is_empty()).unwrap_or(false)
4762 {
4763 eprintln!(
4764 "[frspec-trim] DISABLED under MEMRA_FULL_PREC — using the natural full MTP head"
4765 );
4766 }
4767 mtp = match (
4768 if crate::model::full_prec_enabled() {
4769 Err(std::env::VarError::NotPresent)
4770 } else {
4771 trim_env
4772 },
4773 mtp,
4774 ) {
4775 (Ok(path), Some(mut head)) if !path.is_empty() => {
4776 let e = crate::pp::layer_engine(e, n_trunk, n_trunk)?;
4780 let path = memra_gguf::hf::resolve_arg(&path)
4784 .map_err(|err| format!("MEMRA_FRSPEC_TRIM={path:?}: {err}"))?;
4785 let d2t: Vec<u32> = frspec_read_d2t(&path)?;
4789 frspec_src_sha16 = Some(sha256_file_hex(std::path::Path::new(&path), 8)?);
4790 let own_head_name = frspec_trim_own_head_name(n_trunk);
4799 let own_head = src.find(&own_head_name);
4800 let from_own_head = own_head.is_some();
4801 let v = own_head
4802 .or_else(|| src.find("output.weight"))
4803 .or_else(|| src.find("token_embd.weight"))
4804 .expect("model has no output.weight for FR-Spec trim");
4805 frspec_validate_ranks(
4810 &d2t,
4811 v.ne[1] as usize,
4812 &format!(
4813 "MEMRA_FRSPEC_TRIM={path} (sha16={}) on {}",
4814 frspec_src_sha16.as_deref().unwrap_or("unknown"),
4815 if from_own_head {
4816 own_head_name.as_str()
4817 } else {
4818 "main output.weight"
4819 }
4820 ),
4821 )?;
4822 let (trimmed, nvfp4_sizes) = frspec_gather_trimmed_head(
4842 e,
4843 &v,
4844 &d2t,
4845 std::env::var("MEMRA_FRSPEC_TRIM_NVFP4").as_deref() == Ok("1"),
4846 match src.find("output.scale") {
4848 Some(sv) => f32::from_le_bytes(sv.bytes[..4].try_into().unwrap()),
4849 None => 1.0,
4850 },
4851 )?;
4852 match nvfp4_sizes {
4853 Some((nvfp4_bytes, gathered_bytes)) => eprintln!(
4854 "[frspec-trim] self-trimmed head: {} rows of {} re-quantized BF16 -> NVFP4 \
4855 ({} MiB, was {} MiB)",
4856 d2t.len(),
4857 if from_own_head {
4858 own_head_name.as_str()
4859 } else {
4860 "main output.weight"
4861 },
4862 nvfp4_bytes >> 20,
4863 gathered_bytes >> 20,
4864 ),
4865 None => eprintln!(
4866 "[frspec-trim] self-trimmed head: {} rows of {} ({:?})",
4867 d2t.len(),
4868 if from_own_head {
4869 own_head_name.as_str()
4870 } else {
4871 "main output.weight"
4872 },
4873 v.ggml_type
4874 ),
4875 }
4876 head.shared_head_head = Some(trimmed);
4877 head.d2t = Some(d2t);
4878 head.d2t_from_target_head = !from_own_head;
4881 Some(head)
4882 }
4883 (_, m) => m,
4884 };
4885 let mut dflash_trim: Option<DflashTrimHead> = match mtp_skip_trim_d2t {
4896 Some((d2t, src_sha16)) => {
4897 let v = src
4898 .find("output.weight")
4899 .or_else(|| src.find("token_embd.weight"))
4900 .ok_or("model has no output.weight for FR-Spec trim")?;
4901 frspec_validate_ranks(
4902 &d2t,
4903 v.ne[1] as usize,
4904 &format!("MEMRA_MTP_SKIP=1 with MEMRA_FRSPEC_TRIM (sha16={src_sha16})"),
4905 )?;
4906 let (head, nvfp4_sizes) = frspec_gather_trimmed_head(
4907 e,
4908 &v,
4909 &d2t,
4910 std::env::var("MEMRA_FRSPEC_TRIM_NVFP4").as_deref() == Ok("1"),
4911 match src.find("output.scale") {
4912 Some(sv) => f32::from_le_bytes(sv.bytes[..4].try_into().unwrap()),
4913 None => 1.0,
4914 },
4915 )?;
4916 eprintln!(
4917 "[mtp-skip] FR-Spec stub draft head built: {} rows of main output.weight \
4918 ({}); DFlash2 trim serves without the embedded MTP block",
4919 d2t.len(),
4920 match nvfp4_sizes {
4921 Some((nvfp4_bytes, gathered_bytes)) => format!(
4922 "re-quantized BF16 -> NVFP4, {} MiB, was {} MiB",
4923 nvfp4_bytes >> 20,
4924 gathered_bytes >> 20
4925 ),
4926 None => format!("{:?}", v.ggml_type),
4927 },
4928 );
4929 Some(DflashTrimHead {
4930 head,
4931 d2t,
4932 src_sha16,
4933 })
4934 }
4935 None => None,
4936 };
4937 if let Some(d2t) = mtp.as_ref().and_then(|head| head.d2t.clone()) {
4950 let want_nvfp4_env = std::env::var("MEMRA_FRSPEC_TRIM_NVFP4").as_deref() == Ok("1");
4951 let mut kept = 0usize;
4952 let e = crate::pp::layer_engine(e, n_trunk, n_trunk)?;
4955 for (i, head) in mtp_extra.iter_mut().enumerate() {
4956 let name = frspec_trim_own_head_name(n_trunk + 1 + i);
4957 let Some(v) = src.find(&name) else { break };
4958 let out_f = v.ne[1] as usize;
4959 let row_bytes = v.bytes.len() / out_f;
4960 if d2t.iter().any(|&t| (t as usize) >= out_f) {
4961 break;
4962 }
4963 let mut gathered = Vec::with_capacity(d2t.len() * row_bytes);
4964 for &t in &d2t {
4965 let off = t as usize * row_bytes;
4966 gathered.extend_from_slice(&v.bytes[off..off + row_bytes]);
4967 }
4968 let want_nvfp4 =
4969 want_nvfp4_env && matches!(v.ggml_type, GgmlType::BF16) && v.ne[0] % 64 == 0;
4970 let trimmed = if want_nvfp4 {
4971 let vals: Vec<f32> = gathered
4972 .chunks_exact(2)
4973 .map(|c| f32::from_bits((u16::from_le_bytes([c[0], c[1]]) as u32) << 16))
4974 .collect();
4975 let blocks = memra_gguf::nvfp4_repack::f32_to_nvfp4(&vals);
4976 GpuTensor::from_quant_bytes(
4977 e,
4978 &blocks,
4979 GgmlType::NVFP4,
4980 v.ne[0],
4981 d2t.len() as u64,
4982 1.0,
4983 )?
4984 } else {
4985 match v.ggml_type {
4986 GgmlType::BF16 => GpuTensor::FloatBf16 {
4987 data: e.htod_bytes(&gathered)?,
4988 ne: vec![v.ne[0], d2t.len() as u64],
4989 },
4990 GgmlType::F32 => GpuTensor::Float {
4991 data: e.htod(
4992 &gathered
4993 .chunks_exact(4)
4994 .map(|c| f32::from_le_bytes(c.try_into().unwrap()))
4995 .collect::<Vec<f32>>(),
4996 )?,
4997 ne: vec![v.ne[0], d2t.len() as u64],
4998 },
4999 _ => GpuTensor::from_quant_bytes(
5000 e,
5001 &gathered,
5002 v.ggml_type,
5003 v.ne[0],
5004 d2t.len() as u64,
5005 1.0,
5006 )?,
5007 }
5008 };
5009 head.shared_head_head = Some(trimmed);
5010 head.d2t = Some(d2t.clone());
5011 head.d2t_from_target_head = false;
5012 kept += 1;
5013 }
5014 let dropped = mtp_extra.len() - kept;
5015 mtp_extra.truncate(kept);
5016 eprintln!(
5017 "[frspec-trim] per-head trim: {kept} extra chain head(s) gathered from their own \
5018 blocks{}",
5019 if dropped > 0 {
5020 format!(" ({dropped} dropped: no own-head tensor)")
5021 } else {
5022 String::new()
5023 }
5024 );
5025 }
5026 if !mtp_extra.is_empty() {
5027 if plan.draft_source != memra_gguf::model_plan::DraftSourcePlan::Embedded
5028 || plan.mtp_blocks.len() != 1 + mtp_extra.len()
5029 || plan
5030 .mtp_blocks
5031 .iter()
5032 .any(|block| !matches!(block.layer.mlp, MlpPlan::Dense(_)))
5033 || mtp
5034 .iter()
5035 .chain(mtp_extra.iter())
5036 .any(|head| !matches!(head.ffn, Ffn::Dense { .. }))
5037 {
5038 return Err(
5039 "multi-head MTP requires embedded dense canonical blocks and matching loaded heads"
5040 .into(),
5041 );
5042 }
5043 eprintln!(
5044 "[mtp-draft] embedded chain: heads={} blocks={}..={} scratch=per-head",
5045 1 + mtp_extra.len(),
5046 n_trunk,
5047 n_trunk + mtp_extra.len()
5048 );
5049 }
5050
5051 let glm5_dflash = match std::env::var("MEMRA_GLM5_DFLASH") {
5067 Ok(spec) if !spec.is_empty() && cfg.arch.is_glm5_next() => {
5068 let dpath = memra_gguf::hf::resolve_arg(&spec)
5069 .map_err(|err| format!("MEMRA_GLM5_DFLASH={spec:?}: {err}"))?;
5070 let de = crate::pp::layer_engine(e, n_trunk, n_trunk)?;
5071 Some(crate::dflash::load_drafter(
5072 de,
5073 std::path::Path::new(&dpath),
5074 "MEMRA_GLM5_DFLASH",
5075 n_trunk,
5076 cfg.n_embd as usize,
5077 output.out_features(),
5078 )?)
5079 }
5080 _ => None,
5081 };
5082
5083 if cfg.arch.is_glm5_next()
5112 && glm5_dflash.is_some()
5113 && dflash_trim.is_none()
5114 && !crate::model::full_prec_enabled()
5115 && !mtp
5116 .as_ref()
5117 .is_some_and(|m| m.d2t_from_target_head && m.d2t.is_some())
5118 && let Ok(spec) = std::env::var("MEMRA_FRSPEC_TRIM")
5119 && !spec.is_empty()
5120 {
5121 let what = "MEMRA_FRSPEC_TRIM on the glm5 DFlash2 draft head";
5122 let path = memra_gguf::hf::resolve_arg(&spec)
5123 .map_err(|err| format!("{what}: {spec:?}: {err}"))?;
5124 let sha16 = sha256_file_hex(std::path::Path::new(&path), 8)?;
5125 let d2t: Vec<u32> = if path.ends_with(".txt") {
5126 let text = std::fs::read_to_string(&path)
5127 .map_err(|err| format!("{what}: {path}: {err}"))?;
5128 frspec_parse_ranks_txt_strict(&text, &format!("{what} ({path}, sha16={sha16})"))?
5129 } else {
5130 frspec_read_d2t(&path)?
5131 };
5132 let n_vocab = output.out_features();
5133 frspec_validate_ranks(&d2t, n_vocab, &format!("{what} ({path}, sha16={sha16})"))?;
5134 let v = src
5135 .find("output.weight")
5136 .or_else(|| src.find("token_embd.weight"))
5137 .ok_or_else(|| {
5138 format!("{what}: model has no output.weight (or tied token_embd.weight)")
5139 })?;
5140 if v.ne[1] as usize != n_vocab {
5141 return Err(format!(
5142 "{what}: source head rows {} != loaded head rows {n_vocab}",
5143 v.ne[1]
5144 )
5145 .into());
5146 }
5147 let de = crate::pp::layer_engine(e, n_trunk, n_trunk)?;
5149 let (head, nvfp4_sizes) = frspec_gather_trimmed_head(
5150 de,
5151 &v,
5152 &d2t,
5153 std::env::var("MEMRA_FRSPEC_TRIM_NVFP4").as_deref() == Ok("1"),
5154 match src.find("output.scale") {
5155 Some(sv) => f32::from_le_bytes(sv.bytes[..4].try_into().unwrap()),
5156 None => 1.0,
5157 },
5158 )?;
5159 eprintln!(
5160 "[frspec-trim] glm5 DFlash2 draft-head slab: {} rows of {} gathered from main \
5161 output.weight ({}) src={sha16} ({path})",
5162 d2t.len(),
5163 n_vocab,
5164 match nvfp4_sizes {
5165 Some((nvfp4_bytes, gathered_bytes)) => format!(
5166 "re-quantized BF16 -> NVFP4, {} MiB, was {} MiB",
5167 nvfp4_bytes >> 20,
5168 gathered_bytes >> 20
5169 ),
5170 None => format!(
5171 "{:?}, {} MiB",
5172 v.ggml_type,
5173 (d2t.len() * (v.bytes.len() / n_vocab)) >> 20
5174 ),
5175 },
5176 );
5177 frspec_src_sha16 = Some(sha16.clone());
5178 dflash_trim = Some(DflashTrimHead {
5179 head,
5180 d2t,
5181 src_sha16: sha16,
5182 });
5183 }
5184
5185 if cfg.arch.is_glm5_next() && crate::glm_spec::glm5_spec_on() {
5193 match (glm5_dflash.as_ref(), mtp.as_ref()) {
5194 (Some(dr), head) => {
5195 let trim_note = match (
5200 head.filter(|h| h.d2t_from_target_head)
5201 .and_then(|h| h.d2t.as_ref())
5202 .filter(|m| !m.is_empty()),
5203 dflash_trim.as_ref(),
5204 ) {
5205 (Some(map), _) => format!(
5206 "draft head RANK-TRIMMED n_ranks={} src={}",
5207 map.len(),
5208 frspec_src_sha16.as_deref().unwrap_or("unknown")
5209 ),
5210 (None, Some(slab)) => format!(
5211 "draft head RANK-TRIMMED n_ranks={} src={}",
5212 slab.d2t.len(),
5213 slab.src_sha16
5214 ),
5215 (None, None) => "draft head FULL target vocab".to_string(),
5216 };
5217 eprintln!(
5218 "[glm5-spec] serve route ARMED: draft source = dflash2 @ {}; {trim_note}; \
5219 native MTP head {}",
5220 dr.sha8,
5221 if head.is_some() {
5222 "ALSO loaded (idle for drafting — dflash2 wins by selection)"
5223 } else {
5224 "NOT loaded (the q38 pattern: a full MoE trunk layer of VRAM saved)"
5225 }
5226 );
5227 }
5228 (None, Some(head)) => {
5229 match head.d2t.as_ref() {
5230 Some(map) => eprintln!(
5231 "[glm5-spec] serve route ARMED: MTP head loaded; draft head TRIMMED \
5232 to {} rows (FR-Spec d2t engaged)",
5233 map.len()
5234 ),
5235 None => eprintln!(
5236 "[glm5-spec] serve route ARMED: MTP head loaded; draft head FULL \
5237 target vocab (no FR-Spec trim)"
5238 ),
5239 }
5240 eprintln!("[glm5-spec] draft source = native-mtp");
5241 }
5242 (None, None) => eprintln!(
5243 "[glm5-spec] MEMRA_GLM5_SPEC=1 but no MTP head loaded \
5244 (set MEMRA_GLM5_MTP=1 or MEMRA_GLM5_DFLASH=<drafter>) — route stays \
5245 fail-closed, plain serving"
5246 ),
5247 }
5248 }
5249
5250 if let Some(ctx) = spill.as_ref() {
5251 eprintln!(
5252 "[spill] experts placed: {} pinned (Tier 1), {} mmap'd from disk (Tier 2, {} MiB)",
5253 ctx.n_pinned,
5254 ctx.n_mmap,
5255 ctx.mmap_bytes >> 20
5256 );
5257 }
5258
5259 if cfg.n_head_kv > 0 && cfg.n_head / cfg.n_head_kv > 8 {
5273 crate::FA_V4_MAX_DEFAULT.store(0, std::sync::atomic::Ordering::Relaxed);
5274 eprintln!(
5275 "[fa] v4 decode family disabled: gqa {} > fa_v4_smem capacity 8 (v3 lane serves)",
5276 cfg.n_head / cfg.n_head_kv
5277 );
5278 }
5279
5280 if gemma_program {
5281 crate::FA_VEC_MIN_DEFAULT.store(1, std::sync::atomic::Ordering::Relaxed);
5283 let real_moe = plan
5286 .trunk_operations()
5287 .contains(&memra_gguf::model_plan::OperationKind::MoeMlp);
5288 crate::FA_SPW_DEFAULT.store(
5289 if real_moe { 32 } else { 64 },
5290 std::sync::atomic::Ordering::Relaxed,
5291 );
5292 crate::FA_SP512_DEFAULT.store(
5294 if real_moe { 16 } else { 32 },
5295 std::sync::atomic::Ordering::Relaxed,
5296 );
5297 crate::FUSED_MR1_DEFAULT.store(!real_moe, std::sync::atomic::Ordering::Relaxed);
5307 crate::RMS_BLOCK_DEFAULT.store(1024, std::sync::atomic::Ordering::Relaxed);
5309 crate::FA_SP_GEMMA.store(true, std::sync::atomic::Ordering::Relaxed);
5311 }
5315 let force_embd_gpu = gemma_program;
5318 let gemma4_aux = if gemma_program {
5319 let rope_freqs = match src.find("rope_freqs.weight") {
5320 Some(t) => {
5321 let host = memra_gguf::dequant::dequantize(
5322 t.ggml_type,
5323 &t.bytes,
5324 t.ne.iter().product::<u64>() as usize,
5325 );
5326 let mut copies = Vec::new();
5327 if let Some(fence) = crate::pp::pp_cuts(n_trunk) {
5328 #[allow(clippy::needless_range_loop)]
5329 for s in 0..fence.len() - 1 {
5331 let owner = crate::pp::layer_engine(e, n_trunk, fence[s])?;
5332 let dev = owner.ctx().ordinal();
5333 if copies.iter().all(|(d, _)| *d != dev) {
5334 copies.push((dev, owner.htod(&host)?));
5335 }
5336 }
5337 } else {
5338 copies.push((e.ctx().ordinal(), e.htod(&host)?));
5339 }
5340 Some(copies)
5341 }
5342 None => {
5350 let g4 = cfg.gemma4.as_ref().unwrap();
5351 let n = (g4.rope_dims_global / 2) as usize;
5352 let keep =
5353 ((n as f32) * g4.partial_rotary_global.clamp(0.0, 1.0)).round() as usize;
5354 let host: Vec<f32> = (0..n)
5355 .map(|i| if i < keep { 1.0 } else { 1.0e30 })
5356 .collect();
5357 eprintln!(
5358 "[gemma4] rope_freqs.weight synthesized ({n} factors, first {keep} \
5359 rotate; source ships none — native checkpoint)"
5360 );
5361 let mut copies = Vec::new();
5362 if let Some(fence) = crate::pp::pp_cuts(n_trunk) {
5363 #[allow(clippy::needless_range_loop)]
5364 for s in 0..fence.len() - 1 {
5366 let owner = crate::pp::layer_engine(e, n_trunk, fence[s])?;
5367 let dev = owner.ctx().ordinal();
5368 if copies.iter().all(|(d, _)| *d != dev) {
5369 copies.push((dev, owner.htod(&host)?));
5370 }
5371 }
5372 } else {
5373 copies.push((e.ctx().ordinal(), e.htod(&host)?));
5374 }
5375 Some(copies)
5376 }
5377 };
5378 let e4b = match src.find("per_layer_token_embd.weight") {
5380 Some(t) => {
5381 let n_epl = cfg
5382 .gemma4
5383 .as_ref()
5384 .map(|g| g.n_embd_per_layer as usize)
5385 .unwrap_or(0);
5386 let row = t.ne[0] as usize; let row_bytes = t.bytes.len() / (t.ne[1] as usize);
5388 eprintln!(
5389 "[gemma4-e4b] per-layer-embed model detected (n_epl={n_epl}, row {row}) — \
5390 first-light forward (eager decode + prime); dc/graph/spec unwired \
5391 (HANDOVER-E4B.md)"
5392 );
5393 Some(crate::hybrid::Gemma4E4bModel {
5394 tok_tbl_gpu: std::sync::OnceLock::new(),
5395 tok_embd_bytes: t.bytes.to_vec(),
5396 tok_embd_qt: match t.ggml_type {
5397 memra_gguf::GgmlType::Q6_K => crate::QT_Q6_K,
5398 memra_gguf::GgmlType::Q8_0 => crate::QT_Q8_0,
5399 other => panic!("e4b per-layer tok embd: unhandled dtype {other:?}"),
5400 },
5401 tok_embd_row_bytes: row_bytes,
5402 model_proj: load_t(e, src, "per_layer_model_proj.weight")?,
5403 proj_norm: load_t(e, src, "per_layer_proj_norm.weight")?,
5404 n_epl,
5405 })
5406 }
5407 None => None,
5408 };
5409 let suppress_d = {
5410 let sup = &cfg.gemma4.as_ref().unwrap().suppress_tokens;
5411 if sup.is_empty() {
5412 None
5413 } else {
5414 let ids: Vec<i32> = sup.iter().map(|&x| x as i32).collect();
5415 eprintln!(
5416 "[gemma4] suppress_tokens: {} ids masked at sampling",
5417 ids.len()
5418 );
5419 Some((e.htod_i32(&ids)?, ids.len()))
5420 }
5421 };
5422 let ones_host = [1.0f32; 512];
5423 let mut ones = Vec::new();
5424 if let Some(fence) = crate::pp::pp_cuts(n_trunk) {
5425 #[allow(clippy::needless_range_loop)]
5426 for s in 0..fence.len() - 1 {
5428 let owner = crate::pp::layer_engine(e, n_trunk, fence[s])?;
5429 let dev = owner.ctx().ordinal();
5430 if ones.iter().all(|(d, _)| *d != dev) {
5431 ones.push((dev, owner.htod(&ones_host)?));
5432 }
5433 }
5434 } else {
5435 ones.push((e.ctx().ordinal(), e.htod(&ones_host)?));
5436 }
5437 Some(GemmaAux {
5438 rope_freqs,
5439 ones,
5440 suppress_d,
5441 e4b,
5442 })
5443 } else {
5444 None
5445 };
5446 let step35_aux = if sliding_gated_moe_program {
5450 let rope_freqs = match src.find("rope_freqs.weight") {
5451 Some(t) => {
5452 let host = memra_gguf::dequant::dequantize(
5453 t.ggml_type,
5454 &t.bytes,
5455 t.ne.iter().product::<u64>() as usize,
5456 );
5457 let mut copies = Vec::new();
5458 if let Some(fence) = crate::pp::pp_cuts(n_trunk) {
5459 #[allow(clippy::needless_range_loop)]
5460 for s in 0..fence.len() - 1 {
5462 let owner = crate::pp::layer_engine(e, n_trunk, fence[s])?;
5463 let dev = owner.ctx().ordinal();
5464 if copies.iter().all(|(d, _)| *d != dev) {
5465 copies.push((dev, owner.htod(&host)?));
5466 }
5467 }
5468 } else {
5469 copies.push((e.ctx().ordinal(), e.htod(&host)?));
5470 }
5471 Some(copies)
5472 }
5473 None => None,
5474 };
5475 Some(Step35Aux { rope_freqs })
5476 } else {
5477 None
5478 };
5479 let mut layers = layers;
5480 {
5487 let q8rp_on = match std::env::var("MEMRA_Q8RP").as_deref() {
5488 Ok("0") => false,
5489 Ok(_) => true,
5490 Err(_) => {
5498 cfg!(memra_hopper_mma) || {
5499 let q8b = |w: &crate::model::GpuTensor| -> usize {
5500 match w {
5501 crate::model::GpuTensor::Quant {
5502 bytes,
5503 qtype,
5504 row_bytes,
5505 ne,
5506 rp4: None,
5507 ..
5508 } if *qtype == crate::QT_Q8_0
5509 && ne.len() == 2
5510 && (ne[0] as usize).is_multiple_of(32)
5511 && *row_bytes == (ne[0] as usize / 32) * 34 =>
5512 {
5513 bytes.len()
5514 }
5515 _ => 0,
5516 }
5517 };
5518 let mut need = q8b(&output);
5519 for layer in layers.iter() {
5520 match &layer.mixer {
5521 Mixer::Full(fa) => {
5522 for w in [&fa.wq, &fa.wk, &fa.wv, &fa.wo] {
5523 need += q8b(w);
5524 }
5525 }
5526 Mixer::Linear(la) => {
5527 for w in [
5528 &la.wqkv,
5529 &la.wqkv_gate,
5530 &la.ssm_beta,
5531 &la.ssm_alpha,
5532 &la.ssm_out,
5533 ] {
5534 need += q8b(w);
5535 }
5536 }
5537 Mixer::Mla(_) => {}
5538 Mixer::Kda(_) => {} }
5540 if let Ffn::Dense {
5541 ffn_gate,
5542 ffn_up,
5543 ffn_down,
5544 } = &layer.ffn
5545 {
5546 for w in [ffn_gate, ffn_up, ffn_down] {
5547 need += q8b(w);
5548 }
5549 }
5550 }
5551 need > 0
5552 && e.ctx()
5553 .mem_get_info()
5554 .map(|(free, _)| free >= need + (8usize << 30))
5555 .unwrap_or(false)
5556 }
5557 }
5558 };
5559 let kqrp_on = crate::Engine::kqrp_enabled() || {
5569 std::env::var("MEMRA_KQRP").is_err() && {
5570 let kqb = |w: &crate::model::GpuTensor| -> usize {
5571 match w {
5572 crate::model::GpuTensor::Quant {
5573 bytes,
5574 qtype,
5575 row_bytes,
5576 ne,
5577 rp4: None,
5578 ..
5579 } if ne.len() == 2 && (ne[0] as usize).is_multiple_of(256) => {
5580 let sb = if *qtype == crate::QT_Q4_K {
5581 144
5582 } else if *qtype == crate::QT_Q6_K {
5583 210
5584 } else {
5585 return 0;
5586 };
5587 if *row_bytes == (ne[0] as usize / 256) * sb {
5588 bytes.len()
5589 } else {
5590 0
5591 }
5592 }
5593 _ => 0,
5594 }
5595 };
5596 let mut need = kqb(&output);
5597 for layer in layers.iter() {
5598 if let Mixer::Full(fa) = &layer.mixer {
5599 for w in [&fa.wq, &fa.wk, &fa.wv, &fa.wo] {
5600 need += kqb(w);
5601 }
5602 }
5603 if let Ffn::Dense {
5604 ffn_gate,
5605 ffn_up,
5606 ffn_down,
5607 } = &layer.ffn
5608 {
5609 for w in [ffn_gate, ffn_up, ffn_down] {
5610 need += kqb(w);
5611 }
5612 }
5613 }
5614 need > 0
5615 && e.ctx()
5616 .mem_get_info()
5617 .map(|(free, _)| free >= need + (8usize << 30))
5618 .unwrap_or(false)
5619 }
5620 };
5621 if q8rp_on || kqrp_on {
5622 let f16_model_ok = gemma_program
5629 || plan
5630 .trunk_operations()
5631 .contains(&memra_gguf::model_plan::OperationKind::MoeMlp)
5632 || std::env::var("MEMRA_PP_F16").as_deref() == Ok("1");
5633 let mut nmir = 0usize;
5634 let mut mir = |e_ref: &crate::Engine,
5638 w: &mut crate::model::GpuTensor|
5639 -> Result<(), Box<dyn std::error::Error>> {
5640 let before = matches!(w, crate::model::GpuTensor::Quant { rp4: Some(_), .. });
5641 if q8rp_on {
5642 e_ref.build_q8_rp4(w)?;
5643 }
5644 if kqrp_on {
5645 e_ref.build_q4k_rp4(w)?;
5646 e_ref.build_q6k_rp4(w)?;
5647 }
5648 let q6k = matches!(w, crate::model::GpuTensor::Quant { qtype, .. }
5653 if *qtype == crate::QT_Q6_K);
5654 if q8rp_on && crate::f16_ffi::pp_f16_enabled() && (f16_model_ok || q6k) {
5655 e_ref.build_q8_f16(w)?;
5656 }
5657 if !before && matches!(w, crate::model::GpuTensor::Quant { rp4: Some(_), .. }) {
5658 nmir += 1;
5659 }
5660 Ok(())
5661 };
5662 for (il, layer) in layers.iter_mut().enumerate() {
5663 let el = crate::pp::layer_engine(e, n_trunk, il)?;
5664 match &mut layer.mixer {
5665 Mixer::Full(fa) => {
5666 for w in [&mut fa.wq, &mut fa.wk, &mut fa.wv, &mut fa.wo] {
5667 mir(el, w)?;
5668 }
5669 }
5670 Mixer::Linear(la) => {
5671 for w in [
5672 &mut la.wqkv,
5673 &mut la.wqkv_gate,
5674 &mut la.ssm_beta,
5675 &mut la.ssm_alpha,
5676 &mut la.ssm_out,
5677 ] {
5678 mir(el, w)?;
5679 }
5680 }
5681 Mixer::Mla(_) => {}
5684 Mixer::Kda(_) => {} }
5686 if let Ffn::Dense {
5687 ffn_gate,
5688 ffn_up,
5689 ffn_down,
5690 } = &mut layer.ffn
5691 {
5692 for w in [ffn_gate, ffn_up, ffn_down] {
5693 mir(el, w)?;
5694 }
5695 }
5696 }
5697 mir(e_head, &mut output)?;
5698 if nmir > 0 {
5699 eprintln!("[q8rp] split-plane decode mirrors built: {nmir} tensors");
5700 }
5701 if q8rp_on && crate::f16_ffi::pp_f16_enabled() {
5716 for (want, tag) in [(crate::QT_Q4_K, "q4kf16"), (crate::QT_Q5_K, "q5kf16")] {
5717 let (mut n4, mut b4) = (0usize, 0usize);
5718 let mut mirk =
5719 |e_ref: &crate::Engine,
5720 w: &mut crate::model::GpuTensor|
5721 -> Result<(), Box<dyn std::error::Error>> {
5722 if matches!(w, crate::model::GpuTensor::Quant { qtype, f16: None, .. }
5723 if *qtype == want)
5724 {
5725 e_ref.build_q8_f16(w)?;
5726 if let crate::model::GpuTensor::Quant { f16: Some(m), .. } = w {
5727 n4 += 1;
5728 b4 += m.len();
5729 }
5730 }
5731 Ok(())
5732 };
5733 for (il, layer) in layers.iter_mut().enumerate() {
5734 let el = crate::pp::layer_engine(e, n_trunk, il)?;
5735 match &mut layer.mixer {
5736 Mixer::Full(fa) => {
5737 for w in [&mut fa.wq, &mut fa.wk, &mut fa.wv, &mut fa.wo] {
5738 mirk(el, w)?;
5739 }
5740 }
5741 Mixer::Linear(la) => {
5742 for w in [
5743 &mut la.wqkv,
5744 &mut la.wqkv_gate,
5745 &mut la.ssm_beta,
5746 &mut la.ssm_alpha,
5747 &mut la.ssm_out,
5748 ] {
5749 mirk(el, w)?;
5750 }
5751 }
5752 Mixer::Mla(_) => {} Mixer::Kda(_) => {} }
5755 if let Ffn::Dense {
5756 ffn_gate,
5757 ffn_up,
5758 ffn_down,
5759 } = &mut layer.ffn
5760 {
5761 for w in [ffn_gate, ffn_up, ffn_down] {
5762 mirk(el, w)?;
5763 }
5764 }
5765 }
5766 mirk(e_head, &mut output)?;
5767 if n4 > 0 {
5768 eprintln!(
5769 "[{tag}] prefill fp16 mirrors built: {n4} tensors \
5770 ({} MB)",
5771 b4 >> 20
5772 );
5773 }
5774 }
5775 }
5776 }
5777 }
5778 if gemma_program && crate::Engine::q4rp_enabled() {
5785 let mut nmir = 0usize;
5786 for (il, layer) in layers.iter_mut().enumerate() {
5787 let e = crate::pp::layer_engine(e, n_trunk, il)?;
5789 let is_moe26 = layer.gemma4.as_ref().is_some_and(|g| g.moe_bits.is_some());
5798 let is_e4b = layer.gemma4.as_ref().is_some_and(|g| g.e4b.is_some());
5799 if !(is_moe26 || is_e4b) {
5800 continue;
5801 }
5802 if let Mixer::Full(fa) = &mut layer.mixer {
5803 for w in [&mut fa.wq, &mut fa.wk, &mut fa.wv, &mut fa.wo] {
5804 e.build_q4_rp4(w)?;
5805 nmir += 1;
5806 }
5807 }
5808 if is_e4b {
5809 let own_kv = layer
5811 .gemma4
5812 .as_ref()
5813 .unwrap()
5814 .e4b
5815 .as_ref()
5816 .is_some_and(|e4| e4.kv_share.is_none());
5817 if own_kv
5818 && let Mixer::Full(fa) = &layer.mixer
5819 && let Some(mut cat) = e.build_q4_out_concat3(&fa.wq, &fa.wk, &fa.wv)?
5820 {
5821 e.build_q4_rp4(&mut cat)?;
5822 nmir += 1;
5823 layer.gemma4.as_mut().unwrap().e4b.as_mut().unwrap().qkv_cat = Some(cat);
5824 }
5825 if let Ffn::Dense {
5826 ffn_gate,
5827 ffn_up,
5828 ffn_down,
5829 } = &mut layer.ffn
5830 {
5831 for w in [ffn_gate, ffn_up, ffn_down] {
5832 e.build_q4_rp4(w)?;
5833 nmir += 1;
5834 }
5835 }
5836 let e4 = layer.gemma4.as_mut().unwrap().e4b.as_mut().unwrap();
5837 for w in [&mut e4.inp_gate, &mut e4.proj] {
5838 e.build_q4_rp4(w)?;
5839 nmir += 1;
5840 }
5841 }
5842 if let Some(mb) = layer.gemma4.as_mut().unwrap().moe_bits.as_mut() {
5843 for w in [&mut mb.shared_gate, &mut mb.shared_up, &mut mb.shared_down] {
5844 e.build_q4_rp4(w)?;
5845 nmir += 1;
5846 }
5847 }
5848 }
5849 if nmir > 0 {
5850 eprintln!("[q4rp] split-plane decode mirrors built: {nmir} trunk tensors");
5851 }
5852 let fast_on = std::env::var("MEMRA_FAST").as_deref() != Ok("0");
5859 if fast_on {
5860 let mut nswap = 0usize;
5861 let mut nf16 = 0usize;
5862 let q4f16_model_ok = matches!(cfg.n_embd, 3840 | 5376); if let Ok(v) = std::env::var("MEMRA_Q4F16")
5881 && v != "0"
5882 && v != "1"
5883 {
5884 return Err(format!(
5885 "MEMRA_Q4F16={v} is not 0 or 1 — this env selects the prefill \
5886 ARITHMETIC (fp16 mirrors vs int8 MMQ) and must never be guessed"
5887 )
5888 .into());
5889 }
5890 let f16_need = {
5891 let f16b = |w: &crate::model::GpuTensor| -> usize {
5892 match w {
5893 crate::model::GpuTensor::Quant {
5894 qtype,
5895 ne,
5896 f16: None,
5897 ..
5898 } if ne.len() == 2
5899 && matches!(
5900 *qtype,
5901 crate::QT_Q8_0
5902 | crate::QT_Q4_0
5903 | crate::QT_Q6_K
5904 | crate::QT_Q4_K
5905 | crate::QT_Q5_K
5906 ) =>
5907 {
5908 (ne[0] as usize) * (ne[1] as usize) * 2
5909 }
5910 _ => 0,
5911 }
5912 };
5913 let mut need = 0usize;
5914 for layer in layers.iter() {
5915 if layer.gemma4.as_ref().is_none_or(|g| g.moe_bits.is_some()) {
5916 continue;
5917 }
5918 if let Mixer::Full(fa) = &layer.mixer {
5919 for w in [&fa.wq, &fa.wk, &fa.wv, &fa.wo] {
5920 need += f16b(w);
5921 }
5922 }
5923 if let Ffn::Dense {
5924 ffn_gate,
5925 ffn_up,
5926 ffn_down,
5927 } = &layer.ffn
5928 {
5929 for w in [ffn_gate, ffn_up, ffn_down] {
5930 need += f16b(w);
5931 }
5932 }
5933 }
5934 need
5935 };
5936 let f16_free = e.ctx().mem_get_info().map(|(free, _)| free).unwrap_or(0);
5937 let f16_auto = q4f16_model_ok
5938 && std::env::var("MEMRA_Q4F16").is_err()
5939 && crate::f16_ffi::pp_f16_capacity_ok(f16_free, f16_need);
5940 let (f16_on, f16_why) = match std::env::var("MEMRA_Q4F16").as_deref() {
5946 Ok("1") => (true, "env MEMRA_Q4F16=1"),
5947 Ok("0") => (false, "env MEMRA_Q4F16=0"),
5948 _ if crate::f16_ffi::pp_f16_enabled() && q4f16_model_ok => {
5949 (true, "env MEMRA_PP_F16")
5950 }
5951 _ if f16_auto => (true, "capacity-keyed auto (UNPINNED)"),
5952 _ if !q4f16_model_ok => (false, "model geometry not eligible"),
5953 _ => (false, "capacity-keyed auto REFUSED (UNPINNED)"),
5954 };
5955 eprintln!(
5962 "[q4f16] prefill program = {} (reason: {}); free {} MiB, mirror mass {} MiB, \
5963 capacity threshold {} MiB (mass + 8192 headroom) — SELECTS PREFILL ARITHMETIC",
5964 if f16_on {
5965 "FP16 MIRRORS"
5966 } else {
5967 "INT8 MMQ (no f16 mirrors)"
5968 },
5969 f16_why,
5970 f16_free >> 20,
5971 f16_need >> 20,
5972 (f16_need + (8usize << 30)) >> 20,
5973 );
5974 for (il, layer) in layers.iter_mut().enumerate() {
5975 let e = crate::pp::layer_engine(e, n_trunk, il)?;
5977 let dense_gemma = layer.gemma4.as_ref().is_some_and(|g| g.moe_bits.is_none());
5978 if !dense_gemma {
5979 continue;
5980 }
5981 if let Mixer::Full(fa) = &mut layer.mixer {
5982 for w in [&mut fa.wq, &mut fa.wk, &mut fa.wv, &mut fa.wo] {
5983 if f16_on {
5984 e.build_q8_f16(w)?;
5985 if matches!(w, crate::model::GpuTensor::Quant { f16: Some(_), .. })
5986 {
5987 nf16 += 1;
5988 }
5989 }
5990 if e.build_q4_rp_swap(w)? {
5991 nswap += 1;
5992 }
5993 }
5994 }
5995 if let Ffn::Dense {
5996 ffn_gate,
5997 ffn_up,
5998 ffn_down,
5999 } = &mut layer.ffn
6000 {
6001 for w in [ffn_gate, ffn_up, ffn_down] {
6002 if f16_on {
6003 e.build_q8_f16(w)?;
6004 if matches!(w, crate::model::GpuTensor::Quant { f16: Some(_), .. })
6005 {
6006 nf16 += 1;
6007 }
6008 }
6009 if e.build_q4_rp_swap(w)? {
6010 nswap += 1;
6011 }
6012 }
6013 }
6014 }
6015 if nswap > 0 {
6016 eprintln!("[q4rp] split-plane IN-PLACE swap: {nswap} dense trunk tensors");
6017 }
6018 if nf16 > 0 {
6019 eprintln!("[q4f16] prefill fp16 mirrors built: {nf16} dense trunk tensors");
6020 }
6021 }
6022 }
6023 let model = HybridModel {
6024 cfg,
6025 plan,
6026 rewrite_qualifications: None,
6027 embd,
6028 output_norm,
6029 output,
6030 layers,
6031 mtp,
6032 mtp_extra,
6033 dflash_trim,
6034 frspec_src_sha16,
6035 embd_gpu: std::sync::OnceLock::new(),
6036 gemma4_aux,
6037 step35_aux,
6038 prime_slabs: std::sync::Mutex::new(std::collections::HashMap::new()),
6039 dspark_vgraphs: std::sync::Mutex::new(None),
6040 step_grouped_prefill: std::sync::Mutex::new(StepEpGroupedPrefill::default()),
6041 step35_token_graph: std::sync::Mutex::new(None),
6042 hyper,
6043 hyper_head,
6044 glm5_dflash,
6045 draft_state_bytes: std::sync::atomic::AtomicUsize::new(0),
6046 };
6047 e.configure_moe_cache_layout(model.moe_cache_block_sizes());
6048 if force_embd_gpu {
6049 let _ = model
6050 .embd_gpu
6051 .get_or_init(|| e.upload_u8(&model.embd.raw).expect("embed table upload"));
6052 }
6053 crate::pp::sync_stages_after_load(e, n_trunk)?;
6059 Ok(model)
6060 }
6061
6062 pub fn ensure_embed_resident(&self, e: &Engine) -> Result<(), Box<dyn std::error::Error>> {
6072 if std::env::var("MEMRA_EMBED_DEV").as_deref() == Ok("0") {
6073 return Ok(());
6074 }
6075 if self.embd_gpu.get().is_none() {
6076 let buf = e.upload_u8(&self.embd.raw)?;
6077 let _ = self.embd_gpu.set(buf); }
6079 Ok(())
6080 }
6081
6082 pub fn embed(
6083 &self,
6084 e: &Engine,
6085 tokens: &[u32],
6086 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6087 let n_embd = self.cfg.n_embd as usize;
6088 if std::env::var("MEMRA_EMBED_DEV").as_deref() != Ok("0") {
6094 let tbl = self
6095 .embd_gpu
6096 .get_or_init(|| e.upload_u8(&self.embd.raw).expect("embed table upload"));
6097 let tok_d = e.htod_u32_v(tokens)?;
6098 let (qt, rb) = self.embd.qt_and_row_bytes(n_embd);
6099 return e.embed_gather_device_td(tbl, &tok_d, tokens.len(), n_embd, qt, rb);
6100 }
6101 let x = self.embd.try_gather(n_embd, tokens)?;
6102 e.htod(&x)
6103 }
6104}
6105
6106fn illegal_pipeline_cuts(fence: &[usize], legal_boundaries: &[usize]) -> Vec<usize> {
6107 fence
6108 .get(1..fence.len().saturating_sub(1))
6109 .unwrap_or_default()
6110 .iter()
6111 .copied()
6112 .filter(|cut| !legal_boundaries.contains(cut))
6113 .collect()
6114}
6115
6116#[cfg(test)]
6117mod pipeline_cut_tests {
6118 use super::illegal_pipeline_cuts;
6119
6120 #[test]
6121 fn manual_pipeline_cuts_cannot_bypass_model_plan_boundaries() {
6122 assert!(illegal_pipeline_cuts(&[0, 8, 16, 24], &[8, 16]).is_empty());
6123 assert_eq!(illegal_pipeline_cuts(&[0, 7, 16, 24], &[8, 16]), vec![7]);
6124 assert_eq!(
6125 illegal_pipeline_cuts(&[0, 7, 15, 24], &[8, 16]),
6126 vec![7, 15]
6127 );
6128 }
6129}
6130
6131#[cfg(test)]
6132mod auto_parallel_policy_tests {
6133 use super::{
6134 parse_auto_parallel_tp_attention, parse_auto_parallel_tp_attention_ranks,
6135 parse_auto_w4a16_bf16_mmv,
6136 };
6137
6138 #[test]
6139 fn automatic_w4a16_bf16_residency_defaults_on_with_explicit_rollback() {
6140 assert!(parse_auto_w4a16_bf16_mmv(None).unwrap());
6141 assert!(!parse_auto_w4a16_bf16_mmv(Some("0")).unwrap());
6142 assert!(parse_auto_w4a16_bf16_mmv(Some("1")).unwrap());
6143 assert!(parse_auto_w4a16_bf16_mmv(Some("true")).is_err());
6144 assert!(parse_auto_w4a16_bf16_mmv(Some("")).is_err());
6145 }
6146
6147 #[test]
6148 fn automatic_tp_attention_is_strict_and_defaults_off() {
6149 assert!(!parse_auto_parallel_tp_attention(None).unwrap());
6150 assert!(!parse_auto_parallel_tp_attention(Some("")).unwrap());
6151 assert!(!parse_auto_parallel_tp_attention(Some("0")).unwrap());
6152 assert!(parse_auto_parallel_tp_attention(Some("1")).unwrap());
6153 assert!(parse_auto_parallel_tp_attention(Some("true")).is_err());
6154 assert!(parse_auto_parallel_tp_attention(Some("2")).is_err());
6155 }
6156
6157 #[test]
6158 fn automatic_tp_attention_rank_count_is_explicit_and_bounded() {
6159 assert_eq!(parse_auto_parallel_tp_attention_ranks(None).unwrap(), None);
6160 assert_eq!(
6161 parse_auto_parallel_tp_attention_ranks(Some("2")).unwrap(),
6162 Some(2)
6163 );
6164 assert_eq!(
6165 parse_auto_parallel_tp_attention_ranks(Some("3")).unwrap(),
6166 Some(3)
6167 );
6168 assert_eq!(
6169 parse_auto_parallel_tp_attention_ranks(Some("4")).unwrap(),
6170 Some(4)
6171 );
6172 for bad in ["", "0", "1", "5", "all"] {
6173 assert!(parse_auto_parallel_tp_attention_ranks(Some(bad)).is_err());
6174 }
6175 }
6176}
6177
6178#[cfg(test)]
6179mod step_expert_selection_tests {
6180 use super::{
6181 StepExpertArtifact, StepExpertLayout, StepParallelLoadConfig, StepParallelRuntimeRegistry,
6182 StepTpAttentionPlacement, select_step_expert_layout, select_step_expert_layout_inner,
6183 };
6184 use crate::tp::StepEpLayerSpec;
6185
6186 fn spec(layer: usize, ranks: usize) -> StepEpLayerSpec {
6187 StepEpLayerSpec {
6188 layer,
6189 devices: (0..ranks).collect(),
6190 }
6191 }
6192
6193 #[test]
6194 fn tp2_keeps_projection_sharded_experts() {
6195 let selection = select_step_expert_layout(24, &[], &[spec(24, 2)])
6196 .unwrap()
6197 .unwrap();
6198 assert_eq!(selection.layout, StepExpertLayout::TensorParallel);
6199 assert!(selection.configured_by_tp);
6200 }
6201
6202 #[test]
6203 fn tp4_and_tp8_use_expert_ownership_without_a_second_flag() {
6204 for ranks in [4, 8] {
6205 let selection = select_step_expert_layout(24, &[], &[spec(24, ranks)])
6206 .unwrap()
6207 .unwrap();
6208 assert_eq!(selection.layout, StepExpertLayout::ExpertParallel);
6209 assert!(selection.configured_by_tp);
6210 assert_eq!(selection.spec.devices.len(), ranks);
6211 }
6212 }
6213
6214 #[test]
6215 fn explicit_ep_remains_expert_parallel() {
6216 let selection = select_step_expert_layout(24, &[spec(24, 2)], &[])
6217 .unwrap()
6218 .unwrap();
6219 assert_eq!(selection.layout, StepExpertLayout::ExpertParallel);
6220 assert!(!selection.configured_by_tp);
6221 }
6222
6223 #[test]
6224 fn conflicting_ep_and_tp_assignments_fail_closed() {
6225 let error = select_step_expert_layout(24, &[spec(24, 4)], &[spec(24, 4)]).unwrap_err();
6226 assert!(error.contains("cannot enable MEMRA_STEP_EP and MEMRA_STEP_TP together"));
6227 }
6228
6229 #[test]
6230 fn automatic_tp2_attention_can_overlap_ep4_expert_ownership() {
6231 let selection = select_step_expert_layout_inner(24, &[spec(24, 4)], &[spec(24, 2)], true)
6232 .unwrap()
6233 .unwrap();
6234 assert_eq!(selection.layout, StepExpertLayout::ExpertParallel);
6235 assert!(!selection.configured_by_tp);
6236 assert_eq!(selection.spec.devices, vec![0, 1, 2, 3]);
6237
6238 let error =
6239 select_step_expert_layout_inner(24, &[spec(24, 4)], &[spec(24, 2)], false).unwrap_err();
6240 assert!(error.contains("cannot enable MEMRA_STEP_EP and MEMRA_STEP_TP together"));
6241 }
6242
6243 #[test]
6244 fn runtime_registry_owns_one_immutable_load_snapshot() {
6245 let mut source_specs = vec![spec(24, 8)];
6246 let registry = StepParallelRuntimeRegistry::with_config(StepParallelLoadConfig {
6247 ep_specs: Vec::new(),
6248 tp_specs: source_specs.clone(),
6249 native_p2p: true,
6250 ep_device_arithmetic: true,
6251 f32_mirror: true,
6252 bulk_p2p: true,
6253 nvfp4_device_routes: true,
6254 auto_parallel: true,
6255 tp_attention_expert_overlap: false,
6256 expert_artifact: StepExpertArtifact::default(),
6257 });
6258 source_specs[0].devices.clear();
6259
6260 let stored = registry.tp_spec(24).unwrap();
6261 assert_eq!(stored.devices, (0..8).collect::<Vec<_>>());
6262 assert!(registry.config.native_p2p);
6263 assert!(registry.config.ep_device_arithmetic);
6264 assert!(registry.config.f32_mirror);
6265 assert!(registry.config.bulk_p2p);
6266 assert!(registry.config.nvfp4_device_routes);
6267 assert!(registry.config.auto_parallel);
6268 assert_eq!(
6269 registry.expert_selection(24).unwrap().unwrap().layout,
6270 StepExpertLayout::ExpertParallel
6271 );
6272
6273 let standalone = StepParallelRuntimeRegistry::default();
6274 assert!(standalone.tp_spec(24).is_none());
6275 assert!(!standalone.config.native_p2p);
6276 assert!(!standalone.config.ep_device_arithmetic);
6277 assert!(!standalone.config.f32_mirror);
6278 assert!(!standalone.config.bulk_p2p);
6279 }
6280
6281 #[test]
6282 fn rank_local_attention_uses_bounded_swa_rings_only_with_native_p2p() {
6283 assert_eq!(
6284 StepTpAttentionPlacement::resolve(true, None),
6285 StepTpAttentionPlacement::RankLocalGlobal
6286 );
6287 assert_eq!(
6288 StepTpAttentionPlacement::resolve(true, Some(512)),
6289 StepTpAttentionPlacement::RankLocalSwa
6290 );
6291 assert_eq!(
6292 StepTpAttentionPlacement::resolve(false, None),
6293 StepTpAttentionPlacement::OwnerTransportFallback
6294 );
6295 assert_eq!(
6296 StepTpAttentionPlacement::resolve(false, Some(512)),
6297 StepTpAttentionPlacement::OwnerSwa
6298 );
6299 }
6300}
6301
6302#[cfg(test)]
6303mod residency_tests {
6304 use super::{DevExpertFp8ProjectionScales, ResidentPlan, residency_bytes_by_device};
6305 use crate::model::HostExpertFp8BlockScales;
6306 use std::collections::HashMap;
6307
6308 #[test]
6309 fn pp_residency_counts_only_each_devices_expert_slice() {
6310 let tensors = [
6311 ("blk.0.ffn_gate_exps.weight", 10usize),
6312 ("blk.0.ffn_up_exps.weight", 20),
6313 ("blk.1.ffn_down_exps.weight", 30),
6314 ("blk.2.ffn_gate_exps.weight", 40),
6315 ("blk.3.ffn_up_exps.weight", 50),
6316 ("blk.0.attn_q.weight", 7),
6317 ("output.weight", 11),
6318 ];
6319 let bytes = residency_bytes_by_device(tensors, &[0, 0, 1, 1], 0);
6320 assert_eq!(bytes.experts.get(&0), Some(&60));
6321 assert_eq!(bytes.experts.get(&1), Some(&90));
6322 assert_eq!(bytes.rest, 18);
6323 assert!(bytes.saw_experts);
6324 }
6325
6326 #[test]
6327 fn pp_residency_combines_stages_that_share_one_device() {
6328 let tensors = [
6329 ("blk.0.ffn_gate_exps.weight", 10usize),
6330 ("blk.1.ffn_gate_exps.weight", 20),
6331 ("blk.2.ffn_gate_exps.weight", 30),
6332 ("blk.3.ffn_gate_exps.weight", 40),
6333 ];
6334 let bytes = residency_bytes_by_device(tensors, &[0, 0, 0, 0], 0);
6335 assert_eq!(bytes.experts.get(&0), Some(&100));
6336 assert_eq!(bytes.experts.len(), 1);
6337 }
6338
6339 #[test]
6340 fn distributed_trunk_layers_do_not_poison_local_mtp_residency_estimates() {
6341 let mut plan = ResidentPlan {
6342 primary_device: 0,
6343 layer_devices: vec![0; 81],
6344 layer_counts: HashMap::from([(0, 81)]),
6345 exact_expert_bytes: None,
6346 trunk_bytes: 0,
6347 decisions: HashMap::new(),
6348 pp: false,
6349 };
6350 plan.exclude_distributed_expert_layers(1..80);
6351 assert_eq!(plan.layer_counts.get(&0), Some(&2));
6352 }
6353
6354 #[test]
6355 fn resident_fp8_scale_slab_must_match_every_expert() {
6356 let valid = HostExpertFp8BlockScales {
6357 scales: vec![1.0; 12],
6358 rows: 2,
6359 cols: 3,
6360 expert_stride: 6,
6361 };
6362 DevExpertFp8ProjectionScales::validate(&valid, 2).unwrap();
6363
6364 let short = HostExpertFp8BlockScales {
6365 scales: vec![1.0; 11],
6366 ..valid
6367 };
6368 assert_eq!(
6369 DevExpertFp8ProjectionScales::validate(&short, 2).unwrap_err(),
6370 "block-E4M3 scale slab length mismatch: got 11, want 2x6=12"
6371 );
6372 }
6373
6374 #[test]
6375 fn resident_fp8_scale_stride_must_match_its_grid() {
6376 let invalid = HostExpertFp8BlockScales {
6377 scales: vec![1.0; 8],
6378 rows: 2,
6379 cols: 2,
6380 expert_stride: 0,
6381 };
6382 assert_eq!(
6383 DevExpertFp8ProjectionScales::validate(&invalid, 2).unwrap_err(),
6384 "block-E4M3 expert scale stride must be nonzero"
6385 );
6386 }
6387}
6388
6389#[cfg(test)]
6390mod draft_head_tests {
6391 use super::{draft_head_tensor, frspec_trim_own_head_name};
6392
6393 const STEP37_DRAFTER: &[&str] = &[
6400 "output.weight",
6401 "output_norm.weight",
6402 "token_embd.weight",
6403 "blk.45.nextn.shared_head_norm.weight",
6404 "blk.45.nextn.shared_head_head.weight",
6405 "blk.46.nextn.shared_head_head.weight",
6406 "blk.47.nextn.shared_head_head.weight",
6407 ];
6408
6409 fn present(names: &'static [&'static str]) -> impl Fn(&str) -> bool {
6410 move |t: &str| names.contains(&t)
6411 }
6412
6413 #[test]
6421 fn step37_drafter_prefers_the_blocks_own_nextn_head_over_file_level_output() {
6422 assert_eq!(
6423 draft_head_tensor(present(STEP37_DRAFTER), 45),
6424 "blk.45.nextn.shared_head_head.weight"
6425 );
6426 }
6427
6428 #[test]
6432 fn each_nextn_block_selects_its_own_head() {
6433 for n in 45..=47u32 {
6434 assert_eq!(
6435 draft_head_tensor(present(STEP37_DRAFTER), n),
6436 format!("blk.{n}.nextn.shared_head_head.weight")
6437 );
6438 }
6439 }
6440
6441 #[test]
6445 fn draft_without_a_nextn_head_falls_back_to_file_level_output() {
6446 let fr_spec: &[&str] = &["output.weight", "output_norm.weight", "d2t.weight"];
6447 assert_eq!(draft_head_tensor(present(fr_spec), 45), "output.weight");
6448 }
6449
6450 #[test]
6455 fn legacy_shared_head_is_probed_but_loses_to_shared_head_head() {
6456 let legacy_only: &[&str] = &["output.weight", "blk.45.nextn.shared_head.weight"];
6457 assert_eq!(
6458 draft_head_tensor(present(legacy_only), 45),
6459 "blk.45.nextn.shared_head.weight"
6460 );
6461
6462 let both: &[&str] = &[
6463 "output.weight",
6464 "blk.45.nextn.shared_head.weight",
6465 "blk.45.nextn.shared_head_head.weight",
6466 ];
6467 assert_eq!(
6468 draft_head_tensor(present(both), 45),
6469 "blk.45.nextn.shared_head_head.weight"
6470 );
6471 }
6472
6473 #[test]
6477 fn a_different_blocks_nextn_head_is_never_borrowed() {
6478 let wrong_block: &[&str] = &[
6479 "output.weight",
6480 "blk.46.nextn.shared_head_head.weight",
6481 "blk.47.nextn.shared_head_head.weight",
6482 ];
6483 assert_eq!(draft_head_tensor(present(wrong_block), 45), "output.weight");
6484 }
6485
6486 #[test]
6491 fn frspec_trim_prefers_the_nextn_blocks_own_head_name() {
6492 assert_eq!(
6493 frspec_trim_own_head_name(45),
6494 "blk.45.nextn.shared_head_head.weight"
6495 );
6496 assert_eq!(
6498 frspec_trim_own_head_name(45),
6499 format!("blk.{}.nextn.shared_head_head.weight", 45)
6500 );
6501 assert_eq!(
6502 frspec_trim_own_head_name(40),
6503 "blk.40.nextn.shared_head_head.weight"
6504 );
6505 }
6506}