Skip to main content

fuel_quantized/
iq_grid_types.rs

1// Derived in part from llama.cpp / ggml (MIT License, Copyright (c) 2023-2026 The ggml authors).
2// The full licence text is in THIRD-PARTY-NOTICES.md at the repository root. No licence identifier line
3// is stamped here: the file derives from somebody else's copyright.
4//! `IQ2_XXS`, `IQ2_XS`, `IQ2_S`, `IQ3_XXS`, `IQ3_S`, `IQ1_S` and `IQ1_M` block
5//! formats (GAP-211 B2): the grid-table IQ quantizations. DECODE-ONLY.
6//!
7//! # Provenance
8//!
9//! Block layouts and dequantization are llama.cpp's / ggml's (MIT License,
10//! Copyright (c) 2023-2026 The ggml authors), checked against the source at
11//! commit `9d57ce456c` (the pin recorded on [`GgmlDType`]):
12//!
13//! - `ggml/src/ggml-common.h`: `block_iq2_xxs`, `block_iq2_xs`, `block_iq2_s`,
14//!   `block_iq3_xxs`, `block_iq3_s`, `block_iq1_s`, `block_iq1_m`, `IQ1S_DELTA`.
15//! - `ggml/src/ggml-quants.c`: `dequantize_row_iq2_xxs` ... `dequantize_row_iq1_m`.
16//! - the lookup tables themselves are in [`crate::iq_grids`], generated from
17//!   `ggml-common.h` by `scripts/gen_iq_grids.py`.
18//!
19//! # Decode-only
20//!
21//! llama.cpp's quantizers for these formats search for grid points under an
22//! importance matrix and are out of scope here. [`GgmlType::from_float`] is
23//! therefore not provided: it panics naming the type, and the fallible
24//! quantize entry points refuse these dtypes first
25//! (`GgmlDType::supports_quantize`). A model stored in these formats loads,
26//! dequantizes and multiplies (dequantize-then-dot); it cannot be produced here.
27
28use crate::iq_grids::*;
29use crate::k_quants::{GgmlType, QK_K};
30use fuel_ir::quantized::GgmlDType;
31use half::f16;
32
33const IQ1S_DELTA: f32 = 0.125;
34
35#[derive(Debug, Clone, Copy, PartialEq, bytemuck::Pod, bytemuck::Zeroable)]
36#[repr(C)]
37pub struct BlockIq2Xxs {
38    pub(crate) d: f16,
39    pub(crate) qs: [u16; QK_K / 8],
40}
41const _: () = assert!(std::mem::size_of::<BlockIq2Xxs>() == 66);
42
43#[derive(Debug, Clone, Copy, PartialEq, bytemuck::Pod, bytemuck::Zeroable)]
44#[repr(C)]
45pub struct BlockIq2Xs {
46    pub(crate) d: f16,
47    pub(crate) qs: [u16; QK_K / 8],
48    pub(crate) scales: [u8; QK_K / 32],
49}
50const _: () = assert!(std::mem::size_of::<BlockIq2Xs>() == 74);
51
52#[derive(Debug, Clone, Copy, PartialEq, bytemuck::Pod, bytemuck::Zeroable)]
53#[repr(C)]
54pub struct BlockIq2S {
55    pub(crate) d: f16,
56    pub(crate) qs: [u8; QK_K / 4],
57    pub(crate) qh: [u8; QK_K / 32],
58    pub(crate) scales: [u8; QK_K / 32],
59}
60const _: () = assert!(std::mem::size_of::<BlockIq2S>() == 82);
61
62#[derive(Debug, Clone, Copy, PartialEq, bytemuck::Pod, bytemuck::Zeroable)]
63#[repr(C)]
64pub struct BlockIq3Xxs {
65    pub(crate) d: f16,
66    pub(crate) qs: [u8; 3 * QK_K / 8],
67}
68const _: () = assert!(std::mem::size_of::<BlockIq3Xxs>() == 98);
69
70#[derive(Debug, Clone, Copy, PartialEq, bytemuck::Pod, bytemuck::Zeroable)]
71#[repr(C)]
72pub struct BlockIq3S {
73    pub(crate) d: f16,
74    pub(crate) qs: [u8; QK_K / 4],
75    pub(crate) qh: [u8; QK_K / 32],
76    pub(crate) signs: [u8; QK_K / 8],
77    pub(crate) scales: [u8; QK_K / 64],
78}
79const _: () = assert!(std::mem::size_of::<BlockIq3S>() == 110);
80
81#[derive(Debug, Clone, Copy, PartialEq, bytemuck::Pod, bytemuck::Zeroable)]
82#[repr(C)]
83pub struct BlockIq1S {
84    pub(crate) d: f16,
85    pub(crate) qs: [u8; QK_K / 8],
86    pub(crate) qh: [u16; QK_K / 32],
87}
88const _: () = assert!(std::mem::size_of::<BlockIq1S>() == 50);
89
90#[derive(Debug, Clone, Copy, PartialEq, bytemuck::Pod, bytemuck::Zeroable)]
91#[repr(C)]
92pub struct BlockIq1M {
93    pub(crate) qs: [u8; QK_K / 8],
94    pub(crate) qh: [u8; QK_K / 16],
95    pub(crate) scales: [u8; QK_K / 32],
96}
97const _: () = assert!(std::mem::size_of::<BlockIq1M>() == 56);
98
99/// `(signs & kmask_iq2xs[j]) ? -1.f : 1.f`.
100fn sign(signs: u8, j: usize) -> f32 {
101    if signs & KMASK_IQ2XS[j] != 0 {
102        -1.0
103    } else {
104        1.0
105    }
106}
107
108impl BlockIq2Xxs {
109    // ggml-quants.c `dequantize_row_iq2_xxs`.
110    pub(crate) fn decode(&self, y: &mut [f32]) {
111        let d = self.d.to_f32();
112        for ib32 in 0..QK_K / 32 {
113            let q = &self.qs[4 * ib32..4 * ib32 + 4];
114            let aux0 = u32::from(q[0]) | (u32::from(q[1]) << 16);
115            let aux1 = u32::from(q[2]) | (u32::from(q[3]) << 16);
116            let aux8 = aux0.to_le_bytes();
117            let db = d * (0.5 + (aux1 >> 28) as f32) * 0.25;
118            for l in 0..4 {
119                let grid = IQ2XXS_GRID[aux8[l] as usize].to_le_bytes();
120                let signs = KSIGNS_IQ2XS[((aux1 >> (7 * l)) & 127) as usize];
121                for j in 0..8 {
122                    y[32 * ib32 + 8 * l + j] = db * f32::from(grid[j]) * sign(signs, j);
123                }
124            }
125        }
126    }
127}
128
129impl BlockIq2Xs {
130    // ggml-quants.c `dequantize_row_iq2_xs`.
131    pub(crate) fn decode(&self, y: &mut [f32]) {
132        let d = self.d.to_f32();
133        for ib32 in 0..QK_K / 32 {
134            let db = [
135                d * (0.5 + (self.scales[ib32] & 0xf) as f32) * 0.25,
136                d * (0.5 + (self.scales[ib32] >> 4) as f32) * 0.25,
137            ];
138            for l in 0..4 {
139                let q = self.qs[4 * ib32 + l];
140                let grid = IQ2XS_GRID[(q & 511) as usize].to_le_bytes();
141                let signs = KSIGNS_IQ2XS[(q >> 9) as usize];
142                for j in 0..8 {
143                    y[32 * ib32 + 8 * l + j] = db[l / 2] * f32::from(grid[j]) * sign(signs, j);
144                }
145            }
146        }
147    }
148}
149
150impl BlockIq2S {
151    // ggml-quants.c `dequantize_row_iq2_s`.
152    pub(crate) fn decode(&self, y: &mut [f32]) {
153        let d = self.d.to_f32();
154        let signs_base = QK_K / 8;
155        for ib32 in 0..QK_K / 32 {
156            let db = [
157                d * (0.5 + (self.scales[ib32] & 0xf) as f32) * 0.25,
158                d * (0.5 + (self.scales[ib32] >> 4) as f32) * 0.25,
159            ];
160            for l in 0..4 {
161                let dl = db[l / 2];
162                let idx = usize::from(self.qs[4 * ib32 + l])
163                    | ((usize::from(self.qh[ib32]) << (8 - 2 * l)) & 0x300);
164                let grid = IQ2S_GRID[idx].to_le_bytes();
165                let signs = self.qs[signs_base + 4 * ib32 + l];
166                for j in 0..8 {
167                    y[32 * ib32 + 8 * l + j] = dl * f32::from(grid[j]) * sign(signs, j);
168                }
169            }
170        }
171    }
172}
173
174impl BlockIq3Xxs {
175    // ggml-quants.c `dequantize_row_iq3_xxs`.
176    pub(crate) fn decode(&self, y: &mut [f32]) {
177        let d = self.d.to_f32();
178        let scales_and_signs = QK_K / 4;
179        for ib32 in 0..QK_K / 32 {
180            let o = scales_and_signs + 4 * ib32;
181            let aux32 =
182                u32::from_le_bytes([self.qs[o], self.qs[o + 1], self.qs[o + 2], self.qs[o + 3]]);
183            let db = d * (0.5 + (aux32 >> 28) as f32) * 0.5;
184            for l in 0..4 {
185                let signs = KSIGNS_IQ2XS[((aux32 >> (7 * l)) & 127) as usize];
186                let g1 = IQ3XXS_GRID[self.qs[8 * ib32 + 2 * l] as usize].to_le_bytes();
187                let g2 = IQ3XXS_GRID[self.qs[8 * ib32 + 2 * l + 1] as usize].to_le_bytes();
188                for j in 0..4 {
189                    y[32 * ib32 + 8 * l + j] = db * f32::from(g1[j]) * sign(signs, j);
190                    y[32 * ib32 + 8 * l + j + 4] = db * f32::from(g2[j]) * sign(signs, j + 4);
191                }
192            }
193        }
194    }
195}
196
197impl BlockIq3S {
198    // ggml-quants.c `dequantize_row_iq3_s`.
199    pub(crate) fn decode(&self, y: &mut [f32]) {
200        let d = self.d.to_f32();
201        for pair in 0..QK_K / 64 {
202            let db = [
203                d * (1 + 2 * i32::from(self.scales[pair] & 0xf)) as f32,
204                d * (1 + 2 * i32::from(self.scales[pair] >> 4)) as f32,
205            ];
206            for (half, dbh) in db.iter().enumerate() {
207                let ib32 = 2 * pair + half;
208                let qh = usize::from(self.qh[ib32]);
209                let qs = &self.qs[8 * ib32..8 * ib32 + 8];
210                for l in 0..4 {
211                    let i1 = usize::from(qs[2 * l]) | ((qh << (8 - 2 * l)) & 256);
212                    let i2 = usize::from(qs[2 * l + 1]) | ((qh << (7 - 2 * l)) & 256);
213                    let g1 = IQ3S_GRID[i1].to_le_bytes();
214                    let g2 = IQ3S_GRID[i2].to_le_bytes();
215                    let signs = self.signs[4 * ib32 + l];
216                    for j in 0..4 {
217                        y[32 * ib32 + 8 * l + j] = dbh * f32::from(g1[j]) * sign(signs, j);
218                        y[32 * ib32 + 8 * l + j + 4] = dbh * f32::from(g2[j]) * sign(signs, j + 4);
219                    }
220                }
221            }
222        }
223    }
224}
225
226impl BlockIq1S {
227    // ggml-quants.c `dequantize_row_iq1_s`.
228    pub(crate) fn decode(&self, y: &mut [f32]) {
229        let d = self.d.to_f32();
230        for ib in 0..QK_K / 32 {
231            let qh = self.qh[ib];
232            let dl = d * (2 * i32::from((qh >> 12) & 7) + 1) as f32;
233            let delta = if qh & 0x8000 != 0 {
234                -IQ1S_DELTA
235            } else {
236                IQ1S_DELTA
237            };
238            for l in 0..4 {
239                let idx =
240                    usize::from(self.qs[4 * ib + l]) | ((usize::from(qh >> (3 * l)) & 7) << 8);
241                let grid = IQ1S_GRID[idx].to_le_bytes();
242                for j in 0..8 {
243                    y[32 * ib + 8 * l + j] = dl * (f32::from(grid[j] as i8) + delta);
244                }
245            }
246        }
247    }
248}
249
250impl BlockIq1M {
251    fn sc(&self, i: usize) -> u16 {
252        u16::from_le_bytes([self.scales[2 * i], self.scales[2 * i + 1]])
253    }
254
255    // ggml-quants.c `dequantize_row_iq1_m`.
256    pub(crate) fn decode(&self, y: &mut [f32]) {
257        let sc = [self.sc(0), self.sc(1), self.sc(2), self.sc(3)];
258        let scale =
259            (sc[0] >> 12) | ((sc[1] >> 8) & 0x00f0) | ((sc[2] >> 4) & 0x0f00) | (sc[3] & 0xf000);
260        let d = f16::from_bits(scale).to_f32();
261        for ib in 0..QK_K / 32 {
262            let dl1 = d * (2 * i32::from((sc[ib / 2] >> (6 * (ib % 2))) & 0x7) + 1) as f32;
263            let dl2 = d * (2 * i32::from((sc[ib / 2] >> (6 * (ib % 2) + 3)) & 0x7) + 1) as f32;
264            let qs = &self.qs[4 * ib..4 * ib + 4];
265            let qh = &self.qh[2 * ib..2 * ib + 2];
266            let idx = [
267                usize::from(qs[0]) | ((usize::from(qh[0]) << 8) & 0x700),
268                usize::from(qs[1]) | ((usize::from(qh[0]) << 4) & 0x700),
269                usize::from(qs[2]) | ((usize::from(qh[1]) << 8) & 0x700),
270                usize::from(qs[3]) | ((usize::from(qh[1]) << 4) & 0x700),
271            ];
272            let bit = |b: u8, m: u8| if b & m != 0 { -IQ1S_DELTA } else { IQ1S_DELTA };
273            let delta = [
274                bit(qh[0], 0x08),
275                bit(qh[0], 0x80),
276                bit(qh[1], 0x08),
277                bit(qh[1], 0x80),
278            ];
279            for l in 0..4 {
280                let dl = if l < 2 { dl1 } else { dl2 };
281                let grid = IQ1S_GRID[idx[l]].to_le_bytes();
282                for j in 0..8 {
283                    y[32 * ib + 8 * l + j] = dl * (f32::from(grid[j] as i8) + delta[l]);
284                }
285            }
286        }
287    }
288}
289
290macro_rules! decode_only_type {
291    ($blk:ty, $dtype:expr) => {
292        impl GgmlType for $blk {
293            const DTYPE: GgmlDType = $dtype;
294            const BLCK_SIZE: usize = QK_K;
295
296            fn to_float(xs: &[Self], ys: &mut [f32]) {
297                for (b, y) in xs.iter().zip(ys.as_chunks_mut::<QK_K>().0.iter_mut()) {
298                    b.decode(y);
299                }
300            }
301
302            fn from_float(_xs: &[f32], _ys: &mut [Self]) {
303                // Unreachable through the fallible entry points, which check
304                // `GgmlDType::supports_quantize` first.
305                panic!(
306                    "{:?} is decode-only: Fuel cannot quantize to it (GAP-211 B2)",
307                    $dtype
308                );
309            }
310        }
311    };
312}
313
314decode_only_type!(BlockIq2Xxs, GgmlDType::Iq2Xxs);
315decode_only_type!(BlockIq2Xs, GgmlDType::Iq2Xs);
316decode_only_type!(BlockIq2S, GgmlDType::Iq2S);
317decode_only_type!(BlockIq3Xxs, GgmlDType::Iq3Xxs);
318decode_only_type!(BlockIq3S, GgmlDType::Iq3S);
319decode_only_type!(BlockIq1S, GgmlDType::Iq1S);
320decode_only_type!(BlockIq1M, GgmlDType::Iq1M);
321
322#[cfg(test)]
323mod tests {
324    use super::*;
325
326    fn one<T: GgmlType>(b: T) -> Vec<f32> {
327        let mut y = vec![0f32; QK_K];
328        T::to_float(&[b], &mut y);
329        y
330    }
331
332    /// Properties of the tables that do not depend on reading them back from the
333    /// generated file: `ksigns` is "7-bit pattern plus an even-parity bit",
334    /// `kmask` is the eight single-bit masks, and each grid only ever holds the
335    /// byte values its format is built from.
336    #[test]
337    fn tables_have_the_structure_ggml_builds_them_with() {
338        for (i, s) in KSIGNS_IQ2XS.iter().enumerate() {
339            let parity = (i as u32).count_ones() & 1;
340            assert_eq!(u32::from(*s), i as u32 | (parity << 7), "ksigns[{i}]");
341        }
342        for (j, m) in KMASK_IQ2XS.iter().enumerate() {
343            assert_eq!(*m, 1u8 << j);
344        }
345        let all_in = |grid: &[u64], allowed: &[u8]| {
346            grid.iter()
347                .all(|e| e.to_le_bytes().iter().all(|b| allowed.contains(b)))
348        };
349        assert!(all_in(&IQ2XXS_GRID, &[0x08, 0x19, 0x2b]));
350        assert!(all_in(&IQ2XS_GRID, &[0x08, 0x19, 0x2b]));
351        assert!(all_in(&IQ2S_GRID, &[0x08, 0x19, 0x2b]));
352        assert!(all_in(&IQ1S_GRID, &[0xff, 0x00, 0x01]));
353        assert!(
354            IQ3S_GRID
355                .iter()
356                .all(|e| e.to_le_bytes().iter().all(|b| *b % 2 == 1 && *b <= 15))
357        );
358    }
359
360    /// Block geometry agrees between `GgmlDType`, the `GgmlType` impls and the
361    /// structs (the structs carry `size_of` asserts of their own).
362    #[test]
363    fn geometry_agrees_with_ggml_dtype() {
364        fn check<T: GgmlType>() {
365            assert_eq!(
366                std::mem::size_of::<T>(),
367                T::DTYPE.type_size(),
368                "{:?}",
369                T::DTYPE
370            );
371            assert_eq!(T::BLCK_SIZE, T::DTYPE.block_size(), "{:?}", T::DTYPE);
372            assert!(!T::DTYPE.supports_quantize());
373            assert!(T::DTYPE.check_quantize().is_err());
374        }
375        check::<BlockIq2Xxs>();
376        check::<BlockIq2Xs>();
377        check::<BlockIq2S>();
378        check::<BlockIq3Xxs>();
379        check::<BlockIq3S>();
380        check::<BlockIq1S>();
381        check::<BlockIq1M>();
382        assert!(GgmlDType::Q4K.supports_quantize());
383    }
384
385    /// Hand-evaluated `dequantize_row_iq2_xxs`. d = 1, group 0 picks grid[0]
386    /// (all 0x08) then grid[1] (0x2b, then 0x08s), scale nibble 15 gives
387    /// db = 15.5 * 0.25 = 3.875, sign index 1 is 0x81 (bits 0 and 7 negative).
388    #[test]
389    fn iq2_xxs_dequantizes_as_the_ggml_formula() {
390        let mut b = BlockIq2Xxs::zeros();
391        b.d = f16::from_f32(1.0);
392        b.qs[0] = 0x0100; // aux8 = [0, 1, 0, 0]
393        b.qs[2] = 1; // sign index of l = 0
394        b.qs[3] = 0xF000; // scale 15
395        let y = one(b);
396        assert_eq!(y[0], -31.0);
397        assert_eq!(y[1], 31.0);
398        assert_eq!(y[7], -31.0);
399        assert_eq!(y[8], 3.875 * 43.0); // grid[1] byte 0 = 0x2b, no sign
400        assert_eq!(y[9], 31.0);
401        assert_eq!(y[32], 1.0); // untouched group: 0.125 * 8
402    }
403
404    /// Hand-evaluated `dequantize_row_iq2_xs`: scales byte 0x21 gives db0 =
405    /// 1.5 * 0.25 = 0.375 (l = 0, 1) and db1 = 2.5 * 0.25 = 0.625 (l = 2, 3).
406    #[test]
407    fn iq2_xs_dequantizes_as_the_ggml_formula() {
408        let mut b = BlockIq2Xs::zeros();
409        b.d = f16::from_f32(1.0);
410        b.scales[0] = 0x21;
411        b.qs[0] = (1 << 9) | 1; // grid[1], sign index 1 (0x81)
412        let y = one(b);
413        assert_eq!(y[0], -(0.375 * 43.0));
414        assert_eq!(y[1], 3.0);
415        assert_eq!(y[7], -3.0);
416        assert_eq!(y[8], 3.0); // l = 1: grid[0]
417        assert_eq!(y[16], 5.0); // l = 2 uses db1
418    }
419
420    /// Hand-evaluated `dequantize_row_iq2_s`: signs come from qs[32..].
421    #[test]
422    fn iq2_s_dequantizes_as_the_ggml_formula() {
423        let mut b = BlockIq2S::zeros();
424        b.d = f16::from_f32(1.0);
425        b.scales[0] = 0x21;
426        b.qs[0] = 1; // grid[1]
427        b.qs[QK_K / 8] = 0x01; // sign byte of l = 0: bit 0 negative
428        let y = one(b);
429        assert_eq!(y[0], -(0.375 * 43.0));
430        assert_eq!(y[1], 3.0);
431        assert_eq!(y[8], 3.0);
432        assert_eq!(y[16], 5.0);
433    }
434
435    /// Hand-evaluated `dequantize_row_iq3_xxs`: scale nibble 3 gives
436    /// db = 3.5 * 0.5 = 1.75; grid[1] = 0x04040414 (byte 0 = 0x14).
437    #[test]
438    fn iq3_xxs_dequantizes_as_the_ggml_formula() {
439        let mut b = BlockIq3Xxs::zeros();
440        b.d = f16::from_f32(1.0);
441        b.qs[0] = 1;
442        let aux32: u32 = (3 << 28) | 1; // scale 3, sign index 1 (0x81) for l = 0
443        b.qs[QK_K / 4..QK_K / 4 + 4].copy_from_slice(&aux32.to_le_bytes());
444        let y = one(b);
445        assert_eq!(y[0], -(1.75 * 20.0));
446        assert_eq!(y[1], 7.0);
447        assert_eq!(y[4], 7.0);
448        assert_eq!(y[7], -7.0);
449    }
450
451    /// Hand-evaluated `dequantize_row_iq3_s`: scales 0x21 gives db1 = 3,
452    /// db2 = 5; grid[1] = 0x01010103 (byte 0 = 3), grid[0] all 1.
453    #[test]
454    fn iq3_s_dequantizes_as_the_ggml_formula() {
455        let mut b = BlockIq3S::zeros();
456        b.d = f16::from_f32(1.0);
457        b.scales[0] = 0x21;
458        b.qs[0] = 1;
459        b.signs[0] = 0x01;
460        let y = one(b);
461        assert_eq!(y[0], -9.0);
462        assert_eq!(y[1], 3.0);
463        assert_eq!(y[4], 3.0);
464        assert_eq!(y[32], 5.0); // second half of the pair uses db2
465    }
466
467    /// Hand-evaluated `dequantize_row_iq1_s`: grid[1] = 0xffffffffffffff01
468    /// (byte 0 = +1, rest -1), delta +-0.125, dl = d * (2 * s + 1).
469    #[test]
470    fn iq1_s_dequantizes_as_the_ggml_formula() {
471        let mut b = BlockIq1S::zeros();
472        b.d = f16::from_f32(1.0);
473        b.qs[0] = 1;
474        let y = one(b);
475        assert_eq!(y[0], 1.125);
476        assert_eq!(y[1], -0.875);
477        assert_eq!(y[8], -0.875);
478        b.qh[0] = 0x9000; // scale field 1 -> dl = 3, sign bit -> delta -0.125
479        let y = one(b);
480        assert_eq!(y[0], 3.0 * (1.0 - 0.125));
481        assert_eq!(y[1], 3.0 * (-1.0 - 0.125));
482    }
483
484    /// Hand-evaluated `dequantize_row_iq1_m`: d is assembled from the top
485    /// nibbles of the four scale words (0x3c00 = 1.0), the low 9 bits of
486    /// scales[0] hold the 3-bit sub-block scales.
487    #[test]
488    fn iq1_m_dequantizes_as_the_ggml_formula() {
489        let mut b = BlockIq1M::zeros();
490        b.scales = [0x01, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x30];
491        b.qs[0] = 1;
492        let y = one(b);
493        assert_eq!(y[0], 3.0 * 1.125); // dl1 = 2 * 1 + 1
494        assert_eq!(y[1], 3.0 * -0.875);
495        assert_eq!(y[8], 3.0 * -0.875);
496        assert_eq!(y[16], -0.875); // l = 2 uses dl2 = 1
497        assert_eq!(y[32], -0.875);
498        b.qh[0] = 0x08; // delta[0] negative
499        let y = one(b);
500        assert_eq!(y[0], 3.0 * 0.875);
501    }
502
503    /// The decode-only types refuse quantizing with a typed error naming them.
504    #[test]
505    fn quantizing_to_a_decode_only_type_is_a_typed_error_naming_it() {
506        let e = GgmlDType::Iq2Xxs.check_quantize().unwrap_err().to_string();
507        assert!(e.contains("Iq2Xxs") && e.contains("no quantizer"), "{e}");
508    }
509}