use cubecl::prelude::*;
use cubecl::std::tensor::layout::CoordsDyn;
use crate::{Axis, Coords, Integer, IntegerExpand, IntegerSeq, IntegerSeqExpand, Projection};
#[derive(CubeType, Clone)]
#[expand(derive(Clone))]
pub(crate) struct RuntimeMap {
pub(crate) coefficients: Coords<u32>,
pub(crate) residues: Coords<u32>,
}
#[cube]
impl RuntimeMap {
pub(crate) fn integral(#[comptime] physical_rank: usize) -> RuntimeMap {
RuntimeMap {
coefficients: Coords::<u32>::new(),
residues: Coords::constant(comptime!(vec![0; physical_rank])),
}
}
pub(crate) fn stored(&self) -> RuntimeMap {
RuntimeMap {
coefficients: self.coefficients.stored(),
residues: self.residues.stored(),
}
}
pub(crate) fn store_from(&mut self, src: &RuntimeMap) {
self.coefficients.store_from(&src.coefficients);
self.residues.store_from(&src.residues);
}
}
#[cube]
pub(crate) fn logical_extent(
#[comptime] projection: Projection,
physical_shape: &Coords<u32>,
) -> Coords<u32> {
let mut bound = Coords::<u32>::new();
#[unroll]
for i in 0..comptime!(projection.logical_rank()) {
let picks = comptime!(projection.carriers(projection.logical_axes()[i]).to_vec());
bound.push(physical_shape.product(picks));
}
bound
}
#[cube]
pub(crate) fn step_offset(
#[comptime] projection: Projection,
#[comptime] axis: Axis,
#[comptime] edge: usize,
physical_shape: &Coords<u32>,
strides: &Coords<u32>,
) -> u32 {
let carriers = comptime!(projection.carriers(axis));
let picks = comptime!((0..carriers.len()).collect::<Vec<_>>());
let mut parts = Sequence::<u32>::new();
#[unroll]
for k in 0..comptime!(carriers.len()) {
let pa = comptime!(carriers[k]);
let (finer, modulo) = comptime!(projection.digit(pa, axis));
let scale = comptime!(projection.scale(pa, axis) as u32);
let quot = comptime!(edge as u32)
.runtime()
.divided_by(physical_shape.product(comptime!(finer.to_vec())));
let digit = match comptime!(modulo) {
Some(m) => quot.remainder(physical_shape.at(m)),
None => quot,
};
parts.push(
digit
.times(comptime!(scale).runtime())
.times(strides.at(pa)),
);
}
parts.sum(picks)
}
#[cube]
pub(crate) fn fold_physical(
#[comptime] projection: Projection,
digits: &Coords<u32>,
physical_shape: &Coords<u32>,
) -> CoordsDyn {
comptime!(assert!(
projection.is_invertible(),
"Projection::fold_physical: not invertible (an affine term mixes several logical \
coordinates into one physical cell)"
));
let mut out = CoordsDyn::new();
#[unroll]
for i in 0..comptime!(projection.logical_rank()) {
let axis = comptime!(projection.logical_axes()[i]);
let carriers = comptime!(projection.carriers(axis));
let picks = comptime!((0..carriers.len()).collect::<Vec<_>>());
let mut parts = Sequence::<u32>::new();
#[unroll]
for k in 0..comptime!(carriers.len()) {
let pa = comptime!(carriers[k]);
let (finer, _) = comptime!(projection.digit(pa, axis));
parts.push(
digits
.at(pa)
.times(physical_shape.product(comptime!(finer.to_vec()))),
);
}
out.push(parts.sum(picks));
}
out
}
#[cube]
pub(crate) fn line_digit(x: u32, shape: &Coords<u32>, #[comptime] j: usize) -> u32 {
let plen = shape.len();
x.divided_by(shape.product(comptime!(((j + 1)..plen).collect::<Vec<_>>())))
.remainder(shape.at(j))
}
#[cfg(test)]
mod tests {
use super::*;
const A: Axis = Axis(0);
#[test]
fn fold_physical_digits_invert_to_source_pos_digits() {
let shape = [3usize, 8];
let p = Projection::new(
&[A],
&[crate::PhysicalAxisMap::of(A), crate::PhysicalAxisMap::of(A)],
);
assert!(p.is_invertible());
let block = |pa| {
p.digit(pa, A)
.0
.iter()
.map(|&q| shape[q])
.product::<usize>()
};
for coord in [0usize, 1, 7, 8, 9, 19, 23] {
let digit = |pa| match p.digit(pa, A).1 {
Some(m) => (coord / block(pa)) % shape[m],
None => coord / block(pa),
};
assert_eq!(digit(0) * block(0) + digit(1) * block(1), coord);
}
}
}