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