Skip to main content

turbo_quant/
codebook.rs

1//! Small scalar codebook utilities used by quantizer profiles and tests.
2
3use schemars::JsonSchema;
4use serde::{Deserialize, Serialize};
5
6use crate::error::{Result, TurboQuantError};
7
8/// A deterministic uniform scalar codebook over a closed interval.
9#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
10pub struct ScalarCodebook {
11    pub bits: u8,
12    pub min: f32,
13    pub max: f32,
14}
15
16impl ScalarCodebook {
17    pub fn uniform(bits: u8, min: f32, max: f32) -> Result<Self> {
18        if bits == 0 || bits > 16 {
19            return Err(TurboQuantError::InvalidBitWidth { got: bits });
20        }
21        if !min.is_finite() || !max.is_finite() || min >= max {
22            return Err(TurboQuantError::MalformedCode {
23                reason: "invalid uniform codebook range".into(),
24            });
25        }
26        Ok(Self { bits, min, max })
27    }
28
29    pub fn levels(&self) -> u32 {
30        1u32 << self.bits
31    }
32
33    pub fn quantize(&self, value: f32) -> Result<u16> {
34        if !value.is_finite() {
35            return Err(TurboQuantError::NonFiniteInput { index: 0 });
36        }
37        let levels = self.levels();
38        let clipped = value.clamp(self.min, self.max);
39        let normalized = (clipped - self.min) / (self.max - self.min);
40        let index = (normalized * levels as f32).floor() as u32;
41        Ok(index.min(levels - 1) as u16)
42    }
43
44    pub fn dequantize_midpoint(&self, index: u16) -> Result<f32> {
45        let levels = self.levels();
46        if u32::from(index) >= levels {
47            return Err(TurboQuantError::MalformedCode {
48                reason: format!("codebook index {index} is outside [0, {levels})"),
49            });
50        }
51        let width = (self.max - self.min) / levels as f32;
52        Ok(self.min + (f32::from(index) + 0.5) * width)
53    }
54}