1use crate::TorshResult;
7use torsh_core::TorshError;
8use torsh_tensor::{creation::zeros, Tensor};
9
10pub struct SparseMaxPool2d {
15 kernel_size: (usize, usize),
17 stride: (usize, usize),
19 padding: (usize, usize),
21 dilation: (usize, usize),
23}
24
25impl SparseMaxPool2d {
26 pub fn new(
28 kernel_size: (usize, usize),
29 stride: Option<(usize, usize)>,
30 padding: Option<(usize, usize)>,
31 dilation: Option<(usize, usize)>,
32 ) -> Self {
33 let stride = stride.unwrap_or(kernel_size);
34 let padding = padding.unwrap_or((0, 0));
35 let dilation = dilation.unwrap_or((1, 1));
36
37 Self {
38 kernel_size,
39 stride,
40 padding,
41 dilation,
42 }
43 }
44
45 pub fn forward(&self, input: &Tensor) -> TorshResult<Tensor> {
47 let input_shape = input.shape();
49 if input_shape.ndim() != 4 {
50 return Err(TorshError::InvalidArgument(
51 "Input must be 4D tensor (batch_size, channels, height, width)".to_string(),
52 ));
53 }
54
55 let batch_size = input_shape.dims()[0];
56 let channels = input_shape.dims()[1];
57 let input_height = input_shape.dims()[2];
58 let input_width = input_shape.dims()[3];
59
60 let output_height =
62 (input_height + 2 * self.padding.0 - self.dilation.0 * (self.kernel_size.0 - 1) - 1)
63 / self.stride.0
64 + 1;
65 let output_width =
66 (input_width + 2 * self.padding.1 - self.dilation.1 * (self.kernel_size.1 - 1) - 1)
67 / self.stride.1
68 + 1;
69
70 let mut output = zeros::<f32>(&[batch_size, channels, output_height, output_width])?;
71
72 for b in 0..batch_size {
74 for c in 0..channels {
75 self.max_pool_channel(
76 input,
77 &mut output,
78 b,
79 c,
80 input_height,
81 input_width,
82 output_height,
83 output_width,
84 )?;
85 }
86 }
87
88 Ok(output)
89 }
90
91 #[allow(clippy::too_many_arguments)]
93 fn max_pool_channel(
94 &self,
95 input: &Tensor,
96 output: &mut Tensor,
97 batch_idx: usize,
98 channel_idx: usize,
99 input_height: usize,
100 input_width: usize,
101 output_height: usize,
102 output_width: usize,
103 ) -> TorshResult<()> {
104 for out_h in 0..output_height {
105 for out_w in 0..output_width {
106 let mut max_val = f32::NEG_INFINITY;
107 let mut found_value = false;
108
109 for kh in 0..self.kernel_size.0 {
111 for kw in 0..self.kernel_size.1 {
112 let in_h = out_h * self.stride.0 + kh * self.dilation.0;
113 let in_w = out_w * self.stride.1 + kw * self.dilation.1;
114
115 if in_h >= self.padding.0 && in_w >= self.padding.1 {
117 let padded_in_h = in_h - self.padding.0;
118 let padded_in_w = in_w - self.padding.1;
119
120 if padded_in_h < input_height && padded_in_w < input_width {
122 let val = input.get(&[
123 batch_idx,
124 channel_idx,
125 padded_in_h,
126 padded_in_w,
127 ])?;
128 if val > max_val || !found_value {
129 max_val = val;
130 found_value = true;
131 }
132 }
133 }
134 }
135 }
136
137 output.set(
139 &[batch_idx, channel_idx, out_h, out_w],
140 if found_value { max_val } else { 0.0 },
141 )?;
142 }
143 }
144
145 Ok(())
146 }
147}
148
149pub struct SparseAvgPool2d {
154 kernel_size: (usize, usize),
156 stride: (usize, usize),
158 padding: (usize, usize),
160 count_include_pad: bool,
162}
163
164impl SparseAvgPool2d {
165 pub fn new(
167 kernel_size: (usize, usize),
168 stride: Option<(usize, usize)>,
169 padding: Option<(usize, usize)>,
170 count_include_pad: bool,
171 ) -> Self {
172 let stride = stride.unwrap_or(kernel_size);
173 let padding = padding.unwrap_or((0, 0));
174
175 Self {
176 kernel_size,
177 stride,
178 padding,
179 count_include_pad,
180 }
181 }
182
183 pub fn forward(&self, input: &Tensor) -> TorshResult<Tensor> {
185 let input_shape = input.shape();
187 if input_shape.ndim() != 4 {
188 return Err(TorshError::InvalidArgument(
189 "Input must be 4D tensor (batch_size, channels, height, width)".to_string(),
190 ));
191 }
192
193 let batch_size = input_shape.dims()[0];
194 let channels = input_shape.dims()[1];
195 let input_height = input_shape.dims()[2];
196 let input_width = input_shape.dims()[3];
197
198 let output_height =
200 (input_height + 2 * self.padding.0 - self.kernel_size.0) / self.stride.0 + 1;
201 let output_width =
202 (input_width + 2 * self.padding.1 - self.kernel_size.1) / self.stride.1 + 1;
203
204 let mut output = zeros::<f32>(&[batch_size, channels, output_height, output_width])?;
205
206 for b in 0..batch_size {
208 for c in 0..channels {
209 self.avg_pool_channel(
210 input,
211 &mut output,
212 b,
213 c,
214 input_height,
215 input_width,
216 output_height,
217 output_width,
218 )?;
219 }
220 }
221
222 Ok(output)
223 }
224
225 #[allow(clippy::too_many_arguments)]
227 fn avg_pool_channel(
228 &self,
229 input: &Tensor,
230 output: &mut Tensor,
231 batch_idx: usize,
232 channel_idx: usize,
233 input_height: usize,
234 input_width: usize,
235 output_height: usize,
236 output_width: usize,
237 ) -> TorshResult<()> {
238 for out_h in 0..output_height {
239 for out_w in 0..output_width {
240 let mut sum = 0.0;
241 let mut count = 0;
242
243 for kh in 0..self.kernel_size.0 {
245 for kw in 0..self.kernel_size.1 {
246 let in_h = out_h * self.stride.0 + kh;
247 let in_w = out_w * self.stride.1 + kw;
248
249 if in_h >= self.padding.0 && in_w >= self.padding.1 {
251 let padded_in_h = in_h - self.padding.0;
252 let padded_in_w = in_w - self.padding.1;
253
254 if padded_in_h < input_height && padded_in_w < input_width {
256 let val = input.get(&[
257 batch_idx,
258 channel_idx,
259 padded_in_h,
260 padded_in_w,
261 ])?;
262 sum += val;
263 count += 1;
264 } else if self.count_include_pad {
265 count += 1; }
267 } else if self.count_include_pad {
268 count += 1; }
270 }
271 }
272
273 let avg = if count > 0 { sum / count as f32 } else { 0.0 };
275 output.set(&[batch_idx, channel_idx, out_h, out_w], avg)?;
276 }
277 }
278
279 Ok(())
280 }
281}
282
283pub struct SparseMaxPool1d {
285 kernel_size: usize,
287 stride: usize,
289 padding: usize,
291 dilation: usize,
293}
294
295impl SparseMaxPool1d {
296 pub fn new(
298 kernel_size: usize,
299 stride: Option<usize>,
300 padding: Option<usize>,
301 dilation: Option<usize>,
302 ) -> Self {
303 let stride = stride.unwrap_or(kernel_size);
304 let padding = padding.unwrap_or(0);
305 let dilation = dilation.unwrap_or(1);
306
307 Self {
308 kernel_size,
309 stride,
310 padding,
311 dilation,
312 }
313 }
314
315 pub fn forward(&self, input: &Tensor) -> TorshResult<Tensor> {
317 let input_shape = input.shape();
319 if input_shape.ndim() != 3 {
320 return Err(TorshError::InvalidArgument(
321 "Input must be 3D tensor (batch_size, channels, length)".to_string(),
322 ));
323 }
324
325 let batch_size = input_shape.dims()[0];
326 let channels = input_shape.dims()[1];
327 let input_length = input_shape.dims()[2];
328
329 let output_length =
331 (input_length + 2 * self.padding - self.dilation * (self.kernel_size - 1) - 1)
332 / self.stride
333 + 1;
334
335 let output = zeros::<f32>(&[batch_size, channels, output_length])?;
336
337 for b in 0..batch_size {
339 for c in 0..channels {
340 for out_pos in 0..output_length {
341 let mut max_val = f32::NEG_INFINITY;
342 let mut found_value = false;
343
344 for k in 0..self.kernel_size {
346 let in_pos = out_pos * self.stride + k * self.dilation;
347
348 if in_pos >= self.padding {
350 let padded_in_pos = in_pos - self.padding;
351
352 if padded_in_pos < input_length {
354 let val = input.get(&[b, c, padded_in_pos])?;
355 if val > max_val || !found_value {
356 max_val = val;
357 found_value = true;
358 }
359 }
360 }
361 }
362
363 output.set(&[b, c, out_pos], if found_value { max_val } else { 0.0 })?;
365 }
366 }
367 }
368
369 Ok(output)
370 }
371}
372
373pub struct SparseAvgPool1d {
375 kernel_size: usize,
377 stride: usize,
379 padding: usize,
381 count_include_pad: bool,
383}
384
385impl SparseAvgPool1d {
386 pub fn new(
388 kernel_size: usize,
389 stride: Option<usize>,
390 padding: Option<usize>,
391 count_include_pad: bool,
392 ) -> Self {
393 let stride = stride.unwrap_or(kernel_size);
394 let padding = padding.unwrap_or(0);
395
396 Self {
397 kernel_size,
398 stride,
399 padding,
400 count_include_pad,
401 }
402 }
403
404 pub fn forward(&self, input: &Tensor) -> TorshResult<Tensor> {
406 let input_shape = input.shape();
408 if input_shape.ndim() != 3 {
409 return Err(TorshError::InvalidArgument(
410 "Input must be 3D tensor (batch_size, channels, length)".to_string(),
411 ));
412 }
413
414 let batch_size = input_shape.dims()[0];
415 let channels = input_shape.dims()[1];
416 let input_length = input_shape.dims()[2];
417
418 let output_length = (input_length + 2 * self.padding - self.kernel_size) / self.stride + 1;
420
421 let output = zeros::<f32>(&[batch_size, channels, output_length])?;
422
423 for b in 0..batch_size {
425 for c in 0..channels {
426 for out_pos in 0..output_length {
427 let mut sum = 0.0;
428 let mut count = 0;
429
430 for k in 0..self.kernel_size {
432 let in_pos = out_pos * self.stride + k;
433
434 if in_pos >= self.padding {
436 let padded_in_pos = in_pos - self.padding;
437
438 if padded_in_pos < input_length {
440 let val = input.get(&[b, c, padded_in_pos])?;
441 sum += val;
442 count += 1;
443 } else if self.count_include_pad {
444 count += 1; }
446 } else if self.count_include_pad {
447 count += 1; }
449 }
450
451 let avg = if count > 0 { sum / count as f32 } else { 0.0 };
453 output.set(&[b, c, out_pos], avg)?;
454 }
455 }
456 }
457
458 Ok(output)
459 }
460}
461
462pub struct SparseAdaptiveMaxPool2d {
467 output_size: (usize, usize),
469}
470
471impl SparseAdaptiveMaxPool2d {
472 pub fn new(output_size: (usize, usize)) -> Self {
474 Self { output_size }
475 }
476
477 pub fn forward(&self, input: &Tensor) -> TorshResult<Tensor> {
479 let input_shape = input.shape();
481 if input_shape.ndim() != 4 {
482 return Err(TorshError::InvalidArgument(
483 "Input must be 4D tensor (batch_size, channels, height, width)".to_string(),
484 ));
485 }
486
487 let batch_size = input_shape.dims()[0];
488 let channels = input_shape.dims()[1];
489 let input_height = input_shape.dims()[2];
490 let input_width = input_shape.dims()[3];
491
492 let output_height = self.output_size.0;
493 let output_width = self.output_size.1;
494
495 let output = zeros::<f32>(&[batch_size, channels, output_height, output_width])?;
496
497 for b in 0..batch_size {
499 for c in 0..channels {
500 for out_h in 0..output_height {
501 for out_w in 0..output_width {
502 let start_h = (out_h * input_height) / output_height;
504 let end_h = ((out_h + 1) * input_height).div_ceil(output_height);
505 let start_w = (out_w * input_width) / output_width;
506 let end_w = ((out_w + 1) * input_width).div_ceil(output_width);
507
508 let mut max_val = f32::NEG_INFINITY;
509 let mut found_value = false;
510
511 for h in start_h..end_h {
513 for w in start_w..end_w {
514 if h < input_height && w < input_width {
515 let val = input.get(&[b, c, h, w])?;
516 if val > max_val || !found_value {
517 max_val = val;
518 found_value = true;
519 }
520 }
521 }
522 }
523
524 output.set(
525 &[b, c, out_h, out_w],
526 if found_value { max_val } else { 0.0 },
527 )?;
528 }
529 }
530 }
531 }
532
533 Ok(output)
534 }
535}
536
537pub struct SparseAdaptiveAvgPool2d {
542 output_size: (usize, usize),
544}
545
546impl SparseAdaptiveAvgPool2d {
547 pub fn new(output_size: (usize, usize)) -> Self {
549 Self { output_size }
550 }
551
552 pub fn forward(&self, input: &Tensor) -> TorshResult<Tensor> {
554 let input_shape = input.shape();
556 if input_shape.ndim() != 4 {
557 return Err(TorshError::InvalidArgument(
558 "Input must be 4D tensor (batch_size, channels, height, width)".to_string(),
559 ));
560 }
561
562 let batch_size = input_shape.dims()[0];
563 let channels = input_shape.dims()[1];
564 let input_height = input_shape.dims()[2];
565 let input_width = input_shape.dims()[3];
566
567 let output_height = self.output_size.0;
568 let output_width = self.output_size.1;
569
570 let output = zeros::<f32>(&[batch_size, channels, output_height, output_width])?;
571
572 for b in 0..batch_size {
574 for c in 0..channels {
575 for out_h in 0..output_height {
576 for out_w in 0..output_width {
577 let start_h = (out_h * input_height) / output_height;
579 let end_h = ((out_h + 1) * input_height).div_ceil(output_height);
580 let start_w = (out_w * input_width) / output_width;
581 let end_w = ((out_w + 1) * input_width).div_ceil(output_width);
582
583 let mut sum = 0.0;
584 let mut count = 0;
585
586 for h in start_h..end_h {
588 for w in start_w..end_w {
589 if h < input_height && w < input_width {
590 let val = input.get(&[b, c, h, w])?;
591 sum += val;
592 count += 1;
593 }
594 }
595 }
596
597 let avg = if count > 0 { sum / count as f32 } else { 0.0 };
598 output.set(&[b, c, out_h, out_w], avg)?;
599 }
600 }
601 }
602 }
603
604 Ok(output)
605 }
606}