use crate::error::Error;
use crate::neural_network::layers::ParamCounts;
use crate::neural_network::layers::validation::start_build;
use crate::neural_network::layers::{
built_layer_shape_functions, no_trainable_parameters_layer_functions,
};
use crate::neural_network::traits::{LayerBase, UnaryLayer};
use crate::neural_network::{Ctx, Shape, Tensor};
use ndarray::{Axis, IxDyn};
#[derive(Debug)]
pub struct RepeatVector {
n: usize,
built: Option<Shape>,
}
impl RepeatVector {
pub fn new(n: usize) -> Result<Self, Error> {
if n == 0 {
return Err(Error::invalid_parameter(
"n",
"is 0, and the output must hold at least 1 step",
));
}
Ok(RepeatVector { n, built: None })
}
fn validate(&self, input: &Tensor) -> Result<(), Error> {
if input.ndim() != 2 {
return Err(Error::invalid_input(format!(
"RepeatVector layer expects a 2D input [batch_size, features], got a {}D tensor",
input.ndim()
)));
}
if input.is_empty() {
return Err(Error::empty_input("input tensor"));
}
Ok(())
}
fn repeat(&self, input: &Tensor) -> Tensor {
let shape = input.shape();
let mut output = Tensor::zeros(IxDyn(&[shape[0], self.n, shape[1]]));
output.assign(&input.view().insert_axis(Axis(1)));
output
}
}
impl LayerBase for RepeatVector {
fn layer_type(&self) -> &str {
"RepeatVector"
}
built_layer_shape_functions!();
no_trainable_parameters_layer_functions!();
}
impl UnaryLayer for RepeatVector {
fn build(&mut self, input: &Shape) -> Result<(), Error> {
let Some(built) = start_build(&self.built, "RepeatVector", input)? else {
return Ok(());
};
self.compute_output_shape(&built)?;
self.built = Some(built);
Ok(())
}
fn forward(&self, input: &Tensor, ctx: &mut Ctx) -> Result<Tensor, Error> {
self.validate(input)?;
if ctx.is_training() {
ctx.push_cache("RepeatVector", input.shape().to_vec());
}
Ok(self.repeat(input))
}
fn backward(&self, grad_output: &Tensor, ctx: &mut Ctx) -> Result<Tensor, Error> {
let input_shape: Vec<usize> = ctx.pop_cache("RepeatVector")?;
let expected = [input_shape[0], self.n, input_shape[1]];
if grad_output.shape() != expected {
return Err(Error::shape_mismatch(expected, grad_output.shape()));
}
Ok(grad_output.sum_axis(Axis(1)))
}
fn compute_output_shape(&self, input: &Shape) -> Result<Shape, Error> {
input.check_rank("RepeatVector", 2)?;
let (batch, tail) = input.split_batch("RepeatVector")?;
Ok(Shape::from_batch(batch, &[self.n, tail[0]]))
}
}