use cubecl::prelude::*;
use cubecl::server::Handle;
use dspk_ops::core::buffer;
use dspk_core::compute::LaunchGeometry;
use super::kernels::points::{block_sums_kernel, gather_points_kernel};
pub const SUM_BLOCK: usize = 1024;
#[derive(Clone)]
pub struct DevicePoints {
pub handle: Handle,
pub n: usize,
pub d: usize,
}
impl DevicePoints {
pub fn upload(client: &Client, x: &[f32], n: usize, d: usize) -> Self {
assert_eq!(x.len(), n * d, "points: data size mismatch");
let mut t = vec![0.0f32; n * d];
for i in 0..n {
for f in 0..d {
t[f * n + i] = x[i * d + f];
}
}
Self { handle: buffer::upload(client, &t), n, d }
}
pub fn gather(&self, client: &Client, index: &[u32]) -> Self {
let (n, d, m) = (self.n, self.d, index.len());
let out = buffer::empty::<f32>(client, m * d);
if m > 0 {
let geom = LaunchGeometry::elementwise(client, m * d);
unsafe {
gather_points_kernel::launch::<f32>(
client,
geom.cube_count,
geom.cube_dim,
BufferArg::from_raw_parts(self.handle.clone(), d * n),
BufferArg::from_raw_parts(buffer::upload(client, index), m),
BufferArg::from_raw_parts(out.clone(), m * d),
n as u32,
m as u32,
d as u32,
);
}
}
Self { handle: out, n: m, d }
}
pub fn point(&self, client: &Client, i: usize) -> Vec<f32> {
let one = self.gather(client, &[i as u32]);
buffer::download_prefix::<f32>(client, one.handle, self.d)
}
}
pub fn block_sums(client: &Client, values: &Handle, n: usize) -> Vec<f32> {
let blocks = n.div_ceil(SUM_BLOCK).max(1);
let sums = buffer::empty::<f32>(client, blocks);
let geom = LaunchGeometry::elementwise(client, blocks);
unsafe {
block_sums_kernel::launch::<f32>(
client,
geom.cube_count,
geom.cube_dim,
BufferArg::from_raw_parts(values.clone(), n.max(1)),
BufferArg::from_raw_parts(sums.clone(), blocks),
n as u32,
SUM_BLOCK as u32,
blocks as u32,
);
}
buffer::download_prefix::<f32>(client, sums, blocks)
}
pub fn device_sum(client: &Client, values: &Handle, n: usize) -> f64 {
block_sums(client, values, n).iter().map(|&v| v as f64).sum()
}