1#![allow(unused_imports)]
9
10use anyhow::{Context, Result, bail, ensure};
11use metrale_gpu_runtime::gpu::{DevicePtr, GpuBackend};
12use metrale_model_weights::weights::{WeightDtype, WeightStore};
13
14use super::*;
15
16#[path = "ssm_qwen35/dequant_fp8.rs"]
17mod dequant_fp8;
18use dequant_fp8::dequant_fp8_block_slice_bf16;
19
20pub struct SsmWeightsQwen35 {
23 pub in_proj_qkv: DenseWeight,
25 pub in_proj_z: DenseWeight,
26 pub in_proj_a: DenseWeight,
27 pub in_proj_b: DenseWeight,
28 pub conv1d: DenseWeight,
29 pub a_log: DenseWeight,
30 pub dt_bias: DenseWeight,
31 pub norm: DenseWeight,
32 pub out_proj: DenseWeight,
33}
34
35pub fn load_ssm_qwen35(
37 store: &WeightStore,
38 layer_prefix: &str,
39 gpu: &dyn GpuBackend,
40 _variant: Nvfp4Variant,
42) -> Result<SsmWeightsQwen35> {
43 let p = format!("{layer_prefix}.linear_attn");
44
45 let load_proj = |prefix: &str| -> Result<DenseWeight> {
49 if store.contains(&format!("{prefix}.weight_packed")) {
50 let shape = store.get(&format!("{prefix}.weight_packed"))?.shape.clone();
51 dequant_nvfp4_to_bf16(store, prefix, shape[0], shape[1] * 2, gpu)
52 } else {
53 dense_auto(store, &format!("{prefix}.weight"), gpu)
54 }
55 };
56
57 Ok(SsmWeightsQwen35 {
58 in_proj_qkv: load_proj(&format!("{p}.in_proj_qkv"))?,
59 in_proj_z: load_proj(&format!("{p}.in_proj_z"))?,
60 in_proj_a: load_proj(&format!("{p}.in_proj_a"))?,
61 in_proj_b: load_proj(&format!("{p}.in_proj_b"))?,
62 conv1d: dense_auto(store, &format!("{p}.conv1d.weight"), gpu)?,
63 a_log: dense_keep_f32(store, &format!("{p}.A_log"), gpu)?,
64 dt_bias: dense_keep_f32(store, &format!("{p}.dt_bias"), gpu)?,
65 norm: dense_f32_safe(store, &format!("{p}.norm.weight"), gpu)?,
66 out_proj: load_proj(&format!("{p}.out_proj"))?,
67 })
68}
69
70#[derive(Clone, Copy, Debug, PartialEq, Eq)]
73pub struct Nvfp4MoeCopies {
74 pub routed_gate_up: bool,
76 pub routed_down: bool,
78 pub shared: bool,
80}
81
82impl Nvfp4MoeCopies {
83 pub const ALL: Self = Self {
85 routed_gate_up: true,
86 routed_down: true,
87 shared: true,
88 };
89 pub const SHARED_ONLY: Self = Self {
91 routed_gate_up: false,
92 routed_down: false,
93 shared: true,
94 };
95}
96
97pub fn load_moe_qwen35(
103 store: &WeightStore,
104 layer_prefix: &str,
105 num_experts: usize,
106 gpu: &dyn GpuBackend,
107 config: &metrale_config::ModelConfig,
108 variant: Nvfp4Variant,
109 absmax_k: metrale_gpu_runtime::gpu::KernelHandle,
110 quantize_k: metrale_gpu_runtime::gpu::KernelHandle,
111 stream: u64,
112 copies: Nvfp4MoeCopies,
113) -> Result<MoeWeights> {
114 let p = format!("{layer_prefix}.mlp");
115
116 let gate = dense_auto(store, &format!("{p}.gate.weight"), gpu)?;
117 let shared_expert_gate = dense_auto(store, &format!("{p}.shared_expert_gate.weight"), gpu)?;
118
119 let inter = config.moe_intermediate_size;
120 let h = config.hidden_size;
121
122 let qctx = QuantizeCtx {
123 absmax_k,
124 quantize_k,
125 stream,
126 };
127
128 let fused_gate_up_key = format!("{p}.experts.gate_up_proj");
129 let fused_down_key = format!("{p}.experts.down_proj");
130 let is_fused = store.contains(&fused_gate_up_key) && store.contains(&fused_down_key);
136 anyhow::ensure!(
137 !is_fused || copies == Nvfp4MoeCopies::ALL,
138 "load_moe_qwen35: the fused expert layout builds every NVFP4 projection ({copies:?} asked)"
139 );
140 let fused_is_fp8 = is_fused
141 && store
142 .get(&fused_gate_up_key)
143 .map(|w| w.dtype == WeightDtype::FP8E4M3)
144 .unwrap_or(false);
145
146 let load_expert_fused = |expert_idx: usize| -> Result<ExpertWeight> {
147 let fused_gu = store.get(&fused_gate_up_key)?;
148 let fused_d = store.get(&fused_down_key)?;
149 if fused_is_fp8 {
150 let gu_s = store.get(&format!("{fused_gate_up_key}_scale_inv"))?;
151 let d_s = store.get(&format!("{fused_down_key}_scale_inv"))?;
152 let (gu_sn, gu_sk) = (gu_s.shape[1], gu_s.shape[2]);
153 let (d_sn, d_sk) = (d_s.shape[1], d_s.shape[2]);
154 let gu_s_f32 = gu_s.dtype == WeightDtype::FP32;
155 let d_s_f32 = d_s.dtype == WeightDtype::FP32;
156 let gu_w_stride = 2 * inter * h;
157 let d_w_stride = h * inter;
158 let gu_s_elem = if gu_s_f32 { 4 } else { 2 };
159 let d_s_elem = if d_s_f32 { 4 } else { 2 };
160 let gu_s_stride = gu_sn * gu_sk * gu_s_elem;
161 let d_s_stride = d_sn * d_sk * d_s_elem;
162 let gu_bf16 = dequant_fp8_block_slice_bf16(
165 gpu,
166 fused_gu.ptr.offset(expert_idx * gu_w_stride),
167 gu_s.ptr.offset(expert_idx * gu_s_stride),
168 2 * inter,
169 h,
170 gu_sn,
171 gu_sk,
172 gu_s_f32,
173 )?;
174 let down_bf16 = dequant_fp8_block_slice_bf16(
175 gpu,
176 fused_d.ptr.offset(expert_idx * d_w_stride),
177 d_s.ptr.offset(expert_idx * d_s_stride),
178 h,
179 inter,
180 d_sn,
181 d_sk,
182 d_s_f32,
183 )?;
184 let gate_dw = DenseWeight { weight: gu_bf16 };
185 let up_dw = DenseWeight {
186 weight: gu_bf16.offset(inter * h * 2),
187 };
188 let down_dw = DenseWeight { weight: down_bf16 };
189 let out = ExpertWeight {
190 gate_proj: quantize_to_nvfp4(
191 &gate_dw, inter, h, gpu, absmax_k, quantize_k, stream,
192 )?,
193 up_proj: quantize_to_nvfp4(&up_dw, inter, h, gpu, absmax_k, quantize_k, stream)?,
194 down_proj: quantize_to_nvfp4(
195 &down_dw, h, inter, gpu, absmax_k, quantize_k, stream,
196 )?,
197 };
198 gpu.free(gu_bf16)?;
199 gpu.free(down_bf16)?;
200 Ok(out)
201 } else {
202 let bf16 = 2usize;
203 let gu_per_expert_bytes = 2 * inter * h * bf16;
204 let d_per_expert_bytes = h * inter * bf16;
205 let gate_off = expert_idx * gu_per_expert_bytes;
206 let up_off = gate_off + inter * h * bf16;
207 let down_off = expert_idx * d_per_expert_bytes;
208 let gate_dw = DenseWeight {
209 weight: fused_gu.ptr.offset(gate_off),
210 };
211 let up_dw = DenseWeight {
212 weight: fused_gu.ptr.offset(up_off),
213 };
214 let down_dw = DenseWeight {
215 weight: fused_d.ptr.offset(down_off),
216 };
217 Ok(ExpertWeight {
218 gate_proj: quantize_to_nvfp4(
219 &gate_dw, inter, h, gpu, absmax_k, quantize_k, stream,
220 )?,
221 up_proj: quantize_to_nvfp4(&up_dw, inter, h, gpu, absmax_k, quantize_k, stream)?,
222 down_proj: quantize_to_nvfp4(
223 &down_dw, h, inter, gpu, absmax_k, quantize_k, stream,
224 )?,
225 })
226 }
227 };
228
229 let load_expert = |prefix: &str, gate_up: bool, down: bool| -> Result<ExpertWeight> {
232 let proj = |name: &str, n: usize, k: usize, on: bool| -> Result<QuantizedWeight> {
233 if on {
234 quantized_any(store, &format!("{prefix}.{name}"), n, k, gpu, variant, qctx)
235 } else {
236 Ok(QuantizedWeight::null())
237 }
238 };
239 Ok(ExpertWeight {
240 gate_proj: proj("gate_proj", inter, h, gate_up)?,
241 up_proj: proj("up_proj", inter, h, gate_up)?,
242 down_proj: proj("down_proj", h, inter, down)?,
243 })
244 };
245
246 let shared_expert = if copies.shared {
247 load_expert(&format!("{p}.shared_expert"), true, true)?
248 } else {
249 ExpertWeight::null()
250 };
251 let routed_any = copies.routed_gate_up || copies.routed_down;
252
253 let mut experts = Vec::with_capacity(num_experts);
254 for e in 0..num_experts {
255 if !routed_any || !config.is_local_expert(e) {
256 experts.push(ExpertWeight::null());
257 } else if is_fused {
258 experts.push(load_expert_fused(e)?);
259 } else {
260 experts.push(load_expert(
261 &format!("{p}.experts.{e}"),
262 copies.routed_gate_up,
263 copies.routed_down,
264 )?);
265 }
266 }
267
268 if is_fused {
272 if let Ok(w) = store.get(&fused_gate_up_key) {
273 let _ = gpu.free(w.ptr);
274 }
275 if let Ok(w) = store.get(&fused_down_key) {
276 let _ = gpu.free(w.ptr);
277 }
278 }
279
280 Ok(MoeWeights {
281 gate,
282 shared_expert,
283 shared_expert_gate,
284 experts,
285 router_pre_norm: None,
286 correction_bias: None,
287 })
288}
289
290pub fn load_moe_qwen35_fp8_experts(
294 store: &WeightStore,
295 layer_prefix: &str,
296 num_experts: usize,
297 gpu: &dyn GpuBackend,
298 config: &metrale_config::ModelConfig,
299) -> Result<Vec<Fp8ExpertWeight>> {
300 let p = format!("{layer_prefix}.mlp");
301 let mut fp8_experts = Vec::with_capacity(num_experts);
302
303 for e in 0..num_experts {
304 if config.is_local_expert(e) {
305 let ep = format!("{p}.experts.{e}");
306 fp8_experts.push(Fp8ExpertWeight {
307 gate_proj: load_fp8_block_scaled_as_fp8weight(
308 store,
309 &format!("{ep}.gate_proj"),
310 gpu,
311 )?,
312 up_proj: load_fp8_block_scaled_as_fp8weight(store, &format!("{ep}.up_proj"), gpu)?,
313 down_proj: load_fp8_block_scaled_as_fp8weight(
314 store,
315 &format!("{ep}.down_proj"),
316 gpu,
317 )?,
318 });
319 } else {
320 let null_block = Fp8Weight {
321 weight: DevicePtr::NULL,
322 row_scale: DevicePtr::NULL,
323 n: 0,
324 k: 0,
325 scale_format: WeightQuantFormat::Fp8BlockScaled,
326 };
327 fp8_experts.push(Fp8ExpertWeight {
328 gate_proj: null_block,
329 up_proj: null_block,
330 down_proj: null_block,
331 });
332 }
333 }
334
335 let shared_prefix = format!("{p}.shared_expert");
338 let _shared_fp8 = Fp8ExpertWeight {
339 gate_proj: load_fp8_block_scaled_as_fp8weight(
340 store,
341 &format!("{shared_prefix}.gate_proj"),
342 gpu,
343 )?,
344 up_proj: load_fp8_block_scaled_as_fp8weight(
345 store,
346 &format!("{shared_prefix}.up_proj"),
347 gpu,
348 )?,
349 down_proj: load_fp8_block_scaled_as_fp8weight(
350 store,
351 &format!("{shared_prefix}.down_proj"),
352 gpu,
353 )?,
354 };
355
356 Ok(fp8_experts)
357}
358
359pub fn load_moe_no_shared(
364 store: &WeightStore,
365 layer_prefix: &str,
366 num_experts: usize,
367 gpu: &dyn GpuBackend,
368 config: &metrale_config::ModelConfig,
369 variant: Nvfp4Variant,
370) -> Result<MoeWeights> {
371 let p = format!("{layer_prefix}.mlp");
372
373 let gate = dense(store, &format!("{p}.gate.weight"))?;
374
375 let h = config.hidden_size;
376 let inter = config.moe_intermediate_size;
377 let group_size = 16usize;
378
379 let gu_packed_bytes = inter * h / 2;
380 let gu_scale_bytes = inter * (h / group_size);
381 let d_packed_bytes = h * inter / 2;
382 let d_scale_bytes = h * (inter / group_size);
383
384 let alloc_zero = |size: usize| -> Result<DevicePtr> {
385 let ptr = gpu.alloc(size)?;
386 gpu.memset(ptr, 0, size)?;
387 Ok(ptr)
388 };
389
390 let mk_zero_quant = |packed_sz: usize, scale_sz: usize| -> Result<QuantizedWeight> {
391 Ok(QuantizedWeight {
392 weight: alloc_zero(packed_sz)?,
393 weight_scale: alloc_zero(scale_sz)?,
394 weight_scale_2: 0.0,
395 input_scale: DevicePtr::NULL,
396 weight_scale_2_vec: DevicePtr::NULL,
397 act: metrale_config::Nvfp4Act::Unstamped,
398 })
399 };
400
401 let shared_expert = ExpertWeight {
402 gate_proj: mk_zero_quant(gu_packed_bytes, gu_scale_bytes)?,
403 up_proj: mk_zero_quant(gu_packed_bytes, gu_scale_bytes)?,
404 down_proj: mk_zero_quant(d_packed_bytes, d_scale_bytes)?,
405 };
406 let shared_expert_gate = DenseWeight {
408 weight: alloc_zero(h * 2)?,
409 };
410
411 let mut experts = Vec::with_capacity(num_experts);
412 for e in 0..num_experts {
413 if config.is_local_expert(e) {
414 experts.push(ExpertWeight {
415 gate_proj: quantized_auto(
416 store,
417 &format!("{p}.experts.{e}.gate_proj"),
418 gpu,
419 variant,
420 )?,
421 up_proj: quantized_auto(store, &format!("{p}.experts.{e}.up_proj"), gpu, variant)?,
422 down_proj: quantized_auto(
423 store,
424 &format!("{p}.experts.{e}.down_proj"),
425 gpu,
426 variant,
427 )?,
428 });
429 } else {
430 experts.push(ExpertWeight::null());
431 }
432 }
433
434 Ok(MoeWeights {
435 gate,
436 shared_expert,
437 shared_expert_gate,
438 experts,
439 router_pre_norm: None,
440 correction_bias: None,
441 })
442}