use crate::error::Result;
use crate::lattice::PaddedTileLattice;
#[derive(Clone, Copy, PartialEq, Debug)]
pub struct QuantParams {
pub scale: f32,
pub zero_point: i8,
}
impl QuantParams {
pub fn symmetric(abs_max: f32) -> Self {
let scale = if abs_max == 0.0 { 1.0 } else { abs_max / 127.0 };
QuantParams {
scale,
zero_point: 0,
}
}
pub fn asymmetric(min: f32, max: f32) -> Self {
let span = (max - min).max(f32::MIN_POSITIVE);
let scale = span / 255.0;
let zp = (-128.0 - min / scale).round().clamp(-128.0, 127.0);
QuantParams {
scale,
zero_point: zp as i8,
}
}
#[inline]
pub fn quantize(&self, value: f32) -> i8 {
let q = (value / self.scale).round() + self.zero_point as f32;
q.clamp(-128.0, 127.0) as i8
}
#[inline]
pub fn dequantize(&self, q: i8) -> f32 {
self.scale * (q as f32 - self.zero_point as f32)
}
}
impl PaddedTileLattice<f32> {
pub fn abs_max(&self) -> f32 {
let mut m = 0.0f32;
for (_, _, v) in self.iter_logical() {
m = m.max(v.abs());
}
m
}
pub fn quantize(&self, params: QuantParams) -> Result<PaddedTileLattice<i8>> {
let mut out = PaddedTileLattice::<i8>::zeroed(self.rows(), self.cols(), *self.geometry())?;
for (row, col, v) in self.iter_logical() {
out.set(row, col, params.quantize(v))?;
}
Ok(out)
}
}
impl PaddedTileLattice<i8> {
pub fn dequantize(&self, params: QuantParams) -> Result<PaddedTileLattice<f32>> {
let mut out = PaddedTileLattice::<f32>::zeroed(self.rows(), self.cols(), *self.geometry())?;
for (row, col, q) in self.iter_logical() {
out.set(row, col, params.dequantize(q))?;
}
Ok(out)
}
}