cubek_std/
matrix_layout.rs1use cubecl::{
2 prelude::*,
3 quant::scheme::QuantScheme,
4 zspace::{Strides, strides},
5};
6
7use crate::InvalidConfigError;
8
9#[derive(CubeType, Copy, Clone, PartialEq, Eq, Hash, Debug, Default)]
10pub enum MatrixLayout {
13 #[default]
14 RowMajor,
15 ColMajor,
16}
17
18impl MatrixLayout {
19 pub fn from_shape_and_strides(
20 shape: &[usize],
21 strides: &[usize],
22 scheme: Option<&QuantScheme>,
23 ) -> Result<Self, InvalidConfigError> {
24 assert!(
25 shape.len() >= 2 && shape.len() == strides.len(),
26 "Shape/stride mismatch or not a matrix"
27 );
28
29 if let Some(packing_dim) = scheme.and_then(|s| s.packing_dim()) {
30 if packing_dim == 0 {
31 return Ok(MatrixLayout::RowMajor);
32 }
33 if packing_dim == 1 {
34 return Ok(MatrixLayout::ColMajor);
35 }
36
37 return Err(Box::new(format!(
38 "Invalid or non-contiguous matrix layout: packing_dim={packing_dim:?}"
39 )));
40 }
41
42 let n = shape.len();
43
44 let outer = shape[n - 2];
45 let inner = shape[n - 1];
46
47 let stride_outer = strides[n - 2];
48 let stride_inner = strides[n - 1];
49
50 if (stride_inner == 1) && (outer == 1 || stride_outer >= inner) {
67 return Ok(MatrixLayout::RowMajor);
68 }
69
70 if (stride_outer == 1) && (inner == 1 || stride_inner >= outer) {
72 return Ok(MatrixLayout::ColMajor);
73 }
74
75 Err(Box::new(format!(
76 "Invalid or non-contiguous matrix layout: shape={shape:?}, strides={strides:?}",
77 )))
78 }
79
80 pub fn to_strides(&self, shape: &[usize]) -> Strides {
81 assert!(shape.len() >= 2, "Shape must have at least 2 dimensions");
82
83 let n = shape.len();
84 let mut strides = strides![0; n];
85
86 match self {
88 MatrixLayout::RowMajor => {
89 strides[n - 1] = 1; strides[n - 2] = shape[n - 1]; }
92 MatrixLayout::ColMajor => {
93 strides[n - 2] = 1; strides[n - 1] = shape[n - 2]; }
96 }
97
98 for i in (0..n - 2).rev() {
100 strides[i] = strides[i + 1] * shape[i + 1];
101 }
102
103 strides
104 }
105}
106
107#[cfg(feature = "testing")]
108impl From<MatrixLayout> for cubek_test_utils::StridedLayout {
109 fn from(layout: MatrixLayout) -> Self {
110 match layout {
111 MatrixLayout::RowMajor => Self::RowMajor,
112 MatrixLayout::ColMajor => Self::ColMajor,
113 }
114 }
115}
116
117#[cfg(feature = "testing")]
118impl From<MatrixLayout> for cubek_test_utils::LayoutSpec {
119 fn from(layout: MatrixLayout) -> Self {
120 cubek_test_utils::StridedLayout::from(layout).into()
121 }
122}
123
124#[cube]
125pub fn as_cmma_layout(#[comptime] layout: MatrixLayout) -> cmma::MatrixLayout {
127 match layout {
128 MatrixLayout::RowMajor => cmma::MatrixLayout::RowMajor,
129 MatrixLayout::ColMajor => cmma::MatrixLayout::ColMajor,
130 }
131}
132
133#[cube]
134pub fn from_cmma_layout(#[comptime] layout: cmma::MatrixLayout) -> comptime_type!(MatrixLayout) {
136 match layout {
137 cmma::MatrixLayout::RowMajor => MatrixLayout::RowMajor,
138 cmma::MatrixLayout::ColMajor => MatrixLayout::ColMajor,
139 cmma::MatrixLayout::Undefined => MatrixLayout::RowMajor,
140 }
141}
142
143#[cfg(test)]
144mod tests {
145 use super::*;
146
147 fn layout(shape: &[usize], strides: &[usize]) -> Result<MatrixLayout, String> {
148 MatrixLayout::from_shape_and_strides(shape, strides, None).map_err(|err| err.to_string())
149 }
150
151 #[test]
152 fn contiguous_is_row_major() {
153 assert_eq!(layout(&[4, 8], &[8, 1]).unwrap(), MatrixLayout::RowMajor);
154 }
155
156 #[test]
157 fn transposed_is_col_major() {
158 assert_eq!(layout(&[4, 8], &[1, 4]).unwrap(), MatrixLayout::ColMajor);
159 }
160
161 #[test]
162 fn pitched_rows_are_row_major() {
163 assert_eq!(layout(&[4, 8], &[16, 1]).unwrap(), MatrixLayout::RowMajor);
165 }
166
167 #[test]
168 fn single_row_is_row_major() {
169 assert_eq!(layout(&[1, 8], &[1, 1]).unwrap(), MatrixLayout::RowMajor);
172 }
173
174 #[test]
175 fn single_column_is_col_major() {
176 assert_eq!(layout(&[8, 1], &[1, 4]).unwrap(), MatrixLayout::ColMajor);
179 }
180
181 #[test]
182 fn overlapping_strides_are_rejected() {
183 assert!(layout(&[4, 8], &[2, 1]).is_err());
184 }
185
186 #[test]
187 fn batches_do_not_change_the_matrix_layout() {
188 assert_eq!(
189 layout(&[2, 4, 8], &[32, 8, 1]).unwrap(),
190 MatrixLayout::RowMajor
191 );
192 assert_eq!(
193 layout(&[2, 4, 8], &[32, 1, 4]).unwrap(),
194 MatrixLayout::ColMajor
195 );
196 }
197}