use smallvec::{SmallVec, smallvec};
use crate::{Element, Recordable, Shape, Tensor};
use super::{Cotangents, Operation, Reads, unary};
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct Permute {
pub(crate) order: SmallVec<[usize; 4]>,
}
impl Permute {
pub(crate) fn arity(&self) -> usize {
1
}
pub(crate) fn reads(&self) -> Reads {
Reads::NOTHING
}
pub(crate) fn infer_shape(&self, operands: &[Shape]) -> Shape {
let operand = unary(operands);
assert_eq!(
self.order.len(),
operand.rank(),
"permute order must cover every axis of {operand}"
);
let mut seen = vec![false; operand.rank()];
for &axis in &self.order {
assert!(
axis < operand.rank(),
"permute axis {axis} is out of rank for {operand}"
);
assert!(
!std::mem::replace(&mut seen[axis], true),
"permute order repeats axis {axis}"
);
}
Shape::new(self.order.iter().map(|&axis| operand.axes()[axis]))
}
fn inverse(&self) -> SmallVec<[usize; 4]> {
let mut inverse: SmallVec<[usize; 4]> =
std::iter::repeat_n(0usize, self.order.len()).collect();
for (position, &axis) in self.order.iter().enumerate() {
inverse[axis] = position;
}
inverse
}
}
impl Permute {
pub(crate) fn forward<E: Element>(&self, operands: &[&Tensor<E>]) -> Tensor<E> {
unary(operands).permute(&self.order)
}
}
impl<Rule: Recordable> Operation<Rule> for Permute {
fn backward(&self, _operands: &[&Rule], _output: &Rule, gradient: &Rule) -> Cotangents<Rule> {
smallvec![Some(gradient.permute(&self.inverse()))]
}
}