spark_model/layers/qwen3_ssm/init_q2.rs
1// SPDX-License-Identifier: AGPL-3.0-only
2
3//! Q2_0 keep-packed qkvz init + transient-dequant prefill GEMM.
4
5use super::*;
6
7impl Qwen3SsmLayer {
8 /// Install the Tier-1c keep-packed ternary Q2_0 fused `in_proj_qkvz`
9 /// (`ATLAS_GGUF_NATIVE_Q2`). Decode dispatches `q2_0_gemv_vec`; prefill
10 /// transient-dequants via `Self::qkvz_q2_prefill_gemm`. `out_proj` is
11 /// unaffected (stays NVFP4). Requires `sequential_qkvz` (Bonsai concats
12 /// [Q|K|V|Z] at load).
13 ///
14 /// The keep-packed MMQ kernels are resolved HERE, not in the constructor:
15 /// they ship only in GGUF-serving targets, and probing them on models
16 /// that never install packed-Q2 weights fails the fail-closed boot audit
17 /// on every other GDN target.
18 pub fn set_packed_q2_qkvz(
19 &mut self,
20 qkvz: crate::weight_map::PackedQ2Weight,
21 gpu: &dyn GpuBackend,
22 ) {
23 self.qkvz_q2 = Some(qkvz);
24 self.q2_0_mmq_nc_k = super::super::try_kernel(gpu, "q2_0_mmq", "atlas_q2_0_mmq128_nc");
25 self.q2_0_mmq_wc_k = super::super::try_kernel(gpu, "q2_0_mmq", "atlas_q2_0_mmq128_wc");
26 self.q4k_quant_act_k =
27 super::super::try_kernel(gpu, "q4k_mmq", "atlas_q8_1_quantize_ds4_bf16");
28 }
29
30 /// Transient-dequant prefill GEMM for the packed qkvz: dequant the 2-bit
31 /// `[qkvz_size, h]` weight into the caller-provided PERSISTENT BF16 `scratch`
32 /// (the arena `q2_dequant_scratch`, sized to the largest packed projection),
33 /// run `dense_gemm` (`out[m, qkvz_size] = in[m, h] @ w^T`). Mirrors the
34 /// FFN/attention packed prefill — no per-matmul alloc/sync/free; the dequant
35 /// orders before the GEMM on the same `stream`. Errors if the dequant kernel
36 /// is absent.
37 #[allow(clippy::too_many_arguments)]
38 pub(crate) fn qkvz_q2_prefill_gemm(
39 &self,
40 gpu: &dyn GpuBackend,
41 input: DevicePtr,
42 out: DevicePtr,
43 scratch: DevicePtr,
44 act_q8: DevicePtr,
45 m: u32,
46 stream: u64,
47 ) -> Result<()> {
48 let w = self
49 .qkvz_q2
50 .as_ref()
51 .ok_or_else(|| anyhow::anyhow!("qkvz_q2_prefill_gemm: no packed qkvz installed"))?;
52 let (n, k) = (w.n, w.k);
53
54 // Tier-2 native MMQ (ATLAS_GGUF_NATIVE_Q2_MMQ=1): quantize `input` to q8_1
55 // then run the packed 2-bit MMQ GEMM for the fused qkvz — no BF16 weight
56 // dequant, no shared `q2_dequant_scratch` race. Group-128 only.
57 if self.q2_0_mmq_nc_k.0 != 0
58 && self.q4k_quant_act_k.0 != 0
59 && crate::layers::ops::native_q2_mmq_enabled()
60 && w.group == 128
61 {
62 crate::layers::ops::quantize_act_q8_1(
63 gpu,
64 self.q4k_quant_act_k,
65 input,
66 act_q8,
67 m,
68 k,
69 stream,
70 )?;
71 return crate::layers::ops::q2_0_mmq_gemm(
72 gpu,
73 self.q2_0_mmq_nc_k,
74 self.q2_0_mmq_wc_k,
75 act_q8,
76 w.weight,
77 out,
78 m,
79 n,
80 k,
81 stream,
82 );
83 }
84
85 if self.dequant_q2_0_gn_k.0 == 0 {
86 anyhow::bail!(
87 "dequant_q2_0_gn_to_bf16 kernel missing — packed-Q2 GDN prefill unavailable"
88 );
89 }
90 crate::layers::ops::dequant_q2_0_gn_to_bf16(
91 gpu,
92 self.dequant_q2_0_gn_k,
93 w.weight,
94 scratch,
95 n,
96 k,
97 w.group as u32,
98 stream,
99 )?;
100 let dw = DenseWeight { weight: scratch };
101 if self.dense_gemm_pipelined_k.0 != 0 {
102 crate::layers::ops::dense_gemm_bf16_pipelined(
103 gpu,
104 self.dense_gemm_pipelined_k,
105 input,
106 &dw,
107 out,
108 m,
109 n,
110 k,
111 stream,
112 )?;
113 } else {
114 crate::layers::ops::dense_gemm(
115 gpu,
116 self.dense_gemm_k,
117 input,
118 &dw,
119 out,
120 m,
121 n,
122 k,
123 stream,
124 )?;
125 }
126 Ok(())
127 }
128}