use crate::quantization::*;
#[test]
fn test_dot_product_quantized_simd_simple() {
let query = vec![1.0, 0.0, 0.0];
let vector = vec![1.0, 0.0, 0.0];
let quantized = QuantizedVector::from_f32(&vector);
let result = dot_product_quantized_simd(&query, &quantized);
assert!(
(result - 1.0).abs() < 0.1,
"Result {result} not close to 1.0"
);
}
#[test]
#[allow(clippy::cast_precision_loss)]
fn test_dot_product_quantized_simd_768d() {
let dimension = 768;
let query: Vec<f32> = (0..dimension).map(|i| (i as f32) / 1000.0).collect();
let vector: Vec<f32> = (0..dimension).map(|i| (i as f32) / 1000.0).collect();
let quantized = QuantizedVector::from_f32(&vector);
let scalar = dot_product_quantized(&query, &quantized);
let simd = dot_product_quantized_simd(&query, &quantized);
let rel_error = ((scalar - simd) / scalar).abs();
assert!(rel_error < 0.01, "Relative error {rel_error} too high");
}
#[test]
#[allow(clippy::cast_precision_loss)]
fn test_euclidean_squared_quantized_simd_768d() {
let dimension = 768;
let query: Vec<f32> = (0..dimension).map(|i| (i as f32) / 1000.0).collect();
let vector: Vec<f32> = (0..dimension).map(|i| ((i + 10) as f32) / 1000.0).collect();
let quantized = QuantizedVector::from_f32(&vector);
let scalar = euclidean_squared_quantized(&query, &quantized);
let simd = euclidean_squared_quantized_simd(&query, &quantized);
let rel_error = ((scalar - simd) / scalar).abs();
assert!(rel_error < 0.01, "Relative error {rel_error} too high");
}
#[test]
fn test_cosine_similarity_quantized_simd_identical() {
let vector = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0];
let quantized = QuantizedVector::from_f32(&vector);
let similarity = cosine_similarity_quantized_simd(&vector, &quantized);
assert!(
(similarity - 1.0).abs() < 0.05,
"Similarity {similarity} not close to 1.0"
);
}
#[test]
fn test_quantize_simple_vector() {
let vector = vec![0.0, 0.5, 1.0];
let quantized = QuantizedVector::from_f32(&vector);
assert_eq!(quantized.dimension(), 3);
assert!((quantized.min - 0.0).abs() < f32::EPSILON);
assert!((quantized.max - 1.0).abs() < f32::EPSILON);
assert_eq!(quantized.data[0], 0); assert_eq!(quantized.data[1], 128); assert_eq!(quantized.data[2], 255); }
#[test]
fn test_quantize_negative_values() {
let vector = vec![-1.0, 0.0, 1.0];
let quantized = QuantizedVector::from_f32(&vector);
assert!((quantized.min - (-1.0)).abs() < f32::EPSILON);
assert!((quantized.max - 1.0).abs() < f32::EPSILON);
assert_eq!(quantized.data[0], 0); assert_eq!(quantized.data[1], 128); assert_eq!(quantized.data[2], 255); }
#[test]
fn test_quantize_constant_vector() {
let vector = vec![0.5, 0.5, 0.5];
let quantized = QuantizedVector::from_f32(&vector);
assert_eq!(quantized.dimension(), 3);
for &v in &quantized.data {
assert_eq!(v, 128);
}
}
#[test]
fn test_dequantize_roundtrip() {
let original = vec![0.1, 0.5, 0.9, -0.3, 0.0];
let quantized = QuantizedVector::from_f32(&original);
let reconstructed = quantized.to_f32();
assert_eq!(reconstructed.len(), original.len());
for (orig, recon) in original.iter().zip(reconstructed.iter()) {
let error = (orig - recon).abs();
let max_error = (quantized.max - quantized.min) / 255.0;
assert!(
error <= max_error + f32::EPSILON,
"Error {error} exceeds max {max_error}"
);
}
}
#[test]
#[allow(clippy::cast_precision_loss)]
fn test_memory_reduction() {
let dimension = 768;
let vector: Vec<f32> = (0..dimension)
.map(|i| i as f32 / dimension as f32)
.collect();
let quantized = QuantizedVector::from_f32(&vector);
let f32_size = dimension * 4; let sq8_size = quantized.memory_size();
assert_eq!(f32_size, 3072);
assert_eq!(sq8_size, 776);
#[allow(clippy::cast_precision_loss)]
let ratio = f32_size as f32 / sq8_size as f32;
assert!(ratio > 3.9);
}
#[test]
fn test_serialization_roundtrip() {
let vector = vec![0.1, 0.5, 0.9, -0.3];
let quantized = QuantizedVector::from_f32(&vector);
let bytes = quantized.to_bytes();
let deserialized = QuantizedVector::from_bytes(&bytes).unwrap();
assert!((deserialized.min - quantized.min).abs() < f32::EPSILON);
assert!((deserialized.max - quantized.max).abs() < f32::EPSILON);
assert_eq!(deserialized.data, quantized.data);
}
#[test]
fn test_from_bytes_invalid() {
let bytes = vec![0u8; 5];
let result = QuantizedVector::from_bytes(&bytes);
assert!(result.is_err());
}
#[test]
fn test_dot_product_quantized_simple() {
let query = vec![1.0, 0.0, 0.0];
let vector = vec![1.0, 0.0, 0.0];
let quantized = QuantizedVector::from_f32(&vector);
let dot = dot_product_quantized(&query, &quantized);
assert!(
(dot - 1.0).abs() < 0.1,
"Dot product {dot} not close to 1.0"
);
}
#[test]
fn test_dot_product_quantized_orthogonal() {
let query = vec![1.0, 0.0, 0.0];
let vector = vec![0.0, 1.0, 0.0];
let quantized = QuantizedVector::from_f32(&vector);
let dot = dot_product_quantized(&query, &quantized);
assert!(dot.abs() < 0.1, "Dot product {dot} not close to 0");
}
#[test]
fn test_euclidean_squared_quantized() {
let query = vec![0.0, 0.0, 0.0];
let vector = vec![1.0, 0.0, 0.0];
let quantized = QuantizedVector::from_f32(&vector);
let dist = euclidean_squared_quantized(&query, &quantized);
assert!(
(dist - 1.0).abs() < 0.1,
"Euclidean squared {dist} not close to 1.0"
);
}
#[test]
fn test_euclidean_squared_quantized_same_point() {
let vector = vec![0.5, 0.5, 0.5];
let quantized = QuantizedVector::from_f32(&vector);
let dist = euclidean_squared_quantized(&vector, &quantized);
assert!(dist < 0.01, "Distance to self {dist} should be ~0");
}
#[test]
fn test_cosine_similarity_quantized_identical() {
let vector = vec![1.0, 2.0, 3.0];
let quantized = QuantizedVector::from_f32(&vector);
let similarity = cosine_similarity_quantized(&vector, &quantized);
assert!(
(similarity - 1.0).abs() < 0.05,
"Cosine similarity to self {similarity} not close to 1.0"
);
}
#[test]
fn test_cosine_similarity_quantized_opposite() {
let query = vec![1.0, 0.0, 0.0];
let vector = vec![-1.0, 0.0, 0.0];
let quantized = QuantizedVector::from_f32(&vector);
let similarity = cosine_similarity_quantized(&query, &quantized);
assert!(
(similarity - (-1.0)).abs() < 0.1,
"Cosine similarity {similarity} not close to -1.0"
);
}
#[test]
#[allow(clippy::cast_precision_loss)]
fn test_recall_accuracy_high_dimension() {
let dimension = 768;
let num_vectors = 100;
let vectors: Vec<Vec<f32>> = (0..num_vectors)
.map(|i| {
(0..dimension)
.map(|j| {
let x = ((i * 7 + j * 13) % 1000) as f32 / 1000.0;
x * 2.0 - 1.0 })
.collect()
})
.collect();
let quantized: Vec<QuantizedVector> = vectors
.iter()
.map(|v| QuantizedVector::from_f32(v))
.collect();
let query = &vectors[0];
let mut f32_distances: Vec<(usize, f32)> = vectors
.iter()
.enumerate()
.map(|(i, v)| {
let dot: f32 = query.iter().zip(v.iter()).map(|(a, b)| a * b).sum();
(i, dot)
})
.collect();
let mut sq8_distances: Vec<(usize, f32)> = quantized
.iter()
.enumerate()
.map(|(i, q)| (i, dot_product_quantized(query, q)))
.collect();
f32_distances.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap());
sq8_distances.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap());
let k = 10;
let f32_top_k: std::collections::HashSet<usize> =
f32_distances.iter().take(k).map(|(i, _)| *i).collect();
let sq8_top_k: std::collections::HashSet<usize> =
sq8_distances.iter().take(k).map(|(i, _)| *i).collect();
let recall = f32_top_k.intersection(&sq8_top_k).count() as f32 / k as f32;
assert!(
recall >= 0.8,
"Recall@{k} is {recall}, expected >= 0.8 (80%)"
);
}
#[test]
fn test_storage_mode_enum() {
let full = StorageMode::Full;
let sq8 = StorageMode::SQ8;
let binary = StorageMode::Binary;
let pq = StorageMode::ProductQuantization;
let default = StorageMode::default();
assert_eq!(full, StorageMode::Full);
assert_eq!(sq8, StorageMode::SQ8);
assert_eq!(binary, StorageMode::Binary);
assert_eq!(pq, StorageMode::ProductQuantization);
assert_eq!(default, StorageMode::Full);
assert_ne!(full, sq8);
assert_ne!(sq8, binary);
assert_ne!(binary, pq);
}
#[test]
fn test_binary_quantize_simple_vector() {
let vector = vec![-1.0, 0.5, -0.5, 1.0];
let binary = BinaryQuantizedVector::from_f32(&vector);
assert_eq!(binary.dimension(), 4);
assert_eq!(binary.data.len(), 1); }
#[test]
fn test_binary_quantize_768d_memory() {
let vector: Vec<f32> = (0..768)
.map(|i| if i % 2 == 0 { 0.5 } else { -0.5 })
.collect();
let binary = BinaryQuantizedVector::from_f32(&vector);
assert_eq!(binary.dimension(), 768);
assert_eq!(binary.data.len(), 96);
let f32_size = 768 * 4;
let binary_size = binary.memory_size();
assert_eq!(binary_size, 96);
#[allow(clippy::cast_precision_loss)]
let ratio = f32_size as f32 / binary_size as f32;
assert!(ratio >= 32.0, "Expected 32x reduction, got {ratio}x");
}
#[test]
fn test_binary_quantize_threshold_at_zero() {
let vector = vec![0.0, 0.001, -0.001, f32::EPSILON];
let binary = BinaryQuantizedVector::from_f32(&vector);
let bits = binary.get_bits();
assert!(bits[0], "0.0 should be 1");
assert!(bits[1], "0.001 should be 1");
assert!(!bits[2], "-0.001 should be 0");
assert!(bits[3], "EPSILON should be 1");
}
#[test]
fn test_binary_hamming_distance_identical() {
let vector = vec![0.5, -0.5, 0.5, -0.5, 0.5, -0.5, 0.5, -0.5];
let binary = BinaryQuantizedVector::from_f32(&vector);
let distance = binary.hamming_distance(&binary);
assert_eq!(distance, 0);
}
#[test]
fn test_binary_hamming_distance_opposite() {
let v1 = vec![1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0];
let v2 = vec![-1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0];
let b1 = BinaryQuantizedVector::from_f32(&v1);
let b2 = BinaryQuantizedVector::from_f32(&v2);
let distance = b1.hamming_distance(&b2);
assert_eq!(distance, 8);
}
#[test]
fn test_binary_hamming_distance_half_different() {
let v1 = vec![1.0, 1.0, 1.0, 1.0, -1.0, -1.0, -1.0, -1.0];
let v2 = vec![1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, -1.0];
let b1 = BinaryQuantizedVector::from_f32(&v1);
let b2 = BinaryQuantizedVector::from_f32(&v2);
let distance = b1.hamming_distance(&b2);
assert_eq!(distance, 4);
}
#[test]
fn test_binary_serialization_roundtrip() {
let vector: Vec<f32> = (0..768)
.map(|i| if i % 3 == 0 { 0.5 } else { -0.5 })
.collect();
let binary = BinaryQuantizedVector::from_f32(&vector);
let bytes = binary.to_bytes();
let deserialized = BinaryQuantizedVector::from_bytes(&bytes).unwrap();
assert_eq!(deserialized.dimension(), binary.dimension());
assert_eq!(deserialized.data, binary.data);
assert_eq!(deserialized.hamming_distance(&binary), 0);
}
#[test]
fn test_binary_from_bytes_invalid() {
let bytes = vec![0u8; 3];
let result = BinaryQuantizedVector::from_bytes(&bytes);
assert!(result.is_err());
}
#[test]
fn test_binary_serialization_normal_dimension() {
let vector: Vec<f32> = (0..1024)
.map(|i| if i % 2 == 0 { 0.5 } else { -0.5 })
.collect();
let binary = BinaryQuantizedVector::from_f32(&vector);
let bytes = binary.to_bytes();
let deserialized = BinaryQuantizedVector::from_bytes(&bytes).unwrap();
assert_eq!(deserialized.dimension(), 1024);
}