#![allow(
clippy::collapsible_if,
clippy::neg_cmp_op_on_partial_ord,
missing_docs
)]
use cubecl::prelude::*;
use cubecl_utils_rs::prelude::*;
use crate::errors::EdgeErrors;
const STEP_CUTOFF: f32 = 40.0;
const GRADIENT_TOLERANCE: f32 = 0.01;
const PLANES_PER_CUBE: u32 = 2;
pub const F32_NOISE_SCALE: f32 = 1e-6;
pub const SUBJECT_SLOTS: u32 = 8;
const SLOT_LOG_W: u32 = 0;
const SLOT_NEW_LOG_W: u32 = 1;
const SLOT_STEP_LOG_W: u32 = 2;
const SLOT_DAMP_LOG_W: u32 = 3;
const SLOT_W: u32 = 4;
const SLOT_DW: u32 = 5;
const SLOT_VW: u32 = 6;
const SLOT_DWVW: u32 = 7;
pub const OUT_HEADER: u32 = 6;
pub const MAX_BETA_CAP: usize = 8;
#[cube(launch_unchecked)]
#[allow(clippy::too_many_arguments)]
pub fn opt_pml_gpu<F: Float + CubeElement>(
design: &Tensor<F>,
log_offset: &Tensor<F>,
subject_start: &Tensor<u32>,
counts: &Tensor<F>,
cells: &Tensor<u32>,
subject_ptr: &Tensor<u32>,
subject_total: &Tensor<F>,
subject_mean: &Tensor<F>,
request_gene: &Tensor<u32>,
request_params: &Tensor<F>,
beta_init: &Tensor<F>,
log_w_init: &Tensor<F>,
tolerance: &Tensor<F>,
subject_scratch: &mut Tensor<F>,
vwb_scratch: &mut Tensor<F>,
out: &mut Tensor<F>,
n_genes: u32,
n_req: u32,
k: u32,
nb: u32,
max_iter: u32,
max_backtrack: u32,
final_assembly: u32,
#[comptime] nb_cap: u32,
) {
let q = (CUBE_POS_Y * CUBE_COUNT_X + CUBE_POS_X) * (CUBE_DIM_X / PLANE_DIM) + PLANE_POS;
if q >= n_req {
terminate!();
}
let lane = UNIT_POS_PLANE;
let gene = request_gene[q as usize];
let zero = F::new(0.0_f32);
let one = F::new(1.0_f32);
let two = F::new(2.0_f32);
let cutoff = F::new(STEP_CUTOFF);
let huge = F::new(f32::MAX);
let eps = tolerance[0];
let noise_scale = tolerance[1];
let alpha = request_params[q as usize];
let lambda = request_params[(n_req + q) as usize];
let gamma = request_params[(2u32 * n_req + q) as usize];
let mut beta = Array::<F>::new(nb_cap as usize);
let mut new_beta = Array::<F>::new(nb_cap as usize);
let mut step_beta = Array::<F>::new(nb_cap as usize);
let mut damp_beta = Array::<F>::new(nb_cap as usize);
let mut db = Array::<F>::new(nb_cap as usize);
let mut db_lane = Array::<F>::new(nb_cap as usize);
let mut x = Array::<F>::new(nb_cap as usize);
let mut centre = Array::<F>::new(nb_cap as usize);
let mut anchor = Array::<F>::new(nb_cap as usize);
let mut first = Array::<F>::new(nb_cap as usize);
let mut delta = Array::<F>::new(nb_cap as usize);
let mut spread = Array::<F>::new((nb_cap * nb_cap) as usize);
let mut vb2 = Array::<F>::new((nb_cap * nb_cap) as usize);
let mut j = 0u32;
while j < nb {
beta[j as usize] = beta_init[(j * n_req + q) as usize];
j += 1u32;
}
let mut s = 0u32;
while s < k {
subject_scratch[((SLOT_LOG_W * k + s) * n_req + q) as usize] =
log_w_init[(s * n_req + q) as usize];
s += 1u32;
}
let mut ll = evaluate_pass::<F>(
design,
log_offset,
subject_start,
counts,
cells,
subject_ptr,
subject_total,
&*subject_scratch,
&beta,
SLOT_LOG_W,
alpha,
lambda,
gamma,
n_genes,
n_req,
k,
q,
gene,
lane,
nb,
nb_cap,
);
let mut ll_prev = zero;
let mut likdif = zero;
let mut step = 0u32;
let mut backtracks = 0u32;
let mut settled = false;
let mut confirmed = false;
let mut done = false;
while !done {
j = 0u32;
while j < nb {
damp_beta[j as usize] = one;
db[j as usize] = zero;
j += 1u32;
}
s = 0u32;
while s < k {
subject_scratch[((SLOT_DAMP_LOG_W * k + s) * n_req + q) as usize] = one;
s += 1u32;
}
let mut i = 0u32;
while i < nb * nb {
vb2[i as usize] = zero;
i += 1u32;
}
s = 0u32;
while s < k {
let log_w_s = subject_scratch[((SLOT_LOG_W * k + s) * n_req + q) as usize];
let w_s = F::exp(log_w_s);
subject_scratch[((SLOT_W * k + s) * n_req + q) as usize] = w_s;
let begin = subject_start[s as usize];
let end = subject_start[(s + 1u32) as usize];
let sp_lo = subject_ptr[(gene * (k + 1u32) + s) as usize];
let sp_hi = subject_ptr[(gene * (k + 1u32) + s + 1u32) as usize];
let mut resid = zero;
let mut weight = zero;
#[unroll]
for jj in 0..nb_cap {
db_lane[jj as usize] = zero;
first[jj as usize] = zero;
anchor[jj as usize] = subject_mean[(s * nb + jj) as usize];
}
#[unroll]
for ii in 0..nb_cap * nb_cap {
spread[ii as usize] = zero;
}
let mut r = begin + lane;
while r < end {
let mut eta = log_offset[r as usize];
#[unroll]
for jj in 0..nb_cap {
x[jj as usize] = design[(r * nb + jj) as usize];
eta += x[jj as usize] * beta[jj as usize];
}
let extb = F::exp(eta + log_w_s);
let u = one / (one + gamma / extb);
let d = zero - gamma * u;
let phi_c = gamma * u / (extb + gamma);
moment_step::<F>(
&x,
&anchor,
d,
phi_c,
&mut resid,
&mut db_lane,
&mut weight,
&mut first,
&mut spread,
&mut delta,
nb_cap,
);
r += PLANE_DIM;
}
let mut p = sp_lo + lane;
while p < sp_hi {
let c = cells[p as usize];
let y = counts[p as usize];
let mut eta = log_offset[c as usize];
#[unroll]
for jj in 0..nb_cap {
x[jj as usize] = design[(c * nb + jj) as usize];
eta += x[jj as usize] * beta[jj as usize];
}
let extb = F::exp(eta + log_w_s);
let u = one / (one + gamma / extb);
let d = y * (one - u);
let phi_c = y * u / (extb + gamma);
moment_step::<F>(
&x,
&anchor,
d,
phi_c,
&mut resid,
&mut db_lane,
&mut weight,
&mut first,
&mut spread,
&mut delta,
nb_cap,
);
p += PLANE_DIM;
}
let resid_s = plane_sum(resid);
#[unroll]
for jj in 0..nb_cap {
db[jj as usize] += plane_sum(db_lane[jj as usize]);
}
weight = plane_sum(weight);
#[unroll]
for a in 0..nb_cap {
first[a as usize] = plane_sum(first[a as usize]);
#[unroll]
for b in a..nb_cap {
let idx = (a * nb_cap + b) as usize;
spread[idx] = plane_sum(spread[idx]);
}
}
#[unroll]
for jj in 0..nb_cap {
centre[jj as usize] = anchor[jj as usize];
}
if weight > zero {
#[unroll]
for a in 0..nb_cap {
let shift = first[a as usize] / weight;
#[unroll]
for b in a..nb_cap {
spread[(a * nb_cap + b) as usize] -= shift * first[b as usize];
}
centre[a as usize] = anchor[a as usize] + shift;
}
}
let dw_s = resid_s + (alpha - lambda * w_s);
subject_scratch[((SLOT_DW * k + s) * n_req + q) as usize] = dw_s;
let vw_s = gamma * weight + lambda * w_s;
subject_scratch[((SLOT_VW * k + s) * n_req + q) as usize] = vw_s;
#[unroll]
for jj in 0..nb_cap {
vwb_scratch[((s * nb + jj) * n_req + q) as usize] =
gamma * centre[jj as usize] * weight;
}
let shrink = lambda * w_s / vw_s;
#[unroll]
for a in 0..nb_cap {
#[unroll]
for b in a..nb_cap {
let idx = (a * nb_cap + b) as usize;
vb2[idx] += gamma * spread[idx]
+ gamma * centre[a as usize] * centre[b as usize] * weight * shrink;
}
}
s += 1u32;
}
let mut a = 0u32;
while a < nb {
let mut b = a + 1u32;
while b < nb {
vb2[(b * nb + a) as usize] = vb2[(a * nb + b) as usize];
b += 1u32;
}
a += 1u32;
}
if settled {
done = true;
} else {
step += 1u32;
s = 0u32;
while s < k {
let dw_s = subject_scratch[((SLOT_DW * k + s) * n_req + q) as usize];
let vw_s = subject_scratch[((SLOT_VW * k + s) * n_req + q) as usize];
subject_scratch[((SLOT_DWVW * k + s) * n_req + q) as usize] = dw_s / vw_s;
s += 1u32;
}
j = 0u32;
while j < nb {
let mut acc = zero;
s = 0u32;
while s < k {
acc += vwb_scratch[((s * nb + j) * n_req + q) as usize]
* subject_scratch[((SLOT_DWVW * k + s) * n_req + q) as usize];
s += 1u32;
}
step_beta[j as usize] = db[j as usize] - acc;
j += 1u32;
}
i = 0u32;
while i < nb * nb {
out[((OUT_HEADER + nb + i) * n_req + q) as usize] = vb2[i as usize];
i += 1u32;
}
ldlt_solve::<F>(&mut vb2, &mut step_beta, nb, nb_cap);
s = 0u32;
while s < k {
let mut acc = zero;
j = 0u32;
while j < nb {
acc += vwb_scratch[((s * nb + j) * n_req + q) as usize] * step_beta[j as usize];
j += 1u32;
}
let vw_s = subject_scratch[((SLOT_VW * k + s) * n_req + q) as usize];
let dwvw = subject_scratch[((SLOT_DWVW * k + s) * n_req + q) as usize];
subject_scratch[((SLOT_STEP_LOG_W * k + s) * n_req + q) as usize] =
dwvw - acc / vw_s;
s += 1u32;
}
j = 0u32;
while j < nb {
new_beta[j as usize] = beta[j as usize] + step_beta[j as usize];
j += 1u32;
}
s = 0u32;
while s < k {
let base = subject_scratch[((SLOT_LOG_W * k + s) * n_req + q) as usize];
let d = subject_scratch[((SLOT_STEP_LOG_W * k + s) * n_req + q) as usize];
subject_scratch[((SLOT_NEW_LOG_W * k + s) * n_req + q) as usize] = base + d;
s += 1u32;
}
ll_prev = ll;
ll = evaluate_pass::<F>(
design,
log_offset,
subject_start,
counts,
cells,
subject_ptr,
subject_total,
&*subject_scratch,
&new_beta,
SLOT_NEW_LOG_W,
alpha,
lambda,
gamma,
n_genes,
n_req,
k,
q,
gene,
lane,
nb,
nb_cap,
);
likdif = ll - ll_prev;
let noise = F::abs(ll_prev) * noise_scale;
backtracks = 0u32;
let mut min_step = cutoff;
let mut accepted = !(likdif < zero - noise || ll > huge || ll < zero - huge);
while !accepted {
backtracks += 1u32;
min_step /= two;
if backtracks > max_backtrack {
likdif = zero;
ll = ll_prev;
let mut worst = zero;
j = 0u32;
while j < nb {
let v = F::abs(db[j as usize]);
if v > worst {
worst = v;
}
j += 1u32;
}
s = 0u32;
while s < k {
let v = F::abs(subject_scratch[((SLOT_DW * k + s) * n_req + q) as usize]);
if v > worst {
worst = v;
}
s += 1u32;
}
if worst > F::new(GRADIENT_TOLERANCE) {
backtracks += 1u32;
}
accepted = true;
} else {
j = 0u32;
while j < nb {
let d = step_beta[j as usize];
if d < cutoff && d > zero - cutoff {
damp_beta[j as usize] /= two;
new_beta[j as usize] = beta[j as usize] + d * damp_beta[j as usize];
} else if d > zero {
new_beta[j as usize] = beta[j as usize] + min_step;
} else {
new_beta[j as usize] = beta[j as usize] - min_step;
}
j += 1u32;
}
s = 0u32;
while s < k {
let d = subject_scratch[((SLOT_STEP_LOG_W * k + s) * n_req + q) as usize];
let base = subject_scratch[((SLOT_LOG_W * k + s) * n_req + q) as usize];
let damp_idx = ((SLOT_DAMP_LOG_W * k + s) * n_req + q) as usize;
let trial = if d < cutoff && d > zero - cutoff {
let damped = subject_scratch[damp_idx] / two;
subject_scratch[damp_idx] = damped;
base + d * damped
} else if d > zero {
base + min_step
} else {
base - min_step
};
subject_scratch[((SLOT_NEW_LOG_W * k + s) * n_req + q) as usize] = trial;
s += 1u32;
}
ll = evaluate_pass::<F>(
design,
log_offset,
subject_start,
counts,
cells,
subject_ptr,
subject_total,
&*subject_scratch,
&new_beta,
SLOT_NEW_LOG_W,
alpha,
lambda,
gamma,
n_genes,
n_req,
k,
q,
gene,
lane,
nb,
nb_cap,
);
likdif = ll - ll_prev;
accepted = !(likdif < zero - noise || ll > huge || ll < zero - huge);
}
}
j = 0u32;
while j < nb {
beta[j as usize] = new_beta[j as usize];
j += 1u32;
}
s = 0u32;
while s < k {
subject_scratch[((SLOT_LOG_W * k + s) * n_req + q) as usize] =
subject_scratch[((SLOT_NEW_LOG_W * k + s) * n_req + q) as usize];
s += 1u32;
}
if step >= max_iter {
settled = true;
} else if !(likdif > eps) {
settled = confirmed;
confirmed = true;
}
if settled && final_assembly == 0u32 {
done = true;
}
}
}
let mut log_det = zero;
s = 0u32;
while s < k {
log_det += F::ln(F::abs(
subject_scratch[((SLOT_VW * k + s) * n_req + q) as usize],
));
s += 1u32;
}
out[q as usize] = ll;
out[(n_req + q) as usize] = ll_prev;
out[(2u32 * n_req + q) as usize] = log_det;
out[(3u32 * n_req + q) as usize] = likdif;
out[(4u32 * n_req + q) as usize] = F::cast_from(step);
out[(5u32 * n_req + q) as usize] = F::cast_from(backtracks);
j = 0u32;
while j < nb {
out[((OUT_HEADER + j) * n_req + q) as usize] = beta[j as usize];
j += 1u32;
}
if final_assembly != 0u32 {
let mut idx = 0u32;
while idx < nb * nb {
out[((OUT_HEADER + nb + idx) * n_req + q) as usize] = vb2[idx as usize];
idx += 1u32;
}
}
s = 0u32;
while s < k {
out[((OUT_HEADER + nb + nb * nb + s) * n_req + q) as usize] =
subject_scratch[((SLOT_LOG_W * k + s) * n_req + q) as usize];
out[((OUT_HEADER + nb + nb * nb + k + s) * n_req + q) as usize] =
subject_scratch[((SLOT_VW * k + s) * n_req + q) as usize];
j = 0u32;
while j < nb {
out[((OUT_HEADER + nb + nb * nb + 2u32 * k + s * nb + j) * n_req + q) as usize] =
vwb_scratch[((s * nb + j) * n_req + q) as usize];
j += 1u32;
}
s += 1u32;
}
}
#[cube]
#[allow(clippy::too_many_arguments)]
fn evaluate_pass<F: Float>(
design: &Tensor<F>,
log_offset: &Tensor<F>,
subject_start: &Tensor<u32>,
counts: &Tensor<F>,
cells: &Tensor<u32>,
subject_ptr: &Tensor<u32>,
subject_total: &Tensor<F>,
subject_scratch: &Tensor<F>,
beta: &Array<F>,
slot_log_w: u32,
alpha: F,
lambda: F,
gamma: F,
n_genes: u32,
n_req: u32,
k: u32,
q: u32,
gene: u32,
lane: u32,
nb: u32,
#[comptime] nb_cap: u32,
) -> F {
let zero = F::new(0.0_f32);
let mut linear = zero;
let mut phil = zero;
let mut weighted = zero;
let mut subject_term = zero;
let mut sum_log_w = zero;
let mut sum_w = zero;
let mut s = 0u32;
while s < k {
let log_w_s = subject_scratch[((slot_log_w * k + s) * n_req + q) as usize];
sum_log_w += log_w_s;
sum_w += F::exp(log_w_s);
subject_term += log_w_s * subject_total[(s * n_genes + gene) as usize];
let begin = subject_start[s as usize];
let end = subject_start[(s + 1u32) as usize];
let sp_lo = subject_ptr[(gene * (k + 1u32) + s) as usize];
let sp_hi = subject_ptr[(gene * (k + 1u32) + s + 1u32) as usize];
let mut lin_lane = zero;
let mut phil_lane = zero;
let mut weighted_lane = zero;
let mut r = begin + lane;
while r + 3u32 * PLANE_DIM < end {
let r1 = r + PLANE_DIM;
let r2 = r1 + PLANE_DIM;
let r3 = r2 + PLANE_DIM;
let mut eta0 = log_offset[r as usize];
let mut eta1 = log_offset[r1 as usize];
let mut eta2 = log_offset[r2 as usize];
let mut eta3 = log_offset[r3 as usize];
#[unroll]
for j in 0..nb_cap {
let b = beta[j as usize];
eta0 += design[(r * nb + j) as usize] * b;
eta1 += design[(r1 * nb + j) as usize] * b;
eta2 += design[(r2 * nb + j) as usize] * b;
eta3 += design[(r3 * nb + j) as usize] * b;
}
let t0 = F::exp(eta0 + log_w_s) + gamma;
let t1 = F::exp(eta1 + log_w_s) + gamma;
let t2 = F::exp(eta2 + log_w_s) + gamma;
let t3 = F::exp(eta3 + log_w_s) + gamma;
phil_lane += F::ln(t0 * t1) + F::ln(t2 * t3);
r += 4u32 * PLANE_DIM;
}
while r < end {
let mut eta = log_offset[r as usize];
#[unroll]
for j in 0..nb_cap {
eta += design[(r * nb + j) as usize] * beta[j as usize];
}
phil_lane += F::ln(F::exp(eta + log_w_s) + gamma);
r += PLANE_DIM;
}
let mut p = sp_lo + lane;
while p < sp_hi {
let c = cells[p as usize];
let y = counts[p as usize];
let mut eta = log_offset[c as usize];
#[unroll]
for j in 0..nb_cap {
eta += design[(c * nb + j) as usize] * beta[j as usize];
}
lin_lane += eta * y;
weighted_lane += y * F::ln(F::exp(eta + log_w_s) + gamma);
p += PLANE_DIM;
}
linear += plane_sum(lin_lane);
phil += plane_sum(phil_lane);
weighted += plane_sum(weighted_lane);
s += 1u32;
}
let mut acc = linear + subject_term;
acc -= gamma * phil;
acc -= weighted;
acc + (alpha * sum_log_w - lambda * sum_w)
}
#[cube]
#[allow(clippy::too_many_arguments)]
fn moment_step<F: Float>(
x: &Array<F>,
anchor: &Array<F>,
d: F,
w: F,
resid: &mut F,
db_lane: &mut Array<F>,
weight: &mut F,
first: &mut Array<F>,
spread: &mut Array<F>,
delta: &mut Array<F>,
#[comptime] nb_cap: u32,
) {
*resid += d;
*weight += w;
#[unroll]
for j in 0..nb_cap {
db_lane[j as usize] += x[j as usize] * d;
delta[j as usize] = x[j as usize] - anchor[j as usize];
}
#[unroll]
for a in 0..nb_cap {
let wa = w * delta[a as usize];
first[a as usize] += wa;
#[unroll]
for b in a..nb_cap {
spread[(a * nb_cap + b) as usize] += wa * delta[b as usize];
}
}
}
#[cube]
fn ldlt_solve<F: Float>(a: &mut Array<F>, b: &mut Array<F>, n: u32, #[comptime] n_cap: u32) {
let zero = F::new(0.0_f32);
let tiny = F::new(f32::MIN_POSITIVE);
let mut perm = Array::<u32>::new(n_cap as usize);
let mut tmp = Array::<F>::new(n_cap as usize);
let mut step = 0u32;
while step < n {
let mut pivot = step;
let mut best = F::abs(a[(step * n + step) as usize]);
let mut i = step + 1u32;
while i < n {
let v = F::abs(a[(i * n + i) as usize]);
if v > best {
best = v;
pivot = i;
}
i += 1u32;
}
perm[step as usize] = pivot;
if pivot != step {
let mut j = 0u32;
while j < step {
let t = a[(step * n + j) as usize];
a[(step * n + j) as usize] = a[(pivot * n + j) as usize];
a[(pivot * n + j) as usize] = t;
j += 1u32;
}
i = pivot + 1u32;
while i < n {
let t = a[(i * n + step) as usize];
a[(i * n + step) as usize] = a[(i * n + pivot) as usize];
a[(i * n + pivot) as usize] = t;
i += 1u32;
}
let t = a[(step * n + step) as usize];
a[(step * n + step) as usize] = a[(pivot * n + pivot) as usize];
a[(pivot * n + pivot) as usize] = t;
i = step + 1u32;
while i < pivot {
let t2 = a[(i * n + step) as usize];
a[(i * n + step) as usize] = a[(pivot * n + i) as usize];
a[(pivot * n + i) as usize] = t2;
i += 1u32;
}
}
if step > 0u32 {
let mut j = 0u32;
while j < step {
tmp[j as usize] = a[(j * n + j) as usize] * a[(step * n + j) as usize];
j += 1u32;
}
let mut acc = zero;
j = 0u32;
while j < step {
acc += a[(step * n + j) as usize] * tmp[j as usize];
j += 1u32;
}
a[(step * n + step) as usize] -= acc;
i = step + 1u32;
while i < n {
let mut acc2 = zero;
j = 0u32;
while j < step {
acc2 += a[(i * n + j) as usize] * tmp[j as usize];
j += 1u32;
}
a[(i * n + step) as usize] -= acc2;
i += 1u32;
}
}
let d = a[(step * n + step) as usize];
if d != zero {
i = step + 1u32;
while i < n {
a[(i * n + step) as usize] /= d;
i += 1u32;
}
}
step += 1u32;
}
step = 0u32;
while step < n {
let pv = perm[step as usize];
let t = b[step as usize];
b[step as usize] = b[pv as usize];
b[pv as usize] = t;
step += 1u32;
}
let mut row = 0u32;
while row < n {
let mut acc = b[row as usize];
let mut j = 0u32;
while j < row {
acc -= a[(row * n + j) as usize] * b[j as usize];
j += 1u32;
}
b[row as usize] = acc;
row += 1u32;
}
row = 0u32;
while row < n {
let d = a[(row * n + row) as usize];
if F::abs(d) > tiny {
b[row as usize] /= d;
} else {
b[row as usize] = zero;
}
row += 1u32;
}
row = n;
while row > 0u32 {
row -= 1u32;
let mut acc = b[row as usize];
let mut j = row + 1u32;
while j < n {
acc -= a[(j * n + row) as usize] * b[j as usize];
j += 1u32;
}
b[row as usize] = acc;
}
step = n;
while step > 0u32 {
step -= 1u32;
let pv = perm[step as usize];
let t = b[step as usize];
b[step as usize] = b[pv as usize];
b[pv as usize] = t;
}
}
#[allow(clippy::too_many_arguments)]
pub fn launch_opt_pml<F>(
tensors: &PmlGpuTensors<F>,
n_genes: usize,
n_req: usize,
k: usize,
nb: usize,
max_iter: u32,
max_backtrack: u32,
final_assembly: bool,
client: &Client,
) -> Result<(), EdgeErrors>
where
F: Float + cubecl::CubeElement,
{
if nb == 0 || nb > MAX_BETA_CAP {
return Err(EdgeErrors::InvalidArgument(format!(
"The GPU NEBULA path is compiled for designs of one to {MAX_BETA_CAP} columns; got {nb}."
)));
}
if n_req == 0 {
return Ok(());
}
let limits = GpuLimits::from_client(client);
let cube_width = PLANES_PER_CUBE * limits.plane_size_max;
if limits.plane_size_max == 0 || cube_width > limits.max_units_per_cube {
return Err(EdgeErrors::Gpu(format!(
"The GPU NEBULA kernel needs plane operations; this device reports planes of {} to {} lanes and at most {} units per cube.",
limits.plane_size_min, limits.plane_size_max, limits.max_units_per_cube
)));
}
let blocks = (n_req as u32).div_ceil(PLANES_PER_CUBE);
let (gx, gy) = grid_2d(blocks, &limits).map_err(|e| EdgeErrors::Gpu(e.to_string()))?;
let count = checked_cube_count("opt_pml_gpu", gx, gy, 1, &limits)
.map_err(|e| EdgeErrors::Gpu(e.to_string()))?;
macro_rules! dispatch {
($cap:expr) => {
unsafe {
debug_assert_eq!(nb, $cap as usize);
opt_pml_gpu::launch_unchecked::<F>(
client,
count,
CubeDim::new_1d(cube_width),
tensors.design.clone().into_tensor_arg(),
tensors.log_offset.clone().into_tensor_arg(),
tensors.subject_start.clone().into_tensor_arg(),
tensors.counts.clone().into_tensor_arg(),
tensors.cells.clone().into_tensor_arg(),
tensors.subject_ptr.clone().into_tensor_arg(),
tensors.subject_total.clone().into_tensor_arg(),
tensors.subject_mean.clone().into_tensor_arg(),
tensors.request_gene.clone().into_tensor_arg(),
tensors.request_params.clone().into_tensor_arg(),
tensors.beta_init.clone().into_tensor_arg(),
tensors.log_w_init.clone().into_tensor_arg(),
tensors.tolerance.clone().into_tensor_arg(),
tensors.subject_scratch.clone().into_tensor_arg(),
tensors.vwb_scratch.clone().into_tensor_arg(),
tensors.out.clone().into_tensor_arg(),
n_genes as u32,
n_req as u32,
k as u32,
nb as u32,
max_iter,
max_backtrack,
u32::from(final_assembly),
$cap,
);
}
};
}
match nb {
1 => dispatch!(1),
2 => dispatch!(2),
3 => dispatch!(3),
4 => dispatch!(4),
5 => dispatch!(5),
6 => dispatch!(6),
7 => dispatch!(7),
_ => dispatch!(8),
}
Ok(())
}
pub struct PmlGpuTensors<F: cubecl::CubeElement + Numeric> {
pub design: GpuTensor<F>,
pub log_offset: GpuTensor<F>,
pub subject_start: GpuTensor<u32>,
pub counts: GpuTensor<F>,
pub cells: GpuTensor<u32>,
pub subject_ptr: GpuTensor<u32>,
pub subject_total: GpuTensor<F>,
pub subject_mean: GpuTensor<F>,
pub request_gene: GpuTensor<u32>,
pub request_params: GpuTensor<F>,
pub beta_init: GpuTensor<F>,
pub log_w_init: GpuTensor<F>,
pub tolerance: GpuTensor<F>,
pub subject_scratch: GpuTensor<F>,
pub vwb_scratch: GpuTensor<F>,
pub out: GpuTensor<F>,
}