use hodu_cpu_kernels::*;
fn approx(v: Vec<f32>, digits: i32) -> Vec<f32> {
let b = 10f32.powi(digits);
v.iter().map(|t| f32::round(t * b) / b).collect()
}
fn calculate_strides(shape: &[usize]) -> Vec<usize> {
let mut strides = vec![1; shape.len()];
for i in (0..shape.len() - 1).rev() {
strides[i] = strides[i + 1] * shape[i + 1];
}
strides
}
fn calculate_output_shape(shape: &[usize], reduce_dims: &[usize], keep_dim: bool) -> Vec<usize> {
let mut output_shape = shape.to_vec();
for &dim in reduce_dims.iter() {
if keep_dim {
output_shape[dim] = 1;
} else {
output_shape[dim] = 0;
}
}
if !keep_dim {
output_shape.retain(|&size| size != 0);
if output_shape.is_empty() {
output_shape = vec![1];
}
}
output_shape
}
#[test]
fn test_reduce_sum_f32() {
let input = [1.0f32, 2.0, 3.0, 4.0, 5.0, 6.0];
let shape = vec![2, 3]; let reduce_dims = vec![1]; let keep_dim = false;
let strides = calculate_strides(&shape);
let output_shape = calculate_output_shape(&shape, &reduce_dims, keep_dim);
let output_size: usize = output_shape.iter().product();
let reduce_size: usize = reduce_dims.iter().map(|&d| shape[d]).product();
let mut output = vec![0.0f32; output_size];
let mut metadata = vec![shape.len()];
metadata.extend(&shape);
metadata.extend(&strides);
metadata.push(0); metadata.push(output_shape.len());
metadata.extend(&output_shape);
metadata.push(reduce_dims.len());
metadata.extend(&reduce_dims);
metadata.push(if keep_dim { 1 } else { 0 });
metadata.push(reduce_size);
call_ops_reduce(
sum::F32,
input.as_ptr() as *const core::ffi::c_void,
output.as_mut_ptr() as *mut core::ffi::c_void,
&metadata,
)
.unwrap();
assert_eq!(approx(output, 4), vec![6.0, 15.0]);
}
#[test]
fn test_reduce_sum_f32_dim0() {
let input = [1.0f32, 2.0, 3.0, 4.0];
let shape = vec![2, 2];
let reduce_dims = vec![0]; let keep_dim = false;
let strides = calculate_strides(&shape);
let output_shape = calculate_output_shape(&shape, &reduce_dims, keep_dim);
let output_size: usize = output_shape.iter().product();
let reduce_size: usize = reduce_dims.iter().map(|&d| shape[d]).product();
let mut output = vec![0.0f32; output_size];
let mut metadata = vec![shape.len()];
metadata.extend(&shape);
metadata.extend(&strides);
metadata.push(0);
metadata.push(output_shape.len());
metadata.extend(&output_shape);
metadata.push(reduce_dims.len());
metadata.extend(&reduce_dims);
metadata.push(if keep_dim { 1 } else { 0 });
metadata.push(reduce_size);
call_ops_reduce(
sum::F32,
input.as_ptr() as *const core::ffi::c_void,
output.as_mut_ptr() as *mut core::ffi::c_void,
&metadata,
)
.unwrap();
assert_eq!(approx(output, 4), vec![4.0, 6.0]);
}
#[test]
fn test_reduce_sum_3d() {
let input: Vec<f32> = (1..=24).map(|x| x as f32).collect();
let shape = vec![2, 3, 4]; let reduce_dims = vec![2]; let keep_dim = false;
let strides = calculate_strides(&shape);
let output_shape = calculate_output_shape(&shape, &reduce_dims, keep_dim);
let output_size: usize = output_shape.iter().product();
let reduce_size: usize = reduce_dims.iter().map(|&d| shape[d]).product();
let mut output = vec![0.0f32; output_size];
let mut metadata = vec![shape.len()];
metadata.extend(&shape);
metadata.extend(&strides);
metadata.push(0);
metadata.push(output_shape.len());
metadata.extend(&output_shape);
metadata.push(reduce_dims.len());
metadata.extend(&reduce_dims);
metadata.push(if keep_dim { 1 } else { 0 });
metadata.push(reduce_size);
call_ops_reduce(
sum::F32,
input.as_ptr() as *const core::ffi::c_void,
output.as_mut_ptr() as *mut core::ffi::c_void,
&metadata,
)
.unwrap();
assert_eq!(approx(output, 4), vec![10.0, 26.0, 42.0, 58.0, 74.0, 90.0]);
}
#[test]
fn test_reduce_sum_f32_keep_dim() {
let input = [1.0f32, 2.0, 3.0, 4.0, 5.0, 6.0];
let shape = vec![2, 3]; let reduce_dims = vec![1]; let keep_dim = true;
let strides = calculate_strides(&shape);
let output_shape = calculate_output_shape(&shape, &reduce_dims, keep_dim);
let output_size: usize = output_shape.iter().product();
let reduce_size: usize = reduce_dims.iter().map(|&d| shape[d]).product();
let mut output = vec![0.0f32; output_size];
let mut metadata = vec![shape.len()];
metadata.extend(&shape);
metadata.extend(&strides);
metadata.push(0);
metadata.push(output_shape.len());
metadata.extend(&output_shape);
metadata.push(reduce_dims.len());
metadata.extend(&reduce_dims);
metadata.push(if keep_dim { 1 } else { 0 });
metadata.push(reduce_size);
call_ops_reduce(
sum::F32,
input.as_ptr() as *const core::ffi::c_void,
output.as_mut_ptr() as *mut core::ffi::c_void,
&metadata,
)
.unwrap();
assert_eq!(approx(output, 4), vec![6.0, 15.0]);
}
#[test]
fn test_reduce_sum_i32() {
let input = [1i32, 2, 3, 4, 5, 6];
let shape = vec![2, 3];
let reduce_dims = vec![1];
let keep_dim = false;
let strides = calculate_strides(&shape);
let output_shape = calculate_output_shape(&shape, &reduce_dims, keep_dim);
let output_size: usize = output_shape.iter().product();
let reduce_size: usize = reduce_dims.iter().map(|&d| shape[d]).product();
let mut output = vec![0i32; output_size];
let mut metadata = vec![shape.len()];
metadata.extend(&shape);
metadata.extend(&strides);
metadata.push(0);
metadata.push(output_shape.len());
metadata.extend(&output_shape);
metadata.push(reduce_dims.len());
metadata.extend(&reduce_dims);
metadata.push(if keep_dim { 1 } else { 0 });
metadata.push(reduce_size);
call_ops_reduce(
sum::I32,
input.as_ptr() as *const core::ffi::c_void,
output.as_mut_ptr() as *mut core::ffi::c_void,
&metadata,
)
.unwrap();
assert_eq!(output, vec![6, 15]);
}
#[test]
fn test_reduce_mean_f32() {
let input = [2.0f32, 4.0, 6.0, 8.0, 10.0, 12.0];
let shape = vec![2, 3];
let reduce_dims = vec![1];
let keep_dim = false;
let strides = calculate_strides(&shape);
let output_shape = calculate_output_shape(&shape, &reduce_dims, keep_dim);
let output_size: usize = output_shape.iter().product();
let reduce_size: usize = reduce_dims.iter().map(|&d| shape[d]).product();
let mut output = vec![0.0f32; output_size];
let mut metadata = vec![shape.len()];
metadata.extend(&shape);
metadata.extend(&strides);
metadata.push(0);
metadata.push(output_shape.len());
metadata.extend(&output_shape);
metadata.push(reduce_dims.len());
metadata.extend(&reduce_dims);
metadata.push(if keep_dim { 1 } else { 0 });
metadata.push(reduce_size);
call_ops_reduce(
mean::F32,
input.as_ptr() as *const core::ffi::c_void,
output.as_mut_ptr() as *mut core::ffi::c_void,
&metadata,
)
.unwrap();
assert_eq!(approx(output, 4), vec![4.0, 10.0]);
}
#[test]
fn test_reduce_max_f32() {
let input = [1.0f32, 5.0, 3.0, 2.0, 8.0, 1.0];
let shape = vec![2, 3];
let reduce_dims = vec![1];
let keep_dim = false;
let strides = calculate_strides(&shape);
let output_shape = calculate_output_shape(&shape, &reduce_dims, keep_dim);
let output_size: usize = output_shape.iter().product();
let reduce_size: usize = reduce_dims.iter().map(|&d| shape[d]).product();
let mut output = vec![0.0f32; output_size];
let mut metadata = vec![shape.len()];
metadata.extend(&shape);
metadata.extend(&strides);
metadata.push(0);
metadata.push(output_shape.len());
metadata.extend(&output_shape);
metadata.push(reduce_dims.len());
metadata.extend(&reduce_dims);
metadata.push(if keep_dim { 1 } else { 0 });
metadata.push(reduce_size);
call_ops_reduce(
max::F32,
input.as_ptr() as *const core::ffi::c_void,
output.as_mut_ptr() as *mut core::ffi::c_void,
&metadata,
)
.unwrap();
assert_eq!(approx(output, 4), vec![5.0, 8.0]);
}
#[test]
fn test_reduce_max_f32_keep_dim() {
let input = [1.0f32, 5.0, 3.0, 2.0, 8.0, 1.0];
let shape = vec![2, 3];
let reduce_dims = vec![1];
let keep_dim = true;
let strides = calculate_strides(&shape);
let output_shape = calculate_output_shape(&shape, &reduce_dims, keep_dim);
let output_size: usize = output_shape.iter().product();
let reduce_size: usize = reduce_dims.iter().map(|&d| shape[d]).product();
let mut output = vec![0.0f32; output_size];
let mut metadata = vec![shape.len()];
metadata.extend(&shape);
metadata.extend(&strides);
metadata.push(0);
metadata.push(output_shape.len());
metadata.extend(&output_shape);
metadata.push(reduce_dims.len());
metadata.extend(&reduce_dims);
metadata.push(if keep_dim { 1 } else { 0 });
metadata.push(reduce_size);
call_ops_reduce(
max::F32,
input.as_ptr() as *const core::ffi::c_void,
output.as_mut_ptr() as *mut core::ffi::c_void,
&metadata,
)
.unwrap();
assert_eq!(approx(output, 4), vec![5.0, 8.0]);
}
#[test]
fn test_reduce_min_f32() {
let input = [1.0f32, 5.0, 3.0, 2.0, 8.0, 1.0];
let shape = vec![2, 3];
let reduce_dims = vec![1];
let keep_dim = false;
let strides = calculate_strides(&shape);
let output_shape = calculate_output_shape(&shape, &reduce_dims, keep_dim);
let output_size: usize = output_shape.iter().product();
let reduce_size: usize = reduce_dims.iter().map(|&d| shape[d]).product();
let mut output = vec![0.0f32; output_size];
let mut metadata = vec![shape.len()];
metadata.extend(&shape);
metadata.extend(&strides);
metadata.push(0);
metadata.push(output_shape.len());
metadata.extend(&output_shape);
metadata.push(reduce_dims.len());
metadata.extend(&reduce_dims);
metadata.push(if keep_dim { 1 } else { 0 });
metadata.push(reduce_size);
call_ops_reduce(
min::F32,
input.as_ptr() as *const core::ffi::c_void,
output.as_mut_ptr() as *mut core::ffi::c_void,
&metadata,
)
.unwrap();
assert_eq!(approx(output, 4), vec![1.0, 1.0]);
}
#[test]
fn test_reduce_prod_f32() {
let input = [1.0f32, 2.0, 3.0, 4.0, 5.0, 6.0];
let shape = vec![2, 3];
let reduce_dims = vec![1];
let keep_dim = false;
let strides = calculate_strides(&shape);
let output_shape = calculate_output_shape(&shape, &reduce_dims, keep_dim);
let output_size: usize = output_shape.iter().product();
let reduce_size: usize = reduce_dims.iter().map(|&d| shape[d]).product();
let mut output = vec![0.0f32; output_size];
let mut metadata = vec![shape.len()];
metadata.extend(&shape);
metadata.extend(&strides);
metadata.push(0);
metadata.push(output_shape.len());
metadata.extend(&output_shape);
metadata.push(reduce_dims.len());
metadata.extend(&reduce_dims);
metadata.push(if keep_dim { 1 } else { 0 });
metadata.push(reduce_size);
call_ops_reduce(
prod::F32,
input.as_ptr() as *const core::ffi::c_void,
output.as_mut_ptr() as *mut core::ffi::c_void,
&metadata,
)
.unwrap();
assert_eq!(approx(output, 4), vec![6.0, 120.0]);
}
#[test]
fn test_reduce_std_f32() {
let input = [1.0f32, 2.0, 3.0, 4.0, 5.0, 6.0];
let shape = vec![2, 3];
let reduce_dims = vec![1];
let keep_dim = false;
let strides = calculate_strides(&shape);
let output_shape = calculate_output_shape(&shape, &reduce_dims, keep_dim);
let output_size: usize = output_shape.iter().product();
let reduce_size: usize = reduce_dims.iter().map(|&d| shape[d]).product();
let mut output = vec![0.0f32; output_size];
let mut metadata = vec![shape.len()];
metadata.extend(&shape);
metadata.extend(&strides);
metadata.push(0);
metadata.push(output_shape.len());
metadata.extend(&output_shape);
metadata.push(reduce_dims.len());
metadata.extend(&reduce_dims);
metadata.push(if keep_dim { 1 } else { 0 });
metadata.push(reduce_size);
call_ops_reduce(
std::F32,
input.as_ptr() as *const core::ffi::c_void,
output.as_mut_ptr() as *mut core::ffi::c_void,
&metadata,
)
.unwrap();
assert_eq!(approx(output, 2), vec![0.82, 0.82]);
}
#[test]
fn test_reduce_var_f32() {
let input = [1.0f32, 2.0, 3.0, 4.0, 5.0, 6.0];
let shape = vec![2, 3];
let reduce_dims = vec![1];
let keep_dim = false;
let strides = calculate_strides(&shape);
let output_shape = calculate_output_shape(&shape, &reduce_dims, keep_dim);
let output_size: usize = output_shape.iter().product();
let reduce_size: usize = reduce_dims.iter().map(|&d| shape[d]).product();
let mut output = vec![0.0f32; output_size];
let mut metadata = vec![shape.len()];
metadata.extend(&shape);
metadata.extend(&strides);
metadata.push(0);
metadata.push(output_shape.len());
metadata.extend(&output_shape);
metadata.push(reduce_dims.len());
metadata.extend(&reduce_dims);
metadata.push(if keep_dim { 1 } else { 0 });
metadata.push(reduce_size);
call_ops_reduce(
var::F32,
input.as_ptr() as *const core::ffi::c_void,
output.as_mut_ptr() as *mut core::ffi::c_void,
&metadata,
)
.unwrap();
assert_eq!(approx(output, 2), vec![0.67, 0.67]);
}
#[test]
fn test_reduce_norm_f32() {
let input = [3.0f32, 4.0, 5.0, 12.0];
let shape = vec![2, 2];
let reduce_dims = vec![1];
let keep_dim = false;
let strides = calculate_strides(&shape);
let output_shape = calculate_output_shape(&shape, &reduce_dims, keep_dim);
let output_size: usize = output_shape.iter().product();
let reduce_size: usize = reduce_dims.iter().map(|&d| shape[d]).product();
let mut output = vec![0.0f32; output_size];
let mut metadata = vec![shape.len()];
metadata.extend(&shape);
metadata.extend(&strides);
metadata.push(0);
metadata.push(output_shape.len());
metadata.extend(&output_shape);
metadata.push(reduce_dims.len());
metadata.extend(&reduce_dims);
metadata.push(if keep_dim { 1 } else { 0 });
metadata.push(reduce_size);
call_ops_reduce(
norm::F32,
input.as_ptr() as *const core::ffi::c_void,
output.as_mut_ptr() as *mut core::ffi::c_void,
&metadata,
)
.unwrap();
assert_eq!(approx(output, 4), vec![5.0, 13.0]);
}
#[test]
fn test_reduce_argmax_f32() {
let input = [1.0f32, 5.0, 3.0, 2.0, 8.0, 1.0];
let shape = vec![2, 3];
let reduce_dims = vec![1];
let keep_dim = false;
let strides = calculate_strides(&shape);
let output_shape = calculate_output_shape(&shape, &reduce_dims, keep_dim);
let output_size: usize = output_shape.iter().product();
let reduce_size: usize = reduce_dims.iter().map(|&d| shape[d]).product();
let mut output = vec![0i32; output_size];
let mut metadata = vec![shape.len()];
metadata.extend(&shape);
metadata.extend(&strides);
metadata.push(0);
metadata.push(output_shape.len());
metadata.extend(&output_shape);
metadata.push(reduce_dims.len());
metadata.extend(&reduce_dims);
metadata.push(if keep_dim { 1 } else { 0 });
metadata.push(reduce_size);
call_ops_reduce(
argmax::F32,
input.as_ptr() as *const core::ffi::c_void,
output.as_mut_ptr() as *mut core::ffi::c_void,
&metadata,
)
.unwrap();
assert_eq!(output, vec![1, 1]);
}
#[test]
fn test_reduce_argmin_f32() {
let input = [1.0f32, 5.0, 3.0, 2.0, 8.0, 1.0];
let shape = vec![2, 3];
let reduce_dims = vec![1];
let keep_dim = false;
let strides = calculate_strides(&shape);
let output_shape = calculate_output_shape(&shape, &reduce_dims, keep_dim);
let output_size: usize = output_shape.iter().product();
let reduce_size: usize = reduce_dims.iter().map(|&d| shape[d]).product();
let mut output = vec![0i32; output_size];
let mut metadata = vec![shape.len()];
metadata.extend(&shape);
metadata.extend(&strides);
metadata.push(0);
metadata.push(output_shape.len());
metadata.extend(&output_shape);
metadata.push(reduce_dims.len());
metadata.extend(&reduce_dims);
metadata.push(if keep_dim { 1 } else { 0 });
metadata.push(reduce_size);
call_ops_reduce(
argmin::F32,
input.as_ptr() as *const core::ffi::c_void,
output.as_mut_ptr() as *mut core::ffi::c_void,
&metadata,
)
.unwrap();
assert_eq!(output, vec![0, 2]);
}
#[test]
fn test_reduce_any_f32() {
let input = [0.0f32, 1.0, 0.0, 0.0, 2.0, 0.0];
let shape = vec![2, 3];
let reduce_dims = vec![1];
let keep_dim = false;
let strides = calculate_strides(&shape);
let output_shape = calculate_output_shape(&shape, &reduce_dims, keep_dim);
let output_size: usize = output_shape.iter().product();
let reduce_size: usize = reduce_dims.iter().map(|&d| shape[d]).product();
let mut output = vec![0u8; output_size];
let mut metadata = vec![shape.len()];
metadata.extend(&shape);
metadata.extend(&strides);
metadata.push(0);
metadata.push(output_shape.len());
metadata.extend(&output_shape);
metadata.push(reduce_dims.len());
metadata.extend(&reduce_dims);
metadata.push(if keep_dim { 1 } else { 0 });
metadata.push(reduce_size);
call_ops_reduce(
any::F32,
input.as_ptr() as *const core::ffi::c_void,
output.as_mut_ptr() as *mut core::ffi::c_void,
&metadata,
)
.unwrap();
assert_eq!(output, vec![1, 1]);
}
#[test]
fn test_reduce_any_f32_all_zeros() {
let input = [0.0f32, 0.0, 0.0, 0.0];
let shape = vec![2, 2];
let reduce_dims = vec![1];
let keep_dim = false;
let strides = calculate_strides(&shape);
let output_shape = calculate_output_shape(&shape, &reduce_dims, keep_dim);
let output_size: usize = output_shape.iter().product();
let reduce_size: usize = reduce_dims.iter().map(|&d| shape[d]).product();
let mut output = vec![0u8; output_size];
let mut metadata = vec![shape.len()];
metadata.extend(&shape);
metadata.extend(&strides);
metadata.push(0);
metadata.push(output_shape.len());
metadata.extend(&output_shape);
metadata.push(reduce_dims.len());
metadata.extend(&reduce_dims);
metadata.push(if keep_dim { 1 } else { 0 });
metadata.push(reduce_size);
call_ops_reduce(
any::F32,
input.as_ptr() as *const core::ffi::c_void,
output.as_mut_ptr() as *mut core::ffi::c_void,
&metadata,
)
.unwrap();
assert_eq!(output, vec![0, 0]);
}
#[test]
fn test_reduce_any_i32() {
let input = [0i32, 1, 0, 0, 2, 0];
let shape = vec![2, 3];
let reduce_dims = vec![1];
let keep_dim = false;
let strides = calculate_strides(&shape);
let output_shape = calculate_output_shape(&shape, &reduce_dims, keep_dim);
let output_size: usize = output_shape.iter().product();
let reduce_size: usize = reduce_dims.iter().map(|&d| shape[d]).product();
let mut output = vec![0u8; output_size];
let mut metadata = vec![shape.len()];
metadata.extend(&shape);
metadata.extend(&strides);
metadata.push(0);
metadata.push(output_shape.len());
metadata.extend(&output_shape);
metadata.push(reduce_dims.len());
metadata.extend(&reduce_dims);
metadata.push(if keep_dim { 1 } else { 0 });
metadata.push(reduce_size);
call_ops_reduce(
any::I32,
input.as_ptr() as *const core::ffi::c_void,
output.as_mut_ptr() as *mut core::ffi::c_void,
&metadata,
)
.unwrap();
assert_eq!(output, vec![1, 1]);
}
#[test]
fn test_reduce_all_f32() {
let input = [1.0f32, 2.0, 3.0, 4.0, 5.0, 6.0];
let shape = vec![2, 3];
let reduce_dims = vec![1];
let keep_dim = false;
let strides = calculate_strides(&shape);
let output_shape = calculate_output_shape(&shape, &reduce_dims, keep_dim);
let output_size: usize = output_shape.iter().product();
let reduce_size: usize = reduce_dims.iter().map(|&d| shape[d]).product();
let mut output = vec![0u8; output_size];
let mut metadata = vec![shape.len()];
metadata.extend(&shape);
metadata.extend(&strides);
metadata.push(0);
metadata.push(output_shape.len());
metadata.extend(&output_shape);
metadata.push(reduce_dims.len());
metadata.extend(&reduce_dims);
metadata.push(if keep_dim { 1 } else { 0 });
metadata.push(reduce_size);
call_ops_reduce(
all::F32,
input.as_ptr() as *const core::ffi::c_void,
output.as_mut_ptr() as *mut core::ffi::c_void,
&metadata,
)
.unwrap();
assert_eq!(output, vec![1, 1]);
}
#[test]
fn test_reduce_all_f32_with_zero() {
let input = [1.0f32, 0.0, 3.0, 4.0, 5.0, 6.0];
let shape = vec![2, 3];
let reduce_dims = vec![1];
let keep_dim = false;
let strides = calculate_strides(&shape);
let output_shape = calculate_output_shape(&shape, &reduce_dims, keep_dim);
let output_size: usize = output_shape.iter().product();
let reduce_size: usize = reduce_dims.iter().map(|&d| shape[d]).product();
let mut output = vec![0u8; output_size];
let mut metadata = vec![shape.len()];
metadata.extend(&shape);
metadata.extend(&strides);
metadata.push(0);
metadata.push(output_shape.len());
metadata.extend(&output_shape);
metadata.push(reduce_dims.len());
metadata.extend(&reduce_dims);
metadata.push(if keep_dim { 1 } else { 0 });
metadata.push(reduce_size);
call_ops_reduce(
all::F32,
input.as_ptr() as *const core::ffi::c_void,
output.as_mut_ptr() as *mut core::ffi::c_void,
&metadata,
)
.unwrap();
assert_eq!(output, vec![0, 1]);
}
#[test]
fn test_reduce_all_i32() {
let input = [1i32, 2, 3, 4, 5, 6];
let shape = vec![2, 3];
let reduce_dims = vec![1];
let keep_dim = false;
let strides = calculate_strides(&shape);
let output_shape = calculate_output_shape(&shape, &reduce_dims, keep_dim);
let output_size: usize = output_shape.iter().product();
let reduce_size: usize = reduce_dims.iter().map(|&d| shape[d]).product();
let mut output = vec![0u8; output_size];
let mut metadata = vec![shape.len()];
metadata.extend(&shape);
metadata.extend(&strides);
metadata.push(0);
metadata.push(output_shape.len());
metadata.extend(&output_shape);
metadata.push(reduce_dims.len());
metadata.extend(&reduce_dims);
metadata.push(if keep_dim { 1 } else { 0 });
metadata.push(reduce_size);
call_ops_reduce(
all::I32,
input.as_ptr() as *const core::ffi::c_void,
output.as_mut_ptr() as *mut core::ffi::c_void,
&metadata,
)
.unwrap();
assert_eq!(output, vec![1, 1]);
}