Formatting only. cargo fmt --check was already failing on main (accel SIMD kernels, agent, format, migrate, bench); CI now enforces it. Co-Authored-By: Claude Fable 5.1 <[email protected]>
143 lines
3.9 KiB
Rust
143 lines
3.9 KiB
Rust
//! Portable scalar implementations of all operations.
|
|
//! These serve as fallbacks when SIMD is not available.
|
|
|
|
pub fn dot_product(a: &[f32], b: &[f32]) -> f32 {
|
|
assert_eq!(a.len(), b.len(), "vectors must have equal length");
|
|
a.iter().zip(b.iter()).map(|(x, y)| x * y).sum()
|
|
}
|
|
|
|
pub fn vector_norm(v: &[f32]) -> f32 {
|
|
dot_product(v, v).sqrt()
|
|
}
|
|
|
|
pub fn cosine_similarity(a: &[f32], b: &[f32]) -> f32 {
|
|
assert_eq!(a.len(), b.len(), "vectors must have equal length");
|
|
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 denom = (norm_a * norm_b).sqrt();
|
|
if denom < f32::EPSILON {
|
|
0.0
|
|
} else {
|
|
dot / denom
|
|
}
|
|
}
|
|
|
|
pub fn batch_cosine(query: &[f32], vectors: &[&[f32]], results: &mut [(usize, f32)]) {
|
|
for (i, v) in vectors.iter().enumerate() {
|
|
results[i] = (i, cosine_similarity(query, v));
|
|
}
|
|
}
|
|
|
|
pub fn batch_cosine_prenorm(
|
|
query_normed: &[f32],
|
|
vectors: &[&[f32]],
|
|
norms: &[f32],
|
|
results: &mut [(usize, f32)],
|
|
) {
|
|
for (i, v) in vectors.iter().enumerate() {
|
|
let dot: f32 = query_normed.iter().zip(v.iter()).map(|(x, y)| x * y).sum();
|
|
let sim = if norms[i] == 0.0 { 0.0 } else { dot / norms[i] };
|
|
results[i] = (i, sim);
|
|
}
|
|
}
|
|
|
|
pub fn l2_distance(a: &[f32], b: &[f32]) -> f32 {
|
|
assert_eq!(a.len(), b.len(), "vectors must have equal length");
|
|
a.iter()
|
|
.zip(b.iter())
|
|
.map(|(x, y)| {
|
|
let d = x - y;
|
|
d * d
|
|
})
|
|
.sum::<f32>()
|
|
.sqrt()
|
|
}
|
|
|
|
pub fn batch_norms(vectors: &[&[f32]], norms: &mut [f32]) {
|
|
for (i, v) in vectors.iter().enumerate() {
|
|
norms[i] = vector_norm(v);
|
|
}
|
|
}
|
|
|
|
pub fn checksum_fletcher32(data: &[u8]) -> u32 {
|
|
let mut sum1: u32 = 0xFFFF;
|
|
let mut sum2: u32 = 0xFFFF;
|
|
|
|
// Process data as 16-bit words (big-endian, per HDF5 spec)
|
|
let mut i = 0;
|
|
while i + 1 < data.len() {
|
|
let word = ((data[i] as u32) << 8) | (data[i + 1] as u32);
|
|
sum1 = (sum1 + word) % 65535;
|
|
sum2 = (sum2 + sum1) % 65535;
|
|
i += 2;
|
|
}
|
|
// Handle trailing byte
|
|
if i < data.len() {
|
|
let word = (data[i] as u32) << 8;
|
|
sum1 = (sum1 + word) % 65535;
|
|
sum2 = (sum2 + sum1) % 65535;
|
|
}
|
|
|
|
(sum2 << 16) | sum1
|
|
}
|
|
|
|
#[cfg(feature = "float16")]
|
|
pub fn f16_to_f32_batch(input: &[u16], output: &mut [f32]) {
|
|
assert_eq!(input.len(), output.len());
|
|
for (i, &bits) in input.iter().enumerate() {
|
|
output[i] = half::f16::from_bits(bits).to_f32();
|
|
}
|
|
}
|
|
|
|
#[cfg(not(feature = "float16"))]
|
|
pub fn f16_to_f32_batch(input: &[u16], output: &mut [f32]) {
|
|
assert_eq!(input.len(), output.len());
|
|
// Software f16 -> f32 conversion without external deps
|
|
for (i, &bits) in input.iter().enumerate() {
|
|
output[i] = f16_to_f32_soft(bits);
|
|
}
|
|
}
|
|
|
|
/// Software half-precision to single-precision conversion.
|
|
#[cfg(not(feature = "float16"))]
|
|
fn f16_to_f32_soft(h: u16) -> f32 {
|
|
let sign = ((h >> 15) & 1) as u32;
|
|
let exp = ((h >> 10) & 0x1F) as u32;
|
|
let mant = (h & 0x3FF) as u32;
|
|
|
|
let f32_bits = if exp == 0 {
|
|
if mant == 0 {
|
|
// Zero
|
|
sign << 31
|
|
} else {
|
|
// Subnormal: normalize
|
|
let mut m = mant;
|
|
let mut e = 0i32;
|
|
while (m & 0x400) == 0 {
|
|
m <<= 1;
|
|
e += 1;
|
|
}
|
|
let exp32 = (127 - 15 - e) as u32;
|
|
let mant32 = (m & 0x3FF) << 13;
|
|
(sign << 31) | (exp32 << 23) | mant32
|
|
}
|
|
} else if exp == 31 {
|
|
// Inf or NaN
|
|
let mant32 = mant << 13;
|
|
(sign << 31) | (0xFF << 23) | mant32
|
|
} else {
|
|
// Normal
|
|
let exp32 = (exp as i32 - 15 + 127) as u32;
|
|
let mant32 = mant << 13;
|
|
(sign << 31) | (exp32 << 23) | mant32
|
|
};
|
|
|
|
f32::from_bits(f32_bits)
|
|
}
|