#![cfg(feature = "cpu")]
use rlx_ir::infer::GraphExt;
use rlx_ir::{DType, Graph, Shape};
use rlx_runtime::{Device, Session};
mod common;
fn run(
device: Device,
dims: &[usize],
axis: usize,
start: usize,
len: usize,
step: i64,
x: &[f32],
) -> Vec<f32> {
let mut g = Graph::new("slice");
let inp = g.input("x", Shape::new(dims, DType::F32));
let y = g.slice_(inp, axis, start, len, step);
g.set_outputs(vec![y]);
Session::new(device)
.compile(g)
.run(&[("x", x)])
.pop()
.unwrap()
}
fn reference(
dims: &[usize],
axis: usize,
start: usize,
len: usize,
step: i64,
x: &[f32],
) -> Vec<f32> {
let rank = dims.len();
let mut out_dims = dims.to_vec();
out_dims[axis] = len;
let stride = |d: &[usize]| {
let mut s = vec![1usize; rank];
for i in (0..rank.saturating_sub(1)).rev() {
s[i] = s[i + 1] * d[i + 1];
}
s
};
let in_stride = stride(dims);
let out_stride = stride(&out_dims);
let total: usize = out_dims.iter().product();
let mut out = vec![0f32; total];
for o in 0..total {
let mut rem = o;
let mut inflat = 0usize;
for ax in 0..rank {
let oc = rem / out_stride[ax];
rem %= out_stride[ax];
let ic = if ax == axis {
(start as i64 + oc as i64 * step) as usize
} else {
oc
};
inflat += ic * in_stride[ax];
}
out[o] = x[inflat];
}
out
}
fn cases() -> Vec<(Vec<usize>, usize, usize, usize, i64)> {
vec![
(vec![6], 0, 2, 3, 1), (vec![6], 0, 0, 3, 2), (vec![6], 0, 1, 3, 2), (vec![6], 0, 5, 6, -1), (vec![6], 0, 5, 3, -2), (vec![3, 6], 1, 0, 3, 2), (vec![3, 6], 1, 5, 3, -2), (vec![2, 3, 4], 2, 3, 2, -1), ]
}
#[test]
fn slice_1d_matches_numpy() {
let _gpu = common::serialize_gpu();
let x: Vec<f32> = (0..6).map(|i| i as f32).collect();
assert_eq!(run(Device::Cpu, &[6], 0, 0, 3, 2, &x), vec![0., 2., 4.]);
assert_eq!(run(Device::Cpu, &[6], 0, 1, 3, 2, &x), vec![1., 3., 5.]);
assert_eq!(
run(Device::Cpu, &[6], 0, 5, 6, -1, &x),
vec![5., 4., 3., 2., 1., 0.]
);
assert_eq!(run(Device::Cpu, &[6], 0, 5, 3, -2, &x), vec![5., 3., 1.]);
assert_eq!(run(Device::Cpu, &[6], 0, 2, 3, 1, &x), vec![2., 3., 4.]);
}
#[test]
fn slice_cpu_all_cases() {
let _gpu = common::serialize_gpu();
for (dims, axis, start, len, step) in cases() {
let n: usize = dims.iter().product();
let x: Vec<f32> = (0..n).map(|i| (i % 9) as f32 * 0.5 - 2.0).collect();
assert_eq!(
run(Device::Cpu, &dims, axis, start, len, step, &x),
reference(&dims, axis, start, len, step, &x),
"cpu slice dims={dims:?} axis={axis} start={start} len={len} step={step}"
);
}
}
#[cfg(any(
all(target_os = "macos", feature = "metal"),
feature = "gpu",
feature = "cuda"
))]
fn check_device(device: Device, label: &str) {
for (dims, axis, start, len, step) in cases() {
let n: usize = dims.iter().product();
let x: Vec<f32> = (0..n).map(|i| (i % 9) as f32 * 0.5 - 2.0).collect();
assert_eq!(
run(device, &dims, axis, start, len, step, &x),
run(Device::Cpu, &dims, axis, start, len, step, &x),
"{label} slice dims={dims:?} axis={axis} start={start} len={len} step={step}"
);
}
}
#[test]
#[cfg(all(target_os = "macos", feature = "metal"))]
fn slice_metal_matches_cpu() {
let _gpu = common::serialize_gpu();
check_device(Device::Metal, "metal");
}
#[test]
#[cfg(feature = "gpu")]
fn slice_wgpu_matches_cpu() {
let _gpu = common::serialize_gpu();
if common::skip_unless_available(rlx_runtime::Device::Gpu, "wgpu") {
return;
}
check_device(Device::Gpu, "wgpu");
}
#[test]
#[cfg(feature = "cuda")]
fn slice_cuda_matches_cpu() {
let _gpu = common::serialize_gpu();
if common::skip_unless_available(Device::Cuda, "cuda") {
return;
}
check_device(Device::Cuda, "cuda");
}