1use crate::k_quants::{GgmlType, QK_K};
29use fuel_ir::quantized::GgmlDType;
30use half::f16;
31
32pub const QK_MXFP4: usize = 32;
33pub const QK_NVFP4: usize = 64;
34const QK_NVFP4_SUB: usize = 16;
35
36pub const KVALUES_FP4: [i8; 16] = [0, 1, 2, 3, 4, 6, 8, 12, 0, -1, -2, -3, -4, -6, -8, -12];
39
40#[derive(Debug, Clone, Copy, PartialEq, bytemuck::Pod, bytemuck::Zeroable)]
41#[repr(C)]
42pub struct BlockTq1_0 {
43 pub(crate) qs: [u8; (QK_K - 4 * QK_K / 64) / 5],
44 pub(crate) qh: [u8; QK_K / 64],
45 pub(crate) d: f16,
46}
47const _: () = assert!(std::mem::size_of::<BlockTq1_0>() == 54);
48
49#[derive(Debug, Clone, Copy, PartialEq, bytemuck::Pod, bytemuck::Zeroable)]
50#[repr(C)]
51pub struct BlockTq2_0 {
52 pub(crate) qs: [u8; QK_K / 4],
53 pub(crate) d: f16,
54}
55const _: () = assert!(std::mem::size_of::<BlockTq2_0>() == 66);
56
57#[derive(Debug, Clone, Copy, PartialEq, bytemuck::Pod, bytemuck::Zeroable)]
58#[repr(C)]
59pub struct BlockMxfp4 {
60 pub(crate) e: u8,
61 pub(crate) qs: [u8; QK_MXFP4 / 2],
62}
63const _: () = assert!(std::mem::size_of::<BlockMxfp4>() == 17);
64
65#[derive(Debug, Clone, Copy, PartialEq, bytemuck::Pod, bytemuck::Zeroable)]
66#[repr(C)]
67pub struct BlockNvfp4 {
68 pub(crate) d: [u8; QK_NVFP4 / QK_NVFP4_SUB],
69 pub(crate) qs: [u8; QK_NVFP4 / 2],
70}
71const _: () = assert!(std::mem::size_of::<BlockNvfp4>() == 36);
72
73fn e8m0_to_f32_half(x: u8) -> f32 {
76 let bits: u32 = if x < 2 {
77 0x0020_0000u32 << x
78 } else {
79 u32::from(x - 1) << 23
80 };
81 f32::from_bits(bits)
82}
83
84fn ldexp(x: f32, e: i32) -> f32 {
86 x * f32::from_bits(((e + 127) as u32) << 23)
87}
88
89fn ue4m3_to_f32(x: u8) -> f32 {
92 if x == 0 || x == 0x7F {
93 return 0.0;
94 }
95 let exp = i32::from((x >> 3) & 0xF);
96 let man = f32::from(x & 0x7);
97 let raw = if exp == 0 {
98 ldexp(man, -9)
99 } else {
100 ldexp(1.0 + man / 8.0, exp - 7)
101 };
102 raw * 0.5
103}
104
105fn tq1_digit(byte: u8, pow3: u8) -> f32 {
107 let q = byte.wrapping_mul(pow3);
108 let xi = ((u16::from(q) * 3) >> 8) as i16;
109 f32::from(xi - 1)
110}
111
112impl BlockTq1_0 {
113 fn decode(&self, y: &mut [f32]) {
114 const POW3: [u8; 5] = [1, 3, 9, 27, 81];
115 let d = self.d.to_f32();
116 let mut out = 0;
117 let (head, tail) = self.qs.split_at(self.qs.len() - self.qs.len() % 32);
119 let runs32 = head.as_chunks::<32>().0.iter().map(|c| &c[..]);
120 let runs16 = tail.as_chunks::<16>().0.iter().map(|c| &c[..]);
121 for chunk in runs32.chain(runs16) {
122 for p in POW3 {
123 for q in chunk {
124 y[out] = tq1_digit(*q, p) * d;
125 out += 1;
126 }
127 }
128 }
129 for p in &POW3[..4] {
130 for q in &self.qh {
131 y[out] = tq1_digit(*q, *p) * d;
132 out += 1;
133 }
134 }
135 debug_assert_eq!(out, QK_K);
136 }
137}
138
139impl BlockTq2_0 {
140 fn decode(&self, y: &mut [f32]) {
141 let d = self.d.to_f32();
142 let mut out = 0;
143 for chunk in self.qs.as_chunks::<32>().0 {
144 for l in 0..4 {
145 for q in chunk {
146 let v = ((q >> (l * 2)) & 3) as i8;
147 y[out] = f32::from(v - 1) * d;
148 out += 1;
149 }
150 }
151 }
152 }
153}
154
155impl BlockMxfp4 {
156 fn decode(&self, y: &mut [f32]) {
157 let d = e8m0_to_f32_half(self.e);
158 for (j, q) in self.qs.iter().enumerate() {
159 y[j] = f32::from(KVALUES_FP4[usize::from(q & 0x0F)]) * d;
160 y[j + QK_MXFP4 / 2] = f32::from(KVALUES_FP4[usize::from(q >> 4)]) * d;
161 }
162 }
163}
164
165impl BlockNvfp4 {
166 fn decode(&self, y: &mut [f32]) {
167 for (s, scale) in self.d.iter().enumerate() {
168 let d = ue4m3_to_f32(*scale);
169 let yb = &mut y[s * QK_NVFP4_SUB..(s + 1) * QK_NVFP4_SUB];
170 let qs = &self.qs[s * QK_NVFP4_SUB / 2..(s + 1) * QK_NVFP4_SUB / 2];
171 for (j, q) in qs.iter().enumerate() {
172 yb[j] = f32::from(KVALUES_FP4[usize::from(q & 0x0F)]) * d;
173 yb[j + QK_NVFP4_SUB / 2] = f32::from(KVALUES_FP4[usize::from(q >> 4)]) * d;
174 }
175 }
176 }
177}
178
179macro_rules! decode_only_type {
180 ($blk:ty, $dtype:expr, $qk:expr, $vecdot:ty) => {
181 impl GgmlType for $blk {
182 const DTYPE: GgmlDType = $dtype;
183 const BLCK_SIZE: usize = $qk;
184
185 fn to_float(xs: &[Self], ys: &mut [f32]) {
186 for (b, y) in xs.iter().zip(ys.chunks_exact_mut($qk)) {
187 b.decode(y);
188 }
189 }
190
191 fn from_float(_xs: &[f32], _ys: &mut [Self]) {
192 panic!(
195 "{:?} is decode-only: Fuel cannot quantize to it (GAP-211 C)",
196 $dtype
197 );
198 }
199 }
200 };
201}
202
203decode_only_type!(BlockTq1_0, GgmlDType::Tq1_0, QK_K, BlockQ8K);
204decode_only_type!(BlockTq2_0, GgmlDType::Tq2_0, QK_K, BlockQ8K);
205decode_only_type!(BlockMxfp4, GgmlDType::Mxfp4, QK_MXFP4, BlockQ8_0);
206decode_only_type!(BlockNvfp4, GgmlDType::Nvfp4, QK_NVFP4, BlockQ8_0);
207
208#[cfg(test)]
209mod tests {
210 use super::*;
211
212 fn one<T: GgmlType>(b: T) -> Vec<f32> {
213 let mut y = vec![0f32; T::BLCK_SIZE];
214 T::to_float(&[b], &mut y);
215 y
216 }
217
218 #[test]
219 fn geometry_agrees_with_ggml_dtype() {
220 fn check<T: GgmlType>() {
221 assert_eq!(
222 std::mem::size_of::<T>(),
223 T::DTYPE.type_size(),
224 "{:?}",
225 T::DTYPE
226 );
227 assert_eq!(T::BLCK_SIZE, T::DTYPE.block_size(), "{:?}", T::DTYPE);
228 assert!(!T::DTYPE.supports_quantize());
229 assert!(T::DTYPE.check_quantize().is_err());
230 }
231 check::<BlockTq1_0>();
232 check::<BlockTq2_0>();
233 check::<BlockMxfp4>();
234 check::<BlockNvfp4>();
235 assert!(GgmlDType::Q4K.supports_quantize());
236 }
237
238 #[test]
240 fn e8m0_half_scale_cases() {
241 assert_eq!(e8m0_to_f32_half(127), 0.5);
242 assert_eq!(e8m0_to_f32_half(128), 1.0);
243 assert_eq!(e8m0_to_f32_half(1), f32::from_bits(0x0040_0000));
244 assert_eq!(e8m0_to_f32_half(0), f32::from_bits(0x0020_0000));
245 }
246
247 #[test]
250 fn ue4m3_scale_cases() {
251 assert_eq!(ue4m3_to_f32(0x38), 0.5);
252 assert_eq!(ue4m3_to_f32(0x01), 2f32.powi(-10));
253 assert_eq!(ue4m3_to_f32(0x7E), 224.0);
254 assert_eq!(ue4m3_to_f32(0x00), 0.0);
255 assert_eq!(ue4m3_to_f32(0x7F), 0.0);
256 }
257
258 #[test]
259 fn mxfp4_dequantizes_as_the_ggml_formula() {
260 let mut b = BlockMxfp4::zeros();
261 b.e = 128; b.qs[0] = 0x21; b.qs[1] = 0xF8; let y = one(b);
265 assert_eq!(y[0], 1.0);
266 assert_eq!(y[16], 2.0);
267 assert_eq!(y[1], 0.0);
268 assert_eq!(y[17], -12.0);
269 assert_eq!(y[2], 0.0); }
271
272 #[test]
273 fn nvfp4_each_subblock_uses_its_own_scale() {
274 let mut b = BlockNvfp4::zeros();
275 b.d = [0x38, 0x38 + 8, 0x7F, 0x38]; b.qs[0] = 0x42; b.qs[8] = 0x42; b.qs[16] = 0x42; let y = one(b);
280 assert_eq!(y[0], 1.0); assert_eq!(y[8], 2.0); assert_eq!(y[16], 2.0); assert_eq!(y[24], 4.0);
284 assert_eq!(y[32], 0.0);
285 assert_eq!(y[40], 0.0);
286 }
287
288 #[test]
289 fn tq2_0_dequantizes_as_the_ggml_formula() {
290 let mut b = BlockTq2_0::zeros();
291 b.d = f16::from_f32(2.0);
292 b.qs[0] = 0b11_10_01_00;
293 let y = one(b);
294 assert_eq!(y[0], -2.0); assert_eq!(y[32], 0.0); assert_eq!(y[64], 2.0); assert_eq!(y[96], 4.0); assert_eq!(y[1], -2.0); }
300
301 #[test]
304 fn tq1_0_dequantizes_as_the_ggml_formula() {
305 let mut b = BlockTq1_0::zeros();
306 b.d = f16::from_f32(2.0);
307 b.qs[0] = 171; b.qs[32] = 171; b.qh[0] = 171;
310 let y = one(b);
311 assert_eq!(y[0], 2.0);
312 assert_eq!(y[32], -2.0);
313 assert_eq!(y[160], 2.0); assert_eq!(y[160 + 16], -2.0);
315 assert_eq!(y[240], 2.0); assert_eq!(y[244], -2.0);
317 assert_eq!(y[1], -2.0); }
319}