Skip to main content

tenflowers_neural/layers/pooling/
pool2d.rs

1use crate::layers::Layer;
2use scirs2_core::num_traits::{Float, FromPrimitive, One, Zero};
3use tenflowers_core::{Result, Tensor};
4
5#[derive(Clone)]
6pub struct MaxPool2D {
7    #[allow(dead_code)]
8    kernel_size: (usize, usize),
9    #[allow(dead_code)]
10    stride: (usize, usize),
11    #[allow(dead_code)]
12    padding: String,
13}
14
15impl MaxPool2D {
16    pub fn new(kernel_size: (usize, usize), stride: Option<(usize, usize)>) -> Self {
17        Self {
18            kernel_size,
19            stride: stride.unwrap_or(kernel_size),
20            padding: "valid".to_string(),
21        }
22    }
23}
24
25impl<T> Layer<T> for MaxPool2D
26where
27    T: Clone
28        + Default
29        + Zero
30        + PartialOrd
31        + Send
32        + Sync
33        + 'static
34        + bytemuck::Pod
35        + bytemuck::Zeroable,
36{
37    fn forward(&self, input: &Tensor<T>) -> Result<Tensor<T>> {
38        tenflowers_core::ops::max_pool2d(input, self.kernel_size, self.stride, &self.padding)
39    }
40
41    fn parameters(&self) -> Vec<&Tensor<T>> {
42        vec![]
43    }
44
45    fn parameters_mut(&mut self) -> Vec<&mut Tensor<T>> {
46        vec![]
47    }
48
49    fn set_training(&mut self, _training: bool) {
50        // Pooling layers don't have different behavior in training/eval mode
51    }
52
53    fn clone_box(&self) -> Box<dyn Layer<T>> {
54        Box::new(self.clone())
55    }
56}
57
58/// Adaptive max pooling 2D forward implementation
59fn adaptive_max_pool2d_forward<T>(
60    input: &Tensor<T>,
61    output_size: (usize, usize),
62    input_shape: (usize, usize, usize, usize), // (batch, channels, height, width)
63) -> Result<Tensor<T>>
64where
65    T: Clone + Default + Zero + One + PartialOrd + Send + Sync + 'static,
66{
67    let (batch_size, channels, input_height, input_width) = input_shape;
68    let (output_height, output_width) = output_size;
69
70    // Get input data
71    let input_data = input.as_slice().ok_or_else(|| {
72        tenflowers_core::TensorError::device_error_simple(
73            "Cannot access input tensor data".to_string(),
74        )
75    })?;
76
77    // Initialize output tensor
78    let total_output_elements = batch_size * channels * output_height * output_width;
79    let mut output_data = vec![T::zero(); total_output_elements];
80
81    // Calculate adaptive pooling regions
82    for b in 0..batch_size {
83        for c in 0..channels {
84            for oh in 0..output_height {
85                for ow in 0..output_width {
86                    // Calculate input region for this output pixel
87                    let ih_start = (oh * input_height) / output_height;
88                    let ih_end = ((oh + 1) * input_height + output_height - 1) / output_height;
89                    let iw_start = (ow * input_width) / output_width;
90                    let iw_end = ((ow + 1) * input_width + output_width - 1) / output_width;
91
92                    // Find maximum in this region
93                    let mut max_val = T::zero();
94                    let mut first = true;
95
96                    for ih in ih_start..ih_end {
97                        for iw in iw_start..iw_end {
98                            if ih < input_height && iw < input_width {
99                                let input_idx = b * channels * input_height * input_width
100                                    + c * input_height * input_width
101                                    + ih * input_width
102                                    + iw;
103
104                                if first || input_data[input_idx] > max_val {
105                                    max_val = input_data[input_idx].clone();
106                                    first = false;
107                                }
108                            }
109                        }
110                    }
111
112                    // Store result
113                    let output_idx = b * channels * output_height * output_width
114                        + c * output_height * output_width
115                        + oh * output_width
116                        + ow;
117                    output_data[output_idx] = max_val;
118                }
119            }
120        }
121    }
122
123    Tensor::from_data(
124        output_data,
125        &[batch_size, channels, output_height, output_width],
126    )
127}
128
129/// Adaptive average pooling 2D forward implementation
130fn adaptive_avg_pool2d_forward<T>(
131    input: &Tensor<T>,
132    output_size: (usize, usize),
133    input_shape: (usize, usize, usize, usize), // (batch, channels, height, width)
134) -> Result<Tensor<T>>
135where
136    T: Clone + Default + Zero + One + Float + FromPrimitive + Send + Sync + 'static,
137{
138    let (batch_size, channels, input_height, input_width) = input_shape;
139    let (output_height, output_width) = output_size;
140
141    // Get input data
142    let input_data = input.as_slice().ok_or_else(|| {
143        tenflowers_core::TensorError::device_error_simple(
144            "Cannot access input tensor data".to_string(),
145        )
146    })?;
147
148    // Initialize output tensor
149    let total_output_elements = batch_size * channels * output_height * output_width;
150    let mut output_data = vec![T::zero(); total_output_elements];
151
152    // Calculate adaptive pooling regions
153    for b in 0..batch_size {
154        for c in 0..channels {
155            for oh in 0..output_height {
156                for ow in 0..output_width {
157                    // Calculate input region for this output pixel
158                    let ih_start = (oh * input_height) / output_height;
159                    let ih_end = ((oh + 1) * input_height + output_height - 1) / output_height;
160                    let iw_start = (ow * input_width) / output_width;
161                    let iw_end = ((ow + 1) * input_width + output_width - 1) / output_width;
162
163                    // Calculate average in this region
164                    let mut sum = T::zero();
165                    let mut count = 0;
166
167                    for ih in ih_start..ih_end {
168                        for iw in iw_start..iw_end {
169                            if ih < input_height && iw < input_width {
170                                let input_idx = b * channels * input_height * input_width
171                                    + c * input_height * input_width
172                                    + ih * input_width
173                                    + iw;
174
175                                sum = sum + input_data[input_idx];
176                                count += 1;
177                            }
178                        }
179                    }
180
181                    // Calculate average
182                    let avg = if count > 0 {
183                        sum / T::from_usize(count).unwrap_or(T::one())
184                    } else {
185                        T::zero()
186                    };
187
188                    // Store result
189                    let output_idx = b * channels * output_height * output_width
190                        + c * output_height * output_width
191                        + oh * output_width
192                        + ow;
193                    output_data[output_idx] = avg;
194                }
195            }
196        }
197    }
198
199    Tensor::from_data(
200        output_data,
201        &[batch_size, channels, output_height, output_width],
202    )
203}
204
205#[derive(Clone)]
206pub struct AvgPool2D {
207    #[allow(dead_code)]
208    kernel_size: (usize, usize),
209    #[allow(dead_code)]
210    stride: (usize, usize),
211    #[allow(dead_code)]
212    padding: String,
213}
214
215impl AvgPool2D {
216    pub fn new(kernel_size: (usize, usize), stride: Option<(usize, usize)>) -> Self {
217        Self {
218            kernel_size,
219            stride: stride.unwrap_or(kernel_size),
220            padding: "valid".to_string(),
221        }
222    }
223}
224
225impl<T> Layer<T> for AvgPool2D
226where
227    T: Clone
228        + Default
229        + Zero
230        + Float
231        + FromPrimitive
232        + Send
233        + Sync
234        + 'static
235        + bytemuck::Pod
236        + bytemuck::Zeroable,
237{
238    fn forward(&self, input: &Tensor<T>) -> Result<Tensor<T>> {
239        tenflowers_core::ops::avg_pool2d(input, self.kernel_size, self.stride, &self.padding)
240    }
241
242    fn parameters(&self) -> Vec<&Tensor<T>> {
243        vec![]
244    }
245
246    fn parameters_mut(&mut self) -> Vec<&mut Tensor<T>> {
247        vec![]
248    }
249
250    fn set_training(&mut self, _training: bool) {
251        // Pooling layers don't have different behavior in training/eval mode
252    }
253
254    fn clone_box(&self) -> Box<dyn Layer<T>> {
255        Box::new(self.clone())
256    }
257}
258
259#[derive(Clone)]
260pub struct AdaptiveMaxPool2D {
261    output_size: (usize, usize),
262}
263
264impl AdaptiveMaxPool2D {
265    pub fn new(output_size: (usize, usize)) -> Self {
266        Self { output_size }
267    }
268}
269
270impl<T> Layer<T> for AdaptiveMaxPool2D
271where
272    T: Clone + Default + Zero + One + PartialOrd + Send + Sync + 'static,
273{
274    fn forward(&self, input: &Tensor<T>) -> Result<Tensor<T>> {
275        let input_shape = input.shape().dims();
276        if input_shape.len() != 4 {
277            return Err(tenflowers_core::TensorError::invalid_shape(
278                "AdaptiveMaxPool2D",
279                "4D tensor [batch, channels, height, width]",
280                &format!("{}D tensor", input_shape.len()),
281            ));
282        }
283
284        let shape_tuple = (
285            input_shape[0],
286            input_shape[1],
287            input_shape[2],
288            input_shape[3],
289        );
290        adaptive_max_pool2d_forward(input, self.output_size, shape_tuple)
291    }
292
293    fn parameters(&self) -> Vec<&Tensor<T>> {
294        vec![]
295    }
296
297    fn parameters_mut(&mut self) -> Vec<&mut Tensor<T>> {
298        vec![]
299    }
300
301    fn set_training(&mut self, _training: bool) {
302        // Adaptive pooling layers don't have different behavior in training/eval mode
303    }
304
305    fn clone_box(&self) -> Box<dyn Layer<T>> {
306        Box::new(self.clone())
307    }
308}
309
310#[derive(Clone)]
311pub struct AdaptiveAvgPool2D {
312    output_size: (usize, usize),
313}
314
315impl AdaptiveAvgPool2D {
316    pub fn new(output_size: (usize, usize)) -> Self {
317        Self { output_size }
318    }
319}
320
321impl<T> Layer<T> for AdaptiveAvgPool2D
322where
323    T: Clone + Default + Zero + One + Float + FromPrimitive + Send + Sync + 'static,
324{
325    fn forward(&self, input: &Tensor<T>) -> Result<Tensor<T>> {
326        let input_shape = input.shape().dims();
327        if input_shape.len() != 4 {
328            return Err(tenflowers_core::TensorError::invalid_shape(
329                "AdaptiveAvgPool2D",
330                "4D tensor [batch, channels, height, width]",
331                &format!("{}D tensor", input_shape.len()),
332            ));
333        }
334
335        let shape_tuple = (
336            input_shape[0],
337            input_shape[1],
338            input_shape[2],
339            input_shape[3],
340        );
341        adaptive_avg_pool2d_forward(input, self.output_size, shape_tuple)
342    }
343
344    fn parameters(&self) -> Vec<&Tensor<T>> {
345        vec![]
346    }
347
348    fn parameters_mut(&mut self) -> Vec<&mut Tensor<T>> {
349        vec![]
350    }
351
352    fn set_training(&mut self, _training: bool) {
353        // Adaptive pooling layers don't have different behavior in training/eval mode
354    }
355
356    fn clone_box(&self) -> Box<dyn Layer<T>> {
357        Box::new(self.clone())
358    }
359}