use ariadnetor_core::backend::{
ComputeBackend, DeviceType, ExecPolicy, MemoryOrder, TransposeDescriptor,
};
use ariadnetor_native::NativeBackend;
use num_complex::Complex;
#[test]
fn test_backend_metadata() {
let backend = NativeBackend::new();
assert_eq!(backend.name(), "cpu");
assert_eq!(backend.device_type(), DeviceType::Cpu);
assert!(backend.is_available());
}
#[test]
fn test_transpose_f64_2d() {
let backend = NativeBackend::new();
let input = [1.0f64, 2.0, 3.0, 4.0, 5.0, 6.0];
let mut output = [0.0f64; 6];
let desc = TransposeDescriptor {
input: &input,
output: &mut output,
shape: &[2, 3],
perm: &[1, 0],
order: MemoryOrder::RowMajor,
conj: false,
policy: ExecPolicy::Sequential,
};
backend.transpose(desc).unwrap();
assert_eq!(output, [1.0, 4.0, 2.0, 5.0, 3.0, 6.0]);
}
#[test]
fn test_transpose_f64_3d() {
let backend = NativeBackend::new();
let input: Vec<f64> = (0..24).map(|i| i as f64).collect();
let mut output = vec![0.0f64; 24];
let desc = TransposeDescriptor {
input: &input,
output: &mut output,
shape: &[2, 3, 4],
perm: &[1, 0, 2],
order: MemoryOrder::RowMajor,
conj: false,
policy: ExecPolicy::Sequential,
};
backend.transpose(desc).unwrap();
assert_eq!(output[10], 6.0);
assert_eq!(output[7], 15.0);
}
#[test]
fn test_transpose_f32_2d() {
let backend = NativeBackend::new();
let input = [1.0f32, 2.0, 3.0, 4.0, 5.0, 6.0];
let mut output = [0.0f32; 6];
let desc = TransposeDescriptor {
input: &input,
output: &mut output,
shape: &[2, 3],
perm: &[1, 0],
order: MemoryOrder::RowMajor,
conj: false,
policy: ExecPolicy::Sequential,
};
backend.transpose(desc).unwrap();
assert_eq!(output, [1.0, 4.0, 2.0, 5.0, 3.0, 6.0]);
}
#[test]
fn test_transpose_complex_f64_2d() {
let backend = NativeBackend::new();
let input = [
Complex::new(1.0, 2.0),
Complex::new(3.0, 4.0),
Complex::new(5.0, 6.0),
Complex::new(7.0, 8.0),
Complex::new(9.0, 10.0),
Complex::new(11.0, 12.0),
];
let mut output = [Complex::new(0.0, 0.0); 6];
let desc = TransposeDescriptor {
input: &input,
output: &mut output,
shape: &[2, 3],
perm: &[1, 0],
order: MemoryOrder::RowMajor,
conj: false,
policy: ExecPolicy::Sequential,
};
backend.transpose(desc).unwrap();
assert_eq!(output[0], Complex::new(1.0, 2.0));
assert_eq!(output[1], Complex::new(7.0, 8.0));
assert_eq!(output[2], Complex::new(3.0, 4.0));
assert_eq!(output[3], Complex::new(9.0, 10.0));
}
fn run_transpose_f64(
backend: &NativeBackend,
input: &[f64],
shape: &[usize],
perm: &[usize],
order: MemoryOrder,
policy: ExecPolicy,
) -> Vec<f64> {
let total = shape.iter().product();
let mut output = vec![0.0f64; total];
let desc = TransposeDescriptor {
input,
output: &mut output,
shape,
perm,
order,
conj: false,
policy,
};
backend.transpose(desc).unwrap();
output
}
#[test]
fn parallel_policy_matches_sequential_2d() {
let backend = NativeBackend::new();
let input: Vec<f64> = (0..6).map(|i| i as f64).collect();
let shape = [2usize, 3];
let perm = [1usize, 0];
let seq = run_transpose_f64(
&backend,
&input,
&shape,
&perm,
MemoryOrder::RowMajor,
ExecPolicy::Sequential,
);
let par0 = run_transpose_f64(
&backend,
&input,
&shape,
&perm,
MemoryOrder::RowMajor,
ExecPolicy::Parallel(0),
);
let par2 = run_transpose_f64(
&backend,
&input,
&shape,
&perm,
MemoryOrder::RowMajor,
ExecPolicy::Parallel(2),
);
assert_eq!(seq, par0);
assert_eq!(seq, par2);
}
#[test]
fn parallel_policy_matches_sequential_3d() {
let backend = NativeBackend::new();
let input: Vec<f64> = (0..24).map(|i| i as f64).collect();
let shape = [2usize, 3, 4];
let perm = [1usize, 0, 2];
let seq = run_transpose_f64(
&backend,
&input,
&shape,
&perm,
MemoryOrder::RowMajor,
ExecPolicy::Sequential,
);
let par = run_transpose_f64(
&backend,
&input,
&shape,
&perm,
MemoryOrder::RowMajor,
ExecPolicy::Parallel(0),
);
assert_eq!(seq, par);
}
#[test]
fn parallel_policy_handles_chunk_boundaries() {
let backend = NativeBackend::new();
let n = 128usize;
let input: Vec<f64> = (0..n * n).map(|i| i as f64).collect();
let shape = [n, n];
let perm = [1usize, 0];
let seq = run_transpose_f64(
&backend,
&input,
&shape,
&perm,
MemoryOrder::RowMajor,
ExecPolicy::Sequential,
);
let par = run_transpose_f64(
&backend,
&input,
&shape,
&perm,
MemoryOrder::RowMajor,
ExecPolicy::Parallel(0),
);
assert_eq!(seq, par);
}
#[test]
fn parallel_policy_matches_sequential_column_major() {
let backend = NativeBackend::new();
let input: Vec<f64> = (0..24).map(|i| i as f64).collect();
let shape = [2usize, 3, 4];
let perm = [2usize, 0, 1];
let seq = run_transpose_f64(
&backend,
&input,
&shape,
&perm,
MemoryOrder::ColumnMajor,
ExecPolicy::Sequential,
);
let par = run_transpose_f64(
&backend,
&input,
&shape,
&perm,
MemoryOrder::ColumnMajor,
ExecPolicy::Parallel(0),
);
assert_eq!(seq, par);
}
#[test]
fn parallel_kernel_output_invariant_under_n() {
let backend = NativeBackend::new();
let n = 128usize;
let input: Vec<f64> = (0..n * n).map(|i| i as f64).collect();
let shape = [n, n];
let perm = [1usize, 0];
let seq = run_transpose_f64(
&backend,
&input,
&shape,
&perm,
MemoryOrder::RowMajor,
ExecPolicy::Sequential,
);
for &nt in &[0usize, 1, 2, 4, 8, 16] {
let par = run_transpose_f64(
&backend,
&input,
&shape,
&perm,
MemoryOrder::RowMajor,
ExecPolicy::Parallel(nt),
);
assert_eq!(seq, par, "Parallel({nt}) deviated from Sequential");
}
}
#[test]
fn parallel_policy_conjugates_each_element_once() {
let backend = NativeBackend::new();
let n = 128usize;
let input: Vec<Complex<f64>> = (0..n * n)
.map(|i| Complex::new(i as f64, (2 * i + 1) as f64))
.collect();
let shape = [n, n];
let perm = [1usize, 0];
let mut out_seq = vec![Complex::new(0.0, 0.0); n * n];
let desc_seq = TransposeDescriptor {
input: &input,
output: &mut out_seq,
shape: &shape,
perm: &perm,
order: MemoryOrder::RowMajor,
conj: true,
policy: ExecPolicy::Sequential,
};
backend.transpose(desc_seq).unwrap();
let mut out_par = vec![Complex::new(0.0, 0.0); n * n];
let desc_par = TransposeDescriptor {
input: &input,
output: &mut out_par,
shape: &shape,
perm: &perm,
order: MemoryOrder::RowMajor,
conj: true,
policy: ExecPolicy::Parallel(0),
};
backend.transpose(desc_par).unwrap();
assert_eq!(out_seq, out_par);
}