use smallvec::smallvec;
use crate::{Element, Recordable, Shape, Tensor};
use super::{Cotangents, Operation, Reads, unary};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) struct BroadcastAlong {
pub(crate) axis: usize,
pub(crate) extent: usize,
}
impl BroadcastAlong {
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 <= operand.rank(),
"broadcast axis {} is out of rank for {operand}",
self.axis
);
assert!(self.extent > 0, "broadcast extent must be positive");
let mut axes: Vec<usize> = operand.axes().to_vec();
axes.insert(self.axis, self.extent);
Shape::new(axes)
}
}
impl BroadcastAlong {
pub(crate) fn forward<E: Element>(&self, operands: &[&Tensor<E>]) -> Tensor<E> {
unary(operands).broadcast_along(self.axis, self.extent)
}
}
impl<Rule: Recordable> Operation<Rule> for BroadcastAlong {
fn backward(&self, _operands: &[&Rule], _output: &Rule, gradient: &Rule) -> Cotangents<Rule> {
smallvec![Some(gradient.sum_along(self.axis))]
}
}