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};
#[derive(Debug, Default)]
pub struct Identity {
built: Option<Shape>,
}
impl Identity {
pub fn new() -> Self {
Identity::default()
}
fn validate(input: &Tensor) -> Result<(), Error> {
if input.ndim() == 0 {
return Err(Error::invalid_input(
"Identity layer expects an input with a batch axis, got a 0D tensor",
));
}
if input.is_empty() {
return Err(Error::empty_input("input tensor"));
}
Ok(())
}
}
fn copy_in_c_order(input: &Tensor) -> Tensor {
input.as_standard_layout().into_owned()
}
impl LayerBase for Identity {
fn layer_type(&self) -> &str {
"Identity"
}
built_layer_shape_functions!();
no_trainable_parameters_layer_functions!();
}
impl UnaryLayer for Identity {
fn build(&mut self, input: &Shape) -> Result<(), Error> {
let Some(built) = start_build(&self.built, "Identity", 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("Identity", input.shape().to_vec());
}
Ok(copy_in_c_order(input))
}
fn backward(&self, grad_output: &Tensor, ctx: &mut Ctx) -> Result<Tensor, Error> {
let input_shape: Vec<usize> = ctx.pop_cache("Identity")?;
if grad_output.shape() != input_shape.as_slice() {
return Err(Error::shape_mismatch(input_shape, grad_output.shape()));
}
Ok(copy_in_c_order(grad_output))
}
}