use super::distance::DistanceEngine;
use super::layer::NodeId;
use super::quantization::{QuantizedVector, QuantizedVectorStore, ScalarQuantizer};
use super::quantized_precision::{QuantizedPrecisionHnsw, TraversalCodec};
use std::sync::Arc;
#[derive(Debug, Clone)]
pub struct Sq8PrecisionConfig {
pub oversampling_ratio: usize,
pub min_index_size: usize,
}
impl Default for Sq8PrecisionConfig {
fn default() -> Self {
Self {
oversampling_ratio: Sq8Codec::DEFAULT_OVERSAMPLING,
min_index_size: Sq8Codec::DEFAULT_MIN_INDEX_SIZE,
}
}
}
pub struct Sq8Codec;
impl TraversalCodec for Sq8Codec {
type Quantizer = ScalarQuantizer;
type Store = QuantizedVectorStore;
type Prepared = QuantizedVector;
type Dist = u32;
const MAX_DIST: u32 = u32::MAX;
const DEFAULT_OVERSAMPLING: usize = 4;
const DEFAULT_MIN_INDEX_SIZE: usize = 10_000;
const ENCODES_PREPARED: bool = true;
fn supports_metric(metric: crate::DistanceMetric) -> bool {
matches!(
metric,
crate::DistanceMetric::Euclidean | crate::DistanceMetric::Cosine
)
}
fn can_train() -> bool {
true
}
fn train(samples: &[Vec<f32>]) -> crate::error::Result<Self::Quantizer> {
let refs: Vec<&[f32]> = samples.iter().map(Vec::as_slice).collect();
ScalarQuantizer::train(&refs)
}
fn new_store(
quantizer: &Arc<Self::Quantizer>,
_dimension: usize,
capacity: usize,
) -> Self::Store {
QuantizedVectorStore::new(Arc::clone(quantizer), capacity)
}
fn encode_push(
_quantizer: &Self::Quantizer,
vector: &[f32],
store: &mut Self::Store,
) -> crate::error::Result<()> {
store.push(vector);
Ok(())
}
fn prepare(
quantizer: &Self::Quantizer,
_raw: &[f32],
prepared: &[f32],
) -> Option<Self::Prepared> {
Some(quantizer.quantize(prepared))
}
fn distance(
quantizer: &Self::Quantizer,
store: &Self::Store,
prepared: &Self::Prepared,
node: NodeId,
) -> Option<Self::Dist> {
let code = store.get_slice(node)?;
Some(quantizer.distance_l2_quantized_slice(&prepared.data, code))
}
}
pub type Sq8PrecisionHnsw<D> = QuantizedPrecisionHnsw<D, Sq8Codec>;
impl<D: DistanceEngine> Sq8PrecisionHnsw<D> {
#[must_use]
pub fn search_with_config(
&self,
query: &[f32],
k: usize,
ef_search: usize,
config: &Sq8PrecisionConfig,
) -> Vec<(NodeId, f32)> {
self.search_bounded(
query,
k,
ef_search,
config.oversampling_ratio,
config.min_index_size,
)
}
pub fn install_trained_sq8(
&self,
quantizer: Arc<ScalarQuantizer>,
) -> crate::error::Result<bool> {
self.install_trained_quantizer(quantizer)
}
}