use crate::autograd::Variable;
use crate::nn::Module;
use num_traits::Float;
use std::fmt::Debug;
#[derive(Debug)]
pub struct AdaptiveMaxPool2d<
T: Float + Send + Sync + ndarray::ScalarOperand + num_traits::FromPrimitive,
> {
output_size: (usize, usize),
_return_indices: bool,
phantom: std::marker::PhantomData<T>,
}
impl<T> AdaptiveMaxPool2d<T>
where
T: Float
+ Debug
+ Default
+ From<f32>
+ 'static
+ Send
+ Sync
+ Copy
+ ndarray::ScalarOperand
+ num_traits::FromPrimitive,
{
pub fn new(output_size: (usize, usize), return_indices: Option<bool>) -> Self {
let return_indices = return_indices.unwrap_or(false);
assert!(
output_size.0 > 0 && output_size.1 > 0,
"output_size must be positive"
);
Self {
output_size,
_return_indices: return_indices,
phantom: std::marker::PhantomData,
}
}
pub fn global() -> Self {
Self::new((1, 1), None)
}
pub fn forward(&self, input: &Variable<T>) -> Variable<T> {
input.clone()
}
pub fn parameters(&self) -> Vec<Variable<T>> {
Vec::new()
}
pub fn calculate_pooling_params(
&self,
input_size: (usize, usize),
) -> ((usize, usize), (usize, usize)) {
let (input_h, input_w) = input_size;
let (output_h, output_w) = self.output_size;
let kernel_h = input_h.div_ceil(output_h);
let kernel_w = input_w.div_ceil(output_w);
let stride_h = input_h / output_h;
let stride_w = input_w / output_w;
((kernel_h, kernel_w), (stride_h, stride_w))
}
pub fn get_output_size(&self) -> (usize, usize) {
self.output_size
}
}
impl<T> Module<T> for AdaptiveMaxPool2d<T>
where
T: Float
+ Debug
+ Default
+ From<f32>
+ 'static
+ Send
+ Sync
+ Copy
+ ndarray::ScalarOperand
+ num_traits::FromPrimitive,
{
fn forward(&self, input: &Variable<T>) -> Variable<T> {
self.forward(input)
}
fn parameters(&self) -> Vec<Variable<T>> {
self.parameters()
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
}
#[derive(Debug)]
pub struct AdaptiveAvgPool2d<T: Float + Send + Sync> {
output_size: (usize, usize),
phantom: std::marker::PhantomData<T>,
}
impl<T> AdaptiveAvgPool2d<T>
where
T: Float
+ Debug
+ Default
+ From<f32>
+ 'static
+ Send
+ Sync
+ Copy
+ ndarray::ScalarOperand
+ num_traits::FromPrimitive,
{
pub fn new(output_size: (usize, usize)) -> Self {
assert!(
output_size.0 > 0 && output_size.1 > 0,
"output_size must be positive"
);
Self {
output_size,
phantom: std::marker::PhantomData,
}
}
pub fn global() -> Self {
Self::new((1, 1))
}
pub fn forward(&self, input: &Variable<T>) -> Variable<T> {
input.clone()
}
pub fn parameters(&self) -> Vec<Variable<T>> {
Vec::new()
}
pub fn calculate_pooling_params(
&self,
input_size: (usize, usize),
) -> ((usize, usize), (usize, usize)) {
let (input_h, input_w) = input_size;
let (output_h, output_w) = self.output_size;
let kernel_h = input_h.div_ceil(output_h);
let kernel_w = input_w.div_ceil(output_w);
let stride_h = input_h / output_h;
let stride_w = input_w / output_w;
((kernel_h, kernel_w), (stride_h, stride_w))
}
pub fn get_output_size(&self) -> (usize, usize) {
self.output_size
}
}
impl<T> Module<T> for AdaptiveAvgPool2d<T>
where
T: Float
+ Debug
+ Default
+ From<f32>
+ 'static
+ Send
+ Sync
+ Copy
+ ndarray::ScalarOperand
+ num_traits::FromPrimitive,
{
fn forward(&self, input: &Variable<T>) -> Variable<T> {
self.forward(input)
}
fn parameters(&self) -> Vec<Variable<T>> {
self.parameters()
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_adaptive_max_pool2d_creation() {
let layer: AdaptiveMaxPool2d<f32> = AdaptiveMaxPool2d::new((7, 7), None);
assert_eq!(layer.output_size, (7, 7));
assert!(!layer._return_indices);
}
#[test]
fn test_adaptive_max_pool2d_global() {
let layer: AdaptiveMaxPool2d<f32> = AdaptiveMaxPool2d::global();
assert_eq!(layer.output_size, (1, 1));
}
#[test]
fn test_adaptive_max_pool2d_pooling_params() {
let layer: AdaptiveMaxPool2d<f32> = AdaptiveMaxPool2d::new((7, 7), None);
let input_size = (224, 224);
let (kernel_size, stride) = layer.calculate_pooling_params(input_size);
assert_eq!(kernel_size, (32, 32));
assert_eq!(stride, (32, 32));
}
#[test]
fn test_adaptive_avg_pool2d_creation() {
let layer: AdaptiveAvgPool2d<f32> = AdaptiveAvgPool2d::new((14, 14));
assert_eq!(layer.output_size, (14, 14));
}
#[test]
fn test_adaptive_avg_pool2d_global() {
let layer: AdaptiveAvgPool2d<f32> = AdaptiveAvgPool2d::global();
assert_eq!(layer.output_size, (1, 1));
}
#[test]
fn test_adaptive_avg_pool2d_pooling_params() {
let layer: AdaptiveAvgPool2d<f32> = AdaptiveAvgPool2d::new((2, 2));
let input_size = (8, 8);
let (kernel_size, stride) = layer.calculate_pooling_params(input_size);
assert_eq!(kernel_size, (4, 4));
assert_eq!(stride, (4, 4));
}
#[test]
fn test_pooling_layers_no_parameters() {
let max_pool: AdaptiveMaxPool2d<f32> = AdaptiveMaxPool2d::new((4, 4), None);
let avg_pool: AdaptiveAvgPool2d<f32> = AdaptiveAvgPool2d::new((4, 4));
assert_eq!(max_pool.parameters().len(), 0);
assert_eq!(avg_pool.parameters().len(), 0);
}
#[test]
fn test_adaptive_pooling_with_return_indices() {
let layer: AdaptiveMaxPool2d<f32> = AdaptiveMaxPool2d::new((3, 3), Some(true));
assert!(layer._return_indices);
assert_eq!(layer.output_size, (3, 3));
}
#[test]
fn test_adaptive_pooling_irregular_sizes() {
let layer: AdaptiveAvgPool2d<f32> = AdaptiveAvgPool2d::new((5, 3));
let input_size = (17, 13);
let (kernel_size, stride) = layer.calculate_pooling_params(input_size);
assert_eq!(kernel_size, (4, 5));
assert_eq!(stride, (3, 4));
}
#[test]
fn test_get_output_size() {
let max_pool: AdaptiveMaxPool2d<f32> = AdaptiveMaxPool2d::new((6, 8), None);
let avg_pool: AdaptiveAvgPool2d<f32> = AdaptiveAvgPool2d::new((6, 8));
assert_eq!(max_pool.get_output_size(), (6, 8));
assert_eq!(avg_pool.get_output_size(), (6, 8));
}
}