use cubecl::{
prelude::*,
std::tensor::layout::{Coords2d, CoordsDyn, Layout, LayoutExpand},
};
use crate::*;
pub(crate) type MatrixView<'a, T> = Masked<'a, T, Coords2d>;
pub(crate) type MatrixViewMut<'a, T> = MaskedMut<'a, T, Coords2d>;
#[derive(CubeType, Clone)]
#[expand(derive(Clone))]
pub(crate) struct TileMatrix {
batches: Coords<u32>,
row_extents: Coords<u32>,
col_extents: Coords<u32>,
tile_shape: Coords2d,
}
pub(crate) type ProjectedMatrix = Projected<TileMatrix>;
#[cube]
impl TileMatrix {
pub(crate) fn new(
batches: Coords<u32>,
row_extents: Coords<u32>,
col_extents: Coords<u32>,
#[comptime] rows: usize,
#[comptime] cols: usize,
) -> Self {
TileMatrix {
batches,
row_extents,
col_extents,
tile_shape: (rows as u32, cols as u32).runtime(),
}
}
pub(crate) fn batch(
bound: &Coords<u32>,
#[comptime] space: &Space,
#[comptime] composition: Composition,
#[comptime] load: &VectorTile,
#[comptime] axes: MatrixAxes,
i: usize,
) -> Self {
let rank = comptime!(space.rank());
let counts = comptime!(load.counts(space));
let row_edge = comptime!(TileMatrix::innermost_past_one(
space,
axes.row_split..axes.col_split
));
let col_edge = comptime!(TileMatrix::innermost_past_one(space, axes.col_split..rank));
comptime!(assert!(
(0..rank).all(|p| counts[p] == space.extent_at(p)
|| Some(p) == row_edge
|| Some(p) == col_edge),
"TileMatrix::batch: a load of {:?} over {space:?} reaches along an axis that is neither the \
innermost of the matrix's rows nor of its columns",
load.extents()
));
let rows = comptime!(
counts[axes.row_split..axes.col_split]
.iter()
.product::<usize>()
);
let cols = comptime!(counts[axes.col_split..rank].iter().product::<usize>());
let extents = Self::leading_extents(
bound,
comptime!(space),
composition,
comptime!(axes.row_split),
);
Self::new(
extents.unravel(i.cast::<u32>()),
Coords::constant(comptime!(counts[axes.row_split..axes.col_split].to_vec())),
Coords::constant(comptime!(counts[axes.col_split..rank].to_vec())),
rows,
cols,
)
}
pub(crate) fn value_coords(
row: u32,
col: u32,
i: usize,
#[comptime] space: &Space,
#[comptime] axes: MatrixAxes,
#[comptime] vector_size: usize,
) -> Coords<u32> {
let rank = comptime!(space.rank());
let mut coords = Coords::<u32>::new();
let batches = Coords::constant(comptime!(
(0..axes.row_split)
.map(|p| space.extent_at(p))
.collect::<Vec<_>>()
))
.unravel(i.cast::<u32>());
#[unroll]
for p in 0..batches.len() {
coords.push(batches.at(p));
}
let rows = Coords::constant(comptime!(
(axes.row_split..axes.col_split)
.map(|p| space.extent_at(p))
.collect::<Vec<_>>()
))
.unravel(row);
#[unroll]
for p in 0..rows.len() {
coords.push(rows.at(p));
}
let cols = Coords::constant(comptime!(line_extents(
space,
vector_size,
axes.col_split,
rank
)))
.unravel(col);
let n = cols.len();
#[unroll]
for p in 0..n {
if comptime!(p == n - 1) {
coords.push(cols.at(p).times(comptime!(vector_size as u32)));
} else {
coords.push(cols.at(p));
}
}
coords
}
pub(crate) fn whole(
#[comptime] space: &Space,
#[comptime] vector_size: usize,
#[comptime] rows: usize,
#[comptime] cols: usize,
) -> Self {
let rank = comptime!(space.rank());
let split = comptime!(MatrixAxes::whole(space, rows, cols, vector_size).col_split);
Self::new(
Coords::<u32>::new(),
Coords::constant(comptime!(line_extents(space, vector_size, 0, split))),
Coords::constant(comptime!(line_extents(space, vector_size, split, rank))),
rows,
comptime!(cols / vector_size),
)
}
fn leading_extents(
bound: &Coords<u32>,
#[comptime] space: &Space,
#[comptime] composition: Composition,
#[comptime] upto: usize,
) -> Coords<u32> {
let mut out = Coords::<u32>::new();
#[unroll]
for p in 0..upto {
if comptime!(composition == Composition::Overlapping) {
out.push(comptime!(space.extent_at(p) as u32));
} else {
out.push(bound.at(p));
}
}
out
}
}
impl TileMatrix {
fn innermost_past_one(space: &Space, group: core::ops::Range<usize>) -> Option<usize> {
group.rev().find(|&p| space.extent_at(p) > 1)
}
}
#[cube]
impl Layout for TileMatrix {
type Coordinates = Coords2d;
type SourceCoordinates = CoordsDyn;
fn to_source_pos(&self, pos: Self::Coordinates) -> Self::SourceCoordinates {
let (row, col) = pos;
let mut coords = self.batches.clone();
coords.extend(&self.row_extents.unravel(row));
coords.extend(&self.col_extents.unravel(col));
coords.to_dyn()
}
fn to_source_pos_checked(&self, pos: Self::Coordinates) -> (Self::SourceCoordinates, bool) {
let in_bounds = self.is_in_bounds(pos);
(self.to_source_pos(pos), in_bounds)
}
fn shape(&self) -> Self::Coordinates {
self.tile_shape
}
fn is_in_bounds(&self, pos: Self::Coordinates) -> bool {
let (row, col) = pos;
let (rows, cols) = self.tile_shape;
row < rows && col < cols
}
}
#[cube]
impl<T: Numeric> Tile<T> {
pub(crate) fn matrix_packed<W: Size>(
&self,
#[comptime] axes: MatrixAxes,
i: usize,
) -> MatrixView<'_, Vector<T, W>> {
let g = self.mem("matrix");
let layout = g.batch_matrix(comptime!(self.place.space.clone()), axes, i);
g.packed::<W, Coords2d, ProjectedMatrix>(layout, comptime!(Guard::Checked))
}
pub(crate) fn fragment_matrix_packed<W: Size>(
&self,
#[comptime] rows: usize,
#[comptime] cols: usize,
) -> MatrixView<'_, Vector<T, W>> {
let g = self.mem("fragment_matrix");
let layout = g.whole_matrix(comptime!(self.place.space.clone()), rows, cols);
g.packed::<W, Coords2d, ProjectedMatrix>(layout, comptime!(Guard::Checked))
}
}