1#![allow(unused_imports)]
6
7use anyhow::{Context, Result, bail, ensure};
8use spark_runtime::gpu::{DevicePtr, GpuBackend};
9use spark_runtime::weights::{WeightDtype, WeightStore};
10
11use super::*;
12
13#[path = "ssm_qwen35/dequant_fp8.rs"]
14mod dequant_fp8;
15use dequant_fp8::dequant_fp8_block_slice_bf16;
16
17pub struct SsmWeightsQwen35 {
19 pub in_proj_qkv: DenseWeight,
21 pub in_proj_z: DenseWeight,
23 pub in_proj_a: DenseWeight,
25 pub in_proj_b: DenseWeight,
27 pub conv1d: DenseWeight,
29 pub a_log: DenseWeight,
31 pub dt_bias: DenseWeight,
33 pub norm: DenseWeight,
35 pub out_proj: DenseWeight,
37}
38
39pub(crate) fn load_ssm_qwen35(
41 store: &WeightStore,
42 layer_prefix: &str,
43 gpu: &dyn GpuBackend,
44 _variant: Nvfp4Variant,
47) -> Result<SsmWeightsQwen35> {
48 let p = format!("{layer_prefix}.linear_attn");
49
50 let load_proj = |prefix: &str| -> Result<DenseWeight> {
59 if store.contains(&format!("{prefix}.weight_packed")) {
60 let shape = store.get(&format!("{prefix}.weight_packed"))?.shape.clone();
61 dequant_nvfp4_to_bf16(store, prefix, shape[0], shape[1] * 2, gpu)
62 } else {
63 dense_auto(store, &format!("{prefix}.weight"), gpu)
64 }
65 };
66
67 Ok(SsmWeightsQwen35 {
68 in_proj_qkv: load_proj(&format!("{p}.in_proj_qkv"))?,
69 in_proj_z: load_proj(&format!("{p}.in_proj_z"))?,
70 in_proj_a: load_proj(&format!("{p}.in_proj_a"))?,
71 in_proj_b: load_proj(&format!("{p}.in_proj_b"))?,
72 conv1d: dense_auto(store, &format!("{p}.conv1d.weight"), gpu)?,
73 a_log: dense_keep_f32(store, &format!("{p}.A_log"), gpu)?,
76 dt_bias: dense_keep_f32(store, &format!("{p}.dt_bias"), gpu)?,
77 norm: dense_f32_safe(store, &format!("{p}.norm.weight"), gpu)?,
79 out_proj: load_proj(&format!("{p}.out_proj"))?,
80 })
81}
82
83pub(crate) fn load_moe_qwen35(
90 store: &WeightStore,
91 layer_prefix: &str,
92 num_experts: usize,
93 gpu: &dyn GpuBackend,
94 config: &atlas_core::config::ModelConfig,
95 variant: Nvfp4Variant,
96 absmax_k: spark_runtime::gpu::KernelHandle,
97 quantize_k: spark_runtime::gpu::KernelHandle,
98 stream: u64,
99 skip_routed_experts: bool,
100) -> Result<MoeWeights> {
101 let p = format!("{layer_prefix}.mlp");
102
103 let gate = dense_auto(store, &format!("{p}.gate.weight"), gpu)?;
104 let shared_expert_gate = dense_auto(store, &format!("{p}.shared_expert_gate.weight"), gpu)?;
105
106 let inter = config.moe_intermediate_size;
107 let h = config.hidden_size;
108
109 let qctx = QuantizeCtx {
110 absmax_k,
111 quantize_k,
112 stream,
113 };
114
115 let fused_gate_up_key = format!("{p}.experts.gate_up_proj");
120 let fused_down_key = format!("{p}.experts.down_proj");
121 let is_fused = store.contains(&fused_gate_up_key) && store.contains(&fused_down_key);
133 let fused_is_fp8 = is_fused
134 && store
135 .get(&fused_gate_up_key)
136 .map(|w| w.dtype == WeightDtype::FP8E4M3)
137 .unwrap_or(false);
138
139 let load_expert_fused = |expert_idx: usize| -> Result<ExpertWeight> {
140 let fused_gu = store.get(&fused_gate_up_key)?;
141 let fused_d = store.get(&fused_down_key)?;
142 if fused_is_fp8 {
143 let gu_s = store.get(&format!("{fused_gate_up_key}_scale_inv"))?;
146 let d_s = store.get(&format!("{fused_down_key}_scale_inv"))?;
147 let (gu_sn, gu_sk) = (gu_s.shape[1], gu_s.shape[2]);
148 let (d_sn, d_sk) = (d_s.shape[1], d_s.shape[2]);
149 let gu_s_f32 = gu_s.dtype == WeightDtype::FP32;
150 let d_s_f32 = d_s.dtype == WeightDtype::FP32;
151 let gu_w_stride = 2 * inter * h; let d_w_stride = h * inter;
153 let gu_s_elem = if gu_s_f32 { 4 } else { 2 };
154 let d_s_elem = if d_s_f32 { 4 } else { 2 };
155 let gu_s_stride = gu_sn * gu_sk * gu_s_elem;
156 let d_s_stride = d_sn * d_sk * d_s_elem;
157 let gu_bf16 = dequant_fp8_block_slice_bf16(
160 gpu,
161 fused_gu.ptr.offset(expert_idx * gu_w_stride),
162 gu_s.ptr.offset(expert_idx * gu_s_stride),
163 2 * inter,
164 h,
165 gu_sn,
166 gu_sk,
167 gu_s_f32,
168 )?;
169 let down_bf16 = dequant_fp8_block_slice_bf16(
170 gpu,
171 fused_d.ptr.offset(expert_idx * d_w_stride),
172 d_s.ptr.offset(expert_idx * d_s_stride),
173 h,
174 inter,
175 d_sn,
176 d_sk,
177 d_s_f32,
178 )?;
179 let gate_dw = DenseWeight { weight: gu_bf16 };
180 let up_dw = DenseWeight {
181 weight: gu_bf16.offset(inter * h * 2), };
183 let down_dw = DenseWeight { weight: down_bf16 };
184 let out = ExpertWeight {
185 gate_proj: quantize_to_nvfp4(
186 &gate_dw, inter, h, gpu, absmax_k, quantize_k, stream,
187 )?,
188 up_proj: quantize_to_nvfp4(&up_dw, inter, h, gpu, absmax_k, quantize_k, stream)?,
189 down_proj: quantize_to_nvfp4(
190 &down_dw, h, inter, gpu, absmax_k, quantize_k, stream,
191 )?,
192 };
193 gpu.free(gu_bf16)?;
194 gpu.free(down_bf16)?;
195 Ok(out)
196 } else {
197 let bf16 = 2usize;
199 let gu_per_expert_bytes = 2 * inter * h * bf16;
200 let d_per_expert_bytes = h * inter * bf16;
201 let gate_off = expert_idx * gu_per_expert_bytes;
202 let up_off = gate_off + inter * h * bf16;
203 let down_off = expert_idx * d_per_expert_bytes;
204 let gate_dw = DenseWeight {
205 weight: fused_gu.ptr.offset(gate_off),
206 };
207 let up_dw = DenseWeight {
208 weight: fused_gu.ptr.offset(up_off),
209 };
210 let down_dw = DenseWeight {
211 weight: fused_d.ptr.offset(down_off),
212 };
213 Ok(ExpertWeight {
214 gate_proj: quantize_to_nvfp4(
215 &gate_dw, inter, h, gpu, absmax_k, quantize_k, stream,
216 )?,
217 up_proj: quantize_to_nvfp4(&up_dw, inter, h, gpu, absmax_k, quantize_k, stream)?,
218 down_proj: quantize_to_nvfp4(
219 &down_dw, h, inter, gpu, absmax_k, quantize_k, stream,
220 )?,
221 })
222 }
223 };
224
225 let load_expert = |prefix: &str| -> Result<ExpertWeight> {
233 Ok(ExpertWeight {
234 gate_proj: quantized_any(
235 store,
236 &format!("{prefix}.gate_proj"),
237 inter,
238 h,
239 gpu,
240 variant,
241 qctx,
242 )?,
243 up_proj: quantized_any(
244 store,
245 &format!("{prefix}.up_proj"),
246 inter,
247 h,
248 gpu,
249 variant,
250 qctx,
251 )?,
252 down_proj: quantized_any(
253 store,
254 &format!("{prefix}.down_proj"),
255 h,
256 inter,
257 gpu,
258 variant,
259 qctx,
260 )?,
261 })
262 };
263
264 let shared_expert = load_expert(&format!("{p}.shared_expert"))?;
265
266 let mut experts = Vec::with_capacity(num_experts);
267 for e in 0..num_experts {
268 if skip_routed_experts || !config.is_local_expert(e) {
269 experts.push(ExpertWeight::null());
270 } else if is_fused {
271 experts.push(load_expert_fused(e)?);
272 } else {
273 experts.push(load_expert(&format!("{p}.experts.{e}"))?);
274 }
275 }
276
277 if is_fused {
285 if let Ok(w) = store.get(&fused_gate_up_key) {
286 let _ = gpu.free(w.ptr);
287 }
288 if let Ok(w) = store.get(&fused_down_key) {
289 let _ = gpu.free(w.ptr);
290 }
291 }
292
293 Ok(MoeWeights {
294 gate,
295 shared_expert,
296 shared_expert_gate,
297 experts,
298 router_pre_norm: None,
299 correction_bias: None,
300 })
301}
302
303pub(crate) fn load_moe_qwen35_fp8_experts(
308 store: &WeightStore,
309 layer_prefix: &str,
310 num_experts: usize,
311 gpu: &dyn GpuBackend,
312 config: &atlas_core::config::ModelConfig,
313) -> Result<Vec<Fp8ExpertWeight>> {
314 let p = format!("{layer_prefix}.mlp");
315 let mut fp8_experts = Vec::with_capacity(num_experts);
316
317 for e in 0..num_experts {
318 if config.is_local_expert(e) {
319 let ep = format!("{p}.experts.{e}");
320 fp8_experts.push(Fp8ExpertWeight {
321 gate_proj: load_fp8_block_scaled_as_fp8weight(
322 store,
323 &format!("{ep}.gate_proj"),
324 gpu,
325 )?,
326 up_proj: load_fp8_block_scaled_as_fp8weight(store, &format!("{ep}.up_proj"), gpu)?,
327 down_proj: load_fp8_block_scaled_as_fp8weight(
328 store,
329 &format!("{ep}.down_proj"),
330 gpu,
331 )?,
332 });
333 } else {
334 let null_block = Fp8Weight {
340 weight: DevicePtr::NULL,
341 row_scale: DevicePtr::NULL,
342 n: 0,
343 k: 0,
344 scale_format: WeightQuantFormat::Fp8BlockScaled,
345 };
346 fp8_experts.push(Fp8ExpertWeight {
347 gate_proj: null_block,
348 up_proj: null_block,
349 down_proj: null_block,
350 });
351 }
352 }
353
354 let shared_prefix = format!("{p}.shared_expert");
356 let _shared_fp8 = Fp8ExpertWeight {
357 gate_proj: load_fp8_block_scaled_as_fp8weight(
358 store,
359 &format!("{shared_prefix}.gate_proj"),
360 gpu,
361 )?,
362 up_proj: load_fp8_block_scaled_as_fp8weight(
363 store,
364 &format!("{shared_prefix}.up_proj"),
365 gpu,
366 )?,
367 down_proj: load_fp8_block_scaled_as_fp8weight(
368 store,
369 &format!("{shared_prefix}.down_proj"),
370 gpu,
371 )?,
372 };
373
374 Ok(fp8_experts)
375}
376
377pub(crate) fn load_moe_no_shared(
383 store: &WeightStore,
384 layer_prefix: &str,
385 num_experts: usize,
386 gpu: &dyn GpuBackend,
387 config: &atlas_core::config::ModelConfig,
388 variant: Nvfp4Variant,
389) -> Result<MoeWeights> {
390 let p = format!("{layer_prefix}.mlp");
391
392 let gate = dense(store, &format!("{p}.gate.weight"))?;
393
394 let h = config.hidden_size;
400 let inter = config.moe_intermediate_size;
401 let group_size = 16usize; let gu_packed_bytes = inter * h / 2;
405 let gu_scale_bytes = inter * (h / group_size);
406 let d_packed_bytes = h * inter / 2;
408 let d_scale_bytes = h * (inter / group_size);
409
410 let alloc_zero = |size: usize| -> Result<DevicePtr> {
411 let ptr = gpu.alloc(size)?;
412 gpu.memset(ptr, 0, size)?;
413 Ok(ptr)
414 };
415
416 let mk_zero_quant = |packed_sz: usize, scale_sz: usize| -> Result<QuantizedWeight> {
417 Ok(QuantizedWeight {
418 weight: alloc_zero(packed_sz)?,
419 weight_scale: alloc_zero(scale_sz)?,
420 weight_scale_2: 0.0,
421 input_scale: DevicePtr::NULL,
422 weight_scale_2_vec: DevicePtr::NULL,
423 })
424 };
425
426 let shared_expert = ExpertWeight {
427 gate_proj: mk_zero_quant(gu_packed_bytes, gu_scale_bytes)?,
428 up_proj: mk_zero_quant(gu_packed_bytes, gu_scale_bytes)?,
429 down_proj: mk_zero_quant(d_packed_bytes, d_scale_bytes)?,
430 };
431 let shared_expert_gate = DenseWeight {
434 weight: alloc_zero(h * 2)?,
435 };
436
437 let mut experts = Vec::with_capacity(num_experts);
438 for e in 0..num_experts {
439 if config.is_local_expert(e) {
440 experts.push(ExpertWeight {
441 gate_proj: quantized_auto(
442 store,
443 &format!("{p}.experts.{e}.gate_proj"),
444 gpu,
445 variant,
446 )?,
447 up_proj: quantized_auto(store, &format!("{p}.experts.{e}.up_proj"), gpu, variant)?,
448 down_proj: quantized_auto(
449 store,
450 &format!("{p}.experts.{e}.down_proj"),
451 gpu,
452 variant,
453 )?,
454 });
455 } else {
456 experts.push(ExpertWeight::null());
457 }
458 }
459
460 Ok(MoeWeights {
461 gate,
462 shared_expert,
463 shared_expert_gate,
464 experts,
465 router_pre_norm: None,
466 correction_bias: None,
467 })
468}