use cubecl::prelude::*;
use crate::*;
pub const LOGIT_MASKED: f32 = -6e4;
pub(crate) const FULLY_MASKED_ROW_THRESHOLD: f32 = 1e-4;
#[cube]
pub fn masked_recip<E: Float>(l: E) -> E {
let eps = E::new(FULLY_MASKED_ROW_THRESHOLD);
E::cast_from(l >= eps) * clamp_min(l, eps).recip()
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum RowShare {
Unit { rows: usize },
Plane { rows: usize, units: usize },
}
impl RowShare {
pub fn rows(&self) -> usize {
match self {
RowShare::Unit { rows } | RowShare::Plane { rows, .. } => *rows,
}
}
pub fn units(&self) -> usize {
match self {
RowShare::Unit { .. } => 1,
RowShare::Plane {
units: plane_units, ..
} => *plane_units,
}
}
}
#[cube]
pub(crate) fn owned_unit(#[comptime] share: RowShare) -> usize {
match comptime!(share) {
RowShare::Unit { rows: _ } => 0usize,
RowShare::Plane {
rows: _,
units: plane_units,
} => UNIT_POS_X as usize % plane_units,
}
}
#[derive(CubeType)]
pub struct RowState<E: Float> {
pub m: Array<E>,
pub l: Array<E>,
#[cube(comptime)]
pub space: Space,
#[cube(comptime)]
pub share: RowShare,
pub team: TeamUnit,
}
#[cube]
impl<E: Float> RowState<E> {
pub fn new(#[comptime] space: Space, #[comptime] units: usize) -> RowState<E> {
let rows = comptime!(space.cells().div_ceil(units));
RowState::<E>::of(space, comptime!(RowShare::Unit { rows }))
}
pub fn over_plane(#[comptime] space: Space, #[comptime] plane_units: usize) -> RowState<E> {
let rows = comptime!(space.cells());
RowState::<E>::of(
space,
comptime!(RowShare::Plane {
rows,
units: plane_units
}),
)
}
pub fn of(#[comptime] space: Space, #[comptime] share: RowShare) -> RowState<E> {
RowState::<E>::in_team(space, share, &TeamUnit::along_x())
}
pub fn in_team(
#[comptime] space: Space,
#[comptime] share: RowShare,
team: &TeamUnit,
) -> RowState<E> {
let rows = comptime!(share.rows());
let mut m = Array::new(rows);
let mut l = Array::new(rows);
for i in 0..rows {
m[i] = E::min_value();
l[i] = E::from_int(0);
}
RowState::<E> {
m,
l,
space,
share,
team: team.clone(),
}
}
pub(crate) fn owned_row(&self, ri: usize) -> usize {
match comptime!(self.share) {
RowShare::Unit { rows } => self.team.index * rows + ri,
RowShare::Plane { rows: _, units: _ } => ri,
}
}
pub fn update(&mut self, max_buf: &Array<E>, sum_buf: &Array<E>) -> Array<E> {
let rows = comptime!(self.share.rows());
let mut corr = Array::new(rows);
for i in 0..rows {
corr[i] = (self.m[i] - max_buf[i]).exp();
self.l[i] = corr[i] * self.l[i] + sum_buf[i];
self.m[i] = max_buf[i];
}
corr
}
pub fn lse(&self, i: usize) -> E {
self.m[i] + E::ln(self.l[i])
}
pub fn recip_l(&self, i: usize) -> E {
masked_recip::<E>(self.l[i])
}
}
#[derive(CubeType)]
pub struct MaskProbe {
pub origin_q: usize,
pub row_origin: usize,
pub origin_s: usize,
pub bound_q: usize,
pub bound_s: usize,
#[cube(comptime)]
pub q_rows: usize,
#[cube(comptime)]
pub causal: bool,
#[cube(comptime)]
pub materialized: bool,
}
#[cube]
impl MaskProbe {
pub fn masked(&self, q: usize, s: usize, mask: &Tile<u32>) -> bool {
let mut masked = q >= self.bound_q || s >= self.bound_s;
if comptime!(self.causal) {
masked = masked || s > q;
}
if comptime!(self.materialized) {
let size!(W) = mask.vector_size();
let rank = comptime!(mask.place.space.rank());
let cols = mask.runtime_extent(comptime!(mask.place.space.axis_at(rank - 1)));
masked = masked || mask.flat::<W>().read(q * cols + s).extract(0usize) != 0;
}
masked
}
pub fn keys(&self) -> usize {
let mut keys = self.bound_s;
if comptime!(self.causal) {
keys = keys.min_with(self.origin_q.plus(comptime!(self.q_rows).runtime()));
}
keys
}
pub fn blocks(&self, #[comptime] block: usize) -> usize {
self.keys().div_ceil(block)
}
pub(crate) fn row_q(&self, r: usize) -> usize {
let q_rows = comptime!(self.q_rows);
self.origin_q + (self.row_origin + r) % q_rows
}
pub fn step_s(&self, offset: usize) -> MaskProbe {
MaskProbe {
origin_q: self.origin_q,
row_origin: self.row_origin,
origin_s: self.origin_s + offset,
bound_q: self.bound_q,
bound_s: self.bound_s,
q_rows: comptime!(self.q_rows),
causal: comptime!(self.causal),
materialized: comptime!(self.materialized),
}
}
}