use cubecl::prelude::*;
use crate::*;
define_size!(pub(crate) RA);
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub(crate) enum Accumulation {
Contraction(Semiring),
Reduction(Monoid),
}
impl Accumulation {
pub(crate) fn monoid(self) -> Monoid {
match self {
Accumulation::Contraction(semiring) => semiring.add(),
Accumulation::Reduction(monoid) => monoid,
}
}
pub(crate) fn semiring(self, site: &str) -> Semiring {
match self {
Accumulation::Contraction(semiring) => semiring,
Accumulation::Reduction(monoid) => {
panic!("{site}: a block opened to reduce under {monoid:?} contracts nothing")
}
}
}
}
#[derive(CubeType, Clone)]
#[expand(derive(Clone))]
pub(crate) struct RegisterData<T: Numeric> {
pub(crate) data: Array<Vector<T, RA>>,
#[cube(comptime)]
pub(crate) vector_size: usize,
#[cube(comptime)]
pub(crate) fold: usize,
#[cube(comptime)]
pub(crate) mr: usize,
#[cube(comptime)]
pub(crate) nr: usize,
#[cube(comptime)]
pub(crate) axes: MatrixAxes,
#[cube(comptime)]
pub(crate) config: RegisterBlock,
#[cube(comptime)]
pub(crate) accumulation: Accumulation,
}
#[cube]
fn register_block_size(#[comptime] vector_size: usize) {
intrinsic!(|scope| {
scope.register_size::<RA>(vector_size);
});
}
#[cube]
impl<T: Numeric> RegisterData<T> {
#[allow(clippy::too_many_arguments)]
pub(crate) fn alloc(
#[comptime] m: usize,
#[comptime] n: usize,
#[comptime] axes: MatrixAxes,
#[comptime] vector_size: usize,
#[comptime] fold: usize,
#[comptime] config: RegisterBlock,
#[comptime] accumulation: Accumulation,
) -> RegisterData<T> {
comptime!(assert!(
fold == 1 || vector_size == 1,
"RegisterData::alloc: a block contracting {fold} values of a cell a step sums them into \
the cell, so it is one value wide, not {vector_size}"
));
comptime!(assert!(
vector_size > 0 && n.is_multiple_of(vector_size),
"RegisterData::alloc: n ({n}) must be a whole number of {vector_size}-wide lines"
));
register_block_size(vector_size);
let nr = comptime!(n / vector_size);
RegisterData::<T> {
data: Array::<Vector<T, RA>>::new(comptime!(m * nr)),
vector_size,
fold,
mr: m,
nr,
axes,
config,
accumulation,
}
}
pub(crate) fn zero(&mut self) {
self.init(T::from_int(0));
}
pub(crate) fn init(&mut self, val: T) {
let count = comptime!(self.mr * self.nr);
#[allow(clippy::needless_range_loop)]
#[unroll]
for i in 0..count {
self.data[i] = Vector::<T, RA>::cast_from(val);
}
}
pub(crate) fn add_cast_from<S: Numeric>(&mut self, src: &RegisterData<S>) {
comptime!(assert!(
self.mr == src.mr && self.nr == src.nr && self.fold == src.fold,
"RegisterData::add_cast_from: the blocks are {}x{} fold {} and {}x{} fold {}; a \
promotion adds line for line and both are opened against one sink",
self.mr,
self.nr,
self.fold,
src.mr,
src.nr,
src.fold
));
let count = comptime!(self.mr * self.nr);
#[allow(clippy::needless_range_loop)]
#[unroll]
for i in 0..count {
self.data[i] += Vector::<T, RA>::cast_from(src.data[i]);
}
}
pub(crate) fn mul_along(&mut self, factors: &Array<T>, #[comptime] first: usize) {
#[unroll]
for r in 0..self.mr {
let factor = Vector::<T, RA>::cast_from(factors[first + r]);
#[unroll]
for n in 0..self.nr {
self.data[r * self.nr + n] *= factor;
}
}
}
pub(crate) fn scale(&mut self, factor: T) {
let count = comptime!(self.mr * self.nr);
let line = Vector::<T, RA>::cast_from(factor);
#[allow(clippy::needless_range_loop)]
#[unroll]
for i in 0..count {
self.data[i] *= line;
}
}
}
#[cube]
impl<T: Numeric> RegisterData<T> {
pub(crate) fn store_cast_window<Out: Numeric>(
&self,
mem: &mut Memory<Out>,
#[comptime] space: Space,
) {
self.drain::<Out, RA>(mem, space);
}
fn drain<Out: Numeric, A: Size>(&self, mem: &mut Memory<Out>, #[comptime] space: Space) {
let mem_write = comptime!(mem.access.write);
let unit_share = comptime!(mem.unit_share);
let monoid = comptime!(self.accumulation.monoid());
let mut sink = mem.matrix_mut::<A>(0usize, comptime!(self.axes), space);
match comptime!(Drain::of(unit_share, mem_write)) {
Drain::EachUnit =>
{
#[unroll]
for i in 0..comptime!(self.mr) {
#[unroll]
for n in 0..comptime!(self.nr) {
let cell =
Vector::<Out, A>::cast_from(self.data[comptime!(i * self.nr + n)]);
sink.write(((i as u32).runtime(), (n as u32).runtime()), cell);
}
}
}
Drain::UnitZero =>
{
#[unroll]
for i in 0..comptime!(self.mr) {
#[unroll]
for n in 0..comptime!(self.nr) {
let cell =
Vector::<Out, A>::cast_from(self.data[comptime!(i * self.nr + n)]);
if UNIT_POS_X == 0 {
sink.write(((i as u32).runtime(), (n as u32).runtime()), cell);
}
}
}
}
Drain::PlaneFold =>
{
#[unroll]
for i in 0..comptime!(self.mr) {
#[unroll]
for n in 0..comptime!(self.nr) {
let combined = UnitShare::Plane
.reduce::<Vector<T, RA>>(self.data[comptime!(i * self.nr + n)], monoid);
let cell = Vector::<Out, A>::cast_from(combined);
if UNIT_POS_X == 0 {
sink.write(((i as u32).runtime(), (n as u32).runtime()), cell);
}
}
}
}
Drain::GroupFold { unit_bits } =>
{
#[unroll]
for i in 0..comptime!(self.mr) {
#[unroll]
for n in 0..comptime!(self.nr) {
let combined = comptime!(UnitShare::Group { unit_bits })
.reduce::<Vector<T, RA>>(self.data[comptime!(i * self.nr + n)], monoid);
let cell = Vector::<Out, A>::cast_from(combined);
let unit_in_group = UNIT_POS_X & comptime!(unit_bits as u32);
if unit_in_group == 0 {
sink.write(((i as u32).runtime(), (n as u32).runtime()), cell);
}
}
}
}
}
}
}
#[cube]
impl<E: Float> RegisterData<E> {
pub(crate) fn scale_add_along(
&mut self,
scale: E,
bias: &Procedural<E>,
#[comptime] space: Space,
) {
#[unroll]
for r in 0..self.mr {
#[unroll]
for n in 0..self.nr {
let at = r * self.nr + n;
let mut line = self.data[at];
#[unroll]
for j in 0..self.vector_size {
let column = n * self.vector_size + j;
let cell = bias.value_at(r as u32, column as u32, space.clone());
line.insert(j, line.extract(j) * scale + cell);
}
self.data[at] = line;
}
}
}
pub(crate) fn maxima_along(&self, seed: &Array<E>) -> Array<E> {
let mut maxima = Array::<E>::new(self.mr);
#[unroll]
for r in 0..self.mr {
let mut slice = seed[r];
#[unroll]
for n in 0..self.nr {
let line = self.data[r * self.nr + n];
#[unroll]
for j in 0..self.vector_size {
slice = max(slice, line.extract(j));
}
}
maxima[r] = slice;
}
maxima
}
pub(crate) fn exp_minus_along(&mut self, slices: &Array<E>) {
#[unroll]
for r in 0..self.mr {
#[unroll]
for n in 0..self.nr {
let at = r * self.nr + n;
let mut line = self.data[at];
#[unroll]
for j in 0..self.vector_size {
line.insert(
j,
AxisSlices::<E>::exp_minus_cell(line.extract(j), slices[r]),
);
}
self.data[at] = line;
}
}
}
pub(crate) fn sums_along(&self) -> Array<E> {
let mut sums = Array::<E>::new(self.mr);
#[unroll]
for r in 0..self.mr {
let mut slice = E::from_int(0);
#[unroll]
for n in 0..self.nr {
let line = self.data[r * self.nr + n];
#[unroll]
for j in 0..self.vector_size {
slice += line.extract(j);
}
}
sums[r] = slice;
}
sums
}
}