pub use ruprim_host::reduce::*;
#[cfg(test)]
mod tests {
use alloc::vec;
use ruda_tensor::TensorData;
use ruda_tensor::ops::{FloatTensorOps, IntTensorOps};
use ruda_tensor::{bf16, f16};
use crate::{Host, HostTensor};
#[test]
fn test_mean_f16_overflow_intermediate_sum() {
let data: Vec<f16> = (0..1024).map(|i| f16::from_f32(i as f32)).collect();
let tensor = HostTensor::from_data(TensorData::new(data, [1024]));
let result = Host::float_mean(tensor);
let result_data = result.into_data();
let values: &[f16] = bytemuck::cast_slice(&result_data.bytes);
assert_eq!(values.len(), 1);
let mean = values[0].to_f32();
assert!(mean.is_finite(), "mean overflowed to {mean}");
assert!((mean - 511.5).abs() < 0.5, "expected ~511.5, got {mean}");
}
#[test]
fn test_mean_dim_f16_zero_outer_dim() {
let data: Vec<f16> = Vec::new();
let tensor = HostTensor::from_data(TensorData::new(data, [0, 4]));
let result = Host::float_mean_dim(tensor, 1);
assert_eq!(result.layout().shape().to_vec(), vec![0, 1]);
let result_data = result.into_data();
let values: &[f16] = bytemuck::cast_slice(&result_data.bytes);
assert!(values.is_empty());
}
#[test]
fn test_sum_dim_f32_zero_outer_dim() {
let data: Vec<f32> = Vec::new();
let tensor = HostTensor::from_data(TensorData::new(data, [0, 4]));
let result = Host::float_sum_dim(tensor, 1);
assert_eq!(result.layout().shape().to_vec(), vec![0, 1]);
assert!(result.into_data().bytes.is_empty());
}
#[test]
fn test_sum_dim_f32_zero_inner_dim() {
let data: Vec<f32> = Vec::new();
let tensor = HostTensor::from_data(TensorData::new(data, [3, 0]));
let result = Host::float_sum_dim(tensor, 0);
assert_eq!(result.layout().shape().to_vec(), vec![1, 0]);
assert!(result.into_data().bytes.is_empty());
}
#[test]
fn test_sum_dim_f64_zero_outer_dim() {
let data: Vec<f64> = Vec::new();
let tensor = HostTensor::from_data(TensorData::new(data, [0, 4]));
let result = Host::float_sum_dim(tensor, 1);
assert_eq!(result.layout().shape().to_vec(), vec![0, 1]);
assert!(result.into_data().bytes.is_empty());
}
#[test]
fn test_sum_dim_i8_zero_outer_dim() {
let data: Vec<i8> = Vec::new();
let tensor = HostTensor::from_data(TensorData::new(data, [0, 4]));
let result = Host::int_sum_dim(tensor, 1);
assert_eq!(result.layout().shape().to_vec(), vec![0, 1]);
assert!(result.into_data().bytes.is_empty());
}
#[test]
fn test_sum_dim_bf16_zero_outer_dim() {
let data: Vec<bf16> = Vec::new();
let tensor = HostTensor::from_data(TensorData::new(data, [0, 4]));
let result = Host::float_sum_dim(tensor, 1);
assert_eq!(result.layout().shape().to_vec(), vec![0, 1]);
assert!(result.into_data().bytes.is_empty());
}
#[test]
fn test_mean_dim_i8_large_dimension() {
let mut data: Vec<i8> = vec![0i8; 200];
data[0] = 100;
let tensor = HostTensor::from_data(TensorData::new(data, [1, 200]));
let result = Host::int_mean_dim(tensor, 1);
let result_data = result.into_data();
let values: Vec<i8> = bytemuck::cast_slice(&result_data.bytes).to_vec();
assert_eq!(values, vec![0]);
}
#[test]
fn test_mean_dim_i16_large_dimension() {
let mut data: Vec<i16> = vec![0i16; 40000];
data[0] = 32000;
let tensor = HostTensor::from_data(TensorData::new(data, [1, 40000]));
let result = Host::int_mean_dim(tensor, 1);
let result_data = result.into_data();
let values: Vec<i16> = bytemuck::cast_slice(&result_data.bytes).to_vec();
assert_eq!(values, vec![0]);
}
#[test]
fn test_sum_i32() {
let data: Vec<i32> = vec![1, 2, 3, 4, 5];
let tensor = HostTensor::from_data(TensorData::new(data, [5]));
let result = Host::int_sum(tensor);
assert_eq!(result.layout().shape().to_vec(), vec![1]);
let result_data = result.into_data();
let values: Vec<i32> = bytemuck::cast_slice(&result_data.bytes).to_vec();
assert_eq!(values, vec![15]);
}
#[test]
fn test_sum_dim_i32() {
let data: Vec<i32> = vec![1, 2, 3, 4, 5, 6];
let tensor = HostTensor::from_data(TensorData::new(data, [2, 3]));
let result = Host::int_sum_dim(tensor, 1);
assert_eq!(result.layout().shape().to_vec(), vec![2, 1]);
let result_data = result.into_data();
let values: Vec<i32> = bytemuck::cast_slice(&result_data.bytes).to_vec();
assert_eq!(values, vec![6, 15]);
}
#[test]
fn test_argmax_i32() {
let data: Vec<i32> = vec![1, 5, 3, 2, 4];
let tensor = HostTensor::from_data(TensorData::new(data, [5]));
let result = Host::int_argmax(tensor, 0);
assert_eq!(result.layout().shape().to_vec(), vec![1]);
let result_data = result.into_data();
#[cfg(target_pointer_width = "64")]
let values: Vec<i64> = bytemuck::cast_slice(&result_data.bytes).to_vec();
#[cfg(target_pointer_width = "32")]
let values: Vec<i64> = bytemuck::cast_slice::<u8, i32>(&result_data.bytes)
.iter()
.map(|&v| v as i64)
.collect();
assert_eq!(values, vec![1]);
}
#[test]
#[should_panic(expected = "dimension 0 has size 0")]
fn test_max_dim_zero_size_panics() {
let tensor = HostTensor::from_data(TensorData::new(Vec::<f32>::new(), [0, 3]));
Host::float_max_dim(tensor, 0);
}
#[test]
#[should_panic(expected = "dimension 1 has size 0")]
fn test_min_dim_zero_size_panics() {
let tensor = HostTensor::from_data(TensorData::new(Vec::<f32>::new(), [3, 0]));
Host::float_min_dim(tensor, 1);
}
#[test]
fn test_sum_u32() {
let tensor = HostTensor::from_data(TensorData::new(vec![10u32, 20, 30], [3]));
let result = Host::int_sum(tensor);
let data: Vec<u32> = result.into_data().to_vec().unwrap();
assert_eq!(data, vec![60]);
}
#[test]
fn test_sum_u64() {
let tensor = HostTensor::from_data(TensorData::new(vec![100u64, 200, 300], [3]));
let result = Host::int_sum(tensor);
let data: Vec<u64> = result.into_data().to_vec().unwrap();
assert_eq!(data, vec![600]);
}
#[test]
fn test_sum_dim_u8() {
let tensor = HostTensor::from_data(TensorData::new(vec![1u8, 2, 3, 4], [2, 2]));
let result = Host::int_sum_dim(tensor, 1);
let data: Vec<u8> = result.into_data().to_vec().unwrap();
assert_eq!(data, vec![3, 7]);
}
#[test]
fn test_prod_u16() {
let tensor = HostTensor::from_data(TensorData::new(vec![2u16, 3, 5], [3]));
let result = Host::int_prod(tensor);
let data: Vec<u16> = result.into_data().to_vec().unwrap();
assert_eq!(data, vec![30]);
}
#[test]
fn test_max_u32() {
let tensor = HostTensor::from_data(TensorData::new(vec![5u32, 100, 42], [3]));
let result = Host::int_max(tensor);
let data: Vec<u32> = result.into_data().to_vec().unwrap();
assert_eq!(data, vec![100]);
}
#[test]
fn test_min_u8() {
let tensor = HostTensor::from_data(TensorData::new(vec![5u8, 1, 42], [3]));
let result = Host::int_min(tensor);
let data: Vec<u8> = result.into_data().to_vec().unwrap();
assert_eq!(data, vec![1]);
}
#[test]
fn test_max_dim_u64() {
let tensor = HostTensor::from_data(TensorData::new(vec![10u64, 20, 30, 5], [2, 2]));
let result = Host::int_max_dim(tensor, 1);
let data: Vec<u64> = result.into_data().to_vec().unwrap();
assert_eq!(data, vec![20, 30]);
}
#[test]
fn test_min_dim_u16() {
let tensor = HostTensor::from_data(TensorData::new(vec![10u16, 2, 30, 5], [2, 2]));
let result = Host::int_min_dim(tensor, 1);
let data: Vec<u16> = result.into_data().to_vec().unwrap();
assert_eq!(data, vec![2, 5]);
}
#[test]
fn test_mean_dim_u8() {
let tensor = HostTensor::from_data(TensorData::new(vec![10u8, 20, 30, 40], [2, 2]));
let result = Host::int_mean_dim(tensor, 1);
let data: Vec<u8> = result.into_data().to_vec().unwrap();
assert_eq!(data, vec![15, 35]);
}
#[test]
fn test_max_dim_with_indices_u32() {
let tensor = HostTensor::from_data(TensorData::new(vec![5u32, 10, 3, 8], [2, 2]));
let (values, indices) = Host::int_max_dim_with_indices(tensor, 1);
let vals: Vec<u32> = values.into_data().to_vec().unwrap();
let idxs: Vec<isize> = bytemuck::cast_slice(&indices.into_data().bytes).to_vec();
assert_eq!(vals, vec![10, 8]);
assert_eq!(idxs, vec![1, 1]);
}
#[test]
fn test_argmax_scalar_and_simd_paths_agree_on_leading_nan() {
let short =
HostTensor::from_data(TensorData::new(vec![f32::NAN, f32::NAN, f32::NAN], [1, 3]));
let short_idxs: Vec<isize> =
bytemuck::cast_slice(&super::argmax(short, 1).into_data().bytes).to_vec();
let mut long_data = alloc::vec![1.0f32; 600];
long_data[0] = f32::NAN;
long_data[1] = f32::NAN;
long_data[300] = 5.0;
let long = HostTensor::from_data(TensorData::new(long_data, [1, 600]));
let long_idxs: Vec<isize> =
bytemuck::cast_slice(&super::argmax(long, 1).into_data().bytes).to_vec();
assert_eq!(short_idxs, vec![0], "scalar path");
assert_eq!(long_idxs, vec![0], "SIMD path");
}
}