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