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