1use self::unary_basic_int::BasicIntUnaryKind;
2use burn_backend::cubecl::dtype_to_storage_type;
3
4use super::{expand, numeric, permute, unfold};
5use crate::kernel::prng::{random_bernoulli, random_normal, random_uniform};
6use crate::kernel::{
7 BitwiseShlOp, BitwiseShrOp, NumericUnaryOp, NumericUnaryOpFamily, launch_binop_int,
8 launch_scalar_binop_int, launch_unary_numeric, reduce, unary_basic_int,
9};
10use crate::{
11 CubeBackend, CubeRuntime,
12 kernel::{
13 self,
14 matmul::{MatmulStrategy, matmul},
15 },
16};
17use burn_backend::tensor::{BoolTensor, Device, FloatTensor, IntTensor};
18use burn_backend::{DType, IntDType, Slice, ops::IntTensorOps};
19use burn_backend::{Distribution, ElementConversion, Shape, TensorData, get_device_settings};
20use burn_backend::{ExecutionError, Scalar};
21use burn_std::{BoolDType, FloatDType};
22use cubecl::frontend::Numeric;
23use cubecl::prelude::*;
24use cubek::reduce::components::instructions::ReduceOperationConfig;
25use std::ops::Range;
26
27impl<R: CubeRuntime> IntTensorOps<Self> for CubeBackend<R> {
28 fn int_empty(shape: Shape, device: &Device<Self>, dtype: IntDType) -> IntTensor<Self> {
29 let dtype = dtype.into();
30 super::empty(shape, device, dtype)
31 }
32
33 async fn int_into_data(tensor: IntTensor<Self>) -> Result<TensorData, ExecutionError> {
34 super::into_data(tensor).await
35 }
36
37 fn int_from_data(data: TensorData, device: &Device<Self>) -> IntTensor<Self> {
38 match data.dtype {
39 DType::I64
40 | DType::I32
41 | DType::I16
42 | DType::I8
43 | DType::U64
44 | DType::U32
45 | DType::U16
46 | DType::U8 => super::from_data(data, device),
47 _ => unimplemented!("Unsupported dtype for `int_from_data`"),
48 }
49 }
50
51 fn int_to_device(tensor: IntTensor<Self>, device: &Device<Self>) -> IntTensor<Self> {
52 super::to_device(tensor, device)
53 }
54
55 fn int_reshape(tensor: IntTensor<Self>, shape: Shape) -> IntTensor<Self> {
56 super::reshape(tensor, shape)
57 }
58
59 fn int_slice(tensor: IntTensor<Self>, slices: &[Slice]) -> IntTensor<Self> {
60 let all_steps_one = slices.iter().all(|info| info.step == 1);
62
63 if all_steps_one {
64 let simple_ranges: Vec<Range<usize>> = slices
66 .iter()
67 .enumerate()
68 .map(|(i, slice)| slice.to_range(tensor.meta.shape()[i]))
69 .collect();
70
71 kernel::slice(tensor, &simple_ranges)
72 } else {
73 kernel::slice_with_steps(tensor, slices)
75 }
76 }
77
78 fn int_slice_assign(
79 tensor: IntTensor<Self>,
80 ranges: &[Slice],
81 value: IntTensor<Self>,
82 ) -> IntTensor<Self> {
83 kernel::slice_assign(tensor, ranges, value)
84 }
85
86 fn int_matmul(lhs: IntTensor<Self>, rhs: IntTensor<Self>) -> IntTensor<Self> {
87 let dtype = lhs.dtype;
88 matmul(lhs, rhs, None, MatmulStrategy::default(), dtype).unwrap()
89 }
90
91 fn int_mask_where(
92 tensor: IntTensor<Self>,
93 mask: BoolTensor<Self>,
94 value: IntTensor<Self>,
95 ) -> IntTensor<Self> {
96 let bool_dtype = mask.dtype;
97 kernel::mask_where_auto(tensor, mask, value, bool_dtype)
98 }
99
100 fn int_mask_fill(
101 tensor: IntTensor<Self>,
102 mask: BoolTensor<Self>,
103 value: Scalar,
104 ) -> IntTensor<Self> {
105 let dtype = tensor.dtype;
106 let bool_dtype = mask.dtype;
107 kernel::mask_fill_auto(
108 tensor,
109 mask,
110 InputScalar::new(value, dtype_to_storage_type(dtype)),
111 bool_dtype,
112 )
113 }
114
115 fn int_gather(
116 dim: usize,
117 tensor: IntTensor<Self>,
118 indices: IntTensor<Self>,
119 ) -> IntTensor<Self> {
120 kernel::gather(dim, tensor, indices)
121 }
122
123 fn int_scatter_add(
124 dim: usize,
125 tensor: IntTensor<Self>,
126 indices: IntTensor<Self>,
127 value: IntTensor<Self>,
128 ) -> IntTensor<Self> {
129 kernel::scatter(dim, tensor, indices, value, false)
130 }
131
132 fn int_scatter_nd(
133 data: IntTensor<Self>,
134 indices: IntTensor<Self>,
135 values: IntTensor<Self>,
136 reduction: burn_backend::tensor::IndexingUpdateOp,
137 ) -> IntTensor<Self> {
138 kernel::scatter_nd(data, indices, values, reduction)
139 }
140
141 fn int_gather_nd(data: IntTensor<Self>, indices: IntTensor<Self>) -> IntTensor<Self> {
142 kernel::gather_nd(data, indices)
143 }
144
145 fn int_select(
146 tensor: IntTensor<Self>,
147 dim: usize,
148 indices: IntTensor<Self>,
149 ) -> IntTensor<Self> {
150 kernel::select(tensor, dim, indices)
151 }
152
153 fn int_select_add(
154 tensor: IntTensor<Self>,
155 dim: usize,
156 indices: IntTensor<Self>,
157 value: IntTensor<Self>,
158 ) -> IntTensor<Self> {
159 kernel::select_assign(tensor, dim, indices, value, false)
160 }
161
162 fn int_equal(
163 lhs: IntTensor<Self>,
164 rhs: IntTensor<Self>,
165 out_dtype: BoolDType,
166 ) -> BoolTensor<Self> {
167 kernel::equal(lhs, rhs, out_dtype.into())
168 }
169
170 fn int_equal_elem(lhs: IntTensor<Self>, rhs: Scalar, out_dtype: BoolDType) -> BoolTensor<Self> {
171 let dtype = lhs.dtype;
172 kernel::equal_elem(
173 lhs,
174 InputScalar::new(rhs, dtype_to_storage_type(dtype)),
175 out_dtype.into(),
176 )
177 }
178
179 fn int_greater(
180 lhs: IntTensor<Self>,
181 rhs: IntTensor<Self>,
182 out_dtype: BoolDType,
183 ) -> BoolTensor<Self> {
184 kernel::greater(lhs, rhs, out_dtype.into())
185 }
186
187 fn int_greater_elem(
188 lhs: IntTensor<Self>,
189 rhs: Scalar,
190 out_dtype: BoolDType,
191 ) -> BoolTensor<Self> {
192 let dtype = lhs.dtype;
193 kernel::greater_elem(
194 lhs,
195 InputScalar::new(rhs, dtype_to_storage_type(dtype)),
196 out_dtype.into(),
197 )
198 }
199
200 fn int_greater_equal(
201 lhs: IntTensor<Self>,
202 rhs: IntTensor<Self>,
203 out_dtype: BoolDType,
204 ) -> BoolTensor<Self> {
205 kernel::greater_equal(lhs, rhs, out_dtype.into())
206 }
207
208 fn int_greater_equal_elem(
209 lhs: IntTensor<Self>,
210 rhs: Scalar,
211 out_dtype: BoolDType,
212 ) -> BoolTensor<Self> {
213 let dtype = lhs.dtype;
214 kernel::greater_equal_elem(
215 lhs,
216 InputScalar::new(rhs, dtype_to_storage_type(dtype)),
217 out_dtype.into(),
218 )
219 }
220
221 fn int_lower(
222 lhs: IntTensor<Self>,
223 rhs: IntTensor<Self>,
224 out_dtype: BoolDType,
225 ) -> BoolTensor<Self> {
226 kernel::lower(lhs, rhs, out_dtype.into())
227 }
228
229 fn int_lower_elem(lhs: IntTensor<Self>, rhs: Scalar, out_dtype: BoolDType) -> BoolTensor<Self> {
230 let dtype = lhs.dtype;
231 kernel::lower_elem(
232 lhs,
233 InputScalar::new(rhs, dtype_to_storage_type(dtype)),
234 out_dtype.into(),
235 )
236 }
237
238 fn int_lower_equal(
239 lhs: IntTensor<Self>,
240 rhs: IntTensor<Self>,
241 out_dtype: BoolDType,
242 ) -> BoolTensor<Self> {
243 kernel::lower_equal(lhs, rhs, out_dtype.into())
244 }
245
246 fn int_lower_equal_elem(
247 lhs: IntTensor<Self>,
248 rhs: Scalar,
249 out_dtype: BoolDType,
250 ) -> BoolTensor<Self> {
251 let dtype = lhs.dtype;
252 kernel::lower_equal_elem(
253 lhs,
254 InputScalar::new(rhs, dtype_to_storage_type(dtype)),
255 out_dtype.into(),
256 )
257 }
258
259 fn int_add(lhs: IntTensor<Self>, rhs: IntTensor<Self>) -> IntTensor<Self> {
260 numeric::add(lhs, rhs)
261 }
262
263 fn int_add_scalar(lhs: IntTensor<Self>, rhs: Scalar) -> IntTensor<Self> {
264 let dtype = lhs.dtype;
265 numeric::add_scalar(lhs, InputScalar::new(rhs, dtype_to_storage_type(dtype)))
266 }
267
268 fn int_sub(lhs: IntTensor<Self>, rhs: IntTensor<Self>) -> IntTensor<Self> {
269 numeric::sub(lhs, rhs)
270 }
271
272 fn int_sub_scalar(lhs: IntTensor<Self>, rhs: Scalar) -> IntTensor<Self> {
273 let dtype = lhs.dtype;
274 numeric::sub_scalar(lhs, InputScalar::new(rhs, dtype_to_storage_type(dtype)))
275 }
276
277 fn int_mul(lhs: IntTensor<Self>, rhs: IntTensor<Self>) -> IntTensor<Self> {
278 numeric::mul(lhs, rhs)
279 }
280
281 fn int_mul_scalar(lhs: IntTensor<Self>, rhs: Scalar) -> IntTensor<Self> {
282 let dtype = lhs.dtype;
283 numeric::mul_scalar(lhs, InputScalar::new(rhs, dtype_to_storage_type(dtype)))
284 }
285
286 fn int_div(lhs: IntTensor<Self>, rhs: IntTensor<Self>) -> IntTensor<Self> {
287 numeric::div(lhs, rhs)
288 }
289
290 fn int_div_scalar(lhs: IntTensor<Self>, rhs: Scalar) -> IntTensor<Self> {
291 let dtype = lhs.dtype;
292 numeric::div_scalar(lhs, InputScalar::new(rhs, dtype_to_storage_type(dtype)))
293 }
294
295 fn int_remainder(lhs: IntTensor<Self>, rhs: IntTensor<Self>) -> IntTensor<Self> {
296 numeric::remainder(lhs, rhs)
297 }
298
299 fn int_remainder_scalar(lhs: IntTensor<Self>, rhs: Scalar) -> IntTensor<Self> {
300 let dtype = lhs.dtype;
301 numeric::remainder_scalar(lhs, InputScalar::new(rhs, dtype_to_storage_type(dtype)))
302 }
303
304 fn int_zeros(shape: Shape, device: &Device<Self>, dtype: IntDType) -> IntTensor<Self> {
305 let dtype = dtype.into();
306 numeric::zeros(device.clone(), shape, dtype)
307 }
308
309 fn int_ones(shape: Shape, device: &Device<Self>, dtype: IntDType) -> IntTensor<Self> {
310 let dtype = dtype.into();
311 numeric::ones(device.clone(), shape, dtype)
312 }
313
314 fn int_full(
315 shape: Shape,
316 fill_value: Scalar,
317 device: &Device<Self>,
318 dtype: IntDType,
319 ) -> IntTensor<Self> {
320 let dtype: DType = dtype.into();
321 let client = R::client(device);
322 numeric::full_device_dtype(
323 client,
324 shape,
325 device.clone(),
326 InputScalar::new(fill_value, dtype_to_storage_type(dtype)),
327 dtype,
328 )
329 }
330
331 fn int_sum(tensor: IntTensor<Self>) -> IntTensor<Self> {
332 reduce::sum_fallback(tensor, Default::default()).unwrap()
333 }
334
335 fn int_sum_dim(tensor: IntTensor<Self>, dim: usize) -> IntTensor<Self> {
336 reduce::reduce_dim(
337 tensor,
338 None,
339 dim,
340 Default::default(),
341 ReduceOperationConfig::Sum,
342 )
343 .unwrap()
344 }
345
346 fn int_any(tensor: IntTensor<Self>, out_dtype: BoolDType) -> BoolTensor<Self> {
347 reduce::reduce_logical(tensor, None, ReduceOperationConfig::Any, out_dtype)
348 }
349
350 fn int_any_dim(tensor: IntTensor<Self>, dim: usize, out_dtype: BoolDType) -> BoolTensor<Self> {
351 reduce::reduce_logical(tensor, Some(dim), ReduceOperationConfig::Any, out_dtype)
352 }
353
354 fn int_all(tensor: IntTensor<Self>, out_dtype: BoolDType) -> BoolTensor<Self> {
355 reduce::reduce_logical(tensor, None, ReduceOperationConfig::All, out_dtype)
356 }
357
358 fn int_all_dim(tensor: IntTensor<Self>, dim: usize, out_dtype: BoolDType) -> BoolTensor<Self> {
359 reduce::reduce_logical(tensor, Some(dim), ReduceOperationConfig::All, out_dtype)
360 }
361
362 fn int_prod(tensor: IntTensor<Self>) -> IntTensor<Self> {
363 reduce::reduce(
364 tensor,
365 None,
366 Default::default(),
367 ReduceOperationConfig::Prod,
368 )
369 .unwrap()
370 }
371
372 fn int_prod_dim(tensor: IntTensor<Self>, dim: usize) -> IntTensor<Self> {
373 reduce::reduce_dim(
374 tensor,
375 None,
376 dim,
377 Default::default(),
378 ReduceOperationConfig::Prod,
379 )
380 .unwrap()
381 }
382
383 fn int_max(tensor: IntTensor<Self>) -> IntTensor<Self> {
384 reduce::reduce(tensor, None, Default::default(), ReduceOperationConfig::Max).unwrap()
385 }
386
387 fn int_max_dim(tensor: IntTensor<Self>, dim: usize) -> IntTensor<Self> {
388 reduce::reduce_dim(
389 tensor,
390 None,
391 dim,
392 Default::default(),
393 ReduceOperationConfig::Max,
394 )
395 .unwrap()
396 }
397
398 fn int_topk(tensor: IntTensor<Self>, dim: usize, k: usize) -> IntTensor<Self> {
399 reduce::reduce_dim(
400 tensor,
401 None,
402 dim,
403 Default::default(),
404 ReduceOperationConfig::TopK(k),
405 )
406 .unwrap()
407 }
408
409 fn int_topk_with_indices(
410 tensor: IntTensor<Self>,
411 dim: usize,
412 k: usize,
413 ) -> (IntTensor<Self>, IntTensor<Self>) {
414 let dtype = tensor.dtype;
418 reduce::reduce_dim_with_indices(
419 tensor,
420 dtype,
421 dim,
422 Default::default(),
423 ReduceOperationConfig::TopK(k),
424 )
425 .unwrap()
426 }
427
428 fn int_max_dim_with_indices(
429 tensor: IntTensor<Self>,
430 dim: usize,
431 ) -> (IntTensor<Self>, IntTensor<Self>) {
432 let dtype = tensor.dtype;
434 reduce::reduce_dim_with_indices(
435 tensor,
436 dtype,
437 dim,
438 Default::default(),
439 ReduceOperationConfig::Max,
440 )
441 .unwrap()
442 }
443
444 fn int_min_dim_with_indices(
445 tensor: IntTensor<Self>,
446 dim: usize,
447 ) -> (IntTensor<Self>, IntTensor<Self>) {
448 let dtype = tensor.dtype;
450 reduce::reduce_dim_with_indices(
451 tensor,
452 dtype,
453 dim,
454 Default::default(),
455 ReduceOperationConfig::Min,
456 )
457 .unwrap()
458 }
459
460 fn int_max_abs(tensor: IntTensor<Self>) -> IntTensor<Self> {
461 reduce::reduce(
462 tensor,
463 None,
464 Default::default(),
465 ReduceOperationConfig::MaxAbs,
466 )
467 .unwrap()
468 }
469
470 fn int_max_abs_dim(tensor: IntTensor<Self>, dim: usize) -> IntTensor<Self> {
471 reduce::reduce_dim(
472 tensor,
473 None,
474 dim,
475 Default::default(),
476 ReduceOperationConfig::MaxAbs,
477 )
478 .unwrap()
479 }
480
481 fn int_min(tensor: IntTensor<Self>) -> IntTensor<Self> {
482 reduce::reduce(tensor, None, Default::default(), ReduceOperationConfig::Min).unwrap()
483 }
484
485 fn int_min_dim(tensor: IntTensor<Self>, dim: usize) -> IntTensor<Self> {
486 reduce::reduce_dim(
487 tensor,
488 None,
489 dim,
490 Default::default(),
491 ReduceOperationConfig::Min,
492 )
493 .unwrap()
494 }
495
496 fn int_mean_dim(tensor: IntTensor<Self>, dim: usize) -> IntTensor<Self> {
497 reduce::reduce_dim(
498 tensor,
499 None,
500 dim,
501 Default::default(),
502 ReduceOperationConfig::Mean,
503 )
504 .unwrap()
505 }
506
507 fn int_cumsum(tensor: IntTensor<Self>, dim: usize) -> IntTensor<Self> {
508 numeric::cumsum(tensor, dim)
509 }
510
511 fn int_cumprod(tensor: IntTensor<Self>, dim: usize) -> IntTensor<Self> {
512 numeric::cumprod(tensor, dim)
513 }
514
515 fn int_cummin(tensor: IntTensor<Self>, dim: usize) -> IntTensor<Self> {
516 numeric::cummin(tensor, dim)
517 }
518
519 fn int_cummax(tensor: IntTensor<Self>, dim: usize) -> IntTensor<Self> {
520 numeric::cummax(tensor, dim)
521 }
522
523 fn int_argmax(tensor: IntTensor<Self>, dim: usize) -> IntTensor<Self> {
524 let dtype = tensor.dtype;
525 reduce::reduce_dim(
526 tensor,
527 Some(dtype),
528 dim,
529 Default::default(),
530 ReduceOperationConfig::ArgMax,
531 )
532 .unwrap()
533 }
534
535 fn int_argtopk(tensor: IntTensor<Self>, dim: usize, k: usize) -> IntTensor<Self> {
536 let dtype = tensor.dtype;
537 reduce::reduce_dim(
538 tensor,
539 Some(dtype),
540 dim,
541 Default::default(),
542 ReduceOperationConfig::ArgTopK(k),
543 )
544 .unwrap()
545 }
546
547 fn int_argmin(tensor: IntTensor<Self>, dim: usize) -> IntTensor<Self> {
548 let dtype = tensor.dtype;
549 reduce::reduce_dim(
550 tensor,
551 Some(dtype),
552 dim,
553 Default::default(),
554 ReduceOperationConfig::ArgMin,
555 )
556 .unwrap()
557 }
558
559 fn int_clamp(tensor: IntTensor<Self>, min: Scalar, max: Scalar) -> IntTensor<Self> {
560 let dtype = tensor.dtype;
561 kernel::clamp(
562 tensor,
563 InputScalar::new(min, dtype_to_storage_type(dtype)),
564 InputScalar::new(max, dtype_to_storage_type(dtype)),
565 )
566 }
567
568 fn int_abs(tensor: IntTensor<Self>) -> IntTensor<Self> {
569 struct Abs;
570
571 #[cube]
572 impl<T: Numeric, N: Size> NumericUnaryOp<T, N> for Abs {
573 type Options = ();
574
575 fn execute(input: Vector<T, N>, _options: &Self::Options) -> Vector<T, N> {
576 Vector::abs(input)
577 }
578 }
579
580 impl NumericUnaryOpFamily for Abs {
581 type Options = ();
582 type Unary<T: Numeric, N: Size> = Self;
583 }
584
585 launch_unary_numeric::<R, Abs, _>(tensor, |_| ())
586 }
587
588 fn int_sign(tensor: IntTensor<Self>) -> IntTensor<Self> {
589 unary_basic_int::launch::<R, _>(tensor, |_| BasicIntUnaryKind::Sign)
590 }
591
592 fn int_into_float(tensor: IntTensor<Self>, out_dtype: FloatDType) -> FloatTensor<Self> {
593 kernel::cast(tensor, out_dtype.into())
594 }
595
596 fn int_swap_dims(mut tensor: IntTensor<Self>, dim1: usize, dim2: usize) -> IntTensor<Self> {
597 tensor.meta.swap(dim1, dim2);
598
599 tensor
600 }
601
602 fn int_repeat_dim(tensor: IntTensor<Self>, dim: usize, times: usize) -> IntTensor<Self> {
603 kernel::repeat_dim(tensor, dim, times)
604 }
605
606 fn int_random(
607 shape: Shape,
608 distribution: Distribution,
609 device: &Device<Self>,
610 dtype: IntDType,
611 ) -> IntTensor<Self> {
612 let dtype = dtype.into();
613 match distribution {
614 Distribution::Default => random_uniform(shape, device, 0., 255., dtype),
615 Distribution::Uniform(low, high) => {
616 random_uniform(shape, device, low.elem(), high.elem(), dtype)
617 }
618 Distribution::Bernoulli(prob) => random_bernoulli(shape, device, prob as f32, dtype),
619 Distribution::Normal(mean, std) => {
620 random_normal(shape, device, mean.elem(), std.elem(), dtype)
621 }
622 }
623 }
624
625 fn int_permute(tensor: IntTensor<Self>, axes: &[usize]) -> IntTensor<Self> {
626 permute(tensor, axes)
627 }
628
629 fn int_expand(tensor: IntTensor<Self>, shape: Shape) -> IntTensor<Self> {
630 expand(tensor, shape)
631 }
632
633 fn int_flip(tensor: IntTensor<Self>, axes: &[usize]) -> IntTensor<Self> {
634 let bool_dtype = get_device_settings::<Self>(&tensor.device).bool_dtype;
635 kernel::flip(tensor, axes, bool_dtype.into())
636 }
637
638 fn bitwise_and(lhs: IntTensor<Self>, rhs: IntTensor<Self>) -> IntTensor<Self> {
639 numeric::bitwise_and(lhs, rhs)
640 }
641
642 fn bitwise_and_scalar(lhs: IntTensor<Self>, rhs: Scalar) -> IntTensor<Self> {
643 let dtype = lhs.dtype;
644 numeric::bitwise_and_scalar(lhs, InputScalar::new(rhs, dtype_to_storage_type(dtype)))
645 }
646
647 fn bitwise_or(lhs: IntTensor<Self>, rhs: IntTensor<Self>) -> IntTensor<Self> {
648 numeric::bitwise_or(lhs, rhs)
649 }
650
651 fn bitwise_or_scalar(lhs: IntTensor<Self>, rhs: Scalar) -> IntTensor<Self> {
652 let dtype = lhs.dtype;
653 numeric::bitwise_or_scalar(lhs, InputScalar::new(rhs, dtype_to_storage_type(dtype)))
654 }
655
656 fn bitwise_xor(lhs: IntTensor<Self>, rhs: IntTensor<Self>) -> IntTensor<Self> {
657 numeric::bitwise_xor(lhs, rhs)
658 }
659
660 fn bitwise_xor_scalar(lhs: IntTensor<Self>, rhs: Scalar) -> IntTensor<Self> {
661 let dtype = lhs.dtype;
662 numeric::bitwise_xor_scalar(lhs, InputScalar::new(rhs, dtype_to_storage_type(dtype)))
663 }
664
665 fn bitwise_not(tensor: IntTensor<Self>) -> IntTensor<Self> {
666 unary_basic_int::launch::<R, _>(tensor, |_| BasicIntUnaryKind::BitwiseNot)
667 }
668
669 fn bitwise_left_shift(lhs: IntTensor<Self>, rhs: IntTensor<Self>) -> IntTensor<Self> {
670 launch_binop_int::<R, kernel::BitwiseShlOp>(lhs, rhs)
671 }
672
673 fn bitwise_left_shift_scalar(lhs: IntTensor<Self>, rhs: Scalar) -> IntTensor<Self> {
674 let dtype = lhs.dtype;
675 launch_scalar_binop_int::<R, BitwiseShlOp>(
676 lhs,
677 InputScalar::new(rhs, dtype_to_storage_type(dtype)),
678 )
679 }
680
681 fn bitwise_right_shift(lhs: IntTensor<Self>, rhs: IntTensor<Self>) -> IntTensor<Self> {
682 launch_binop_int::<R, BitwiseShrOp>(lhs, rhs)
683 }
684
685 fn bitwise_right_shift_scalar(lhs: IntTensor<Self>, rhs: Scalar) -> IntTensor<Self> {
686 let dtype = lhs.dtype;
687 launch_scalar_binop_int::<R, BitwiseShrOp>(
688 lhs,
689 InputScalar::new(rhs, dtype_to_storage_type(dtype)),
690 )
691 }
692
693 fn int_cast(tensor: IntTensor<Self>, dtype: IntDType) -> IntTensor<Self> {
694 kernel::cast(tensor, dtype.into())
695 }
696
697 fn int_unfold(
698 tensor: FloatTensor<Self>,
699 dim: usize,
700 size: usize,
701 step: usize,
702 ) -> FloatTensor<Self> {
703 unfold(tensor, dim, size, step)
704 }
705
706 }