use cubecl::zspace::{Shape, Strides};
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub enum StridedLayout {
#[default]
RowMajor,
ColMajor,
Explicit(Vec<usize>),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum LayoutSpec {
Strided(StridedLayout),
}
impl Default for LayoutSpec {
fn default() -> Self {
Self::Strided(StridedLayout::default())
}
}
impl LayoutSpec {
pub fn base(&self) -> &StridedLayout {
match self {
LayoutSpec::Strided(base) => base,
}
}
pub fn compute_strides(&self, shape: &Shape) -> Strides {
self.base().compute_strides(shape)
}
}
impl From<StridedLayout> for LayoutSpec {
fn from(base: StridedLayout) -> Self {
Self::Strided(base)
}
}
pub fn physical_extent(shape: &Shape, strides: &Strides) -> usize {
let mut max_offset = 0usize;
for (s, d) in strides.iter().zip(shape.iter()) {
if *d > 0 && *s > 0 {
max_offset += (d - 1) * s;
}
}
max_offset + 1
}
impl StridedLayout {
pub fn compute_strides(&self, shape: &Shape) -> Strides {
let n = shape.len();
match self {
StridedLayout::RowMajor => {
assert!(n >= 1, "RowMajor requires at least 1 dimension");
let mut strides = vec![0; n];
strides[n - 1] = 1;
for i in (0..n - 1).rev() {
strides[i] = strides[i + 1] * shape[i + 1];
}
Strides::new(&strides)
}
StridedLayout::ColMajor => {
assert!(n >= 2, "ColMajor requires at least 2 dimensions");
let mut strides = vec![0; n];
strides[n - 2] = 1;
strides[n - 1] = shape[n - 2];
for i in (0..n - 2).rev() {
strides[i] = strides[i + 1] * shape[i + 1];
}
Strides::new(&strides)
}
StridedLayout::Explicit(strides) => {
assert!(
strides.len() == n,
"Explicit strides must have the same rank as the shape"
);
strides.clone().into()
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn physical_extent_contiguous_row_major() {
let shape = Shape::from(vec![2, 3]);
let strides = Strides::new(&[3, 1]);
assert_eq!(physical_extent(&shape, &strides), 6);
}
#[test]
fn physical_extent_jumpy_strides_exceed_logical() {
let shape = Shape::from(vec![256, 256]);
let strides = Strides::new(&[512, 1]);
assert_eq!(physical_extent(&shape, &strides), 130816);
assert!(physical_extent(&shape, &strides) > 256 * 256);
}
#[test]
fn physical_extent_broadcast_strides_undercount_logical() {
let shape = Shape::from(vec![4, 3]);
let strides = Strides::new(&[0, 1]);
assert_eq!(physical_extent(&shape, &strides), 3);
assert!(physical_extent(&shape, &strides) < 4 * 3);
}
}