use std::fmt;
use serde::{Deserialize, Serialize};
use crate::superfile::{
BuildError,
vector::{
cell_posting::{
EncodedCellRow, note_transcode_clamped_components,
residual_family_materialize_into_cluster_quant,
},
distance::{
Metric, SQ8_RESIDUAL_DIVISOR, dequantize_sq8_residual_into,
dequantize_sq16_adaptive_into, dequantize_sq16_into, encode_sq16_adaptive_row,
sq8_residual_norm_sq, sq16_adaptive_norm_sq, sq16_decoded_norm_sq,
},
},
};
const LOW_DIM_RERANK_FLOOR_THRESHOLD: usize = 384;
const FP32_LOW_DIM_RERANK_FLOOR: usize = 20;
const FP32_HIGH_DIM_RERANK_FLOOR: usize = 50;
const SQ8_LOW_DIM_RERANK_FLOOR: usize = 50;
const SQ8_HIGH_DIM_RERANK_FLOOR: usize = 100;
pub(crate) const SQ8_FIXED_OFFSET: f32 = -1.0;
pub(crate) const SQ8_FIXED_SCALE: f32 = 2.0 / 255.0;
pub(crate) const SQ8_FIXED_RESIDUAL_DIVISOR: f32 = 256.0;
pub(crate) const SQ16_CODE_MAX: f32 = 65535.0;
pub(crate) const SQ16_FIXED_OFFSET: f32 = -1.0;
pub(crate) const SQ16_FIXED_SCALE: f32 = 2.0 / SQ16_CODE_MAX;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum RerankCodec {
Fp32,
Sq8Residual,
Sq8FixedResidual,
Sq16,
Sq16Adaptive,
RabitqOnly,
}
impl Default for RerankCodec {
fn default() -> Self {
Self::Sq16
}
}
impl RerankCodec {
#[inline]
pub const fn codec_id(self) -> u8 {
match self {
Self::Fp32 => 0,
Self::Sq8Residual => 1,
Self::RabitqOnly => 2,
Self::Sq8FixedResidual => 3,
Self::Sq16 => 4,
Self::Sq16Adaptive => 5,
}
}
#[inline]
pub const fn from_codec_id(id: u8) -> Option<Self> {
match id {
0 => Some(Self::Fp32),
1 => Some(Self::Sq8Residual),
2 => Some(Self::RabitqOnly),
3 => Some(Self::Sq8FixedResidual),
4 => Some(Self::Sq16),
5 => Some(Self::Sq16Adaptive),
_ => None,
}
}
#[inline]
pub const fn name(self) -> &'static str {
match self {
Self::Fp32 => "fp32",
Self::Sq8Residual => "sq8_residual",
Self::RabitqOnly => "rabitq_only",
Self::Sq8FixedResidual => "sq8_fixed_residual",
Self::Sq16 => "sq16",
Self::Sq16Adaptive => "sq16_adaptive",
}
}
#[inline]
pub const fn per_vector_bytes(self, dim: usize) -> usize {
match self {
Self::Fp32 => dim * 4,
Self::Sq8Residual | Self::Sq8FixedResidual | Self::Sq16 | Self::Sq16Adaptive => dim * 2,
Self::RabitqOnly => 0,
}
}
#[inline]
pub(crate) fn dim_from_codes_len(self, codes_len: usize) -> usize {
if self.is_sq8_residual_family() {
codes_len
} else {
codes_len / 2
}
}
#[inline]
pub const fn writes_full(self) -> bool {
!matches!(self, Self::RabitqOnly)
}
#[inline]
pub const fn is_implemented(self) -> bool {
matches!(
self,
Self::Fp32
| Self::Sq8Residual
| Self::Sq8FixedResidual
| Self::Sq16
| Self::Sq16Adaptive
| Self::RabitqOnly
)
}
#[inline]
pub const fn is_sq8_residual_family(self) -> bool {
matches!(self, Self::Sq8Residual | Self::Sq8FixedResidual)
}
#[inline]
pub const fn is_ivf_mergeable(self) -> bool {
matches!(
self,
Self::Sq8Residual | Self::Sq8FixedResidual | Self::Sq16 | Self::Sq16Adaptive
)
}
#[inline]
pub const fn writes_single_u16_plane(self) -> bool {
matches!(self, Self::Sq16 | Self::Sq16Adaptive)
}
#[inline]
pub const fn code_max(self) -> f32 {
if self.writes_single_u16_plane() {
SQ16_CODE_MAX
} else {
255.0
}
}
#[inline]
pub const fn carries_cluster_quant_meta(self) -> bool {
matches!(
self,
Self::Sq8Residual | Self::Sq8FixedResidual | Self::Sq16Adaptive
)
}
#[inline]
pub const fn fits_per_cluster_ruler(self) -> bool {
matches!(self, Self::Sq8Residual | Self::Sq16Adaptive)
}
#[inline]
pub const fn is_sq16(self) -> bool {
matches!(self, Self::Sq16)
}
#[inline]
pub const fn residual_divisor(self) -> Option<f32> {
match self {
Self::Sq8Residual => Some(SQ8_RESIDUAL_DIVISOR),
Self::Sq8FixedResidual => Some(SQ8_FIXED_RESIDUAL_DIVISOR),
Self::Fp32 | Self::Sq16 | Self::Sq16Adaptive | Self::RabitqOnly => None,
}
}
#[inline]
pub const fn uses_fixed_quantizer(self) -> bool {
matches!(self, Self::Sq8FixedResidual | Self::Sq16)
}
#[inline]
pub const fn supports_metric(self, metric: Metric) -> bool {
!matches!(self, Self::Sq8FixedResidual | Self::Sq16) || matches!(metric, Metric::Cosine)
}
#[inline]
pub const fn recommended_rerank_mult_floor(self, dim: usize) -> Option<usize> {
let high_dim = dim > LOW_DIM_RERANK_FLOOR_THRESHOLD;
match self {
Self::Fp32 | Self::Sq16 | Self::Sq16Adaptive => Some(if high_dim {
FP32_HIGH_DIM_RERANK_FLOOR
} else {
FP32_LOW_DIM_RERANK_FLOOR
}),
Self::Sq8Residual => Some(if high_dim {
SQ8_HIGH_DIM_RERANK_FLOOR
} else {
SQ8_LOW_DIM_RERANK_FLOOR
}),
Self::Sq8FixedResidual => Some(if high_dim {
SQ8_HIGH_DIM_RERANK_FLOOR
} else {
SQ8_LOW_DIM_RERANK_FLOOR
}),
Self::RabitqOnly => None,
}
}
#[inline]
pub const fn codec_meta_bytes(
self,
dim: usize,
n_docs: usize,
n_cent: usize,
metric: Metric,
) -> usize {
match self {
Self::Fp32 | Self::RabitqOnly => 0,
Self::Sq16 => match metric {
Metric::L2Sq | Metric::Cosine => n_docs * 4,
Metric::NegDot => 0,
},
Self::Sq8Residual | Self::Sq8FixedResidual | Self::Sq16Adaptive => {
let scale_offset_bytes = 2 * n_cent * dim * 4;
let norms_bytes = match metric {
Metric::L2Sq | Metric::Cosine => n_docs * 4,
Metric::NegDot => 0,
};
scale_offset_bytes + norms_bytes
}
}
}
}
impl fmt::Display for RerankCodec {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.name())
}
}
pub(crate) struct EncodedRowParts {
pub codes: Vec<u8>,
pub residuals: Vec<u8>,
pub norm_sq: Option<f32>,
}
pub(crate) struct CodecMetaLayout {
pub scale_off: Option<usize>,
pub offset_off: Option<usize>,
pub norms_off: Option<usize>,
}
pub(crate) trait RerankCodecOps: Sync {
fn parse_materialized_row(
&self,
row: &[u8],
dim: usize,
scale: &[f32],
offset: &[f32],
store_norm: bool,
) -> EncodedRowParts;
fn dequantize_row_into(
&self,
codes: &[u8],
residuals: &[u8],
dim: usize,
scale: &[f32],
offset: &[f32],
out: &mut [f32],
);
fn decoded_norm_sq(&self, code: &[u8], dim: usize, scale: &[f32], offset: &[f32]) -> f32;
fn materialize_row_into_cluster_quant(
&self,
row: &EncodedCellRow,
dst_scale: &[f32],
dst_offset: &[f32],
dim: usize,
out: &mut [u8],
store_norm: bool,
) -> Result<Option<f32>, BuildError>;
fn codec_meta_layout(
&self,
meta_off: usize,
n_cent: usize,
dim: usize,
metric: Metric,
) -> CodecMetaLayout;
}
pub(crate) struct Sq8ResidualOps;
pub(crate) struct Sq8FixedResidualOps;
pub(crate) struct Sq16Ops;
pub(crate) struct Sq16AdaptiveOps;
fn parse_residual_family_row(
row: &[u8],
dim: usize,
scale: &[f32],
offset: &[f32],
divisor: f32,
store_norm: bool,
) -> EncodedRowParts {
let codes = row[..dim].to_vec();
let residuals = row[dim..dim * 2].to_vec();
let norm_sq =
store_norm.then(|| sq8_residual_norm_sq(scale, offset, &codes, &residuals, divisor));
EncodedRowParts {
codes,
residuals,
norm_sq,
}
}
fn dequantize_residual_family_into(
codes: &[u8],
residuals: &[u8],
scale: &[f32],
offset: &[f32],
divisor: f32,
out: &mut [f32],
) {
dequantize_sq8_residual_into(scale, offset, codes, residuals, divisor, out);
}
fn residual_family_norm_sq(
code: &[u8],
dim: usize,
scale: &[f32],
offset: &[f32],
divisor: f32,
) -> f32 {
sq8_residual_norm_sq(scale, offset, &code[..dim], &code[dim..dim * 2], divisor)
}
fn residual_family_codec_meta_layout(
meta_off: usize,
n_cent: usize,
dim: usize,
metric: Metric,
) -> CodecMetaLayout {
let scale_off = meta_off;
let offset_off = scale_off + n_cent * dim * size_of::<f32>();
let norms_off = matches!(metric, Metric::L2Sq | Metric::Cosine)
.then_some(offset_off + n_cent * dim * size_of::<f32>());
CodecMetaLayout {
scale_off: Some(scale_off),
offset_off: Some(offset_off),
norms_off,
}
}
impl RerankCodecOps for Sq8ResidualOps {
fn parse_materialized_row(
&self,
row: &[u8],
dim: usize,
scale: &[f32],
offset: &[f32],
store_norm: bool,
) -> EncodedRowParts {
parse_residual_family_row(row, dim, scale, offset, SQ8_RESIDUAL_DIVISOR, store_norm)
}
fn dequantize_row_into(
&self,
codes: &[u8],
residuals: &[u8],
_dim: usize,
scale: &[f32],
offset: &[f32],
out: &mut [f32],
) {
dequantize_residual_family_into(codes, residuals, scale, offset, SQ8_RESIDUAL_DIVISOR, out);
}
fn decoded_norm_sq(&self, code: &[u8], dim: usize, scale: &[f32], offset: &[f32]) -> f32 {
residual_family_norm_sq(code, dim, scale, offset, SQ8_RESIDUAL_DIVISOR)
}
fn materialize_row_into_cluster_quant(
&self,
row: &EncodedCellRow,
dst_scale: &[f32],
dst_offset: &[f32],
dim: usize,
out: &mut [u8],
store_norm: bool,
) -> Result<Option<f32>, BuildError> {
residual_family_materialize_into_cluster_quant(
row,
RerankCodec::Sq8Residual,
dst_scale,
dst_offset,
dim,
out,
store_norm,
)
}
fn codec_meta_layout(
&self,
meta_off: usize,
n_cent: usize,
dim: usize,
metric: Metric,
) -> CodecMetaLayout {
residual_family_codec_meta_layout(meta_off, n_cent, dim, metric)
}
}
impl RerankCodecOps for Sq8FixedResidualOps {
fn parse_materialized_row(
&self,
row: &[u8],
dim: usize,
scale: &[f32],
offset: &[f32],
store_norm: bool,
) -> EncodedRowParts {
parse_residual_family_row(
row,
dim,
scale,
offset,
SQ8_FIXED_RESIDUAL_DIVISOR,
store_norm,
)
}
fn dequantize_row_into(
&self,
codes: &[u8],
residuals: &[u8],
_dim: usize,
scale: &[f32],
offset: &[f32],
out: &mut [f32],
) {
dequantize_residual_family_into(
codes,
residuals,
scale,
offset,
SQ8_FIXED_RESIDUAL_DIVISOR,
out,
);
}
fn decoded_norm_sq(&self, code: &[u8], dim: usize, scale: &[f32], offset: &[f32]) -> f32 {
residual_family_norm_sq(code, dim, scale, offset, SQ8_FIXED_RESIDUAL_DIVISOR)
}
fn materialize_row_into_cluster_quant(
&self,
row: &EncodedCellRow,
dst_scale: &[f32],
dst_offset: &[f32],
dim: usize,
out: &mut [u8],
store_norm: bool,
) -> Result<Option<f32>, BuildError> {
residual_family_materialize_into_cluster_quant(
row,
RerankCodec::Sq8FixedResidual,
dst_scale,
dst_offset,
dim,
out,
store_norm,
)
}
fn codec_meta_layout(
&self,
meta_off: usize,
n_cent: usize,
dim: usize,
metric: Metric,
) -> CodecMetaLayout {
residual_family_codec_meta_layout(meta_off, n_cent, dim, metric)
}
}
impl RerankCodecOps for Sq16Ops {
fn parse_materialized_row(
&self,
row: &[u8],
dim: usize,
_scale: &[f32],
_offset: &[f32],
store_norm: bool,
) -> EncodedRowParts {
let codes = row[..dim * 2].to_vec();
let norm_sq = store_norm.then(|| sq16_decoded_norm_sq(&codes, dim));
EncodedRowParts {
codes,
residuals: Vec::new(),
norm_sq,
}
}
fn dequantize_row_into(
&self,
codes: &[u8],
_residuals: &[u8],
_dim: usize,
_scale: &[f32],
_offset: &[f32],
out: &mut [f32],
) {
dequantize_sq16_into(codes, out);
}
fn decoded_norm_sq(&self, code: &[u8], dim: usize, _scale: &[f32], _offset: &[f32]) -> f32 {
sq16_decoded_norm_sq(&code[..dim * 2], dim)
}
fn materialize_row_into_cluster_quant(
&self,
row: &EncodedCellRow,
_dst_scale: &[f32],
_dst_offset: &[f32],
dim: usize,
out: &mut [u8],
store_norm: bool,
) -> Result<Option<f32>, BuildError> {
if row.rerank_codec != RerankCodec::Sq16 {
return Err(BuildError::VectorSchemaMismatch(format!(
"cannot transcode Sq16 row from {} to {}",
row.rerank_codec.name(),
RerankCodec::Sq16.name()
)));
}
out[..dim * 2].copy_from_slice(&row.codes);
Ok(store_norm.then(|| {
row.norm_sq
.unwrap_or_else(|| sq16_decoded_norm_sq(&row.codes, dim))
}))
}
fn codec_meta_layout(
&self,
meta_off: usize,
_n_cent: usize,
_dim: usize,
metric: Metric,
) -> CodecMetaLayout {
let norms_off = matches!(metric, Metric::L2Sq | Metric::Cosine).then_some(meta_off);
CodecMetaLayout {
scale_off: None,
offset_off: None,
norms_off,
}
}
}
impl RerankCodecOps for Sq16AdaptiveOps {
fn parse_materialized_row(
&self,
row: &[u8],
dim: usize,
scale: &[f32],
offset: &[f32],
store_norm: bool,
) -> EncodedRowParts {
let codes = row[..dim * 2].to_vec();
let norm_sq = store_norm.then(|| sq16_adaptive_norm_sq(&codes, dim, scale, offset));
EncodedRowParts {
codes,
residuals: Vec::new(),
norm_sq,
}
}
fn dequantize_row_into(
&self,
codes: &[u8],
_residuals: &[u8],
_dim: usize,
scale: &[f32],
offset: &[f32],
out: &mut [f32],
) {
dequantize_sq16_adaptive_into(codes, scale, offset, out);
}
fn decoded_norm_sq(&self, code: &[u8], dim: usize, scale: &[f32], offset: &[f32]) -> f32 {
sq16_adaptive_norm_sq(&code[..dim * 2], dim, scale, offset)
}
fn materialize_row_into_cluster_quant(
&self,
row: &EncodedCellRow,
dst_scale: &[f32],
dst_offset: &[f32],
dim: usize,
out: &mut [u8],
store_norm: bool,
) -> Result<Option<f32>, BuildError> {
if row.rerank_codec != RerankCodec::Sq16Adaptive {
return Err(BuildError::VectorSchemaMismatch(format!(
"cannot transcode Sq16Adaptive row from {} to {}",
row.rerank_codec.name(),
RerankCodec::Sq16Adaptive.name()
)));
}
let mut decoded = vec![0.0f32; dim];
dequantize_sq16_adaptive_into(&row.codes, &row.scale, &row.offset, &mut decoded);
let clamped = encode_sq16_adaptive_row(&decoded, dst_scale, dst_offset, out);
note_transcode_clamped_components(clamped);
Ok(store_norm.then(|| sq16_adaptive_norm_sq(out, dim, dst_scale, dst_offset)))
}
fn codec_meta_layout(
&self,
meta_off: usize,
n_cent: usize,
dim: usize,
metric: Metric,
) -> CodecMetaLayout {
residual_family_codec_meta_layout(meta_off, n_cent, dim, metric)
}
}
impl RerankCodec {
pub(crate) fn ops(&self) -> Option<&'static dyn RerankCodecOps> {
match self {
Self::Sq8Residual => Some(&Sq8ResidualOps),
Self::Sq8FixedResidual => Some(&Sq8FixedResidualOps),
Self::Sq16 => Some(&Sq16Ops),
Self::Sq16Adaptive => Some(&Sq16AdaptiveOps),
Self::Fp32 | Self::RabitqOnly => None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_is_sq16() {
assert_eq!(RerankCodec::default(), RerankCodec::Sq16);
}
#[test]
fn fp32_codec_id_is_zero() {
assert_eq!(RerankCodec::Fp32.codec_id(), 0u8);
}
#[test]
fn codec_id_roundtrips_every_variant() {
for c in [
RerankCodec::Fp32,
RerankCodec::Sq8Residual,
RerankCodec::Sq8FixedResidual,
RerankCodec::Sq16,
RerankCodec::Sq16Adaptive,
RerankCodec::RabitqOnly,
] {
assert_eq!(
RerankCodec::from_codec_id(c.codec_id()),
Some(c),
"round-trip mismatch for {c:?}"
);
}
}
#[test]
fn sq16_adaptive_sits_on_both_axes() {
let c = RerankCodec::Sq16Adaptive;
assert_eq!(c.codec_id(), 5);
assert_eq!(c.per_vector_bytes(384), 384 * 2);
assert_eq!(c.code_max(), 65535.0);
assert_eq!(RerankCodec::Sq8Residual.code_max(), 255.0);
assert!(c.writes_single_u16_plane());
assert!(!c.is_sq8_residual_family());
assert!(c.fits_per_cluster_ruler());
assert!(c.carries_cluster_quant_meta());
assert!(!c.uses_fixed_quantizer());
assert_eq!(c.residual_divisor(), None);
assert!(c.is_ivf_mergeable());
assert!(c.supports_metric(Metric::L2Sq) && c.supports_metric(Metric::NegDot));
assert_eq!(c.dim_from_codes_len(384 * 2), 384);
}
#[test]
fn unknown_codec_id_is_none() {
assert_eq!(
RerankCodec::from_codec_id(5),
Some(RerankCodec::Sq16Adaptive)
);
for id in [6u8, 16, 200, 255] {
assert_eq!(
RerankCodec::from_codec_id(id),
None,
"unknown id {id} must not map to a codec"
);
}
}
#[test]
fn per_vector_bytes_matches_spec() {
assert_eq!(RerankCodec::Fp32.per_vector_bytes(384), 1536);
assert_eq!(RerankCodec::Sq8Residual.per_vector_bytes(384), 768);
assert_eq!(RerankCodec::Sq8FixedResidual.per_vector_bytes(384), 768);
assert_eq!(RerankCodec::Sq16.per_vector_bytes(384), 768);
assert_eq!(RerankCodec::RabitqOnly.per_vector_bytes(384), 0);
}
#[test]
fn dim_from_codes_len_inverts_code_plane_size() {
let dim = 1024usize;
assert_eq!(RerankCodec::Sq8Residual.dim_from_codes_len(dim), dim);
assert_eq!(RerankCodec::Sq8FixedResidual.dim_from_codes_len(dim), dim);
assert_eq!(RerankCodec::Sq16.dim_from_codes_len(dim * 2), dim);
}
#[test]
fn writes_full_matches_per_vector_bytes() {
for c in [
RerankCodec::Fp32,
RerankCodec::Sq8Residual,
RerankCodec::Sq8FixedResidual,
RerankCodec::Sq16,
RerankCodec::RabitqOnly,
] {
assert_eq!(
c.writes_full(),
c.per_vector_bytes(384) > 0,
"writes_full disagrees with per_vector_bytes for {c:?}"
);
}
}
#[test]
fn all_codecs_implemented() {
assert!(RerankCodec::Fp32.is_implemented());
assert!(RerankCodec::Sq8Residual.is_implemented());
assert!(RerankCodec::Sq8FixedResidual.is_implemented());
assert!(RerankCodec::Sq16.is_implemented());
assert!(RerankCodec::RabitqOnly.is_implemented());
}
#[test]
fn recommended_rerank_mult_floor_matches_calibration_table() {
assert_eq!(
RerankCodec::Fp32.recommended_rerank_mult_floor(384),
Some(20)
);
assert_eq!(
RerankCodec::Sq8Residual.recommended_rerank_mult_floor(384),
Some(50)
);
assert_eq!(
RerankCodec::Sq8FixedResidual.recommended_rerank_mult_floor(384),
Some(50)
);
assert_eq!(
RerankCodec::Sq16.recommended_rerank_mult_floor(384),
Some(20)
);
assert_eq!(
RerankCodec::RabitqOnly.recommended_rerank_mult_floor(384),
None
);
assert_eq!(
RerankCodec::Fp32.recommended_rerank_mult_floor(1024),
Some(50)
);
assert_eq!(
RerankCodec::Sq8Residual.recommended_rerank_mult_floor(1024),
Some(100)
);
assert_eq!(
RerankCodec::Sq8FixedResidual.recommended_rerank_mult_floor(1024),
Some(100)
);
assert_eq!(
RerankCodec::Sq16.recommended_rerank_mult_floor(1024),
Some(50)
);
assert_eq!(
RerankCodec::RabitqOnly.recommended_rerank_mult_floor(1024),
None
);
assert_eq!(
RerankCodec::Sq8Residual.recommended_rerank_mult_floor(385),
Some(100)
);
}
#[test]
fn display_renders_stable_name() {
assert_eq!(RerankCodec::Fp32.to_string(), "fp32");
assert_eq!(RerankCodec::Sq8Residual.to_string(), "sq8_residual");
assert_eq!(
RerankCodec::Sq8FixedResidual.to_string(),
"sq8_fixed_residual"
);
assert_eq!(RerankCodec::Sq16.to_string(), "sq16");
assert_eq!(RerankCodec::RabitqOnly.to_string(), "rabitq_only");
for c in [
RerankCodec::Fp32,
RerankCodec::Sq8Residual,
RerankCodec::Sq8FixedResidual,
RerankCodec::Sq16,
RerankCodec::RabitqOnly,
] {
assert_eq!(c.to_string(), c.name());
}
}
#[test]
fn codec_meta_bytes_matches_layout_spec() {
for c in [RerankCodec::Fp32, RerankCodec::RabitqOnly] {
for m in [Metric::L2Sq, Metric::Cosine, Metric::NegDot] {
assert_eq!(
c.codec_meta_bytes(384, 1_000_000, 1024, m),
0,
"{c:?} / {m:?}"
);
}
}
let so_bytes = 2 * 1024 * 384 * 4;
assert_eq!(
RerankCodec::Sq8Residual.codec_meta_bytes(384, 1_000_000, 1024, Metric::NegDot),
so_bytes
);
assert_eq!(
RerankCodec::Sq8Residual.codec_meta_bytes(384, 1_000_000, 1024, Metric::Cosine),
so_bytes + 1_000_000 * 4
);
assert_eq!(
RerankCodec::Sq8FixedResidual.codec_meta_bytes(384, 1_000_000, 1024, Metric::Cosine),
so_bytes + 1_000_000 * 4
);
assert_eq!(
RerankCodec::Sq8Residual.codec_meta_bytes(384, 1_000_000, 1024, Metric::L2Sq),
so_bytes + 1_000_000 * 4
);
assert_eq!(
RerankCodec::Sq8Residual.codec_meta_bytes(384, 1_000_000, 1024, Metric::NegDot),
so_bytes
);
}
#[test]
fn fixed_residual_contract_is_cosine_only() {
assert!(RerankCodec::Sq8FixedResidual.supports_metric(Metric::Cosine));
assert!(!RerankCodec::Sq8FixedResidual.supports_metric(Metric::L2Sq));
assert!(!RerankCodec::Sq8FixedResidual.supports_metric(Metric::NegDot));
assert_eq!(
RerankCodec::Sq8FixedResidual.residual_divisor(),
Some(SQ8_FIXED_RESIDUAL_DIVISOR)
);
assert!(RerankCodec::Sq8FixedResidual.uses_fixed_quantizer());
assert!(RerankCodec::Sq8FixedResidual.is_sq8_residual_family());
}
#[test]
fn sq16_contract_is_flat_cosine_only_norms_meta() {
assert!(RerankCodec::Sq16.supports_metric(Metric::Cosine));
assert!(!RerankCodec::Sq16.supports_metric(Metric::L2Sq));
assert!(!RerankCodec::Sq16.supports_metric(Metric::NegDot));
assert_eq!(RerankCodec::Sq16.residual_divisor(), None);
assert!(RerankCodec::Sq16.is_sq16());
assert!(!RerankCodec::Sq16.is_sq8_residual_family());
assert!(RerankCodec::Sq16.uses_fixed_quantizer());
assert!(RerankCodec::Sq16.writes_full());
assert_eq!(RerankCodec::Sq16.per_vector_bytes(1024), 1024 * 2);
assert_eq!(
RerankCodec::Sq16.codec_meta_bytes(384, 1_000_000, 1024, Metric::Cosine),
1_000_000 * 4
);
assert_eq!(
RerankCodec::Sq16.codec_meta_bytes(384, 1_000_000, 1024, Metric::L2Sq),
1_000_000 * 4
);
assert_eq!(
RerankCodec::Sq16.codec_meta_bytes(384, 1_000_000, 1024, Metric::NegDot),
0
);
}
}