use crate::error::Error;
use crate::neural_network::layers::ParamCounts;
use crate::neural_network::layers::convolution::PaddingType;
use crate::neural_network::layers::pooling::pooling_engine::{
PoolKind, windowed_pool_backward, windowed_pool_forward,
};
use crate::neural_network::layers::pooling::validation::{
validate_pool_size_2d, validate_strides_2d,
};
use crate::neural_network::layers::pooling::{
layer_base_functions_pooling, layer_functions_2d_pooling,
};
use crate::neural_network::layers::shape_helpers::calculate_output_shape_2d_pooling;
use crate::neural_network::layers::validation::validate_built_input;
use crate::neural_network::traits::{LayerBase, UnaryLayer};
use crate::neural_network::{Ctx, Shape, Tensor};
#[derive(Debug)]
pub struct MaxPooling2D {
pool_size: (usize, usize),
strides: (usize, usize),
built: Option<Shape>,
padding: PaddingType,
}
impl MaxPooling2D {
pub fn new(pool_size: (usize, usize)) -> Self {
MaxPooling2D {
pool_size,
strides: pool_size,
built: None,
padding: PaddingType::Valid,
}
}
pub fn with_strides(mut self, strides: (usize, usize)) -> Result<Self, Error> {
validate_strides_2d(strides)?;
self.strides = strides;
Ok(self)
}
pub fn with_padding(mut self, padding: PaddingType) -> Self {
self.padding = padding;
self
}
}
struct MaxPooling2DCache {
input_shape: Vec<usize>,
argmax: Vec<usize>,
}
impl LayerBase for MaxPooling2D {
fn layer_type(&self) -> &str {
"MaxPooling2D"
}
layer_base_functions_pooling!();
}
impl UnaryLayer for MaxPooling2D {
fn forward(&self, input: &Tensor, ctx: &mut Ctx) -> Result<Tensor, Error> {
validate_built_input(&self.built, "MaxPooling2D", input.shape())?;
let (output, argmax) = windowed_pool_forward(
input,
&[self.pool_size.0, self.pool_size.1],
&[self.strides.0, self.strides.1],
PoolKind::Max,
self.padding,
);
if ctx.is_training() {
ctx.push_cache(
"MaxPooling2D",
MaxPooling2DCache {
input_shape: input.shape().to_vec(),
argmax: argmax.expect("PoolKind::Max always tracks the arg-max"),
},
);
}
Ok(output)
}
fn backward(&self, grad_output: &Tensor, ctx: &mut Ctx) -> Result<Tensor, Error> {
let cache: MaxPooling2DCache = ctx.pop_cache("MaxPooling2D")?;
Ok(windowed_pool_backward(
grad_output,
&cache.input_shape,
&[self.pool_size.0, self.pool_size.1],
&[self.strides.0, self.strides.1],
PoolKind::Max,
Some(&cache.argmax),
self.padding,
))
}
layer_functions_2d_pooling!("MaxPooling2D");
}