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