Skip to main content

burn_ndarray/ops/
module.rs

1use super::{
2    adaptive_avgpool::{adaptive_avg_pool2d, adaptive_avg_pool2d_backward},
3    avgpool::{avg_pool2d, avg_pool2d_backward},
4    conv::{conv_transpose2d, conv_transpose3d, conv2d, conv3d},
5    deform_conv::{backward::deform_conv2d_backward, deform_conv2d},
6    interpolate::{
7        bicubic_interpolate, bilinear_interpolate, lanczos3_interpolate, nearest_interpolate,
8    },
9    maxpool::{max_pool2d, max_pool2d_backward, max_pool2d_with_indices},
10};
11use crate::ops::interpolate::nearest_interpolate_backward;
12#[cfg(feature = "simd")]
13use crate::ops::simd::{
14    avgpool::try_avg_pool2d_simd, conv::try_conv2d_simd, maxpool::try_max_pool2d_simd,
15};
16use crate::{
17    NdArray, SharedArray, execute_with_int_dtype, execute_with_int_out_dtype, tensor::NdArrayTensor,
18};
19use burn_backend::{
20    TensorMetadata,
21    ops::{attention::attention_fallback, *},
22    tensor::FloatTensor,
23};
24use burn_std::IntDType;
25
26macro_rules! module_op {
27    // Module op with inputs (inp), optional (opt) and arguments (args).
28    // Converts NdArrayStorage to SharedArray for compatibility with existing operations.
29    (inp($($x:tt),+), opt($($opt:tt),*), $element:ident, $op:expr) => {{
30        #[allow(unused_parens, unreachable_patterns)]
31        match ($($x),+) {
32            ($(NdArrayTensor::F32($x)),+) => {
33                type $element = f32;
34                $op(
35                    $($x.into_shared()),+
36                    $(, $opt.map(|o| match o { NdArrayTensor::F32(val) => val.into_shared(), _ => panic!("Optional argument type mismatch") }))*
37                )
38            }
39            ($(NdArrayTensor::F64($x)),+) => {
40                type $element = f64;
41                $op(
42                    $($x.into_shared()),+
43                    $(, $opt.map(|o| match o { NdArrayTensor::F64(val) => val.into_shared(), _ => panic!("Optional argument type mismatch") }))*
44                )
45            }
46            _ => panic!("Data type mismatch"),
47        }
48    }};
49}
50
51impl ModuleOps<Self> for NdArray {
52    fn conv2d(
53        x: NdArrayTensor,
54        weight: NdArrayTensor,
55        bias: Option<NdArrayTensor>,
56        options: ConvOptions<2>,
57    ) -> NdArrayTensor {
58        module_op!(inp(x, weight), opt(bias), E, |x, weight, bias| {
59            #[cfg(feature = "simd")]
60            let (x, weight, bias) = match try_conv2d_simd(x, weight, bias, options.clone()) {
61                Ok(out) => return out.into(),
62                Err(args) => args,
63            };
64            conv2d::<E>(x, weight, bias, options).into()
65        })
66    }
67
68    fn deform_conv2d(
69        x: FloatTensor<Self>,
70        offset: FloatTensor<Self>,
71        weight: FloatTensor<Self>,
72        mask: Option<FloatTensor<Self>>,
73        bias: Option<FloatTensor<Self>>,
74        options: DeformConvOptions<2>,
75    ) -> FloatTensor<Self> {
76        module_op!(
77            inp(x, offset, weight),
78            opt(mask, bias),
79            E,
80            |x, offset, weight, mask, bias| deform_conv2d::<E>(
81                x, offset, weight, mask, bias, options
82            )
83            .into()
84        )
85    }
86
87    fn deform_conv2d_backward(
88        x: FloatTensor<Self>,
89        offset: FloatTensor<Self>,
90        weight: FloatTensor<Self>,
91        mask: Option<FloatTensor<Self>>,
92        bias: Option<FloatTensor<Self>>,
93        output_grad: FloatTensor<Self>,
94        options: DeformConvOptions<2>,
95    ) -> DeformConv2dBackward<Self> {
96        module_op!(
97            inp(x, offset, weight, output_grad),
98            opt(mask, bias),
99            E,
100            |x, offset, weight, output_grad, mask, bias| {
101                let (x, offset, weight, mask, bias) = deform_conv2d_backward::<E>(
102                    x,
103                    offset,
104                    weight,
105                    mask,
106                    bias,
107                    output_grad,
108                    options,
109                );
110                DeformConv2dBackward::new(
111                    x.into(),
112                    offset.into(),
113                    weight.into(),
114                    mask.map(|m| m.into()),
115                    bias.map(|b| b.into()),
116                )
117            }
118        )
119    }
120
121    fn conv_transpose2d(
122        x: FloatTensor<Self>,
123        weight: FloatTensor<Self>,
124        bias: Option<FloatTensor<Self>>,
125        options: ConvTransposeOptions<2>,
126    ) -> FloatTensor<Self> {
127        module_op!(inp(x, weight), opt(bias), E, |x, weight, bias| {
128            conv_transpose2d::<E>(x, weight, bias, options).into()
129        })
130    }
131
132    fn avg_pool2d(
133        x: FloatTensor<Self>,
134        kernel_size: [usize; 2],
135        stride: [usize; 2],
136        padding: [usize; 2],
137        count_include_pad: bool,
138        ceil_mode: bool,
139    ) -> FloatTensor<Self> {
140        module_op!(inp(x), opt(), E, |x| {
141            #[cfg(feature = "simd")]
142            let x = match if ceil_mode {
143                // SIMD path doesn't support ceil_mode yet, skip it
144                Err(x)
145            } else {
146                try_avg_pool2d_simd(x, kernel_size, stride, padding, count_include_pad)
147            } {
148                Ok(out) => return out.into(),
149                Err(x) => x,
150            };
151            avg_pool2d::<E>(
152                x,
153                kernel_size,
154                stride,
155                padding,
156                count_include_pad,
157                ceil_mode,
158            )
159            .into()
160        })
161    }
162
163    fn avg_pool2d_backward(
164        x: FloatTensor<Self>,
165        grad: FloatTensor<Self>,
166        kernel_size: [usize; 2],
167        stride: [usize; 2],
168        padding: [usize; 2],
169        count_include_pad: bool,
170        ceil_mode: bool,
171    ) -> FloatTensor<Self> {
172        module_op!(inp(x, grad), opt(), E, |x, grad| avg_pool2d_backward::<E>(
173            x,
174            grad,
175            kernel_size,
176            stride,
177            padding,
178            count_include_pad,
179            ceil_mode
180        )
181        .into())
182    }
183
184    fn max_pool2d(
185        x: FloatTensor<Self>,
186        kernel_size: [usize; 2],
187        stride: [usize; 2],
188        padding: [usize; 2],
189        dilation: [usize; 2],
190        ceil_mode: bool,
191    ) -> FloatTensor<Self> {
192        module_op!(inp(x), opt(), E, |x| {
193            #[cfg(feature = "simd")]
194            let x = match if ceil_mode {
195                // SIMD path doesn't support ceil_mode yet, skip it
196                Err(x)
197            } else {
198                try_max_pool2d_simd(x, kernel_size, stride, padding, dilation)
199            } {
200                Ok(out) => return out.into(),
201                Err(x) => x,
202            };
203            max_pool2d::<E>(x, kernel_size, stride, padding, dilation, ceil_mode).into()
204        })
205    }
206
207    fn max_pool2d_with_indices(
208        x: FloatTensor<Self>,
209        kernel_size: [usize; 2],
210        stride: [usize; 2],
211        padding: [usize; 2],
212        dilation: [usize; 2],
213        ceil_mode: bool,
214        indices_dtype: IntDType,
215    ) -> MaxPool2dWithIndices<Self> {
216        execute_with_int_out_dtype!(indices_dtype, I, {
217            module_op!(inp(x), opt(), E, |x| {
218                let (output, indices) = max_pool2d_with_indices::<E, I>(
219                    x,
220                    kernel_size,
221                    stride,
222                    padding,
223                    dilation,
224                    ceil_mode,
225                );
226                MaxPool2dWithIndices::new(output.into(), indices.into())
227            })
228        })
229    }
230
231    fn max_pool2d_with_indices_backward(
232        x: FloatTensor<Self>,
233        kernel_size: [usize; 2],
234        stride: [usize; 2],
235        padding: [usize; 2],
236        dilation: [usize; 2],
237        ceil_mode: bool,
238        output_grad: FloatTensor<Self>,
239        indices: NdArrayTensor,
240    ) -> MaxPool2dBackward<Self> {
241        execute_with_int_dtype!(indices, IntElem, |idx_s: SharedArray<IntElem>| {
242            module_op!(inp(x, output_grad), opt(), E, |x, output_grad| {
243                let output = max_pool2d_backward::<E, IntElem>(
244                    x,
245                    kernel_size,
246                    stride,
247                    padding,
248                    dilation,
249                    ceil_mode,
250                    output_grad,
251                    idx_s,
252                );
253                MaxPool2dBackward::new(output.into())
254            })
255        })
256    }
257
258    fn adaptive_avg_pool2d(x: FloatTensor<Self>, output_size: [usize; 2]) -> FloatTensor<Self> {
259        module_op!(inp(x), opt(), E, |x| adaptive_avg_pool2d::<E>(
260            x,
261            output_size
262        )
263        .into())
264    }
265
266    fn adaptive_avg_pool2d_backward(
267        x: FloatTensor<Self>,
268        grad: FloatTensor<Self>,
269    ) -> FloatTensor<Self> {
270        module_op!(inp(x, grad), opt(), E, |x, grad| {
271            adaptive_avg_pool2d_backward::<E>(x, grad).into()
272        })
273    }
274
275    fn interpolate(
276        x: FloatTensor<Self>,
277        output_size: [usize; 2],
278        options: InterpolateOptions,
279    ) -> FloatTensor<Self> {
280        match options.mode {
281            InterpolateMode::Nearest => {
282                module_op!(inp(x), opt(), E, |x| nearest_interpolate::<E>(
283                    x,
284                    output_size
285                )
286                .into())
287            }
288            InterpolateMode::NearestExact => {
289                panic!("nearest exact interpolation is not supported for ndarray backend")
290            }
291            InterpolateMode::Bilinear => {
292                let align_corners = options.align_corners;
293                module_op!(inp(x), opt(), E, |x| bilinear_interpolate::<E>(
294                    x,
295                    output_size,
296                    align_corners
297                )
298                .into())
299            }
300            InterpolateMode::Bicubic => {
301                let align_corners = options.align_corners;
302                module_op!(inp(x), opt(), E, |x| bicubic_interpolate::<E>(
303                    x,
304                    output_size,
305                    align_corners
306                )
307                .into())
308            }
309            InterpolateMode::Lanczos3 => {
310                let align_corners = options.align_corners;
311                module_op!(inp(x), opt(), E, |x| lanczos3_interpolate::<E>(
312                    x,
313                    output_size,
314                    align_corners
315                )
316                .into())
317            }
318        }
319    }
320
321    fn interpolate_backward(
322        x: FloatTensor<Self>,
323        grad: FloatTensor<Self>,
324        output_size: [usize; 2],
325        options: InterpolateOptions,
326    ) -> FloatTensor<Self> {
327        match options.mode {
328            InterpolateMode::Nearest => module_op!(inp(x, grad), opt(), E, |x, grad| {
329                nearest_interpolate_backward::<E>(x, grad, output_size).into()
330            }),
331            InterpolateMode::NearestExact => {
332                panic!("nearest exact interpolation backward is not supported for ndarray backend")
333            }
334            InterpolateMode::Bilinear => {
335                panic!("bilinear interpolation backward is not supported for ndarray backend")
336            }
337            InterpolateMode::Bicubic => {
338                panic!("bicubic interpolation backward is not supported for ndarray backend")
339            }
340            InterpolateMode::Lanczos3 => {
341                panic!("lanczos3 interpolation backward is not supported for ndarray backend")
342            }
343        }
344    }
345
346    fn conv3d(
347        x: FloatTensor<Self>,
348        weight: FloatTensor<Self>,
349        bias: Option<FloatTensor<Self>>,
350        options: ConvOptions<3>,
351    ) -> FloatTensor<Self> {
352        module_op!(inp(x, weight), opt(bias), E, |x, weight, bias| conv3d::<E>(
353            x, weight, bias, options
354        )
355        .into())
356    }
357
358    fn conv_transpose3d(
359        x: FloatTensor<Self>,
360        weight: FloatTensor<Self>,
361        bias: Option<FloatTensor<Self>>,
362        options: ConvTransposeOptions<3>,
363    ) -> FloatTensor<Self> {
364        module_op!(inp(x, weight), opt(bias), E, |x, weight, bias| {
365            conv_transpose3d::<E>(x, weight, bias, options).into()
366        })
367    }
368
369    fn attention(
370        query: FloatTensor<Self>,
371        key: FloatTensor<Self>,
372        value: FloatTensor<Self>,
373        mask: Option<burn_backend::tensor::BoolTensor<Self>>,
374        attn_bias: Option<FloatTensor<Self>>,
375        options: AttentionModuleOptions,
376    ) -> FloatTensor<Self> {
377        attention_fallback::<Self>(query, key, value, mask, attn_bias, options)
378    }
379
380    fn rfft(
381        _signal: FloatTensor<Self>,
382        _dim: usize,
383        _n: Option<usize>,
384    ) -> (FloatTensor<Self>, FloatTensor<Self>) {
385        todo!("rfft is not supported for ndarray")
386    }
387
388    fn irfft(
389        _spectrum_re: FloatTensor<Self>,
390        _spectrum_im: FloatTensor<Self>,
391        _dim: usize,
392        _n: Option<usize>,
393    ) -> FloatTensor<Self> {
394        todo!("irfft is not supported for ndarray")
395    }
396}