use crate::error::Error;
use crate::neural_network::Tensor;
use crate::neural_network::layers::TrainingParameters;
use crate::neural_network::layers::layer_weight::LayerWeight;
use crate::neural_network::layers::no_trainable_parameters_layer_functions;
use crate::neural_network::traits::Layer;
#[derive(Debug, Default)]
pub struct Identity {
input_shape: Option<Vec<usize>>,
}
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 Layer for Identity {
fn forward(&mut self, input: &Tensor) -> Result<Tensor, Error> {
Self::validate(input)?;
self.input_shape = Some(input.shape().to_vec());
Ok(copy_in_c_order(input))
}
fn predict(&self, input: &Tensor) -> Result<Tensor, Error> {
Self::validate(input)?;
Ok(copy_in_c_order(input))
}
fn backward(&mut self, grad_output: &Tensor) -> Result<Tensor, Error> {
let Some(input_shape) = &self.input_shape else {
return Err(Error::forward_pass_not_run("Identity"));
};
if grad_output.shape() != input_shape.as_slice() {
return Err(Error::shape_mismatch(
input_shape.clone(),
grad_output.shape(),
));
}
Ok(copy_in_c_order(grad_output))
}
fn layer_type(&self) -> &str {
"Identity"
}
fn output_shape(&self) -> String {
match &self.input_shape {
Some(shape) => {
let axes: Vec<String> = shape[1..].iter().map(|e| e.to_string()).collect();
if axes.is_empty() {
"(None,)".to_string()
} else {
format!("(None, {})", axes.join(", "))
}
}
None => "Unknown".to_string(),
}
}
no_trainable_parameters_layer_functions!();
}