use super::fragments::fragments;
use cubecl::tile::Axis;
use cubecl::zspace::metadata::Metadata;
pub fn contiguous(meta: &Metadata, axes: [Axis; 2]) -> Option<Axis> {
let dims = fragments(meta);
let mut lined = axes
.into_iter()
.zip(&dims[dims.len() - 2..])
.filter(|(_, dim)| {
dim.iter()
.any(|&(extent, stride)| extent > 1 && stride == 1)
})
.map(|(axis, _)| axis);
match (lined.next(), lined.next()) {
(Some(axis), None) => Some(axis),
_ => None,
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::matmul_axes::{K, M, N};
use cubecl::zspace::{Shape, Strides, Tiling};
fn plain(shape: &[usize], strides: &[usize]) -> Metadata {
Metadata::new(shape.to_vec(), strides.to_vec())
}
#[test]
fn a_weight_keys_by_the_axis_it_is_contiguous_along() {
let (k, n) = (4096, 6144);
assert_eq!(contiguous(&plain(&[k, n], &[1, k]), [K, N]), Some(K));
assert_eq!(contiguous(&plain(&[k, n], &[n, 1]), [K, N]), Some(N));
assert_eq!(contiguous(&plain(&[k, n], &[n + 128, 1]), [K, N]), Some(N));
}
#[test]
fn a_left_hand_side_keys_transposed_and_strided_apart() {
let (m, k) = (64, 4096);
assert_eq!(contiguous(&plain(&[m, k], &[k, 1]), [M, K]), Some(K));
assert_eq!(contiguous(&plain(&[m, k], &[1, m]), [M, K]), Some(M));
assert_eq!(contiguous(&plain(&[m, k], &[2 * k, 2]), [M, K]), None);
assert_eq!(contiguous(&plain(&[m, 1], &[1, 1]), [M, K]), Some(M));
}
#[test]
fn the_batch_dims_do_not_reach_the_key() {
assert_eq!(
contiguous(&plain(&[8, 64, 4096], &[0, 4096, 1]), [M, K]),
Some(K)
);
}
#[test]
fn a_weight_in_tiles_is_contiguous_along_its_innermost_fragment() {
let (k, n) = (64, 32);
let tiles = |inner: [usize; 2]| {
Metadata::new(
Shape::from(vec![k / 8, n / 8, 8, 8]),
Strides::from(vec![n * 8, 64, inner[0], inner[1]]),
)
.with_tiling(Tiling::new(&[2, 2]).expect("two fragments a dim"))
.expect("the tiling describes the dims")
};
assert_eq!(contiguous(&tiles([8, 1]), [K, N]), Some(N));
assert_eq!(contiguous(&tiles([1, 8]), [K, N]), Some(K));
}
}