use super::{Level, Partitioning, Space};
use crate::{
Axis, ComputeScope, Coords, Coverage, Integer, IntegerExpand, MatrixAxes, Path, Step, Walk,
};
use cubecl::{prelude::*, unexpanded};
#[derive(CubeType, Clone)]
#[expand(derive(Clone))]
pub struct Region {
step_coords: Sequence<Coords<u32>>,
sizes: Sequence<usize>,
#[cube(comptime)]
pub(crate) path: Path,
}
#[cube]
impl Region {
pub(crate) fn new(
step_coords: Sequence<Coords<u32>>,
sizes: Sequence<usize>,
#[comptime] path: Path,
) -> Region {
Region {
step_coords,
sizes,
path,
}
}
pub(crate) fn root(partitioning: &Partitioning) -> Region {
Region::rooted(
&partitioning.space,
comptime!(partitioning.levels.clone()),
0usize,
)
}
pub(crate) fn rooted(
space: &Space,
#[comptime] levels: Vec<Level>,
#[comptime] depth: usize,
) -> Region {
Region::new(
Sequence::new(),
space.sizes.clone(),
comptime!(Path::new(
depth,
Partitioning::new(space.clone(), levels),
Vec::new()
)),
)
}
pub fn walk(&self) -> Walk {
Walk::of(&self.child(), comptime!(self.path.next()), self.clone())
}
pub fn leaves(&self) -> Leaves {
Leaves {
region: self.clone(),
}
}
pub fn planes(&self) -> Planes {
Planes {
region: self.clone(),
}
}
pub fn trailing(
#[comptime] depth: usize,
#[comptime] space: Space,
#[comptime] level: Level,
c0: usize,
c1: usize,
) -> Region {
let edges = comptime!(MatrixAxes::edges(&space));
Region::cell(depth, space, level, edges, c0, c1)
}
pub(crate) fn cell(
#[comptime] depth: usize,
#[comptime] space: Space,
#[comptime] level: Level,
#[comptime] edges: MatrixAxes,
c0: usize,
c1: usize,
) -> Region {
let rank = comptime!(space.rank());
let mut coords = Coords::<u32>::new();
#[unroll]
for p in 0..rank {
let c = if comptime!(p == edges.row_split) {
c0.cast::<u32>()
} else if comptime!(p == edges.col_split) {
c1.cast::<u32>()
} else {
0u32.runtime()
};
coords.push(c);
}
let mut step_coords = Sequence::<Coords<u32>>::new();
step_coords.push(coords);
Region::new(
step_coords,
Sequence::new(),
comptime!(Path::new(
depth,
Partitioning::new(space.clone(), Vec::new()),
vec![level]
)),
)
}
pub(crate) fn step(&self, #[comptime] i: usize) -> Step {
Step::new(
self.step_coords.index(i).clone(),
comptime!(self.path.space_at(i)),
comptime!(self.path.level(i).clone()),
comptime!(self.path.depth() - self.path.len() + i),
)
}
pub(crate) fn child(&self) -> Space {
Space::with_sizes(comptime!(self.path.child()), self.sizes.clone())
}
pub fn coord(&self, #[comptime] axis: Axis) -> usize {
let last = comptime!(self.path.len() - 1);
self.step(last).coord(axis)
}
pub fn origin(&self, #[comptime] axis: Axis) -> usize {
let mut origin = 0usize;
#[unroll]
for i in 0..comptime!(self.path.len()) {
if let Some(tile) = comptime!(self.path.level(i).tile(axis)) {
origin += self.step(i).coord(axis) * tile;
}
}
origin
}
pub(crate) fn below(&self, coords: Coords<u32>, #[comptime] level: Level) -> Region {
let n = comptime!(self.step_coords.len());
let mut step_coords = Sequence::<Coords<u32>>::new();
#[unroll]
for i in 0..n {
step_coords.push(self.step_coords.index(i).clone());
}
step_coords.push(coords);
Region::new(
step_coords,
self.sizes.clone(),
comptime!(self.path.below(level)),
)
}
pub fn over(&self, #[comptime] level: &Level) -> Walk {
Walk::of(&self.child(), comptime!(level.clone()), self.clone())
}
}
impl IntoIterator for Region {
type Item = Region;
type IntoIter = std::vec::IntoIter<Region>;
fn into_iter(self) -> Self::IntoIter {
unexpanded!()
}
}
impl IntoIterator for &Region {
type Item = Region;
type IntoIter = std::vec::IntoIter<Region>;
fn into_iter(self) -> Self::IntoIter {
unexpanded!()
}
}
impl Iterable for RegionExpand {
type Item = RegionExpand;
fn expand(self, scope: &Scope, body: &mut dyn FnMut(&Scope, RegionExpand)) {
let walk = self.__expand_walk_method(scope);
if walk.const_len() == Some(1) {
walk.expand_unroll(scope, body)
} else {
walk.expand(scope, body)
}
}
fn expand_unroll(self, scope: &Scope, body: &mut dyn FnMut(&Scope, RegionExpand)) {
self.__expand_walk_method(scope).expand_unroll(scope, body)
}
}
impl Iterable for &RegionExpand {
type Item = RegionExpand;
fn expand(self, scope: &Scope, body: &mut dyn FnMut(&Scope, RegionExpand)) {
self.clone().expand(scope, body)
}
fn expand_unroll(self, scope: &Scope, body: &mut dyn FnMut(&Scope, RegionExpand)) {
self.clone().expand_unroll(scope, body)
}
}
#[derive(CubeType, Clone)]
#[expand(derive(Clone))]
pub struct Leaves {
#[allow(dead_code)] region: Region,
}
impl IntoIterator for Leaves {
type Item = Region;
type IntoIter = std::vec::IntoIter<Region>;
fn into_iter(self) -> Self::IntoIter {
unexpanded!()
}
}
impl Iterable for LeavesExpand {
type Item = RegionExpand;
fn expand(self, scope: &Scope, body: &mut dyn FnMut(&Scope, RegionExpand)) {
self.region.each_leaf(scope, body, false);
}
fn expand_unroll(self, scope: &Scope, body: &mut dyn FnMut(&Scope, RegionExpand)) {
self.region.each_leaf(scope, body, true);
}
}
#[derive(CubeType, Clone)]
#[expand(derive(Clone))]
pub struct Planes {
#[allow(dead_code)] region: Region,
}
impl IntoIterator for Planes {
type Item = Region;
type IntoIter = std::vec::IntoIter<Region>;
fn into_iter(self) -> Self::IntoIter {
unexpanded!()
}
}
impl Iterable for PlanesExpand {
type Item = RegionExpand;
fn expand(self, scope: &Scope, body: &mut dyn FnMut(&Scope, RegionExpand)) {
self.region.each_plane(scope, body, false);
}
fn expand_unroll(self, scope: &Scope, body: &mut dyn FnMut(&Scope, RegionExpand)) {
self.region.each_plane(scope, body, true);
}
}
impl RegionExpand {
fn each_plane(self, scope: &Scope, body: &mut dyn FnMut(&Scope, RegionExpand), unroll: bool) {
match self.path.next().coverage() {
Coverage::Distribute(ComputeScope::Plane) => match unroll {
true => self.expand_unroll(scope, body),
false => self.expand(scope, body),
},
Coverage::Walk => {
let first = self
.__expand_walk_method(scope)
.__expand_region_method(scope, 0usize.into_expand(scope));
first.each_plane(scope, body, unroll)
}
Coverage::Distribute(other) => panic!(
"Region::planes: a level dealt to each {other:?} lies below this region before any \
plane level, so this region has no planes of its own"
),
}
}
fn each_leaf(self, scope: &Scope, body: &mut dyn FnMut(&Scope, RegionExpand), unroll: bool) {
if self.path.at_bottom() {
body(scope, self);
return;
}
let mut below = |scope: &Scope, child: RegionExpand| child.each_leaf(scope, body, unroll);
match unroll {
true => self.expand_unroll(scope, &mut below),
false => self.expand(scope, &mut below),
}
}
}