use crate::config::DistanceMetric;
use crate::error::SearchError;
use crate::simd::DistanceKernel;
#[allow(clippy::cast_possible_truncation)]
pub(crate) fn squared_norm(vector: &[f32], position: Option<usize>) -> Result<f32, SearchError> {
let mut squared = 0.0_f64;
for (dimension, value) in vector.iter().copied().enumerate() {
if !value.is_finite() {
return Err(SearchError::NonFiniteValue {
vector: position,
dimension,
});
}
squared += f64::from(value) * f64::from(value);
}
Ok(squared as f32)
}
pub(crate) fn inverse_norm(vector: &[f32], position: Option<usize>) -> Result<f32, SearchError> {
let squared = squared_norm(vector, position)?;
if squared == 0.0 {
return Err(SearchError::ZeroVector { vector: position });
}
Ok(squared.sqrt().recip())
}
pub(crate) fn distance(
kernel: DistanceKernel,
metric: DistanceMetric,
left: &[f32],
left_squared_norm: f32,
right: &[f32],
right_squared_norm: f32,
) -> f32 {
let dot = kernel.dot(left, right);
match metric {
DistanceMetric::Cosine => {
let denominator = (left_squared_norm * right_squared_norm).sqrt();
(1.0 - dot / denominator).clamp(0.0, 2.0)
}
DistanceMetric::Dot => -dot,
DistanceMetric::SquaredEuclidean => left_squared_norm
.mul_add(1.0, right_squared_norm - 2.0 * dot)
.max(0.0),
}
}