use super::half_precision::*;
const EPSILON: f32 = 1e-3;
fn generate_test_vector(dim: usize, seed: f32) -> Vec<f32> {
#[allow(clippy::cast_precision_loss)]
(0..dim).map(|i| (seed + i as f32 * 0.1).sin()).collect()
}
#[test]
fn test_precision_bytes_per_element() {
assert_eq!(VectorPrecision::F32.bytes_per_element(), 4);
assert_eq!(VectorPrecision::F16.bytes_per_element(), 2);
assert_eq!(VectorPrecision::BF16.bytes_per_element(), 2);
}
#[test]
fn test_precision_memory_size() {
assert_eq!(VectorPrecision::F32.memory_size(768), 3072); assert_eq!(VectorPrecision::F16.memory_size(768), 1536); assert_eq!(VectorPrecision::BF16.memory_size(768), 1536); }
#[test]
fn test_precision_default() {
assert_eq!(VectorPrecision::default(), VectorPrecision::F32);
}
#[test]
fn test_vector_data_from_f32_slice_f32() {
let data = vec![0.1, 0.2, 0.3];
let v = VectorData::from_f32_slice(&data, VectorPrecision::F32);
assert_eq!(v.precision(), VectorPrecision::F32);
assert_eq!(v.len(), 3);
assert!(!v.is_empty());
}
#[test]
fn test_vector_data_from_f32_slice_f16() {
let data = vec![0.1, 0.2, 0.3];
let v = VectorData::from_f32_slice(&data, VectorPrecision::F16);
assert_eq!(v.precision(), VectorPrecision::F16);
assert_eq!(v.len(), 3);
}
#[test]
fn test_vector_data_from_f32_slice_bf16() {
let data = vec![0.1, 0.2, 0.3];
let v = VectorData::from_f32_slice(&data, VectorPrecision::BF16);
assert_eq!(v.precision(), VectorPrecision::BF16);
assert_eq!(v.len(), 3);
}
#[test]
#[allow(clippy::similar_names)]
fn test_vector_data_memory_size() {
let data = generate_test_vector(768, 0.0);
let full = VectorData::from_f32_slice(&data, VectorPrecision::F32);
let half = VectorData::from_f32_slice(&data, VectorPrecision::F16);
let brain = VectorData::from_f32_slice(&data, VectorPrecision::BF16);
assert_eq!(full.memory_size(), 3072);
assert_eq!(half.memory_size(), 1536);
assert_eq!(brain.memory_size(), 1536);
assert_eq!(half.memory_size(), full.memory_size() / 2);
}
#[test]
fn test_vector_data_to_f32_roundtrip() {
let original = vec![0.1, 0.5, 1.0, -0.5, 0.0];
let v_f32 = VectorData::from_f32_slice(&original, VectorPrecision::F32);
let back = v_f32.to_f32_vec();
assert_eq!(original, back);
}
#[test]
fn test_vector_data_f16_roundtrip_precision() {
let original = vec![0.1, 0.5, 1.0, -0.5, 0.0];
let v_f16 = VectorData::from_f32_slice(&original, VectorPrecision::F16);
let back = v_f16.to_f32_vec();
for (orig, converted) in original.iter().zip(back.iter()) {
assert!(
(orig - converted).abs() < 0.001,
"f16 roundtrip error: {orig} vs {converted}"
);
}
}
#[test]
fn test_vector_data_bf16_roundtrip_precision() {
let original = vec![0.1, 0.5, 1.0, -0.5, 0.0];
let v_bf16 = VectorData::from_f32_slice(&original, VectorPrecision::BF16);
let back = v_bf16.to_f32_vec();
for (orig, converted) in original.iter().zip(back.iter()) {
assert!(
(orig - converted).abs() < 0.01,
"bf16 roundtrip error: {orig} vs {converted}"
);
}
}
#[test]
fn test_vector_data_convert() {
let data = vec![0.1, 0.2, 0.3];
let original = VectorData::from_f32_slice(&data, VectorPrecision::F32);
let to_half = original.convert(VectorPrecision::F16);
assert_eq!(to_half.precision(), VectorPrecision::F16);
let to_brain = original.convert(VectorPrecision::BF16);
assert_eq!(to_brain.precision(), VectorPrecision::BF16);
let same = original.convert(VectorPrecision::F32);
assert_eq!(same.precision(), VectorPrecision::F32);
}
#[test]
fn test_vector_data_as_f32_slice() {
let data = vec![0.1, 0.2, 0.3];
let v_f32 = VectorData::from_f32_slice(&data, VectorPrecision::F32);
assert!(v_f32.as_f32_slice().is_some());
let v_f16 = VectorData::from_f32_slice(&data, VectorPrecision::F16);
assert!(v_f16.as_f32_slice().is_none());
}
#[test]
fn test_dot_product_f32() {
let a = VectorData::from_f32_slice(&[1.0, 2.0, 3.0], VectorPrecision::F32);
let b = VectorData::from_f32_slice(&[4.0, 5.0, 6.0], VectorPrecision::F32);
let result = dot_product(&a, &b);
assert!(
(result - 32.0).abs() < EPSILON,
"Expected 32.0, got {result}"
);
}
#[test]
fn test_dot_product_f16() {
let a = VectorData::from_f32_slice(&[1.0, 2.0, 3.0], VectorPrecision::F16);
let b = VectorData::from_f32_slice(&[4.0, 5.0, 6.0], VectorPrecision::F16);
let result = dot_product(&a, &b);
assert!((result - 32.0).abs() < 0.1, "f16 dot product: got {result}");
}
#[test]
fn test_dot_product_bf16() {
let a = VectorData::from_f32_slice(&[1.0, 2.0, 3.0], VectorPrecision::BF16);
let b = VectorData::from_f32_slice(&[4.0, 5.0, 6.0], VectorPrecision::BF16);
let result = dot_product(&a, &b);
assert!(
(result - 32.0).abs() < 0.5,
"bf16 dot product: got {result}"
);
}
#[test]
fn test_cosine_similarity_identical_f16() {
let data = generate_test_vector(768, 0.0);
let a = VectorData::from_f32_slice(&data, VectorPrecision::F16);
let b = VectorData::from_f32_slice(&data, VectorPrecision::F16);
let result = cosine_similarity(&a, &b);
assert!(
(result - 1.0).abs() < 0.01,
"Identical f16 vectors cosine ≈ 1.0, got {result}"
);
}
#[test]
fn test_euclidean_distance_f16() {
let a = VectorData::from_f32_slice(&[0.0, 0.0, 0.0], VectorPrecision::F16);
let b = VectorData::from_f32_slice(&[3.0, 4.0, 0.0], VectorPrecision::F16);
let result = euclidean_distance(&a, &b);
assert!(
(result - 5.0).abs() < 0.1,
"f16 euclidean 3-4-5: got {result}"
);
}
#[test]
fn test_mixed_precision_distance() {
let a = VectorData::from_f32_slice(&[1.0, 2.0, 3.0], VectorPrecision::F32);
let b = VectorData::from_f32_slice(&[1.0, 2.0, 3.0], VectorPrecision::F16);
let result = cosine_similarity(&a, &b);
assert!(
(result - 1.0).abs() < 0.01,
"Mixed precision cosine: got {result}"
);
}
#[test]
fn test_f16_preserves_ranking() {
let query = generate_test_vector(768, 0.0);
let close = generate_test_vector(768, 0.1); let far = generate_test_vector(768, 5.0);
let q_f32 = VectorData::from_f32_slice(&query, VectorPrecision::F32);
let close_f32 = VectorData::from_f32_slice(&close, VectorPrecision::F32);
let far_f32 = VectorData::from_f32_slice(&far, VectorPrecision::F32);
let dist_close_f32 = cosine_similarity(&q_f32, &close_f32);
let dist_far_f32 = cosine_similarity(&q_f32, &far_f32);
let q_f16 = VectorData::from_f32_slice(&query, VectorPrecision::F16);
let close_f16 = VectorData::from_f32_slice(&close, VectorPrecision::F16);
let far_f16 = VectorData::from_f32_slice(&far, VectorPrecision::F16);
let dist_close_f16 = cosine_similarity(&q_f16, &close_f16);
let dist_far_f16 = cosine_similarity(&q_f16, &far_f16);
assert!(
dist_close_f32 > dist_far_f32,
"F32: close should be more similar than far"
);
assert!(
dist_close_f16 > dist_far_f16,
"F16: ranking should be preserved"
);
}
#[test]
fn test_vector_data_serialization() {
let data = vec![0.1, 0.2, 0.3];
for precision in [
VectorPrecision::F32,
VectorPrecision::F16,
VectorPrecision::BF16,
] {
let v = VectorData::from_f32_slice(&data, precision);
let json = serde_json::to_string(&v).expect("serialize");
let back: VectorData = serde_json::from_str(&json).expect("deserialize");
assert_eq!(v.precision(), back.precision());
assert_eq!(v.len(), back.len());
}
}
#[test]
fn test_precision_serialization() {
for precision in [
VectorPrecision::F32,
VectorPrecision::F16,
VectorPrecision::BF16,
] {
let json = serde_json::to_string(&precision).expect("serialize");
let back: VectorPrecision = serde_json::from_str(&json).expect("deserialize");
assert_eq!(precision, back);
}
}
#[test]
fn test_empty_vector() {
let v = VectorData::from_f32_slice(&[], VectorPrecision::F16);
assert!(v.is_empty());
assert_eq!(v.len(), 0);
assert_eq!(v.memory_size(), 0);
}
#[test]
fn test_large_vector_4096d() {
let data = generate_test_vector(4096, 0.0);
let v_f16 = VectorData::from_f32_slice(&data, VectorPrecision::F16);
assert_eq!(v_f16.len(), 4096);
assert_eq!(v_f16.memory_size(), 8192);
let back = v_f16.to_f32_vec();
assert_eq!(back.len(), 4096);
}
#[test]
fn test_from_impls() {
let data = vec![0.1, 0.2, 0.3];
let v: VectorData = data.clone().into();
assert_eq!(v.precision(), VectorPrecision::F32);
let v: VectorData = data.as_slice().into();
assert_eq!(v.precision(), VectorPrecision::F32);
}