Skip to main content

teeny_quant/quant/
mod.rs

1/*
2 * Copyright (c) 2026 Teenygrad.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *   http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17//! Weight quantization primitives: INT8/INT4 affine quantization (`affine`, `pack4`) and
18//! `F8_E4M3`/`F8_E5M2` (`fp8`), each parameterized by a [`Granularity`].
19
20pub mod affine;
21pub mod fp8;
22pub mod granularity;
23pub(crate) mod groups;
24pub mod pack4;
25
26pub use affine::{AffineParams, QuantizedAffine, dequantize_affine, quantize_affine};
27
28/// Computes the flat-index -> group-id table for a tensor of `shape` under `granularity`, and
29/// the resulting group count. Quantizing functions in this module compute and use this
30/// internally; it's exposed so callers reconstructing a quantized tensor (e.g.
31/// [`crate::validate`], or an external consumer of `teeny-quant`'s output) can recompute the
32/// same element -> group mapping without duplicating the grouping logic.
33pub fn compute_groups(shape: &[usize], granularity: Granularity) -> (Vec<u32>, usize) {
34    groups::assign_groups(shape, granularity)
35}
36pub use fp8::{Fp8Variant, QuantizedFp8, dequantize_fp8, f8_to_f32, f32_to_f8, quantize_fp8};
37pub use granularity::Granularity;
38
39/// Which quantization scheme to apply to a tensor.
40#[derive(Debug, Clone, Copy, PartialEq, Eq)]
41pub enum Scheme {
42    /// 8-bit affine integer quantization.
43    Int8 {
44        /// Symmetric (`zero_point = 0`) vs asymmetric.
45        symmetric: bool,
46    },
47    /// 4-bit affine integer quantization, nibble-packed into `U8` (see [`pack4`]).
48    Int4 {
49        /// Symmetric (`zero_point = 0`) vs asymmetric.
50        symmetric: bool,
51    },
52    /// 8-bit floating point.
53    Fp8 {
54        /// Which OCP FP8 encoding.
55        variant: Fp8Variant,
56    },
57}
58
59impl Scheme {
60    /// The compressed-tensors `weights.type` field for this scheme.
61    pub fn type_name(self) -> &'static str {
62        match self {
63            Scheme::Int8 { .. } | Scheme::Int4 { .. } => "int",
64            Scheme::Fp8 { .. } => "float",
65        }
66    }
67
68    /// The compressed-tensors `weights.num_bits` field for this scheme.
69    pub fn num_bits(self) -> u8 {
70        match self {
71            Scheme::Int8 { .. } => 8,
72            Scheme::Int4 { .. } => 4,
73            Scheme::Fp8 { .. } => 8,
74        }
75    }
76
77    /// Whether this scheme is symmetric (`zero_point = 0` / no zero-point tensor written).
78    pub fn is_symmetric(self) -> bool {
79        match self {
80            Scheme::Int8 { symmetric } | Scheme::Int4 { symmetric } => symmetric,
81            // FP8 quantization here is amax-scaled with no zero-point, i.e. always symmetric.
82            Scheme::Fp8 { .. } => true,
83        }
84    }
85
86    /// A short, stable name used in CLI output and file names (e.g. `int8`, `int4`, `fp8_e4m3`).
87    pub fn short_name(self) -> String {
88        match self {
89            Scheme::Int8 { .. } => "int8".to_string(),
90            Scheme::Int4 { .. } => "int4".to_string(),
91            Scheme::Fp8 {
92                variant: Fp8Variant::E4M3,
93            } => "fp8_e4m3".to_string(),
94            Scheme::Fp8 {
95                variant: Fp8Variant::E5M2,
96            } => "fp8_e5m2".to_string(),
97        }
98    }
99}