use smallvec::smallvec;
use crate::{Element, Recordable, Shape, Tensor};
use super::{Cotangents, Operation, Reads, unary};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) struct Fold {
pub(crate) axis: usize,
pub(crate) size: usize,
pub(crate) step: usize,
pub(crate) dilation: usize,
pub(crate) extent: usize,
}
impl Fold {
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!(
self.axis + 1 < operand.rank(),
"fold needs a (count, size) pair at axis {}, but {operand} has no pair there",
self.axis
);
assert!(self.size > 0, "fold windows must hold at least one element");
assert!(self.step > 0, "fold step must be positive");
assert!(self.dilation > 0, "fold dilation must be positive");
let span = self
.dilation
.checked_mul(self.size - 1)
.and_then(|reach| reach.checked_add(1))
.expect("fold window span overflows `usize`");
assert!(
span <= self.extent,
"fold window span {span} exceeds the target extent {}",
self.extent
);
let count = (self.extent - span) / self.step + 1;
assert_eq!(
operand.axes()[self.axis],
count,
"fold expects {count} windows at axis {} for extent {}, got {}",
self.axis,
self.extent,
operand.axes()[self.axis]
);
assert_eq!(
operand.axes()[self.axis + 1],
self.size,
"fold expects window size {} at axis {}, got {}",
self.size,
self.axis + 1,
operand.axes()[self.axis + 1]
);
let mut folded: Vec<usize> = operand.axes().to_vec();
folded[self.axis] = self.extent;
folded.remove(self.axis + 1);
Shape::new(folded)
}
}
impl Fold {
pub(crate) fn forward<E: Element>(&self, operands: &[&Tensor<E>]) -> Tensor<E> {
unary(operands).fold(self.axis, self.size, self.step, self.dilation, self.extent)
}
}
impl<Rule: Recordable> Operation<Rule> for Fold {
fn backward(&self, _operands: &[&Rule], _output: &Rule, gradient: &Rule) -> Cotangents<Rule> {
smallvec![Some(gradient.unfold(
self.axis,
self.size,
self.step,
self.dilation
))]
}
}
#[cfg(test)]
#[path = "tests/fold_tests.rs"]
mod tests;