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