use anyhow::Result;
use scirs2_core::ndarray_ext::ArrayView1;
use scirs2_core::simd::{simd_dot_f32, simd_sub_f32};
pub fn simd_cosine_distance(a: &[f32], b: &[f32]) -> Result<f32> {
if a.len() != b.len() {
return Err(anyhow::anyhow!(
"Vector dimensions must match: {} != {}",
a.len(),
b.len()
));
}
let a_view = ArrayView1::from(a);
let b_view = ArrayView1::from(b);
let dot = simd_dot_f32(&a_view, &b_view);
let norm_a_sq = simd_dot_f32(&a_view, &a_view);
let norm_b_sq = simd_dot_f32(&b_view, &b_view);
let norm_a = norm_a_sq.sqrt();
let norm_b = norm_b_sq.sqrt();
if norm_a == 0.0 || norm_b == 0.0 {
return Ok(1.0); }
Ok(1.0 - (dot / (norm_a * norm_b)))
}
pub fn simd_euclidean(a: &[f32], b: &[f32]) -> Result<f32> {
if a.len() != b.len() {
return Err(anyhow::anyhow!(
"Vector dimensions must match: {} != {}",
a.len(),
b.len()
));
}
let a_view = ArrayView1::from(a);
let b_view = ArrayView1::from(b);
let diff = simd_sub_f32(&a_view, &b_view);
let diff_view = diff.view();
let dist_sq = simd_dot_f32(&diff_view, &diff_view);
Ok(dist_sq.sqrt())
}
pub fn simd_manhattan(a: &[f32], b: &[f32]) -> Result<f32> {
if a.len() != b.len() {
return Err(anyhow::anyhow!(
"Vector dimensions must match: {} != {}",
a.len(),
b.len()
));
}
let a_view = ArrayView1::from(a);
let b_view = ArrayView1::from(b);
let diff = simd_sub_f32(&a_view, &b_view);
let dist: f32 = diff.iter().map(|x| x.abs()).sum();
Ok(dist)
}
pub fn simd_dot_product_distance(a: &[f32], b: &[f32]) -> Result<f32> {
if a.len() != b.len() {
return Err(anyhow::anyhow!(
"Vector dimensions must match: {} != {}",
a.len(),
b.len()
));
}
let a_view = ArrayView1::from(a);
let b_view = ArrayView1::from(b);
let dot = simd_dot_f32(&a_view, &b_view);
Ok(-dot)
}
pub fn simd_batch_cosine_distances(query: &[f32], vectors: &[Vec<f32>]) -> Result<Vec<f32>> {
let mut distances = Vec::with_capacity(vectors.len());
for vector in vectors {
let dist = simd_cosine_distance(query, vector)?;
distances.push(dist);
}
Ok(distances)
}
pub fn simd_batch_euclidean_distances(query: &[f32], vectors: &[Vec<f32>]) -> Result<Vec<f32>> {
let mut distances = Vec::with_capacity(vectors.len());
for vector in vectors {
let dist = simd_euclidean(query, vector)?;
distances.push(dist);
}
Ok(distances)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_simd_cosine_distance() -> Result<()> {
let a = vec![1.0, 0.0, 0.0];
let b = vec![1.0, 0.0, 0.0];
let dist = simd_cosine_distance(&a, &b)?;
assert!((dist - 0.0).abs() < 1e-6);
let c = vec![0.0, 1.0, 0.0];
let dist = simd_cosine_distance(&a, &c)?;
assert!((dist - 1.0).abs() < 1e-6); Ok(())
}
#[test]
fn test_simd_euclidean() -> Result<()> {
let a = vec![0.0, 0.0, 0.0];
let b = vec![1.0, 1.0, 1.0];
let dist = simd_euclidean(&a, &b)?;
let expected = 3.0_f32.sqrt();
assert!((dist - expected).abs() < 1e-6);
Ok(())
}
#[test]
fn test_simd_manhattan() -> Result<()> {
let a = vec![0.0, 0.0, 0.0];
let b = vec![1.0, 1.0, 1.0];
let dist = simd_manhattan(&a, &b)?;
assert!((dist - 3.0).abs() < 1e-6);
Ok(())
}
#[test]
fn test_simd_batch_distances() -> Result<()> {
let query = vec![1.0, 0.0, 0.0];
let vectors = vec![
vec![1.0, 0.0, 0.0],
vec![0.0, 1.0, 0.0],
vec![0.0, 0.0, 1.0],
];
let distances = simd_batch_cosine_distances(&query, &vectors)?;
assert_eq!(distances.len(), 3);
assert!((distances[0] - 0.0).abs() < 1e-6); assert!((distances[1] - 1.0).abs() < 1e-6); assert!((distances[2] - 1.0).abs() < 1e-6); Ok(())
}
}