use crate::error::Error;
use crate::neural_network::layers::ParamCounts;
use crate::neural_network::layers::activation::{Activation, ActivationCache, ActivationInput};
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)]
pub struct LeakyReLU {
pub(super) negative_slope: f32,
built: Option<Shape>,
}
impl LeakyReLU {
pub fn new(negative_slope: f32) -> Result<Self, Error> {
Activation::LeakyReLU { negative_slope }.validate()?;
Ok(LeakyReLU {
negative_slope,
built: None,
})
}
}
impl Default for LeakyReLU {
fn default() -> Self {
LeakyReLU {
negative_slope: 0.3,
built: None,
}
}
}
impl LayerBase for LeakyReLU {
fn layer_type(&self) -> &str {
"LeakyReLU"
}
built_layer_shape_functions!();
no_trainable_parameters_layer_functions!();
}
impl UnaryLayer for LeakyReLU {
fn build(&mut self, input: &Shape) -> Result<(), Error> {
let Some(built) = start_build(&self.built, "LeakyReLU", 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> {
if input.is_empty() {
return Err(Error::empty_input("input tensor"));
}
let output = Activation::LeakyReLU {
negative_slope: self.negative_slope,
}
.forward(input)?;
if ctx.is_training() {
ctx.push_cache(
"LeakyReLU",
ActivationCache::new(ActivationInput::Output, output.clone()),
);
}
Ok(output)
}
fn backward(&self, grad_output: &Tensor, ctx: &mut Ctx) -> Result<Tensor, Error> {
let cache: ActivationCache = ctx.pop_cache("LeakyReLU")?;
Activation::LeakyReLU {
negative_slope: self.negative_slope,
}
.backward(&cache, grad_output)
}
}