#![cfg_attr(feature = "simd", feature(portable_simd))]
use std::simd::{f64x4, f64x8};
use std::simd::prelude::*;
#[inline]
pub fn simd_sum(data: &[f64]) -> f64 {
let mut sum = f64x4::splat(0.0);
let chunks = data.chunks_exact(4);
let remainder = chunks.remainder();
for chunk in chunks {
let vec = f64x4::from_slice(chunk);
sum += vec;
}
sum.reduce_sum() + remainder.iter().sum::<f64>()
}
#[inline]
pub fn simd_sum_wide(data: &[f64]) -> f64 {
let mut sum = f64x8::splat(0.0);
let chunks = data.chunks_exact(8);
let remainder = chunks.remainder();
for chunk in chunks {
let vec = f64x8::from_slice(chunk);
sum += vec;
}
sum.reduce_sum() + simd_sum(remainder)
}
#[inline]
pub fn simd_mean(data: &[f64]) -> f64 {
if data.is_empty() {
return 0.0;
}
simd_sum(data) / data.len() as f64
}
#[inline]
pub fn simd_variance(data: &[f64], mean: f64) -> f64 {
if data.is_empty() {
return 0.0;
}
let mean_vec = f64x4::splat(mean);
let mut sum_sq = f64x4::splat(0.0);
let chunks = data.chunks_exact(4);
let remainder = chunks.remainder();
for chunk in chunks {
let vec = f64x4::from_slice(chunk);
let diff = vec - mean_vec;
sum_sq += diff * diff;
}
let simd_variance = sum_sq.reduce_sum();
let remainder_variance: f64 = remainder.iter().map(|x| (x - mean).powi(2)).sum();
(simd_variance + remainder_variance) / data.len() as f64
}
#[inline]
pub fn simd_normalize(data: &[f64], mean: f64, std: f64) -> Vec<f64> {
let mean_vec = f64x4::splat(mean);
let std_vec = f64x4::splat(std);
let mut result = Vec::with_capacity(data.len());
let chunks = data.chunks_exact(4);
let remainder = chunks.remainder();
for chunk in chunks {
let vec = f64x4::from_slice(chunk);
let normalized = (vec - mean_vec) / std_vec;
result.extend_from_slice(normalized.as_array());
}
for &val in remainder {
result.push((val - mean) / std);
}
result
}
#[inline]
pub fn simd_denormalize(data: &[f64], mean: f64, std: f64) -> Vec<f64> {
let mean_vec = f64x4::splat(mean);
let std_vec = f64x4::splat(std);
let mut result = Vec::with_capacity(data.len());
let chunks = data.chunks_exact(4);
let remainder = chunks.remainder();
for chunk in chunks {
let vec = f64x4::from_slice(chunk);
let denormalized = vec * std_vec + mean_vec;
result.extend_from_slice(denormalized.as_array());
}
for &val in remainder {
result.push(val * std + mean);
}
result
}
#[inline]
pub fn simd_min_max_normalize(data: &[f64], min: f64, max: f64) -> Vec<f64> {
let range = max - min;
if range <= 1e-10 {
return vec![0.5; data.len()];
}
let min_vec = f64x4::splat(min);
let range_vec = f64x4::splat(range);
let mut result = Vec::with_capacity(data.len());
let chunks = data.chunks_exact(4);
let remainder = chunks.remainder();
for chunk in chunks {
let vec = f64x4::from_slice(chunk);
let normalized = (vec - min_vec) / range_vec;
result.extend_from_slice(normalized.as_array());
}
for &val in remainder {
result.push((val - min) / range);
}
result
}
#[inline]
pub fn simd_min_max_denormalize(data: &[f64], min: f64, max: f64) -> Vec<f64> {
let range = max - min;
let min_vec = f64x4::splat(min);
let range_vec = f64x4::splat(range);
let mut result = Vec::with_capacity(data.len());
let chunks = data.chunks_exact(4);
let remainder = chunks.remainder();
for chunk in chunks {
let vec = f64x4::from_slice(chunk);
let denormalized = vec * range_vec + min_vec;
result.extend_from_slice(denormalized.as_array());
}
for &val in remainder {
result.push(val * range + min);
}
result
}
#[inline]
pub fn simd_rolling_mean(data: &[f64], window: usize) -> Vec<f64> {
if window > data.len() {
return Vec::new();
}
data.windows(window)
.map(|w| simd_mean(w))
.collect()
}
#[inline]
pub fn simd_rolling_std(data: &[f64], window: usize) -> Vec<f64> {
if window > data.len() {
return Vec::new();
}
data.windows(window)
.map(|w| {
let mean = simd_mean(w);
let variance = simd_variance(w, mean);
variance.sqrt()
})
.collect()
}
#[inline]
pub fn simd_ema(data: &[f64], alpha: f64) -> Vec<f64> {
if data.is_empty() {
return Vec::new();
}
let mut result = Vec::with_capacity(data.len());
let mut ema_value = data[0];
result.push(ema_value);
let alpha_vec = f64x4::splat(alpha);
let one_minus_alpha = f64x4::splat(1.0 - alpha);
let chunks = data[1..].chunks_exact(4);
let remainder = chunks.remainder();
for chunk in chunks {
for &value in chunk {
let value_vec = f64x4::splat(value);
let ema_vec = f64x4::splat(ema_value);
let new_ema = alpha_vec * value_vec + one_minus_alpha * ema_vec;
ema_value = new_ema.as_array()[0];
result.push(ema_value);
}
}
for &value in remainder {
ema_value = alpha * value + (1.0 - alpha) * ema_value;
result.push(ema_value);
}
result
}
#[inline]
pub fn simd_add(a: &[f64], b: &[f64]) -> Vec<f64> {
assert_eq!(a.len(), b.len(), "Vectors must have same length");
let mut result = Vec::with_capacity(a.len());
let chunks_a = a.chunks_exact(4);
let chunks_b = b.chunks_exact(4);
let remainder_a = chunks_a.remainder();
let remainder_b = chunks_b.remainder();
for (chunk_a, chunk_b) in chunks_a.zip(chunks_b) {
let vec_a = f64x4::from_slice(chunk_a);
let vec_b = f64x4::from_slice(chunk_b);
let sum = vec_a + vec_b;
result.extend_from_slice(sum.as_array());
}
for (val_a, val_b) in remainder_a.iter().zip(remainder_b.iter()) {
result.push(val_a + val_b);
}
result
}
#[inline]
pub fn simd_multiply(a: &[f64], b: &[f64]) -> Vec<f64> {
assert_eq!(a.len(), b.len(), "Vectors must have same length");
let mut result = Vec::with_capacity(a.len());
let chunks_a = a.chunks_exact(4);
let chunks_b = b.chunks_exact(4);
let remainder_a = chunks_a.remainder();
let remainder_b = chunks_b.remainder();
for (chunk_a, chunk_b) in chunks_a.zip(chunks_b) {
let vec_a = f64x4::from_slice(chunk_a);
let vec_b = f64x4::from_slice(chunk_b);
let product = vec_a * vec_b;
result.extend_from_slice(product.as_array());
}
for (val_a, val_b) in remainder_a.iter().zip(remainder_b.iter()) {
result.push(val_a * val_b);
}
result
}
#[inline]
pub fn simd_scalar_multiply(data: &[f64], scalar: f64) -> Vec<f64> {
let scalar_vec = f64x4::splat(scalar);
let mut result = Vec::with_capacity(data.len());
let chunks = data.chunks_exact(4);
let remainder = chunks.remainder();
for chunk in chunks {
let vec = f64x4::from_slice(chunk);
let product = vec * scalar_vec;
result.extend_from_slice(product.as_array());
}
for &val in remainder {
result.push(val * scalar);
}
result
}
#[cfg(test)]
mod tests {
use super::*;
const EPSILON: f64 = 1e-10;
#[test]
fn test_simd_sum() {
let data = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0];
let sum = simd_sum(&data);
assert_eq!(sum, 36.0);
let data = vec![1.0, 2.0, 3.0, 4.0, 5.0];
let sum = simd_sum(&data);
assert_eq!(sum, 15.0);
}
#[test]
fn test_simd_mean() {
let data = vec![1.0, 2.0, 3.0, 4.0, 5.0];
let mean = simd_mean(&data);
assert!((mean - 3.0).abs() < EPSILON);
}
#[test]
fn test_simd_variance() {
let data = vec![1.0, 2.0, 3.0, 4.0, 5.0];
let mean = 3.0;
let variance = simd_variance(&data, mean);
assert!((variance - 2.0).abs() < EPSILON);
}
#[test]
fn test_simd_normalize() {
let data = vec![1.0, 2.0, 3.0, 4.0, 5.0];
let mean = 3.0;
let std = 1.414;
let normalized = simd_normalize(&data, mean, std);
let norm_mean = simd_mean(&normalized);
assert!(norm_mean.abs() < 0.1);
let denormalized = simd_denormalize(&normalized, mean, std);
for (orig, denorm) in data.iter().zip(&denormalized) {
assert!((orig - denorm).abs() < 0.01);
}
}
#[test]
fn test_simd_min_max_normalize() {
let data = vec![1.0, 2.0, 3.0, 4.0, 5.0];
let normalized = simd_min_max_normalize(&data, 1.0, 5.0);
assert!((normalized[0] - 0.0).abs() < EPSILON);
assert!((normalized[4] - 1.0).abs() < EPSILON);
let denormalized = simd_min_max_denormalize(&normalized, 1.0, 5.0);
for (orig, denorm) in data.iter().zip(&denormalized) {
assert!((orig - denorm).abs() < EPSILON);
}
}
#[test]
fn test_simd_rolling_mean() {
let data = vec![1.0, 2.0, 3.0, 4.0, 5.0];
let means = simd_rolling_mean(&data, 3);
assert_eq!(means.len(), 3);
assert!((means[0] - 2.0).abs() < EPSILON); assert!((means[1] - 3.0).abs() < EPSILON); assert!((means[2] - 4.0).abs() < EPSILON); }
#[test]
fn test_simd_rolling_std() {
let data = vec![1.0, 2.0, 3.0, 4.0, 5.0];
let stds = simd_rolling_std(&data, 3);
assert_eq!(stds.len(), 3);
assert!((stds[0] - 0.816).abs() < 0.01);
}
#[test]
fn test_simd_ema() {
let data = vec![1.0, 2.0, 3.0, 4.0, 5.0];
let ema_values = simd_ema(&data, 0.5);
assert_eq!(ema_values.len(), data.len());
assert_eq!(ema_values[0], 1.0);
assert!(ema_values[4] > ema_values[0]);
}
#[test]
fn test_simd_add() {
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 result = simd_add(&a, &b);
assert_eq!(result, vec![6.0, 6.0, 6.0, 6.0, 6.0]);
}
#[test]
fn test_simd_multiply() {
let a = vec![1.0, 2.0, 3.0, 4.0];
let b = vec![2.0, 3.0, 4.0, 5.0];
let result = simd_multiply(&a, &b);
assert_eq!(result, vec![2.0, 6.0, 12.0, 20.0]);
}
#[test]
fn test_simd_scalar_multiply() {
let data = vec![1.0, 2.0, 3.0, 4.0, 5.0];
let result = simd_scalar_multiply(&data, 2.0);
assert_eq!(result, vec![2.0, 4.0, 6.0, 8.0, 10.0]);
}
#[test]
fn test_accuracy_comparison() {
let data: Vec<f64> = (0..100).map(|i| i as f64 * 0.1).collect();
let scalar_sum: f64 = data.iter().sum();
let simd_sum_result = simd_sum(&data);
assert!((scalar_sum - simd_sum_result).abs() < 1e-10);
}
}