Skip to main content

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}