#[allow(unused_imports)]
use crate::prelude::*;
use core::ops::{Add, Mul};
use num_traits::Zero;
pub fn simd_sum<T>(values: &[T]) -> T
where
T: Copy + Add<Output = T> + Zero,
{
if values.len() < 8 {
return values.iter().copied().fold(T::zero(), Add::add);
}
let chunk_size = 8;
let mut sum = T::zero();
for chunk in values.chunks(chunk_size) {
let chunk_sum = chunk.iter().copied().fold(T::zero(), Add::add);
sum = sum + chunk_sum;
}
sum
}
pub fn simd_dot_product<T>(a: &[T], b: &[T]) -> T
where
T: Copy + Add<Output = T> + Mul<Output = T> + Zero,
{
assert_eq!(a.len(), b.len(), "vectors must have same length");
if a.len() < 8 {
return a
.iter()
.zip(b.iter())
.map(|(&x, &y)| x * y)
.fold(T::zero(), Add::add);
}
let chunk_size = 8;
let mut sum = T::zero();
let chunks_a = a.chunks(chunk_size);
let chunks_b = b.chunks(chunk_size);
for (chunk_a, chunk_b) in chunks_a.zip(chunks_b) {
let chunk_sum = chunk_a
.iter()
.zip(chunk_b.iter())
.map(|(&x, &y)| x * y)
.fold(T::zero(), Add::add);
sum = sum + chunk_sum;
}
sum
}
pub fn simd_norm_squared<T>(values: &[T]) -> T
where
T: Copy + Add<Output = T> + Mul<Output = T> + Zero,
{
simd_dot_product(values, values)
}
pub fn simd_matrix_vec_mul<T>(matrix: &[Vec<T>], vec: &[T]) -> Vec<T>
where
T: Copy + Add<Output = T> + Mul<Output = T> + Zero,
{
assert!(!matrix.is_empty(), "matrix must not be empty");
assert_eq!(
matrix[0].len(),
vec.len(),
"matrix columns must match vector length"
);
matrix
.iter()
.map(|row| simd_dot_product(row, vec))
.collect()
}
pub fn simd_vec_add<T>(a: &[T], b: &[T]) -> Vec<T>
where
T: Copy + Add<Output = T>,
{
assert_eq!(a.len(), b.len(), "vectors must have same length");
a.iter().zip(b.iter()).map(|(&x, &y)| x + y).collect()
}
pub fn simd_vec_mul<T>(a: &[T], b: &[T]) -> Vec<T>
where
T: Copy + Mul<Output = T>,
{
assert_eq!(a.len(), b.len(), "vectors must have same length");
a.iter().zip(b.iter()).map(|(&x, &y)| x * y).collect()
}
pub fn simd_vec_scale<T>(vec: &[T], scalar: T) -> Vec<T>
where
T: Copy + Mul<Output = T>,
{
vec.iter().map(|&x| x * scalar).collect()
}
pub fn simd_weighted_sum<T>(values: &[T], weights: &[T]) -> T
where
T: Copy + Add<Output = T> + Mul<Output = T> + Zero,
{
simd_dot_product(values, weights)
}
pub fn simd_reduce<T, F>(values: &[T], init: T, op: F) -> T
where
T: Copy,
F: Fn(T, T) -> T,
{
if values.is_empty() {
return init;
}
let mut working = values.to_vec();
while working.len() > 1 {
let mut next = Vec::with_capacity(working.len().div_ceil(2));
for chunk in working.chunks(2) {
if chunk.len() == 2 {
next.push(op(chunk[0], chunk[1]));
} else {
next.push(chunk[0]);
}
}
working = next;
}
working[0]
}
pub fn simd_max<T>(values: &[T]) -> Option<T>
where
T: Copy + PartialOrd,
{
if values.is_empty() {
return None;
}
Some(simd_reduce(
values,
values[0],
|a, b| {
if a > b { a } else { b }
},
))
}
pub fn simd_min<T>(values: &[T]) -> Option<T>
where
T: Copy + PartialOrd,
{
if values.is_empty() {
return None;
}
Some(simd_reduce(
values,
values[0],
|a, b| {
if a < b { a } else { b }
},
))
}
pub fn simd_mean_f64(values: &[f64]) -> Option<f64> {
if values.is_empty() {
return None;
}
let sum = simd_sum(values);
Some(sum / values.len() as f64)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_simd_sum() {
let values = vec![1.0, 2.0, 3.0, 4.0, 5.0];
let sum = simd_sum(&values);
assert_eq!(sum, 15.0);
}
#[test]
fn test_simd_dot_product() {
let a = vec![1.0, 2.0, 3.0, 4.0];
let b = vec![5.0, 6.0, 7.0, 8.0];
let dot = simd_dot_product(&a, &b);
assert_eq!(dot, 1.0 * 5.0 + 2.0 * 6.0 + 3.0 * 7.0 + 4.0 * 8.0);
}
#[test]
fn test_simd_norm_squared() {
let values = vec![3.0, 4.0];
let norm_sq = simd_norm_squared(&values);
assert_eq!(norm_sq, 25.0);
}
#[test]
fn test_simd_matrix_vec_mul() {
let matrix = vec![vec![1.0, 2.0], vec![3.0, 4.0]];
let vec = vec![5.0, 6.0];
let result = simd_matrix_vec_mul(&matrix, &vec);
assert_eq!(result, vec![17.0, 39.0]);
}
#[test]
fn test_simd_vec_add() {
let a = vec![1.0, 2.0, 3.0];
let b = vec![4.0, 5.0, 6.0];
let result = simd_vec_add(&a, &b);
assert_eq!(result, vec![5.0, 7.0, 9.0]);
}
#[test]
fn test_simd_max() {
let values = vec![3.0, 1.0, 4.0, 1.0, 5.0];
let max = simd_max(&values);
assert_eq!(max, Some(5.0));
}
#[test]
fn test_simd_min() {
let values = vec![3.0, 1.0, 4.0, 1.0, 5.0];
let min = simd_min(&values);
assert_eq!(min, Some(1.0));
}
#[test]
fn test_simd_reduce() {
let values = vec![1, 2, 3, 4, 5];
let sum = simd_reduce(&values, 0, |a, b| a + b);
assert_eq!(sum, 15);
}
}