pub mod pq;
pub mod sq8;
pub use pq::{PQConfig, PQDistanceTable, PQEncodedVector, PQQuantizer};
pub use sq8::{SQ8EncodedVector, SQ8Quantizer};
pub trait Quantizer: Send + Sync {
type Encoded;
fn train(vectors: &[Vec<f32>]) -> Self;
fn encode(&self, vector: &[f32]) -> Self::Encoded;
fn encode_batch(&self, vectors: &[Vec<f32>]) -> Vec<Self::Encoded> {
vectors.iter().map(|v| self.encode(v)).collect()
}
fn decode(&self, encoded: &Self::Encoded) -> Vec<f32>;
fn dimension(&self) -> usize;
fn encoded_size(&self) -> usize;
fn compression_ratio(&self) -> f32 {
(self.dimension() * 4) as f32 / self.encoded_size() as f32
}
}
pub trait AsymmetricDistance<E> {
fn asymmetric_l2(&self, query: &[f32], encoded: &E) -> f32;
fn asymmetric_inner_product(&self, query: &[f32], encoded: &E) -> f32;
fn asymmetric_cosine(&self, query: &[f32], encoded: &E) -> f32;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_compression_ratio() {
let vectors: Vec<Vec<f32>> = (0..100)
.map(|i| (0..128).map(|j| (i * j) as f32 * 0.01).collect())
.collect();
let quantizer = SQ8Quantizer::train(&vectors);
assert!((quantizer.compression_ratio() - 4.0).abs() < 0.01);
}
}