Merge commit 'd803bfe2b1fe7f5e219e50ac20d6801a0a58ac75' as 'vendor/ruvector'
This commit is contained in:
347
vendor/ruvector/crates/ruvector-postgres/src/distance/mod.rs
vendored
Normal file
347
vendor/ruvector/crates/ruvector-postgres/src/distance/mod.rs
vendored
Normal file
@@ -0,0 +1,347 @@
|
||||
//! SIMD-optimized distance functions for vector similarity search
|
||||
//!
|
||||
//! This module provides high-performance distance calculations with:
|
||||
//! - AVX-512 support (16 floats per operation)
|
||||
//! - AVX2 support (8 floats per operation)
|
||||
//! - ARM NEON support (4 floats per operation)
|
||||
//! - Scalar fallback for all platforms
|
||||
|
||||
pub mod scalar;
|
||||
pub mod simd;
|
||||
|
||||
pub use scalar::*;
|
||||
pub use simd::*;
|
||||
|
||||
use std::sync::OnceLock;
|
||||
|
||||
/// Distance metric types
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum DistanceMetric {
|
||||
/// L2 (Euclidean) distance: sqrt(sum((a[i] - b[i])^2))
|
||||
Euclidean,
|
||||
/// Cosine distance: 1 - (a·b)/(‖a‖‖b‖)
|
||||
Cosine,
|
||||
/// Negative inner product: -sum(a[i] * b[i])
|
||||
InnerProduct,
|
||||
/// L1 (Manhattan) distance: sum(|a[i] - b[i]|)
|
||||
Manhattan,
|
||||
/// Hamming distance (for binary vectors)
|
||||
Hamming,
|
||||
}
|
||||
|
||||
/// SIMD capability levels
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum SimdCapability {
|
||||
/// AVX-512 (512-bit, 16 floats)
|
||||
Avx512,
|
||||
/// AVX2 (256-bit, 8 floats)
|
||||
Avx2,
|
||||
/// ARM NEON (128-bit, 4 floats)
|
||||
Neon,
|
||||
/// Scalar fallback
|
||||
Scalar,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for SimdCapability {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
SimdCapability::Avx512 => write!(f, "avx512"),
|
||||
SimdCapability::Avx2 => write!(f, "avx2"),
|
||||
SimdCapability::Neon => write!(f, "neon"),
|
||||
SimdCapability::Scalar => write!(f, "scalar"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Detected SIMD capability (cached)
|
||||
static SIMD_CAPABILITY: OnceLock<SimdCapability> = OnceLock::new();
|
||||
|
||||
/// Function pointer table for distance calculations
|
||||
pub struct DistanceFunctions {
|
||||
pub euclidean: fn(&[f32], &[f32]) -> f32,
|
||||
pub cosine: fn(&[f32], &[f32]) -> f32,
|
||||
pub inner_product: fn(&[f32], &[f32]) -> f32,
|
||||
pub manhattan: fn(&[f32], &[f32]) -> f32,
|
||||
}
|
||||
|
||||
static DISTANCE_FNS: OnceLock<DistanceFunctions> = OnceLock::new();
|
||||
|
||||
/// Initialize SIMD dispatch (called at extension load)
|
||||
pub fn init_simd_dispatch() {
|
||||
let cap = detect_simd_capability();
|
||||
SIMD_CAPABILITY.get_or_init(|| cap);
|
||||
DISTANCE_FNS.get_or_init(|| create_distance_functions(cap));
|
||||
}
|
||||
|
||||
/// Detect best available SIMD capability
|
||||
fn detect_simd_capability() -> SimdCapability {
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
{
|
||||
if is_x86_feature_detected!("avx512f") && is_x86_feature_detected!("avx512vl") {
|
||||
return SimdCapability::Avx512;
|
||||
}
|
||||
if is_x86_feature_detected!("avx2") && is_x86_feature_detected!("fma") {
|
||||
return SimdCapability::Avx2;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
{
|
||||
// NEON is always available on aarch64
|
||||
return SimdCapability::Neon;
|
||||
}
|
||||
|
||||
#[allow(unreachable_code)]
|
||||
SimdCapability::Scalar
|
||||
}
|
||||
|
||||
/// Create distance function table for the detected capability
|
||||
///
|
||||
/// Uses the new optimized functions that integrate simsimd 5.9 and
|
||||
/// dimension-specialized implementations for maximum performance.
|
||||
fn create_distance_functions(cap: SimdCapability) -> DistanceFunctions {
|
||||
match cap {
|
||||
SimdCapability::Avx512 => DistanceFunctions {
|
||||
// Use optimized functions that auto-dispatch based on dimensions
|
||||
euclidean: simd::l2_distance_optimized,
|
||||
cosine: simd::cosine_distance_optimized,
|
||||
inner_product: simd::inner_product_distance_optimized,
|
||||
manhattan: simd::manhattan_distance_avx2_wrapper,
|
||||
},
|
||||
SimdCapability::Avx2 => DistanceFunctions {
|
||||
// Use optimized functions with simsimd + 4x unrolled AVX2
|
||||
euclidean: simd::l2_distance_optimized,
|
||||
cosine: simd::cosine_distance_optimized,
|
||||
inner_product: simd::inner_product_distance_optimized,
|
||||
manhattan: simd::manhattan_distance_avx2_wrapper,
|
||||
},
|
||||
SimdCapability::Neon => DistanceFunctions {
|
||||
// Use simsimd on NEON (auto-dispatched)
|
||||
euclidean: simd::l2_distance_simsimd,
|
||||
cosine: simd::cosine_distance_simsimd,
|
||||
inner_product: simd::inner_product_distance_simsimd,
|
||||
manhattan: scalar::manhattan_distance, // NEON manhattan not critical
|
||||
},
|
||||
SimdCapability::Scalar => DistanceFunctions {
|
||||
// Use simsimd even on scalar (it has good fallbacks)
|
||||
euclidean: simd::l2_distance_simsimd,
|
||||
cosine: simd::cosine_distance_simsimd,
|
||||
inner_product: simd::inner_product_distance_simsimd,
|
||||
manhattan: scalar::manhattan_distance,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Get SIMD info string
|
||||
pub fn simd_info() -> &'static str {
|
||||
match SIMD_CAPABILITY.get() {
|
||||
Some(SimdCapability::Avx512) => "avx512",
|
||||
Some(SimdCapability::Avx2) => "avx2",
|
||||
Some(SimdCapability::Neon) => "neon",
|
||||
Some(SimdCapability::Scalar) => "scalar",
|
||||
None => "uninitialized",
|
||||
}
|
||||
}
|
||||
|
||||
/// Get detailed SIMD info
|
||||
pub fn simd_info_detailed() -> String {
|
||||
let cap = SIMD_CAPABILITY
|
||||
.get()
|
||||
.copied()
|
||||
.unwrap_or(SimdCapability::Scalar);
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
{
|
||||
let mut features = Vec::new();
|
||||
if is_x86_feature_detected!("avx512f") {
|
||||
features.push("avx512f");
|
||||
}
|
||||
if is_x86_feature_detected!("avx512vl") {
|
||||
features.push("avx512vl");
|
||||
}
|
||||
if is_x86_feature_detected!("avx2") {
|
||||
features.push("avx2");
|
||||
}
|
||||
if is_x86_feature_detected!("fma") {
|
||||
features.push("fma");
|
||||
}
|
||||
if is_x86_feature_detected!("sse4.2") {
|
||||
features.push("sse4.2");
|
||||
}
|
||||
|
||||
let floats_per_op = match cap {
|
||||
SimdCapability::Avx512 => 16,
|
||||
SimdCapability::Avx2 => 8,
|
||||
_ => 1,
|
||||
};
|
||||
|
||||
return format!(
|
||||
"architecture: x86_64, active: {}, features: [{}], floats_per_op: {}",
|
||||
cap,
|
||||
features.join(", "),
|
||||
floats_per_op
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
{
|
||||
return format!("architecture: aarch64, active: neon, floats_per_op: 4");
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
|
||||
{
|
||||
format!("architecture: unknown, active: scalar, floats_per_op: 1")
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Public Distance Functions (dispatch to optimal implementation)
|
||||
// ============================================================================
|
||||
|
||||
/// Calculate Euclidean (L2) distance
|
||||
#[inline]
|
||||
pub fn euclidean_distance(a: &[f32], b: &[f32]) -> f32 {
|
||||
debug_assert_eq!(a.len(), b.len(), "Vector dimensions must match");
|
||||
|
||||
if let Some(fns) = DISTANCE_FNS.get() {
|
||||
(fns.euclidean)(a, b)
|
||||
} else {
|
||||
scalar::euclidean_distance(a, b)
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculate Cosine distance
|
||||
#[inline]
|
||||
pub fn cosine_distance(a: &[f32], b: &[f32]) -> f32 {
|
||||
debug_assert_eq!(a.len(), b.len(), "Vector dimensions must match");
|
||||
|
||||
if let Some(fns) = DISTANCE_FNS.get() {
|
||||
(fns.cosine)(a, b)
|
||||
} else {
|
||||
scalar::cosine_distance(a, b)
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculate negative Inner Product distance
|
||||
#[inline]
|
||||
pub fn inner_product_distance(a: &[f32], b: &[f32]) -> f32 {
|
||||
debug_assert_eq!(a.len(), b.len(), "Vector dimensions must match");
|
||||
|
||||
if let Some(fns) = DISTANCE_FNS.get() {
|
||||
(fns.inner_product)(a, b)
|
||||
} else {
|
||||
scalar::inner_product_distance(a, b)
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculate Manhattan (L1) distance
|
||||
#[inline]
|
||||
pub fn manhattan_distance(a: &[f32], b: &[f32]) -> f32 {
|
||||
debug_assert_eq!(a.len(), b.len(), "Vector dimensions must match");
|
||||
|
||||
if let Some(fns) = DISTANCE_FNS.get() {
|
||||
(fns.manhattan)(a, b)
|
||||
} else {
|
||||
scalar::manhattan_distance(a, b)
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculate distance using specified metric
|
||||
#[inline]
|
||||
pub fn distance(a: &[f32], b: &[f32], metric: DistanceMetric) -> f32 {
|
||||
match metric {
|
||||
DistanceMetric::Euclidean => euclidean_distance(a, b),
|
||||
DistanceMetric::Cosine => cosine_distance(a, b),
|
||||
DistanceMetric::InnerProduct => inner_product_distance(a, b),
|
||||
DistanceMetric::Manhattan => manhattan_distance(a, b),
|
||||
DistanceMetric::Hamming => {
|
||||
// For f32 vectors, treat as binary (sign bit)
|
||||
scalar::hamming_distance_f32(a, b)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Fast cosine distance for pre-normalized vectors
|
||||
/// Only computes dot product (avoids norm calculation)
|
||||
#[inline]
|
||||
pub fn cosine_distance_normalized(a: &[f32], b: &[f32]) -> f32 {
|
||||
debug_assert_eq!(a.len(), b.len(), "Vector dimensions must match");
|
||||
simd::cosine_distance_normalized(a, b)
|
||||
}
|
||||
|
||||
/// Batch distance calculation with parallelism
|
||||
pub fn batch_distances(query: &[f32], vectors: &[&[f32]], metric: DistanceMetric) -> Vec<f32> {
|
||||
use rayon::prelude::*;
|
||||
|
||||
vectors
|
||||
.par_iter()
|
||||
.map(|v| distance(query, v, metric))
|
||||
.collect()
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Tests
|
||||
// ============================================================================
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn init_for_tests() {
|
||||
let _ = SIMD_CAPABILITY.get_or_init(detect_simd_capability);
|
||||
let cap = *SIMD_CAPABILITY.get().unwrap();
|
||||
let _ = DISTANCE_FNS.get_or_init(|| create_distance_functions(cap));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_euclidean() {
|
||||
init_for_tests();
|
||||
let a = vec![0.0, 0.0, 0.0];
|
||||
let b = vec![3.0, 4.0, 0.0];
|
||||
let dist = euclidean_distance(&a, &b);
|
||||
assert!((dist - 5.0).abs() < 1e-5);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cosine() {
|
||||
init_for_tests();
|
||||
let a = vec![1.0, 0.0, 0.0];
|
||||
let b = vec![1.0, 0.0, 0.0];
|
||||
let dist = cosine_distance(&a, &b);
|
||||
assert!(dist.abs() < 1e-5); // Same direction = 0 distance
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_inner_product() {
|
||||
init_for_tests();
|
||||
let a = vec![1.0, 2.0, 3.0];
|
||||
let b = vec![4.0, 5.0, 6.0];
|
||||
let dist = inner_product_distance(&a, &b);
|
||||
assert!((dist - (-32.0)).abs() < 1e-5); // -(1*4 + 2*5 + 3*6) = -32
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_manhattan() {
|
||||
init_for_tests();
|
||||
let a = vec![1.0, 2.0, 3.0];
|
||||
let b = vec![4.0, 6.0, 8.0];
|
||||
let dist = manhattan_distance(&a, &b);
|
||||
assert!((dist - 12.0).abs() < 1e-5); // |3| + |4| + |5| = 12
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_simd_matches_scalar() {
|
||||
init_for_tests();
|
||||
|
||||
let a: Vec<f32> = (0..128).map(|i| i as f32 * 0.01).collect();
|
||||
let b: Vec<f32> = (0..128).map(|i| (128 - i) as f32 * 0.01).collect();
|
||||
|
||||
let scalar_euclidean = scalar::euclidean_distance(&a, &b);
|
||||
let simd_euclidean = euclidean_distance(&a, &b);
|
||||
assert!((scalar_euclidean - simd_euclidean).abs() < 1e-4);
|
||||
|
||||
let scalar_cosine = scalar::cosine_distance(&a, &b);
|
||||
let simd_cosine = cosine_distance(&a, &b);
|
||||
assert!((scalar_cosine - simd_cosine).abs() < 1e-4);
|
||||
}
|
||||
}
|
||||
306
vendor/ruvector/crates/ruvector-postgres/src/distance/scalar.rs
vendored
Normal file
306
vendor/ruvector/crates/ruvector-postgres/src/distance/scalar.rs
vendored
Normal file
@@ -0,0 +1,306 @@
|
||||
//! Scalar (non-SIMD) distance implementations
|
||||
//!
|
||||
//! These are fallback implementations that work on all platforms.
|
||||
|
||||
/// Euclidean (L2) distance - scalar implementation
|
||||
#[inline]
|
||||
pub fn euclidean_distance(a: &[f32], b: &[f32]) -> f32 {
|
||||
debug_assert_eq!(a.len(), b.len());
|
||||
|
||||
let sum: f32 = a
|
||||
.iter()
|
||||
.zip(b.iter())
|
||||
.map(|(x, y)| {
|
||||
let diff = x - y;
|
||||
diff * diff
|
||||
})
|
||||
.sum();
|
||||
|
||||
sum.sqrt()
|
||||
}
|
||||
|
||||
/// Squared Euclidean distance (avoids sqrt for comparisons)
|
||||
#[inline]
|
||||
pub fn euclidean_distance_squared(a: &[f32], b: &[f32]) -> f32 {
|
||||
debug_assert_eq!(a.len(), b.len());
|
||||
|
||||
a.iter()
|
||||
.zip(b.iter())
|
||||
.map(|(x, y)| {
|
||||
let diff = x - y;
|
||||
diff * diff
|
||||
})
|
||||
.sum()
|
||||
}
|
||||
|
||||
/// Cosine distance - scalar implementation
|
||||
#[inline]
|
||||
pub fn cosine_distance(a: &[f32], b: &[f32]) -> f32 {
|
||||
debug_assert_eq!(a.len(), b.len());
|
||||
|
||||
let mut dot = 0.0f32;
|
||||
let mut norm_a = 0.0f32;
|
||||
let mut norm_b = 0.0f32;
|
||||
|
||||
for (x, y) in a.iter().zip(b.iter()) {
|
||||
dot += x * y;
|
||||
norm_a += x * x;
|
||||
norm_b += y * y;
|
||||
}
|
||||
|
||||
let denominator = (norm_a * norm_b).sqrt();
|
||||
|
||||
if denominator == 0.0 {
|
||||
return 1.0; // Max distance if either vector is zero
|
||||
}
|
||||
|
||||
1.0 - (dot / denominator)
|
||||
}
|
||||
|
||||
/// Cosine similarity (1 - distance)
|
||||
#[inline]
|
||||
pub fn cosine_similarity(a: &[f32], b: &[f32]) -> f32 {
|
||||
1.0 - cosine_distance(a, b)
|
||||
}
|
||||
|
||||
/// Inner product (dot product) distance - scalar implementation
|
||||
/// Returns negative for use with ORDER BY ASC
|
||||
#[inline]
|
||||
pub fn inner_product_distance(a: &[f32], b: &[f32]) -> f32 {
|
||||
debug_assert_eq!(a.len(), b.len());
|
||||
|
||||
let dot: f32 = a.iter().zip(b.iter()).map(|(x, y)| x * y).sum();
|
||||
|
||||
-dot
|
||||
}
|
||||
|
||||
/// Dot product (positive value)
|
||||
#[inline]
|
||||
pub fn dot_product(a: &[f32], b: &[f32]) -> f32 {
|
||||
debug_assert_eq!(a.len(), b.len());
|
||||
|
||||
a.iter().zip(b.iter()).map(|(x, y)| x * y).sum()
|
||||
}
|
||||
|
||||
/// Manhattan (L1) distance - scalar implementation
|
||||
#[inline]
|
||||
pub fn manhattan_distance(a: &[f32], b: &[f32]) -> f32 {
|
||||
debug_assert_eq!(a.len(), b.len());
|
||||
|
||||
a.iter().zip(b.iter()).map(|(x, y)| (x - y).abs()).sum()
|
||||
}
|
||||
|
||||
/// Hamming distance for f32 vectors (based on sign bit)
|
||||
#[inline]
|
||||
pub fn hamming_distance_f32(a: &[f32], b: &[f32]) -> f32 {
|
||||
debug_assert_eq!(a.len(), b.len());
|
||||
|
||||
let count: u32 = a
|
||||
.iter()
|
||||
.zip(b.iter())
|
||||
.map(|(x, y)| {
|
||||
let sign_a = x.to_bits() >> 31;
|
||||
let sign_b = y.to_bits() >> 31;
|
||||
(sign_a ^ sign_b) as u32
|
||||
})
|
||||
.sum();
|
||||
|
||||
count as f32
|
||||
}
|
||||
|
||||
/// Hamming distance for binary vectors (u64)
|
||||
#[inline]
|
||||
pub fn hamming_distance_binary(a: &[u64], b: &[u64]) -> u32 {
|
||||
debug_assert_eq!(a.len(), b.len());
|
||||
|
||||
a.iter()
|
||||
.zip(b.iter())
|
||||
.map(|(x, y)| (x ^ y).count_ones())
|
||||
.sum()
|
||||
}
|
||||
|
||||
/// Jaccard distance for sparse binary vectors
|
||||
#[inline]
|
||||
pub fn jaccard_distance(a: &[u64], b: &[u64]) -> f32 {
|
||||
debug_assert_eq!(a.len(), b.len());
|
||||
|
||||
let mut intersection = 0u32;
|
||||
let mut union = 0u32;
|
||||
|
||||
for (x, y) in a.iter().zip(b.iter()) {
|
||||
intersection += (x & y).count_ones();
|
||||
union += (x | y).count_ones();
|
||||
}
|
||||
|
||||
if union == 0 {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
1.0 - (intersection as f32 / union as f32)
|
||||
}
|
||||
|
||||
/// Chebyshev (L∞) distance
|
||||
#[inline]
|
||||
pub fn chebyshev_distance(a: &[f32], b: &[f32]) -> f32 {
|
||||
debug_assert_eq!(a.len(), b.len());
|
||||
|
||||
a.iter()
|
||||
.zip(b.iter())
|
||||
.map(|(x, y)| (x - y).abs())
|
||||
.fold(0.0f32, f32::max)
|
||||
}
|
||||
|
||||
/// Minkowski distance with parameter p
|
||||
#[inline]
|
||||
pub fn minkowski_distance(a: &[f32], b: &[f32], p: f32) -> f32 {
|
||||
debug_assert_eq!(a.len(), b.len());
|
||||
|
||||
if p == 1.0 {
|
||||
return manhattan_distance(a, b);
|
||||
}
|
||||
if p == 2.0 {
|
||||
return euclidean_distance(a, b);
|
||||
}
|
||||
if p == f32::INFINITY {
|
||||
return chebyshev_distance(a, b);
|
||||
}
|
||||
|
||||
let sum: f32 = a
|
||||
.iter()
|
||||
.zip(b.iter())
|
||||
.map(|(x, y)| (x - y).abs().powf(p))
|
||||
.sum();
|
||||
|
||||
sum.powf(1.0 / p)
|
||||
}
|
||||
|
||||
/// Canberra distance
|
||||
#[inline]
|
||||
pub fn canberra_distance(a: &[f32], b: &[f32]) -> f32 {
|
||||
debug_assert_eq!(a.len(), b.len());
|
||||
|
||||
a.iter()
|
||||
.zip(b.iter())
|
||||
.map(|(x, y)| {
|
||||
let num = (x - y).abs();
|
||||
let denom = x.abs() + y.abs();
|
||||
if denom == 0.0 {
|
||||
0.0
|
||||
} else {
|
||||
num / denom
|
||||
}
|
||||
})
|
||||
.sum()
|
||||
}
|
||||
|
||||
/// Bray-Curtis distance
|
||||
#[inline]
|
||||
pub fn bray_curtis_distance(a: &[f32], b: &[f32]) -> f32 {
|
||||
debug_assert_eq!(a.len(), b.len());
|
||||
|
||||
let mut sum_diff = 0.0f32;
|
||||
let mut sum_total = 0.0f32;
|
||||
|
||||
for (x, y) in a.iter().zip(b.iter()) {
|
||||
sum_diff += (x - y).abs();
|
||||
sum_total += x.abs() + y.abs();
|
||||
}
|
||||
|
||||
if sum_total == 0.0 {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
sum_diff / sum_total
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Tests
|
||||
// ============================================================================
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_euclidean() {
|
||||
let a = vec![0.0, 0.0];
|
||||
let b = vec![3.0, 4.0];
|
||||
assert!((euclidean_distance(&a, &b) - 5.0).abs() < 1e-6);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_euclidean_squared() {
|
||||
let a = vec![0.0, 0.0];
|
||||
let b = vec![3.0, 4.0];
|
||||
assert!((euclidean_distance_squared(&a, &b) - 25.0).abs() < 1e-6);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cosine_same_direction() {
|
||||
let a = vec![1.0, 0.0, 0.0];
|
||||
let b = vec![2.0, 0.0, 0.0];
|
||||
assert!(cosine_distance(&a, &b).abs() < 1e-6);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cosine_opposite() {
|
||||
let a = vec![1.0, 0.0, 0.0];
|
||||
let b = vec![-1.0, 0.0, 0.0];
|
||||
assert!((cosine_distance(&a, &b) - 2.0).abs() < 1e-6);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cosine_orthogonal() {
|
||||
let a = vec![1.0, 0.0, 0.0];
|
||||
let b = vec![0.0, 1.0, 0.0];
|
||||
assert!((cosine_distance(&a, &b) - 1.0).abs() < 1e-6);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_inner_product() {
|
||||
let a = vec![1.0, 2.0, 3.0];
|
||||
let b = vec![4.0, 5.0, 6.0];
|
||||
// dot = 4 + 10 + 18 = 32
|
||||
assert!((inner_product_distance(&a, &b) - (-32.0)).abs() < 1e-6);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_manhattan() {
|
||||
let a = vec![1.0, 2.0, 3.0];
|
||||
let b = vec![4.0, 6.0, 8.0];
|
||||
// |3| + |4| + |5| = 12
|
||||
assert!((manhattan_distance(&a, &b) - 12.0).abs() < 1e-6);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hamming_binary() {
|
||||
let a = vec![0b1010_1010u64];
|
||||
let b = vec![0b1111_0000u64];
|
||||
let dist = hamming_distance_binary(&a, &b);
|
||||
assert_eq!(dist, 4);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_chebyshev() {
|
||||
let a = vec![1.0, 2.0, 3.0];
|
||||
let b = vec![4.0, 10.0, 5.0];
|
||||
// max(|3|, |8|, |2|) = 8
|
||||
assert!((chebyshev_distance(&a, &b) - 8.0).abs() < 1e-6);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_minkowski_p1() {
|
||||
let a = vec![1.0, 2.0];
|
||||
let b = vec![4.0, 6.0];
|
||||
// Same as manhattan
|
||||
assert!((minkowski_distance(&a, &b, 1.0) - manhattan_distance(&a, &b)).abs() < 1e-6);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_minkowski_p2() {
|
||||
let a = vec![0.0, 0.0];
|
||||
let b = vec![3.0, 4.0];
|
||||
// Same as euclidean
|
||||
assert!((minkowski_distance(&a, &b, 2.0) - euclidean_distance(&a, &b)).abs() < 1e-6);
|
||||
}
|
||||
}
|
||||
2128
vendor/ruvector/crates/ruvector-postgres/src/distance/simd.rs
vendored
Normal file
2128
vendor/ruvector/crates/ruvector-postgres/src/distance/simd.rs
vendored
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user