use smallvec::smallvec;
use crate::{Element, Recordable, Shape, Tensor};
use super::{Cotangents, Operation, Reads, binary};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) struct Scatter;
impl Scatter {
pub(crate) fn arity(&self) -> usize {
2
}
pub(crate) fn reads(&self) -> Reads {
Reads {
operands: [false, true],
output: false,
}
}
pub(crate) fn infer_shape(&self, operands: &[Shape]) -> Shape {
let (gradient, selection) = binary(operands);
assert!(
gradient.rank() >= 1,
"scatter needs a gradient with a leading selection axis, got {gradient}"
);
assert_eq!(
selection.rank(),
2,
"scatter selection must be rank 2 [count, vocab], got {selection}"
);
assert_eq!(
selection.axes()[0],
gradient.axes()[0],
"scatter gradient rows {} disagree with the selection count {}",
gradient.axes()[0],
selection.axes()[0]
);
Shape::new(std::iter::once(selection.axes()[1]).chain(gradient.axes()[1..].iter().copied()))
}
}
impl Scatter {
pub(crate) fn forward<E: Element>(&self, operands: &[&Tensor<E>]) -> Tensor<E> {
let (&gradient, &selection) = binary(operands);
gradient.scatter(selection)
}
}
impl<Rule: Recordable> Operation<Rule> for Scatter {
fn backward(&self, operands: &[&Rule], _output: &Rule, gradient: &Rule) -> Cotangents<Rule> {
let (_, &selection) = binary(operands);
smallvec![Some(gradient.gather(selection)), None]
}
}
#[cfg(test)]
#[path = "tests/scatter_tests.rs"]
mod tests;