1use strided_view::INLINE_RANK;
7use tinyvec::TinyVec;
8
9pub type FusionDimVec = TinyVec<[usize; INLINE_RANK]>;
14
15pub type FusionStrideVec = TinyVec<[isize; INLINE_RANK]>;
17
18#[cfg(test)]
24pub fn fuse_dims(dims: &[usize], all_strides: &[&[isize]]) -> Vec<usize> {
25 let n = dims.len();
26 if n <= 1 || all_strides.is_empty() {
27 return dims.to_vec();
28 }
29
30 let mut result = dims.to_vec();
31
32 for i in (1..n).rev() {
34 let mut can_merge = true;
35
36 for strides in all_strides {
38 let expected = result[i - 1] as isize * strides[i - 1];
40 if strides[i] != expected {
41 can_merge = false;
42 break;
43 }
44 }
45
46 if can_merge {
47 result[i - 1] *= result[i];
49 result[i] = 1;
50 }
51 }
52
53 result
54}
55
56#[cfg(test)]
65pub fn compress_dims(dims: &[usize], all_strides: &[Vec<isize>]) -> (Vec<usize>, Vec<Vec<isize>>) {
66 let kept: Vec<usize> = (0..dims.len()).filter(|&i| dims[i] != 1).collect();
67
68 if kept.is_empty() {
69 if dims.is_empty() {
71 return (vec![], all_strides.to_vec());
72 }
73 let new_strides = all_strides.iter().map(|s| vec![s[0]]).collect();
74 return (vec![1], new_strides);
75 }
76
77 let new_dims: Vec<usize> = kept.iter().map(|&i| dims[i]).collect();
78 let new_strides: Vec<Vec<isize>> = all_strides
79 .iter()
80 .map(|s| kept.iter().map(|&i| s[i]).collect())
81 .collect();
82
83 (new_dims, new_strides)
84}
85
86#[cfg(test)]
91pub fn compute_importance(
92 dims: &[usize],
93 all_strides: &[&[isize]],
94 index_orders: &[Vec<usize>],
95) -> Vec<u64> {
96 let n = dims.len();
97 let m = all_strides.len();
98
99 if n == 0 || m == 0 {
100 return vec![];
101 }
102
103 let g = (64 - (m as u64 + 1).leading_zeros()) as u64;
105
106 let mut importance = vec![0u64; n];
107
108 for i in 0..n {
110 let shift = g * (n - index_orders[0][i]) as u64;
111 importance[i] = 2 * (1u64 << shift);
112 }
113
114 #[allow(clippy::needless_range_loop)]
116 for k in 1..m {
117 for i in 0..n {
118 let shift = g * (n - index_orders[k][i]) as u64;
119 importance[i] += 1u64 << shift;
120 }
121 }
122
123 for i in 0..n {
125 if dims[i] <= 1 {
126 importance[i] = 0;
127 }
128 }
129
130 importance
131}
132
133#[cfg(test)]
135pub fn sort_by_importance(importance: &[u64]) -> Vec<usize> {
136 let mut indices: Vec<usize> = (0..importance.len()).collect();
137 indices.sort_by(|&a, &b| importance[b].cmp(&importance[a]));
138 indices
139}
140
141#[cfg(test)]
143pub fn compute_costs<S: AsRef<[isize]>>(all_strides: &[S]) -> Vec<isize> {
144 if all_strides.is_empty() {
145 return vec![];
146 }
147
148 let n = all_strides[0].as_ref().len();
149 let mut costs = vec![isize::MAX; n];
150
151 for strides in all_strides {
152 let strides = strides.as_ref();
153 for i in 0..n {
154 costs[i] = costs[i].min(strides[i].abs());
155 }
156 }
157
158 for cost in &mut costs {
160 if *cost == 0 {
161 *cost = 1;
162 } else {
163 *cost *= 2;
164 }
165 }
166
167 costs
168}
169
170#[derive(Clone, Debug, PartialEq, Eq)]
173pub struct BilateralFusionPlan {
174 pub dims: FusionDimVec,
176 pub src_strides: FusionStrideVec,
178 pub dst_strides: FusionStrideVec,
180}
181
182#[derive(Clone, Debug, thiserror::Error, PartialEq, Eq)]
184pub enum FusionPlanError {
185 #[error(
187 "metadata lengths differ: dims={dims}, src_strides={src_strides}, \
188 dst_strides={dst_strides}"
189 )]
190 LengthMismatch {
191 dims: usize,
193 src_strides: usize,
195 dst_strides: usize,
197 },
198 #[error("fused dimension product overflows usize")]
201 DimensionOverflow,
202}
203
204pub fn plan_bilateral_fusion(
217 dims: &[usize],
218 src_strides: &[isize],
219 dst_strides: &[isize],
220) -> Result<BilateralFusionPlan, FusionPlanError> {
221 if dims.len() != src_strides.len() || dims.len() != dst_strides.len() {
222 return Err(FusionPlanError::LengthMismatch {
223 dims: dims.len(),
224 src_strides: src_strides.len(),
225 dst_strides: dst_strides.len(),
226 });
227 }
228
229 let mut plan = BilateralFusionPlan {
230 dims: FusionDimVec::with_capacity(dims.len()),
231 src_strides: FusionStrideVec::with_capacity(dims.len()),
232 dst_strides: FusionStrideVec::with_capacity(dims.len()),
233 };
234
235 for ((&dim, &src_stride), &dst_stride) in dims.iter().zip(src_strides).zip(dst_strides) {
236 if dim == 1 {
237 continue;
238 }
239 isize::try_from(dim).map_err(|_| FusionPlanError::DimensionOverflow)?;
240
241 if let Some(last) = plan.dims.len().checked_sub(1) {
242 let current_dim =
243 isize::try_from(plan.dims[last]).map_err(|_| FusionPlanError::DimensionOverflow)?;
244 let src_contiguous = plan.src_strides[last]
245 .checked_mul(current_dim)
246 .is_some_and(|expected| src_stride == expected);
247 let dst_contiguous = plan.dst_strides[last]
248 .checked_mul(current_dim)
249 .is_some_and(|expected| dst_stride == expected);
250
251 if src_contiguous && dst_contiguous {
252 plan.dims[last] = plan.dims[last]
253 .checked_mul(dim)
254 .ok_or(FusionPlanError::DimensionOverflow)?;
255 continue;
256 }
257 }
258
259 plan.dims.push(dim);
260 plan.src_strides.push(src_stride);
261 plan.dst_strides.push(dst_stride);
262 }
263
264 Ok(plan)
265}
266
267#[cfg(test)]
268fn fuse_dims_bilateral(
269 dims: &[usize],
270 src_strides: &[isize],
271 dst_strides: &[isize],
272) -> (FusionDimVec, FusionStrideVec, FusionStrideVec) {
273 let plan = plan_bilateral_fusion(dims, src_strides, dst_strides).unwrap();
274 (plan.dims, plan.src_strides, plan.dst_strides)
275}
276
277#[cfg(test)]
278mod tests {
279 use super::*;
280
281 #[test]
282 fn test_fuse_dims_contiguous() {
283 let dims = [3, 4];
284 let strides1 = [1isize, 3];
285 let strides2 = [1isize, 3];
286 let all_strides: Vec<&[isize]> = vec![&strides1, &strides2];
287 let fused = fuse_dims(&dims, &all_strides);
288 assert_eq!(fused, vec![12, 1]);
289 }
290
291 #[test]
292 fn test_fuse_dims_non_contiguous() {
293 let dims = [3, 4];
294 let strides1 = [1isize, 10];
295 let all_strides: Vec<&[isize]> = vec![&strides1];
296 let fused = fuse_dims(&dims, &all_strides);
297 assert_eq!(fused, vec![3, 4]);
298 }
299
300 #[test]
301 fn test_fuse_dims_bilateral_all_contiguous() {
302 let dims = vec![2, 2, 2, 2];
304 let src_strides = vec![1, 2, 4, 8];
305 let dst_strides = vec![1, 2, 4, 8];
306 let (fd, fs, fds) = fuse_dims_bilateral(&dims, &src_strides, &dst_strides);
307 assert_eq!(&fd[..], &[16]);
308 assert_eq!(&fs[..], &[1]);
309 assert_eq!(&fds[..], &[1]);
310 }
311
312 #[test]
313 fn test_fuse_dims_bilateral_partial() {
314 let dims = vec![2, 3, 4];
317 let src_strides = vec![1, 2, 100]; let dst_strides = vec![1, 2, 6]; let (fd, fs, fds) = fuse_dims_bilateral(&dims, &src_strides, &dst_strides);
320 assert_eq!(&fd[..], &[6, 4]); assert_eq!(&fs[..], &[1, 100]);
322 assert_eq!(&fds[..], &[1, 6]);
323 }
324
325 #[test]
326 fn test_fuse_dims_bilateral_scattered() {
327 let dims = vec![2, 2, 2];
329 let src_strides = vec![1, 4194304, 2]; let dst_strides = vec![1, 2, 4]; let (fd, fs, fds) = fuse_dims_bilateral(&dims, &src_strides, &dst_strides);
332 assert_eq!(&fd[..], &[2, 2, 2]); assert_eq!(&fs[..], &[1, 4194304, 2]);
334 assert_eq!(&fds[..], &[1, 2, 4]);
335 }
336
337 #[test]
338 fn test_compress_dims_removes_fused() {
339 let dims = vec![12usize, 1];
340 let strides = vec![vec![1isize, 3]];
341 let (cd, cs) = compress_dims(&dims, &strides);
342 assert_eq!(cd, vec![12]);
343 assert_eq!(cs, vec![vec![1]]);
344 }
345
346 #[test]
347 fn test_compute_costs() {
348 let strides1 = [1isize, 4, 0];
349 let strides2 = [2isize, 1, 0];
350 let all_strides: Vec<&[isize]> = vec![&strides1, &strides2];
351 let costs = compute_costs(&all_strides);
352 assert_eq!(costs, vec![2, 2, 1]);
353 }
354}