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