1#![allow(unused_imports)]
11
12use anyhow::{Context, Result, bail, ensure};
13use metrale_gpu_runtime::gpu::{DevicePtr, GpuBackend};
14use metrale_model_weights::weights::{WeightDtype, WeightStore};
15
16use super::*;
17
18pub fn dequant_nvfp4_to_bf16(
24 store: &WeightStore,
25 prefix: &str,
26 n: usize,
27 k: usize,
28 gpu: &dyn GpuBackend,
29) -> Result<DenseWeight> {
30 let total = n * k;
31
32 let (packed_ptr, scale_ptr, global_scale, is_reciprocal) =
33 if store.contains(&format!("{prefix}.weight_packed")) {
34 let pp = ptr(store, &format!("{prefix}.weight_packed"))?;
35 let sp = ptr(store, &format!("{prefix}.weight_scale"))?;
36 let gs = scalar_f32(store, &format!("{prefix}.weight_global_scale"), gpu)?;
37 (pp, sp, gs, true)
38 } else {
39 let pp = ptr(store, &format!("{prefix}.weight"))?;
40 let sp = ptr(store, &format!("{prefix}.weight_scale"))?;
41 let gs = scalar_f32(store, &format!("{prefix}.weight_scale_2"), gpu)?;
42 (pp, sp, gs, false)
43 };
44
45 let combined_global = if is_reciprocal {
50 if global_scale != 0.0 {
51 1.0 / global_scale
52 } else {
53 0.0
54 }
55 } else {
56 global_scale
57 };
58
59 let out = gpu.alloc(total * 2)?;
62 let kernel = gpu.kernel("dequant_nvfp4_bf16", "dequant_nvfp4_to_bf16")?;
63 let stream = gpu.default_stream();
64 metrale_gpu_runtime::kernel_args::KernelLaunch::new(gpu, kernel)
65 .grid([n as u32, 1, 1])
66 .block([256, 1, 1])
67 .arg_ptr(packed_ptr)
68 .arg_ptr(scale_ptr)
69 .arg_ptr(out)
70 .arg_f32(combined_global)
71 .arg_u32(n as u32)
72 .arg_u32(k as u32)
73 .launch(stream)?;
74 gpu.synchronize(stream)?;
75 Ok(DenseWeight { weight: out })
76}
77
78#[allow(dead_code)]
83pub(crate) fn dequant_nvfp4_e8m0_to_bf16(
84 store: &WeightStore,
85 prefix: &str,
86 n: usize,
87 k: usize,
88 gpu: &dyn GpuBackend,
89) -> Result<DenseWeight> {
90 let total = n * k;
91 let packed_bytes = total / 2;
92 let packed_ptr = ptr(store, &format!("{prefix}.weight"))?;
93 let scale_t = store.get(&format!("{prefix}.scale"))?;
94 let num_groups = scale_t.num_elements();
95 ensure!(
96 num_groups > 0 && total.is_multiple_of(num_groups),
97 "{prefix}: weight elems {total} not divisible by E8M0 scale groups {num_groups}"
98 );
99 let block = total / num_groups;
100
101 let mut packed = vec![0u8; packed_bytes];
102 let mut scales = vec![0u8; num_groups];
103 gpu.copy_d2h(packed_ptr, &mut packed)?;
104 gpu.copy_d2h(scale_t.ptr, &mut scales)?;
105
106 let e2m1_table: [f32; 16] = [
107 0.0, 0.5, 1.0, 1.5, 2.0, 3.0, 4.0, 6.0, -0.0, -0.5, -1.0, -1.5, -2.0, -3.0, -4.0, -6.0,
108 ];
109 let mut bf16_out = vec![0u16; total];
113 for group in 0..num_groups {
114 let block_scale = fp8_e8m0_to_f32(scales[group]);
115 for elem in 0..block {
116 let flat_idx = group * block + elem;
117 let byte_idx = flat_idx / 2;
118 let nibble = if flat_idx.is_multiple_of(2) {
119 packed[byte_idx] & 0x0F
120 } else {
121 (packed[byte_idx] >> 4) & 0x0F
122 };
123 bf16_out[flat_idx] = f32_to_bf16(e2m1_table[nibble as usize] * block_scale);
124 }
125 }
126
127 let buf = gpu.alloc(total * 2)?;
128 let bf16_bytes: &[u8] =
134 unsafe { std::slice::from_raw_parts(bf16_out.as_ptr() as *const u8, total * 2) };
135 gpu.copy_h2d(bf16_bytes, buf)?;
136 Ok(DenseWeight { weight: buf })
137}
138
139pub(super) use metrale_core::numeric::{FP8_E4M3_LUT, f32_to_bf16, fp8_e4m3_to_f32};
142
143const FP8_E8M0_LUT: [f32; 256] = {
146 let mut table = [0.0f32; 256];
147 let mut i: u32 = 0;
148 while i < 256 {
149 let exp = i as u8;
150 table[i as usize] = if exp == 0 {
151 0.0f32
152 } else if exp == 255 {
153 0.0f32
154 } else {
155 f32::from_bits((exp as u32) << 23)
156 };
157 i += 1;
158 }
159 table
160};
161
162#[inline(always)]
164pub(super) fn fp8_e8m0_to_f32(bits: u8) -> f32 {
165 FP8_E8M0_LUT[bits as usize]
166}
167
168pub fn load_dense_ffn(
171 store: &WeightStore,
172 prefix: &str,
173 gpu: &dyn GpuBackend,
174 variant: Nvfp4Variant,
175 absmax_k: metrale_gpu_runtime::gpu::KernelHandle,
176 quantize_k: metrale_gpu_runtime::gpu::KernelHandle,
177 stream: u64,
178 config: &metrale_config::ModelConfig,
179) -> Result<crate::layers::dense_ffn::DenseFfnWeights> {
180 use crate::layers::dense_ffn::DenseFfnWeights;
181 match variant {
182 Nvfp4Variant::Fp8Dequanted => {
183 let inter = if config.intermediate_size > 0 {
186 config.intermediate_size
187 } else {
188 config.moe_intermediate_size
189 };
190 let h = config.hidden_size;
191 let gate = quantized_from_fp8(
192 store,
193 &format!("{prefix}.mlp.gate_proj"),
194 inter,
195 h,
196 gpu,
197 absmax_k,
198 quantize_k,
199 stream,
200 )?;
201 let up = quantized_from_fp8(
202 store,
203 &format!("{prefix}.mlp.up_proj"),
204 inter,
205 h,
206 gpu,
207 absmax_k,
208 quantize_k,
209 stream,
210 )?;
211 let down = quantized_from_fp8(
212 store,
213 &format!("{prefix}.mlp.down_proj"),
214 h,
215 inter,
216 gpu,
217 absmax_k,
218 quantize_k,
219 stream,
220 )?;
221 Ok(DenseFfnWeights {
223 gate_proj: gate,
224 up_proj: up,
225 down_proj: down,
226 gate_proj_t: Some(gate.transpose_for_gemm(gpu, inter, h)?),
227 up_proj_t: Some(up.transpose_for_gemm(gpu, inter, h)?),
228 down_proj_t: Some(down.transpose_for_gemm(gpu, h, inter)?),
229 })
230 }
231 Nvfp4Variant::Bf16Raw => {
232 let inter = if config.intermediate_size > 0 {
235 config.intermediate_size
236 } else {
237 config.moe_intermediate_size
238 };
239 let h = config.hidden_size;
240 let qctx = QuantizeCtx {
241 absmax_k,
242 quantize_k,
243 stream,
244 };
245 let gate = quantized_any(
246 store,
247 &format!("{prefix}.mlp.gate_proj"),
248 inter,
249 h,
250 gpu,
251 variant,
252 qctx,
253 )?;
254 let up = quantized_any(
255 store,
256 &format!("{prefix}.mlp.up_proj"),
257 inter,
258 h,
259 gpu,
260 variant,
261 qctx,
262 )?;
263 let down = quantized_any(
264 store,
265 &format!("{prefix}.mlp.down_proj"),
266 h,
267 inter,
268 gpu,
269 variant,
270 qctx,
271 )?;
272 Ok(DenseFfnWeights {
273 gate_proj: gate,
274 up_proj: up,
275 down_proj: down,
276 gate_proj_t: Some(gate.transpose_for_gemm(gpu, inter, h)?),
277 up_proj_t: Some(up.transpose_for_gemm(gpu, inter, h)?),
278 down_proj_t: Some(down.transpose_for_gemm(gpu, h, inter)?),
279 })
280 }
281 _ => {
282 let inter_ = if config.intermediate_size > 0 {
286 config.intermediate_size
287 } else {
288 config.moe_intermediate_size
289 };
290 let h_ = config.hidden_size;
291 let qctx = QuantizeCtx {
292 absmax_k,
293 quantize_k,
294 stream,
295 };
296 let gate = quantized_any(
297 store,
298 &format!("{prefix}.mlp.gate_proj"),
299 inter_,
300 h_,
301 gpu,
302 variant,
303 qctx,
304 )?;
305 let up = quantized_any(
306 store,
307 &format!("{prefix}.mlp.up_proj"),
308 inter_,
309 h_,
310 gpu,
311 variant,
312 qctx,
313 )?;
314 let down = quantized_any(
315 store,
316 &format!("{prefix}.mlp.down_proj"),
317 h_,
318 inter_,
319 gpu,
320 variant,
321 qctx,
322 )?;
323 let inter = if config.intermediate_size > 0 {
324 config.intermediate_size
325 } else {
326 config.moe_intermediate_size
327 };
328 let h = config.hidden_size;
329 Ok(DenseFfnWeights {
330 gate_proj: gate,
331 up_proj: up,
332 down_proj: down,
333 gate_proj_t: Some(gate.transpose_for_gemm(gpu, inter, h)?),
334 up_proj_t: Some(up.transpose_for_gemm(gpu, inter, h)?),
335 down_proj_t: Some(down.transpose_for_gemm(gpu, h, inter)?),
336 })
337 }
338 }
339}
340
341#[allow(dead_code)]
343pub(crate) fn load_mtp_qwen35(
344 store: &WeightStore,
345 num_experts: usize,
346 gpu: &dyn GpuBackend,
347 variant: Nvfp4Variant,
348) -> Result<MtpWeights> {
349 load_mtp(store, num_experts, gpu, variant)
350}
351
352pub fn gpu_concat_rows(
356 a: &DenseWeight,
357 a_rows: usize,
358 b: &DenseWeight,
359 b_rows: usize,
360 k: usize,
361 gpu: &dyn GpuBackend,
362) -> Result<DenseWeight> {
363 let a_bytes = a_rows * k * 2;
364 let b_bytes = b_rows * k * 2;
365 let total = a_bytes + b_bytes;
366 let buf = gpu.alloc(total)?;
367 tracing::debug!(
368 target: "metrale_model_layers::weight_map::concat",
369 a_rows, b_rows, k, a_bytes, b_bytes, total,
370 a = a.weight.0, b = b.weight.0, buf = buf.0,
371 "gpu concat rows [a;b]"
372 );
373 gpu.copy_d2d(a.weight, buf, a_bytes)?;
374 gpu.copy_d2d(b.weight, buf.offset(a_bytes), b_bytes)?;
375 Ok(DenseWeight { weight: buf })
376}
377
378pub fn interleave_ba(
385 a_weight: &DenseWeight,
386 b_weight: &DenseWeight,
387 nv: usize,
388 nk: usize,
389 k: usize,
390 gpu: &dyn GpuBackend,
391) -> Result<DenseWeight> {
392 let vpg = nv / nk;
393 let row_bytes = k * 2;
394 let ba_size = nv * 2;
395
396 let mut a_cpu = vec![0u8; nv * row_bytes];
397 let mut b_cpu = vec![0u8; nv * row_bytes];
398 gpu.copy_d2h(a_weight.weight, &mut a_cpu)?;
399 gpu.copy_d2h(b_weight.weight, &mut b_cpu)?;
400
401 let mut ba_cpu = vec![0u8; ba_size * row_bytes];
402 for g in 0..nk {
403 for v in 0..vpg {
404 let vh = g * vpg + v;
405 let dst_row = g * (2 * vpg) + v;
406 ba_cpu[dst_row * row_bytes..(dst_row + 1) * row_bytes]
407 .copy_from_slice(&b_cpu[vh * row_bytes..(vh + 1) * row_bytes]);
408 let dst_row = g * (2 * vpg) + vpg + v;
409 ba_cpu[dst_row * row_bytes..(dst_row + 1) * row_bytes]
410 .copy_from_slice(&a_cpu[vh * row_bytes..(vh + 1) * row_bytes]);
411 }
412 }
413
414 let buf = gpu.alloc(ba_size * row_bytes)?;
415 gpu.copy_h2d(&ba_cpu, buf)?;
416 Ok(DenseWeight { weight: buf })
417}