use cubecl::prelude::*;
use cubecl::server::Handle;
use cubecl::zspace::{Shape, Strides};
use super::DspFloat;
use crate::core::buffer;
pub fn execute_gather_channels<F: DspFloat>(client: &Client, input: &Handle, output: &Handle, channels: usize, samples: usize, order: &[usize]) {
assert!(order.iter().all(|&c| c < channels), "gather_channels: a channel of {order:?} is not below {channels}");
let total = order.len() * samples;
if total == 0 {
return;
}
let rows: Vec<u32> = order.iter().map(|&c| c as u32).collect();
execute_gather_rows::<F>(client, input, &buffer::upload(client, &rows), output, channels, order.len(), samples);
}
pub fn execute_gather_rows<F: DspFloat>(client: &Client, input: &Handle, rows: &Handle, output: &Handle, channels: usize, out_channels: usize, samples: usize) {
let total = out_channels * samples;
if total == 0 {
return;
}
let geom = dspk_core::compute::LaunchGeometry::elementwise(client, total);
unsafe {
super::kernels::gather_rows_kernel::launch::<F>(
client,
geom.cube_count,
geom.cube_dim,
BufferArg::from_raw_parts(input.clone(), channels * samples),
BufferArg::from_raw_parts(rows.clone(), out_channels),
BufferArg::from_raw_parts(output.clone(), total),
samples as u32,
total as u32,
);
}
}
pub fn transpose<F: DspFloat>(client: &Client, input: &Handle, output: &Handle, rows: usize, cols: usize) {
transpose_strided::<F>(client, input, output, rows, cols, cols, rows);
}
pub fn transpose_strided<F: DspFloat>(
client: &Client,
input: &Handle,
output: &Handle,
rows: usize,
cols: usize,
in_row_stride: usize,
out_row_stride: usize,
) {
if rows == 0 || cols == 0 {
return;
}
debug_assert!(in_row_stride >= cols && out_row_stride >= rows, "row strides below the row lengths");
if in_row_stride != cols || out_row_stride != rows {
let geom = dspk_core::compute::LaunchGeometry::elementwise(client, rows * cols);
unsafe {
super::kernels::transpose_strided_kernel::launch::<F>(
client,
geom.cube_count,
geom.cube_dim,
BufferArg::from_raw_parts(input.clone(), (rows - 1) * in_row_stride + cols),
BufferArg::from_raw_parts(output.clone(), (cols - 1) * out_row_stride + rows),
rows as u32,
cols as u32,
in_row_stride as u32,
out_row_stride as u32,
);
}
return;
}
let (input, output) = unsafe {
(
TensorBinding::from_raw_parts(input.clone(), Strides::new(&[1, in_row_stride]), Shape::new([cols, rows])),
TensorBinding::from_raw_parts(output.clone(), Strides::new(&[out_row_stride, 1]), Shape::new([cols, rows])),
)
};
cubecl::std::tensor::copy_into(client, input, output, F::elem_type_native());
}
#[cfg(test)]
mod tests {
use super::*;
fn gathers_channels_in_the_given_order(client: &Client) {
let (channels, samples) = (4usize, 1000usize);
let data: Vec<f32> = (0..channels * samples).map(|i| i as f32).collect();
let input = buffer::upload(client, &data);
let order = [3usize, 0, 3];
let output = buffer::empty::<f32>(client, order.len() * samples);
execute_gather_channels::<f32>(client, &input, &output, channels, samples, &order);
let got = buffer::download::<f32>(client, output);
let expected: Vec<f32> = order.iter().flat_map(|&c| data[c * samples..(c + 1) * samples].to_vec()).collect();
assert_eq!(&got[..expected.len()], &expected[..]);
}
runtime_test!(gather_channels_follows_the_order, gathers_channels_in_the_given_order);
}