cubek-pool 0.3.0

CubeK: Pool
use crate::definition::{
    AdaptiveAvgPoolOptions, AvgPoolOptions, MaxPoolOptions, PoolForwardProblem, PoolMode,
};
use cubecl::zspace::Shape;

pub trait PoolGeometry<const N: usize> {
    fn output_shape(&self, input_shape: &Shape) -> Shape;
}

impl<const N: usize> PoolGeometry<N> for PoolForwardProblem<N> {
    fn output_shape(&self, input_shape: &Shape) -> Shape {
        self.mode.output_shape(input_shape)
    }
}

impl<const N: usize> PoolGeometry<N> for PoolMode<N> {
    fn output_shape(&self, input_shape: &Shape) -> Shape {
        match self {
            PoolMode::Max(opts) => opts.output_shape(input_shape),
            PoolMode::Avg(opts) => opts.output_shape(input_shape),
            PoolMode::AdaptiveAvg(opts) => opts.output_shape(input_shape),
        }
    }
}

impl<const N: usize> PoolGeometry<N> for MaxPoolOptions<N> {
    fn output_shape(&self, input_shape: &Shape) -> Shape {
        let input_dims = input_shape.to_vec();
        let mut out = vec![input_dims[0]];
        for i in 0..N {
            let idx = 1 + i;
            let effective_kernel = (self.window.kernel_size[i] - 1) * self.dilation[i] + 1;
            let padded = input_dims[idx] + 2 * self.window.padding[i];
            out.push(pooled_dim(
                padded,
                effective_kernel,
                self.window.stride[i],
                self.window.ceil_mode,
            ));
        }
        out.push(input_dims[input_dims.len() - 1]);
        Shape::from(out)
    }
}

impl<const N: usize> PoolGeometry<N> for AvgPoolOptions<N> {
    fn output_shape(&self, input_shape: &Shape) -> Shape {
        let input_dims = input_shape.to_vec();
        let mut out = vec![input_dims[0]];
        for i in 0..N {
            let idx = 1 + i;
            let padded = input_dims[idx] + 2 * self.window.padding[i];
            let effective_kernel = self.window.kernel_size[i];
            out.push(pooled_dim(
                padded,
                effective_kernel,
                self.window.stride[i],
                self.window.ceil_mode,
            ));
        }
        out.push(input_dims[input_dims.len() - 1]);
        Shape::from(out)
    }
}

impl<const N: usize> PoolGeometry<N> for AdaptiveAvgPoolOptions<N> {
    fn output_shape(&self, _input_shape: &Shape) -> Shape {
        let input_dims = _input_shape.to_vec();
        let mut out = vec![input_dims[0]];
        out.extend_from_slice(&self.output_size);
        out.push(input_dims[input_dims.len() - 1]);
        Shape::from(out)
    }
}

fn pooled_dim(padded: usize, effective_kernel: usize, stride: usize, ceil_mode: bool) -> usize {
    let size = (padded as f32 - effective_kernel as f32) / stride as f32;
    (if ceil_mode { size.ceil() } else { size.floor() }) as usize + 1
}