Skip to main content

burn_tensor/tensor/api/
pad.rs

1use alloc::vec::Vec;
2
3use crate::{Tensor, kind::Numeric, ops::PadMode};
4
5/// Trait for types that can be used as padding specifications.
6///
7/// Padding is specified as `(before, after)` pairs per dimension, returned as a
8/// fixed-size array `[(usize, usize); D]`. If fewer pairs than dimensions are provided,
9/// they apply to the **last** N dimensions (earlier dimensions are left unpadded).
10pub trait IntoPadding<const D: usize> {
11    /// Converts into a fixed-size array of `(before, after)` padding pairs.
12    fn into_padding(self) -> [(usize, usize); D];
13}
14
15impl<const D: usize, const N: usize> IntoPadding<D> for [(usize, usize); N] {
16    fn into_padding(self) -> [(usize, usize); D] {
17        assert!(
18            N <= D,
19            "Padding has {} pairs but tensor only has {} dimensions",
20            N,
21            D
22        );
23        let mut result = [(0usize, 0usize); D];
24        let offset = D - N;
25        for (i, pair) in self.into_iter().enumerate() {
26            result[offset + i] = pair;
27        }
28        result
29    }
30}
31
32/// Backward-compatible: `(left, right, top, bottom)` maps to last 2 dimensions.
33///
34/// Equivalent to `[(top, bottom), (left, right)]`.
35impl<const D: usize> IntoPadding<D> for (usize, usize, usize, usize) {
36    fn into_padding(self) -> [(usize, usize); D] {
37        let (left, right, top, bottom) = self;
38        let mut result = [(0usize, 0usize); D];
39        result[D - 2] = (top, bottom);
40        result[D - 1] = (left, right);
41        result
42    }
43}
44
45impl<const D: usize> IntoPadding<D> for &[(usize, usize)] {
46    fn into_padding(self) -> [(usize, usize); D] {
47        assert!(
48            self.len() <= D,
49            "Padding has {} pairs but tensor only has {} dimensions",
50            self.len(),
51            D
52        );
53        let mut result = [(0usize, 0usize); D];
54        let offset = D - self.len();
55        for (i, &pair) in self.iter().enumerate() {
56            result[offset + i] = pair;
57        }
58        result
59    }
60}
61
62impl<const D: usize> IntoPadding<D> for Vec<(usize, usize)> {
63    fn into_padding(self) -> [(usize, usize); D] {
64        assert!(
65            self.len() <= D,
66            "Padding has {} pairs but tensor only has {} dimensions",
67            self.len(),
68            D
69        );
70        let mut result = [(0usize, 0usize); D];
71        let offset = D - self.len();
72        for (i, pair) in self.into_iter().enumerate() {
73            result[offset + i] = pair;
74        }
75        result
76    }
77}
78
79impl<const D: usize, K> Tensor<D, K>
80where
81    K: Numeric,
82{
83    /// Pads the tensor using the specified padding mode.
84    ///
85    /// Padding is specified as `(before, after)` pairs. If fewer pairs than tensor dimensions
86    /// are provided, they apply to the **last** N dimensions (unspecified leading dimensions
87    /// are left unpadded).
88    ///
89    /// For backward compatibility, a `(left, right, top, bottom)` tuple is also accepted,
90    /// which pads the last two dimensions.
91    ///
92    /// # Arguments
93    ///
94    /// * `padding` - Padding specification. Accepts:
95    ///   - `[(before, after); N]` fixed-size array of pairs (N <= D)
96    ///   - `&[(before, after)]` slice of pairs per dimension
97    ///   - `Vec<(before, after)>` vector of pairs
98    ///   - `(left, right, top, bottom)` tuple for last-2-dim backward compatibility
99    /// * `mode` - The padding mode: `Constant(value)`, `Reflect`, or `Edge`.
100    ///
101    /// # Returns
102    ///
103    /// A new tensor with the specified padding applied.
104    ///
105    /// # Panics
106    ///
107    /// - Panics if more padding pairs are provided than tensor dimensions.
108    /// - `Reflect` mode panics if padding exceeds `dimension_size - 1`.
109    /// - `Edge` mode panics if padding is applied to a zero-sized dimension.
110    ///
111    /// # Example
112    ///
113    /// ```rust
114    /// use burn_tensor::{Tensor, Shape};
115    /// use burn_tensor::ops::PadMode;
116    ///
117    /// let device = Default::default();
118    /// let tensor = Tensor::<2>::from_data([[12.0, -2.0, 3.0], [5.0, 3.0, 6.0]], &device);
119    ///
120    /// // Constant padding with value 0.0 (backward-compatible tuple)
121    /// let padded = tensor.clone().pad((1, 1, 1, 1), PadMode::Constant(0.0));
122    ///
123    /// // Pad arbitrary dimensions with slice of (before, after) pairs
124    /// let padded = tensor.clone().pad([(1, 1), (2, 2)], PadMode::Constant(0.0));
125    ///
126    /// // Pad only the last dimension
127    /// let padded = tensor.pad([(1, 1)], PadMode::Reflect);
128    /// ```
129    pub fn pad(self, padding: impl IntoPadding<D>, mode: impl Into<PadMode>) -> Self {
130        let pairs = padding.into_padding();
131        Tensor::new(K::pad(self.primitive, &pairs, mode.into()))
132    }
133}