use cubecl::prelude::*;
use dspk_core::compute::element_position;
use dspk_core::compute::negative_infinity;
#[cube(launch)]
pub fn gmm_e_step_kernel<F: Float>(
features: &[F],
means: &[F],
precisions: &[F],
log_norms: &[F],
resp: &mut [F],
log_lik: &mut [F],
mahalanobis_sq: &mut [F],
n: u32,
d: u32,
k: u32,
) {
let i = element_position() as u32;
if i < n {
let x = i * d;
let mut best = negative_infinity::<F>();
let mut best_q = F::new(0.0f32);
let mut c = 0u32;
while c < k {
let m = c * d;
let p = c * d * d;
let mut q = F::new(0.0f32);
let mut r = 0u32;
while r < d {
let dr = features[(x + r) as usize] - means[(m + r) as usize];
let mut inner = F::new(0.0f32);
let mut col = 0u32;
while col < d {
let dc = features[(x + col) as usize] - means[(m + col) as usize];
inner += precisions[(p + r * d + col) as usize] * dc;
col += 1u32;
}
q += dr * inner;
r += 1u32;
}
q = F::max(q, F::new(0.0f32));
let lp = log_norms[c as usize] - F::new(0.5f32) * q;
resp[(i * k + c) as usize] = lp;
if lp > best {
best = lp;
best_q = q;
}
c += 1u32;
}
let mut sum = F::new(0.0f32);
let mut c = 0u32;
while c < k {
let e = F::exp(resp[(i * k + c) as usize] - best);
resp[(i * k + c) as usize] = e;
sum += e;
c += 1u32;
}
let mut c = 0u32;
while c < k {
let v = resp[(i * k + c) as usize];
resp[(i * k + c) as usize] = v / sum;
c += 1u32;
}
log_lik[i as usize] = best + F::ln(sum);
mahalanobis_sq[i as usize] = best_q;
}
}
#[cube(launch)]
pub fn gmm_mean_sums_kernel<F: Float>(
features: &[F],
resp: &[F],
mask: &[F],
weighted_sum: &mut [F],
mask_sum: &mut [F],
resp_sum: &mut [F],
n: u32,
d: u32,
k: u32,
masked: u32,
) {
let unit = element_position() as u32;
if unit < k * d {
let c = unit / d;
let f = unit - c * d;
let mut s = F::new(0.0f32);
let mut sm = F::new(0.0f32);
let mut sr = F::new(0.0f32);
let mut i = 0u32;
while i < n {
let r = resp[(i * k + c) as usize];
let mut m = F::new(1.0f32);
if masked != 0u32 {
m = F::clamp(mask[(i * d + f) as usize], F::new(0.0f32), F::new(1.0f32));
}
s += r * m * features[(i * d + f) as usize];
sm += r * m;
sr += r;
i += 1u32;
}
weighted_sum[unit as usize] = s;
mask_sum[unit as usize] = sm;
if f == 0u32 {
resp_sum[c as usize] = sr;
}
}
}
#[cube(launch)]
pub fn gmm_scatter_kernel<F: Float>(
features: &[F],
resp: &[F],
means: &[F],
scatter: &mut [F],
n: u32,
d: u32,
k: u32,
diagonal: u32,
) {
let unit = element_position() as u32;
if unit < k * d * d {
let c = unit / (d * d);
let rem = unit - c * d * d;
let r = rem / d;
let col = rem - r * d;
let mut s = F::new(0.0f32);
if diagonal == 0u32 || r == col {
let (mr, mc) = (means[(c * d + r) as usize], means[(c * d + col) as usize]);
let mut i = 0u32;
while i < n {
let w = resp[(i * k + c) as usize];
s += w * (features[(i * d + r) as usize] - mr) * (features[(i * d + col) as usize] - mc);
i += 1u32;
}
}
scatter[unit as usize] = s;
}
}