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 for row in &index_orders[1..m] {
124 for (i, slot) in importance.iter_mut().enumerate() {
125 if let Some(&order) = row.get(i) {
126 *slot += 1u64 << (g * (n - order) as u64);
127 }
128 }
129 }
130
131 for i in 0..n {
133 if dims[i] <= 1 {
134 importance[i] = 0;
135 }
136 }
137
138 importance
139}
140
141#[cfg(test)]
143pub fn sort_by_importance(importance: &[u64]) -> Vec<usize> {
144 let mut indices: Vec<usize> = (0..importance.len()).collect();
145 indices.sort_by(|&a, &b| importance[b].cmp(&importance[a]));
146 indices
147}
148
149#[cfg(test)]
151pub fn compute_costs<S: AsRef<[isize]>>(all_strides: &[S]) -> Vec<isize> {
152 if all_strides.is_empty() {
153 return vec![];
154 }
155
156 let n = all_strides[0].as_ref().len();
157 let mut costs = vec![isize::MAX; n];
158
159 for strides in all_strides {
160 let strides = strides.as_ref();
161 for i in 0..n {
162 costs[i] = costs[i].min(strides[i].checked_abs().unwrap_or(isize::MAX));
163 }
164 }
165
166 for cost in &mut costs {
170 if *cost == 0 {
171 *cost = 1;
172 } else {
173 *cost = cost.saturating_mul(2);
174 }
175 }
176
177 costs
178}
179
180#[derive(Clone, Debug, PartialEq, Eq)]
183pub struct BilateralFusionPlan {
184 pub dims: FusionDimVec,
186 pub src_strides: FusionStrideVec,
188 pub dst_strides: FusionStrideVec,
190}
191
192#[derive(Clone, Debug, thiserror::Error, PartialEq, Eq)]
194pub enum FusionPlanError {
195 #[error(
197 "metadata lengths differ: dims={dims}, src_strides={src_strides}, \
198 dst_strides={dst_strides}"
199 )]
200 LengthMismatch {
201 dims: usize,
203 src_strides: usize,
205 dst_strides: usize,
207 },
208 #[error("fused dimension product overflows usize")]
211 DimensionOverflow,
212}
213
214pub fn plan_bilateral_fusion(
227 dims: &[usize],
228 src_strides: &[isize],
229 dst_strides: &[isize],
230) -> Result<BilateralFusionPlan, FusionPlanError> {
231 if dims.len() != src_strides.len() || dims.len() != dst_strides.len() {
232 return Err(FusionPlanError::LengthMismatch {
233 dims: dims.len(),
234 src_strides: src_strides.len(),
235 dst_strides: dst_strides.len(),
236 });
237 }
238
239 let mut plan = BilateralFusionPlan {
240 dims: FusionDimVec::with_capacity(dims.len()),
241 src_strides: FusionStrideVec::with_capacity(dims.len()),
242 dst_strides: FusionStrideVec::with_capacity(dims.len()),
243 };
244
245 for ((&dim, &src_stride), &dst_stride) in dims.iter().zip(src_strides).zip(dst_strides) {
246 if dim == 1 {
247 continue;
248 }
249 isize::try_from(dim).map_err(|_| FusionPlanError::DimensionOverflow)?;
250
251 if let Some(last) = plan.dims.len().checked_sub(1) {
252 let current_dim =
253 isize::try_from(plan.dims[last]).map_err(|_| FusionPlanError::DimensionOverflow)?;
254 let src_contiguous = plan.src_strides[last]
255 .checked_mul(current_dim)
256 .is_some_and(|expected| src_stride == expected);
257 let dst_contiguous = plan.dst_strides[last]
258 .checked_mul(current_dim)
259 .is_some_and(|expected| dst_stride == expected);
260
261 if src_contiguous && dst_contiguous {
262 plan.dims[last] = plan.dims[last]
263 .checked_mul(dim)
264 .ok_or(FusionPlanError::DimensionOverflow)?;
265 continue;
266 }
267 }
268
269 plan.dims.push(dim);
270 plan.src_strides.push(src_stride);
271 plan.dst_strides.push(dst_stride);
272 }
273
274 Ok(plan)
275}
276
277#[cfg(test)]
278fn fuse_dims_bilateral(
279 dims: &[usize],
280 src_strides: &[isize],
281 dst_strides: &[isize],
282) -> (FusionDimVec, FusionStrideVec, FusionStrideVec) {
283 let plan = plan_bilateral_fusion(dims, src_strides, dst_strides).unwrap();
284 (plan.dims, plan.src_strides, plan.dst_strides)
285}
286
287#[cfg(test)]
288mod tests;