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