use crate::inline::{InlineVec, OperandVec};
pub fn fuse_dims(dims: &[usize], all_strides: &[&[isize]]) -> InlineVec<usize> {
let n = dims.len();
if n <= 1 || all_strides.is_empty() {
return dims.iter().copied().collect();
}
let mut result: InlineVec<usize> = dims.iter().copied().collect();
for i in (1..n).rev() {
let mut can_merge = true;
for strides in all_strides {
if strides[i - 1] == 0 && strides[i] == 0 {
continue;
}
let expected = isize::try_from(result[i - 1])
.ok()
.and_then(|dim| dim.checked_mul(strides[i - 1]));
if expected != Some(strides[i]) {
can_merge = false;
break;
}
}
if can_merge {
if let Some(merged) = result[i - 1].checked_mul(result[i]) {
result[i - 1] = merged;
result[i] = 1;
}
}
}
result
}
pub fn compress_dims<S: AsRef<[isize]>>(
dims: &[usize],
all_strides: &[S],
) -> (InlineVec<usize>, OperandVec<InlineVec<isize>>) {
let kept: InlineVec<usize> = (0..dims.len()).filter(|&i| dims[i] != 1).collect();
if kept.is_empty() {
if dims.is_empty() {
let strides = all_strides
.iter()
.map(|s| s.as_ref().iter().copied().collect())
.collect();
return (InlineVec::new(), strides);
}
let new_strides = all_strides
.iter()
.map(|s| {
let mut v = InlineVec::new();
v.push(s.as_ref()[0]);
v
})
.collect();
let mut dims_out = InlineVec::new();
dims_out.push(1);
return (dims_out, new_strides);
}
let new_dims: InlineVec<usize> = kept.iter().map(|&i| dims[i]).collect();
let new_strides: OperandVec<InlineVec<isize>> = all_strides
.iter()
.map(|s| kept.iter().map(|&i| s.as_ref()[i]).collect())
.collect();
(new_dims, new_strides)
}
pub fn compute_importance<I: AsRef<[usize]>>(
dims: &[usize],
all_strides: &[&[isize]],
index_orders: &[I],
) -> InlineVec<u64> {
let n = dims.len();
let m = all_strides.len();
if n == 0 || m == 0 {
return InlineVec::new();
}
let g = (64 - (m as u64 + 1).leading_zeros()) as u64;
let mut importance: InlineVec<u64> = (0..n).map(|_| 0u64).collect();
let output_weight = 1u64 << (g + 1);
for i in 0..n {
if all_strides[0][i] != 0 {
let shift = g * (n - index_orders[0].as_ref()[i]) as u64;
importance[i] = output_weight * (1u64 << shift);
}
}
#[allow(clippy::needless_range_loop)]
for k in 1..m {
for i in 0..n {
if all_strides[k][i] != 0 {
let shift = g * (n - index_orders[k].as_ref()[i]) as u64;
importance[i] += 1u64 << shift;
}
}
}
for i in 0..n {
if dims[i] <= 1 {
importance[i] = 0;
}
}
importance
}
pub fn sort_by_importance(importance: &[u64]) -> InlineVec<usize> {
let mut indices: InlineVec<usize> = (0..importance.len()).collect();
indices.sort_by(|&a, &b| importance[b].cmp(&importance[a]));
indices
}
pub(crate) fn compute_costs<S: AsRef<[isize]>>(all_strides: &[S]) -> InlineVec<isize> {
if all_strides.is_empty() {
return InlineVec::new();
}
let n = all_strides[0].as_ref().len();
let mut costs: InlineVec<isize> = (0..n).map(|_| isize::MAX).collect();
for strides in all_strides {
let strides = strides.as_ref();
for i in 0..n {
costs[i] = costs[i].min(strides[i].checked_abs().unwrap_or(isize::MAX));
}
}
for cost in &mut costs {
if *cost == 0 {
*cost = 1;
} else {
*cost = cost.saturating_mul(2);
}
}
costs
}
#[cfg(test)]
#[path = "fuse/tests/tests.rs"]
mod tests;