1use crate::api::{
2 Bool, Int, Tensor, TensorPrimitive,
3 backend::Backend,
4 check,
5 check::TensorCheck,
6 ops::{
7 AttentionModuleOptions, ConvOptions, ConvTransposeOptions, InterpolateOptions, PadMode,
8 PaddedConvOptions, UnfoldOptions,
9 },
10};
11
12use super::ops::DeformConvOptions;
13
14pub fn ctc_loss<B>(
28 log_probs: Tensor<B, 3>,
29 targets: Tensor<B, 2, Int>,
30 input_lengths: Tensor<B, 1, Int>,
31 target_lengths: Tensor<B, 1, Int>,
32 blank: usize,
33) -> Tensor<B, 1>
34where
35 B: Backend,
36{
37 Tensor::new(TensorPrimitive::Float(B::ctc_loss(
38 log_probs.primitive.tensor(),
39 targets.primitive,
40 input_lengths.primitive,
41 target_lengths.primitive,
42 blank,
43 )))
44}
45
46pub fn embedding<B>(weights: Tensor<B, 2>, indices: Tensor<B, 2, Int>) -> Tensor<B, 3>
48where
49 B: Backend,
50{
51 Tensor::new(TensorPrimitive::Float(B::embedding(
52 weights.primitive.tensor(),
53 indices.primitive,
54 )))
55}
56
57pub fn conv1d<B>(
63 x: Tensor<B, 3>,
64 weight: Tensor<B, 3>,
65 bias: Option<Tensor<B, 1>>,
66 options: impl Into<PaddedConvOptions<1>>,
67) -> Tensor<B, 3>
68where
69 B: Backend,
70{
71 let padded_options = options.into();
72 check!(TensorCheck::conv(
73 "conv1d",
74 x.dims(),
75 weight.dims(),
76 padded_options.options.groups,
77 ));
78
79 if let Some(padding_end) = padded_options.padding_end {
80 let left = padded_options.options.padding[0];
81 let right = padding_end[0];
82 let padded = x.pad((left, right, 0, 0), PadMode::Constant(0.0));
84 let zero_options = ConvOptions::new(
85 padded_options.options.stride,
86 [0],
87 padded_options.options.dilation,
88 padded_options.options.groups,
89 );
90 Tensor::new(TensorPrimitive::Float(B::conv1d(
91 padded.primitive.tensor(),
92 weight.primitive.tensor(),
93 bias.map(|b| b.primitive.tensor()),
94 zero_options,
95 )))
96 } else {
97 Tensor::new(TensorPrimitive::Float(B::conv1d(
98 x.primitive.tensor(),
99 weight.primitive.tensor(),
100 bias.map(|b| b.primitive.tensor()),
101 padded_options.options,
102 )))
103 }
104}
105
106pub fn conv2d<B>(
112 x: Tensor<B, 4>,
113 weight: Tensor<B, 4>,
114 bias: Option<Tensor<B, 1>>,
115 options: impl Into<PaddedConvOptions<2>>,
116) -> Tensor<B, 4>
117where
118 B: Backend,
119{
120 let padded_options = options.into();
121 check!(TensorCheck::conv(
122 "conv2d",
123 x.dims(),
124 weight.dims(),
125 padded_options.options.groups,
126 ));
127
128 if let Some(padding_end) = padded_options.padding_end {
129 let top = padded_options.options.padding[0];
130 let left = padded_options.options.padding[1];
131 let bottom = padding_end[0];
132 let right = padding_end[1];
133 let padded = x.pad((left, right, top, bottom), PadMode::Constant(0.0));
135 let zero_options = ConvOptions::new(
136 padded_options.options.stride,
137 [0, 0],
138 padded_options.options.dilation,
139 padded_options.options.groups,
140 );
141 Tensor::new(TensorPrimitive::Float(B::conv2d(
142 padded.primitive.tensor(),
143 weight.primitive.tensor(),
144 bias.map(|b| b.primitive.tensor()),
145 zero_options,
146 )))
147 } else {
148 Tensor::new(TensorPrimitive::Float(B::conv2d(
149 x.primitive.tensor(),
150 weight.primitive.tensor(),
151 bias.map(|b| b.primitive.tensor()),
152 padded_options.options,
153 )))
154 }
155}
156
157pub fn conv3d<B>(
163 x: Tensor<B, 5>,
164 weight: Tensor<B, 5>,
165 bias: Option<Tensor<B, 1>>,
166 options: impl Into<PaddedConvOptions<3>>,
167) -> Tensor<B, 5>
168where
169 B: Backend,
170{
171 let padded_options = options.into();
172 check!(TensorCheck::conv(
173 "conv3d",
174 x.dims(),
175 weight.dims(),
176 padded_options.options.groups,
177 ));
178
179 let mut options = padded_options.options;
180 let x = if let Some(padding_end) = padded_options.padding_end {
181 let padding: [(usize, usize); 3] =
182 core::array::from_fn(|axis| (options.padding[axis], padding_end[axis]));
183 options.padding = [0; 3];
184 x.pad(padding, PadMode::Constant(0.0))
185 } else {
186 x
187 };
188
189 Tensor::new(TensorPrimitive::Float(B::conv3d(
190 x.primitive.tensor(),
191 weight.primitive.tensor(),
192 bias.map(|b| b.primitive.tensor()),
193 options,
194 )))
195}
196
197pub fn deform_conv2d<B>(
199 x: Tensor<B, 4>,
200 offset: Tensor<B, 4>,
201 weight: Tensor<B, 4>,
202 mask: Option<Tensor<B, 4>>,
203 bias: Option<Tensor<B, 1>>,
204 options: DeformConvOptions<2>,
205) -> Tensor<B, 4>
206where
207 B: Backend,
208{
209 check!(TensorCheck::conv(
210 "deform_conv2d",
211 x.dims(),
212 weight.dims(),
213 options.weight_groups,
214 ));
215 let [batch, channels, height, width] = x.dims();
216 let [_, _, kernel_height, kernel_width] = weight.dims();
217 assert!(
218 options.offset_groups > 0 && channels.is_multiple_of(options.offset_groups),
219 "deform_conv2d input channels must be divisible by non-zero offset groups"
220 );
221 let [out_height, out_width] = options.output_size(
222 [height, width], [kernel_height, kernel_width],
223 );
224 let mask_channels = options.offset_groups * kernel_height * kernel_width;
225 assert_eq!(
226 offset.dims(), [batch, 2 * mask_channels, out_height, out_width],
227 "deform_conv2d offset shape must match groups, kernel and output"
228 );
229 if let Some(mask) = mask.as_ref() {
230 assert_eq!(
231 mask.dims(), [batch, mask_channels, out_height, out_width],
232 "deform_conv2d mask shape must match groups, kernel and output"
233 );
234 }
235 Tensor::new(TensorPrimitive::Float(B::deform_conv2d(
236 x.primitive.tensor(),
237 offset.primitive.tensor(),
238 weight.primitive.tensor(),
239 mask.map(|m| m.primitive.tensor()),
240 bias.map(|b| b.primitive.tensor()),
241 options,
242 )))
243}
244
245pub fn conv_transpose1d<B>(
247 x: Tensor<B, 3>,
248 weight: Tensor<B, 3>,
249 bias: Option<Tensor<B, 1>>,
250 options: ConvTransposeOptions<1>,
251) -> Tensor<B, 3>
252where
253 B: Backend,
254{
255 check!(TensorCheck::conv_transpose(
256 "conv_transpose1d",
257 x.dims(),
258 weight.dims(),
259 ));
260 Tensor::new(TensorPrimitive::Float(B::conv_transpose1d(
261 x.primitive.tensor(),
262 weight.primitive.tensor(),
263 bias.map(|b| b.primitive.tensor()),
264 options,
265 )))
266}
267
268pub fn conv_transpose2d<B>(
270 x: Tensor<B, 4>,
271 weight: Tensor<B, 4>,
272 bias: Option<Tensor<B, 1>>,
273 options: ConvTransposeOptions<2>,
274) -> Tensor<B, 4>
275where
276 B: Backend,
277{
278 check!(TensorCheck::conv_transpose(
279 "conv_transpose2d",
280 x.dims(),
281 weight.dims(),
282 ));
283 Tensor::new(TensorPrimitive::Float(B::conv_transpose2d(
284 x.primitive.tensor(),
285 weight.primitive.tensor(),
286 bias.map(|b| b.primitive.tensor()),
287 options,
288 )))
289}
290
291pub fn conv_transpose3d<B>(
293 x: Tensor<B, 5>,
294 weight: Tensor<B, 5>,
295 bias: Option<Tensor<B, 1>>,
296 options: ConvTransposeOptions<3>,
297) -> Tensor<B, 5>
298where
299 B: Backend,
300{
301 check!(TensorCheck::conv_transpose(
302 "conv_transpose3d",
303 x.dims(),
304 weight.dims(),
305 ));
306 Tensor::new(TensorPrimitive::Float(B::conv_transpose3d(
307 x.primitive.tensor(),
308 weight.primitive.tensor(),
309 bias.map(|b| b.primitive.tensor()),
310 options,
311 )))
312}
313
314pub fn unfold4d<B>(x: Tensor<B, 4>, kernel_size: [usize; 2], options: UnfoldOptions) -> Tensor<B, 3>
316where
317 B: Backend,
318{
319 Tensor::new(TensorPrimitive::Float(B::unfold4d(
320 x.primitive.tensor(),
321 kernel_size,
322 options,
323 )))
324}
325
326pub fn max_pool1d<B>(
328 x: Tensor<B, 3>,
329 kernel_size: usize,
330 stride: usize,
331 padding: usize,
332 dilation: usize,
333 ceil_mode: bool,
334) -> Tensor<B, 3>
335where
336 B: Backend,
337{
338 Tensor::new(TensorPrimitive::Float(B::max_pool1d(
339 x.primitive.tensor(),
340 kernel_size,
341 stride,
342 padding,
343 dilation,
344 ceil_mode,
345 )))
346}
347
348pub fn max_pool2d<B>(
350 x: Tensor<B, 4>,
351 kernel_size: [usize; 2],
352 stride: [usize; 2],
353 padding: [usize; 2],
354 dilation: [usize; 2],
355 ceil_mode: bool,
356) -> Tensor<B, 4>
357where
358 B: Backend,
359{
360 Tensor::new(TensorPrimitive::Float(B::max_pool2d(
361 x.primitive.tensor(),
362 kernel_size,
363 stride,
364 padding,
365 dilation,
366 ceil_mode,
367 )))
368}
369
370pub fn avg_pool2d<B>(
372 x: Tensor<B, 4>,
373 kernel_size: [usize; 2],
374 stride: [usize; 2],
375 padding: [usize; 2],
376 count_include_pad: bool,
377 ceil_mode: bool,
378) -> Tensor<B, 4>
379where
380 B: Backend,
381{
382 Tensor::new(TensorPrimitive::Float(B::avg_pool2d(
383 x.primitive.tensor(),
384 kernel_size,
385 stride,
386 padding,
387 count_include_pad,
388 ceil_mode,
389 )))
390}
391
392pub fn avg_pool1d<B>(
394 x: Tensor<B, 3>,
395 kernel_size: usize,
396 stride: usize,
397 padding: usize,
398 count_include_pad: bool,
399 ceil_mode: bool,
400) -> Tensor<B, 3>
401where
402 B: Backend,
403{
404 Tensor::new(TensorPrimitive::Float(B::avg_pool1d(
405 x.primitive.tensor(),
406 kernel_size,
407 stride,
408 padding,
409 count_include_pad,
410 ceil_mode,
411 )))
412}
413
414pub fn max_pool1d_with_indices<B>(
416 x: Tensor<B, 3>,
417 kernel_size: usize,
418 stride: usize,
419 padding: usize,
420 dilation: usize,
421 ceil_mode: bool,
422) -> (Tensor<B, 3>, Tensor<B, 3, Int>)
423where
424 B: Backend,
425{
426 let output = B::max_pool1d_with_indices(
427 x.primitive.tensor(),
428 kernel_size,
429 stride,
430 padding,
431 dilation,
432 ceil_mode,
433 );
434
435 (
436 Tensor::new(TensorPrimitive::Float(output.output)),
437 Tensor::new(output.indices),
438 )
439}
440
441pub fn max_pool2d_with_indices<B>(
443 x: Tensor<B, 4>,
444 kernel_size: [usize; 2],
445 stride: [usize; 2],
446 padding: [usize; 2],
447 dilation: [usize; 2],
448 ceil_mode: bool,
449) -> (Tensor<B, 4>, Tensor<B, 4, Int>)
450where
451 B: Backend,
452{
453 let output = B::max_pool2d_with_indices(
454 x.primitive.tensor(),
455 kernel_size,
456 stride,
457 padding,
458 dilation,
459 ceil_mode,
460 );
461
462 (
463 Tensor::new(TensorPrimitive::Float(output.output)),
464 Tensor::new(output.indices),
465 )
466}
467
468pub fn adaptive_avg_pool2d<B>(x: Tensor<B, 4>, output_size: [usize; 2]) -> Tensor<B, 4>
470where
471 B: Backend,
472{
473 Tensor::new(TensorPrimitive::Float(B::adaptive_avg_pool2d(
474 x.primitive.tensor(),
475 output_size,
476 )))
477}
478
479pub fn adaptive_avg_pool1d<B>(x: Tensor<B, 3>, output_size: usize) -> Tensor<B, 3>
481where
482 B: Backend,
483{
484 Tensor::new(TensorPrimitive::Float(B::adaptive_avg_pool1d(
485 x.primitive.tensor(),
486 output_size,
487 )))
488}
489
490pub fn interpolate<B>(
492 x: Tensor<B, 4>,
493 output_size: [usize; 2],
494 options: InterpolateOptions,
495) -> Tensor<B, 4>
496where
497 B: Backend,
498{
499 Tensor::new(TensorPrimitive::Float(B::interpolate(
500 x.primitive.tensor(),
501 output_size,
502 options,
503 )))
504}
505
506pub fn linear<B: Backend, const D: usize>(
532 input: Tensor<B, D>,
533 weight: Tensor<B, 2>,
534 bias: Option<Tensor<B, 1>>,
535) -> Tensor<B, D> {
536 if D == 1 {
537 let input = input.unsqueeze::<2>();
539 let output = linear(input, weight, bias);
540 return output.squeeze_dim(0);
541 }
542
543 Tensor::new(TensorPrimitive::Float(B::linear(
544 input.primitive.tensor(),
545 weight.primitive.tensor(),
546 bias.map(|b| b.primitive.tensor()),
547 )))
548}
549
550pub fn attention<B: Backend>(
572 query: Tensor<B, 4>,
573 key: Tensor<B, 4>,
574 value: Tensor<B, 4>,
575 mask: Option<Tensor<B, 4, Bool>>,
576 attn_bias: Option<Tensor<B, 4>>,
577 options: AttentionModuleOptions,
578) -> Tensor<B, 4> {
579 Tensor::new(TensorPrimitive::Float(B::attention(
580 query.primitive.tensor(),
581 key.primitive.tensor(),
582 value.primitive.tensor(),
583 mask.map(|mask| mask.primitive),
584 attn_bias.map(|bias| bias.primitive.tensor()),
585 options,
586 )))
587}
588
589pub fn attention_fallback<B: Backend>(
591 query: Tensor<B, 4>,
592 key: Tensor<B, 4>,
593 value: Tensor<B, 4>,
594 mask: Option<Tensor<B, 4, Bool>>,
595 attn_bias: Option<Tensor<B, 4>>,
596 options: AttentionModuleOptions,
597) -> Tensor<B, 4> {
598 Tensor::new(TensorPrimitive::Float(
599 crate::api::ops::attention::attention_fallback::<B>(
600 query.primitive.tensor(),
601 key.primitive.tensor(),
602 value.primitive.tensor(),
603 mask.map(|mask| mask.primitive),
604 attn_bias.map(|bias| bias.primitive.tensor()),
605 options,
606 ),
607 ))
608}