1use burn_backend::ops::ModuleOps;
2use burn_dispatch::Dispatch;
3use burn_std::{MatmulTransformAction, MatmulTransformAnalysis, MatmulTransformPolicy};
4
5use crate::{
6 Bool, DType, Int, Tensor, check,
7 check::TensorCheck,
8 ops::{
9 AttentionModuleOptions, BridgeTensor, ConvOptions, ConvTransposeOptions, DeformConvOptions,
10 InterpolateOptions, PadMode, PaddedConvOptions, UnfoldOptions,
11 },
12};
13
14pub fn ctc_loss(
28 log_probs: Tensor<3>,
29 targets: Tensor<2, Int>,
30 input_lengths: Tensor<1, Int>,
31 target_lengths: Tensor<1, Int>,
32 blank: usize,
33) -> Tensor<1> {
34 Tensor::new(BridgeTensor::float(Dispatch::ctc_loss(
35 log_probs.primitive.into_float(),
36 targets.primitive.into(),
37 input_lengths.primitive.into(),
38 target_lengths.primitive.into(),
39 blank,
40 )))
41}
42
43pub fn embedding(weights: Tensor<2>, indices: Tensor<2, Int>) -> Tensor<3> {
45 Tensor::new(BridgeTensor::float(Dispatch::embedding(
46 weights.primitive.into_float(),
47 indices.primitive.into(),
48 )))
49}
50
51pub fn conv1d(
57 x: Tensor<3>,
58 weight: Tensor<3>,
59 bias: Option<Tensor<1>>,
60 options: impl Into<PaddedConvOptions<1>>,
61) -> Tensor<3> {
62 let padded_options = options.into();
63 check!(TensorCheck::conv(
64 "conv1d",
65 x.dims(),
66 weight.dims(),
67 padded_options.options.groups,
68 ));
69
70 if let Some(padding_end) = padded_options.padding_end {
71 let left = padded_options.options.padding[0];
72 let right = padding_end[0];
73 let padded = x.pad((left, right, 0, 0), PadMode::Constant(0.0));
75 let zero_options = ConvOptions::new(
76 padded_options.options.stride,
77 [0],
78 padded_options.options.dilation,
79 padded_options.options.groups,
80 );
81 Tensor::new(BridgeTensor::float(Dispatch::conv1d(
82 padded.primitive.into_float(),
83 weight.primitive.into_float(),
84 bias.map(|b| b.primitive.into_float()),
85 zero_options,
86 )))
87 } else {
88 Tensor::new(BridgeTensor::float(Dispatch::conv1d(
89 x.primitive.into_float(),
90 weight.primitive.into_float(),
91 bias.map(|b| b.primitive.into_float()),
92 padded_options.options,
93 )))
94 }
95}
96
97pub fn conv2d(
103 x: Tensor<4>,
104 weight: Tensor<4>,
105 bias: Option<Tensor<1>>,
106 options: impl Into<PaddedConvOptions<2>>,
107) -> Tensor<4> {
108 let padded_options = options.into();
109 check!(TensorCheck::conv(
110 "conv2d",
111 x.dims(),
112 weight.dims(),
113 padded_options.options.groups,
114 ));
115
116 if let Some(padding_end) = padded_options.padding_end {
117 let top = padded_options.options.padding[0];
118 let left = padded_options.options.padding[1];
119 let bottom = padding_end[0];
120 let right = padding_end[1];
121 let padded = x.pad((left, right, top, bottom), PadMode::Constant(0.0));
123 let zero_options = ConvOptions::new(
124 padded_options.options.stride,
125 [0, 0],
126 padded_options.options.dilation,
127 padded_options.options.groups,
128 );
129 Tensor::new(BridgeTensor::float(Dispatch::conv2d(
130 padded.primitive.into_float(),
131 weight.primitive.into_float(),
132 bias.map(|b| b.primitive.into_float()),
133 zero_options,
134 )))
135 } else {
136 Tensor::new(BridgeTensor::float(Dispatch::conv2d(
137 x.primitive.into_float(),
138 weight.primitive.into_float(),
139 bias.map(|b| b.primitive.into_float()),
140 padded_options.options,
141 )))
142 }
143}
144
145pub fn conv3d(
150 x: Tensor<5>,
151 weight: Tensor<5>,
152 bias: Option<Tensor<1>>,
153 options: impl Into<PaddedConvOptions<3>>,
154) -> Tensor<5> {
155 let padded_options = options.into();
156 check!(TensorCheck::conv(
157 "conv3d",
158 x.dims(),
159 weight.dims(),
160 padded_options.options.groups,
161 ));
162
163 if padded_options.is_asymmetric() {
164 panic!("Asymmetric padding is not yet supported for conv3d");
165 }
166
167 Tensor::new(BridgeTensor::float(Dispatch::conv3d(
168 x.primitive.into_float(),
169 weight.primitive.into_float(),
170 bias.map(|b| b.primitive.into_float()),
171 padded_options.options,
172 )))
173}
174
175pub fn deform_conv2d(
177 x: Tensor<4>,
178 offset: Tensor<4>,
179 weight: Tensor<4>,
180 mask: Option<Tensor<4>>,
181 bias: Option<Tensor<1>>,
182 options: DeformConvOptions<2>,
183) -> Tensor<4> {
184 check!(TensorCheck::conv(
185 "deform_conv2d",
186 x.dims(),
187 weight.dims(),
188 options.weight_groups,
189 ));
190 Tensor::new(BridgeTensor::float(Dispatch::deform_conv2d(
191 x.primitive.into_float(),
192 offset.primitive.into_float(),
193 weight.primitive.into_float(),
194 mask.map(|m| m.primitive.into_float()),
195 bias.map(|b| b.primitive.into_float()),
196 options,
197 )))
198}
199
200pub fn conv_transpose1d(
202 x: Tensor<3>,
203 weight: Tensor<3>,
204 bias: Option<Tensor<1>>,
205 options: ConvTransposeOptions<1>,
206) -> Tensor<3> {
207 check!(TensorCheck::conv_transpose(
208 "conv_transpose1d",
209 x.dims(),
210 weight.dims(),
211 ));
212 Tensor::new(BridgeTensor::float(Dispatch::conv_transpose1d(
213 x.primitive.into_float(),
214 weight.primitive.into_float(),
215 bias.map(|b| b.primitive.into_float()),
216 options,
217 )))
218}
219
220pub fn conv_transpose2d(
222 x: Tensor<4>,
223 weight: Tensor<4>,
224 bias: Option<Tensor<1>>,
225 options: ConvTransposeOptions<2>,
226) -> Tensor<4> {
227 check!(TensorCheck::conv_transpose(
228 "conv_transpose2d",
229 x.dims(),
230 weight.dims(),
231 ));
232 Tensor::new(BridgeTensor::float(Dispatch::conv_transpose2d(
233 x.primitive.into_float(),
234 weight.primitive.into_float(),
235 bias.map(|b| b.primitive.into_float()),
236 options,
237 )))
238}
239
240pub fn conv_transpose3d(
242 x: Tensor<5>,
243 weight: Tensor<5>,
244 bias: Option<Tensor<1>>,
245 options: ConvTransposeOptions<3>,
246) -> Tensor<5> {
247 check!(TensorCheck::conv_transpose(
248 "conv_transpose3d",
249 x.dims(),
250 weight.dims(),
251 ));
252 Tensor::new(BridgeTensor::float(Dispatch::conv_transpose3d(
253 x.primitive.into_float(),
254 weight.primitive.into_float(),
255 bias.map(|b| b.primitive.into_float()),
256 options,
257 )))
258}
259
260pub fn unfold4d(x: Tensor<4>, kernel_size: [usize; 2], options: UnfoldOptions) -> Tensor<3> {
262 Tensor::new(BridgeTensor::float(Dispatch::unfold4d(
263 x.primitive.into_float(),
264 kernel_size,
265 options,
266 )))
267}
268
269pub fn fold4d(
288 x: Tensor<3>,
289 output_size: [usize; 2],
290 kernel_size: [usize; 2],
291 options: UnfoldOptions,
292) -> Tensor<4> {
293 Tensor::new(BridgeTensor::float(Dispatch::fold4d(
294 x.primitive.into_float(),
295 output_size,
296 kernel_size,
297 options,
298 )))
299}
300
301pub fn max_pool1d(
303 x: Tensor<3>,
304 kernel_size: usize,
305 stride: usize,
306 padding: usize,
307 dilation: usize,
308 ceil_mode: bool,
309) -> Tensor<3> {
310 Tensor::new(BridgeTensor::float(Dispatch::max_pool1d(
311 x.primitive.into_float(),
312 kernel_size,
313 stride,
314 padding,
315 dilation,
316 ceil_mode,
317 )))
318}
319
320pub fn max_pool2d(
322 x: Tensor<4>,
323 kernel_size: [usize; 2],
324 stride: [usize; 2],
325 padding: [usize; 2],
326 dilation: [usize; 2],
327 ceil_mode: bool,
328) -> Tensor<4> {
329 Tensor::new(BridgeTensor::float(Dispatch::max_pool2d(
330 x.primitive.into_float(),
331 kernel_size,
332 stride,
333 padding,
334 dilation,
335 ceil_mode,
336 )))
337}
338
339pub fn avg_pool2d(
341 x: Tensor<4>,
342 kernel_size: [usize; 2],
343 stride: [usize; 2],
344 padding: [usize; 2],
345 count_include_pad: bool,
346 ceil_mode: bool,
347) -> Tensor<4> {
348 Tensor::new(BridgeTensor::float(Dispatch::avg_pool2d(
349 x.primitive.into_float(),
350 kernel_size,
351 stride,
352 padding,
353 count_include_pad,
354 ceil_mode,
355 )))
356}
357
358pub fn avg_pool1d(
360 x: Tensor<3>,
361 kernel_size: usize,
362 stride: usize,
363 padding: usize,
364 count_include_pad: bool,
365 ceil_mode: bool,
366) -> Tensor<3> {
367 Tensor::new(BridgeTensor::float(Dispatch::avg_pool1d(
368 x.primitive.into_float(),
369 kernel_size,
370 stride,
371 padding,
372 count_include_pad,
373 ceil_mode,
374 )))
375}
376
377pub fn max_pool1d_with_indices(
379 x: Tensor<3>,
380 kernel_size: usize,
381 stride: usize,
382 padding: usize,
383 dilation: usize,
384 ceil_mode: bool,
385) -> (Tensor<3>, Tensor<3, Int>) {
386 let indices_dtype = x.device().settings().int_dtype;
387 let output = Dispatch::max_pool1d_with_indices(
388 x.primitive.into_float(),
389 kernel_size,
390 stride,
391 padding,
392 dilation,
393 ceil_mode,
394 indices_dtype,
395 );
396
397 (
398 Tensor::new(BridgeTensor::float(output.output)),
399 Tensor::new(BridgeTensor::int(output.indices)),
400 )
401}
402
403pub fn max_pool2d_with_indices(
405 x: Tensor<4>,
406 kernel_size: [usize; 2],
407 stride: [usize; 2],
408 padding: [usize; 2],
409 dilation: [usize; 2],
410 ceil_mode: bool,
411) -> (Tensor<4>, Tensor<4, Int>) {
412 let indices_dtype = x.device().settings().int_dtype;
413 let output = Dispatch::max_pool2d_with_indices(
414 x.primitive.into_float(),
415 kernel_size,
416 stride,
417 padding,
418 dilation,
419 ceil_mode,
420 indices_dtype,
421 );
422
423 (
424 Tensor::new(BridgeTensor::float(output.output)),
425 Tensor::new(BridgeTensor::int(output.indices)),
426 )
427}
428
429pub fn adaptive_avg_pool2d(x: Tensor<4>, output_size: [usize; 2]) -> Tensor<4> {
431 Tensor::new(BridgeTensor::float(Dispatch::adaptive_avg_pool2d(
432 x.primitive.into_float(),
433 output_size,
434 )))
435}
436
437pub fn adaptive_avg_pool3d(x: Tensor<5>, output_size: [usize; 3]) -> Tensor<5> {
439 Tensor::new(BridgeTensor::float(Dispatch::adaptive_avg_pool3d(
440 x.primitive.into_float(),
441 output_size,
442 )))
443}
444
445pub fn adaptive_avg_pool1d(x: Tensor<3>, output_size: usize) -> Tensor<3> {
447 Tensor::new(BridgeTensor::float(Dispatch::adaptive_avg_pool1d(
448 x.primitive.into_float(),
449 output_size,
450 )))
451}
452
453pub fn interpolate(
455 x: Tensor<4>,
456 output_size: [usize; 2],
457 options: InterpolateOptions,
458) -> Tensor<4> {
459 Tensor::new(BridgeTensor::float(Dispatch::interpolate(
460 x.primitive.into_float(),
461 output_size,
462 options,
463 )))
464}
465
466pub fn linear<const D: usize>(
492 input: Tensor<D>,
493 weight: Tensor<2>,
494 bias: Option<Tensor<1>>,
495) -> Tensor<D> {
496 if D == 1 {
497 let input = input.unsqueeze::<2>();
499 let output = linear(input, weight, bias);
500 return output.squeeze_dim(0);
501 }
502
503 if let DType::QFloat(_) = weight.dtype() {
511 let dims = input.dims();
512 let analysis = MatmulTransformAnalysis::from_shapes(&input.shape(), &weight.shape());
513
514 let output = match MatmulTransformPolicy::default().action(&analysis) {
515 MatmulTransformAction::MergeBatches { rows } => {
516 let d_in = dims[D - 1];
517 let d_out = weight.dims()[1];
518
519 let folded = input.reshape([rows, d_in]).matmul(weight);
520
521 let mut out_dims = dims;
522 out_dims[D - 1] = d_out;
523 folded.reshape(out_dims)
524 }
525 MatmulTransformAction::Keep => input.matmul(weight.unsqueeze::<D>()),
526 };
527
528 return match bias {
529 Some(bias) => output + bias.unsqueeze(),
530 None => output,
531 };
532 }
533
534 Tensor::new(linear_impl(
535 input.primitive,
536 weight.primitive,
537 bias.map(|b| b.primitive),
538 ))
539}
540
541fn linear_impl(
542 input: BridgeTensor,
543 weight: BridgeTensor,
544 bias: Option<BridgeTensor>,
545) -> BridgeTensor {
546 BridgeTensor::float(Dispatch::linear(
547 input.into_float(),
548 weight.into_float(),
549 bias.map(|b| b.into_float()),
550 ))
551}
552
553pub fn attention(
575 query: Tensor<4>,
576 key: Tensor<4>,
577 value: Tensor<4>,
578 mask: Option<Tensor<4, Bool>>,
579 attn_bias: Option<Tensor<4>>,
580 options: AttentionModuleOptions,
581) -> Tensor<4> {
582 Tensor::new(BridgeTensor::float(Dispatch::attention(
583 query.primitive.into_float(),
584 key.primitive.into_float(),
585 value.primitive.into_float(),
586 mask.map(|mask| mask.primitive.into()),
587 attn_bias.map(|bias| bias.primitive.into_float()),
588 options,
589 )))
590}
591
592pub fn attention_fallback(
594 query: Tensor<4>,
595 key: Tensor<4>,
596 value: Tensor<4>,
597 mask: Option<Tensor<4, Bool>>,
598 attn_bias: Option<Tensor<4>>,
599 options: AttentionModuleOptions,
600) -> Tensor<4> {
601 Tensor::new(BridgeTensor::float(
602 burn_backend::ops::attention::attention_fallback::<Dispatch>(
603 query.primitive.into_float(),
604 key.primitive.into_float(),
605 value.primitive.into_float(),
606 mask.map(|mask| mask.primitive.into()),
607 attn_bias.map(|bias| bias.primitive.into_float()),
608 options,
609 ),
610 ))
611}
612
613pub fn conv2d_weight_backward(
615 x: Tensor<4>,
616 weight: Tensor<4>,
617 output_grad: Tensor<4>,
618 options: ConvOptions<2>,
619) -> Tensor<4> {
620 Tensor::new(BridgeTensor::float(Dispatch::conv2d_weight_backward(
621 x.primitive.into_float(),
622 weight.primitive.into_float(),
623 output_grad.primitive.into_float(),
624 options,
625 )))
626}
627
628pub fn avg_pool2d_backward(
630 x: Tensor<4>,
631 grad: Tensor<4>,
632 kernel_size: [usize; 2],
633 stride: [usize; 2],
634 padding: [usize; 2],
635 count_include_pad: bool,
636 ceil_mode: bool,
637) -> Tensor<4> {
638 Tensor::new(BridgeTensor::float(Dispatch::avg_pool2d_backward(
639 x.primitive.into_float(),
640 grad.primitive.into_float(),
641 kernel_size,
642 stride,
643 padding,
644 count_include_pad,
645 ceil_mode,
646 )))
647}
648
649#[allow(clippy::too_many_arguments)]
651pub fn max_pool2d_with_indices_backward(
652 x: Tensor<4>,
653 kernel_size: [usize; 2],
654 stride: [usize; 2],
655 padding: [usize; 2],
656 dilation: [usize; 2],
657 ceil_mode: bool,
658 output_grad: Tensor<4>,
659 indices: Tensor<4, Int>,
660) -> Tensor<4> {
661 Tensor::new(BridgeTensor::float(
662 Dispatch::max_pool2d_with_indices_backward(
663 x.primitive.into_float(),
664 kernel_size,
665 stride,
666 padding,
667 dilation,
668 ceil_mode,
669 output_grad.primitive.into_float(),
670 indices.primitive.into(),
671 )
672 .x_grad,
673 ))
674}
675
676pub fn layer_norm<const D: usize>(
686 input: Tensor<D>,
687 gamma: Tensor<1>,
688 beta: Option<Tensor<1>>,
689 epsilon: f64,
690) -> Tensor<D> {
691 Tensor::new(layer_norm_impl(
692 input.primitive,
693 gamma.primitive,
694 beta.map(|b| b.primitive),
695 epsilon,
696 ))
697}
698
699fn layer_norm_impl(
700 input: BridgeTensor,
701 gamma: BridgeTensor,
702 beta: Option<BridgeTensor>,
703 epsilon: f64,
704) -> BridgeTensor {
705 BridgeTensor::float(Dispatch::layer_norm(
706 input.into_float(),
707 gamma.into_float(),
708 beta.map(|b| b.into_float()),
709 epsilon,
710 ))
711}