ruvector/crates/ruvector-postgres/tests/simd_consistency_tests.rs
rUv 4b1fd0e286 fix(ci): Fix PostgreSQL Extension CI failures
- Remove invalid feature flags (hybrid-search, filtered-search) that don't exist
- Replace with valid all-features flag for comprehensive testing
- Add PostgreSQL apt repository for older versions on Ubuntu 24.04
- Apply cargo fmt formatting to all crates

This fixes CI failures caused by:
- Feature flags that were planned but not implemented
- PostgreSQL 14 packages not available on Ubuntu 24.04 default repos

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-03 23:43:01 +00:00

334 lines
11 KiB
Rust

//! SIMD consistency tests - verify SIMD and scalar implementations match
//!
//! These tests ensure that optimized SIMD code paths produce the same results
//! as the scalar fallback implementations.
use ruvector_postgres::distance::{scalar, simd};
#[cfg(test)]
mod simd_consistency {
use super::*;
const EPSILON: f32 = 1e-5;
// ========================================================================
// Euclidean Distance Consistency
// ========================================================================
#[test]
fn test_euclidean_scalar_vs_simd_small() {
let a = vec![1.0, 2.0, 3.0, 4.0, 5.0];
let b = vec![5.0, 4.0, 3.0, 2.0, 1.0];
let scalar_result = scalar::euclidean_distance(&a, &b);
#[cfg(target_arch = "x86_64")]
{
if is_x86_feature_detected!("avx2") {
let simd_result = simd::euclidean_distance_avx2_wrapper(&a, &b);
assert!(
(scalar_result - simd_result).abs() < EPSILON,
"AVX2: scalar={}, simd={}",
scalar_result,
simd_result
);
}
if is_x86_feature_detected!("avx512f") {
let simd_result = simd::euclidean_distance_avx512_wrapper(&a, &b);
assert!(
(scalar_result - simd_result).abs() < EPSILON,
"AVX512: scalar={}, simd={}",
scalar_result,
simd_result
);
}
}
#[cfg(target_arch = "aarch64")]
{
let simd_result = simd::euclidean_distance_neon_wrapper(&a, &b);
assert!((scalar_result - simd_result).abs() < EPSILON);
}
}
#[test]
fn test_euclidean_scalar_vs_simd_various_sizes() {
// Test different sizes to exercise SIMD remainder handling
for size in [1, 3, 7, 8, 15, 16, 31, 32, 63, 64, 127, 128, 255, 256] {
let a: Vec<f32> = (0..size).map(|i| i as f32 * 0.1).collect();
let b: Vec<f32> = (0..size).map(|i| (size - i) as f32 * 0.1).collect();
let scalar_result = scalar::euclidean_distance(&a, &b);
#[cfg(target_arch = "x86_64")]
{
if is_x86_feature_detected!("avx2") {
let simd_result = simd::euclidean_distance_avx2_wrapper(&a, &b);
assert!(
(scalar_result - simd_result).abs() < EPSILON,
"Size {}: AVX2 mismatch",
size
);
}
}
#[cfg(target_arch = "aarch64")]
{
let simd_result = simd::euclidean_distance_neon_wrapper(&a, &b);
assert!(
(scalar_result - simd_result).abs() < EPSILON,
"Size {}: NEON mismatch",
size
);
}
}
}
#[test]
fn test_euclidean_scalar_vs_simd_negative() {
let a = vec![-1.0, -2.0, -3.0, -4.0, -5.0, -6.0, -7.0, -8.0];
let b = vec![8.0, 7.0, 6.0, 5.0, 4.0, 3.0, 2.0, 1.0];
let scalar_result = scalar::euclidean_distance(&a, &b);
#[cfg(target_arch = "x86_64")]
{
if is_x86_feature_detected!("avx2") {
let simd_result = simd::euclidean_distance_avx2_wrapper(&a, &b);
assert!((scalar_result - simd_result).abs() < EPSILON);
}
}
}
// ========================================================================
// Cosine Distance Consistency
// ========================================================================
#[test]
fn test_cosine_scalar_vs_simd_small() {
let a = vec![1.0, 2.0, 3.0, 4.0];
let b = vec![4.0, 3.0, 2.0, 1.0];
let scalar_result = scalar::cosine_distance(&a, &b);
#[cfg(target_arch = "x86_64")]
{
if is_x86_feature_detected!("avx2") {
let simd_result = simd::cosine_distance_avx2_wrapper(&a, &b);
assert!((scalar_result - simd_result).abs() < EPSILON);
}
}
#[cfg(target_arch = "aarch64")]
{
let simd_result = simd::cosine_distance_neon_wrapper(&a, &b);
assert!((scalar_result - simd_result).abs() < EPSILON);
}
}
#[test]
fn test_cosine_scalar_vs_simd_various_sizes() {
for size in [8, 16, 32, 64, 128, 256] {
let a: Vec<f32> = (0..size).map(|i| (i % 10) as f32).collect();
let b: Vec<f32> = (0..size).map(|i| ((i + 5) % 10) as f32).collect();
// Skip if zero vectors
if a.iter().all(|&x| x == 0.0) || b.iter().all(|&x| x == 0.0) {
continue;
}
let scalar_result = scalar::cosine_distance(&a, &b);
#[cfg(target_arch = "x86_64")]
{
if is_x86_feature_detected!("avx2") {
let simd_result = simd::cosine_distance_avx2_wrapper(&a, &b);
assert!(
(scalar_result - simd_result).abs() < 1e-4,
"Size {}: scalar={}, simd={}",
size,
scalar_result,
simd_result
);
}
}
}
}
#[test]
fn test_cosine_scalar_vs_simd_normalized() {
// Test with pre-normalized vectors
let a = vec![0.6, 0.8, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0];
let b = vec![0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0];
let scalar_result = scalar::cosine_distance(&a, &b);
#[cfg(target_arch = "x86_64")]
{
if is_x86_feature_detected!("avx2") {
let simd_result = simd::cosine_distance_avx2_wrapper(&a, &b);
assert!((scalar_result - simd_result).abs() < EPSILON);
}
}
}
// ========================================================================
// Inner Product Consistency
// ========================================================================
#[test]
fn test_inner_product_scalar_vs_simd_small() {
let a = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0];
let b = vec![8.0, 7.0, 6.0, 5.0, 4.0, 3.0, 2.0, 1.0];
let scalar_result = scalar::inner_product_distance(&a, &b);
#[cfg(target_arch = "x86_64")]
{
if is_x86_feature_detected!("avx2") {
let simd_result = simd::inner_product_avx2_wrapper(&a, &b);
assert!((scalar_result - simd_result).abs() < EPSILON);
}
}
#[cfg(target_arch = "aarch64")]
{
let simd_result = simd::inner_product_neon_wrapper(&a, &b);
assert!((scalar_result - simd_result).abs() < EPSILON);
}
}
#[test]
fn test_inner_product_scalar_vs_simd_various_sizes() {
for size in [4, 8, 16, 32, 64, 128] {
let a: Vec<f32> = (0..size).map(|i| i as f32 * 0.1).collect();
let b: Vec<f32> = (0..size).map(|i| (size - i) as f32 * 0.1).collect();
let scalar_result = scalar::inner_product_distance(&a, &b);
#[cfg(target_arch = "x86_64")]
{
if is_x86_feature_detected!("avx2") {
let simd_result = simd::inner_product_avx2_wrapper(&a, &b);
assert!(
(scalar_result - simd_result).abs() < 1e-4,
"Size {}: mismatch",
size
);
}
}
}
}
// ========================================================================
// Manhattan Distance Consistency
// ========================================================================
#[test]
fn test_manhattan_scalar_vs_simd_small() {
let a = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0];
let b = vec![8.0, 7.0, 6.0, 5.0, 4.0, 3.0, 2.0, 1.0];
let scalar_result = scalar::manhattan_distance(&a, &b);
#[cfg(target_arch = "x86_64")]
{
if is_x86_feature_detected!("avx2") {
let simd_result = simd::manhattan_distance_avx2_wrapper(&a, &b);
assert!((scalar_result - simd_result).abs() < EPSILON);
}
}
}
// ========================================================================
// Edge Cases
// ========================================================================
#[test]
fn test_zero_vectors() {
let a = vec![0.0; 32];
let b = vec![0.0; 32];
let scalar_euclidean = scalar::euclidean_distance(&a, &b);
#[cfg(target_arch = "x86_64")]
{
if is_x86_feature_detected!("avx2") {
let simd_euclidean = simd::euclidean_distance_avx2_wrapper(&a, &b);
assert!((scalar_euclidean - simd_euclidean).abs() < EPSILON);
}
}
}
#[test]
fn test_small_values() {
let a: Vec<f32> = (0..64).map(|_| 1e-6).collect();
let b: Vec<f32> = (0..64).map(|_| 1e-6).collect();
let scalar_result = scalar::euclidean_distance(&a, &b);
#[cfg(target_arch = "x86_64")]
{
if is_x86_feature_detected!("avx2") {
let simd_result = simd::euclidean_distance_avx2_wrapper(&a, &b);
assert!((scalar_result - simd_result).abs() < 1e-5);
}
}
}
#[test]
fn test_large_values() {
let a: Vec<f32> = (0..64).map(|_| 1e6).collect();
let b: Vec<f32> = (0..64).map(|_| 9e5).collect();
let scalar_result = scalar::euclidean_distance(&a, &b);
#[cfg(target_arch = "x86_64")]
{
if is_x86_feature_detected!("avx2") {
let simd_result = simd::euclidean_distance_avx2_wrapper(&a, &b);
// Allow larger epsilon for large values
assert!((scalar_result - simd_result).abs() < 1.0);
}
}
}
// ========================================================================
// Random Data Tests
// ========================================================================
#[test]
fn test_random_data_consistency() {
use rand::Rng;
let mut rng = rand::thread_rng();
for _ in 0..100 {
let size = rng.gen_range(8..256);
let a: Vec<f32> = (0..size).map(|_| rng.gen_range(-100.0..100.0)).collect();
let b: Vec<f32> = (0..size).map(|_| rng.gen_range(-100.0..100.0)).collect();
let scalar_euclidean = scalar::euclidean_distance(&a, &b);
let scalar_manhattan = scalar::manhattan_distance(&a, &b);
#[cfg(target_arch = "x86_64")]
{
if is_x86_feature_detected!("avx2") {
let simd_euclidean = simd::euclidean_distance_avx2_wrapper(&a, &b);
let simd_manhattan = simd::manhattan_distance_avx2_wrapper(&a, &b);
assert!(
(scalar_euclidean - simd_euclidean).abs() < 1e-3,
"Euclidean mismatch at size {}",
size
);
assert!(
(scalar_manhattan - simd_manhattan).abs() < 1e-3,
"Manhattan mismatch at size {}",
size
);
}
}
}
}
}