burn_backend/backend/ops/tensor.rs
1use super::cat::cat_with_slice_assign;
2use super::grid_sample::float_grid_sample_2d_ref;
3use super::repeat_dim::repeat_with_slice_assign;
4use super::sort::{argsort, sort, sort_with_indices};
5use crate::ops::GridSampleOptions;
6use crate::tensor::{BoolTensor, Device, FloatTensor, IntTensor};
7use crate::{Backend, Distribution, TensorData, get_device_settings};
8use crate::{ExecutionError, Scalar, TensorMetadata};
9use alloc::vec::Vec;
10use burn_std::reader::try_read_sync;
11use burn_std::{BoolDType, FloatDType, IntDType, Shape, Slice};
12
13/// Operations on float tensors.
14pub trait FloatTensorOps<B: Backend> {
15 /// Creates a new tensor from the data structure.
16 ///
17 /// # Arguments
18 ///
19 /// * `data` - The data structure.
20 /// * `device` - The device to create the tensor on.
21 ///
22 /// # Returns
23 ///
24 /// The tensor with the given data.
25 fn float_from_data(data: TensorData, device: &Device<B>) -> FloatTensor<B>;
26
27 /// Creates a new tensor with random values.
28 ///
29 /// # Arguments
30 ///
31 /// * `shape` - The shape of the tensor.
32 /// * `distribution` - The distribution to sample from.
33 /// * `device` - The device to create the tensor on.
34 /// * `dtype` - The target data type.
35 ///
36 /// # Returns
37 ///
38 /// The tensor with the given shape and random values.
39 fn float_random(
40 shape: Shape,
41 distribution: Distribution,
42 device: &Device<B>,
43 dtype: FloatDType,
44 ) -> FloatTensor<B>;
45
46 /// Creates a new tensor with zeros.
47 ///
48 /// # Arguments
49 ///
50 /// * `shape` - The shape of the tensor.
51 /// * `device` - The device to create the tensor on.
52 /// * `dtype` - The target data type.
53 ///
54 /// # Returns
55 ///
56 /// The tensor with the given shape and zeros.
57 fn float_zeros(shape: Shape, device: &Device<B>, dtype: FloatDType) -> FloatTensor<B> {
58 Self::float_from_data(TensorData::full_dtype(shape, 0., dtype.into()), device)
59 }
60
61 /// Creates a new tensor with ones.
62 ///
63 /// # Arguments
64 ///
65 /// * `shape` - The shape of the tensor.
66 /// * `device` - The device to create the tensor on.
67 /// * `dtype` - The target data type.
68 ///
69 /// # Returns
70 ///
71 /// The tensor with the given shape and ones.
72 fn float_ones(shape: Shape, device: &Device<B>, dtype: FloatDType) -> FloatTensor<B> {
73 Self::float_from_data(TensorData::full_dtype(shape, 1., dtype.into()), device)
74 }
75
76 /// Creates a tensor filled with given value.
77 ///
78 /// # Arguments
79 ///
80 /// * `shape` - The shape of the tensor.
81 /// * `fill_value` - The value with which to fill the tensor.
82 /// * `device` - The device to create the tensor on.
83 /// * `dtype` - The target data type.
84 ///
85 /// # Returns
86 ///
87 /// The tensor filled with given value
88 fn float_full(
89 shape: Shape,
90 fill_value: Scalar,
91 device: &Device<B>,
92 dtype: FloatDType,
93 ) -> FloatTensor<B> {
94 Self::float_from_data(
95 TensorData::full_dtype(shape, fill_value, dtype.into()),
96 device,
97 )
98 }
99
100 /// Converts the tensor to a data structure.
101 ///
102 /// # Arguments
103 ///
104 /// * `tensor` - The tensor.
105 ///
106 /// # Returns
107 ///
108 /// The data structure with the tensor's data.
109 fn float_into_data(
110 tensor: FloatTensor<B>,
111 ) -> impl Future<Output = Result<TensorData, ExecutionError>> + Send;
112
113 /// Moves the tensor to the given device.
114 ///
115 /// # Arguments
116 ///
117 /// * `tensor` - The tensor.
118 /// * `device` - The device to move the tensor to.
119 ///
120 /// # Returns
121 ///
122 /// The tensor on the given device.
123 fn float_to_device(tensor: FloatTensor<B>, device: &Device<B>) -> FloatTensor<B>;
124
125 /// Converts float tensor to int tensor.
126 ///
127 /// # Arguments
128 ///
129 /// * `tensor` - The tensor.
130 /// * `out_dtype` - The output tensor dtype.
131 ///
132 /// # Returns
133 ///
134 /// The int tensor with the same data as the float tensor.
135 fn float_into_int(tensor: FloatTensor<B>, out_dtype: IntDType) -> IntTensor<B>;
136
137 /// Creates an empty tensor with the given shape.
138 ///
139 /// # Arguments
140 ///
141 /// * `shape` - The shape of the tensor.
142 /// * `device` - The device to create the tensor on.
143 /// * `dtype` - The target data type.
144 ///
145 /// # Returns
146 ///
147 /// The empty tensor with the given shape.
148 fn float_empty(shape: Shape, device: &Device<B>, dtype: FloatDType) -> FloatTensor<B>;
149
150 /// Repeat the tensor along the given dimension.
151 ///
152 /// # Arguments
153 ///
154 /// * `tensor` - The tensor.
155 /// * `dim` - The dimension to repeat.
156 /// * `times` - The number of times to repeat the dimension.
157 ///
158 /// # Returns
159 ///
160 /// The tensor with the given dimension repeated.
161 fn float_repeat_dim(tensor: FloatTensor<B>, dim: usize, times: usize) -> FloatTensor<B> {
162 let device = tensor.device();
163 repeat_with_slice_assign::<B, _, _, _>(
164 tensor,
165 dim,
166 times,
167 device,
168 |shape, device, dtype| B::float_empty(shape, device, dtype.into()),
169 B::float_slice_assign,
170 )
171 }
172
173 /// Adds two tensors together.
174 ///
175 /// # Arguments
176 ///
177 /// * `lhs` - The left-hand side tensor.
178 /// * `rhs` - The right-hand side tensor.
179 ///
180 /// # Returns
181 ///
182 /// The result of adding the two tensors together.
183 fn float_add(lhs: FloatTensor<B>, rhs: FloatTensor<B>) -> FloatTensor<B>;
184
185 /// Adds a scalar to a tensor.
186 ///
187 /// # Arguments
188 ///
189 /// * `lhs` - The left-hand side tensor.
190 /// * `rhs` - The right-hand side scalar.
191 ///
192 /// # Returns
193 ///
194 /// The result of adding the scalar to the tensor.
195 fn float_add_scalar(lhs: FloatTensor<B>, rhs: Scalar) -> FloatTensor<B>;
196
197 /// Clamps a tensor under a minimum value.
198 ///
199 /// # Arguments
200 ///
201 /// * `tensor` - The tensor to clamp.
202 /// * `min` - The minimum value.
203 ///
204 /// # Returns
205 ///
206 /// The clamped tensor.
207 fn float_clamp_min(tensor: FloatTensor<B>, min: Scalar) -> FloatTensor<B> {
208 let dtype = get_device_settings::<B>(&tensor.device()).bool_dtype;
209 let mask = Self::float_lower_elem(tensor.clone(), min, dtype);
210 B::float_mask_fill(tensor, mask, min)
211 }
212
213 /// Clamps a tensor over a maximum value.
214 ///
215 /// # Arguments
216 ///
217 /// * `tensor` - The tensor to clamp.
218 /// * `max` - The maximum value.
219 ///
220 /// # Returns
221 ///
222 /// The clamped tensor.
223 fn float_clamp_max(tensor: FloatTensor<B>, max: Scalar) -> FloatTensor<B> {
224 let dtype = get_device_settings::<B>(&tensor.device()).bool_dtype;
225 let mask = Self::float_greater_elem(tensor.clone(), max, dtype);
226 B::float_mask_fill(tensor, mask, max)
227 }
228
229 /// Clamps a tensor between a minimum and maximum value.
230 ///
231 /// # Arguments
232 ///
233 /// * `tensor` - The tensor to clamp.
234 /// * `min` - The minimum value.
235 /// * `max` - The maximum value.
236 ///
237 /// # Returns
238 ///
239 /// The clamped tensor.
240 fn float_clamp(tensor: FloatTensor<B>, min: Scalar, max: Scalar) -> FloatTensor<B> {
241 // Default implementation
242 Self::float_clamp_min(Self::float_clamp_max(tensor, max), min)
243 }
244
245 /// Subtracts two tensors.
246 ///
247 /// # Arguments
248 ///
249 /// * `lhs` - The left-hand side tensor.
250 /// * `rhs` - The right-hand side tensor.
251 ///
252 /// # Returns
253 ///
254 /// The result of subtracting the two tensors.
255 fn float_sub(lhs: FloatTensor<B>, rhs: FloatTensor<B>) -> FloatTensor<B>;
256
257 /// Subtracts a scalar from a tensor.
258 ///
259 /// # Arguments
260 ///
261 /// * `lhs` - The left-hand side tensor.
262 /// * `rhs` - The right-hand side scalar.
263 ///
264 /// # Returns
265 ///
266 /// The result of subtracting the scalar from the tensor.
267 fn float_sub_scalar(lhs: FloatTensor<B>, rhs: Scalar) -> FloatTensor<B>;
268
269 /// Multiplies two tensors together element-wise.
270 fn float_mul(lhs: FloatTensor<B>, rhs: FloatTensor<B>) -> FloatTensor<B>;
271
272 /// Multiplies a tensor by a scalar.
273 ///
274 /// # Arguments
275 ///
276 /// * `lhs` - The left-hand side tensor.
277 /// * `rhs` - The right-hand side scalar.
278 ///
279 /// # Returns
280 ///
281 /// The result of multiplying the tensor by the scalar.
282 fn float_mul_scalar(lhs: FloatTensor<B>, rhs: Scalar) -> FloatTensor<B>;
283
284 /// Divides two tensors element-wise.
285 ///
286 /// # Arguments
287 ///
288 /// * `lhs` - The left-hand side tensor.
289 /// * `rhs` - The right-hand side tensor.
290 ///
291 /// # Returns
292 ///
293 /// The result of dividing the two tensors.
294 fn float_div(lhs: FloatTensor<B>, rhs: FloatTensor<B>) -> FloatTensor<B>;
295
296 /// Divides a tensor by a scalar.
297 ///
298 /// # Arguments
299 ///
300 /// * `lhs` - The left-hand side tensor.
301 /// * `rhs` - The right-hand side scalar.
302 ///
303 /// # Returns
304 ///
305 /// The result of dividing the tensor by the scalar.
306 fn float_div_scalar(lhs: FloatTensor<B>, rhs: Scalar) -> FloatTensor<B>;
307
308 /// Computes the remainder of division between two tensors element-wise.
309 ///
310 /// # Arguments
311 ///
312 /// * `lhs` - The left-hand side tensor.
313 /// * `rhs` - The right-hand side tensor.
314 ///
315 /// # Returns
316 ///
317 /// The element-wise remainder when dividing `lhs` by `rhs`.
318 fn float_remainder(lhs: FloatTensor<B>, rhs: FloatTensor<B>) -> FloatTensor<B>;
319
320 /// Computes the modulus of a tensor given a scalar.
321 ///
322 /// # Arguments
323 /// * `lhs` - The left-hand side tensor.
324 /// * `rhs` - The right-hand side scalar.
325 ///
326 /// # Returns
327 ///
328 /// The result of applying the modulus of the scalar to the tensor.
329 fn float_remainder_scalar(lhs: FloatTensor<B>, rhs: Scalar) -> FloatTensor<B>;
330
331 /// Multiplies two tensors together using matrix multiplication.
332 ///
333 /// # Arguments
334 ///
335 /// * `lhs` - The left-hand side tensor.
336 /// * `rhs` - The right-hand side tensor.
337 ///
338 /// # Returns
339 ///
340 /// The result of multiplying the two tensors together using matrix multiplication.
341 fn float_matmul(lhs: FloatTensor<B>, rhs: FloatTensor<B>) -> FloatTensor<B>;
342
343 /// Computes the cross product of two tensors along a given dimension.
344 ///
345 /// # Arguments
346 ///
347 /// * `lhs` - The left-hand side tensor.
348 /// * `rhs` - The right-hand side tensor.
349 /// * `dim` - The dimension to compute the cross product along.
350 ///
351 /// # Returns
352 ///
353 /// The cross product of the two tensors.
354 fn float_cross(lhs: FloatTensor<B>, rhs: FloatTensor<B>, dim: usize) -> FloatTensor<B>;
355
356 /// Negates a tensor element-wise.
357 fn float_neg(tensor: FloatTensor<B>) -> FloatTensor<B> {
358 Self::float_mul_scalar(tensor, (-1f32).into())
359 }
360
361 /// Calculates the reciprocals element-wise
362 fn float_recip(tensor: FloatTensor<B>) -> FloatTensor<B>;
363
364 /// Transposes a tensor.
365 ///
366 /// # Arguments
367 ///
368 /// * `tensor` - The tensor to transpose.
369 ///
370 /// # Returns
371 ///
372 /// The transposed tensor.
373 fn float_transpose(tensor: FloatTensor<B>) -> FloatTensor<B> {
374 let ndims = tensor.shape().num_dims();
375 Self::float_swap_dims(tensor, ndims - 2, ndims - 1)
376 }
377
378 /// Swaps two dimensions of a tensor.
379 ///
380 /// # Arguments
381 ///
382 /// * `tensor` - The tensor to swap the dimensions of.
383 /// * `dim1` - The first dimension to swap.
384 /// * `dim2` - The second dimension to swap.
385 ///
386 /// # Returns
387 ///
388 /// The tensor with the dimensions swapped.
389 fn float_swap_dims(tensor: FloatTensor<B>, dim1: usize, dim2: usize) -> FloatTensor<B>;
390
391 /// Permutes the dimensions of a tensor.
392 ///
393 /// # Arguments
394 ///
395 /// * `tensor` - The tensor to permute the dimensions of.
396 /// * `axes` - The new order of the dimensions.
397 /// # Returns
398 ///
399 /// The tensor with the dimensions permuted.
400 fn float_permute(tensor: FloatTensor<B>, axes: &[usize]) -> FloatTensor<B>;
401
402 /// Reverse the order of elements in a tensor along the given axes.
403 ///
404 /// # Arguments
405 ///
406 /// * `tensor` - The tensor to reverse.
407 /// * `axes` - The axes to reverse.
408 ///
409 /// The tensor with the elements reversed.
410 fn float_flip(tensor: FloatTensor<B>, axes: &[usize]) -> FloatTensor<B>;
411
412 /// Reshapes a tensor.
413 ///
414 /// # Arguments
415 ///
416 /// * `tensor` - The tensor to reshape.
417 /// * `shape` - The new shape of the tensor.
418 ///
419 /// # Returns
420 ///
421 /// The tensor with the new shape.
422 fn float_reshape(tensor: FloatTensor<B>, shape: Shape) -> FloatTensor<B>;
423
424 /// Gather elements from a tensor.
425 ///
426 /// # Arguments
427 ///
428 /// * `dim` - The dimension to gather from.
429 /// * `tensor` - The tensor to gather from.
430 /// * `indices` - The indices to gather.
431 ///
432 /// # Returns
433 ///
434 /// The gathered elements.
435 fn float_gather(dim: usize, tensor: FloatTensor<B>, indices: IntTensor<B>) -> FloatTensor<B>;
436
437 /// Scatter elements into a tensor using sum reduction.
438 ///
439 /// # Arguments
440 ///
441 /// * `dim` - The dimension to scatter into.
442 /// * `tensor` - The tensor to scatter into.
443 /// * `indices` - The indices to scatter into.
444 /// * `value` - The value to scatter.
445 ///
446 /// # Returns
447 ///
448 /// The tensor with the scattered elements.
449 fn float_scatter_add(
450 dim: usize,
451 tensor: FloatTensor<B>,
452 indices: IntTensor<B>,
453 value: FloatTensor<B>,
454 ) -> FloatTensor<B>;
455
456 /// Multi-dimensional scatter: update `data` at locations specified by `indices` with `values`.
457 ///
458 /// # Arguments
459 ///
460 /// * `data` - The tensor to scatter into.
461 /// * `indices` - An M-dimensional integer tensor whose last dimension indexes into `data`.
462 /// * `values` - The values to scatter.
463 /// * `reduction` - How to combine with existing values.
464 ///
465 /// # Returns
466 ///
467 /// The tensor with scattered values.
468 fn float_scatter_nd(
469 _data: FloatTensor<B>,
470 _indices: IntTensor<B>,
471 _values: FloatTensor<B>,
472 _reduction: crate::tensor::IndexingUpdateOp,
473 ) -> FloatTensor<B> {
474 unimplemented!("float_scatter_nd is not implemented for this backend")
475 }
476
477 /// Multi-dimensional gather: collect slices from `data` at locations specified by `indices`.
478 ///
479 /// # Arguments
480 ///
481 /// * `data` - The tensor to gather from.
482 /// * `indices` - An M-dimensional integer tensor whose last dimension indexes into `data`.
483 ///
484 /// # Returns
485 ///
486 /// The gathered tensor.
487 fn float_gather_nd(_data: FloatTensor<B>, _indices: IntTensor<B>) -> FloatTensor<B> {
488 unimplemented!("float_gather_nd is not implemented for this backend")
489 }
490
491 /// Select tensor elements along the given dimension corresponding for the given indices.
492 ///
493 /// # Arguments
494 ///
495 /// * `tensor` - The tensor to select from.
496 /// * `dim` - The dimension to select from.
497 /// * `indices` - The indices to select.
498 ///
499 /// # Returns
500 ///
501 /// The selected elements.
502 fn float_select(tensor: FloatTensor<B>, dim: usize, indices: IntTensor<B>) -> FloatTensor<B>;
503
504 /// Assign the selected elements along the given dimension corresponding for the given indices
505 /// to the given value using sum reduction.
506 ///
507 /// # Arguments
508 ///
509 /// * `tensor` - The tensor to select from.
510 /// * `dim` - The dimension to select from.
511 /// * `indices` - The indices to select.
512 /// * `value` - The value to assign.
513 ///
514 /// # Returns
515 ///
516 /// The tensor with the selected elements assigned to the given value.
517 fn float_select_add(
518 tensor: FloatTensor<B>,
519 dim: usize,
520 indices: IntTensor<B>,
521 value: FloatTensor<B>,
522 ) -> FloatTensor<B>;
523
524 /// Select tensor elements corresponding to the given slices.
525 ///
526 /// # Arguments
527 ///
528 /// * `tensor` - The tensor to select from.
529 /// * `slices` - The slices specifying ranges and steps for each dimension.
530 ///
531 /// # Returns
532 ///
533 /// The selected elements in a new tensor.
534 ///
535 /// # Note
536 ///
537 /// Empty slices (where start >= end) are handled at the high-level tensor API and will not
538 /// be passed to this method. Backend implementations do not need to handle empty slices.
539 fn float_slice(tensor: FloatTensor<B>, slices: &[Slice]) -> FloatTensor<B>;
540
541 /// Assign the selected elements corresponding to the given slices to the given value.
542 ///
543 /// # Arguments
544 ///
545 /// * `tensor` - The tensor to select from.
546 /// * `ranges` - The ranges to select.
547 /// * `value` - The value to assign.
548 ///
549 /// # Returns
550 ///
551 /// The tensor with the selected elements assigned to the given value.
552 ///
553 /// # Note
554 ///
555 /// Empty slice assignments (where any slice range produces 0 elements) are handled at the
556 /// high-level tensor API and will not be passed to this method. Backend implementations do
557 /// not need to handle empty slice assignments.
558 fn float_slice_assign(
559 tensor: FloatTensor<B>,
560 slices: &[Slice],
561 value: FloatTensor<B>,
562 ) -> FloatTensor<B>;
563
564 /// Update the given tensor with the value tensor where the mask is true.
565 ///
566 /// # Arguments
567 ///
568 /// * `tensor` - The tensor to select from.
569 /// * `mask` - The boolean mask to select with.
570 /// * `value` - The value to assign to the selected elements from the value tensor.
571 ///
572 /// # Returns
573 ///
574 /// The tensor with the selected elements assigned to the given value.
575 fn float_mask_where(
576 tensor: FloatTensor<B>,
577 mask: BoolTensor<B>,
578 value: FloatTensor<B>,
579 ) -> FloatTensor<B>;
580
581 /// Update the given tensor with the value where the mask is true.
582 ///
583 /// # Arguments
584 ///
585 /// * `tensor` - The tensor to select from.
586 /// * `mask` - The boolean mask to select with.
587 /// * `value` - The value to assign to the selected elements.
588 ///
589 /// # Returns
590 ///
591 /// The tensor with the selected elements assigned to the given value.
592 fn float_mask_fill(
593 tensor: FloatTensor<B>,
594 mask: BoolTensor<B>,
595 value: Scalar,
596 ) -> FloatTensor<B>;
597
598 /// Equal comparison of two tensors.
599 ///
600 /// # Arguments
601 ///
602 /// * `lhs` - The left-hand side tensor.
603 /// * `rhs` - The right-hand side tensor.
604 /// * `out_dtype` - The output tensor dtype.
605 ///
606 /// # Returns
607 ///
608 /// A boolean tensor with the result of the comparison.
609 fn float_equal(lhs: FloatTensor<B>, rhs: FloatTensor<B>, out_dtype: BoolDType)
610 -> BoolTensor<B>;
611
612 /// Element-wise non-equality comparison.
613 ///
614 /// # Arguments
615 ///
616 /// * `lhs` - The left-hand side tensor.
617 /// * `rhs` - The right-hand side tensor.
618 /// * `out_dtype` - The output tensor dtype.
619 ///
620 /// # Returns
621 ///
622 /// A boolean tensor with the result of the comparison.
623 fn float_not_equal(
624 lhs: FloatTensor<B>,
625 rhs: FloatTensor<B>,
626 out_dtype: BoolDType,
627 ) -> BoolTensor<B> {
628 let equal_tensor = B::float_equal(lhs, rhs, out_dtype);
629 B::bool_not(equal_tensor)
630 }
631
632 /// Equal comparison of a tensor and a scalar.
633 ///
634 /// # Arguments
635 ///
636 /// * `lhs` - The left-hand side tensor.
637 /// * `rhs` - The right-hand side scalar.
638 /// * `out_dtype` - The output tensor dtype.
639 ///
640 /// # Returns
641 ///
642 /// A boolean tensor with the result of the comparison.
643 fn float_equal_elem(lhs: FloatTensor<B>, rhs: Scalar, out_dtype: BoolDType) -> BoolTensor<B>;
644
645 /// Element-wise non-equality comparison with a scalar.
646 ///
647 /// # Arguments
648 ///
649 /// * `lhs` - The left-hand side tensor.
650 /// * `rhs` - The right-hand side scalar.
651 /// * `out_dtype` - The output tensor dtype.
652 ///
653 /// # Returns
654 ///
655 /// A boolean tensor with the result of the comparison.
656 fn float_not_equal_elem(
657 lhs: FloatTensor<B>,
658 rhs: Scalar,
659 out_dtype: BoolDType,
660 ) -> BoolTensor<B> {
661 let equal_tensor = B::float_equal_elem(lhs, rhs, out_dtype);
662 B::bool_not(equal_tensor)
663 }
664
665 /// Greater than comparison of two tensors.
666 ///
667 /// # Arguments
668 ///
669 /// * `lhs` - The left-hand side tensor.
670 /// * `rhs` - The right-hand side tensor.
671 /// * `out_dtype` - The output tensor dtype.
672 ///
673 /// # Returns
674 ///
675 /// A boolean tensor with the result of the comparison.
676 fn float_greater(
677 lhs: FloatTensor<B>,
678 rhs: FloatTensor<B>,
679 out_dtype: BoolDType,
680 ) -> BoolTensor<B>;
681
682 /// Greater than comparison of a tensor and a scalar.
683 ///
684 /// # Arguments
685 ///
686 /// * `lhs` - The left-hand side tensor.
687 /// * `rhs` - The right-hand side scalar.
688 /// * `out_dtype` - The output tensor dtype.
689 ///
690 /// # Returns
691 ///
692 /// A boolean tensor with the result of the comparison.
693 fn float_greater_elem(lhs: FloatTensor<B>, rhs: Scalar, out_dtype: BoolDType) -> BoolTensor<B>;
694
695 /// Greater than or equal comparison of two tensors.
696 ///
697 /// # Arguments
698 ///
699 /// * `lhs` - The left-hand side tensor.
700 /// * `rhs` - The right-hand side tensor.
701 /// * `out_dtype` - The output tensor dtype.
702 ///
703 /// # Returns
704 ///
705 /// A boolean tensor with the result of the comparison.
706 fn float_greater_equal(
707 lhs: FloatTensor<B>,
708 rhs: FloatTensor<B>,
709 out_dtype: BoolDType,
710 ) -> BoolTensor<B>;
711
712 /// Greater than or equal comparison of a tensor and a scalar.
713 ///
714 /// # Arguments
715 ///
716 /// * `lhs` - The left-hand side tensor.
717 /// * `rhs` - The right-hand side scalar.
718 /// * `out_dtype` - The output tensor dtype.
719 ///
720 /// # Returns
721 ///
722 /// A boolean tensor with the result of the comparison.
723 fn float_greater_equal_elem(
724 lhs: FloatTensor<B>,
725 rhs: Scalar,
726 out_dtype: BoolDType,
727 ) -> BoolTensor<B>;
728
729 /// Less than comparison of two tensors.
730 ///
731 /// # Arguments
732 ///
733 /// * `lhs` - The left-hand side tensor.
734 /// * `rhs` - The right-hand side tensor.
735 /// * `out_dtype` - The output tensor dtype.
736 ///
737 /// # Returns
738 ///
739 /// A boolean tensor with the result of the comparison.
740 fn float_lower(lhs: FloatTensor<B>, rhs: FloatTensor<B>, out_dtype: BoolDType)
741 -> BoolTensor<B>;
742
743 /// Less than comparison of a tensor and a scalar.
744 ///
745 /// # Arguments
746 ///
747 /// * `lhs` - The left-hand side tensor.
748 /// * `rhs` - The right-hand side scalar.
749 /// * `out_dtype` - The output tensor dtype.
750 ///
751 /// # Returns
752 ///
753 /// A boolean tensor with the result of the comparison.
754 fn float_lower_elem(lhs: FloatTensor<B>, rhs: Scalar, out_dtype: BoolDType) -> BoolTensor<B>;
755
756 /// Less than or equal comparison of two tensors.
757 ///
758 /// # Arguments
759 ///
760 /// * `lhs` - The left-hand side tensor.
761 /// * `rhs` - The right-hand side tensor.
762 /// * `out_dtype` - The output tensor dtype.
763 ///
764 /// # Returns
765 ///
766 /// A boolean tensor with the result of the comparison.
767 fn float_lower_equal(
768 lhs: FloatTensor<B>,
769 rhs: FloatTensor<B>,
770 out_dtype: BoolDType,
771 ) -> BoolTensor<B>;
772
773 /// Less than or equal comparison of a tensor and a scalar.
774 ///
775 /// # Arguments
776 ///
777 /// * `lhs` - The left-hand side tensor.
778 /// * `rhs` - The right-hand side scalar.
779 /// * `out_dtype` - The output tensor dtype.
780 ///
781 /// # Returns
782 ///
783 /// A boolean tensor with the result of the comparison.
784 fn float_lower_equal_elem(
785 lhs: FloatTensor<B>,
786 rhs: Scalar,
787 out_dtype: BoolDType,
788 ) -> BoolTensor<B>;
789
790 /// Detaches a tensor from the computation graph.
791 fn float_detach(tensor: FloatTensor<B>) -> FloatTensor<B> {
792 // Should only be overridden by autodiff backends.
793 tensor
794 }
795
796 /// Sets the `require_grad` flag of a tensor.
797 fn float_set_require_grad(tensor: FloatTensor<B>, _require_grad: bool) -> FloatTensor<B> {
798 // Should only be overridden by autodiff backends.
799 tensor
800 }
801
802 /// Returns the `require_grad` flag of a tensor.
803 fn float_is_require_grad(_tensor: &FloatTensor<B>) -> bool {
804 // Should only be overridden by autodiff backends.
805 false
806 }
807
808 /// Sum of all elements in a tensor.
809 ///
810 /// # Arguments
811 ///
812 /// * `tensor` - The tensor to sum.
813 ///
814 /// # Returns
815 ///
816 /// A scalar tensor with the sum of all elements in `tensor`.
817 fn float_sum(tensor: FloatTensor<B>) -> FloatTensor<B>;
818
819 /// Sum of all elements in a tensor along a dimension.
820 ///
821 /// # Arguments
822 ///
823 /// * `tensor` - The tensor to sum.
824 /// * `dim` - The dimension along which to sum.
825 ///
826 /// # Returns
827 ///
828 /// A tensor with the sum of all elements in `tensor` along `dim`.
829 fn float_sum_dim(tensor: FloatTensor<B>, dim: usize) -> FloatTensor<B>;
830
831 /// Product of all elements in a tensor.
832 ///
833 /// # Arguments
834 ///
835 /// * `tensor` - The tensor to product.
836 ///
837 /// # Returns
838 ///
839 /// A scalar tensor with the product of all elements in `tensor`.
840 fn float_prod(tensor: FloatTensor<B>) -> FloatTensor<B> {
841 // Product of all elements in a tensor
842 B::float_exp(B::float_sum(B::float_log(tensor)))
843 }
844
845 /// Product of all elements in a tensor along a dimension.
846 ///
847 /// # Arguments
848 ///
849 /// * `tensor` - The tensor to product.
850 ///
851 /// # Returns
852 ///
853 /// A tensor with the product of all elements in `tensor` along `dim`.
854 fn float_prod_dim(tensor: FloatTensor<B>, dim: usize) -> FloatTensor<B> {
855 // Product of all elements in a tensor along a dimension
856 B::float_exp(B::float_sum_dim(B::float_log(tensor), dim))
857 }
858
859 /// Mean of all elements in a tensor.
860 ///
861 /// # Arguments
862 ///
863 /// * `tensor` - The tensor to mean.
864 ///
865 /// # Returns
866 ///
867 /// A scalar tensor with the mean of all elements in `tensor`.
868 fn float_mean(tensor: FloatTensor<B>) -> FloatTensor<B> {
869 let num_elems = tensor.shape().num_elements() as f32;
870 B::float_div_scalar(B::float_sum(tensor), num_elems.into())
871 }
872
873 /// Mean of all elements in a tensor along a dimension.
874 ///
875 /// # Arguments
876 ///
877 /// * `tensor` - The tensor to mean.
878 /// * `dim` - The dimension along which to mean.
879 ///
880 /// # Returns
881 ///
882 /// A tensor with the mean of all elements in `tensor` along `dim`.
883 fn float_mean_dim(tensor: FloatTensor<B>, dim: usize) -> FloatTensor<B>;
884
885 /// Computes the cumulative sum of elements along a dimension.
886 ///
887 /// # Arguments
888 ///
889 /// * `tensor` - The tensor to compute the cumulative sum of.
890 /// * `dim` - The dimension along which to compute the cumulative sum.
891 ///
892 /// # Returns
893 ///
894 /// A tensor with the same shape where each element is the cumulative sum
895 /// of all elements up to and including that position along the dimension.
896 fn float_cumsum(tensor: FloatTensor<B>, dim: usize) -> FloatTensor<B>;
897
898 /// Computes the cumulative product of elements along a dimension.
899 ///
900 /// # Arguments
901 ///
902 /// * `tensor` - The tensor to compute the cumulative product of.
903 /// * `dim` - The dimension along which to compute the cumulative product.
904 ///
905 /// # Returns
906 ///
907 /// A tensor with the same shape where each element is the cumulative product
908 /// of all elements up to and including that position along the dimension.
909 fn float_cumprod(tensor: FloatTensor<B>, dim: usize) -> FloatTensor<B>;
910
911 /// Computes the cumulative minimum of elements along a dimension.
912 ///
913 /// # Arguments
914 ///
915 /// * `tensor` - The tensor to compute the cumulative minimum of.
916 /// * `dim` - The dimension along which to compute the cumulative minimum.
917 ///
918 /// # Returns
919 ///
920 /// A tensor with the same shape where each element is the minimum
921 /// of all elements up to and including that position along the dimension.
922 fn float_cummin(tensor: FloatTensor<B>, dim: usize) -> FloatTensor<B>;
923
924 /// Computes the cumulative maximum of elements along a dimension.
925 ///
926 /// # Arguments
927 ///
928 /// * `tensor` - The tensor to compute the cumulative maximum of.
929 /// * `dim` - The dimension along which to compute the cumulative maximum.
930 ///
931 /// # Returns
932 ///
933 /// A tensor with the same shape where each element is the maximum
934 /// of all elements up to and including that position along the dimension.
935 fn float_cummax(tensor: FloatTensor<B>, dim: usize) -> FloatTensor<B>;
936
937 /// Converts a tensor to another floating point data type.
938 ///
939 /// # Arguments
940 ///
941 /// * `tensor` - The tensor to convert.
942 /// * `dtype` - The target data type.
943 ///
944 /// # Returns
945 ///
946 /// A tensor with the same values as `tensor` but in the target floating point data type.
947 fn float_cast(tensor: FloatTensor<B>, dtype: FloatDType) -> FloatTensor<B>;
948
949 /// Returns a new tensor with exponential values.
950 ///
951 /// # Arguments
952 ///
953 /// * `tensor` - The tensor to exponentiate.
954 ///
955 /// # Returns
956 ///
957 /// A tensor with the same shape as `tensor` with exponential values.
958 fn float_exp(tensor: FloatTensor<B>) -> FloatTensor<B>;
959
960 /// Returns a new tensor with natural logarithm values.
961 ///
962 /// # Arguments
963 ///
964 /// * `tensor` - The tensor to take the logarithm of.
965 ///
966 /// # Returns
967 ///
968 /// A tensor with the same shape as `tensor` with natural logarithm values.
969 fn float_log(tensor: FloatTensor<B>) -> FloatTensor<B>;
970
971 /// Returns a new tensor with logarithm values of (1 + Xi).
972 ///
973 /// # Arguments
974 ///
975 /// * `tensor` - The tensor to take the logarithm of.
976 ///
977 /// # Returns
978 ///
979 /// A tensor with the same shape as `tensor` with logarithm values of (1 + Xi).
980 fn float_log1p(tensor: FloatTensor<B>) -> FloatTensor<B>;
981
982 /// Element-wise power with a FloatTensor.
983 ///
984 /// # Arguments
985 ///
986 /// * `lhs` - The left-hand side tensor.
987 /// * `rhs` - The right-hand side tensor.
988 ///
989 /// # Returns
990 ///
991 /// The elements of `lhs` raised to the power of the elements of `rhs`.
992 fn float_powf(lhs: FloatTensor<B>, rhs: FloatTensor<B>) -> FloatTensor<B>;
993
994 /// Element-wise power with an IntTensor.
995 ///
996 /// # Arguments
997 ///
998 /// * `lhs` - The left-hand side tensor.
999 /// * `rhs` - The right-hand side floatTensor.
1000 ///
1001 /// # Returns
1002 ///
1003 /// The elements of `lhs` raised to the value of `rhs`. Result is an IntTensor.
1004 fn float_powi(lhs: FloatTensor<B>, rhs: IntTensor<B>) -> FloatTensor<B> {
1005 let dtype = lhs.dtype();
1006 Self::float_powf(lhs, B::int_into_float(rhs, dtype.into()))
1007 }
1008
1009 /// Raises a tensor to the power of an int scalar.
1010 ///
1011 /// # Backend Implementors Note
1012 ///
1013 /// A number of common exponent cases can be implemented with operations
1014 /// which are much cheaper than generic exponentiation.
1015 ///
1016 /// This (`Backend` impl overridable) operation handles generic optimizations
1017 /// for several common integer exponent cases; and then dispatches to
1018 /// the (`Backend` impl overridable) [`Self::float_powi_scalar_impl`]
1019 /// operation to handle the generic case.
1020 ///
1021 /// # Arguments
1022 ///
1023 /// * `lhs` - The left-hand side tensor.
1024 /// * `rhs` - The right-hand side scalar.
1025 ///
1026 /// # Returns
1027 ///
1028 /// The elements of `lhs` raised to the value of `rhs`.
1029 fn float_powi_scalar(lhs: FloatTensor<B>, rhs: Scalar) -> FloatTensor<B> {
1030 match rhs.elem::<i64>() {
1031 0 => Self::float_ones(lhs.shape(), &lhs.device(), lhs.dtype().into()),
1032 1 => lhs,
1033 2 => B::float_mul(lhs.clone(), lhs),
1034 -1 => Self::float_recip(lhs),
1035 -2 => Self::float_recip(B::float_mul(lhs.clone(), lhs)),
1036 _ => Self::float_powi_scalar_impl(lhs, rhs),
1037 }
1038 }
1039
1040 /// Raises a tensor to the power of an int scalar.
1041 ///
1042 /// # Backend Implementors Note
1043 ///
1044 /// This is the generic implementation of integer exponentiation
1045 /// called by [`Self::float_powi_scalar`] in the fallback case.
1046 ///
1047 /// As a general rule, this should not be called directly.
1048 ///
1049 /// # Arguments
1050 ///
1051 /// * `lhs` - The left-hand side tensor.
1052 /// * `rhs` - The right-hand side scalar.
1053 ///
1054 /// # Returns
1055 ///
1056 /// The elements of `lhs` raised to the value of `rhs`.
1057 fn float_powi_scalar_impl(lhs: FloatTensor<B>, rhs: Scalar) -> FloatTensor<B> {
1058 // Avoid a recursive loop by deferring directly to float_powf_scalar_impl.
1059 Self::float_powf_scalar_impl(lhs, rhs)
1060 }
1061
1062 /// Returns a new tensor with values raised to the power of float `value`.
1063 ///
1064 /// # Backend Implementors Note
1065 ///
1066 /// This (`Backend` impl overridable) operation dispatches integer exponentiation
1067 /// to [`Self::float_powi_scalar`], and the remaining non-integer exponent cases to
1068 /// the (`Backend` impl overridable) [`Self::float_powf_scalar_impl`]
1069 /// operation to handle the generic case.
1070 ///
1071 /// # Arguments
1072 ///
1073 /// * `tensor` - The tensor to exponentiate.
1074 /// * `value` - The exponent.
1075 ///
1076 /// # Returns
1077 ///
1078 /// A tensor with the same shape as `tensor` with values raised to the power of `value`.
1079 fn float_powf_scalar(tensor: FloatTensor<B>, value: Scalar) -> FloatTensor<B> {
1080 if let Some(exp) = value.try_as_integer() {
1081 Self::float_powi_scalar(tensor, exp)
1082 } else {
1083 Self::float_powf_scalar_impl(tensor, value)
1084 }
1085 }
1086
1087 /// Returns a new tensor with values raised to the power of float `value`.
1088 ///
1089 /// # Backend Implementors Note
1090 ///
1091 /// This is the generic implementation of integer exponentiation
1092 /// called by [`Self::float_powf_scalar`] in the fallback case.
1093 ///
1094 /// This is the minimal required support a `Backend` must implement
1095 /// for exponentiation.
1096 ///
1097 /// As a general rule, this should not be called directly.
1098 ///
1099 /// # Arguments
1100 ///
1101 /// * `tensor` - The tensor to exponentiate.
1102 /// * `value` - The exponent.
1103 ///
1104 /// # Returns
1105 ///
1106 /// A tensor with the same shape as `tensor` with values raised to the power of `value`.
1107 fn float_powf_scalar_impl(tensor: FloatTensor<B>, value: Scalar) -> FloatTensor<B>;
1108
1109 /// Returns a new tensor with square root values.
1110 ///
1111 /// # Arguments
1112 ///
1113 /// * `tensor` - The tensor to take the square root of.
1114 ///
1115 /// # Returns
1116 ///
1117 /// A tensor with the same shape as `tensor` with square root values.
1118 fn float_sqrt(tensor: FloatTensor<B>) -> FloatTensor<B>;
1119
1120 /// Returns a new tensor with the Euclidean distance values.
1121 ///
1122 /// # Arguments
1123 ///
1124 /// * `lhs` - The left-hand side tensor.
1125 /// * `rhs` - The right-hand side tensor.
1126 ///
1127 /// # Returns
1128 ///
1129 /// A tensor with the same shape as `lhs` and `rhs` with hypotenuse values.
1130 fn float_hypot(lhs: FloatTensor<B>, rhs: FloatTensor<B>) -> FloatTensor<B> {
1131 // default implementation for any backend that can't either iterator over elements or doesn't have
1132 // a native hypot implementation
1133
1134 // Mirrors glibc's approach: scale by max(|lhs|, |rhs|) to avoid
1135 // overflow/underflow in the intermediate squaring step.
1136 //
1137 // hypot(x, y) = |max| * sqrt(1 + (min/max)^2)
1138 //
1139 // Edge cases:
1140 // - If max == 0, both inputs are 0, result is 0 (division guarded by clamp)
1141 // - If max is inf, result is inf (propagates naturally through sqrt)
1142 // - NaN propagates naturally
1143 let abs_lhs = B::float_abs(lhs);
1144 let abs_rhs = B::float_abs(rhs);
1145
1146 let diff = B::float_clamp_min(B::float_sub(abs_rhs.clone(), abs_lhs.clone()), 0.0.into());
1147 let max = B::float_add(abs_lhs.clone(), diff.clone());
1148 let min = B::float_sub(abs_rhs, diff);
1149
1150 // Clamp max to at least epsilon to avoid 0/0; result will be 0 anyway
1151 // since min <= max, so (min/clamped_max)^2 won't blow up meaningfully.
1152 let max_safe = B::float_clamp_min(
1153 max.clone(),
1154 max.dtype().finfo().unwrap().min_positive.into(),
1155 );
1156
1157 let ratio = B::float_div(min, max_safe);
1158 let ratio_sq = B::float_mul(ratio.clone(), ratio);
1159
1160 let inner = B::float_add_scalar(ratio_sq, 1.0.into());
1161
1162 B::float_mul(max, B::float_sqrt(inner))
1163 }
1164
1165 /// Returns a new tensor with absolute values.
1166 ///
1167 /// # Arguments
1168 ///
1169 /// * `tensor` - The tensor to take absolute value of.
1170 ///
1171 /// # Returns
1172 ///
1173 /// A tensor with the same shape as `tensor` with absolute values.
1174 fn float_abs(tensor: FloatTensor<B>) -> FloatTensor<B>;
1175
1176 /// Returns a new tensor with cosine values.
1177 ///
1178 /// # Arguments
1179 ///
1180 /// * `tensor` - The tensor to take the cosine of.
1181 ///
1182 /// # Returns
1183 ///
1184 /// A tensor with the same shape as `tensor` with cosine values.
1185 fn float_cos(tensor: FloatTensor<B>) -> FloatTensor<B>;
1186
1187 /// Returns a new tensor with sine values.
1188 ///
1189 /// # Arguments
1190 ///
1191 /// * `tensor` - The tensor to take the sine of.
1192 ///
1193 /// # Returns
1194 ///
1195 /// A tensor with the same shape as `tensor` with sine values.
1196 fn float_sin(tensor: FloatTensor<B>) -> FloatTensor<B>;
1197
1198 /// Returns a new tensor with tangent values.
1199 ///
1200 /// # Arguments
1201 ///
1202 /// * `tensor` - The tensor to take the tangent of.
1203 ///
1204 /// # Returns
1205 ///
1206 /// A tensor with the same shape as `tensor` with tangent values.
1207 fn float_tan(tensor: FloatTensor<B>) -> FloatTensor<B>;
1208
1209 /// Returns a new tensor with hyperbolic cosine values.
1210 ///
1211 /// # Arguments
1212 ///
1213 /// * `tensor` - The tensor to take the hyperbolic cosine of.
1214 ///
1215 /// # Returns
1216 ///
1217 /// A tensor with the same shape as `tensor` with hyperbolic cosine values.
1218 fn float_cosh(tensor: FloatTensor<B>) -> FloatTensor<B>;
1219
1220 /// Returns a new tensor with hyperbolic sine values.
1221 ///
1222 /// # Arguments
1223 ///
1224 /// * `tensor` - The tensor to take the hyperbolic sine of.
1225 ///
1226 /// # Returns
1227 ///
1228 /// A tensor with the same shape as `tensor` with hyperbolic sine values.
1229 fn float_sinh(tensor: FloatTensor<B>) -> FloatTensor<B>;
1230
1231 /// Returns a new tensor with hyperbolic tangent values.
1232 ///
1233 /// # Arguments
1234 ///
1235 /// * `tensor` - The tensor to take the hyperbolic tangent of.
1236 ///
1237 /// # Returns
1238 ///
1239 /// A tensor with the same shape as `tensor` with hyperbolic tangent values.
1240 fn float_tanh(tensor: FloatTensor<B>) -> FloatTensor<B>;
1241
1242 /// Returns a new tensor with inverse cosine values.
1243 ///
1244 /// # Arguments
1245 ///
1246 /// * `tensor` - The input tensor.
1247 ///
1248 /// # Returns
1249 ///
1250 /// A tensor with the same shape as `tensor` with inverse cosine values.
1251 fn float_acos(tensor: FloatTensor<B>) -> FloatTensor<B>;
1252
1253 /// Returns a new tensor with inverse hyperbolic cosine values.
1254 ///
1255 /// # Arguments
1256 ///
1257 /// * `tensor` - The input tensor.
1258 ///
1259 /// # Returns
1260 ///
1261 /// A tensor with the same shape as `tensor` with inverse hyperbolic cosine values.
1262 fn float_acosh(tensor: FloatTensor<B>) -> FloatTensor<B>;
1263
1264 /// Returns a new tensor with inverse sine values.
1265 ///
1266 /// # Arguments
1267 ///
1268 /// * `tensor` - The input tensor.
1269 ///
1270 /// # Returns
1271 ///
1272 /// A tensor with the same shape as `tensor` with inverse sine values.
1273 fn float_asin(tensor: FloatTensor<B>) -> FloatTensor<B>;
1274
1275 /// Returns a new tensor with inverse hyperbolic sine values.
1276 ///
1277 /// # Arguments
1278 ///
1279 /// * `tensor` - The input tensor.
1280 ///
1281 /// # Returns
1282 ///
1283 /// A tensor with the same shape as `tensor` with inverse hyperbolic sine values.
1284 fn float_asinh(tensor: FloatTensor<B>) -> FloatTensor<B>;
1285
1286 /// Returns a new tensor with the inverse tangent values.
1287 ///
1288 /// # Arguments
1289 ///
1290 /// * `tensor` - The input tensor.
1291 ///
1292 /// # Returns
1293 ///
1294 /// A tensor with the same shape as `tensor` with the inverse tangent values.
1295 fn float_atan(tensor: FloatTensor<B>) -> FloatTensor<B>;
1296
1297 /// Returns a new tensor with the inverse hyperbolic tangent values.
1298 ///
1299 /// # Arguments
1300 ///
1301 /// * `tensor` - The input tensor.
1302 ///
1303 /// # Returns
1304 ///
1305 /// A tensor with the same shape as `tensor` with the inverse hyperbolic tangent values.
1306 fn float_atanh(tensor: FloatTensor<B>) -> FloatTensor<B>;
1307
1308 /// Returns a tensor with the four-quadrant inverse tangent values of `y` and `x`.
1309 ///
1310 /// # Arguments
1311 ///
1312 /// * `lhs` - The tensor with y coordinates.
1313 /// * `rhs` - The tensor with x coordinates.
1314 ///
1315 /// # Returns
1316 ///
1317 /// A tensor with the four-quadrant inverse tangent values.
1318 fn float_atan2(lhs: FloatTensor<B>, rhs: FloatTensor<B>) -> FloatTensor<B>;
1319
1320 /// Returns a new tensor with rounded values.
1321 ///
1322 /// This function should implement the [round half to even](https://en.wikipedia.org/wiki/Rounding#Rounding_half_to_even)
1323 /// strategy, with halfway cases rounded to the nearest even integer value.
1324 ///
1325 /// # Arguments
1326 ///
1327 /// * `tensor` - The tensor to be rounded.
1328 ///
1329 /// # Returns
1330 ///
1331 /// A tensor with the same shape as `tensor` with rounded values.
1332 fn float_round(tensor: FloatTensor<B>) -> FloatTensor<B>;
1333
1334 /// Returns a new tensor with floored values.
1335 ///
1336 /// # Arguments
1337 ///
1338 /// * `tensor` - The tensor to be floored.
1339 ///
1340 /// # Returns
1341 ///
1342 /// A tensor with the same shape as `tensor` with floored values.
1343 fn float_floor(tensor: FloatTensor<B>) -> FloatTensor<B>;
1344
1345 /// Returns a new tensor with ceiled values.
1346 ///
1347 /// # Arguments
1348 ///
1349 /// * `tensor` - The tensor to be ceiled.
1350 ///
1351 /// # Returns
1352 ///
1353 /// A tensor with the same shape as `tensor` with ceiled values.
1354 fn float_ceil(tensor: FloatTensor<B>) -> FloatTensor<B>;
1355
1356 /// Returns a new tensor with truncated values.
1357 ///
1358 /// # Arguments
1359 ///
1360 /// * `tensor` - The tensor to be truncated.
1361 ///
1362 /// # Returns
1363 ///
1364 /// A tensor with the same shape as `tensor` with truncated values.
1365 fn float_trunc(tensor: FloatTensor<B>) -> FloatTensor<B>;
1366
1367 /// Returns a new tensor with the error function values.
1368 ///
1369 /// # Arguments
1370 ///
1371 /// * `tensor` - The tensor to take the error function of.
1372 ///
1373 /// # Returns
1374 ///
1375 /// A tensor with the same shape as `tensor` with error function values.
1376 fn float_erf(tensor: FloatTensor<B>) -> FloatTensor<B>;
1377
1378 /// Concatenates tensors along a dimension.
1379 ///
1380 /// # Arguments
1381 ///
1382 /// * `tensors` - The tensors to concatenate.
1383 /// * `dim` - The dimension along which to concatenate.
1384 ///
1385 /// # Returns
1386 ///
1387 /// A tensor with the concatenated tensors along `dim`.
1388 ///
1389 /// # Note
1390 ///
1391 /// Empty tensors (where the concatenation dimension has size 0) are filtered out at the
1392 /// high-level tensor API and will not be passed to this method. Backend implementations do
1393 /// not need to handle empty tensors.
1394 fn float_cat(tensors: Vec<FloatTensor<B>>, dim: usize) -> FloatTensor<B> {
1395 let first_tensor = tensors.first().expect("Tensors should not be empty");
1396 let device = first_tensor.device();
1397
1398 cat_with_slice_assign::<B, _, _, _>(
1399 tensors,
1400 dim,
1401 device,
1402 |shape, device, dtype| B::float_empty(shape, device, dtype.into()),
1403 B::float_slice_assign,
1404 )
1405 }
1406
1407 /// Gets the indices of the maximum elements of a tensor along an axis.
1408 ///
1409 /// # Arguments
1410 ///
1411 /// * `tensor` - The tensor to get the maximum elements of.
1412 /// * `dim` - The dimension along which to get the maximum elements.
1413 /// * `out_dtype` - The output tensor dtype.
1414 ///
1415 /// # Returns
1416 ///
1417 /// A tensor with the indices of the maximum elements of `tensor` along `dim`.
1418 fn float_argmax(tensor: FloatTensor<B>, dim: usize, out_dtype: IntDType) -> IntTensor<B>;
1419
1420 /// Gets the indices of the k maximum elements of a tensor along an axis.
1421 /// if two elements are equals, it will be ordered by lowest indices
1422 ///
1423 /// # Arguments
1424 ///
1425 /// * `tensor` - The tensor to get the maximum elements of.
1426 /// * `dim` - The dimension along which to get the maximum elements.
1427 /// * `k` - number of maximum elements
1428 /// * `out_dtype` - The output tensor dtype.
1429 ///
1430 /// # Returns
1431 ///
1432 /// A tensor with the indices of the maximum elements of `tensor` along `dim`.
1433 fn float_argtopk(
1434 tensor: FloatTensor<B>,
1435 dim: usize,
1436 k: usize,
1437 out_dtype: IntDType,
1438 ) -> IntTensor<B> {
1439 let device = tensor.device();
1440 let dtype = get_device_settings::<B>(&device).int_dtype;
1441 let k_indices = B::int_arange(0..k as i64, &device, dtype);
1442 B::int_select(
1443 Self::float_argsort(tensor, dim, true, out_dtype),
1444 dim,
1445 k_indices,
1446 )
1447 }
1448
1449 /// Gets the values of the k maximum elements of a tensor along an axis.
1450 ///
1451 /// # Arguments
1452 ///
1453 /// * `tensor` - The tensor to get the maximum elements of.
1454 /// * `dim` - The dimension along which to get the maximum elements.
1455 /// * `k` - number of maximum elements
1456 /// * `out_dtype` - The output tensor dtype.
1457 ///
1458 /// # Returns
1459 ///
1460 /// A tensor with the values of the maximum elements of `tensor` along `dim`.
1461 fn float_topk(tensor: FloatTensor<B>, dim: usize, k: usize) -> FloatTensor<B> {
1462 let device = tensor.device();
1463 let dtype = get_device_settings::<B>(&device).int_dtype;
1464 let k_indices = B::int_arange(0..k as i64, &device, dtype);
1465 Self::float_select(Self::float_sort(tensor, dim, true), dim, k_indices)
1466 }
1467
1468 /// Gets the values of the k maximum elements of a tensor along an axis, and their indices.
1469 ///
1470 /// # Arguments
1471 ///
1472 /// * `tensor` - The tensor to get the maximum elements of.
1473 /// * `dim` - The dimension along which to get the maximum elements.
1474 /// * `k` - number of maximum elements
1475 /// * `out_dtype` - The indices tensor dtype.
1476 ///
1477 /// # Returns
1478 ///
1479 /// A tuple with the values of the k maximum elements of `tensor` along `dim`, and their
1480 /// indices.
1481 ///
1482 /// The default sorts once and keeps the first `k` of each half. It deliberately does not
1483 /// compose `float_topk` with `float_argtopk`: those default to a sort each, so that would
1484 /// sort twice, and it would also require `float_argtopk` from backends that only have
1485 /// sorting. Backends whose top-k already carries both results should override this and
1486 /// produce them in a single pass.
1487 fn float_topk_with_indices(
1488 tensor: FloatTensor<B>,
1489 dim: usize,
1490 k: usize,
1491 out_dtype: IntDType,
1492 ) -> (FloatTensor<B>, IntTensor<B>) {
1493 let device = tensor.device();
1494 let dtype = get_device_settings::<B>(&device).int_dtype;
1495 let k_indices = B::int_arange(0..k as i64, &device, dtype);
1496 let (values, indices) = Self::float_sort_with_indices(tensor, dim, true, out_dtype);
1497
1498 (
1499 Self::float_select(values, dim, k_indices.clone()),
1500 B::int_select(indices, dim, k_indices),
1501 )
1502 }
1503
1504 /// Gets the indices of the minimum elements of a tensor along an axis.
1505 ///
1506 /// # Arguments
1507 ///
1508 /// * `tensor` - The tensor to get the minimum elements of.
1509 /// * `dim` - The dimension along which to get the minimum elements.
1510 /// * `out_dtype` - The output tensor dtype.
1511 ///
1512 /// # Returns
1513 ///
1514 /// A tensor with the indices of the minimum elements of `tensor` along `dim`.
1515 fn float_argmin(tensor: FloatTensor<B>, dim: usize, out_dtype: IntDType) -> IntTensor<B>;
1516
1517 /// Gets the maximum element of a tensor.
1518 ///
1519 /// # Arguments
1520 ///
1521 /// * `tensor` - The tensor to get the maximum elements of.
1522 ///
1523 /// # Returns
1524 ///
1525 /// A tensor with the maximum element of `tensor`.
1526 fn float_max(tensor: FloatTensor<B>) -> FloatTensor<B> {
1527 let shape = tensor.shape();
1528 let tensor = B::float_reshape(tensor, Shape::new([shape.num_elements()]));
1529
1530 B::float_max_dim(tensor, 0)
1531 }
1532
1533 /// Gets the maximum elements of a tensor along an axis.
1534 ///
1535 /// # Arguments
1536 ///
1537 /// * `tensor` - The tensor to get the maximum elements of.
1538 /// * `dim` - The dimension along which to get the maximum elements.
1539 ///
1540 /// # Returns
1541 ///
1542 /// A tensor with the maximum elements of `tensor` along `dim`.
1543 fn float_max_dim(tensor: FloatTensor<B>, dim: usize) -> FloatTensor<B> {
1544 let dtype = get_device_settings::<B>(&tensor.device()).int_dtype;
1545 let index = B::float_argmax(tensor.clone(), dim, dtype);
1546
1547 B::float_gather(dim, tensor, index)
1548 }
1549
1550 /// Gets the maximum elements of a tensor along an axis and their indices.
1551 ///
1552 /// # Arguments
1553 ///
1554 /// * `tensor` - The tensor to get the maximum elements of.
1555 /// * `dim` - The dimension along which to get the maximum elements.
1556 /// * `indices_dtype` - The indices tensor dtype.
1557 ///
1558 /// # Returns
1559 ///
1560 /// A tuple with the maximum elements of `tensor` along `dim` and their indices.
1561 fn float_max_dim_with_indices(
1562 tensor: FloatTensor<B>,
1563 dim: usize,
1564 indices_dtype: IntDType,
1565 ) -> (FloatTensor<B>, IntTensor<B>) {
1566 let index = B::float_argmax(tensor.clone(), dim, indices_dtype);
1567 let values = B::float_gather(dim, tensor, index.clone());
1568
1569 (values, index)
1570 }
1571
1572 /// Gets the minimum element of a tensor.
1573 ///
1574 /// # Arguments
1575 ///
1576 /// * `tensor` - The tensor to get the minimum elements of.
1577 ///
1578 /// # Returns
1579 ///
1580 /// A tensor with the minimum element of `tensor`.
1581 fn float_min(tensor: FloatTensor<B>) -> FloatTensor<B> {
1582 let shape = tensor.shape();
1583 let tensor = B::float_reshape(tensor, Shape::new([shape.num_elements()]));
1584
1585 B::float_min_dim(tensor, 0)
1586 }
1587
1588 /// Gets the minimum elements of a tensor along an axis.
1589 ///
1590 /// # Arguments
1591 ///
1592 /// * `tensor` - The tensor to get the minimum elements of.
1593 /// * `dim` - The dimension along which to get the minimum elements.
1594 ///
1595 /// # Returns
1596 ///
1597 /// A tensor with the minimum elements of `tensor` along `dim`.
1598 fn float_min_dim(tensor: FloatTensor<B>, dim: usize) -> FloatTensor<B> {
1599 let dtype = get_device_settings::<B>(&tensor.device()).int_dtype;
1600 let index = B::float_argmin(tensor.clone(), dim, dtype);
1601
1602 B::float_gather(dim, tensor, index)
1603 }
1604
1605 /// Gets the minimum elements of a tensor along an axis and their indices.
1606 ///
1607 /// # Arguments
1608 ///
1609 /// * `tensor` - The tensor to get the minimum elements of.
1610 /// * `dim` - The dimension along which to get the minimum elements.
1611 /// * `indices_dtype` - The indices tensor dtype.
1612 ///
1613 /// # Returns
1614 ///
1615 /// A tuple with the minimum elements of `tensor` along `dim` and their indices.
1616 fn float_min_dim_with_indices(
1617 tensor: FloatTensor<B>,
1618 dim: usize,
1619 indices_dtype: IntDType,
1620 ) -> (FloatTensor<B>, IntTensor<B>) {
1621 let index = B::float_argmin(tensor.clone(), dim, indices_dtype);
1622 let values = B::float_gather(dim, tensor, index.clone());
1623
1624 (values, index)
1625 }
1626
1627 /// Gets the maximum absolute element of a tensor.
1628 ///
1629 /// # Arguments
1630 ///
1631 /// * `tensor` - The tensor to get the maximum elements of.
1632 ///
1633 /// # Returns
1634 ///
1635 /// A tensor with the maximum element of `tensor`.
1636 fn float_max_abs(tensor: FloatTensor<B>) -> FloatTensor<B> {
1637 let shape = tensor.shape();
1638 let tensor = B::float_reshape(tensor, Shape::new([shape.num_elements()]));
1639
1640 B::float_max_abs_dim(tensor, 0)
1641 }
1642
1643 /// Gets the maximum absolute elements of a tensor along an axis.
1644 ///
1645 /// # Arguments
1646 ///
1647 /// * `tensor` - The tensor to get the maximum elements of.
1648 /// * `dim` - The dimension along which to get the maximum elements.
1649 ///
1650 /// # Returns
1651 ///
1652 /// A tensor with the maximum elements of `tensor` along `dim`.
1653 fn float_max_abs_dim(tensor: FloatTensor<B>, dim: usize) -> FloatTensor<B> {
1654 B::float_max_dim(B::float_abs(tensor), dim)
1655 }
1656
1657 /// Tests if any element in the float `tensor` evaluates to True.
1658 ///
1659 /// # Arguments
1660 ///
1661 /// * `tensor` - The tensor to test.
1662 /// * `out_dtype` - The output tensor dtype.
1663 ///
1664 /// # Returns
1665 ///
1666 /// A boolean tensor with a single element, True if any element in the tensor is True, False otherwise.
1667 fn float_any(tensor: FloatTensor<B>, out_dtype: BoolDType) -> BoolTensor<B> {
1668 let float_dtype = tensor.dtype();
1669 let bool_tensor = B::float_equal_elem(tensor, 0f32.into(), out_dtype);
1670 let bool_tensor = B::bool_not(bool_tensor);
1671 let sum = B::float_sum(B::bool_into_float(bool_tensor, float_dtype.into()));
1672 B::float_greater_elem(sum, 0f32.into(), out_dtype)
1673 }
1674
1675 /// Tests if any element in the float `tensor` evaluates to True along a given dimension `dim`.
1676 ///
1677 /// # Arguments
1678 ///
1679 /// * `tensor` - The tensor to test.
1680 /// * `dim` - The axis along which to test.
1681 /// * `out_dtype` - The output tensor dtype.
1682 ///
1683 /// # Returns
1684 ///
1685 /// A boolean tensor `Tensor<B, D, Bool>` with the same size as input `tensor`, except in the `dim` axis
1686 /// where the size is 1. The elem in the `dim` axis is True if any element along this dim in the
1687 /// input evaluates to True, False otherwise.
1688 fn float_any_dim(tensor: FloatTensor<B>, dim: usize, out_dtype: BoolDType) -> BoolTensor<B> {
1689 let float_dtype = tensor.dtype();
1690 let bool_tensor = B::float_equal_elem(tensor, 0f32.into(), out_dtype);
1691 let bool_tensor = B::bool_not(bool_tensor);
1692 let sum = B::float_sum_dim(B::bool_into_float(bool_tensor, float_dtype.into()), dim);
1693 B::float_greater_elem(sum, 0f32.into(), out_dtype)
1694 }
1695
1696 /// Tests if all elements in the float `tensor` evaluate to True.
1697 ///
1698 /// # Arguments
1699 ///
1700 /// * `tensor` - The tensor to test.
1701 /// * `out_dtype` - The output tensor dtype.
1702 ///
1703 /// # Returns
1704 ///
1705 /// A boolean tensor `Tensor<B, 1, Bool>` with a single element, True if all elements in the input tensor
1706 /// evaluate to True, False otherwise.
1707 fn float_all(tensor: FloatTensor<B>, out_dtype: BoolDType) -> BoolTensor<B> {
1708 let float_dtype = tensor.dtype();
1709 let num_elems = tensor.shape().num_elements() as f32;
1710 let bool_tensor = B::float_equal_elem(tensor, 0f32.into(), out_dtype);
1711 let bool_tensor = B::bool_not(bool_tensor);
1712 let sum = B::float_sum(B::bool_into_float(bool_tensor, float_dtype.into()));
1713 B::float_equal_elem(sum, num_elems.into(), out_dtype)
1714 }
1715
1716 /// Tests if all elements in the float `tensor` evaluate to True along a given dimension `dim`.
1717 ///
1718 /// # Arguments
1719 ///
1720 /// * `tensor` - The tensor to test.
1721 /// * `dim` - The axis along which to test.
1722 /// * `out_dtype` - The output tensor dtype.
1723 ///
1724 /// # Returns
1725 ///
1726 /// A boolean tensor `Tensor<B, D, Bool>` with the same size as input `tensor`, except in the `dim` axis
1727 /// where the size is 1. The elem in the `dim` axis is True if all elements along this dim in the input
1728 /// evaluates to True, False otherwise.
1729 fn float_all_dim(tensor: FloatTensor<B>, dim: usize, out_dtype: BoolDType) -> BoolTensor<B> {
1730 let float_dtype = tensor.dtype();
1731 let num_elems = tensor.shape()[dim] as f32;
1732 let bool_tensor = B::float_equal_elem(tensor, 0f32.into(), out_dtype);
1733 let bool_tensor = B::bool_not(bool_tensor);
1734 let sum = B::float_sum_dim(B::bool_into_float(bool_tensor, float_dtype.into()), dim);
1735 B::float_equal_elem(sum, num_elems.into(), out_dtype)
1736 }
1737
1738 /// Returns the signs of the float `tensor`.
1739 ///
1740 /// # Arguments
1741 ///
1742 /// * `tensor` - The tensor to extract the signs from.
1743 ///
1744 /// # Returns
1745 ///
1746 /// A tensor with the same shape as `tensor` containing the signs of the elements of `tensor`.
1747 fn float_sign(tensor: FloatTensor<B>) -> FloatTensor<B> {
1748 let device = tensor.device();
1749 let bool_dtype = get_device_settings::<B>(&tensor.device()).bool_dtype;
1750 let zeros = B::float_zeros(tensor.shape(), &device, tensor.dtype().into());
1751 let less_than_zero = B::float_lower_elem(tensor.clone(), 0f32.into(), bool_dtype);
1752 let greater_than_zero = B::float_greater_elem(tensor, 0f32.into(), bool_dtype);
1753
1754 let mut result = B::float_mask_fill(zeros, less_than_zero, (-1f32).into());
1755 result = B::float_mask_fill(result, greater_than_zero, 1f32.into());
1756 result
1757 }
1758
1759 /// Broadcasts the float `tensor` to the given `shape`.
1760 fn float_expand(tensor: FloatTensor<B>, shape: Shape) -> FloatTensor<B>;
1761
1762 /// Sort the elements of the input `tensor` by value in along a given dimension.
1763 ///
1764 /// This sort is unstable (i.e., may reorder equal elements).
1765 ///
1766 /// # Arguments
1767 ///
1768 /// * `tensor` - The input tensor.
1769 /// * `dim` - The axis along which to sort.
1770 /// * `descending` - The sorting order.
1771 ///
1772 /// # Returns
1773 ///
1774 /// A tensor with the same shape as the input tensor, where the elements are sorted by value.
1775 fn float_sort(tensor: FloatTensor<B>, dim: usize, descending: bool) -> FloatTensor<B> {
1776 let device = tensor.device();
1777 sort::<B, _, _, _>(
1778 tensor,
1779 dim,
1780 descending,
1781 device,
1782 |tensor| {
1783 let msg = "Failed to synchronously read tensor data. This operation is not supported until this backend has a GPU sorting implementation.";
1784 try_read_sync(B::float_into_data(tensor))
1785 .expect(msg)
1786 .expect(msg)
1787 },
1788 |data, device, _dtype| B::float_from_data(data, device),
1789 )
1790 }
1791
1792 /// Sort the elements of the input `tensor` by value in along a given dimension.
1793 ///
1794 /// This sort is unstable (i.e., may reorder equal elements).
1795 ///
1796 /// # Arguments
1797 ///
1798 /// * `tensor` - The input tensor.
1799 /// * `dim` - The axis along which to sort.
1800 /// * `descending` - The sorting order.
1801 /// * `indices_dtype` - The indices tensor dtype.
1802 ///
1803 /// # Returns
1804 ///
1805 /// A tensor with the same shape as the input tensor and corresponding indices, where
1806 /// the elements are sorted by value and the indices map back to the original input tensor.
1807 fn float_sort_with_indices(
1808 tensor: FloatTensor<B>,
1809 dim: usize,
1810 descending: bool,
1811 indices_dtype: IntDType,
1812 ) -> (FloatTensor<B>, IntTensor<B>) {
1813 let device = tensor.device();
1814 sort_with_indices::<B, _, _, _>(
1815 tensor,
1816 dim,
1817 descending,
1818 indices_dtype,
1819 device,
1820 |tensor| {
1821 let msg = "Failed to synchronously read tensor data. This operation is not supported until this backend has a GPU sorting implementation.";
1822 try_read_sync(B::float_into_data(tensor))
1823 .expect(msg)
1824 .expect(msg)
1825 },
1826 |data, device, _dtype| B::float_from_data(data, device),
1827 )
1828 }
1829
1830 /// Returns the indices that sort the elements of the input `tensor` by value along a given dimension.
1831 ///
1832 /// This sort is unstable (i.e., may reorder equal elements).
1833 ///
1834 /// # Arguments
1835 ///
1836 /// * `tensor` - The input tensor.
1837 /// * `dim` - The axis along which to sort.
1838 /// * `descending` - The sorting order.
1839 /// * `out_dtype` - The output tensor dtype.
1840 ///
1841 /// # Returns
1842 ///
1843 /// A tensor with the same shape as the input tensor the indices map back to the original input tensor.
1844 fn float_argsort(
1845 tensor: FloatTensor<B>,
1846 dim: usize,
1847 descending: bool,
1848 out_dtype: IntDType,
1849 ) -> IntTensor<B> {
1850 let device = tensor.device();
1851 argsort::<B, _, _>(tensor, dim, descending, out_dtype, device, |tensor| {
1852 let msg = "Failed to synchronously read tensor data. This operation is not supported until this backend has a GPU sorting implementation.";
1853 try_read_sync(B::float_into_data(tensor))
1854 .expect(msg)
1855 .expect(msg)
1856 })
1857 }
1858
1859 /// Samples tensor as a two-dimensional spatial grid of (possibly multi-channel) values,
1860 /// using the given locations in [-1, 1].
1861 ///
1862 /// # Arguments
1863 ///
1864 /// * `tensor` - The tensor being sampled from, must be contiguous with shape (N, C, H_in, W_in)
1865 /// * `grid` - A tensor of locations, with shape (N, H_out, W_out, 2). Values are [-1, 1].
1866 /// A [x = -1, y = -1] means top-left, and [x = 1, y = 1] means bottom-right
1867 /// * `options` - Grid sampling options (mode, padding_mode, align_corners)
1868 ///
1869 /// # Returns
1870 ///
1871 /// A tensor with shape (N, C, H_out, W_out)
1872 fn float_grid_sample_2d(
1873 tensor: FloatTensor<B>,
1874 grid: FloatTensor<B>,
1875 options: GridSampleOptions,
1876 ) -> FloatTensor<B> {
1877 // TODO: default impl should get int default dtype
1878 float_grid_sample_2d_ref::<B>(tensor, grid, options)
1879 }
1880
1881 /// Unfold windows along a dimension.
1882 ///
1883 /// Returns a view of the tensor with all complete windows of size `size` in dimension `dim`;
1884 /// where windows are advanced by `step` at each index.
1885 ///
1886 /// The number of windows is `max(0, (shape[dim] - size).ceil_div(step))`.
1887 ///
1888 /// # Arguments
1889 ///
1890 /// * `tensor` - The input tensor to unfold; of shape ``[pre=..., dim shape, post=...]``
1891 /// * `dim` - the selected dim.
1892 /// * `size` - the size of each unfolded window.
1893 /// * `step` - the step between each window.
1894 ///
1895 /// # Returns
1896 ///
1897 /// A tensor view with shape ``[pre=..., windows, size, post=...]``.
1898 fn float_unfold(tensor: FloatTensor<B>, dim: usize, size: usize, step: usize)
1899 -> FloatTensor<B>;
1900
1901 /// Returns a new tensor with boolean elements indicating whether each element of the input is NaN.
1902 ///
1903 /// # Returns
1904 ///
1905 /// A boolean tensor where `true` indicates NaN and `false` indicates a non-NaN value.
1906 fn float_is_nan(tensor: FloatTensor<B>, out_dtype: BoolDType) -> BoolTensor<B> {
1907 // Check if the input tensor is NaN by comparing it to itself
1908 // NaN is the only value that is not equal to itself
1909 B::float_not_equal(tensor.clone(), tensor, out_dtype)
1910 }
1911
1912 /// Returns a new tensor with boolean elements indicating whether each element of the input is infinite (either +INF or -INF).
1913 ///
1914 /// # Returns
1915 ///
1916 /// A boolean tensor where `true` indicates that the value is infinite
1917 fn float_is_inf(tensor: FloatTensor<B>, out_dtype: BoolDType) -> BoolTensor<B> {
1918 B::float_equal_elem(B::float_abs(tensor), f64::INFINITY.into(), out_dtype)
1919 }
1920}