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