use hermes_simd_core::{
align::Unaligned,
arch::SimdArch,
execution::Unmasked,
kernel::SimdKernel,
ops::{AbsMax, AbsSum},
scalar::Scalar,
view::SimdView,
};
use hermes_simd_macros::runtime_dispatch;
#[runtime_dispatch(avx512f, avx2, neon, scalar)]
pub(super) fn dispatch_abs_sum_kernel<T, A>(data: &[T]) -> T
where
T: Scalar,
A: SimdArch + SimdKernel<T>,
{
match SimdView::<T, A, Unaligned, Unmasked, &[T]>::new(data) {
Some(v) => v.reduce(AbsSum),
None => unsafe { core::hint::unreachable_unchecked() },
}
}
#[runtime_dispatch(avx512f, avx2, neon, scalar)]
pub(super) fn dispatch_abs_max_kernel<T, A>(data: &[T]) -> T
where
T: Scalar,
A: SimdArch + SimdKernel<T>,
{
match SimdView::<T, A, Unaligned, Unmasked, &[T]>::new(data) {
Some(v) => v.reduce(AbsMax),
None => unsafe { core::hint::unreachable_unchecked() },
}
}
#[cfg(test)]
mod tests {
use super::super::{abs_max, abs_sum};
#[test]
fn abs_sum_matches_scalar_reference_across_tail_sizes() {
for &len in &[0usize, 1, 3, 7, 8, 9, 15, 16, 17, 63, 64, 65, 1027] {
let data: Vec<f64> = (0..len)
.map(|i| (i as f64 - len as f64 / 2.0) * 0.75)
.collect();
let expected: f64 = data.iter().map(|x| x.abs()).sum();
assert_eq!(abs_sum(&data), expected, "len {len}");
}
}
#[test]
fn abs_max_matches_scalar_reference_across_tail_sizes() {
for &len in &[0usize, 1, 3, 7, 8, 9, 15, 16, 17, 63, 64, 65, 1027] {
let data: Vec<f64> = (0..len)
.map(|i| (i as f64 - len as f64 / 2.0) * -1.25)
.collect();
let expected = data.iter().map(|x| x.abs()).fold(0.0f64, f64::max);
assert_eq!(abs_max(&data), expected, "len {len}");
}
}
#[test]
fn abs_reductions_single_precision_match_reference() {
let data: Vec<f32> = (0..133)
.map(|i| (i as f32) * if i % 2 == 0 { 1.0 } else { -1.0 })
.collect();
let expected_sum: f32 = data.iter().map(|x| x.abs()).sum();
let expected_max = data.iter().map(|x| x.abs()).fold(0.0f32, f32::max);
assert_eq!(abs_sum(&data), expected_sum);
assert_eq!(abs_max(&data), expected_max);
}
#[test]
fn abs_reductions_empty_are_zero() {
let empty: [f64; 0] = [];
assert_eq!(abs_sum(&empty), 0.0);
assert_eq!(abs_max(&empty), 0.0);
}
#[test]
fn abs_max_all_negative_returns_largest_magnitude() {
let data = [-3.0f64, -7.5, -0.25, -7.25];
assert_eq!(abs_max(&data), 7.5);
}
}