use crate::numeric::Float;
use crate::simd_ops::{AutoOptimizer, SimdUnifiedOps};
use ::ndarray::{Array1, ArrayView1};
pub fn normalize_simd<F>(x: &ArrayView1<F>) -> Array1<F>
where
F: Float + SimdUnifiedOps,
{
if x.is_empty() {
return Array1::zeros(0);
}
let optimizer = AutoOptimizer::new();
if optimizer.should_use_simd(x.len()) {
return F::simd_normalize(x);
}
let norm = x
.iter()
.map(|&val| val * val)
.fold(F::zero(), |acc, val| acc + val)
.sqrt();
if norm == F::zero() || norm.is_nan() {
return Array1::zeros(x.len());
}
x.mapv(|val| val / norm)
}
pub fn standardize_simd<F>(x: &ArrayView1<F>) -> Array1<F>
where
F: Float + SimdUnifiedOps,
{
if x.is_empty() {
return Array1::zeros(0);
}
if x.len() <= 1 {
return Array1::zeros(x.len());
}
let optimizer = AutoOptimizer::new();
if optimizer.should_use_simd(x.len()) {
return F::simd_standardize(x);
}
let n = F::from(x.len()).expect("Operation failed");
let mean = x.iter().fold(F::zero(), |acc, &val| acc + val) / n;
let n_minus_1 = F::from(x.len() - 1).expect("Operation failed");
let variance = x
.iter()
.map(|&val| {
let diff = val - mean;
diff * diff
})
.fold(F::zero(), |acc, val| acc + val)
/ n_minus_1;
let std = variance.sqrt();
if std == F::zero() || std.is_nan() {
return Array1::zeros(x.len());
}
x.mapv(|val| (val - mean) / std)
}
pub fn clip_simd<F>(x: &ArrayView1<F>, min_val: F, max_val: F) -> Array1<F>
where
F: Float + SimdUnifiedOps,
{
assert!(min_val <= max_val, "min_val must be <= max_val");
if x.is_empty() {
return Array1::zeros(0);
}
let optimizer = AutoOptimizer::new();
if optimizer.should_use_simd(x.len()) {
return F::simd_clip(x, min_val, max_val);
}
x.mapv(|val| {
if val < min_val {
min_val
} else if val > max_val {
max_val
} else {
val
}
})
}
#[allow(dead_code)]
pub fn softmax_simd<F>(x: &ArrayView1<F>) -> Array1<F>
where
F: Float + SimdUnifiedOps,
{
if x.is_empty() {
return Array1::zeros(0);
}
let optimizer = AutoOptimizer::new();
if optimizer.should_use_simd(x.len()) {
let max_val = F::simd_max_element(x);
let shifted = x.mapv(|val| val - max_val);
let exp_vals = F::simd_exp(&shifted.view());
let sum = F::simd_sum(&exp_vals.view());
if sum > F::zero() {
let inv_sum = F::one() / sum;
F::simd_scalar_mul(&exp_vals.view(), inv_sum)
} else {
Array1::zeros(x.len())
}
} else {
let max_val = x.fold(F::neg_infinity(), |max, &val| max.max(val));
let mut exp_vals = x.mapv(|val| (val - max_val).exp());
let sum = exp_vals.iter().fold(F::zero(), |acc, &val| acc + val);
if sum > F::zero() {
exp_vals.mapv_inplace(|val| val / sum);
}
exp_vals
}
}
#[allow(dead_code)]
pub fn relu_simd<F>(x: &ArrayView1<F>) -> Array1<F>
where
F: Float + SimdUnifiedOps,
{
if x.is_empty() {
return Array1::zeros(0);
}
let optimizer = AutoOptimizer::new();
if optimizer.should_use_simd(x.len()) {
F::simd_relu(x)
} else {
x.mapv(|val| if val > F::zero() { val } else { F::zero() })
}
}
#[allow(dead_code)]
pub fn leaky_relu_simd<F>(x: &ArrayView1<F>, alpha: F) -> Array1<F>
where
F: Float + SimdUnifiedOps,
{
if x.is_empty() {
return Array1::zeros(0);
}
let optimizer = AutoOptimizer::new();
if optimizer.should_use_simd(x.len()) {
F::simd_leaky_relu(x, alpha)
} else {
x.mapv(|val| if val > F::zero() { val } else { val * alpha })
}
}
#[cfg(test)]
mod tests {
use super::*;
use ::ndarray::array;
#[test]
fn test_normalize_simd_basic_f64() {
let x = array![3.0, 4.0];
let result = normalize_simd(&x.view());
assert!((result[0] - 0.6).abs() < 1e-10);
assert!((result[1] - 0.8).abs() < 1e-10);
let norm: f64 = result.iter().map(|x| x * x).sum::<f64>().sqrt();
assert!((norm - 1.0).abs() < 1e-10);
}
#[test]
fn test_normalize_simd_basic_f32() {
let x = array![3.0f32, 4.0];
let result = normalize_simd(&x.view());
assert!((result[0] - 0.6).abs() < 1e-6);
assert!((result[1] - 0.8).abs() < 1e-6);
let norm: f32 = result.iter().map(|x| x * x).sum::<f32>().sqrt();
assert!((norm - 1.0).abs() < 1e-6);
}
#[test]
fn test_normalize_simd_empty() {
let x: Array1<f64> = array![];
let result = normalize_simd(&x.view());
assert_eq!(result.len(), 0);
}
#[test]
fn test_normalize_simd_zero_vector() {
let x = array![0.0, 0.0, 0.0];
let result = normalize_simd(&x.view());
assert_eq!(result[0], 0.0);
assert_eq!(result[1], 0.0);
assert_eq!(result[2], 0.0);
}
#[test]
fn test_standardize_simd_basic_f64() {
let x = array![2.0, 4.0, 4.0, 4.0, 5.0, 5.0, 7.0, 9.0];
let result = standardize_simd(&x.view());
let mean: f64 = result.iter().sum::<f64>() / result.len() as f64;
assert!(mean.abs() < 1e-10, "Mean should be ~0, got {}", mean);
let variance: f64 = result.iter().map(|&x| x * x).sum::<f64>() / (result.len() - 1) as f64;
let std = variance.sqrt();
assert!((std - 1.0).abs() < 1e-10, "Std should be ~1, got {}", std);
}
#[test]
fn test_standardize_simd_basic_f32() {
let x = array![1.0f32, 2.0, 3.0, 4.0, 5.0];
let result = standardize_simd(&x.view());
let mean: f32 = result.iter().sum::<f32>() / result.len() as f32;
assert!(mean.abs() < 1e-5, "Mean should be ~0, got {}", mean);
let variance: f32 = result.iter().map(|&x| x * x).sum::<f32>() / (result.len() - 1) as f32;
let std = variance.sqrt();
assert!((std - 1.0).abs() < 1e-5, "Std should be ~1, got {}", std);
}
#[test]
fn test_standardize_simd_empty() {
let x: Array1<f64> = array![];
let result = standardize_simd(&x.view());
assert_eq!(result.len(), 0);
}
#[test]
fn test_standardize_simd_single_element() {
let x = array![5.0];
let result = standardize_simd(&x.view());
assert_eq!(result[0], 0.0);
}
#[test]
fn test_standardize_simd_constant() {
let x = array![5.0, 5.0, 5.0, 5.0];
let result = standardize_simd(&x.view());
assert_eq!(result[0], 0.0);
assert_eq!(result[1], 0.0);
assert_eq!(result[2], 0.0);
assert_eq!(result[3], 0.0);
}
#[test]
fn test_softmax_simd_f64_basic() {
let x = array![1.0f64, 2.0, 3.0];
let result = softmax_simd(&x.view());
let sum: f64 = result.iter().sum();
assert!((sum - 1.0).abs() < 1e-10);
for &val in result.iter() {
assert!(val > 0.0 && val < 1.0);
}
let max_idx = result
.iter()
.enumerate()
.max_by(|(_, a), (_, b)| a.partial_cmp(b).expect("Operation failed"))
.map(|(idx, _)| idx)
.expect("Operation failed");
assert_eq!(max_idx, 2); }
#[test]
fn test_softmax_simd_f32_basic() {
let x = array![1.0f32, 2.0, 3.0, 4.0];
let result = softmax_simd(&x.view());
let sum: f32 = result.iter().sum();
assert!((sum - 1.0).abs() < 1e-6);
for &val in result.iter() {
assert!(val > 0.0 && val < 1.0);
}
}
#[test]
fn test_softmax_simd_empty() {
let x: Array1<f64> = array![];
let result = softmax_simd(&x.view());
assert_eq!(result.len(), 0);
}
#[test]
fn test_softmax_simd_single() {
let x = array![42.0f64];
let result = softmax_simd(&x.view());
assert!((result[0] - 1.0).abs() < 1e-10);
}
#[test]
fn test_softmax_simd_uniform() {
let x = array![2.0f64, 2.0, 2.0, 2.0];
let result = softmax_simd(&x.view());
for &val in result.iter() {
assert!((val - 0.25).abs() < 1e-10);
}
let sum: f64 = result.iter().sum();
assert!((sum - 1.0).abs() < 1e-10);
}
#[test]
fn test_softmax_simd_large_values() {
let x = array![1000.0f64, 1001.0, 1002.0];
let result = softmax_simd(&x.view());
assert!(!result.iter().any(|&v| v.is_infinite() || v.is_nan()));
let sum: f64 = result.iter().sum();
assert!((sum - 1.0).abs() < 1e-10);
let max_idx = result
.iter()
.enumerate()
.max_by(|(_, a), (_, b)| a.partial_cmp(b).expect("Operation failed"))
.map(|(idx, _)| idx)
.expect("Operation failed");
assert_eq!(max_idx, 2);
}
#[test]
fn test_softmax_simd_negative_values() {
let x = array![-10.0f64, -5.0, -2.0, -8.0];
let result = softmax_simd(&x.view());
let sum: f64 = result.iter().sum();
assert!((sum - 1.0).abs() < 1e-10);
let max_idx = result
.iter()
.enumerate()
.max_by(|(_, a), (_, b)| a.partial_cmp(b).expect("Operation failed"))
.map(|(idx, _)| idx)
.expect("Operation failed");
assert_eq!(max_idx, 2); }
#[test]
fn test_softmax_simd_attention_scores() {
let scores = array![2.0f64, 4.0, 1.0, 3.0];
let attention_weights = softmax_simd(&scores.view());
let sum: f64 = attention_weights.iter().sum();
assert!((sum - 1.0).abs() < 1e-10);
let max_idx = attention_weights
.iter()
.enumerate()
.max_by(|(_, a), (_, b)| a.partial_cmp(b).expect("Operation failed"))
.map(|(idx, _)| idx)
.expect("Operation failed");
assert_eq!(max_idx, 1);
for &weight in attention_weights.iter() {
assert!(weight > 0.0);
}
}
#[test]
fn test_softmax_simd_large_array() {
let x: Array1<f64> = Array1::from_vec((0..5000).map(|i| (i as f64) * 0.01).collect());
let result = softmax_simd(&x.view());
let sum: f64 = result.iter().sum();
assert!((sum - 1.0).abs() < 1e-9);
assert!(result.iter().all(|&v| v > 0.0));
let max_idx = result
.iter()
.enumerate()
.max_by(|(_, a), (_, b)| a.partial_cmp(b).expect("Operation failed"))
.map(|(idx, _)| idx)
.expect("Operation failed");
assert_eq!(max_idx, 4999); }
#[test]
fn test_softmax_simd_temperature_scaling() {
let logits = array![1.0f64, 2.0, 3.0];
let high_temp = softmax_simd(&logits.mapv(|x| x / 2.0).view());
let low_temp = softmax_simd(&logits.mapv(|x| x * 2.0).view());
let sum_high: f64 = high_temp.iter().sum();
let sum_low: f64 = low_temp.iter().sum();
assert!((sum_high - 1.0).abs() < 1e-10);
assert!((sum_low - 1.0).abs() < 1e-10);
let max_high = high_temp.iter().cloned().fold(0.0f64, f64::max);
let max_low = low_temp.iter().cloned().fold(0.0f64, f64::max);
assert!(max_low > max_high);
}
#[test]
fn test_relu_simd_f64_basic() {
let x = array![-2.0f64, -1.0, 0.0, 1.0, 2.0];
let result = relu_simd(&x.view());
assert_eq!(result[0], 0.0);
assert_eq!(result[1], 0.0);
assert_eq!(result[2], 0.0);
assert_eq!(result[3], 1.0);
assert_eq!(result[4], 2.0);
}
#[test]
fn test_relu_simd_f32_basic() {
let x = array![-2.0f32, -1.0, 0.0, 1.0, 2.0];
let result = relu_simd(&x.view());
assert_eq!(result[0], 0.0);
assert_eq!(result[1], 0.0);
assert_eq!(result[2], 0.0);
assert_eq!(result[3], 1.0);
assert_eq!(result[4], 2.0);
}
#[test]
fn test_relu_simd_all_positive() {
let x = array![1.0f64, 2.0, 3.0, 4.0, 5.0];
let result = relu_simd(&x.view());
for i in 0..5 {
assert_eq!(result[i], x[i]);
}
}
#[test]
fn test_relu_simd_all_negative() {
let x = array![-5.0f64, -4.0, -3.0, -2.0, -1.0];
let result = relu_simd(&x.view());
for i in 0..5 {
assert_eq!(result[i], 0.0);
}
}
#[test]
fn test_relu_simd_empty() {
let x: Array1<f64> = array![];
let result = relu_simd(&x.view());
assert_eq!(result.len(), 0);
}
#[test]
fn test_relu_simd_large_array() {
let x: Array1<f64> = Array1::from_vec((0..5000).map(|i| (i as f64) - 2500.0).collect());
let result = relu_simd(&x.view());
for i in 0..2500 {
assert_eq!(result[i], 0.0);
}
assert_eq!(result[2500], 0.0);
for i in 2501..5000 {
assert!(result[i] > 0.0);
}
}
#[test]
fn test_leaky_relu_simd_f64_basic() {
let x = array![-2.0f64, -1.0, 0.0, 1.0, 2.0];
let result = leaky_relu_simd(&x.view(), 0.01);
assert_eq!(result[0], -0.02);
assert_eq!(result[1], -0.01);
assert_eq!(result[2], 0.0);
assert_eq!(result[3], 1.0);
assert_eq!(result[4], 2.0);
}
#[test]
fn test_leaky_relu_simd_f32_basic() {
let x = array![-2.0f32, -1.0, 0.0, 1.0, 2.0];
let result = leaky_relu_simd(&x.view(), 0.01);
assert!((result[0] - (-0.02)).abs() < 1e-6);
assert!((result[1] - (-0.01)).abs() < 1e-6);
assert_eq!(result[2], 0.0);
assert_eq!(result[3], 1.0);
assert_eq!(result[4], 2.0);
}
#[test]
fn test_leaky_relu_simd_different_alpha() {
let x = array![-10.0f64, -5.0, 0.0, 5.0, 10.0];
let result_01 = leaky_relu_simd(&x.view(), 0.1);
assert_eq!(result_01[0], -1.0); assert_eq!(result_01[1], -0.5);
let result_02 = leaky_relu_simd(&x.view(), 0.2);
assert_eq!(result_02[0], -2.0); assert_eq!(result_02[1], -1.0); }
#[test]
fn test_leaky_relu_simd_preserves_positive() {
let x = array![1.0f64, 2.0, 3.0, 4.0, 5.0];
let result = leaky_relu_simd(&x.view(), 0.01);
for i in 0..5 {
assert_eq!(result[i], x[i]);
}
}
#[test]
fn test_leaky_relu_simd_empty() {
let x: Array1<f64> = array![];
let result = leaky_relu_simd(&x.view(), 0.01);
assert_eq!(result.len(), 0);
}
#[test]
fn test_relu_vs_leaky_relu() {
let x = array![-2.0f64, -1.0, 0.0, 1.0, 2.0];
let relu_result = relu_simd(&x.view());
let leaky_result = leaky_relu_simd(&x.view(), 0.01);
assert_eq!(relu_result[3], leaky_result[3]);
assert_eq!(relu_result[4], leaky_result[4]);
assert!(relu_result[0] != leaky_result[0]);
assert!(relu_result[1] != leaky_result[1]);
assert_eq!(relu_result[0], 0.0);
assert_eq!(leaky_result[0], -0.02);
}
}