use std::io;
use serde::{Deserialize, Serialize};
pub(crate) fn validate_rotation_len(
len: usize,
dimension: usize,
label: &str,
) -> Result<(), crate::error::Error> {
let Some(expected) = dimension.checked_mul(dimension) else {
return Err(crate::error::Error::IndexCorrupted(format!(
"{label} rotation dimension {dimension} squared overflows usize"
)));
};
if len != expected {
return Err(crate::error::Error::IndexCorrupted(format!(
"{label} rotation has {len} elements, expected dimension^2 = {expected}"
)));
}
Ok(())
}
mod binary;
pub(crate) mod codec_helpers;
mod pq;
pub(crate) mod pq_kmeans;
pub(crate) mod pq_opq;
#[cfg(feature = "persistence")]
mod pq_persistence;
mod rabitq;
pub(crate) mod rabitq_store;
mod scalar;
pub use binary::BinaryQuantizedVector;
#[allow(unused_imports)] pub(crate) use pq::distance_pq_l2;
#[allow(unused_imports)] pub(crate) use pq::pq_adc_batch_rescore;
pub use pq::{PQCodebook, PQVector, ProductQuantizer};
#[cfg(feature = "persistence")]
pub use pq_opq::train_opq;
#[cfg(feature = "persistence")]
pub(crate) use rabitq::PreparedQuery;
pub use rabitq::{RaBitQCorrection, RaBitQIndex, RaBitQVector};
#[cfg(feature = "persistence")]
pub(crate) use rabitq_store::RaBitQVectorStore;
pub use scalar::{
cosine_similarity_quantized, cosine_similarity_quantized_simd, dot_product_quantized,
dot_product_quantized_simd, euclidean_squared_quantized, euclidean_squared_quantized_simd,
QuantizedVector,
};
pub trait QuantizationCodec: Sized {
fn to_bytes(&self) -> Vec<u8>;
fn from_bytes(bytes: &[u8]) -> io::Result<Self>;
}
pub const STORAGE_MODE_NAMES: &[&str] = &["full", "sq8", "binary", "pq", "rabitq"];
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
#[non_exhaustive]
pub enum StorageMode {
#[default]
Full,
SQ8,
Binary,
ProductQuantization,
RaBitQ,
}
impl StorageMode {
#[must_use]
pub const fn canonical_name(self) -> &'static str {
match self {
Self::Full => "full",
Self::SQ8 => "sq8",
Self::Binary => "binary",
Self::ProductQuantization => "pq",
Self::RaBitQ => "rabitq",
}
}
#[must_use]
pub fn parse_alias(value: &str) -> Option<Self> {
match value.trim().to_lowercase().as_str() {
"full" | "f32" => Some(Self::Full),
"sq8" | "int8" => Some(Self::SQ8),
"binary" | "bit" => Some(Self::Binary),
"pq" | "product_quantization" => Some(Self::ProductQuantization),
"rabitq" => Some(Self::RaBitQ),
_ => None,
}
}
}
impl std::fmt::Display for StorageMode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.canonical_name())
}
}
impl std::str::FromStr for StorageMode {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::parse_alias(s).ok_or_else(|| {
format!(
"Unknown storage mode '{s}'. Valid options: full, f32, sq8, int8, binary, bit, pq, product_quantization, rabitq"
)
})
}
}
#[cfg(test)]
#[path = "storage_mode_parsing_tests.rs"]
mod storage_mode_parsing_tests;