metrale_model_layers/layers/qwen3_ssm/init_q2.rs
1// SPDX-License-Identifier: MIT OR Apache-2.0
2
3//! 2026-09-25: The packed Q2_0 fused `in_proj_qkvz`: its install and its
4//! prefill GEMM.
5//!
6//! Owner: model-layers (qwen3_ssm).
7//! Invariants: none beyond the types.
8
9use super::*;
10
11impl Qwen3SsmLayer {
12 /// 2026-09-25: Install the packed Q2_0 fused `in_proj_qkvz`. Decode runs
13 /// `q2_0_gemv_vec` on it and prefill runs `qkvz_q2_prefill_gemm`. The layer
14 /// must be `sequential_qkvz`: both paths write the projection straight into
15 /// the deinterleaved buffer, and decode reads `qkvz_q2` only on the
16 /// sequential branch.
17 ///
18 /// The MMQ kernels are looked up here rather than in `new`, so a layer that
19 /// never installs a packed weight issues no lookup and leaves no failed
20 /// row in the boot kernel audit.
21 pub fn set_packed_q2_qkvz(
22 &mut self,
23 qkvz: crate::weight_map::PackedQ2Weight,
24 gpu: &dyn GpuBackend,
25 ) {
26 self.qkvz_q2 = Some(qkvz);
27 self.q2_0_mmq_nc_k = super::super::try_kernel(gpu, "q2_0_mmq", "metrale_q2_0_mmq128_nc");
28 self.q2_0_mmq_wc_k = super::super::try_kernel(gpu, "q2_0_mmq", "metrale_q2_0_mmq128_wc");
29 self.q4k_quant_act_k =
30 super::super::try_kernel(gpu, "q4k_mmq", "metrale_q8_1_quantize_ds4_bf16");
31 }
32
33 /// 2026-09-25: `out[m, n] = input[m, k] @ W^T` for the packed qkvz.
34 ///
35 /// With both MMQ handles resolved, `METRALE_GGUF_NATIVE_Q2_MMQ=1` and group
36 /// size 128, it quantizes `input` to q8_1 into `act_q8` and runs
37 /// `q2_0_mmq_gemm`. Otherwise it dequantizes the weight to BF16 into
38 /// `scratch` and runs `dense_gemm_bf16_pipelined` (or `dense_gemm` when that
39 /// handle is 0) on the same `stream`. Allocates nothing. Errors when no
40 /// packed qkvz is installed or the dequant kernel is missing.
41 #[allow(clippy::too_many_arguments)]
42 pub(crate) fn qkvz_q2_prefill_gemm(
43 &self,
44 gpu: &dyn GpuBackend,
45 input: DevicePtr,
46 out: DevicePtr,
47 scratch: DevicePtr,
48 act_q8: DevicePtr,
49 m: u32,
50 stream: u64,
51 ) -> Result<()> {
52 let w = self
53 .qkvz_q2
54 .as_ref()
55 .ok_or_else(|| anyhow::anyhow!("qkvz_q2_prefill_gemm: no packed qkvz installed"))?;
56 let (n, k) = (w.n, w.k);
57
58 if self.q2_0_mmq_nc_k.0 != 0
59 && self.q4k_quant_act_k.0 != 0
60 && crate::layers::ops::native_q2_mmq_enabled()
61 && w.group == 128
62 {
63 crate::layers::ops::quantize_act_q8_1(
64 gpu,
65 self.q4k_quant_act_k,
66 input,
67 act_q8,
68 m,
69 k,
70 stream,
71 )?;
72 return crate::layers::ops::q2_0_mmq_gemm(
73 gpu,
74 self.q2_0_mmq_nc_k,
75 self.q2_0_mmq_wc_k,
76 act_q8,
77 w.weight,
78 out,
79 m,
80 n,
81 k,
82 stream,
83 );
84 }
85
86 if self.dequant_q2_0_gn_k.0 == 0 {
87 anyhow::bail!(
88 "dequant_q2_0_gn_to_bf16 kernel missing — packed-Q2 GDN prefill unavailable"
89 );
90 }
91 crate::layers::ops::dequant_q2_0_gn_to_bf16(
92 gpu,
93 self.dequant_q2_0_gn_k,
94 w.weight,
95 scratch,
96 n,
97 k,
98 w.group as u32,
99 stream,
100 )?;
101 let dw = DenseWeight { weight: scratch };
102 if self.dense_gemm_pipelined_k.0 != 0 {
103 crate::layers::ops::dense_gemm_bf16_pipelined(
104 gpu,
105 self.dense_gemm_pipelined_k,
106 input,
107 &dw,
108 out,
109 m,
110 n,
111 k,
112 stream,
113 )?;
114 } else {
115 crate::layers::ops::dense_gemm(
116 gpu,
117 self.dense_gemm_k,
118 input,
119 &dw,
120 out,
121 m,
122 n,
123 k,
124 stream,
125 )?;
126 }
127 Ok(())
128 }
129}