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, 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_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_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_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_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_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 modulus.
702 ///
703 /// # Arguments
704 /// * `lhs` - The left-hand side tensor.
705 /// * `rhs` - The right-hand side scalar.
706 ///
707 /// # Returns
708 ///
709 /// The result of applying the modulus of the scalar to the tensor.
710 fn int_remainder(lhs: IntTensor<B>, rhs: IntTensor<B>) -> IntTensor<B>;
711
712 /// Element-wise modulus with a scalar.
713 ///
714 /// # Arguments
715 /// * `lhs` - The left-hand side tensor.
716 /// * `rhs` - The right-hand side scalar.
717 ///
718 /// # Returns
719 ///
720 /// The result of applying the modulus of the scalar to the tensor.
721 fn int_remainder_scalar(lhs: IntTensor<B>, rhs: Scalar) -> IntTensor<B>;
722
723 /// Multiplies two tensors together using matrix multiplication.
724 ///
725 /// # Arguments
726 ///
727 /// * `lhs` - The left-hand side tensor.
728 /// * `rhs` - The right-hand side tensor.
729 ///
730 /// # Returns
731 ///
732 /// The result of multiplying the two tensors together using matrix multiplication.
733 fn int_matmul(lhs: IntTensor<B>, rhs: IntTensor<B>) -> IntTensor<B>;
734
735 /// Element-wise negation.
736 ///
737 /// # Arguments
738 ///
739 /// * `tensor` - The tensor to negate.
740 ///
741 /// # Returns
742 ///
743 /// The negated tensor.
744 fn int_neg(tensor: IntTensor<B>) -> IntTensor<B> {
745 Self::int_mul_scalar(tensor, (-1).into())
746 }
747
748 /// Creates a tensor of zeros.
749 ///
750 /// # Arguments
751 ///
752 /// * `shape` - The shape of the tensor.
753 /// * `device` - The device to create the tensor on.
754 /// * `dtype` - The target data type.
755 ///
756 /// # Returns
757 ///
758 /// The tensor of zeros.
759 fn int_zeros(shape: Shape, device: &Device<B>, dtype: IntDType) -> IntTensor<B> {
760 Self::int_from_data(TensorData::full_dtype(shape, 0, dtype.into()), device)
761 }
762
763 /// Creates a tensor of ones.
764 ///
765 /// # Arguments
766 ///
767 /// * `shape` - The shape of the tensor.
768 /// * `device` - The device to create the tensor on.
769 /// * `dtype` - The target data type.
770 ///
771 /// # Returns
772 ///
773 /// The tensor of ones.
774 fn int_ones(shape: Shape, device: &Device<B>, dtype: IntDType) -> IntTensor<B> {
775 Self::int_from_data(TensorData::full_dtype(shape, 1, dtype.into()), device)
776 }
777
778 /// Creates a tensor filled with given value.
779 ///
780 /// # Arguments
781 ///
782 /// * `shape` - The shape of the tensor.
783 /// * `fill_value` - The value with which to fill the tensor.
784 /// * `device` - The device to create the tensor on.
785 /// * `dtype` - The target data type.
786 ///
787 /// # Returns
788 ///
789 /// The tensor filled with given value
790 fn int_full(
791 shape: Shape,
792 fill_value: Scalar,
793 device: &Device<B>,
794 dtype: IntDType,
795 ) -> IntTensor<B> {
796 Self::int_from_data(
797 TensorData::full_dtype(shape, fill_value, dtype.into()),
798 device,
799 )
800 }
801
802 /// Sums all elements in the tensor.
803 ///
804 /// # Arguments
805 ///
806 /// * `tensor` - The tensor to sum.
807 ///
808 /// # Returns
809 ///
810 /// The sum of all elements in the tensor.
811 fn int_sum(tensor: IntTensor<B>) -> IntTensor<B>;
812
813 /// Sums all elements in the tensor along a dimension.
814 ///
815 /// # Arguments
816 ///
817 /// * `tensor` - The tensor to sum.
818 /// * `dim` - The dimension to sum along.
819 ///
820 /// # Returns
821 ///
822 /// The sum of all elements in the tensor along the dimension.
823 fn int_sum_dim(tensor: IntTensor<B>, dim: usize) -> IntTensor<B>;
824
825 /// Computes the product of all elements in the tensor.
826 ///
827 /// # Arguments
828 ///
829 /// * `tensor` - The tensor to compute the product of.
830 ///
831 /// # Returns
832 ///
833 /// The product of all elements in the tensor.
834 fn int_prod(tensor: IntTensor<B>) -> IntTensor<B>;
835
836 /// Computes the product of all elements in the tensor along a dimension.
837 ///
838 /// # Arguments
839 ///
840 /// * `tensor` - The tensor to compute the product of.
841 /// * `dim` - The dimension to compute the product along.
842 ///
843 /// # Returns
844 ///
845 /// The product of all elements in the tensor along the dimension.
846 fn int_prod_dim(tensor: IntTensor<B>, dim: usize) -> IntTensor<B>;
847
848 /// Computes the mean of all elements in the tensor.
849 ///
850 /// # Arguments
851 ///
852 /// * `tensor` - The tensor to compute the mean of.
853 ///
854 /// # Returns
855 ///
856 /// The mean of all elements in the tensor.
857 fn int_mean(tensor: IntTensor<B>) -> IntTensor<B> {
858 let num_elems = tensor.shape().num_elements() as i64;
859 B::int_div_scalar(B::int_sum(tensor), num_elems.into())
860 }
861
862 /// Computes the mean of all elements in the tensor along a dimension.
863 ///
864 /// # Arguments
865 ///
866 /// * `tensor` - The tensor to compute the mean of.
867 ///
868 /// # Returns
869 ///
870 /// The mean of all elements in the tensor along the dimension.
871 fn int_mean_dim(tensor: IntTensor<B>, dim: usize) -> IntTensor<B>;
872
873 /// Computes the cumulative sum of elements along a dimension.
874 ///
875 /// # Arguments
876 ///
877 /// * `tensor` - The tensor to compute the cumulative sum of.
878 /// * `dim` - The dimension along which to compute the cumulative sum.
879 ///
880 /// # Returns
881 ///
882 /// A tensor with the same shape where each element is the cumulative sum
883 /// of all elements up to and including that position along the dimension.
884 fn int_cumsum(tensor: IntTensor<B>, dim: usize) -> IntTensor<B>;
885
886 /// Computes the cumulative product of elements along a dimension.
887 ///
888 /// # Arguments
889 ///
890 /// * `tensor` - The tensor to compute the cumulative product of.
891 /// * `dim` - The dimension along which to compute the cumulative product.
892 ///
893 /// # Returns
894 ///
895 /// A tensor with the same shape where each element is the cumulative product
896 /// of all elements up to and including that position along the dimension.
897 fn int_cumprod(tensor: IntTensor<B>, dim: usize) -> IntTensor<B>;
898
899 /// Computes the cumulative minimum of elements along a dimension.
900 ///
901 /// # Arguments
902 ///
903 /// * `tensor` - The tensor to compute the cumulative minimum of.
904 /// * `dim` - The dimension along which to compute the cumulative minimum.
905 ///
906 /// # Returns
907 ///
908 /// A tensor with the same shape where each element is the minimum
909 /// of all elements up to and including that position along the dimension.
910 fn int_cummin(tensor: IntTensor<B>, dim: usize) -> IntTensor<B>;
911
912 /// Computes the cumulative maximum of elements along a dimension.
913 ///
914 /// # Arguments
915 ///
916 /// * `tensor` - The tensor to compute the cumulative maximum of.
917 /// * `dim` - The dimension along which to compute the cumulative maximum.
918 ///
919 /// # Returns
920 ///
921 /// A tensor with the same shape where each element is the maximum
922 /// of all elements up to and including that position along the dimension.
923 fn int_cummax(tensor: IntTensor<B>, dim: usize) -> IntTensor<B>;
924
925 /// Gets the indices of the maximum elements along a dimension.
926 ///
927 /// # Arguments
928 ///
929 /// * `tensor` - The tensor to get the maximum indices of.
930 /// * `dim` - The dimension to get the maximum indices along.
931 ///
932 /// # Returns
933 ///
934 /// The indices of the maximum elements along the dimension.
935 fn int_argmax(tensor: IntTensor<B>, dim: usize) -> IntTensor<B>;
936
937 /// Gets the indices of the k maximum elements along a dimension.
938 /// If two elements share the same value, it will be ordered by the lowest
939 /// coordinate
940 ///
941 /// # Arguments
942 ///
943 /// * `tensor` - The tensor to get the maximum indices of.
944 /// * `dim` - The dimension to get the maximum indices along.
945 /// * `k` - number of maximum elements.
946 ///
947 /// # Returns
948 ///
949 /// The indices of the maximum elements along the dimension.
950 fn int_argtopk(tensor: IntTensor<B>, dim: usize, k: usize) -> IntTensor<B> {
951 let device = &tensor.device();
952 let dtype = get_device_settings::<B>(device).int_dtype;
953 let k_indices = B::int_arange(0..k as i64, device, dtype);
954 Self::int_select(Self::int_argsort(tensor, dim, true), dim, k_indices)
955 }
956
957 /// Gets the values of the k maximum elements along a dimension.
958 /// # Arguments
959 ///
960 /// * `tensor` - The tensor to get the maximum values of.
961 /// * `dim` - The dimension to get the maximum values along.
962 /// * `k` - number of maximum elements.
963 ///
964 /// # Returns
965 ///
966 /// The values of the maximum elements along the dimension.
967 fn int_topk(tensor: IntTensor<B>, dim: usize, k: usize) -> IntTensor<B> {
968 let device = &tensor.device();
969 let dtype = get_device_settings::<B>(device).int_dtype;
970 let k_indices = Self::int_arange(0..k as i64, device, dtype);
971 Self::int_select(Self::int_sort(tensor, dim, true), dim, k_indices)
972 }
973
974 /// Gets the values of the k maximum elements along a dimension, and their indices.
975 ///
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 /// A tuple with the values of the k maximum elements along the dimension, and their indices.
985 ///
986 /// The default sorts once and keeps the first `k` of each half. It deliberately does not
987 /// compose `int_topk` with `int_argtopk`: those default to a sort each, so that would sort
988 /// twice, and `int_argtopk` has no default at all, so backends that only sort could not
989 /// serve this. Backends whose top-k already carries both results should override this and
990 /// produce them in a single pass.
991 fn int_topk_with_indices(
992 tensor: IntTensor<B>,
993 dim: usize,
994 k: usize,
995 ) -> (IntTensor<B>, IntTensor<B>) {
996 let device = tensor.device();
997 let dtype = get_device_settings::<B>(&device).int_dtype;
998 let k_indices = Self::int_arange(0..k as i64, &device, dtype);
999 let (values, indices) = Self::int_sort_with_indices(tensor, dim, true);
1000
1001 (
1002 Self::int_select(values, dim, k_indices.clone()),
1003 Self::int_select(indices, dim, k_indices),
1004 )
1005 }
1006
1007 /// Gets the indices of the minimum elements along a dimension.
1008 ///
1009 /// # Arguments
1010 ///
1011 /// * `tensor` - The tensor to get the minimum indices of.
1012 /// * `dim` - The dimension to get the minimum indices along.
1013 ///
1014 /// # Returns
1015 ///
1016 /// The indices of the minimum elements along the dimension.
1017 fn int_argmin(tensor: IntTensor<B>, dim: usize) -> IntTensor<B>;
1018
1019 /// Gets the maximum element in the tensor.
1020 ///
1021 /// # Arguments
1022 ///
1023 /// * `tensor` - The tensor to get the maximum element of.
1024 ///
1025 /// # Returns
1026 ///
1027 /// The maximum element in the tensor.
1028 fn int_max(tensor: IntTensor<B>) -> IntTensor<B> {
1029 let shape = tensor.shape();
1030 let tensor = B::int_reshape(tensor, Shape::new([shape.num_elements()]));
1031
1032 B::int_max_dim(tensor, 0)
1033 }
1034
1035 /// Gets the maximum element in the tensor along a dimension.
1036 ///
1037 /// # Arguments
1038 ///
1039 /// * `tensor` - The tensor to get the maximum element of.
1040 /// * `dim` - The dimension to get the maximum element along.
1041 ///
1042 /// # Returns
1043 ///
1044 /// The maximum element in the tensor along the dimension.
1045 fn int_max_dim(tensor: IntTensor<B>, dim: usize) -> IntTensor<B> {
1046 let index = B::int_argmax(tensor.clone(), dim);
1047 B::int_gather(dim, tensor, index)
1048 }
1049
1050 /// Gets the maximum elements and corresponding indices along a dimension.
1051 ///
1052 /// # Arguments
1053 ///
1054 /// * `tensor` - The tensor to get the maximum elements and indices of.
1055 /// * `dim` - The dimension to get the maximum elements and indices along.
1056 ///
1057 /// # Returns
1058 ///
1059 /// The maximum elements and corresponding indices along the dimension.
1060 fn int_max_dim_with_indices(tensor: IntTensor<B>, dim: usize) -> (IntTensor<B>, IntTensor<B>) {
1061 let index = B::int_argmax(tensor.clone(), dim);
1062 let values = B::int_gather(dim, tensor, index.clone());
1063
1064 (values, index)
1065 }
1066
1067 /// Gets the maximum absolute element in the tensor.
1068 ///
1069 /// # Arguments
1070 ///
1071 /// * `tensor` - The tensor to get the maximum element of.
1072 ///
1073 /// # Returns
1074 ///
1075 /// The maximum element in the tensor.
1076 fn int_max_abs(tensor: IntTensor<B>) -> IntTensor<B> {
1077 let shape = tensor.shape();
1078 let tensor = B::int_reshape(tensor, Shape::new([shape.num_elements()]));
1079
1080 B::int_max_abs_dim(tensor, 0)
1081 }
1082
1083 /// Gets the maximum absolute element in the tensor along a dimension.
1084 ///
1085 /// # Arguments
1086 ///
1087 /// * `tensor` - The tensor to get the maximum element of.
1088 /// * `dim` - The dimension to get the maximum element along.
1089 ///
1090 /// # Returns
1091 ///
1092 /// The maximum element in the tensor along the dimension.
1093 fn int_max_abs_dim(tensor: IntTensor<B>, dim: usize) -> IntTensor<B> {
1094 B::int_max_dim(B::int_abs(tensor), dim)
1095 }
1096
1097 /// Gets the minimum element in the tensor.
1098 ///
1099 /// # Arguments
1100 ///
1101 /// * `tensor` - The tensor to get the minimum element of.
1102 ///
1103 /// # Returns
1104 ///
1105 /// The minimum element in the tensor.
1106 fn int_min(tensor: IntTensor<B>) -> IntTensor<B> {
1107 let shape = tensor.shape();
1108 let tensor = B::int_reshape(tensor, Shape::new([shape.num_elements()]));
1109
1110 B::int_min_dim(tensor, 0)
1111 }
1112
1113 /// Gets the minimum elements in the tensor along a dimension.
1114 ///
1115 /// # Arguments
1116 ///
1117 /// * `tensor` - The tensor to get the minimum element of.
1118 /// * `dim` - The dimension to get the minimum element along.
1119 ///
1120 /// # Returns
1121 ///
1122 /// The minimum element in the tensor along the dimension.
1123 fn int_min_dim(tensor: IntTensor<B>, dim: usize) -> IntTensor<B> {
1124 let index = B::int_argmin(tensor.clone(), dim);
1125 B::int_gather(dim, tensor, index)
1126 }
1127
1128 /// Gets the minimum elements and corresponding indices along a dimension.
1129 ///
1130 /// # Arguments
1131 ///
1132 /// * `tensor` - The tensor to get the minimum elements and indices of.
1133 /// * `dim` - The dimension to get the minimum elements and indices along.
1134 ///
1135 /// # Returns
1136 ///
1137 /// The minimum elements and corresponding indices along the dimension.
1138 fn int_min_dim_with_indices(tensor: IntTensor<B>, dim: usize) -> (IntTensor<B>, IntTensor<B>) {
1139 let indices = B::int_argmin(tensor.clone(), dim);
1140 let values = B::int_gather(dim, tensor, indices.clone());
1141
1142 (values, indices)
1143 }
1144
1145 /// Returns a new tensor with absolute values.
1146 ///
1147 /// # Arguments
1148 ///
1149 /// * `tensor` - The tensor to take absolute value of.
1150 ///
1151 /// # Returns
1152 ///
1153 /// A tensor with the same shape as `tensor` with absolute values.
1154 fn int_abs(tensor: IntTensor<B>) -> IntTensor<B>;
1155
1156 /// Transposes an int tensor.
1157 ///
1158 /// # Arguments
1159 ///
1160 /// * `tensor` - The tensor to transpose.
1161 ///
1162 /// # Returns
1163 ///
1164 /// The transposed tensor.
1165 fn int_transpose(tensor: IntTensor<B>) -> IntTensor<B> {
1166 let ndims = tensor.shape().num_dims();
1167 Self::int_swap_dims(tensor, ndims - 2, ndims - 1)
1168 }
1169
1170 /// Swaps two dimensions of an int tensor.
1171 ///
1172 /// # Arguments
1173 ///
1174 /// * `tensor` - The tensor to swap the dimensions of.
1175 /// * `dim1` - The first dimension to swap.
1176 /// * `dim2` - The second dimension to swap.
1177 ///
1178 /// # Returns
1179 ///
1180 /// The tensor with the dimensions swapped.
1181 fn int_swap_dims(tensor: IntTensor<B>, dim1: usize, dim2: usize) -> IntTensor<B>;
1182
1183 /// Permutes the dimensions of a tensor.
1184 ///
1185 /// # Arguments
1186 ///
1187 /// * `tensor` - The tensor to permute the dimensions of.
1188 /// * `axes` - The new order of the dimensions.
1189 /// # Returns
1190 ///
1191 /// The tensor with the dimensions permuted.
1192 fn int_permute(tensor: IntTensor<B>, axes: &[usize]) -> IntTensor<B>;
1193
1194 /// Reverse the order of elements in a tensor along the given axes.
1195 ///
1196 /// # Arguments
1197 ///
1198 /// * `tensor` - The tensor to reverse.
1199 /// * `axes` - The axes to reverse.
1200 ///
1201 /// The tensor with the elements reversed.
1202 fn int_flip(tensor: IntTensor<B>, axes: &[usize]) -> IntTensor<B>;
1203
1204 /// Creates a new int tensor with random values.
1205 ///
1206 /// # Arguments
1207 /// * `shape` - The shape of the tensor.
1208 /// * `distribution` - The distribution to sample from.
1209 /// * `device` - The device to create the tensor on.
1210 /// * `dtype` - The target data type.
1211 ///
1212 /// # Returns
1213 ///
1214 /// The tensor with the given shape and random values.
1215 fn int_random(
1216 shape: Shape,
1217 distribution: Distribution,
1218 device: &Device<B>,
1219 dtype: IntDType,
1220 ) -> IntTensor<B>;
1221
1222 /// Creates a new tensor with values from the given range with the given step size.
1223 ///
1224 /// # Arguments
1225 ///
1226 /// * `range` - The range of values.
1227 /// * `step` - The step size.
1228 /// * `device` - The device to create the tensor on.
1229 /// * `dtype` - The target data type.
1230 ///
1231 /// # Returns
1232 ///
1233 /// The tensor with the given values.
1234 fn int_arange_step(
1235 range: Range<i64>,
1236 step: usize,
1237 device: &Device<B>,
1238 dtype: IntDType,
1239 ) -> IntTensor<B> {
1240 let value = range.step_by(step).collect::<Vec<_>>();
1241 let shape = Shape::new([value.len()]);
1242 let data = TensorData::new(value, shape).convert_dtype(dtype.into());
1243 B::int_from_data(data, device)
1244 }
1245
1246 /// Creates a new tensor with values from the given range.
1247 ///
1248 /// # Arguments
1249 ///
1250 /// * `range` - The range of values.
1251 /// * `device` - The device to create the tensor on.
1252 ///
1253 /// # Returns
1254 ///
1255 /// The tensor with the given values.
1256 ///
1257 /// # Remarks
1258 ///
1259 /// Uses `arange_step` with a step size of 1 under the hood.
1260 fn int_arange(range: Range<i64>, device: &Device<B>, dtype: IntDType) -> IntTensor<B> {
1261 Self::int_arange_step(range, 1, device, dtype)
1262 }
1263
1264 /// Tests if any element in the int `tensor` evaluates to True.
1265 ///
1266 /// # Arguments
1267 ///
1268 /// * `tensor` - The tensor to test.
1269 ///
1270 /// # Returns
1271 ///
1272 /// A boolean tensor with a single element, True if any element in the tensor is True, False otherwise.
1273 fn int_any(tensor: IntTensor<B>, out_dtype: BoolDType) -> BoolTensor<B> {
1274 let int_dtype = tensor.dtype();
1275 let bool_tensor = B::int_equal_elem(tensor, 0.into(), out_dtype);
1276 let bool_tensor = B::bool_not(bool_tensor);
1277 let sum = B::int_sum(B::bool_into_int(bool_tensor, int_dtype.into()));
1278 B::int_greater_elem(sum, 0.into(), out_dtype)
1279 }
1280
1281 /// Tests if any element in the int `tensor` evaluates to True along a given dimension `dim`.
1282 ///
1283 /// # Arguments
1284 ///
1285 /// * `tensor` - The tensor to test.
1286 /// * `dim` - The axis along which to test.
1287 ///
1288 /// # Returns
1289 ///
1290 /// A boolean tensor primitive with the same size as input `tensor`, except in the `dim` axis
1291 /// where the size is 1. The elem in the `dim` axis is True if any element along this dim in the input
1292 /// evaluates to True, False otherwise.
1293 fn int_any_dim(tensor: IntTensor<B>, dim: usize, out_dtype: BoolDType) -> BoolTensor<B> {
1294 let int_dtype = tensor.dtype();
1295 let bool_tensor = B::int_equal_elem(tensor, 0.into(), out_dtype);
1296 let bool_tensor = B::bool_not(bool_tensor);
1297 let sum = B::int_sum_dim(B::bool_into_int(bool_tensor, int_dtype.into()), dim);
1298 B::int_greater_elem(sum, 0.into(), out_dtype)
1299 }
1300
1301 /// Tests if all elements in the int `tensor` evaluate to True.
1302 ///
1303 /// # Arguments
1304 ///
1305 /// * `tensor` - The tensor to test.
1306 /// * `out_dtype` - The output tensor dtype.
1307 ///
1308 /// # Returns
1309 ///
1310 /// A boolean tensor primitive with a single element, True if all elements in the input tensor
1311 /// evaluate to True, False otherwise.
1312 fn int_all(tensor: IntTensor<B>, out_dtype: BoolDType) -> BoolTensor<B> {
1313 let int_dtype = tensor.dtype();
1314 let num_elems = tensor.shape().num_elements() as i64;
1315 let bool_tensor = B::int_equal_elem(tensor, 0.into(), out_dtype);
1316 let bool_tensor = B::bool_not(bool_tensor);
1317 let sum = B::int_sum(B::bool_into_int(bool_tensor, int_dtype.into()));
1318 B::int_equal_elem(sum, num_elems.into(), out_dtype)
1319 }
1320
1321 /// Tests if all elements in the int `tensor` evaluate to True along a given dimension `dim`.
1322 ///
1323 /// # Arguments
1324 ///
1325 /// * `tensor` - The tensor to test.
1326 /// * `dim` - The axis along which to test.
1327 /// * `out_dtype` - The output tensor dtype.
1328 ///
1329 /// # Returns
1330 ///
1331 /// A boolean tensor primitive with the same size as input `tensor`, except in the `dim` axis
1332 /// where the size is 1. The elem in the `dim` axis is True if all elements along this dim in the input
1333 /// evaluates to True, False otherwise.
1334 fn int_all_dim(tensor: IntTensor<B>, dim: usize, out_dtype: BoolDType) -> BoolTensor<B> {
1335 let int_dtype = tensor.dtype();
1336 let num_elems = tensor.shape()[dim] as i64;
1337 let bool_tensor = B::int_equal_elem(tensor, 0.into(), out_dtype);
1338 let bool_tensor = B::bool_not(bool_tensor);
1339 let sum = B::int_sum_dim(B::bool_into_int(bool_tensor, int_dtype.into()), dim);
1340 B::int_equal_elem(sum, num_elems.into(), out_dtype)
1341 }
1342
1343 /// Returns the signs of the int `tensor`.
1344 ///
1345 /// # Arguments
1346 ///
1347 /// * `tensor` - The tensor to extract the signs from.
1348 ///
1349 /// # Returns
1350 ///
1351 /// A tensor with the same shape as `tensor` containing the signs of the elements of `tensor`.
1352 fn int_sign(tensor: IntTensor<B>) -> IntTensor<B> {
1353 let dtype = tensor.dtype();
1354 let device = &tensor.device();
1355 let bool_dtype = get_device_settings::<B>(&tensor.device()).bool_dtype;
1356 let zeros = B::int_zeros(tensor.shape(), device, dtype.into());
1357 let less_than_zero = B::int_lower_elem(tensor.clone(), 0.into(), bool_dtype);
1358 let greater_than_zero = B::int_greater_elem(tensor, 0.into(), bool_dtype);
1359
1360 let mut result = B::int_mask_fill(zeros, less_than_zero, (-1).into());
1361 result = B::int_mask_fill(result, greater_than_zero, 1.into());
1362 result
1363 }
1364
1365 /// Broadcasts the int `tensor` to the given `shape`.
1366 fn int_expand(tensor: IntTensor<B>, shape: Shape) -> IntTensor<B>;
1367
1368 /// Sort the elements of the input `tensor` by value along a given dimension.
1369 ///
1370 /// This sort is unstable (i.e., may reorder equal elements).
1371 ///
1372 /// # Arguments
1373 ///
1374 /// * `tensor` - The input tensor.
1375 /// * `dim` - The axis along which to sort.
1376 /// * `descending` - The sorting order.
1377 ///
1378 /// # Returns
1379 ///
1380 /// A tensor with the same shape as the input tensor, where the elements are sorted by value.
1381 fn int_sort(tensor: IntTensor<B>, dim: usize, descending: bool) -> IntTensor<B> {
1382 let device = tensor.device();
1383 sort::<B, _, _, _>(
1384 tensor,
1385 dim,
1386 descending,
1387 device,
1388 |tensor| {
1389 let msg = "Failed to synchronously read tensor data. This operation is not supported until this backend has a GPU sorting implementation.";
1390 try_read_sync(B::int_into_data(tensor))
1391 .expect(msg)
1392 .expect(msg)
1393 },
1394 |data, device, _dtype| B::int_from_data(data, device),
1395 )
1396 }
1397
1398 /// Sort the elements of the input `tensor` by value along a given dimension.
1399 ///
1400 /// This sort is unstable (i.e., may reorder equal elements).
1401 ///
1402 /// # Arguments
1403 ///
1404 /// * `tensor` - The input tensor.
1405 /// * `dim` - The axis along which to sort.
1406 ///
1407 /// # Returns
1408 ///
1409 /// A tensor with the same shape as the input tensor and corresponding indices, where
1410 /// the elements are sorted by value and the indices map back to the original input tensor.
1411 fn int_sort_with_indices(
1412 tensor: IntTensor<B>,
1413 dim: usize,
1414 descending: bool,
1415 ) -> (IntTensor<B>, IntTensor<B>) {
1416 let dtype = tensor.dtype();
1417 let device = tensor.device();
1418 sort_with_indices::<B, _, _, _>(
1419 tensor,
1420 dim,
1421 descending,
1422 dtype.into(),
1423 device,
1424 |tensor| {
1425 let msg = "Failed to synchronously read tensor data. This operation is not supported until this backend has a GPU sorting implementation.";
1426 try_read_sync(B::int_into_data(tensor))
1427 .expect(msg)
1428 .expect(msg)
1429 },
1430 |data, device, _dtype| B::int_from_data(data, device),
1431 )
1432 }
1433
1434 /// Returns the indices that sort the elements of the input `tensor` by value
1435 /// along a given dimension.
1436 ///
1437 /// This sort is unstable (i.e., may reorder equal elements).
1438 ///
1439 /// # Arguments
1440 ///
1441 /// * `tensor` - The input tensor.
1442 /// * `dim` - The axis along which to sort.
1443 /// * `descending` - The sorting order.
1444 ///
1445 /// # Returns
1446 ///
1447 /// A tensor with the same shape as the input tensor the indices map back to the original input tensor.
1448 fn int_argsort(tensor: IntTensor<B>, dim: usize, descending: bool) -> IntTensor<B> {
1449 let dtype = tensor.dtype();
1450 let device = tensor.device();
1451 argsort::<B, _, _>(tensor, dim, descending, dtype.into(), device, |tensor| {
1452 let msg = "Failed to synchronously read tensor data. This operation is not supported until this backend has a GPU sorting implementation.";
1453 try_read_sync(B::int_into_data(tensor))
1454 .expect(msg)
1455 .expect(msg)
1456 })
1457 }
1458
1459 /// Bitwise AND operation for Int Tensors
1460 fn bitwise_and(lhs: IntTensor<B>, rhs: IntTensor<B>) -> IntTensor<B>;
1461
1462 /// Bitwise AND operation for Int Tensors with a scalar
1463 fn bitwise_and_scalar(lhs: IntTensor<B>, rhs: Scalar) -> IntTensor<B>;
1464
1465 /// Bitwise OR operation for Int Tensors
1466 fn bitwise_or(lhs: IntTensor<B>, rhs: IntTensor<B>) -> IntTensor<B>;
1467
1468 /// Bitwise OR operation for Int Tensors with a scalar
1469 fn bitwise_or_scalar(lhs: IntTensor<B>, rhs: Scalar) -> IntTensor<B>;
1470
1471 /// Bitwise XOR operation for Int Tensors
1472 fn bitwise_xor(lhs: IntTensor<B>, rhs: IntTensor<B>) -> IntTensor<B>;
1473
1474 /// Bitwise XOR operation for Int Tensors with a scalar
1475 fn bitwise_xor_scalar(lhs: IntTensor<B>, rhs: Scalar) -> IntTensor<B>;
1476
1477 /// Bitwise NOT operation for Int Tensors
1478 fn bitwise_not(tensor: IntTensor<B>) -> IntTensor<B>;
1479
1480 /// Bitwise left shift operation for Int Tensors
1481 fn bitwise_left_shift(lhs: IntTensor<B>, rhs: IntTensor<B>) -> IntTensor<B>;
1482
1483 /// Bitwise left shift operation for Int Tensors with a scalar
1484 fn bitwise_left_shift_scalar(lhs: IntTensor<B>, rhs: Scalar) -> IntTensor<B>;
1485
1486 /// Bitwise right shift operation for Int Tensors
1487 fn bitwise_right_shift(lhs: IntTensor<B>, rhs: IntTensor<B>) -> IntTensor<B>;
1488
1489 /// Bitwise right shift operation for Int Tensors with a scalar
1490 fn bitwise_right_shift_scalar(lhs: IntTensor<B>, rhs: Scalar) -> IntTensor<B>;
1491
1492 /// Converts a tensor to another integer data type.
1493 ///
1494 /// # Arguments
1495 ///
1496 /// * `tensor` - The tensor to convert.
1497 /// * `dtype` - The target data type.
1498 ///
1499 /// # Returns
1500 ///
1501 /// A tensor with the same values as `tensor` but in the target integer data type.
1502 fn int_cast(tensor: IntTensor<B>, dtype: IntDType) -> IntTensor<B>;
1503
1504 /// Unfold windows along a dimension.
1505 ///
1506 /// Returns a view of the tensor with all complete windows of size `size` in dimension `dim`;
1507 /// where windows are advanced by `step` at each index.
1508 ///
1509 /// The number of windows is `0` when `shape[dim] < size`, and otherwise
1510 /// `(shape[dim] - size) / step + 1`.
1511 ///
1512 /// # Arguments
1513 ///
1514 /// * `tensor` - The input tensor to unfold; of shape ``[pre=..., dim shape, post=...]``
1515 /// * `dim` - the selected dim.
1516 /// * `size` - the size of each unfolded window.
1517 /// * `step` - the step between each window.
1518 ///
1519 /// # Returns
1520 ///
1521 /// A tensor view with shape ``[pre=..., windows, size, post=...]``.
1522 fn int_unfold(tensor: IntTensor<B>, dim: usize, size: usize, step: usize) -> IntTensor<B>;
1523
1524 /// Pads a tensor with one `(before, after)` pair per dimension.
1525 fn int_pad(tensor: IntTensor<B>, padding: &[(usize, usize)], mode: PadMode) -> IntTensor<B> {
1526 super::pad::int_pad::<B>(tensor, padding, mode)
1527 }
1528}