#![allow(dead_code)]
#[inline]
pub fn cosine_distance(a: &[f32], b: &[f32]) -> f32 {
1.0 - cosine_similarity(a, b)
}
#[inline]
pub fn cosine_similarity(a: &[f32], b: &[f32]) -> f32 {
debug_assert_eq!(a.len(), b.len(), "Vector length mismatch");
let dot = dot_product(a, b);
let norm_a = l2_norm(a);
let norm_b = l2_norm(b);
if norm_a < 1e-10 || norm_b < 1e-10 {
return 0.0;
}
(dot / (norm_a * norm_b)).clamp(-1.0, 1.0)
}
#[inline]
pub fn euclidean_distance(a: &[f32], b: &[f32]) -> f32 {
squared_euclidean_distance(a, b).sqrt()
}
#[inline]
pub fn squared_euclidean_distance(a: &[f32], b: &[f32]) -> f32 {
debug_assert_eq!(a.len(), b.len(), "Vector length mismatch");
#[cfg(feature = "simd")]
{
simd_squared_euclidean(a, b)
}
#[cfg(not(feature = "simd"))]
{
a.iter()
.zip(b.iter())
.map(|(x, y)| {
let diff = x - y;
diff * diff
})
.sum()
}
}
#[inline]
pub fn dot_product(a: &[f32], b: &[f32]) -> f32 {
debug_assert_eq!(a.len(), b.len(), "Vector length mismatch");
#[cfg(feature = "simd")]
{
simd_dot_product(a, b)
}
#[cfg(not(feature = "simd"))]
{
a.iter().zip(b.iter()).map(|(x, y)| x * y).sum()
}
}
#[inline]
pub fn l2_norm(v: &[f32]) -> f32 {
dot_product(v, v).sqrt()
}
#[inline]
pub fn l1_norm(v: &[f32]) -> f32 {
v.iter().map(|x| x.abs()).sum()
}
#[inline]
pub fn normalize_vector(v: &[f32]) -> Vec<f32> {
let norm = l2_norm(v);
if norm < 1e-10 {
return vec![0.0; v.len()];
}
v.iter().map(|x| x / norm).collect()
}
#[inline]
pub fn normalize_vector_inplace(v: &mut [f32]) {
let norm = l2_norm(v);
if norm < 1e-10 {
v.fill(0.0);
return;
}
for x in v.iter_mut() {
*x /= norm;
}
}
#[inline]
pub fn manhattan_distance(a: &[f32], b: &[f32]) -> f32 {
debug_assert_eq!(a.len(), b.len(), "Vector length mismatch");
a.iter().zip(b.iter()).map(|(x, y)| (x - y).abs()).sum()
}
#[inline]
pub fn chebyshev_distance(a: &[f32], b: &[f32]) -> f32 {
debug_assert_eq!(a.len(), b.len(), "Vector length mismatch");
a.iter()
.zip(b.iter())
.map(|(x, y)| (x - y).abs())
.fold(0.0f32, f32::max)
}
#[inline]
pub fn angular_distance(a: &[f32], b: &[f32]) -> f32 {
let cos_sim = cosine_similarity(a, b);
cos_sim.acos() / std::f32::consts::PI
}
#[inline]
pub fn vector_add(a: &[f32], b: &[f32]) -> Vec<f32> {
debug_assert_eq!(a.len(), b.len(), "Vector length mismatch");
a.iter().zip(b.iter()).map(|(x, y)| x + y).collect()
}
#[inline]
pub fn vector_sub(a: &[f32], b: &[f32]) -> Vec<f32> {
debug_assert_eq!(a.len(), b.len(), "Vector length mismatch");
a.iter().zip(b.iter()).map(|(x, y)| x - y).collect()
}
#[inline]
pub fn vector_scale(v: &[f32], scalar: f32) -> Vec<f32> {
v.iter().map(|x| x * scalar).collect()
}
pub fn centroid(vectors: &[&[f32]]) -> Option<Vec<f32>> {
if vectors.is_empty() {
return None;
}
let dim = vectors[0].len();
let n = vectors.len() as f32;
let mut result = vec![0.0; dim];
for v in vectors {
debug_assert_eq!(v.len(), dim, "Vector dimension mismatch");
for (i, &x) in v.iter().enumerate() {
result[i] += x;
}
}
for x in result.iter_mut() {
*x /= n;
}
Some(result)
}
#[inline]
pub fn is_normalized(v: &[f32], tolerance: f32) -> bool {
let norm = l2_norm(v);
(norm - 1.0).abs() < tolerance
}
#[cfg(feature = "simd")]
fn simd_dot_product(a: &[f32], b: &[f32]) -> f32 {
a.iter().zip(b.iter()).map(|(x, y)| x * y).sum()
}
#[cfg(feature = "simd")]
fn simd_squared_euclidean(a: &[f32], b: &[f32]) -> f32 {
a.iter()
.zip(b.iter())
.map(|(x, y)| {
let diff = x - y;
diff * diff
})
.sum()
}
#[cfg(test)]
mod tests {
use super::*;
use approx::assert_relative_eq;
#[test]
fn test_cosine_similarity_identical() {
let v = vec![1.0, 2.0, 3.0];
let sim = cosine_similarity(&v, &v);
assert_relative_eq!(sim, 1.0, epsilon = 1e-6);
}
#[test]
fn test_cosine_similarity_opposite() {
let v1 = vec![1.0, 0.0, 0.0];
let v2 = vec![-1.0, 0.0, 0.0];
let sim = cosine_similarity(&v1, &v2);
assert_relative_eq!(sim, -1.0, epsilon = 1e-6);
}
#[test]
fn test_cosine_similarity_orthogonal() {
let v1 = vec![1.0, 0.0, 0.0];
let v2 = vec![0.0, 1.0, 0.0];
let sim = cosine_similarity(&v1, &v2);
assert_relative_eq!(sim, 0.0, epsilon = 1e-6);
}
#[test]
fn test_cosine_distance() {
let v1 = vec![1.0, 0.0, 0.0];
let v2 = vec![0.0, 1.0, 0.0];
let dist = cosine_distance(&v1, &v2);
assert_relative_eq!(dist, 1.0, epsilon = 1e-6);
}
#[test]
fn test_euclidean_distance() {
let v1 = vec![0.0, 0.0, 0.0];
let v2 = vec![3.0, 4.0, 0.0];
let dist = euclidean_distance(&v1, &v2);
assert_relative_eq!(dist, 5.0, epsilon = 1e-6);
}
#[test]
fn test_normalize_vector() {
let v = vec![3.0, 4.0];
let normalized = normalize_vector(&v);
assert_relative_eq!(l2_norm(&normalized), 1.0, epsilon = 1e-6);
assert_relative_eq!(normalized[0], 0.6, epsilon = 1e-6);
assert_relative_eq!(normalized[1], 0.8, epsilon = 1e-6);
}
#[test]
fn test_normalize_zero_vector() {
let v = vec![0.0, 0.0, 0.0];
let normalized = normalize_vector(&v);
assert!(normalized.iter().all(|&x| x == 0.0));
}
#[test]
fn test_dot_product() {
let v1 = vec![1.0, 2.0, 3.0];
let v2 = vec![4.0, 5.0, 6.0];
let dot = dot_product(&v1, &v2);
assert_relative_eq!(dot, 32.0, epsilon = 1e-6); }
#[test]
fn test_manhattan_distance() {
let v1 = vec![0.0, 0.0];
let v2 = vec![3.0, 4.0];
let dist = manhattan_distance(&v1, &v2);
assert_relative_eq!(dist, 7.0, epsilon = 1e-6);
}
#[test]
fn test_chebyshev_distance() {
let v1 = vec![0.0, 0.0];
let v2 = vec![3.0, 4.0];
let dist = chebyshev_distance(&v1, &v2);
assert_relative_eq!(dist, 4.0, epsilon = 1e-6);
}
#[test]
fn test_centroid() {
let v1 = vec![0.0, 0.0];
let v2 = vec![2.0, 2.0];
let v3 = vec![4.0, 4.0];
let c = centroid(&[&v1, &v2, &v3]).unwrap();
assert_relative_eq!(c[0], 2.0, epsilon = 1e-6);
assert_relative_eq!(c[1], 2.0, epsilon = 1e-6);
}
#[test]
fn test_is_normalized() {
let v = normalize_vector(&[3.0, 4.0]);
assert!(is_normalized(&v, 1e-6));
let v2 = vec![1.0, 2.0, 3.0];
assert!(!is_normalized(&v2, 1e-6));
}
#[test]
fn test_vector_operations() {
let v1 = vec![1.0, 2.0];
let v2 = vec![3.0, 4.0];
let sum = vector_add(&v1, &v2);
assert_eq!(sum, vec![4.0, 6.0]);
let diff = vector_sub(&v1, &v2);
assert_eq!(diff, vec![-2.0, -2.0]);
let scaled = vector_scale(&v1, 2.0);
assert_eq!(scaled, vec![2.0, 4.0]);
}
#[test]
fn test_angular_distance() {
let v1 = vec![1.0, 0.0];
let v2 = vec![0.0, 1.0];
let dist = angular_distance(&v1, &v2);
assert_relative_eq!(dist, 0.5, epsilon = 1e-6);
}
}