metrale_model_layers/layers/qwen3_ssm/
w8a8_decode.rs1use anyhow::{Result, ensure};
18use metrale_gpu_runtime::gpu::DevicePtr;
19
20use super::Qwen3SsmLayer;
21use crate::layer::ForwardContext;
22use crate::layers::ops::W8a8Weight;
23use crate::layers::{FfnComponent, W8a8Ctx, W8a8Ffn, W8a8Mixer};
24use crate::weight_map::WeightQuantFormat;
25
26impl Qwen3SsmLayer {
27 pub fn set_w8a8_decode_weights(
30 &mut self,
31 w: W8a8Mixer,
32 qkvz_size: u32,
33 hidden: u32,
34 value_dim: u32,
35 ) -> Result<()> {
36 ensure!(
37 self.sequential_qkvz,
38 "W8A8 QKV|Z needs the sequential [QKV | Z] layout"
39 );
40 ensure!(
41 (w.input.n(), w.input.k(), w.output.n(), w.output.k())
42 == (qkvz_size, hidden, hidden, value_dim),
43 "W8A8 GDN shapes [{}, {}] / [{}, {}], want [{qkvz_size}, {hidden}] / [{hidden}, {value_dim}]",
44 w.input.n(),
45 w.input.k(),
46 w.output.n(),
47 w.output.k()
48 );
49 self.w8a8 = Some(w);
50 Ok(())
51 }
52
53 pub fn set_w8a8_ffn_weights(&mut self, w: W8a8Ffn, hidden: u32, inter: u32) -> Result<()> {
56 match self.ffn {
57 FfnComponent::Dense(ref mut d) => d.set_w8a8_decode_weights(w, hidden, inter),
58 _ => anyhow::bail!("W8A8 FFN weights need a dense FFN"),
59 }
60 }
61
62 pub fn adopt_fp8_block_w8a8(&mut self, ctx: W8a8Ctx, hidden: u32) -> Result<bool> {
67 let block = |w: Option<crate::weight_map::Fp8Weight>| {
68 w.filter(|f| f.scale_format == WeightQuantFormat::Fp8BlockScaled)
69 };
70 let (Some(qkvz), Some(out)) = (block(self.qkvz_fp8w), block(self.out_proj_fp8w)) else {
71 return Ok(false);
72 };
73 let (input, output) = (W8a8Weight::new(&[qkvz])?, W8a8Weight::new(&[out])?);
74 self.set_w8a8_decode_weights(W8a8Mixer { ctx, input, output }, qkvz.n, hidden, out.k)?;
75 Ok(true)
76 }
77
78 #[allow(clippy::too_many_arguments)]
83 pub(super) fn pinned_qkvz(
84 &self,
85 ctx: &ForwardContext,
86 normed: DevicePtr,
87 ldx: u32,
88 rows: usize,
89 out: DevicePtr,
90 ldc: u32,
91 stream: u64,
92 ) -> Result<bool> {
93 if self.sequential_qkvz
94 && let Some(ref nvfp4) = self.qkvz_nvfp4
95 && crate::layers::ops::w4a4_proj::fixed_nvfp4_proj(
96 ctx.gpu,
97 metrale_config::ProjFamily::Gdn,
98 nvfp4,
99 normed,
100 out,
101 rows as u32,
102 ldc,
103 ldx,
104 stream,
105 )?
106 {
107 return Ok(true);
108 }
109 match self.w8a8 {
110 Some(ref w) => w
111 .ctx
112 .proj(ctx.gpu, &w.input, normed, ldx, rows, out, ldc, stream),
113 None => Ok(false),
114 }
115 }
116
117 #[allow(clippy::too_many_arguments)]
121 pub(super) fn pinned_out(
122 &self,
123 ctx: &ForwardContext,
124 normed_out: DevicePtr,
125 ldx: u32,
126 rows: usize,
127 out: DevicePtr,
128 ldc: u32,
129 stream: u64,
130 ) -> Result<bool> {
131 let nvfp4 = &self.ssm.out_proj;
132 if self.out_proj_fp8w.is_none()
133 && self.out_proj_dense.is_none()
134 && crate::layers::ops::w4a4_proj::fixed_nvfp4_proj(
135 ctx.gpu,
136 metrale_config::ProjFamily::Gdn,
137 nvfp4,
138 normed_out,
139 out,
140 rows as u32,
141 ldc,
142 ldx,
143 stream,
144 )?
145 {
146 return Ok(true);
147 }
148 match self.w8a8 {
149 Some(ref w) => w
150 .ctx
151 .proj(ctx.gpu, &w.output, normed_out, ldx, rows, out, ldc, stream),
152 None => Ok(false),
153 }
154 }
155}