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 max_pool1d(
271 x: Tensor<3>,
272 kernel_size: usize,
273 stride: usize,
274 padding: usize,
275 dilation: usize,
276 ceil_mode: bool,
277) -> Tensor<3> {
278 Tensor::new(BridgeTensor::float(Dispatch::max_pool1d(
279 x.primitive.into_float(),
280 kernel_size,
281 stride,
282 padding,
283 dilation,
284 ceil_mode,
285 )))
286}
287
288pub fn max_pool2d(
290 x: Tensor<4>,
291 kernel_size: [usize; 2],
292 stride: [usize; 2],
293 padding: [usize; 2],
294 dilation: [usize; 2],
295 ceil_mode: bool,
296) -> Tensor<4> {
297 Tensor::new(BridgeTensor::float(Dispatch::max_pool2d(
298 x.primitive.into_float(),
299 kernel_size,
300 stride,
301 padding,
302 dilation,
303 ceil_mode,
304 )))
305}
306
307pub fn avg_pool2d(
309 x: Tensor<4>,
310 kernel_size: [usize; 2],
311 stride: [usize; 2],
312 padding: [usize; 2],
313 count_include_pad: bool,
314 ceil_mode: bool,
315) -> Tensor<4> {
316 Tensor::new(BridgeTensor::float(Dispatch::avg_pool2d(
317 x.primitive.into_float(),
318 kernel_size,
319 stride,
320 padding,
321 count_include_pad,
322 ceil_mode,
323 )))
324}
325
326pub fn avg_pool1d(
328 x: Tensor<3>,
329 kernel_size: usize,
330 stride: usize,
331 padding: usize,
332 count_include_pad: bool,
333 ceil_mode: bool,
334) -> Tensor<3> {
335 Tensor::new(BridgeTensor::float(Dispatch::avg_pool1d(
336 x.primitive.into_float(),
337 kernel_size,
338 stride,
339 padding,
340 count_include_pad,
341 ceil_mode,
342 )))
343}
344
345pub fn max_pool1d_with_indices(
347 x: Tensor<3>,
348 kernel_size: usize,
349 stride: usize,
350 padding: usize,
351 dilation: usize,
352 ceil_mode: bool,
353) -> (Tensor<3>, Tensor<3, Int>) {
354 let indices_dtype = x.device().settings().int_dtype;
355 let output = Dispatch::max_pool1d_with_indices(
356 x.primitive.into_float(),
357 kernel_size,
358 stride,
359 padding,
360 dilation,
361 ceil_mode,
362 indices_dtype,
363 );
364
365 (
366 Tensor::new(BridgeTensor::float(output.output)),
367 Tensor::new(BridgeTensor::int(output.indices)),
368 )
369}
370
371pub fn max_pool2d_with_indices(
373 x: Tensor<4>,
374 kernel_size: [usize; 2],
375 stride: [usize; 2],
376 padding: [usize; 2],
377 dilation: [usize; 2],
378 ceil_mode: bool,
379) -> (Tensor<4>, Tensor<4, Int>) {
380 let indices_dtype = x.device().settings().int_dtype;
381 let output = Dispatch::max_pool2d_with_indices(
382 x.primitive.into_float(),
383 kernel_size,
384 stride,
385 padding,
386 dilation,
387 ceil_mode,
388 indices_dtype,
389 );
390
391 (
392 Tensor::new(BridgeTensor::float(output.output)),
393 Tensor::new(BridgeTensor::int(output.indices)),
394 )
395}
396
397pub fn adaptive_avg_pool2d(x: Tensor<4>, output_size: [usize; 2]) -> Tensor<4> {
399 Tensor::new(BridgeTensor::float(Dispatch::adaptive_avg_pool2d(
400 x.primitive.into_float(),
401 output_size,
402 )))
403}
404
405pub fn adaptive_avg_pool1d(x: Tensor<3>, output_size: usize) -> Tensor<3> {
407 Tensor::new(BridgeTensor::float(Dispatch::adaptive_avg_pool1d(
408 x.primitive.into_float(),
409 output_size,
410 )))
411}
412
413pub fn interpolate(
415 x: Tensor<4>,
416 output_size: [usize; 2],
417 options: InterpolateOptions,
418) -> Tensor<4> {
419 Tensor::new(BridgeTensor::float(Dispatch::interpolate(
420 x.primitive.into_float(),
421 output_size,
422 options,
423 )))
424}
425
426pub fn linear<const D: usize>(
452 input: Tensor<D>,
453 weight: Tensor<2>,
454 bias: Option<Tensor<1>>,
455) -> Tensor<D> {
456 if D == 1 {
457 let input = input.unsqueeze::<2>();
459 let output = linear(input, weight, bias);
460 return output.squeeze_dim(0);
461 }
462
463 if let DType::QFloat(_) = weight.dtype() {
471 let dims = input.dims();
472 let analysis = MatmulTransformAnalysis::from_shapes(&input.shape(), &weight.shape());
473
474 let output = match MatmulTransformPolicy::default().action(&analysis) {
475 MatmulTransformAction::MergeBatches { rows } => {
476 let d_in = dims[D - 1];
477 let d_out = weight.dims()[1];
478
479 let folded = input.reshape([rows, d_in]).matmul(weight);
480
481 let mut out_dims = dims;
482 out_dims[D - 1] = d_out;
483 folded.reshape(out_dims)
484 }
485 MatmulTransformAction::Keep => input.matmul(weight.unsqueeze::<D>()),
486 };
487
488 return match bias {
489 Some(bias) => output + bias.unsqueeze(),
490 None => output,
491 };
492 }
493
494 Tensor::new(linear_impl(
495 input.primitive,
496 weight.primitive,
497 bias.map(|b| b.primitive),
498 ))
499}
500
501fn linear_impl(
502 input: BridgeTensor,
503 weight: BridgeTensor,
504 bias: Option<BridgeTensor>,
505) -> BridgeTensor {
506 BridgeTensor::float(Dispatch::linear(
507 input.into_float(),
508 weight.into_float(),
509 bias.map(|b| b.into_float()),
510 ))
511}
512
513pub fn attention(
535 query: Tensor<4>,
536 key: Tensor<4>,
537 value: Tensor<4>,
538 mask: Option<Tensor<4, Bool>>,
539 attn_bias: Option<Tensor<4>>,
540 options: AttentionModuleOptions,
541) -> Tensor<4> {
542 Tensor::new(BridgeTensor::float(Dispatch::attention(
543 query.primitive.into_float(),
544 key.primitive.into_float(),
545 value.primitive.into_float(),
546 mask.map(|mask| mask.primitive.into()),
547 attn_bias.map(|bias| bias.primitive.into_float()),
548 options,
549 )))
550}
551
552pub fn attention_fallback(
554 query: Tensor<4>,
555 key: Tensor<4>,
556 value: Tensor<4>,
557 mask: Option<Tensor<4, Bool>>,
558 attn_bias: Option<Tensor<4>>,
559 options: AttentionModuleOptions,
560) -> Tensor<4> {
561 Tensor::new(BridgeTensor::float(
562 burn_backend::ops::attention::attention_fallback::<Dispatch>(
563 query.primitive.into_float(),
564 key.primitive.into_float(),
565 value.primitive.into_float(),
566 mask.map(|mask| mask.primitive.into()),
567 attn_bias.map(|bias| bias.primitive.into_float()),
568 options,
569 ),
570 ))
571}
572
573pub fn conv2d_weight_backward(
575 x: Tensor<4>,
576 weight: Tensor<4>,
577 output_grad: Tensor<4>,
578 options: ConvOptions<2>,
579) -> Tensor<4> {
580 Tensor::new(BridgeTensor::float(Dispatch::conv2d_weight_backward(
581 x.primitive.into_float(),
582 weight.primitive.into_float(),
583 output_grad.primitive.into_float(),
584 options,
585 )))
586}
587
588pub fn avg_pool2d_backward(
590 x: Tensor<4>,
591 grad: Tensor<4>,
592 kernel_size: [usize; 2],
593 stride: [usize; 2],
594 padding: [usize; 2],
595 count_include_pad: bool,
596 ceil_mode: bool,
597) -> Tensor<4> {
598 Tensor::new(BridgeTensor::float(Dispatch::avg_pool2d_backward(
599 x.primitive.into_float(),
600 grad.primitive.into_float(),
601 kernel_size,
602 stride,
603 padding,
604 count_include_pad,
605 ceil_mode,
606 )))
607}
608
609#[allow(clippy::too_many_arguments)]
611pub fn max_pool2d_with_indices_backward(
612 x: Tensor<4>,
613 kernel_size: [usize; 2],
614 stride: [usize; 2],
615 padding: [usize; 2],
616 dilation: [usize; 2],
617 ceil_mode: bool,
618 output_grad: Tensor<4>,
619 indices: Tensor<4, Int>,
620) -> Tensor<4> {
621 Tensor::new(BridgeTensor::float(
622 Dispatch::max_pool2d_with_indices_backward(
623 x.primitive.into_float(),
624 kernel_size,
625 stride,
626 padding,
627 dilation,
628 ceil_mode,
629 output_grad.primitive.into_float(),
630 indices.primitive.into(),
631 )
632 .x_grad,
633 ))
634}
635
636pub fn layer_norm<const D: usize>(
646 input: Tensor<D>,
647 gamma: Tensor<1>,
648 beta: Option<Tensor<1>>,
649 epsilon: f64,
650) -> Tensor<D> {
651 Tensor::new(layer_norm_impl(
652 input.primitive,
653 gamma.primitive,
654 beta.map(|b| b.primitive),
655 epsilon,
656 ))
657}
658
659fn layer_norm_impl(
660 input: BridgeTensor,
661 gamma: BridgeTensor,
662 beta: Option<BridgeTensor>,
663 epsilon: f64,
664) -> BridgeTensor {
665 BridgeTensor::float(Dispatch::layer_norm(
666 input.into_float(),
667 gamma.into_float(),
668 beta.map(|b| b.into_float()),
669 epsilon,
670 ))
671}