Skip to main content

burn_cubecl/ops/
int_tensor.rs

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        // Check if all steps are 1
61        let all_steps_one = slices.iter().all(|info| info.step == 1);
62
63        if all_steps_one {
64            // Use optimized slice for step=1
65            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            // Use slice with steps kernel
74            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        // One pass, rather than the default's TopK followed by ArgTopK: the reduction
415        // already carries both halves, and these kernels are memory bound. Indices take
416        // the input dtype, matching `int_argtopk`.
417        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        // Indices take the input dtype, matching `int_argmax`.
433        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        // Indices take the input dtype, matching `int_argmin`.
449        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    // TODO
707    // fn int_powi(lhs: IntTensor<Self>, rhs: IntTensor<Self>) -> IntTensor<Self> {
708    //     todo!()
709    // }
710
711    // fn int_powi_scalar_impl(lhs: IntTensor<Self>, rhs: Scalar) -> IntTensor<Self> {
712    //     todo!()
713    // }
714}