Skip to main content

burn_ndarray/ops/
module.rs

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