use crate::{element::JitElement, ops::numeric::empty_device, tensor::JitTensor, JitRuntime};
use burn_tensor::Shape;
use cubecl::{calculate_cube_count_elemwise, prelude::*};
use std::ops::Range;
pub(crate) fn slice<R: JitRuntime, E: JitElement>(
tensor: JitTensor<R>,
indices: &[Range<usize>],
) -> JitTensor<R> {
let mut dims = tensor.shape.dims.clone();
let mut offset_start = 0u64;
let mut offset_end = 0u64;
for i in 0..indices.len() {
offset_start += (tensor.strides[i] * indices[i].start) as u64;
offset_end += (tensor.strides[i] * (dims[i] - indices[i].end)) as u64;
dims[i] = indices[i].end - indices[i].start;
}
let offset_start = offset_start * E::cube_elem().size() as u64;
let offset_end = offset_end * E::cube_elem().size() as u64;
let memory_offset_alignment = tensor.client.properties().memory_properties().alignment;
if offset_start % memory_offset_alignment == 0u64
&& offset_end % memory_offset_alignment == 0u64
{
JitTensor::new(
tensor.client,
tensor
.handle
.offset_start(offset_start)
.offset_end(offset_end),
Shape::from(dims),
tensor.device,
tensor.strides,
tensor.dtype,
)
} else {
let shape_output = Shape::from(dims);
let output =
empty_device::<R, E>(tensor.client.clone(), tensor.device.clone(), shape_output);
slice_on_output::<R, E>(tensor, output, indices)
}
}
#[cube(launch_unchecked)]
fn slice_kernel<E: CubePrimitive>(
input: &Tensor<E>,
output: &mut Tensor<E>,
indices: Sequence<u32>,
#[comptime] rank: u32,
) {
if ABSOLUTE_POS >= output.len() {
return;
}
let mut offset_input = 0;
#[unroll]
for i in 0..rank {
let range_start = *indices.index(i);
let offset_local = ABSOLUTE_POS / output.stride(i) % output.shape(i) + range_start;
offset_input += offset_local * input.stride(i);
}
output[ABSOLUTE_POS] = input[offset_input];
}
pub(crate) fn slice_on_output<R: JitRuntime, E: JitElement>(
tensor: JitTensor<R>,
output: JitTensor<R>,
indices: &[Range<usize>],
) -> JitTensor<R> {
let ndims = tensor.shape.num_dims();
let mut indices_sequence = SequenceArg::<R, u32>::new();
for i in 0..ndims {
let start = indices.get(i).map(|index| index.start).unwrap_or(0);
indices_sequence.push(ScalarArg::new(start as u32));
}
let cube_dim = CubeDim::default();
let cube_count = calculate_cube_count_elemwise(output.shape.num_elements(), cube_dim);
unsafe {
slice_kernel::launch_unchecked::<E, R>(
&tensor.client,
cube_count,
cube_dim,
tensor.as_tensor_arg::<E>(1),
output.as_tensor_arg::<E>(1),
indices_sequence,
ndims as u32,
)
};
output
}