Skip to main content

burn_backend/backend/ops/
int_tensor.rs

1use super::cat::cat_with_slice_assign;
2use super::repeat_dim::repeat_with_slice_assign;
3use super::sort::{argsort, sort, sort_with_indices};
4use crate::tensor::{BoolTensor, Device, FloatTensor, IntTensor};
5use crate::{Backend, Distribution, TensorData, TensorMetadata};
6use crate::{ExecutionError, Scalar, get_device_settings};
7use alloc::vec::Vec;
8use burn_std::reader::try_read_sync;
9use burn_std::{BoolDType, FloatDType, IndexingUpdateOp, IntDType, Shape, Slice};
10use core::ops::Range;
11
12/// Int Tensor API for basic and numeric operations, see
13#[cfg_attr(doc, doc = crate::doc_tensor!())]
14#[cfg_attr(not(doc), doc = "`Tensor`")]
15/// for documentation on each function.
16pub trait IntTensorOps<B: Backend> {
17    /// Creates a new int tensor.
18    ///
19    /// # Arguments
20    ///
21    /// * `shape` - The shape of the tensor.
22    /// * `device` - The device to create the tensor on.
23    /// * `dtype` - The target data type.
24    ///
25    /// # Returns
26    ///
27    /// The integer tensor with the given shape.
28    fn int_empty(shape: Shape, device: &Device<B>, dtype: IntDType) -> IntTensor<B>;
29
30    /// Converts the tensor to a data structure.
31    ///
32    /// # Arguments
33    ///
34    /// * `tensor` - The tensor.
35    ///
36    /// # Returns
37    ///
38    /// The data structure with the tensor's data.
39    fn int_into_data(
40        tensor: IntTensor<B>,
41    ) -> impl Future<Output = Result<TensorData, ExecutionError>> + Send;
42
43    /// Creates a tensor from the data structure.
44    ///
45    /// # Arguments
46    ///
47    /// * `data` - The data structure.
48    /// * `device` - The device to create the tensor on.
49    ///
50    /// # Returns
51    ///
52    /// The tensor with the data.
53    fn int_from_data(data: TensorData, device: &Device<B>) -> IntTensor<B>;
54
55    /// Moves the tensor to the given device.
56    fn int_to_device(tensor: IntTensor<B>, device: &Device<B>) -> IntTensor<B>;
57
58    /// Reshapes the tensor.
59    ///
60    /// # Arguments
61    ///
62    /// * `tensor` - The tensor.
63    /// * `shape` - The new shape.
64    ///
65    /// # Returns
66    ///
67    /// The tensor with the new shape.
68    fn int_reshape(tensor: IntTensor<B>, shape: Shape) -> IntTensor<B>;
69
70    /// Gets the element at the given indices.
71    ///
72    /// # Arguments
73    ///
74    /// * `tensor` - The tensor.
75    /// * `slices` - The slices specifying ranges and steps for each dimension.
76    ///
77    /// # Returns
78    ///
79    /// The elements at the given indices.
80    ///
81    /// # Note
82    ///
83    /// Empty slices (where start >= end) are handled at the high-level tensor API and will not
84    /// be passed to this method. Backend implementations do not need to handle empty slices.
85    fn int_slice(tensor: IntTensor<B>, slices: &[Slice]) -> IntTensor<B>;
86
87    /// Sets the values in the tensor for the given ranges.
88    ///
89    /// # Arguments
90    ///
91    /// * `tensor` - The tensor.
92    /// * `ranges` - The ranges to set the values for.
93    ///
94    /// # Returns
95    ///
96    /// The tensor with the values set for the given ranges.
97    ///
98    /// # Note
99    ///
100    /// Empty slice assignments (where any slice range produces 0 elements) are handled at the
101    /// high-level tensor API and will not be passed to this method. Backend implementations do
102    /// not need to handle empty slice assignments.
103    fn int_slice_assign(
104        tensor: IntTensor<B>,
105        slices: &[Slice],
106        value: IntTensor<B>,
107    ) -> IntTensor<B>;
108
109    /// Converts int tensor to float tensor.
110    ///
111    /// # Arguments
112    ///
113    /// * `tensor` - The tensor.
114    /// * `out_dtype` - The output tensor dtype.
115    ///
116    /// # Returns
117    ///
118    /// The int tensor with the same data as the float tensor.
119    fn int_into_float(tensor: IntTensor<B>, out_dtype: FloatDType) -> FloatTensor<B>;
120
121    /// Fills the tensor with values from the value tensor if the mask is true at the given
122    /// indices.
123    ///
124    /// # Arguments
125    ///
126    /// * `tensor` - The tensor.
127    /// * `mask` - The mask.
128    /// * `value` - The value tensor.
129    ///
130    /// # Returns
131    ///
132    /// The tensor with the values filled.
133    fn int_mask_where(
134        tensor: IntTensor<B>,
135        mask: BoolTensor<B>,
136        value: IntTensor<B>,
137    ) -> IntTensor<B>;
138
139    /// Fills the tensor with the given value if the mask is true at the given indices.
140    ///
141    /// # Arguments
142    ///
143    /// * `tensor` - The tensor.
144    /// * `mask` - The mask.
145    /// * `value` - The value.
146    ///
147    /// # Returns
148    ///
149    /// The tensor with the values filled.
150    fn int_mask_fill(tensor: IntTensor<B>, mask: BoolTensor<B>, value: Scalar) -> IntTensor<B>;
151
152    /// Selects the elements of the tensor where the mask is true, returned as a 1D tensor.
153    ///
154    /// The elements are collected in row-major order. Because the number of selected elements
155    /// depends on the mask values, the output shape is data-dependent: computing it may require
156    /// synchronizing with the device, which is why this operation is asynchronous.
157    ///
158    /// # Arguments
159    ///
160    /// * `tensor` - The tensor to select from.
161    /// * `mask` - The boolean mask, with the same shape as the tensor.
162    ///
163    /// # Returns
164    ///
165    /// A 1D tensor containing the selected elements.
166    fn int_mask_select(
167        tensor: IntTensor<B>,
168        mask: BoolTensor<B>,
169    ) -> impl Future<Output = IntTensor<B>> + 'static + Send {
170        async move {
171            // Data-dependent output length, so we defer to `bool_argwhere` (the only pre-existing
172            // data-dependent op) to collect the flat indices of the true mask values, then select.
173            let n = mask.shape().num_elements();
174            let int_dtype = get_device_settings::<B>(&mask.device()).int_dtype;
175            let mask = B::bool_reshape(mask, Shape::new([n]));
176            let indices = B::bool_argwhere(mask, int_dtype).await; // [count, 1]
177            let count = indices.shape()[0];
178            let indices = B::int_reshape(indices, Shape::new([count])); // squeeze to [count]
179            let tensor = B::int_reshape(tensor, Shape::new([n]));
180            B::int_select(tensor, 0, indices)
181        }
182    }
183
184    /// Gather elements from the tensor at the given indices.
185    ///
186    /// # Arguments
187    ///
188    /// * `dim` - The dimension to gather from.
189    /// * `tensor` - The tensor.
190    /// * `indices` - The indices.
191    fn int_gather(dim: usize, tensor: IntTensor<B>, indices: IntTensor<B>) -> IntTensor<B>;
192
193    /// Scatter a given value to the tensor at the given indices using sum reduction.
194    ///
195    /// # Arguments
196    ///
197    /// * `dim` - The dimension to scatter to.
198    /// * `tensor` - The tensor.
199    /// * `indices` - The indices.
200    /// * `value` - The value.
201    ///
202    /// # Returns
203    ///
204    /// The tensor with the values scattered.
205    fn int_scatter_add(
206        dim: usize,
207        tensor: IntTensor<B>,
208        indices: IntTensor<B>,
209        value: IntTensor<B>,
210    ) -> IntTensor<B>;
211
212    /// Scatter elements into a tensor using the specified update operation.
213    ///
214    /// Backend implementations may override this to support operations beyond add.
215    fn int_scatter(
216        dim: usize,
217        tensor: IntTensor<B>,
218        indices: IntTensor<B>,
219        value: IntTensor<B>,
220        update: IndexingUpdateOp,
221    ) -> IntTensor<B> {
222        match update {
223            IndexingUpdateOp::Add => Self::int_scatter_add(dim, tensor, indices, value),
224            other => unimplemented!("int_scatter with {other:?} update is not implemented"),
225        }
226    }
227
228    /// Multi-dimensional scatter for int tensors.
229    fn int_scatter_nd(
230        _data: IntTensor<B>,
231        _indices: IntTensor<B>,
232        _values: IntTensor<B>,
233        _reduction: crate::tensor::IndexingUpdateOp,
234    ) -> IntTensor<B> {
235        unimplemented!("int_scatter_nd is not implemented for this backend")
236    }
237
238    /// Multi-dimensional gather for int tensors.
239    fn int_gather_nd(_data: IntTensor<B>, _indices: IntTensor<B>) -> IntTensor<B> {
240        unimplemented!("int_gather_nd is not implemented for this backend")
241    }
242
243    /// Select tensor elements along the given dimension corresponding to the given indices.
244    ///
245    /// # Arguments
246    ///
247    /// * `tensor` - The tensor.
248    /// * `dim` - The dimension to select from.
249    /// * `indices` - The indices.
250    ///
251    /// # Returns
252    ///
253    /// The tensor with the selected elements.
254    fn int_select(tensor: IntTensor<B>, dim: usize, indices: IntTensor<B>) -> IntTensor<B>;
255
256    /// Assign the selected elements along the given dimension corresponding to the given indices
257    /// to the given value using sum reduction.
258    ///
259    /// # Arguments
260    ///
261    /// * `tensor` - The tensor.
262    /// * `dim` - The dimension to select from.
263    /// * `indices` - The indices.
264    /// * `value` - The value.
265    ///
266    /// # Returns
267    ///
268    /// The tensor with the selected elements assigned to the given value.
269    fn int_select_add(
270        tensor: IntTensor<B>,
271        dim: usize,
272        indices: IntTensor<B>,
273        value: IntTensor<B>,
274    ) -> IntTensor<B>;
275
276    /// Assign selected elements along a dimension using the specified update operation.
277    ///
278    /// Backend implementations may override this to support operations beyond add.
279    fn int_select_assign(
280        tensor: IntTensor<B>,
281        dim: usize,
282        indices: IntTensor<B>,
283        value: IntTensor<B>,
284        update: IndexingUpdateOp,
285    ) -> IntTensor<B> {
286        match update {
287            IndexingUpdateOp::Add => Self::int_select_add(tensor, dim, indices, value),
288            other => unimplemented!("int_select_assign with {other:?} update is not implemented"),
289        }
290    }
291
292    /// Repeats the tensor along the given dimension the given number of times.
293    ///
294    /// # Arguments
295    ///
296    /// * `tensor` - The tensor.
297    /// * `dim` - The dimension to repeat.
298    /// * `times` - The number of times to repeat.
299    ///
300    /// # Returns
301    ///
302    /// The tensor with the given dimension repeated the given number of times.
303    fn int_repeat_dim(tensor: IntTensor<B>, dim: usize, times: usize) -> IntTensor<B> {
304        let device = tensor.device();
305        repeat_with_slice_assign::<B, _, _, _>(
306            tensor,
307            dim,
308            times,
309            device,
310            |shape, device, dtype| B::int_empty(shape, device, dtype.into()),
311            B::int_slice_assign,
312        )
313    }
314
315    /// Concatenates the given tensors along the given dimension.
316    ///
317    /// # Arguments
318    ///
319    /// * `tensors` - The tensors.
320    /// * `dim` - The dimension to concatenate along.
321    ///
322    /// # Returns
323    ///
324    /// The concatenated tensor.
325    ///
326    /// # Note
327    ///
328    /// Empty tensors (where the concatenation dimension has size 0) are filtered out at the
329    /// high-level tensor API and will not be passed to this method. Backend implementations do
330    /// not need to handle empty tensors.
331    fn int_cat(tensors: Vec<IntTensor<B>>, dim: usize) -> IntTensor<B> {
332        let first_tensor = tensors.first().expect("Tensors should not be empty");
333        let device = first_tensor.device();
334        cat_with_slice_assign::<B, _, _, _>(
335            tensors,
336            dim,
337            device,
338            |shape, device, dtype| B::int_empty(shape, device, dtype.into()),
339            B::int_slice_assign,
340        )
341    }
342
343    /// Element-wise equality comparison.
344    ///
345    /// # Arguments
346    ///
347    /// * `lhs` - The left-hand side tensor.
348    /// * `rhs` - The right-hand side tensor.
349    /// * `out_dtype` - The output tensor dtype.
350    ///
351    /// # Returns
352    ///
353    /// The boolean tensor with the result of the comparison.
354    fn int_equal(lhs: IntTensor<B>, rhs: IntTensor<B>, out_dtype: BoolDType) -> BoolTensor<B>;
355
356    /// Element-wise non-equality comparison.
357    ///
358    /// # Arguments
359    ///
360    /// * `lhs` - The left-hand side tensor.
361    /// * `rhs` - The right-hand side tensor.
362    /// * `out_dtype` - The output tensor dtype.
363    ///
364    /// # Returns
365    ///
366    /// The boolean tensor with the result of the comparison.
367    fn int_not_equal(lhs: IntTensor<B>, rhs: IntTensor<B>, out_dtype: BoolDType) -> BoolTensor<B> {
368        let equal_tensor = B::int_equal(lhs, rhs, out_dtype);
369        B::bool_not(equal_tensor)
370    }
371
372    /// Element-wise equality comparison with a scalar.
373    ///
374    /// # Arguments
375    ///
376    /// * `lhs` - The left-hand side tensor.
377    /// * `rhs` - The right-hand side scalar.
378    /// * `out_dtype` - The output tensor dtype.
379    ///
380    /// # Returns
381    ///
382    /// The boolean tensor with the result of the comparison.
383    fn int_equal_elem(lhs: IntTensor<B>, rhs: Scalar, out_dtype: BoolDType) -> BoolTensor<B>;
384
385    /// Element-wise non-equality comparison with a scalar.
386    ///
387    /// # Arguments
388    ///
389    /// * `lhs` - The left-hand side tensor.
390    /// * `rhs` - The right-hand side scalar.
391    /// * `out_dtype` - The output tensor dtype.
392    ///
393    /// # Returns
394    ///
395    /// The boolean tensor with the result of the comparison.
396    fn int_not_equal_elem(lhs: IntTensor<B>, rhs: Scalar, out_dtype: BoolDType) -> BoolTensor<B> {
397        let equal_tensor = B::int_equal_elem(lhs, rhs, out_dtype);
398        B::bool_not(equal_tensor)
399    }
400
401    /// Element-wise greater than comparison.
402    ///
403    /// # Arguments
404    ///
405    /// * `lhs` - The left-hand side tensor.
406    /// * `rhs` - The right-hand side tensor.
407    /// * `out_dtype` - The output tensor dtype.
408    ///
409    /// # Returns
410    ///
411    /// The boolean tensor with the result of the comparison.
412    fn int_greater(lhs: IntTensor<B>, rhs: IntTensor<B>, out_dtype: BoolDType) -> BoolTensor<B>;
413
414    /// Element-wise greater than comparison with a scalar.
415    ///
416    /// # Arguments
417    ///
418    /// * `lhs` - The left-hand side tensor.
419    /// * `rhs` - The right-hand side scalar.
420    /// * `out_dtype` - The output tensor dtype.
421    ///
422    /// # Returns
423    ///
424    /// The boolean tensor with the result of the comparison.
425    fn int_greater_elem(lhs: IntTensor<B>, rhs: Scalar, out_dtype: BoolDType) -> BoolTensor<B>;
426
427    /// Element-wise greater than or equal comparison.
428    ///
429    /// # Arguments
430    ///
431    /// * `lhs` - The left-hand side tensor.
432    /// * `rhs` - The right-hand side tensor.
433    /// * `out_dtype` - The output tensor dtype.
434    ///
435    /// # Returns
436    ///
437    /// The boolean tensor with the result of the comparison.
438    fn int_greater_equal(
439        lhs: IntTensor<B>,
440        rhs: IntTensor<B>,
441        out_dtype: BoolDType,
442    ) -> BoolTensor<B>;
443
444    /// Element-wise greater than or equal comparison with a scalar.
445    ///
446    /// # Arguments
447    ///
448    /// * `lhs` - The left-hand side tensor.
449    /// * `rhs` - The right-hand side scalar.
450    /// * `out_dtype` - The output tensor dtype.
451    ///
452    /// # Returns
453    ///
454    /// The boolean tensor with the result of the comparison.
455    fn int_greater_equal_elem(
456        lhs: IntTensor<B>,
457        rhs: Scalar,
458        out_dtype: BoolDType,
459    ) -> BoolTensor<B>;
460
461    /// Element-wise less than comparison.
462    ///
463    /// # Arguments
464    ///
465    /// * `lhs` - The left-hand side tensor.
466    /// * `rhs` - The right-hand side tensor.
467    /// * `out_dtype` - The output tensor dtype.
468    ///
469    /// # Returns
470    ///
471    /// The boolean tensor with the result of the comparison.
472    fn int_lower(lhs: IntTensor<B>, rhs: IntTensor<B>, out_dtype: BoolDType) -> BoolTensor<B>;
473
474    /// Element-wise less than comparison with a scalar.
475    ///
476    /// # Arguments
477    ///
478    /// * `lhs` - The left-hand side tensor.
479    /// * `rhs` - The right-hand side scalar.
480    /// * `out_dtype` - The output tensor dtype.
481    ///
482    /// # Returns
483    ///
484    /// The boolean tensor with the result of the comparison.
485    fn int_lower_elem(lhs: IntTensor<B>, rhs: Scalar, out_dtype: BoolDType) -> BoolTensor<B>;
486
487    /// Element-wise less than or equal comparison.
488    ///
489    /// # Arguments
490    ///
491    /// * `lhs` - The left-hand side tensor.
492    /// * `rhs` - The right-hand side tensor.
493    /// * `out_dtype` - The output tensor dtype.
494    ///
495    /// # Returns
496    ///
497    /// The boolean tensor with the result of the comparison.
498    fn int_lower_equal(lhs: IntTensor<B>, rhs: IntTensor<B>, out_dtype: BoolDType)
499    -> BoolTensor<B>;
500
501    /// Element-wise less than or equal comparison with a scalar.
502    ///
503    /// # Arguments
504    ///
505    /// * `lhs` - The left-hand side tensor.
506    /// * `rhs` - The right-hand side scalar.
507    /// * `out_dtype` - The output tensor dtype.
508    ///
509    /// # Returns
510    ///
511    /// The boolean tensor with the result of the comparison.
512    fn int_lower_equal_elem(lhs: IntTensor<B>, rhs: Scalar, out_dtype: BoolDType) -> BoolTensor<B>;
513
514    // ====  NUMERIC ==== //
515
516    /// Element-wise addition.
517    ///
518    /// # Arguments
519    ///
520    /// * `lhs` - The left-hand side tensor.
521    /// * `rhs` - The right-hand side tensor.
522    ///
523    /// # Returns
524    ///
525    /// The result of the addition.
526    fn int_add(lhs: IntTensor<B>, rhs: IntTensor<B>) -> IntTensor<B>;
527
528    /// Element-wise addition with a scalar.
529    ///
530    /// # Arguments
531    ///
532    /// * `lhs` - The left-hand side tensor.
533    /// * `rhs` - The right-hand side scalar.
534    ///
535    /// # Returns
536    ///
537    /// The result of the addition.
538    fn int_add_scalar(lhs: IntTensor<B>, rhs: Scalar) -> IntTensor<B>;
539
540    /// Element-wise square with a IntTensor.
541    ///
542    /// # Arguments
543    ///
544    /// * `tensor` - The IntTensor.
545    ///
546    /// # Returns
547    ///
548    /// The element-wise square of `tensor`.
549    fn int_square(tensor: IntTensor<B>) -> IntTensor<B> {
550        Self::int_powi_scalar(tensor, Scalar::from(2))
551    }
552
553    /// Element-wise power with a IntTensor.
554    ///
555    /// # Arguments
556    ///
557    /// * `lhs` - The left-hand side IntTensor.
558    /// * `rhs` - The right-hand side IntTensor.
559    ///
560    /// # Returns
561    ///
562    /// The elements of `lhs` raised to the power of the elements of `rhs`.
563    fn int_powi(lhs: IntTensor<B>, rhs: IntTensor<B>) -> IntTensor<B> {
564        let dtype = lhs.dtype();
565        let float_dtype = get_device_settings::<B>(&lhs.device()).float_dtype;
566        B::float_into_int(
567            B::float_powi(B::int_into_float(lhs, float_dtype), rhs),
568            dtype.into(),
569        )
570    }
571
572    /// Element-wise power with a scalar.
573    ///
574    /// # Backend Implementors Note
575    ///
576    /// A number of common exponent cases can be implemented with operations
577    /// which are much cheaper than generic exponentiation.
578    ///
579    /// This (`Backend` impl overridable) operation handles generic optimizations
580    /// for several common integer exponent cases; and then dispatches to
581    /// the (`Backend` impl overridable) [`Self::int_powi_scalar_impl`]
582    /// operation to handle the generic case.
583    ///
584    /// # Arguments
585    ///
586    /// * `lhs` - The left-hand side tensor.
587    /// * `rhs` - The right-hand side scalar.
588    ///
589    /// # Returns
590    ///
591    /// The elements of `lhs` raised to the value of `rhs`.
592    fn int_powi_scalar(lhs: IntTensor<B>, rhs: Scalar) -> IntTensor<B> {
593        let exp = rhs.elem::<i32>();
594        match exp {
595            0 => Self::int_ones(lhs.shape(), &lhs.device(), lhs.dtype().into()),
596            1 => lhs,
597            2 => Self::int_mul(lhs.clone(), lhs),
598            _ => Self::int_powi_scalar_impl(lhs, rhs),
599        }
600    }
601
602    /// Element-wise power with a scalar.
603    ///
604    /// # Backend Implementors Note
605    ///
606    /// This is the generic implementation of integer exponentiation
607    /// called by [`Self::int_powi_scalar`] in the fallback case.
608    ///
609    /// By default, this performs a relatively expensive conversion to float,
610    /// exponentiation in float, and conversion back to int.
611    /// This reduces the minimal operation set for `Backend`s,
612    /// at the cost of performance.
613    ///
614    /// This is a good target for specialized optimizations in `Backend` implementations.
615    ///
616    /// As a general rule, this should not be called directly.
617    ///
618    /// # Arguments
619    ///
620    /// * `lhs` - The left-hand side tensor.
621    /// * `rhs` - The right-hand side scalar.
622    ///
623    /// # Returns
624    ///
625    /// The elements of `lhs` raised to the value of `rhs`.
626    fn int_powi_scalar_impl(lhs: IntTensor<B>, rhs: Scalar) -> IntTensor<B> {
627        let dtype = lhs.dtype();
628        let float_dtype = get_device_settings::<B>(&lhs.device()).float_dtype;
629        B::float_into_int(
630            B::float_powi_scalar_impl(B::int_into_float(lhs, float_dtype), rhs),
631            dtype.into(),
632        )
633    }
634
635    /// Clamps a tensor under a minimum value.
636    ///
637    /// # Arguments
638    ///
639    /// * `tensor` - The tensor to clamp.
640    /// * `min` - The minimum value.
641    ///
642    /// # Returns
643    ///
644    /// The clamped tensor.
645    fn int_clamp_min(tensor: IntTensor<B>, min: Scalar) -> IntTensor<B> {
646        let dtype = get_device_settings::<B>(&tensor.device()).bool_dtype;
647        let mask = Self::int_lower_elem(tensor.clone(), min, dtype);
648        Self::int_mask_fill(tensor, mask, min)
649    }
650
651    /// Clamps a tensor over a maximum value.
652    ///
653    /// # Arguments
654    ///
655    /// * `tensor` - The tensor to clamp.
656    /// * `max` - The maximum value.
657    ///
658    /// # Returns
659    ///
660    /// The clamped tensor.
661    fn int_clamp_max(tensor: IntTensor<B>, max: Scalar) -> IntTensor<B> {
662        let dtype = get_device_settings::<B>(&tensor.device()).bool_dtype;
663        let mask = Self::int_greater_elem(tensor.clone(), max, dtype);
664        Self::int_mask_fill(tensor, mask, max)
665    }
666
667    /// Clamps a tensor between a minimum and maximum value.
668    ///
669    /// # Arguments
670    ///
671    /// * `tensor` - The tensor to clamp.
672    /// * `min` - The minimum value.
673    /// * `max` - The maximum value.
674    ///
675    /// # Returns
676    ///
677    /// The clamped tensor.
678    fn int_clamp(tensor: IntTensor<B>, min: Scalar, max: Scalar) -> IntTensor<B> {
679        Self::int_clamp_min(Self::int_clamp_max(tensor, max), min)
680    }
681
682    /// Element-wise subtraction.
683    ///
684    /// # Arguments
685    ///
686    /// * `lhs` - The left-hand side tensor.
687    /// * `rhs` - The right-hand side tensor.
688    ///
689    /// # Returns
690    ///
691    /// The result of the subtraction.
692    fn int_sub(lhs: IntTensor<B>, rhs: IntTensor<B>) -> IntTensor<B>;
693
694    /// Element-wise subtraction with a scalar.
695    ///
696    /// # Arguments
697    ///
698    /// * `lhs` - The left-hand side tensor.
699    /// * `rhs` - The right-hand side scalar.
700    ///
701    /// # Returns
702    ///
703    /// The result of the subtraction.
704    fn int_sub_scalar(lhs: IntTensor<B>, rhs: Scalar) -> IntTensor<B>;
705
706    /// Element-wise multiplication.
707    ///
708    /// # Arguments
709    ///
710    /// * `lhs` - The left-hand side tensor.
711    /// * `rhs` - The right-hand side tensor.
712    ///
713    /// # Returns
714    ///
715    /// The result of the multiplication.
716    fn int_mul(lhs: IntTensor<B>, rhs: IntTensor<B>) -> IntTensor<B>;
717
718    /// Element-wise multiplication with a scalar.
719    ///
720    /// # Arguments
721    ///
722    /// * `lhs` - The left-hand side tensor.
723    /// * `rhs` - The right-hand side scalar.
724    ///
725    /// # Returns
726    ///
727    /// The result of the multiplication.
728    fn int_mul_scalar(lhs: IntTensor<B>, rhs: Scalar) -> IntTensor<B>;
729
730    /// Element-wise division.
731    ///
732    /// # Arguments
733    ///
734    /// * `lhs` - The left-hand side tensor.
735    /// * `rhs` - The right-hand side tensor.
736    ///
737    /// # Returns
738    ///
739    /// The result of the division.
740    fn int_div(lhs: IntTensor<B>, rhs: IntTensor<B>) -> IntTensor<B>;
741
742    /// Element-wise division with a scalar.
743    ///
744    /// # Arguments
745    ///
746    /// * `lhs` - The left-hand side tensor.
747    /// * `rhs` - The right-hand side scalar.
748    ///
749    /// # Returns
750    ///
751    /// The result of the division.
752    fn int_div_scalar(lhs: IntTensor<B>, rhs: Scalar) -> IntTensor<B>;
753
754    /// Element-wise modulus.
755    ///
756    /// # Arguments
757    /// * `lhs` - The left-hand side tensor.
758    /// * `rhs` - The right-hand side scalar.
759    ///
760    /// # Returns
761    ///
762    /// The result of applying the modulus of the scalar to the tensor.
763    fn int_remainder(lhs: IntTensor<B>, rhs: IntTensor<B>) -> IntTensor<B>;
764
765    /// Element-wise modulus with a scalar.
766    ///
767    /// # Arguments
768    /// * `lhs` - The left-hand side tensor.
769    /// * `rhs` - The right-hand side scalar.
770    ///
771    /// # Returns
772    ///
773    /// The result of applying the modulus of the scalar to the tensor.
774    fn int_remainder_scalar(lhs: IntTensor<B>, rhs: Scalar) -> IntTensor<B>;
775
776    /// Multiplies two tensors together using matrix multiplication.
777    ///
778    /// # Arguments
779    ///
780    /// * `lhs` - The left-hand side tensor.
781    /// * `rhs` - The right-hand side tensor.
782    ///
783    /// # Returns
784    ///
785    /// The result of multiplying the two tensors together using matrix multiplication.
786    fn int_matmul(lhs: IntTensor<B>, rhs: IntTensor<B>) -> IntTensor<B>;
787
788    /// Element-wise negation.
789    ///
790    /// # Arguments
791    ///
792    /// * `tensor` - The tensor to negate.
793    ///
794    /// # Returns
795    ///
796    /// The negated tensor.
797    fn int_neg(tensor: IntTensor<B>) -> IntTensor<B> {
798        Self::int_mul_scalar(tensor, (-1).into())
799    }
800
801    /// Creates a tensor of zeros.
802    ///
803    /// # Arguments
804    ///
805    /// * `shape` - The shape of the tensor.
806    /// * `device` - The device to create the tensor on.
807    /// * `dtype` - The target data type.
808    ///
809    /// # Returns
810    ///
811    /// The tensor of zeros.
812    fn int_zeros(shape: Shape, device: &Device<B>, dtype: IntDType) -> IntTensor<B> {
813        Self::int_from_data(TensorData::full_dtype(shape, 0, dtype.into()), device)
814    }
815
816    /// Creates a tensor of ones.
817    ///
818    /// # Arguments
819    ///
820    /// * `shape` - The shape of the tensor.
821    /// * `device` - The device to create the tensor on.
822    /// * `dtype` - The target data type.
823    ///
824    /// # Returns
825    ///
826    /// The tensor of ones.
827    fn int_ones(shape: Shape, device: &Device<B>, dtype: IntDType) -> IntTensor<B> {
828        Self::int_from_data(TensorData::full_dtype(shape, 1, dtype.into()), device)
829    }
830
831    /// Creates a tensor filled with given value.
832    ///
833    /// # Arguments
834    ///
835    /// * `shape` - The shape of the tensor.
836    /// * `fill_value` - The value with which to fill the tensor.
837    /// * `device` - The device to create the tensor on.
838    /// * `dtype` - The target data type.
839    ///
840    /// # Returns
841    ///
842    /// The tensor filled with given value
843    fn int_full(
844        shape: Shape,
845        fill_value: Scalar,
846        device: &Device<B>,
847        dtype: IntDType,
848    ) -> IntTensor<B> {
849        Self::int_from_data(
850            TensorData::full_dtype(shape, fill_value, dtype.into()),
851            device,
852        )
853    }
854
855    /// Sums all elements in the tensor.
856    ///
857    /// # Arguments
858    ///
859    /// * `tensor` - The tensor to sum.
860    ///
861    /// # Returns
862    ///
863    /// The sum of all elements in the tensor.
864    fn int_sum(tensor: IntTensor<B>) -> IntTensor<B>;
865
866    /// Sums all elements in the tensor along a dimension.
867    ///
868    /// # Arguments
869    ///
870    /// * `tensor` - The tensor to sum.
871    /// * `dim` - The dimension to sum along.
872    ///
873    /// # Returns
874    ///
875    /// The sum of all elements in the tensor along the dimension.
876    fn int_sum_dim(tensor: IntTensor<B>, dim: usize) -> IntTensor<B>;
877
878    /// Computes the product of all elements in the tensor.
879    ///
880    /// # Arguments
881    ///
882    /// * `tensor` - The tensor to compute the product of.
883    ///
884    /// # Returns
885    ///
886    /// The product of all elements in the tensor.
887    fn int_prod(tensor: IntTensor<B>) -> IntTensor<B>;
888
889    /// Computes the product of all elements in the tensor along a dimension.
890    ///
891    /// # Arguments
892    ///
893    /// * `tensor` - The tensor to compute the product of.
894    /// * `dim` - The dimension to compute the product along.
895    ///
896    /// # Returns
897    ///
898    /// The product of all elements in the tensor along the dimension.
899    fn int_prod_dim(tensor: IntTensor<B>, dim: usize) -> IntTensor<B>;
900
901    /// Computes the mean of all elements in the tensor.
902    ///
903    /// # Arguments
904    ///
905    /// * `tensor` - The tensor to compute the mean of.
906    ///
907    /// # Returns
908    ///
909    /// The mean of all elements in the tensor.
910    fn int_mean(tensor: IntTensor<B>) -> IntTensor<B> {
911        let num_elems = tensor.shape().num_elements() as i64;
912        B::int_div_scalar(B::int_sum(tensor), num_elems.into())
913    }
914
915    /// Computes the mean of all elements in the tensor along a dimension.
916    ///
917    /// # Arguments
918    ///
919    /// * `tensor` - The tensor to compute the mean of.
920    ///
921    /// # Returns
922    ///
923    /// The mean of all elements in the tensor along the dimension.
924    fn int_mean_dim(tensor: IntTensor<B>, dim: usize) -> IntTensor<B>;
925
926    /// Computes the cumulative sum of elements along a dimension.
927    ///
928    /// # Arguments
929    ///
930    /// * `tensor` - The tensor to compute the cumulative sum of.
931    /// * `dim` - The dimension along which to compute the cumulative sum.
932    ///
933    /// # Returns
934    ///
935    /// A tensor with the same shape where each element is the cumulative sum
936    /// of all elements up to and including that position along the dimension.
937    fn int_cumsum(tensor: IntTensor<B>, dim: usize) -> IntTensor<B>;
938
939    /// Computes the cumulative product of elements along a dimension.
940    ///
941    /// # Arguments
942    ///
943    /// * `tensor` - The tensor to compute the cumulative product of.
944    /// * `dim` - The dimension along which to compute the cumulative product.
945    ///
946    /// # Returns
947    ///
948    /// A tensor with the same shape where each element is the cumulative product
949    /// of all elements up to and including that position along the dimension.
950    fn int_cumprod(tensor: IntTensor<B>, dim: usize) -> IntTensor<B>;
951
952    /// Computes the cumulative minimum of elements along a dimension.
953    ///
954    /// # Arguments
955    ///
956    /// * `tensor` - The tensor to compute the cumulative minimum of.
957    /// * `dim` - The dimension along which to compute the cumulative minimum.
958    ///
959    /// # Returns
960    ///
961    /// A tensor with the same shape where each element is the minimum
962    /// of all elements up to and including that position along the dimension.
963    fn int_cummin(tensor: IntTensor<B>, dim: usize) -> IntTensor<B>;
964
965    /// Computes the cumulative maximum of elements along a dimension.
966    ///
967    /// # Arguments
968    ///
969    /// * `tensor` - The tensor to compute the cumulative maximum of.
970    /// * `dim` - The dimension along which to compute the cumulative maximum.
971    ///
972    /// # Returns
973    ///
974    /// A tensor with the same shape where each element is the maximum
975    /// of all elements up to and including that position along the dimension.
976    fn int_cummax(tensor: IntTensor<B>, dim: usize) -> IntTensor<B>;
977
978    /// Gets the indices of the maximum elements along a dimension.
979    ///
980    /// # Arguments
981    ///
982    /// * `tensor` - The tensor to get the maximum indices of.
983    /// * `dim` - The dimension to get the maximum indices along.
984    ///
985    /// # Returns
986    ///
987    /// The indices of the maximum elements along the dimension.
988    fn int_argmax(tensor: IntTensor<B>, dim: usize) -> IntTensor<B>;
989
990    /// Gets the indices of the k maximum elements along a dimension.
991    /// If two elements share the same value, it will be ordered by the lowest
992    /// coordinate
993    ///
994    /// # Arguments
995    ///
996    /// * `tensor` - The tensor to get the maximum indices of.
997    /// * `dim` - The dimension to get the maximum indices along.
998    /// * `k` - number of maximum elements.
999    ///
1000    /// # Returns
1001    ///
1002    /// The indices of the maximum elements along the dimension.
1003    fn int_argtopk(tensor: IntTensor<B>, dim: usize, k: usize) -> IntTensor<B> {
1004        let device = &tensor.device();
1005        let dtype = get_device_settings::<B>(device).int_dtype;
1006        let k_indices = B::int_arange(0..k as i64, device, dtype);
1007        Self::int_select(Self::int_argsort(tensor, dim, true), dim, k_indices)
1008    }
1009
1010    /// Gets the values of the k maximum elements along a dimension.
1011    /// # Arguments
1012    ///
1013    /// * `tensor` - The tensor to get the maximum values of.
1014    /// * `dim` - The dimension to get the maximum values along.
1015    /// * `k` - number of maximum elements.
1016    ///
1017    /// # Returns
1018    ///
1019    /// The values of the maximum elements along the dimension.
1020    fn int_topk(tensor: IntTensor<B>, dim: usize, k: usize) -> IntTensor<B> {
1021        let device = &tensor.device();
1022        let dtype = get_device_settings::<B>(device).int_dtype;
1023        let k_indices = Self::int_arange(0..k as i64, device, dtype);
1024        Self::int_select(Self::int_sort(tensor, dim, true), dim, k_indices)
1025    }
1026
1027    /// Gets the values of the k maximum elements along a dimension, and their indices.
1028    ///
1029    /// # Arguments
1030    ///
1031    /// * `tensor` - The tensor to get the maximum values of.
1032    /// * `dim` - The dimension to get the maximum values along.
1033    /// * `k` - number of maximum elements.
1034    ///
1035    /// # Returns
1036    ///
1037    /// A tuple with the values of the k maximum elements along the dimension, and their indices.
1038    ///
1039    /// The default sorts once and keeps the first `k` of each half. It deliberately does not
1040    /// compose `int_topk` with `int_argtopk`: those default to a sort each, so that would sort
1041    /// twice, and `int_argtopk` has no default at all, so backends that only sort could not
1042    /// serve this. Backends whose top-k already carries both results should override this and
1043    /// produce them in a single pass.
1044    fn int_topk_with_indices(
1045        tensor: IntTensor<B>,
1046        dim: usize,
1047        k: usize,
1048    ) -> (IntTensor<B>, IntTensor<B>) {
1049        let device = tensor.device();
1050        let dtype = get_device_settings::<B>(&device).int_dtype;
1051        let k_indices = Self::int_arange(0..k as i64, &device, dtype);
1052        let (values, indices) = Self::int_sort_with_indices(tensor, dim, true);
1053
1054        (
1055            Self::int_select(values, dim, k_indices.clone()),
1056            Self::int_select(indices, dim, k_indices),
1057        )
1058    }
1059
1060    /// Gets the indices of the minimum elements along a dimension.
1061    ///
1062    /// # Arguments
1063    ///
1064    /// * `tensor` - The tensor to get the minimum indices of.
1065    /// * `dim` - The dimension to get the minimum indices along.
1066    ///
1067    /// # Returns
1068    ///
1069    /// The indices of the minimum elements along the dimension.
1070    fn int_argmin(tensor: IntTensor<B>, dim: usize) -> IntTensor<B>;
1071
1072    /// Gets the maximum element in the tensor.
1073    ///
1074    /// # Arguments
1075    ///
1076    /// * `tensor` - The tensor to get the maximum element of.
1077    ///
1078    /// # Returns
1079    ///
1080    /// The maximum element in the tensor.
1081    fn int_max(tensor: IntTensor<B>) -> IntTensor<B> {
1082        let shape = tensor.shape();
1083        let tensor = B::int_reshape(tensor, Shape::new([shape.num_elements()]));
1084
1085        B::int_max_dim(tensor, 0)
1086    }
1087
1088    /// Gets the maximum element in the tensor along a dimension.
1089    ///
1090    /// # Arguments
1091    ///
1092    /// * `tensor` - The tensor to get the maximum element of.
1093    /// * `dim` - The dimension to get the maximum element along.
1094    ///
1095    /// # Returns
1096    ///
1097    /// The maximum element in the tensor along the dimension.
1098    fn int_max_dim(tensor: IntTensor<B>, dim: usize) -> IntTensor<B> {
1099        let index = B::int_argmax(tensor.clone(), dim);
1100        B::int_gather(dim, tensor, index)
1101    }
1102
1103    /// Gets the maximum elements and corresponding indices along a dimension.
1104    ///
1105    /// # Arguments
1106    ///
1107    /// * `tensor` - The tensor to get the maximum elements and indices of.
1108    /// * `dim` - The dimension to get the maximum elements and indices along.
1109    ///
1110    /// # Returns
1111    ///
1112    /// The maximum elements and corresponding indices along the dimension.
1113    fn int_max_dim_with_indices(tensor: IntTensor<B>, dim: usize) -> (IntTensor<B>, IntTensor<B>) {
1114        let index = B::int_argmax(tensor.clone(), dim);
1115        let values = B::int_gather(dim, tensor, index.clone());
1116
1117        (values, index)
1118    }
1119
1120    /// Gets the maximum absolute element in the tensor.
1121    ///
1122    /// # Arguments
1123    ///
1124    /// * `tensor` - The tensor to get the maximum element of.
1125    ///
1126    /// # Returns
1127    ///
1128    /// The maximum element in the tensor.
1129    fn int_max_abs(tensor: IntTensor<B>) -> IntTensor<B> {
1130        let shape = tensor.shape();
1131        let tensor = B::int_reshape(tensor, Shape::new([shape.num_elements()]));
1132
1133        B::int_max_abs_dim(tensor, 0)
1134    }
1135
1136    /// Gets the maximum absolute element in the tensor along a dimension.
1137    ///
1138    /// # Arguments
1139    ///
1140    /// * `tensor` - The tensor to get the maximum element of.
1141    /// * `dim` - The dimension to get the maximum element along.
1142    ///
1143    /// # Returns
1144    ///
1145    /// The maximum element in the tensor along the dimension.
1146    fn int_max_abs_dim(tensor: IntTensor<B>, dim: usize) -> IntTensor<B> {
1147        B::int_max_dim(B::int_abs(tensor), dim)
1148    }
1149
1150    /// Gets the minimum element in the tensor.
1151    ///
1152    /// # Arguments
1153    ///
1154    /// * `tensor` - The tensor to get the minimum element of.
1155    ///
1156    /// # Returns
1157    ///
1158    /// The minimum element in the tensor.
1159    fn int_min(tensor: IntTensor<B>) -> IntTensor<B> {
1160        let shape = tensor.shape();
1161        let tensor = B::int_reshape(tensor, Shape::new([shape.num_elements()]));
1162
1163        B::int_min_dim(tensor, 0)
1164    }
1165
1166    /// Gets the minimum elements in the tensor along a dimension.
1167    ///
1168    /// # Arguments
1169    ///
1170    /// * `tensor` - The tensor to get the minimum element of.
1171    /// * `dim` - The dimension to get the minimum element along.
1172    ///
1173    /// # Returns
1174    ///
1175    /// The minimum element in the tensor along the dimension.
1176    fn int_min_dim(tensor: IntTensor<B>, dim: usize) -> IntTensor<B> {
1177        let index = B::int_argmin(tensor.clone(), dim);
1178        B::int_gather(dim, tensor, index)
1179    }
1180
1181    /// Gets the minimum elements and corresponding indices along a dimension.
1182    ///
1183    /// # Arguments
1184    ///
1185    /// * `tensor` - The tensor to get the minimum elements and indices of.
1186    /// * `dim` - The dimension to get the minimum elements and indices along.
1187    ///
1188    /// # Returns
1189    ///
1190    /// The minimum elements and corresponding indices along the dimension.
1191    fn int_min_dim_with_indices(tensor: IntTensor<B>, dim: usize) -> (IntTensor<B>, IntTensor<B>) {
1192        let indices = B::int_argmin(tensor.clone(), dim);
1193        let values = B::int_gather(dim, tensor, indices.clone());
1194
1195        (values, indices)
1196    }
1197
1198    /// Returns a new tensor with absolute values.
1199    ///
1200    /// # Arguments
1201    ///
1202    /// * `tensor` - The tensor to take absolute value of.
1203    ///
1204    /// # Returns
1205    ///
1206    /// A tensor with the same shape as `tensor` with absolute values.
1207    fn int_abs(tensor: IntTensor<B>) -> IntTensor<B>;
1208
1209    /// Transposes an int tensor.
1210    ///
1211    /// # Arguments
1212    ///
1213    /// * `tensor` - The tensor to transpose.
1214    ///
1215    /// # Returns
1216    ///
1217    /// The transposed tensor.
1218    fn int_transpose(tensor: IntTensor<B>) -> IntTensor<B> {
1219        let ndims = tensor.shape().num_dims();
1220        Self::int_swap_dims(tensor, ndims - 2, ndims - 1)
1221    }
1222
1223    /// Swaps two dimensions of an int tensor.
1224    ///
1225    /// # Arguments
1226    ///
1227    /// * `tensor` - The tensor to swap the dimensions of.
1228    /// * `dim1` - The first dimension to swap.
1229    /// * `dim2` - The second dimension to swap.
1230    ///
1231    /// # Returns
1232    ///
1233    /// The tensor with the dimensions swapped.
1234    fn int_swap_dims(tensor: IntTensor<B>, dim1: usize, dim2: usize) -> IntTensor<B>;
1235
1236    /// Permutes the dimensions of a tensor.
1237    ///
1238    /// # Arguments
1239    ///
1240    /// * `tensor` - The tensor to permute the dimensions of.
1241    /// * `axes` - The new order of the dimensions.
1242    /// # Returns
1243    ///
1244    /// The tensor with the dimensions permuted.
1245    fn int_permute(tensor: IntTensor<B>, axes: &[usize]) -> IntTensor<B>;
1246
1247    /// Reverse the order of elements in a tensor along the given axes.
1248    ///
1249    /// # Arguments
1250    ///
1251    /// * `tensor` - The tensor to reverse.
1252    /// * `axes` - The axes to reverse.
1253    ///
1254    /// The tensor with the elements reversed.
1255    fn int_flip(tensor: IntTensor<B>, axes: &[usize]) -> IntTensor<B>;
1256
1257    /// Creates a new int tensor with random values.
1258    ///
1259    ///  # Arguments
1260    ///  * `shape` - The shape of the tensor.
1261    ///  * `distribution` - The distribution to sample from.
1262    ///  * `device` - The device to create the tensor on.
1263    /// * `dtype` - The target data type.
1264    ///
1265    ///  # Returns
1266    ///
1267    ///  The tensor with the given shape and random values.
1268    fn int_random(
1269        shape: Shape,
1270        distribution: Distribution,
1271        device: &Device<B>,
1272        dtype: IntDType,
1273    ) -> IntTensor<B>;
1274
1275    /// Creates a new tensor with values from the given range with the given step size.
1276    ///
1277    /// # Arguments
1278    ///
1279    /// * `range` - The range of values.
1280    /// * `step` - The step size.
1281    /// * `device` - The device to create the tensor on.
1282    /// * `dtype` - The target data type.
1283    ///
1284    /// # Returns
1285    ///
1286    /// The tensor with the given values.
1287    fn int_arange_step(
1288        range: Range<i64>,
1289        step: usize,
1290        device: &Device<B>,
1291        dtype: IntDType,
1292    ) -> IntTensor<B> {
1293        let value = range.step_by(step).collect::<Vec<_>>();
1294        let shape = Shape::new([value.len()]);
1295        let data = TensorData::new(value, shape).convert_dtype(dtype.into());
1296        B::int_from_data(data, device)
1297    }
1298
1299    /// Creates a new tensor with values from the given range.
1300    ///
1301    /// # Arguments
1302    ///
1303    /// * `range` - The range of values.
1304    /// * `device` - The device to create the tensor on.
1305    ///
1306    /// # Returns
1307    ///
1308    /// The tensor with the given values.
1309    ///
1310    /// # Remarks
1311    ///
1312    /// Uses `arange_step` with a step size of 1 under the hood.
1313    fn int_arange(range: Range<i64>, device: &Device<B>, dtype: IntDType) -> IntTensor<B> {
1314        Self::int_arange_step(range, 1, device, dtype)
1315    }
1316
1317    /// Tests if any element in the int `tensor` evaluates to True.
1318    ///
1319    /// # Arguments
1320    ///
1321    /// * `tensor` - The tensor to test.
1322    ///
1323    /// # Returns
1324    ///
1325    /// A boolean tensor with a single element, True if any element in the tensor is True, False otherwise.
1326    fn int_any(tensor: IntTensor<B>, out_dtype: BoolDType) -> BoolTensor<B> {
1327        let int_dtype = tensor.dtype();
1328        let bool_tensor = B::int_equal_elem(tensor, 0.into(), out_dtype);
1329        let bool_tensor = B::bool_not(bool_tensor);
1330        let sum = B::int_sum(B::bool_into_int(bool_tensor, int_dtype.into()));
1331        B::int_greater_elem(sum, 0.into(), out_dtype)
1332    }
1333
1334    /// Tests if any element in the int `tensor` evaluates to True along a given dimension `dim`.
1335    ///
1336    /// # Arguments
1337    ///
1338    /// * `tensor` - The tensor to test.
1339    /// * `dim` - The axis along which to test.
1340    ///
1341    /// # Returns
1342    ///
1343    /// A boolean tensor `Tensor<B, D, Bool>` with the same size as input `tensor`, except in the `dim` axis
1344    /// where the size is 1. The elem in the `dim` axis is True if any element along this dim in the input
1345    /// evaluates to True, False otherwise.
1346    fn int_any_dim(tensor: IntTensor<B>, dim: usize, out_dtype: BoolDType) -> BoolTensor<B> {
1347        let int_dtype = tensor.dtype();
1348        let bool_tensor = B::int_equal_elem(tensor, 0.into(), out_dtype);
1349        let bool_tensor = B::bool_not(bool_tensor);
1350        let sum = B::int_sum_dim(B::bool_into_int(bool_tensor, int_dtype.into()), dim);
1351        B::int_greater_elem(sum, 0.into(), out_dtype)
1352    }
1353
1354    /// Tests if all elements in the int `tensor` evaluate to True.
1355    ///
1356    /// # Arguments
1357    ///
1358    /// * `tensor` - The tensor to test.
1359    /// * `out_dtype` - The output tensor dtype.
1360    ///
1361    /// # Returns
1362    ///
1363    /// A boolean tensor `Tensor<B, 1, Bool>` with a single element, True if all elements in the input tensor
1364    /// evaluate to True, False otherwise.
1365    fn int_all(tensor: IntTensor<B>, out_dtype: BoolDType) -> BoolTensor<B> {
1366        let int_dtype = tensor.dtype();
1367        let num_elems = tensor.shape().num_elements() as i64;
1368        let bool_tensor = B::int_equal_elem(tensor, 0.into(), out_dtype);
1369        let bool_tensor = B::bool_not(bool_tensor);
1370        let sum = B::int_sum(B::bool_into_int(bool_tensor, int_dtype.into()));
1371        B::int_equal_elem(sum, num_elems.into(), out_dtype)
1372    }
1373
1374    /// Tests if all elements in the int `tensor` evaluate to True along a given dimension `dim`.
1375    ///
1376    /// # Arguments
1377    ///
1378    /// * `tensor` - The tensor to test.
1379    /// * `dim` - The axis along which to test.
1380    /// * `out_dtype` - The output tensor dtype.
1381    ///
1382    /// # Returns
1383    ///
1384    /// A boolean tensor `Tensor<B, D, Bool>` with the same size as input `tensor`, except in the `dim` axis
1385    /// where the size is 1. The elem in the `dim` axis is True if all elements along this dim in the input
1386    /// evaluates to True, False otherwise.
1387    fn int_all_dim(tensor: IntTensor<B>, dim: usize, out_dtype: BoolDType) -> BoolTensor<B> {
1388        let int_dtype = tensor.dtype();
1389        let num_elems = tensor.shape()[dim] as i64;
1390        let bool_tensor = B::int_equal_elem(tensor, 0.into(), out_dtype);
1391        let bool_tensor = B::bool_not(bool_tensor);
1392        let sum = B::int_sum_dim(B::bool_into_int(bool_tensor, int_dtype.into()), dim);
1393        B::int_equal_elem(sum, num_elems.into(), out_dtype)
1394    }
1395
1396    /// Returns the signs of the int `tensor`.
1397    ///
1398    /// # Arguments
1399    ///
1400    /// * `tensor` - The tensor to extract the signs from.
1401    ///
1402    /// # Returns
1403    ///
1404    /// A tensor with the same shape as `tensor` containing the signs of the elements of `tensor`.
1405    fn int_sign(tensor: IntTensor<B>) -> IntTensor<B> {
1406        let dtype = tensor.dtype();
1407        let device = &tensor.device();
1408        let bool_dtype = get_device_settings::<B>(&tensor.device()).bool_dtype;
1409        let zeros = B::int_zeros(tensor.shape(), device, dtype.into());
1410        let less_than_zero = B::int_lower_elem(tensor.clone(), 0.into(), bool_dtype);
1411        let greater_than_zero = B::int_greater_elem(tensor, 0.into(), bool_dtype);
1412
1413        let mut result = B::int_mask_fill(zeros, less_than_zero, (-1).into());
1414        result = B::int_mask_fill(result, greater_than_zero, 1.into());
1415        result
1416    }
1417
1418    /// Broadcasts the int `tensor` to the given `shape`.
1419    fn int_expand(tensor: IntTensor<B>, shape: Shape) -> IntTensor<B>;
1420
1421    /// Sort the elements of the input `tensor` by value along a given dimension.
1422    ///
1423    /// This sort is unstable (i.e., may reorder equal elements).
1424    ///
1425    /// # Arguments
1426    ///
1427    /// * `tensor` - The input tensor.
1428    /// * `dim` - The axis along which to sort.
1429    /// * `descending` - The sorting order.
1430    ///
1431    /// # Returns
1432    ///
1433    /// A tensor with the same shape as the input tensor, where the elements are sorted by value.
1434    fn int_sort(tensor: IntTensor<B>, dim: usize, descending: bool) -> IntTensor<B> {
1435        let device = tensor.device();
1436        sort::<B, _, _, _>(
1437            tensor,
1438            dim,
1439            descending,
1440            device,
1441            |tensor| {
1442                let msg = "Failed to synchronously read tensor data. This operation is not supported until this backend has a GPU sorting implementation.";
1443                try_read_sync(B::int_into_data(tensor))
1444                    .expect(msg)
1445                    .expect(msg)
1446            },
1447            |data, device, _dtype| B::int_from_data(data, device),
1448        )
1449    }
1450
1451    /// Sort the elements of the input `tensor` by value along a given dimension.
1452    ///
1453    /// This sort is unstable (i.e., may reorder equal elements).
1454    ///
1455    /// # Arguments
1456    ///
1457    /// * `tensor` - The input tensor.
1458    /// * `dim` - The axis along which to sort.
1459    ///
1460    /// # Returns
1461    ///
1462    /// A tensor with the same shape as the input tensor and corresponding indices, where
1463    /// the elements are sorted by value and the indices map back to the original input tensor.
1464    fn int_sort_with_indices(
1465        tensor: IntTensor<B>,
1466        dim: usize,
1467        descending: bool,
1468    ) -> (IntTensor<B>, IntTensor<B>) {
1469        let dtype = tensor.dtype();
1470        let device = tensor.device();
1471        sort_with_indices::<B, _, _, _>(
1472            tensor,
1473            dim,
1474            descending,
1475            dtype.into(),
1476            device,
1477            |tensor| {
1478                let msg = "Failed to synchronously read tensor data. This operation is not supported until this backend has a GPU sorting implementation.";
1479                try_read_sync(B::int_into_data(tensor))
1480                    .expect(msg)
1481                    .expect(msg)
1482            },
1483            |data, device, _dtype| B::int_from_data(data, device),
1484        )
1485    }
1486
1487    /// Returns the indices that sort the elements of the input `tensor` by value
1488    /// along a given dimension.
1489    ///
1490    /// This sort is unstable (i.e., may reorder equal elements).
1491    ///
1492    /// # Arguments
1493    ///
1494    /// * `tensor` - The input tensor.
1495    /// * `dim` - The axis along which to sort.
1496    /// * `descending` - The sorting order.
1497    ///
1498    /// # Returns
1499    ///
1500    /// A tensor with the same shape as the input tensor the indices map back to the original input tensor.
1501    fn int_argsort(tensor: IntTensor<B>, dim: usize, descending: bool) -> IntTensor<B> {
1502        let dtype = tensor.dtype();
1503        let device = tensor.device();
1504        argsort::<B, _, _>(tensor, dim, descending, dtype.into(), device, |tensor| {
1505            let msg = "Failed to synchronously read tensor data. This operation is not supported until this backend has a GPU sorting implementation.";
1506            try_read_sync(B::int_into_data(tensor))
1507                .expect(msg)
1508                .expect(msg)
1509        })
1510    }
1511
1512    /// Bitwise AND operation for Int Tensors
1513    fn bitwise_and(lhs: IntTensor<B>, rhs: IntTensor<B>) -> IntTensor<B>;
1514
1515    /// Bitwise AND operation for Int Tensors with a scalar
1516    fn bitwise_and_scalar(lhs: IntTensor<B>, rhs: Scalar) -> IntTensor<B>;
1517
1518    /// Bitwise OR operation for Int Tensors
1519    fn bitwise_or(lhs: IntTensor<B>, rhs: IntTensor<B>) -> IntTensor<B>;
1520
1521    /// Bitwise OR operation for Int Tensors with a scalar
1522    fn bitwise_or_scalar(lhs: IntTensor<B>, rhs: Scalar) -> IntTensor<B>;
1523
1524    /// Bitwise XOR operation for Int Tensors
1525    fn bitwise_xor(lhs: IntTensor<B>, rhs: IntTensor<B>) -> IntTensor<B>;
1526
1527    /// Bitwise XOR operation for Int Tensors with a scalar
1528    fn bitwise_xor_scalar(lhs: IntTensor<B>, rhs: Scalar) -> IntTensor<B>;
1529
1530    /// Bitwise NOT operation for Int Tensors
1531    fn bitwise_not(tensor: IntTensor<B>) -> IntTensor<B>;
1532
1533    /// Bitwise left shift operation for Int Tensors
1534    fn bitwise_left_shift(lhs: IntTensor<B>, rhs: IntTensor<B>) -> IntTensor<B>;
1535
1536    /// Bitwise left shift operation for Int Tensors with a scalar
1537    fn bitwise_left_shift_scalar(lhs: IntTensor<B>, rhs: Scalar) -> IntTensor<B>;
1538
1539    /// Bitwise right shift operation for Int Tensors
1540    fn bitwise_right_shift(lhs: IntTensor<B>, rhs: IntTensor<B>) -> IntTensor<B>;
1541
1542    /// Bitwise right shift operation for Int Tensors with a scalar
1543    fn bitwise_right_shift_scalar(lhs: IntTensor<B>, rhs: Scalar) -> IntTensor<B>;
1544
1545    /// Converts a tensor to another integer data type.
1546    ///
1547    /// # Arguments
1548    ///
1549    /// * `tensor` - The tensor to convert.
1550    /// * `dtype` - The target data type.
1551    ///
1552    /// # Returns
1553    ///
1554    /// A tensor with the same values as `tensor` but in the target integer data type.
1555    fn int_cast(tensor: IntTensor<B>, dtype: IntDType) -> IntTensor<B>;
1556
1557    /// Unfold windows along a dimension.
1558    ///
1559    /// Returns a view of the tensor with all complete windows of size `size` in dimension `dim`;
1560    /// where windows are advanced by `step` at each index.
1561    ///
1562    /// The number of windows is `max(0, (shape[dim] - size).ceil_div(step))`.
1563    ///
1564    /// # Arguments
1565    ///
1566    /// * `tensor` - The input tensor to unfold; of shape ``[pre=..., dim shape, post=...]``
1567    /// * `dim` - the selected dim.
1568    /// * `size` - the size of each unfolded window.
1569    /// * `step` - the step between each window.
1570    ///
1571    /// # Returns
1572    ///
1573    /// A tensor view with shape ``[pre=..., windows, size, post=...]``.
1574    fn int_unfold(tensor: IntTensor<B>, dim: usize, size: usize, step: usize) -> IntTensor<B>;
1575}