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, IndexingUpdateOp, IntDType, PadMode, 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 the specified update operation.
438 fn float_scatter(
439 dim: usize,
440 tensor: FloatTensor<B>,
441 indices: IntTensor<B>,
442 value: FloatTensor<B>,
443 update: IndexingUpdateOp,
444 ) -> FloatTensor<B>;
445
446 /// Multi-dimensional scatter: update `data` at locations specified by `indices` with `values`.
447 ///
448 /// # Arguments
449 ///
450 /// * `data` - The tensor to scatter into.
451 /// * `indices` - An M-dimensional integer tensor whose last dimension indexes into `data`.
452 /// * `values` - The values to scatter.
453 /// * `reduction` - How to combine with existing values.
454 ///
455 /// # Returns
456 ///
457 /// The tensor with scattered values.
458 fn float_scatter_nd(
459 _data: FloatTensor<B>,
460 _indices: IntTensor<B>,
461 _values: FloatTensor<B>,
462 _reduction: crate::tensor::IndexingUpdateOp,
463 ) -> FloatTensor<B> {
464 unimplemented!("float_scatter_nd is not implemented for this backend")
465 }
466
467 /// Multi-dimensional gather: collect slices from `data` at locations specified by `indices`.
468 ///
469 /// # Arguments
470 ///
471 /// * `data` - The tensor to gather from.
472 /// * `indices` - An M-dimensional integer tensor whose last dimension indexes into `data`.
473 ///
474 /// # Returns
475 ///
476 /// The gathered tensor.
477 fn float_gather_nd(_data: FloatTensor<B>, _indices: IntTensor<B>) -> FloatTensor<B> {
478 unimplemented!("float_gather_nd is not implemented for this backend")
479 }
480
481 /// Select tensor elements along the given dimension corresponding for the given indices.
482 ///
483 /// # Arguments
484 ///
485 /// * `tensor` - The tensor to select from.
486 /// * `dim` - The dimension to select from.
487 /// * `indices` - The indices to select.
488 ///
489 /// # Returns
490 ///
491 /// The selected elements.
492 fn float_select(tensor: FloatTensor<B>, dim: usize, indices: IntTensor<B>) -> FloatTensor<B>;
493
494 /// Assign selected elements along a dimension using the specified update operation.
495 fn float_select_assign(
496 tensor: FloatTensor<B>,
497 dim: usize,
498 indices: IntTensor<B>,
499 value: FloatTensor<B>,
500 update: IndexingUpdateOp,
501 ) -> FloatTensor<B>;
502
503 /// Select tensor elements corresponding to the given slices.
504 ///
505 /// # Arguments
506 ///
507 /// * `tensor` - The tensor to select from.
508 /// * `slices` - The slices specifying ranges and steps for each dimension.
509 ///
510 /// # Returns
511 ///
512 /// The selected elements in a new tensor.
513 ///
514 /// # Note
515 ///
516 /// Empty slices (where start >= end) are handled at the high-level tensor API and will not
517 /// be passed to this method. Backend implementations do not need to handle empty slices.
518 fn float_slice(tensor: FloatTensor<B>, slices: &[Slice]) -> FloatTensor<B>;
519
520 /// Assign the selected elements corresponding to the given slices to the given value.
521 ///
522 /// # Arguments
523 ///
524 /// * `tensor` - The tensor to select from.
525 /// * `ranges` - The ranges to select.
526 /// * `value` - The value to assign.
527 ///
528 /// # Returns
529 ///
530 /// The tensor with the selected elements assigned to the given value.
531 ///
532 /// # Note
533 ///
534 /// Empty slice assignments (where any slice range produces 0 elements) are handled at the
535 /// high-level tensor API and will not be passed to this method. Backend implementations do
536 /// not need to handle empty slice assignments.
537 fn float_slice_assign(
538 tensor: FloatTensor<B>,
539 slices: &[Slice],
540 value: FloatTensor<B>,
541 ) -> FloatTensor<B>;
542
543 /// Update the given tensor with the value tensor where the mask is true.
544 ///
545 /// # Arguments
546 ///
547 /// * `tensor` - The tensor to select from.
548 /// * `mask` - The boolean mask to select with.
549 /// * `value` - The value to assign to the selected elements from the value tensor.
550 ///
551 /// # Returns
552 ///
553 /// The tensor with the selected elements assigned to the given value.
554 fn float_mask_where(
555 tensor: FloatTensor<B>,
556 mask: BoolTensor<B>,
557 value: FloatTensor<B>,
558 ) -> FloatTensor<B>;
559
560 /// Update the given tensor with the value where the mask is true.
561 ///
562 /// # Arguments
563 ///
564 /// * `tensor` - The tensor to select from.
565 /// * `mask` - The boolean mask to select with.
566 /// * `value` - The value to assign to the selected elements.
567 ///
568 /// # Returns
569 ///
570 /// The tensor with the selected elements assigned to the given value.
571 fn float_mask_fill(
572 tensor: FloatTensor<B>,
573 mask: BoolTensor<B>,
574 value: Scalar,
575 ) -> FloatTensor<B>;
576
577 /// Selects the elements of the tensor where the mask is true, returned as a 1D tensor.
578 ///
579 /// The elements are collected in row-major order. Because the number of selected elements
580 /// depends on the mask values, the output shape is data-dependent: computing it may require
581 /// synchronizing with the device, which is why this operation is asynchronous.
582 ///
583 /// # Arguments
584 ///
585 /// * `tensor` - The tensor to select from.
586 /// * `mask` - The boolean mask, with the same shape as the tensor.
587 ///
588 /// # Returns
589 ///
590 /// A 1D tensor containing the selected elements.
591 fn float_mask_select(
592 tensor: FloatTensor<B>,
593 mask: BoolTensor<B>,
594 ) -> impl Future<Output = FloatTensor<B>> + 'static + Send {
595 async move {
596 // Data-dependent output length, so we defer to `bool_argwhere` (the only pre-existing
597 // data-dependent op) to collect the flat indices of the true mask values, then select.
598 let n = mask.shape().num_elements();
599 let int_dtype = get_device_settings::<B>(&mask.device()).int_dtype;
600 let mask = B::bool_reshape(mask, Shape::new([n]));
601 let indices = B::bool_argwhere(mask, int_dtype).await; // [count, 1]
602 let count = indices.shape()[0];
603 let indices = B::int_reshape(indices, Shape::new([count])); // squeeze to [count]
604 let tensor = B::float_reshape(tensor, Shape::new([n]));
605 B::float_select(tensor, 0, indices)
606 }
607 }
608
609 /// Equal comparison of two tensors.
610 ///
611 /// # Arguments
612 ///
613 /// * `lhs` - The left-hand side tensor.
614 /// * `rhs` - The right-hand side tensor.
615 /// * `out_dtype` - The output tensor dtype.
616 ///
617 /// # Returns
618 ///
619 /// A boolean tensor with the result of the comparison.
620 fn float_equal(lhs: FloatTensor<B>, rhs: FloatTensor<B>, out_dtype: BoolDType)
621 -> BoolTensor<B>;
622
623 /// Element-wise non-equality comparison.
624 ///
625 /// # Arguments
626 ///
627 /// * `lhs` - The left-hand side tensor.
628 /// * `rhs` - The right-hand side tensor.
629 /// * `out_dtype` - The output tensor dtype.
630 ///
631 /// # Returns
632 ///
633 /// A boolean tensor with the result of the comparison.
634 fn float_not_equal(
635 lhs: FloatTensor<B>,
636 rhs: FloatTensor<B>,
637 out_dtype: BoolDType,
638 ) -> BoolTensor<B> {
639 let equal_tensor = B::float_equal(lhs, rhs, out_dtype);
640 B::bool_not(equal_tensor)
641 }
642
643 /// Equal comparison of a tensor and a scalar.
644 ///
645 /// # Arguments
646 ///
647 /// * `lhs` - The left-hand side tensor.
648 /// * `rhs` - The right-hand side scalar.
649 /// * `out_dtype` - The output tensor dtype.
650 ///
651 /// # Returns
652 ///
653 /// A boolean tensor with the result of the comparison.
654 fn float_equal_elem(lhs: FloatTensor<B>, rhs: Scalar, out_dtype: BoolDType) -> BoolTensor<B>;
655
656 /// Element-wise non-equality comparison with a scalar.
657 ///
658 /// # Arguments
659 ///
660 /// * `lhs` - The left-hand side tensor.
661 /// * `rhs` - The right-hand side scalar.
662 /// * `out_dtype` - The output tensor dtype.
663 ///
664 /// # Returns
665 ///
666 /// A boolean tensor with the result of the comparison.
667 fn float_not_equal_elem(
668 lhs: FloatTensor<B>,
669 rhs: Scalar,
670 out_dtype: BoolDType,
671 ) -> BoolTensor<B> {
672 let equal_tensor = B::float_equal_elem(lhs, rhs, out_dtype);
673 B::bool_not(equal_tensor)
674 }
675
676 /// Greater than comparison of two tensors.
677 ///
678 /// # Arguments
679 ///
680 /// * `lhs` - The left-hand side tensor.
681 /// * `rhs` - The right-hand side tensor.
682 /// * `out_dtype` - The output tensor dtype.
683 ///
684 /// # Returns
685 ///
686 /// A boolean tensor with the result of the comparison.
687 fn float_greater(
688 lhs: FloatTensor<B>,
689 rhs: FloatTensor<B>,
690 out_dtype: BoolDType,
691 ) -> BoolTensor<B>;
692
693 /// Greater than comparison of a tensor and a scalar.
694 ///
695 /// # Arguments
696 ///
697 /// * `lhs` - The left-hand side tensor.
698 /// * `rhs` - The right-hand side scalar.
699 /// * `out_dtype` - The output tensor dtype.
700 ///
701 /// # Returns
702 ///
703 /// A boolean tensor with the result of the comparison.
704 fn float_greater_elem(lhs: FloatTensor<B>, rhs: Scalar, out_dtype: BoolDType) -> BoolTensor<B>;
705
706 /// Greater than or equal comparison of two tensors.
707 ///
708 /// # Arguments
709 ///
710 /// * `lhs` - The left-hand side tensor.
711 /// * `rhs` - The right-hand side tensor.
712 /// * `out_dtype` - The output tensor dtype.
713 ///
714 /// # Returns
715 ///
716 /// A boolean tensor with the result of the comparison.
717 fn float_greater_equal(
718 lhs: FloatTensor<B>,
719 rhs: FloatTensor<B>,
720 out_dtype: BoolDType,
721 ) -> BoolTensor<B>;
722
723 /// Greater than or equal comparison of a tensor and a scalar.
724 ///
725 /// # Arguments
726 ///
727 /// * `lhs` - The left-hand side tensor.
728 /// * `rhs` - The right-hand side scalar.
729 /// * `out_dtype` - The output tensor dtype.
730 ///
731 /// # Returns
732 ///
733 /// A boolean tensor with the result of the comparison.
734 fn float_greater_equal_elem(
735 lhs: FloatTensor<B>,
736 rhs: Scalar,
737 out_dtype: BoolDType,
738 ) -> BoolTensor<B>;
739
740 /// Less than comparison of two tensors.
741 ///
742 /// # Arguments
743 ///
744 /// * `lhs` - The left-hand side tensor.
745 /// * `rhs` - The right-hand side tensor.
746 /// * `out_dtype` - The output tensor dtype.
747 ///
748 /// # Returns
749 ///
750 /// A boolean tensor with the result of the comparison.
751 fn float_lower(lhs: FloatTensor<B>, rhs: FloatTensor<B>, out_dtype: BoolDType)
752 -> BoolTensor<B>;
753
754 /// Less than comparison of a tensor and a scalar.
755 ///
756 /// # Arguments
757 ///
758 /// * `lhs` - The left-hand side tensor.
759 /// * `rhs` - The right-hand side scalar.
760 /// * `out_dtype` - The output tensor dtype.
761 ///
762 /// # Returns
763 ///
764 /// A boolean tensor with the result of the comparison.
765 fn float_lower_elem(lhs: FloatTensor<B>, rhs: Scalar, out_dtype: BoolDType) -> BoolTensor<B>;
766
767 /// Less than or equal comparison of two tensors.
768 ///
769 /// # Arguments
770 ///
771 /// * `lhs` - The left-hand side tensor.
772 /// * `rhs` - The right-hand side tensor.
773 /// * `out_dtype` - The output tensor dtype.
774 ///
775 /// # Returns
776 ///
777 /// A boolean tensor with the result of the comparison.
778 fn float_lower_equal(
779 lhs: FloatTensor<B>,
780 rhs: FloatTensor<B>,
781 out_dtype: BoolDType,
782 ) -> BoolTensor<B>;
783
784 /// Less than or equal comparison of a tensor and a scalar.
785 ///
786 /// # Arguments
787 ///
788 /// * `lhs` - The left-hand side tensor.
789 /// * `rhs` - The right-hand side scalar.
790 /// * `out_dtype` - The output tensor dtype.
791 ///
792 /// # Returns
793 ///
794 /// A boolean tensor with the result of the comparison.
795 fn float_lower_equal_elem(
796 lhs: FloatTensor<B>,
797 rhs: Scalar,
798 out_dtype: BoolDType,
799 ) -> BoolTensor<B>;
800
801 /// Detaches a tensor from the computation graph.
802 fn float_detach(tensor: FloatTensor<B>) -> FloatTensor<B> {
803 // Should only be overridden by autodiff backends.
804 tensor
805 }
806
807 /// Sets the `require_grad` flag of a tensor.
808 fn float_set_require_grad(tensor: FloatTensor<B>, _require_grad: bool) -> FloatTensor<B> {
809 // Should only be overridden by autodiff backends.
810 tensor
811 }
812
813 /// Returns the `require_grad` flag of a tensor.
814 fn float_is_require_grad(_tensor: &FloatTensor<B>) -> bool {
815 // Should only be overridden by autodiff backends.
816 false
817 }
818
819 /// Sum of all elements in a tensor.
820 ///
821 /// # Arguments
822 ///
823 /// * `tensor` - The tensor to sum.
824 ///
825 /// # Returns
826 ///
827 /// A scalar tensor with the sum of all elements in `tensor`.
828 fn float_sum(tensor: FloatTensor<B>) -> FloatTensor<B>;
829
830 /// Sum of all elements in a tensor along a dimension.
831 ///
832 /// # Arguments
833 ///
834 /// * `tensor` - The tensor to sum.
835 /// * `dim` - The dimension along which to sum.
836 ///
837 /// # Returns
838 ///
839 /// A tensor with the sum of all elements in `tensor` along `dim`.
840 fn float_sum_dim(tensor: FloatTensor<B>, dim: usize) -> FloatTensor<B>;
841
842 /// Sum the tensor along several dimensions at once, keeping each of them
843 /// with length one.
844 ///
845 /// # Arguments
846 ///
847 /// * `tensor` - The tensor to sum.
848 /// * `dims` - The dimensions along which to sum.
849 ///
850 /// # Returns
851 ///
852 /// A tensor with the same rank, and length one along each of `dims`.
853 ///
854 /// The default reduces one dimension at a time, which writes and reads
855 /// back an intermediate per dimension. A backend that can fold the
856 /// dimensions into fewer reductions should override this.
857 fn float_sum_dims(tensor: FloatTensor<B>, dims: &[usize]) -> FloatTensor<B> {
858 dims.iter()
859 .fold(tensor, |tensor, &dim| B::float_sum_dim(tensor, dim))
860 }
861
862 /// Product of all elements in a tensor.
863 ///
864 /// # Arguments
865 ///
866 /// * `tensor` - The tensor to product.
867 ///
868 /// # Returns
869 ///
870 /// A scalar tensor with the product of all elements in `tensor`.
871 fn float_prod(tensor: FloatTensor<B>) -> FloatTensor<B> {
872 // Product of all elements in a tensor
873 B::float_exp(B::float_sum(B::float_log(tensor)))
874 }
875
876 /// Product of all elements in a tensor along a dimension.
877 ///
878 /// # Arguments
879 ///
880 /// * `tensor` - The tensor to product.
881 ///
882 /// # Returns
883 ///
884 /// A tensor with the product of all elements in `tensor` along `dim`.
885 fn float_prod_dim(tensor: FloatTensor<B>, dim: usize) -> FloatTensor<B> {
886 // Product of all elements in a tensor along a dimension
887 B::float_exp(B::float_sum_dim(B::float_log(tensor), dim))
888 }
889
890 /// Mean of all elements in a tensor.
891 ///
892 /// # Arguments
893 ///
894 /// * `tensor` - The tensor to mean.
895 ///
896 /// # Returns
897 ///
898 /// A scalar tensor with the mean of all elements in `tensor`.
899 fn float_mean(tensor: FloatTensor<B>) -> FloatTensor<B> {
900 let num_elems = tensor.shape().num_elements() as f32;
901 B::float_div_scalar(B::float_sum(tensor), num_elems.into())
902 }
903
904 /// Mean of all elements in a tensor along a dimension.
905 ///
906 /// # Arguments
907 ///
908 /// * `tensor` - The tensor to mean.
909 /// * `dim` - The dimension along which to mean.
910 ///
911 /// # Returns
912 ///
913 /// A tensor with the mean of all elements in `tensor` along `dim`.
914 fn float_mean_dim(tensor: FloatTensor<B>, dim: usize) -> FloatTensor<B>;
915
916 /// Computes the cumulative sum of elements along a dimension.
917 ///
918 /// # Arguments
919 ///
920 /// * `tensor` - The tensor to compute the cumulative sum of.
921 /// * `dim` - The dimension along which to compute the cumulative sum.
922 ///
923 /// # Returns
924 ///
925 /// A tensor with the same shape where each element is the cumulative sum
926 /// of all elements up to and including that position along the dimension.
927 fn float_cumsum(tensor: FloatTensor<B>, dim: usize) -> FloatTensor<B>;
928
929 /// Computes the cumulative product of elements along a dimension.
930 ///
931 /// # Arguments
932 ///
933 /// * `tensor` - The tensor to compute the cumulative product of.
934 /// * `dim` - The dimension along which to compute the cumulative product.
935 ///
936 /// # Returns
937 ///
938 /// A tensor with the same shape where each element is the cumulative product
939 /// of all elements up to and including that position along the dimension.
940 fn float_cumprod(tensor: FloatTensor<B>, dim: usize) -> FloatTensor<B>;
941
942 /// Computes the cumulative minimum of elements along a dimension.
943 ///
944 /// # Arguments
945 ///
946 /// * `tensor` - The tensor to compute the cumulative minimum of.
947 /// * `dim` - The dimension along which to compute the cumulative minimum.
948 ///
949 /// # Returns
950 ///
951 /// A tensor with the same shape where each element is the minimum
952 /// of all elements up to and including that position along the dimension.
953 fn float_cummin(tensor: FloatTensor<B>, dim: usize) -> FloatTensor<B>;
954
955 /// Computes the cumulative maximum of elements along a dimension.
956 ///
957 /// # Arguments
958 ///
959 /// * `tensor` - The tensor to compute the cumulative maximum of.
960 /// * `dim` - The dimension along which to compute the cumulative maximum.
961 ///
962 /// # Returns
963 ///
964 /// A tensor with the same shape where each element is the maximum
965 /// of all elements up to and including that position along the dimension.
966 fn float_cummax(tensor: FloatTensor<B>, dim: usize) -> FloatTensor<B>;
967
968 /// Converts a tensor to another floating point data type.
969 ///
970 /// # Arguments
971 ///
972 /// * `tensor` - The tensor to convert.
973 /// * `dtype` - The target data type.
974 ///
975 /// # Returns
976 ///
977 /// A tensor with the same values as `tensor` but in the target floating point data type.
978 fn float_cast(tensor: FloatTensor<B>, dtype: FloatDType) -> FloatTensor<B>;
979
980 /// Returns a new tensor with exponential values.
981 ///
982 /// # Arguments
983 ///
984 /// * `tensor` - The tensor to exponentiate.
985 ///
986 /// # Returns
987 ///
988 /// A tensor with the same shape as `tensor` with exponential values.
989 fn float_exp(tensor: FloatTensor<B>) -> FloatTensor<B>;
990
991 /// Returns a new tensor with natural logarithm values.
992 ///
993 /// # Arguments
994 ///
995 /// * `tensor` - The tensor to take the logarithm of.
996 ///
997 /// # Returns
998 ///
999 /// A tensor with the same shape as `tensor` with natural logarithm values.
1000 fn float_log(tensor: FloatTensor<B>) -> FloatTensor<B>;
1001
1002 /// Returns a new tensor with logarithm values of (1 + Xi).
1003 ///
1004 /// # Arguments
1005 ///
1006 /// * `tensor` - The tensor to take the logarithm of.
1007 ///
1008 /// # Returns
1009 ///
1010 /// A tensor with the same shape as `tensor` with logarithm values of (1 + Xi).
1011 fn float_log1p(tensor: FloatTensor<B>) -> FloatTensor<B>;
1012
1013 /// Element-wise power with a FloatTensor.
1014 ///
1015 /// # Arguments
1016 ///
1017 /// * `lhs` - The left-hand side tensor.
1018 /// * `rhs` - The right-hand side tensor.
1019 ///
1020 /// # Returns
1021 ///
1022 /// The elements of `lhs` raised to the power of the elements of `rhs`.
1023 fn float_powf(lhs: FloatTensor<B>, rhs: FloatTensor<B>) -> FloatTensor<B>;
1024
1025 /// Element-wise power with an IntTensor.
1026 ///
1027 /// # Arguments
1028 ///
1029 /// * `lhs` - The left-hand side tensor.
1030 /// * `rhs` - The right-hand side floatTensor.
1031 ///
1032 /// # Returns
1033 ///
1034 /// The elements of `lhs` raised to the value of `rhs`. Result is an IntTensor.
1035 fn float_powi(lhs: FloatTensor<B>, rhs: IntTensor<B>) -> FloatTensor<B> {
1036 let dtype = lhs.dtype();
1037 Self::float_powf(lhs, B::int_into_float(rhs, dtype.into()))
1038 }
1039
1040 /// Raises a tensor to the power of an int scalar.
1041 ///
1042 /// # Backend Implementors Note
1043 ///
1044 /// A number of common exponent cases can be implemented with operations
1045 /// which are much cheaper than generic exponentiation.
1046 ///
1047 /// This (`Backend` impl overridable) operation handles generic optimizations
1048 /// for several common integer exponent cases; and then dispatches to
1049 /// the (`Backend` impl overridable) [`Self::float_powi_scalar_impl`]
1050 /// operation to handle the generic case.
1051 ///
1052 /// # Arguments
1053 ///
1054 /// * `lhs` - The left-hand side tensor.
1055 /// * `rhs` - The right-hand side scalar.
1056 ///
1057 /// # Returns
1058 ///
1059 /// The elements of `lhs` raised to the value of `rhs`.
1060 fn float_powi_scalar(lhs: FloatTensor<B>, rhs: Scalar) -> FloatTensor<B> {
1061 match rhs.elem::<i64>() {
1062 0 => Self::float_ones(lhs.shape(), &lhs.device(), lhs.dtype().into()),
1063 1 => lhs,
1064 2 => B::float_mul(lhs.clone(), lhs),
1065 -1 => Self::float_recip(lhs),
1066 -2 => Self::float_recip(B::float_mul(lhs.clone(), lhs)),
1067 _ => Self::float_powi_scalar_impl(lhs, rhs),
1068 }
1069 }
1070
1071 /// Raises a tensor to the power of an int scalar.
1072 ///
1073 /// # Backend Implementors Note
1074 ///
1075 /// This is the generic implementation of integer exponentiation
1076 /// called by [`Self::float_powi_scalar`] in the fallback case.
1077 ///
1078 /// As a general rule, this should not be called directly.
1079 ///
1080 /// # Arguments
1081 ///
1082 /// * `lhs` - The left-hand side tensor.
1083 /// * `rhs` - The right-hand side scalar.
1084 ///
1085 /// # Returns
1086 ///
1087 /// The elements of `lhs` raised to the value of `rhs`.
1088 fn float_powi_scalar_impl(lhs: FloatTensor<B>, rhs: Scalar) -> FloatTensor<B> {
1089 // Avoid a recursive loop by deferring directly to float_powf_scalar_impl.
1090 Self::float_powf_scalar_impl(lhs, rhs)
1091 }
1092
1093 /// Returns a new tensor with values raised to the power of float `value`.
1094 ///
1095 /// # Backend Implementors Note
1096 ///
1097 /// This (`Backend` impl overridable) operation dispatches integer exponentiation
1098 /// to [`Self::float_powi_scalar`], and the remaining non-integer exponent cases to
1099 /// the (`Backend` impl overridable) [`Self::float_powf_scalar_impl`]
1100 /// operation to handle the generic case.
1101 ///
1102 /// # Arguments
1103 ///
1104 /// * `tensor` - The tensor to exponentiate.
1105 /// * `value` - The exponent.
1106 ///
1107 /// # Returns
1108 ///
1109 /// A tensor with the same shape as `tensor` with values raised to the power of `value`.
1110 fn float_powf_scalar(tensor: FloatTensor<B>, value: Scalar) -> FloatTensor<B> {
1111 if let Some(exp) = value.try_as_integer() {
1112 Self::float_powi_scalar(tensor, exp)
1113 } else {
1114 Self::float_powf_scalar_impl(tensor, value)
1115 }
1116 }
1117
1118 /// Returns a new tensor with values raised to the power of float `value`.
1119 ///
1120 /// # Backend Implementors Note
1121 ///
1122 /// This is the generic implementation of integer exponentiation
1123 /// called by [`Self::float_powf_scalar`] in the fallback case.
1124 ///
1125 /// This is the minimal required support a `Backend` must implement
1126 /// for exponentiation.
1127 ///
1128 /// As a general rule, this should not be called directly.
1129 ///
1130 /// # Arguments
1131 ///
1132 /// * `tensor` - The tensor to exponentiate.
1133 /// * `value` - The exponent.
1134 ///
1135 /// # Returns
1136 ///
1137 /// A tensor with the same shape as `tensor` with values raised to the power of `value`.
1138 fn float_powf_scalar_impl(tensor: FloatTensor<B>, value: Scalar) -> FloatTensor<B>;
1139
1140 /// Returns a new tensor with square root values.
1141 ///
1142 /// # Arguments
1143 ///
1144 /// * `tensor` - The tensor to take the square root of.
1145 ///
1146 /// # Returns
1147 ///
1148 /// A tensor with the same shape as `tensor` with square root values.
1149 fn float_sqrt(tensor: FloatTensor<B>) -> FloatTensor<B>;
1150
1151 /// Returns a new tensor with the Euclidean distance values.
1152 ///
1153 /// # Arguments
1154 ///
1155 /// * `lhs` - The left-hand side tensor.
1156 /// * `rhs` - The right-hand side tensor.
1157 ///
1158 /// # Returns
1159 ///
1160 /// A tensor with the same shape as `lhs` and `rhs` with hypotenuse values.
1161 fn float_hypot(lhs: FloatTensor<B>, rhs: FloatTensor<B>) -> FloatTensor<B> {
1162 // default implementation for any backend that can't either iterator over elements or doesn't have
1163 // a native hypot implementation
1164
1165 // Mirrors glibc's approach: scale by max(|lhs|, |rhs|) to avoid
1166 // overflow/underflow in the intermediate squaring step.
1167 //
1168 // hypot(x, y) = |max| * sqrt(1 + (min/max)^2)
1169 //
1170 // Edge cases:
1171 // - If max == 0, both inputs are 0, result is 0 (division guarded by clamp)
1172 // - If max is inf, result is inf (propagates naturally through sqrt)
1173 // - NaN propagates naturally
1174 let abs_lhs = B::float_abs(lhs);
1175 let abs_rhs = B::float_abs(rhs);
1176
1177 let diff = B::float_clamp_min(B::float_sub(abs_rhs.clone(), abs_lhs.clone()), 0.0.into());
1178 let max = B::float_add(abs_lhs.clone(), diff.clone());
1179 let min = B::float_sub(abs_rhs, diff);
1180
1181 // Clamp max to at least epsilon to avoid 0/0; result will be 0 anyway
1182 // since min <= max, so (min/clamped_max)^2 won't blow up meaningfully.
1183 let max_safe = B::float_clamp_min(
1184 max.clone(),
1185 max.dtype().finfo().unwrap().min_positive.into(),
1186 );
1187
1188 let ratio = B::float_div(min, max_safe);
1189 let ratio_sq = B::float_mul(ratio.clone(), ratio);
1190
1191 let inner = B::float_add_scalar(ratio_sq, 1.0.into());
1192
1193 B::float_mul(max, B::float_sqrt(inner))
1194 }
1195
1196 /// Returns a new tensor with absolute values.
1197 ///
1198 /// # Arguments
1199 ///
1200 /// * `tensor` - The tensor to take absolute value of.
1201 ///
1202 /// # Returns
1203 ///
1204 /// A tensor with the same shape as `tensor` with absolute values.
1205 fn float_abs(tensor: FloatTensor<B>) -> FloatTensor<B>;
1206
1207 /// Returns a new tensor with cosine values.
1208 ///
1209 /// # Arguments
1210 ///
1211 /// * `tensor` - The tensor to take the cosine of.
1212 ///
1213 /// # Returns
1214 ///
1215 /// A tensor with the same shape as `tensor` with cosine values.
1216 fn float_cos(tensor: FloatTensor<B>) -> FloatTensor<B>;
1217
1218 /// Returns a new tensor with sine values.
1219 ///
1220 /// # Arguments
1221 ///
1222 /// * `tensor` - The tensor to take the sine of.
1223 ///
1224 /// # Returns
1225 ///
1226 /// A tensor with the same shape as `tensor` with sine values.
1227 fn float_sin(tensor: FloatTensor<B>) -> FloatTensor<B>;
1228
1229 /// Returns a new tensor with tangent values.
1230 ///
1231 /// # Arguments
1232 ///
1233 /// * `tensor` - The tensor to take the tangent of.
1234 ///
1235 /// # Returns
1236 ///
1237 /// A tensor with the same shape as `tensor` with tangent values.
1238 fn float_tan(tensor: FloatTensor<B>) -> FloatTensor<B>;
1239
1240 /// Returns a new tensor with hyperbolic cosine values.
1241 ///
1242 /// # Arguments
1243 ///
1244 /// * `tensor` - The tensor to take the hyperbolic cosine of.
1245 ///
1246 /// # Returns
1247 ///
1248 /// A tensor with the same shape as `tensor` with hyperbolic cosine values.
1249 fn float_cosh(tensor: FloatTensor<B>) -> FloatTensor<B>;
1250
1251 /// Returns a new tensor with hyperbolic sine values.
1252 ///
1253 /// # Arguments
1254 ///
1255 /// * `tensor` - The tensor to take the hyperbolic sine of.
1256 ///
1257 /// # Returns
1258 ///
1259 /// A tensor with the same shape as `tensor` with hyperbolic sine values.
1260 fn float_sinh(tensor: FloatTensor<B>) -> FloatTensor<B>;
1261
1262 /// Returns a new tensor with hyperbolic tangent values.
1263 ///
1264 /// # Arguments
1265 ///
1266 /// * `tensor` - The tensor to take the hyperbolic tangent of.
1267 ///
1268 /// # Returns
1269 ///
1270 /// A tensor with the same shape as `tensor` with hyperbolic tangent values.
1271 fn float_tanh(tensor: FloatTensor<B>) -> FloatTensor<B>;
1272
1273 /// Returns a new tensor with inverse cosine values.
1274 ///
1275 /// # Arguments
1276 ///
1277 /// * `tensor` - The input tensor.
1278 ///
1279 /// # Returns
1280 ///
1281 /// A tensor with the same shape as `tensor` with inverse cosine values.
1282 fn float_acos(tensor: FloatTensor<B>) -> FloatTensor<B>;
1283
1284 /// Returns a new tensor with inverse hyperbolic cosine values.
1285 ///
1286 /// # Arguments
1287 ///
1288 /// * `tensor` - The input tensor.
1289 ///
1290 /// # Returns
1291 ///
1292 /// A tensor with the same shape as `tensor` with inverse hyperbolic cosine values.
1293 fn float_acosh(tensor: FloatTensor<B>) -> FloatTensor<B>;
1294
1295 /// Returns a new tensor with inverse sine values.
1296 ///
1297 /// # Arguments
1298 ///
1299 /// * `tensor` - The input tensor.
1300 ///
1301 /// # Returns
1302 ///
1303 /// A tensor with the same shape as `tensor` with inverse sine values.
1304 fn float_asin(tensor: FloatTensor<B>) -> FloatTensor<B>;
1305
1306 /// Returns a new tensor with inverse hyperbolic sine values.
1307 ///
1308 /// # Arguments
1309 ///
1310 /// * `tensor` - The input tensor.
1311 ///
1312 /// # Returns
1313 ///
1314 /// A tensor with the same shape as `tensor` with inverse hyperbolic sine values.
1315 fn float_asinh(tensor: FloatTensor<B>) -> FloatTensor<B>;
1316
1317 /// Returns a new tensor with the inverse tangent values.
1318 ///
1319 /// # Arguments
1320 ///
1321 /// * `tensor` - The input tensor.
1322 ///
1323 /// # Returns
1324 ///
1325 /// A tensor with the same shape as `tensor` with the inverse tangent values.
1326 fn float_atan(tensor: FloatTensor<B>) -> FloatTensor<B>;
1327
1328 /// Returns a new tensor with the inverse hyperbolic tangent values.
1329 ///
1330 /// # Arguments
1331 ///
1332 /// * `tensor` - The input tensor.
1333 ///
1334 /// # Returns
1335 ///
1336 /// A tensor with the same shape as `tensor` with the inverse hyperbolic tangent values.
1337 fn float_atanh(tensor: FloatTensor<B>) -> FloatTensor<B>;
1338
1339 /// Returns a tensor with the four-quadrant inverse tangent values of `y` and `x`.
1340 ///
1341 /// # Arguments
1342 ///
1343 /// * `lhs` - The tensor with y coordinates.
1344 /// * `rhs` - The tensor with x coordinates.
1345 ///
1346 /// # Returns
1347 ///
1348 /// A tensor with the four-quadrant inverse tangent values.
1349 fn float_atan2(lhs: FloatTensor<B>, rhs: FloatTensor<B>) -> FloatTensor<B>;
1350
1351 /// Returns a new tensor with rounded values.
1352 ///
1353 /// This function should implement the [round half to even](https://en.wikipedia.org/wiki/Rounding#Rounding_half_to_even)
1354 /// strategy, with halfway cases rounded to the nearest even integer value.
1355 ///
1356 /// # Arguments
1357 ///
1358 /// * `tensor` - The tensor to be rounded.
1359 ///
1360 /// # Returns
1361 ///
1362 /// A tensor with the same shape as `tensor` with rounded values.
1363 fn float_round(tensor: FloatTensor<B>) -> FloatTensor<B>;
1364
1365 /// Returns a new tensor with floored values.
1366 ///
1367 /// # Arguments
1368 ///
1369 /// * `tensor` - The tensor to be floored.
1370 ///
1371 /// # Returns
1372 ///
1373 /// A tensor with the same shape as `tensor` with floored values.
1374 fn float_floor(tensor: FloatTensor<B>) -> FloatTensor<B>;
1375
1376 /// Returns a new tensor with ceiled values.
1377 ///
1378 /// # Arguments
1379 ///
1380 /// * `tensor` - The tensor to be ceiled.
1381 ///
1382 /// # Returns
1383 ///
1384 /// A tensor with the same shape as `tensor` with ceiled values.
1385 fn float_ceil(tensor: FloatTensor<B>) -> FloatTensor<B>;
1386
1387 /// Returns a new tensor with truncated values.
1388 ///
1389 /// # Arguments
1390 ///
1391 /// * `tensor` - The tensor to be truncated.
1392 ///
1393 /// # Returns
1394 ///
1395 /// A tensor with the same shape as `tensor` with truncated values.
1396 fn float_trunc(tensor: FloatTensor<B>) -> FloatTensor<B>;
1397
1398 /// Returns a new tensor with the error function values.
1399 ///
1400 /// # Arguments
1401 ///
1402 /// * `tensor` - The tensor to take the error function of.
1403 ///
1404 /// # Returns
1405 ///
1406 /// A tensor with the same shape as `tensor` with error function values.
1407 fn float_erf(tensor: FloatTensor<B>) -> FloatTensor<B>;
1408
1409 /// Concatenates tensors along a dimension.
1410 ///
1411 /// # Arguments
1412 ///
1413 /// * `tensors` - The tensors to concatenate.
1414 /// * `dim` - The dimension along which to concatenate.
1415 ///
1416 /// # Returns
1417 ///
1418 /// A tensor with the concatenated tensors along `dim`.
1419 ///
1420 /// # Note
1421 ///
1422 /// Empty tensors (where the concatenation dimension has size 0) are filtered out at the
1423 /// high-level tensor API and will not be passed to this method. Backend implementations do
1424 /// not need to handle empty tensors.
1425 fn float_cat(tensors: Vec<FloatTensor<B>>, dim: usize) -> FloatTensor<B> {
1426 let first_tensor = tensors.first().expect("Tensors should not be empty");
1427 let device = first_tensor.device();
1428
1429 cat_with_slice_assign::<B, _, _, _>(
1430 tensors,
1431 dim,
1432 device,
1433 |shape, device, dtype| B::float_empty(shape, device, dtype.into()),
1434 B::float_slice_assign,
1435 )
1436 }
1437
1438 /// Gets the indices of the maximum elements of a tensor along an axis.
1439 ///
1440 /// # Arguments
1441 ///
1442 /// * `tensor` - The tensor to get the maximum elements of.
1443 /// * `dim` - The dimension along which to get the maximum elements.
1444 /// * `out_dtype` - The output tensor dtype.
1445 ///
1446 /// # Returns
1447 ///
1448 /// A tensor with the indices of the maximum elements of `tensor` along `dim`.
1449 fn float_argmax(tensor: FloatTensor<B>, dim: usize, out_dtype: IntDType) -> IntTensor<B>;
1450
1451 /// Gets the indices of the k maximum elements of a tensor along an axis.
1452 /// if two elements are equals, it will be ordered by lowest indices
1453 ///
1454 /// # Arguments
1455 ///
1456 /// * `tensor` - The tensor to get the maximum elements of.
1457 /// * `dim` - The dimension along which to get the maximum elements.
1458 /// * `k` - number of maximum elements
1459 /// * `out_dtype` - The output tensor dtype.
1460 ///
1461 /// # Returns
1462 ///
1463 /// A tensor with the indices of the maximum elements of `tensor` along `dim`.
1464 fn float_argtopk(
1465 tensor: FloatTensor<B>,
1466 dim: usize,
1467 k: usize,
1468 out_dtype: IntDType,
1469 ) -> IntTensor<B> {
1470 let device = tensor.device();
1471 let dtype = get_device_settings::<B>(&device).int_dtype;
1472 let k_indices = B::int_arange(0..k as i64, &device, dtype);
1473 B::int_select(
1474 Self::float_argsort(tensor, dim, true, out_dtype),
1475 dim,
1476 k_indices,
1477 )
1478 }
1479
1480 /// Gets the values of the k maximum elements of a tensor along an axis.
1481 ///
1482 /// # Arguments
1483 ///
1484 /// * `tensor` - The tensor to get the maximum elements of.
1485 /// * `dim` - The dimension along which to get the maximum elements.
1486 /// * `k` - number of maximum elements
1487 /// * `out_dtype` - The output tensor dtype.
1488 ///
1489 /// # Returns
1490 ///
1491 /// A tensor with the values of the maximum elements of `tensor` along `dim`.
1492 fn float_topk(tensor: FloatTensor<B>, dim: usize, k: usize) -> FloatTensor<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 Self::float_select(Self::float_sort(tensor, dim, true), dim, k_indices)
1497 }
1498
1499 /// Gets the values of the k maximum elements of a tensor along an axis, and their indices.
1500 ///
1501 /// # Arguments
1502 ///
1503 /// * `tensor` - The tensor to get the maximum elements of.
1504 /// * `dim` - The dimension along which to get the maximum elements.
1505 /// * `k` - number of maximum elements
1506 /// * `out_dtype` - The indices tensor dtype.
1507 ///
1508 /// # Returns
1509 ///
1510 /// A tuple with the values of the k maximum elements of `tensor` along `dim`, and their
1511 /// indices.
1512 ///
1513 /// The default sorts once and keeps the first `k` of each half. It deliberately does not
1514 /// compose `float_topk` with `float_argtopk`: those default to a sort each, so that would
1515 /// sort twice, and it would also require `float_argtopk` from backends that only have
1516 /// sorting. Backends whose top-k already carries both results should override this and
1517 /// produce them in a single pass.
1518 fn float_topk_with_indices(
1519 tensor: FloatTensor<B>,
1520 dim: usize,
1521 k: usize,
1522 out_dtype: IntDType,
1523 ) -> (FloatTensor<B>, IntTensor<B>) {
1524 let device = tensor.device();
1525 let dtype = get_device_settings::<B>(&device).int_dtype;
1526 let k_indices = B::int_arange(0..k as i64, &device, dtype);
1527 let (values, indices) = Self::float_sort_with_indices(tensor, dim, true, out_dtype);
1528
1529 (
1530 Self::float_select(values, dim, k_indices.clone()),
1531 B::int_select(indices, dim, k_indices),
1532 )
1533 }
1534
1535 /// Gets the indices of the minimum elements of a tensor along an axis.
1536 ///
1537 /// # Arguments
1538 ///
1539 /// * `tensor` - The tensor to get the minimum elements of.
1540 /// * `dim` - The dimension along which to get the minimum elements.
1541 /// * `out_dtype` - The output tensor dtype.
1542 ///
1543 /// # Returns
1544 ///
1545 /// A tensor with the indices of the minimum elements of `tensor` along `dim`.
1546 fn float_argmin(tensor: FloatTensor<B>, dim: usize, out_dtype: IntDType) -> IntTensor<B>;
1547
1548 /// Gets the maximum element of a tensor.
1549 ///
1550 /// # Arguments
1551 ///
1552 /// * `tensor` - The tensor to get the maximum elements of.
1553 ///
1554 /// # Returns
1555 ///
1556 /// A tensor with the maximum element of `tensor`.
1557 fn float_max(tensor: FloatTensor<B>) -> FloatTensor<B> {
1558 let shape = tensor.shape();
1559 let tensor = B::float_reshape(tensor, Shape::new([shape.num_elements()]));
1560
1561 B::float_max_dim(tensor, 0)
1562 }
1563
1564 /// Gets the maximum elements of a tensor along an axis.
1565 ///
1566 /// # Arguments
1567 ///
1568 /// * `tensor` - The tensor to get the maximum elements of.
1569 /// * `dim` - The dimension along which to get the maximum elements.
1570 ///
1571 /// # Returns
1572 ///
1573 /// A tensor with the maximum elements of `tensor` along `dim`.
1574 fn float_max_dim(tensor: FloatTensor<B>, dim: usize) -> FloatTensor<B> {
1575 let dtype = get_device_settings::<B>(&tensor.device()).int_dtype;
1576 let index = B::float_argmax(tensor.clone(), dim, dtype);
1577
1578 B::float_gather(dim, tensor, index)
1579 }
1580
1581 /// Gets the maximum elements of a tensor along an axis and their indices.
1582 ///
1583 /// # Arguments
1584 ///
1585 /// * `tensor` - The tensor to get the maximum elements of.
1586 /// * `dim` - The dimension along which to get the maximum elements.
1587 /// * `indices_dtype` - The indices tensor dtype.
1588 ///
1589 /// # Returns
1590 ///
1591 /// A tuple with the maximum elements of `tensor` along `dim` and their indices.
1592 fn float_max_dim_with_indices(
1593 tensor: FloatTensor<B>,
1594 dim: usize,
1595 indices_dtype: IntDType,
1596 ) -> (FloatTensor<B>, IntTensor<B>) {
1597 let index = B::float_argmax(tensor.clone(), dim, indices_dtype);
1598 let values = B::float_gather(dim, tensor, index.clone());
1599
1600 (values, index)
1601 }
1602
1603 /// Gets the minimum element of a tensor.
1604 ///
1605 /// # Arguments
1606 ///
1607 /// * `tensor` - The tensor to get the minimum elements of.
1608 ///
1609 /// # Returns
1610 ///
1611 /// A tensor with the minimum element of `tensor`.
1612 fn float_min(tensor: FloatTensor<B>) -> FloatTensor<B> {
1613 let shape = tensor.shape();
1614 let tensor = B::float_reshape(tensor, Shape::new([shape.num_elements()]));
1615
1616 B::float_min_dim(tensor, 0)
1617 }
1618
1619 /// Gets the minimum elements of a tensor along an axis.
1620 ///
1621 /// # Arguments
1622 ///
1623 /// * `tensor` - The tensor to get the minimum elements of.
1624 /// * `dim` - The dimension along which to get the minimum elements.
1625 ///
1626 /// # Returns
1627 ///
1628 /// A tensor with the minimum elements of `tensor` along `dim`.
1629 fn float_min_dim(tensor: FloatTensor<B>, dim: usize) -> FloatTensor<B> {
1630 let dtype = get_device_settings::<B>(&tensor.device()).int_dtype;
1631 let index = B::float_argmin(tensor.clone(), dim, dtype);
1632
1633 B::float_gather(dim, tensor, index)
1634 }
1635
1636 /// Gets the minimum elements of a tensor along an axis and their indices.
1637 ///
1638 /// # Arguments
1639 ///
1640 /// * `tensor` - The tensor to get the minimum elements of.
1641 /// * `dim` - The dimension along which to get the minimum elements.
1642 /// * `indices_dtype` - The indices tensor dtype.
1643 ///
1644 /// # Returns
1645 ///
1646 /// A tuple with the minimum elements of `tensor` along `dim` and their indices.
1647 fn float_min_dim_with_indices(
1648 tensor: FloatTensor<B>,
1649 dim: usize,
1650 indices_dtype: IntDType,
1651 ) -> (FloatTensor<B>, IntTensor<B>) {
1652 let index = B::float_argmin(tensor.clone(), dim, indices_dtype);
1653 let values = B::float_gather(dim, tensor, index.clone());
1654
1655 (values, index)
1656 }
1657
1658 /// Gets the maximum absolute element of a tensor.
1659 ///
1660 /// # Arguments
1661 ///
1662 /// * `tensor` - The tensor to get the maximum elements of.
1663 ///
1664 /// # Returns
1665 ///
1666 /// A tensor with the maximum element of `tensor`.
1667 fn float_max_abs(tensor: FloatTensor<B>) -> FloatTensor<B> {
1668 let shape = tensor.shape();
1669 let tensor = B::float_reshape(tensor, Shape::new([shape.num_elements()]));
1670
1671 B::float_max_abs_dim(tensor, 0)
1672 }
1673
1674 /// Gets the maximum absolute elements of a tensor along an axis.
1675 ///
1676 /// # Arguments
1677 ///
1678 /// * `tensor` - The tensor to get the maximum elements of.
1679 /// * `dim` - The dimension along which to get the maximum elements.
1680 ///
1681 /// # Returns
1682 ///
1683 /// A tensor with the maximum elements of `tensor` along `dim`.
1684 fn float_max_abs_dim(tensor: FloatTensor<B>, dim: usize) -> FloatTensor<B> {
1685 B::float_max_dim(B::float_abs(tensor), dim)
1686 }
1687
1688 /// Tests if any element in the float `tensor` evaluates to True.
1689 ///
1690 /// # Arguments
1691 ///
1692 /// * `tensor` - The tensor to test.
1693 /// * `out_dtype` - The output tensor dtype.
1694 ///
1695 /// # Returns
1696 ///
1697 /// A boolean tensor with a single element, True if any element in the tensor is True, False otherwise.
1698 fn float_any(tensor: FloatTensor<B>, out_dtype: BoolDType) -> BoolTensor<B> {
1699 let float_dtype = tensor.dtype();
1700 let bool_tensor = B::float_equal_elem(tensor, 0f32.into(), out_dtype);
1701 let bool_tensor = B::bool_not(bool_tensor);
1702 let sum = B::float_sum(B::bool_into_float(bool_tensor, float_dtype.into()));
1703 B::float_greater_elem(sum, 0f32.into(), out_dtype)
1704 }
1705
1706 /// Tests if any element in the float `tensor` evaluates to True along a given dimension `dim`.
1707 ///
1708 /// # Arguments
1709 ///
1710 /// * `tensor` - The tensor to test.
1711 /// * `dim` - The axis along which to test.
1712 /// * `out_dtype` - The output tensor dtype.
1713 ///
1714 /// # Returns
1715 ///
1716 /// A boolean tensor `Tensor<B, D, Bool>` with the same size as input `tensor`, except in the `dim` axis
1717 /// where the size is 1. The elem in the `dim` axis is True if any element along this dim in the
1718 /// input evaluates to True, False otherwise.
1719 fn float_any_dim(tensor: FloatTensor<B>, dim: usize, out_dtype: BoolDType) -> BoolTensor<B> {
1720 let float_dtype = tensor.dtype();
1721 let bool_tensor = B::float_equal_elem(tensor, 0f32.into(), out_dtype);
1722 let bool_tensor = B::bool_not(bool_tensor);
1723 let sum = B::float_sum_dim(B::bool_into_float(bool_tensor, float_dtype.into()), dim);
1724 B::float_greater_elem(sum, 0f32.into(), out_dtype)
1725 }
1726
1727 /// Tests if all elements in the float `tensor` evaluate to True.
1728 ///
1729 /// # Arguments
1730 ///
1731 /// * `tensor` - The tensor to test.
1732 /// * `out_dtype` - The output tensor dtype.
1733 ///
1734 /// # Returns
1735 ///
1736 /// A boolean tensor `Tensor<B, 1, Bool>` with a single element, True if all elements in the input tensor
1737 /// evaluate to True, False otherwise.
1738 fn float_all(tensor: FloatTensor<B>, out_dtype: BoolDType) -> BoolTensor<B> {
1739 let float_dtype = tensor.dtype();
1740 let num_elems = tensor.shape().num_elements() as f32;
1741 let bool_tensor = B::float_equal_elem(tensor, 0f32.into(), out_dtype);
1742 let bool_tensor = B::bool_not(bool_tensor);
1743 let sum = B::float_sum(B::bool_into_float(bool_tensor, float_dtype.into()));
1744 B::float_equal_elem(sum, num_elems.into(), out_dtype)
1745 }
1746
1747 /// Tests if all elements in the float `tensor` evaluate to True along a given dimension `dim`.
1748 ///
1749 /// # Arguments
1750 ///
1751 /// * `tensor` - The tensor to test.
1752 /// * `dim` - The axis along which to test.
1753 /// * `out_dtype` - The output tensor dtype.
1754 ///
1755 /// # Returns
1756 ///
1757 /// A boolean tensor `Tensor<B, D, Bool>` with the same size as input `tensor`, except in the `dim` axis
1758 /// where the size is 1. The elem in the `dim` axis is True if all elements along this dim in the input
1759 /// evaluates to True, False otherwise.
1760 fn float_all_dim(tensor: FloatTensor<B>, dim: usize, out_dtype: BoolDType) -> BoolTensor<B> {
1761 let float_dtype = tensor.dtype();
1762 let num_elems = tensor.shape()[dim] as f32;
1763 let bool_tensor = B::float_equal_elem(tensor, 0f32.into(), out_dtype);
1764 let bool_tensor = B::bool_not(bool_tensor);
1765 let sum = B::float_sum_dim(B::bool_into_float(bool_tensor, float_dtype.into()), dim);
1766 B::float_equal_elem(sum, num_elems.into(), out_dtype)
1767 }
1768
1769 /// Returns the signs of the float `tensor`.
1770 ///
1771 /// # Arguments
1772 ///
1773 /// * `tensor` - The tensor to extract the signs from.
1774 ///
1775 /// # Returns
1776 ///
1777 /// A tensor with the same shape as `tensor` containing the signs of the elements of `tensor`:
1778 /// `1` where positive, `-1` where negative, and `0` where zero (either sign) or NaN.
1779 ///
1780 /// `sign(NaN) == 0` is part of this contract and every backend override must uphold it too.
1781 fn float_sign(tensor: FloatTensor<B>) -> FloatTensor<B> {
1782 let device = tensor.device();
1783 let bool_dtype = get_device_settings::<B>(&tensor.device()).bool_dtype;
1784 let zeros = B::float_zeros(tensor.shape(), &device, tensor.dtype().into());
1785 let less_than_zero = B::float_lower_elem(tensor.clone(), 0f32.into(), bool_dtype);
1786 let greater_than_zero = B::float_greater_elem(tensor, 0f32.into(), bool_dtype);
1787
1788 let mut result = B::float_mask_fill(zeros, less_than_zero, (-1f32).into());
1789 result = B::float_mask_fill(result, greater_than_zero, 1f32.into());
1790 result
1791 }
1792
1793 /// Broadcasts the float `tensor` to the given `shape`.
1794 fn float_expand(tensor: FloatTensor<B>, shape: Shape) -> FloatTensor<B>;
1795
1796 /// Sort the elements of the input `tensor` by value in along a given dimension.
1797 ///
1798 /// This sort is unstable (i.e., may reorder equal elements).
1799 ///
1800 /// # Arguments
1801 ///
1802 /// * `tensor` - The input tensor.
1803 /// * `dim` - The axis along which to sort.
1804 /// * `descending` - The sorting order.
1805 ///
1806 /// # Returns
1807 ///
1808 /// A tensor with the same shape as the input tensor, where the elements are sorted by value.
1809 fn float_sort(tensor: FloatTensor<B>, dim: usize, descending: bool) -> FloatTensor<B> {
1810 let device = tensor.device();
1811 sort::<B, _, _, _>(
1812 tensor,
1813 dim,
1814 descending,
1815 device,
1816 |tensor| {
1817 let msg = "Failed to synchronously read tensor data. This operation is not supported until this backend has a GPU sorting implementation.";
1818 try_read_sync(B::float_into_data(tensor))
1819 .expect(msg)
1820 .expect(msg)
1821 },
1822 |data, device, _dtype| B::float_from_data(data, device),
1823 )
1824 }
1825
1826 /// Sort the elements of the input `tensor` by value in along a given dimension.
1827 ///
1828 /// This sort is unstable (i.e., may reorder equal elements).
1829 ///
1830 /// # Arguments
1831 ///
1832 /// * `tensor` - The input tensor.
1833 /// * `dim` - The axis along which to sort.
1834 /// * `descending` - The sorting order.
1835 /// * `indices_dtype` - The indices tensor dtype.
1836 ///
1837 /// # Returns
1838 ///
1839 /// A tensor with the same shape as the input tensor and corresponding indices, where
1840 /// the elements are sorted by value and the indices map back to the original input tensor.
1841 fn float_sort_with_indices(
1842 tensor: FloatTensor<B>,
1843 dim: usize,
1844 descending: bool,
1845 indices_dtype: IntDType,
1846 ) -> (FloatTensor<B>, IntTensor<B>) {
1847 let device = tensor.device();
1848 sort_with_indices::<B, _, _, _>(
1849 tensor,
1850 dim,
1851 descending,
1852 indices_dtype,
1853 device,
1854 |tensor| {
1855 let msg = "Failed to synchronously read tensor data. This operation is not supported until this backend has a GPU sorting implementation.";
1856 try_read_sync(B::float_into_data(tensor))
1857 .expect(msg)
1858 .expect(msg)
1859 },
1860 |data, device, _dtype| B::float_from_data(data, device),
1861 )
1862 }
1863
1864 /// Returns the indices that sort the elements of the input `tensor` by value along a given dimension.
1865 ///
1866 /// This sort is unstable (i.e., may reorder equal elements).
1867 ///
1868 /// # Arguments
1869 ///
1870 /// * `tensor` - The input tensor.
1871 /// * `dim` - The axis along which to sort.
1872 /// * `descending` - The sorting order.
1873 /// * `out_dtype` - The output tensor dtype.
1874 ///
1875 /// # Returns
1876 ///
1877 /// A tensor with the same shape as the input tensor the indices map back to the original input tensor.
1878 fn float_argsort(
1879 tensor: FloatTensor<B>,
1880 dim: usize,
1881 descending: bool,
1882 out_dtype: IntDType,
1883 ) -> IntTensor<B> {
1884 let device = tensor.device();
1885 argsort::<B, _, _>(tensor, dim, descending, out_dtype, device, |tensor| {
1886 let msg = "Failed to synchronously read tensor data. This operation is not supported until this backend has a GPU sorting implementation.";
1887 try_read_sync(B::float_into_data(tensor))
1888 .expect(msg)
1889 .expect(msg)
1890 })
1891 }
1892
1893 /// Samples tensor as a two-dimensional spatial grid of (possibly multi-channel) values,
1894 /// using the given locations in [-1, 1].
1895 ///
1896 /// # Arguments
1897 ///
1898 /// * `tensor` - The tensor being sampled from, must be contiguous with shape (N, C, H_in, W_in)
1899 /// * `grid` - A tensor of locations, with shape (N, H_out, W_out, 2). Values are [-1, 1].
1900 /// A [x = -1, y = -1] means top-left, and [x = 1, y = 1] means bottom-right
1901 /// * `options` - Grid sampling options (mode, padding_mode, align_corners)
1902 ///
1903 /// # Returns
1904 ///
1905 /// A tensor with shape (N, C, H_out, W_out)
1906 fn float_grid_sample_2d(
1907 tensor: FloatTensor<B>,
1908 grid: FloatTensor<B>,
1909 options: GridSampleOptions,
1910 ) -> FloatTensor<B> {
1911 // TODO: default impl should get int default dtype
1912 float_grid_sample_2d_ref::<B>(tensor, grid, options)
1913 }
1914
1915 /// Unfold windows along a dimension.
1916 ///
1917 /// Returns a view of the tensor with all complete windows of size `size` in dimension `dim`;
1918 /// where windows are advanced by `step` at each index.
1919 ///
1920 /// The number of windows is `0` when `shape[dim] < size`, and otherwise
1921 /// `(shape[dim] - size) / step + 1`.
1922 ///
1923 /// # Arguments
1924 ///
1925 /// * `tensor` - The input tensor to unfold; of shape ``[pre=..., dim shape, post=...]``
1926 /// * `dim` - the selected dim.
1927 /// * `size` - the size of each unfolded window.
1928 /// * `step` - the step between each window.
1929 ///
1930 /// # Returns
1931 ///
1932 /// A tensor view with shape ``[pre=..., windows, size, post=...]``.
1933 fn float_unfold(tensor: FloatTensor<B>, dim: usize, size: usize, step: usize)
1934 -> FloatTensor<B>;
1935
1936 /// Returns a new tensor with boolean elements indicating whether each element of the input is NaN.
1937 ///
1938 /// # Returns
1939 ///
1940 /// A boolean tensor where `true` indicates NaN and `false` indicates a non-NaN value.
1941 fn float_is_nan(tensor: FloatTensor<B>, out_dtype: BoolDType) -> BoolTensor<B> {
1942 // Check if the input tensor is NaN by comparing it to itself
1943 // NaN is the only value that is not equal to itself
1944 B::float_not_equal(tensor.clone(), tensor, out_dtype)
1945 }
1946
1947 /// Returns a new tensor with boolean elements indicating whether each element of the input is infinite (either +INF or -INF).
1948 ///
1949 /// # Returns
1950 ///
1951 /// A boolean tensor where `true` indicates that the value is infinite
1952 fn float_is_inf(tensor: FloatTensor<B>, out_dtype: BoolDType) -> BoolTensor<B> {
1953 B::float_equal_elem(B::float_abs(tensor), f64::INFINITY.into(), out_dtype)
1954 }
1955
1956 /// Pads a tensor with one `(before, after)` pair per dimension.
1957 fn float_pad(
1958 tensor: FloatTensor<B>,
1959 padding: &[(usize, usize)],
1960 mode: PadMode,
1961 ) -> FloatTensor<B> {
1962 super::pad::float_pad::<B>(tensor, padding, mode)
1963 }
1964}