use ruda_kernel::dsl as kernel_dsl;
use ruda_test_runtime::TestRuntime;
use ruda_kernel::dsl::prelude::*;
use ruprim::reduce::shared_sum;
use ruda_test_utils::{
ExecutionOutcome, HostData, HostDataType, HostDataVec, StrideSpec, TestInput, TestOutcome,
assert_equals_approx,
};
#[test]
pub fn test_shared_sum() {
test_case().test_shared_sum()
}
fn test_case() -> TestCase {
TestCase {
shape: test_shape(),
stride: test_strides(),
}
}
#[derive(Debug)]
pub struct TestCase {
pub shape: ruda_kernel::dsl::zspace::Shape,
pub stride: ruda_kernel::dsl::zspace::Strides,
}
impl TestCase {
pub fn test_shared_sum(&self) {
if self.stride.iter().any(|&s| s == 0) {
return;
}
let client = TestRuntime::client(&Default::default());
let input_dtype = TestDType::as_type_native_unchecked().storage_type();
let (input_handle, input_host) = TestInput::builder(client.clone(), self.shape.clone())
.dtype(input_dtype)
.stride(StrideSpec::Custom(self.stride.iter().copied().collect()))
.custom(self.input_raw_data())
.generate_with_f32_host_data();
let expected_sum = sum_all(&input_host);
let output_handle = TestInput::builder(client.clone(), ruda_kernel::dsl::zspace::shape![1])
.dtype(input_dtype)
.stride(StrideSpec::Custom(vec![1]))
.zeros()
.generate();
let ruda_count = 3;
let result = shared_sum(
&client,
input_handle.binding(),
output_handle.clone().binding(),
ruda_count,
TestDType::as_type_native_unchecked().elem_type(),
);
let outcome = match ExecutionOutcome::from(result) {
ExecutionOutcome::Executed => {
let actual =
HostData::from_tensor_handle(&client, output_handle, HostDataType::F32);
let expected = HostData {
data: HostDataVec::F32(vec![expected_sum]),
shape: ruda_kernel::dsl::zspace::shape![1],
strides: strides![1],
};
assert_equals_approx(&actual, &expected, 0.0625).as_test_outcome()
}
ExecutionOutcome::CompileError(e) => TestOutcome::CompileError(e),
};
outcome.enforce();
}
fn input_raw_data(&self) -> Vec<f32> {
let shape = self.shape.as_slice();
let rank = shape.len();
let num_elems: usize = shape.iter().product();
let mut data = Vec::with_capacity(num_elems);
let mut coord = vec![0usize; rank];
for linear in 0..num_elems {
let mut rem = linear;
for d in (0..rank).rev() {
coord[d] = rem % shape[d];
rem /= shape[d];
}
for d in 0..rank {
if self.stride[d] == 0 {
coord[d] = 0;
}
}
let mut hash: usize = 0;
for (d, c) in coord.iter().enumerate() {
hash = hash.wrapping_add(c.wrapping_mul(d.wrapping_add(31)));
}
let h = hash % 16;
let magnitude = (h / 2 + 1) as f32 * 0.25;
let sign = if h % 2 == 0 { 1.0 } else { -1.0 };
data.push(sign * magnitude);
}
data
}
}
fn sum_all(input: &HostData) -> f32 {
let rank = input.shape.len();
if rank == 0 {
return input.get_f32(&[]);
}
let mut coord = vec![0usize; rank];
let num: usize = input.shape.iter().product();
let mut acc = 0.0f32;
for linear in 0..num {
let mut rem = linear;
for d in (0..rank).rev() {
coord[d] = rem % input.shape[d];
rem /= input.shape[d];
}
acc += input.get_f32(&coord);
}
acc
}