Skip to main content

burn_cubecl/ops/
tensor.rs

1use super::{expand, numeric, permute, unfold};
2use crate::CubeBackend;
3use crate::CubeDevice;
4use crate::kernel::matmul::{MatmulStrategy, matmul};
5use crate::kernel::prng::{random_bernoulli, random_normal, random_uniform};
6use crate::kernel::unary_basic::BasicFloatUnaryKind;
7use crate::kernel::{
8    self, FloatUnaryOp, FloatUnaryOpFamily, launch_unary_float, reduce, unary_basic,
9};
10use burn_backend::cubecl::dtype_to_storage_type;
11use burn_backend::ops::GridSampleOptions;
12use burn_backend::tensor::{BoolTensor, Device, FloatTensor, IntTensor};
13use burn_backend::{DType, ElementConversion, FloatDType, Slice};
14use burn_backend::{Distribution, Shape, TensorData, ops::FloatTensorOps};
15use burn_backend::{ExecutionError, Scalar, get_or_init_device_settings};
16use burn_std::{BoolDType, IntDType};
17use cubecl::prelude::*;
18use cubek::reduce::components::instructions::ReduceOperationConfig;
19use std::ops::Range;
20
21impl FloatTensorOps<Self> for CubeBackend {
22    #[cfg_attr(feature = "tracing", tracing::instrument(
23        level="trace",
24        skip(data),
25        fields(shape = ?data.shape(), dtype = ?data.dtype())
26    ))]
27    fn float_from_data(data: TensorData, device: &Device<Self>) -> FloatTensor<Self> {
28        match data.dtype() {
29            DType::F64 | DType::F32 | DType::F16 | DType::BF16 => super::from_data(data, device),
30            _ => unimplemented!("Unsupported dtype for `float_from_data`"),
31        }
32    }
33
34    fn float_random(
35        shape: Shape,
36        distribution: Distribution,
37        device: &Device<Self>,
38        dtype: FloatDType,
39    ) -> FloatTensor<Self> {
40        let dtype = dtype.into();
41        match distribution {
42            Distribution::Default => random_uniform(shape, device, 0., 1., dtype),
43            Distribution::Uniform(low, high) => {
44                random_uniform(shape, device, low.elem(), high.elem(), dtype)
45            }
46            Distribution::Bernoulli(prob) => random_bernoulli(shape, device, prob as f32, dtype),
47            Distribution::Normal(mean, std) => {
48                random_normal(shape, device, mean.elem(), std.elem(), dtype)
49            }
50        }
51    }
52
53    #[cfg_attr(feature = "tracing", tracing::instrument(
54        level="trace",
55        skip(tensor),
56        fields(from = ?tensor.device, meta = ?tensor.meta, dtype = ?tensor.dtype)
57    ))]
58    async fn float_into_data(tensor: FloatTensor<Self>) -> Result<TensorData, ExecutionError> {
59        super::into_data(tensor).await
60    }
61
62    #[cfg_attr(feature = "tracing", tracing::instrument(
63        level="trace",
64        skip(tensor),
65        fields(from = ?tensor.device, meta = ?tensor.meta, dtype = ?tensor.dtype)
66    ))]
67    fn float_to_device(tensor: FloatTensor<Self>, device: &Device<Self>) -> FloatTensor<Self> {
68        super::to_device(tensor, device)
69    }
70
71    fn float_empty(shape: Shape, device: &Device<Self>, dtype: FloatDType) -> FloatTensor<Self> {
72        let dtype = dtype.into();
73        super::empty(shape, device, dtype)
74    }
75
76    fn float_add(lhs: FloatTensor<Self>, rhs: FloatTensor<Self>) -> FloatTensor<Self> {
77        numeric::add(lhs, rhs)
78    }
79
80    fn float_add_scalar(lhs: FloatTensor<Self>, rhs: Scalar) -> FloatTensor<Self> {
81        let dtype = lhs.dtype;
82        numeric::add_scalar(lhs, InputScalar::new(rhs, dtype_to_storage_type(dtype)))
83    }
84
85    fn float_zeros(shape: Shape, device: &Device<Self>, dtype: FloatDType) -> FloatTensor<Self> {
86        let dtype = dtype.into();
87        numeric::zeros(device.clone(), shape, dtype)
88    }
89
90    fn float_full(
91        shape: Shape,
92        fill_value: Scalar,
93        device: &CubeDevice,
94        dtype: FloatDType,
95    ) -> FloatTensor<Self> {
96        let dtype: DType = dtype.into();
97        let client = device.client();
98        numeric::full_device_dtype(
99            client,
100            shape,
101            device.clone(),
102            InputScalar::new(fill_value, dtype_to_storage_type(dtype)),
103            dtype,
104        )
105    }
106
107    fn float_ones(shape: Shape, device: &Device<Self>, dtype: FloatDType) -> FloatTensor<Self> {
108        let dtype = dtype.into();
109        numeric::ones(device.clone(), shape, dtype)
110    }
111
112    fn float_sub(lhs: FloatTensor<Self>, rhs: FloatTensor<Self>) -> FloatTensor<Self> {
113        numeric::sub(lhs, rhs)
114    }
115
116    fn float_sub_scalar(lhs: FloatTensor<Self>, rhs: Scalar) -> FloatTensor<Self> {
117        let dtype = lhs.dtype;
118        numeric::sub_scalar(lhs, InputScalar::new(rhs, dtype_to_storage_type(dtype)))
119    }
120
121    fn float_mul(lhs: FloatTensor<Self>, rhs: FloatTensor<Self>) -> FloatTensor<Self> {
122        numeric::mul(lhs, rhs)
123    }
124
125    fn float_mul_scalar(lhs: FloatTensor<Self>, rhs: Scalar) -> FloatTensor<Self> {
126        let dtype = lhs.dtype;
127        numeric::mul_scalar(lhs, InputScalar::new(rhs, dtype_to_storage_type(dtype)))
128    }
129
130    fn float_div(lhs: FloatTensor<Self>, rhs: FloatTensor<Self>) -> FloatTensor<Self> {
131        numeric::div(lhs, rhs)
132    }
133
134    fn float_div_scalar(lhs: FloatTensor<Self>, rhs: Scalar) -> FloatTensor<Self> {
135        let dtype = lhs.dtype;
136        numeric::div_scalar(lhs, InputScalar::new(rhs, dtype_to_storage_type(dtype)))
137    }
138
139    fn float_remainder(lhs: FloatTensor<Self>, rhs: FloatTensor<Self>) -> FloatTensor<Self> {
140        numeric::remainder(lhs, rhs)
141    }
142
143    fn float_remainder_scalar(lhs: FloatTensor<Self>, rhs: Scalar) -> FloatTensor<Self> {
144        let dtype = lhs.dtype;
145        numeric::remainder_scalar(lhs, InputScalar::new(rhs, dtype_to_storage_type(dtype)))
146    }
147
148    fn float_matmul(lhs: FloatTensor<Self>, rhs: FloatTensor<Self>) -> FloatTensor<Self> {
149        let dtype = lhs.dtype;
150        matmul(lhs, rhs, None, MatmulStrategy::default(), dtype).unwrap()
151    }
152
153    fn float_cross(
154        lhs: FloatTensor<Self>,
155        rhs: FloatTensor<Self>,
156        dim: usize,
157    ) -> FloatTensor<Self> {
158        kernel::cross(lhs, rhs, dim)
159    }
160
161    fn float_swap_dims(tensor: FloatTensor<Self>, dim1: usize, dim2: usize) -> FloatTensor<Self> {
162        super::swap_dims(tensor, dim1, dim2)
163    }
164
165    fn float_reshape(tensor: FloatTensor<Self>, shape: Shape) -> FloatTensor<Self> {
166        super::reshape(tensor, shape)
167    }
168
169    fn float_gather(
170        dim: usize,
171        tensor: FloatTensor<Self>,
172        indices: IntTensor<Self>,
173    ) -> FloatTensor<Self> {
174        kernel::gather(dim, tensor, indices)
175    }
176
177    fn float_scatter(
178        dim: usize,
179        tensor: FloatTensor<Self>,
180        indices: IntTensor<Self>,
181        value: FloatTensor<Self>,
182        update: burn_backend::tensor::IndexingUpdateOp,
183    ) -> FloatTensor<Self> {
184        match update {
185            burn_backend::tensor::IndexingUpdateOp::Assign => {
186                kernel::scatter_assign(dim, tensor, indices, value)
187            }
188            burn_backend::tensor::IndexingUpdateOp::Add => {
189                kernel::scatter(dim, tensor, indices, value, false)
190            }
191            burn_backend::tensor::IndexingUpdateOp::Mul => {
192                kernel::scatter_mul(dim, tensor, indices, value)
193            }
194            burn_backend::tensor::IndexingUpdateOp::Min => {
195                kernel::scatter_min(dim, tensor, indices, value)
196            }
197            burn_backend::tensor::IndexingUpdateOp::Max => {
198                kernel::scatter_max(dim, tensor, indices, value)
199            }
200        }
201    }
202
203    fn float_scatter_nd(
204        data: FloatTensor<Self>,
205        indices: IntTensor<Self>,
206        values: FloatTensor<Self>,
207        reduction: burn_backend::tensor::IndexingUpdateOp,
208    ) -> FloatTensor<Self> {
209        kernel::scatter_nd(data, indices, values, reduction)
210    }
211
212    fn float_gather_nd(data: FloatTensor<Self>, indices: IntTensor<Self>) -> FloatTensor<Self> {
213        kernel::gather_nd(data, indices)
214    }
215
216    fn float_select(
217        tensor: FloatTensor<Self>,
218        dim: usize,
219        indices: IntTensor<Self>,
220    ) -> FloatTensor<Self> {
221        kernel::select(tensor, dim, indices)
222    }
223
224    fn float_select_assign(
225        tensor: FloatTensor<Self>,
226        dim: usize,
227        indices: IntTensor<Self>,
228        value: FloatTensor<Self>,
229        update: burn_backend::tensor::IndexingUpdateOp,
230    ) -> FloatTensor<Self> {
231        match update {
232            burn_backend::tensor::IndexingUpdateOp::Assign => {
233                kernel::select_assign_replace(tensor, dim, indices, value)
234            }
235            burn_backend::tensor::IndexingUpdateOp::Add => {
236                kernel::select_assign(tensor, dim, indices, value, false)
237            }
238            burn_backend::tensor::IndexingUpdateOp::Mul => {
239                kernel::select_assign_mul(tensor, dim, indices, value)
240            }
241            burn_backend::tensor::IndexingUpdateOp::Min => {
242                kernel::select_assign_min(tensor, dim, indices, value)
243            }
244            burn_backend::tensor::IndexingUpdateOp::Max => {
245                kernel::select_assign_max(tensor, dim, indices, value)
246            }
247        }
248    }
249
250    fn float_slice(tensor: FloatTensor<Self>, slices: &[Slice]) -> FloatTensor<Self> {
251        // Check if all steps are 1
252        let all_steps_one = slices.iter().all(|info| info.step == 1);
253
254        if all_steps_one {
255            // Use optimized slice for step=1
256            let simple_ranges: Vec<Range<usize>> = slices
257                .iter()
258                .enumerate()
259                .map(|(i, slice)| slice.to_range(tensor.meta.shape()[i]))
260                .collect();
261
262            kernel::slice(tensor, &simple_ranges)
263        } else {
264            // Use slice with steps kernel
265            kernel::slice_with_steps(tensor, slices)
266        }
267    }
268
269    fn float_slice_assign(
270        tensor: FloatTensor<Self>,
271        ranges: &[Slice],
272        value: FloatTensor<Self>,
273    ) -> FloatTensor<Self> {
274        kernel::slice_assign(tensor, ranges, value)
275    }
276
277    fn float_mask_where(
278        tensor: FloatTensor<Self>,
279        mask: BoolTensor<Self>,
280        value: FloatTensor<Self>,
281    ) -> FloatTensor<Self> {
282        let bool_dtype = mask.dtype;
283        kernel::mask_where_auto(tensor, mask, value, bool_dtype)
284    }
285
286    fn float_mask_fill(
287        tensor: FloatTensor<Self>,
288        mask: BoolTensor<Self>,
289        value: Scalar,
290    ) -> FloatTensor<Self> {
291        let dtype = tensor.dtype;
292        let bool_dtype = mask.dtype;
293        kernel::mask_fill_auto(
294            tensor,
295            mask,
296            InputScalar::new(value, dtype_to_storage_type(dtype)),
297            bool_dtype,
298        )
299    }
300
301    fn float_equal(
302        lhs: FloatTensor<Self>,
303        rhs: FloatTensor<Self>,
304        out_dtype: BoolDType,
305    ) -> BoolTensor<Self> {
306        kernel::equal(lhs, rhs, out_dtype.into())
307    }
308
309    fn float_not_equal(
310        lhs: FloatTensor<Self>,
311        rhs: FloatTensor<Self>,
312        out_dtype: BoolDType,
313    ) -> BoolTensor<Self> {
314        kernel::not_equal(lhs, rhs, out_dtype.into())
315    }
316
317    fn float_equal_elem(
318        lhs: FloatTensor<Self>,
319        rhs: Scalar,
320        out_dtype: BoolDType,
321    ) -> BoolTensor<Self> {
322        let dtype = lhs.dtype;
323        kernel::equal_elem(
324            lhs,
325            InputScalar::new(rhs, dtype_to_storage_type(dtype)),
326            out_dtype.into(),
327        )
328    }
329
330    fn float_not_equal_elem(
331        lhs: FloatTensor<Self>,
332        rhs: Scalar,
333        out_dtype: BoolDType,
334    ) -> BoolTensor<Self> {
335        let dtype = lhs.dtype;
336        kernel::not_equal_elem(
337            lhs,
338            InputScalar::new(rhs, dtype_to_storage_type(dtype)),
339            out_dtype.into(),
340        )
341    }
342
343    fn float_greater(
344        lhs: FloatTensor<Self>,
345        rhs: FloatTensor<Self>,
346        out_dtype: BoolDType,
347    ) -> BoolTensor<Self> {
348        kernel::greater(lhs, rhs, out_dtype.into())
349    }
350
351    fn float_greater_elem(
352        lhs: FloatTensor<Self>,
353        rhs: Scalar,
354        out_dtype: BoolDType,
355    ) -> BoolTensor<Self> {
356        let dtype = lhs.dtype;
357        kernel::greater_elem(
358            lhs,
359            InputScalar::new(rhs, dtype_to_storage_type(dtype)),
360            out_dtype.into(),
361        )
362    }
363
364    fn float_greater_equal(
365        lhs: FloatTensor<Self>,
366        rhs: FloatTensor<Self>,
367        out_dtype: BoolDType,
368    ) -> BoolTensor<Self> {
369        kernel::greater_equal(lhs, rhs, out_dtype.into())
370    }
371
372    fn float_greater_equal_elem(
373        lhs: FloatTensor<Self>,
374        rhs: Scalar,
375        out_dtype: BoolDType,
376    ) -> BoolTensor<Self> {
377        let dtype = lhs.dtype;
378        kernel::greater_equal_elem(
379            lhs,
380            InputScalar::new(rhs, dtype_to_storage_type(dtype)),
381            out_dtype.into(),
382        )
383    }
384
385    fn float_lower(
386        lhs: FloatTensor<Self>,
387        rhs: FloatTensor<Self>,
388        out_dtype: BoolDType,
389    ) -> BoolTensor<Self> {
390        kernel::lower(lhs, rhs, out_dtype.into())
391    }
392
393    fn float_lower_elem(
394        lhs: FloatTensor<Self>,
395        rhs: Scalar,
396        out_dtype: BoolDType,
397    ) -> BoolTensor<Self> {
398        let dtype = lhs.dtype;
399        kernel::lower_elem(
400            lhs,
401            InputScalar::new(rhs, dtype_to_storage_type(dtype)),
402            out_dtype.into(),
403        )
404    }
405
406    fn float_lower_equal(
407        lhs: FloatTensor<Self>,
408        rhs: FloatTensor<Self>,
409        out_dtype: BoolDType,
410    ) -> BoolTensor<Self> {
411        kernel::lower_equal(lhs, rhs, out_dtype.into())
412    }
413
414    fn float_lower_equal_elem(
415        lhs: FloatTensor<Self>,
416        rhs: Scalar,
417        out_dtype: BoolDType,
418    ) -> BoolTensor<Self> {
419        let dtype = lhs.dtype;
420        kernel::lower_equal_elem(
421            lhs,
422            InputScalar::new(rhs, dtype_to_storage_type(dtype)),
423            out_dtype.into(),
424        )
425    }
426
427    fn float_sum(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
428        reduce::sum_fallback(tensor, Default::default()).unwrap()
429    }
430
431    fn float_max(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
432        reduce::reduce(tensor, None, Default::default(), ReduceOperationConfig::Max).unwrap()
433    }
434
435    fn float_max_dim(tensor: FloatTensor<Self>, dim: usize) -> FloatTensor<Self> {
436        reduce::reduce_dim(
437            tensor,
438            None,
439            dim,
440            Default::default(),
441            ReduceOperationConfig::Max,
442        )
443        .unwrap()
444    }
445
446    fn float_min(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
447        reduce::reduce(tensor, None, Default::default(), ReduceOperationConfig::Min).unwrap()
448    }
449
450    fn float_min_dim(tensor: FloatTensor<Self>, dim: usize) -> FloatTensor<Self> {
451        reduce::reduce_dim(
452            tensor,
453            None,
454            dim,
455            Default::default(),
456            ReduceOperationConfig::Min,
457        )
458        .unwrap()
459    }
460
461    fn float_max_abs(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
462        reduce::reduce(
463            tensor,
464            None,
465            Default::default(),
466            ReduceOperationConfig::MaxAbs,
467        )
468        .unwrap()
469    }
470
471    fn float_max_abs_dim(tensor: FloatTensor<Self>, dim: usize) -> FloatTensor<Self> {
472        reduce::reduce_dim(
473            tensor,
474            None,
475            dim,
476            Default::default(),
477            ReduceOperationConfig::MaxAbs,
478        )
479        .unwrap()
480    }
481
482    fn float_any(tensor: FloatTensor<Self>, out_dtype: BoolDType) -> BoolTensor<Self> {
483        reduce::reduce_logical(tensor, None, ReduceOperationConfig::Any, out_dtype)
484    }
485
486    fn float_any_dim(
487        tensor: FloatTensor<Self>,
488        dim: usize,
489        out_dtype: BoolDType,
490    ) -> BoolTensor<Self> {
491        reduce::reduce_logical(tensor, Some(dim), ReduceOperationConfig::Any, out_dtype)
492    }
493
494    fn float_all(tensor: FloatTensor<Self>, out_dtype: BoolDType) -> BoolTensor<Self> {
495        reduce::reduce_logical(tensor, None, ReduceOperationConfig::All, out_dtype)
496    }
497
498    fn float_all_dim(
499        tensor: FloatTensor<Self>,
500        dim: usize,
501        out_dtype: BoolDType,
502    ) -> BoolTensor<Self> {
503        reduce::reduce_logical(tensor, Some(dim), ReduceOperationConfig::All, out_dtype)
504    }
505
506    fn float_sum_dim(tensor: FloatTensor<Self>, dim: usize) -> FloatTensor<Self> {
507        reduce::reduce_dim(
508            tensor,
509            None,
510            dim,
511            Default::default(),
512            ReduceOperationConfig::Sum,
513        )
514        .unwrap()
515    }
516
517    fn float_sum_dims(tensor: FloatTensor<Self>, dims: &[usize]) -> FloatTensor<Self> {
518        reduce::reduce_dims(
519            tensor,
520            None,
521            dims,
522            Default::default(),
523            ReduceOperationConfig::Sum,
524        )
525        .unwrap()
526    }
527
528    fn float_mean_dim(tensor: FloatTensor<Self>, dim: usize) -> FloatTensor<Self> {
529        reduce::reduce_dim(
530            tensor,
531            None,
532            dim,
533            Default::default(),
534            ReduceOperationConfig::Mean,
535        )
536        .unwrap()
537    }
538
539    fn float_mean(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
540        reduce::reduce(
541            tensor,
542            None,
543            Default::default(),
544            ReduceOperationConfig::Mean,
545        )
546        .unwrap()
547    }
548
549    fn float_cumsum(tensor: FloatTensor<Self>, dim: usize) -> FloatTensor<Self> {
550        numeric::cumsum(tensor, dim)
551    }
552
553    fn float_cumprod(tensor: FloatTensor<Self>, dim: usize) -> FloatTensor<Self> {
554        numeric::cumprod(tensor, dim)
555    }
556
557    fn float_cummin(tensor: FloatTensor<Self>, dim: usize) -> FloatTensor<Self> {
558        numeric::cummin(tensor, dim)
559    }
560
561    fn float_cummax(tensor: FloatTensor<Self>, dim: usize) -> FloatTensor<Self> {
562        numeric::cummax(tensor, dim)
563    }
564
565    fn float_prod(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
566        reduce::reduce(
567            tensor,
568            None,
569            Default::default(),
570            ReduceOperationConfig::Prod,
571        )
572        .unwrap()
573    }
574
575    fn float_prod_dim(tensor: FloatTensor<Self>, dim: usize) -> FloatTensor<Self> {
576        reduce::reduce_dim(
577            tensor,
578            None,
579            dim,
580            Default::default(),
581            ReduceOperationConfig::Prod,
582        )
583        .unwrap()
584    }
585
586    fn float_exp(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
587        unary_basic::launch::<_>(tensor, |_| BasicFloatUnaryKind::Exp)
588    }
589
590    fn float_log(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
591        unary_basic::launch::<_>(tensor, |_| BasicFloatUnaryKind::Log)
592    }
593
594    fn float_log1p(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
595        unary_basic::launch::<_>(tensor, |_| BasicFloatUnaryKind::Log1p)
596    }
597
598    fn float_powf_scalar_impl(lhs: FloatTensor<Self>, rhs: Scalar) -> FloatTensor<Self> {
599        struct Powf;
600
601        #[cube]
602        impl<F: Float, N: Size> FloatUnaryOp<F, N> for Powf {
603            type Options = InputScalar;
604
605            fn execute(input: Vector<F, N>, options: &Self::Options) -> Vector<F, N> {
606                Vector::powf(input, Vector::new(options.get::<F>()))
607            }
608        }
609
610        impl FloatUnaryOpFamily for Powf {
611            type Options = InputScalar;
612            type Unary<F: Float, N: Size> = Self;
613        }
614
615        let dtype = lhs.dtype;
616        launch_unary_float::<Powf, _>(lhs, |_| InputScalar::new(rhs, dtype_to_storage_type(dtype)))
617    }
618
619    fn float_sqrt(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
620        unary_basic::launch::<_>(tensor, |_| BasicFloatUnaryKind::Sqrt)
621    }
622
623    fn float_abs(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
624        unary_basic::launch::<_>(tensor, |_| BasicFloatUnaryKind::Abs)
625    }
626
627    fn float_sign(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
628        unary_basic::launch::<_>(tensor, |_| BasicFloatUnaryKind::Sign)
629    }
630
631    fn float_cos(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
632        unary_basic::launch::<_>(tensor, |_| BasicFloatUnaryKind::Cos)
633    }
634
635    fn float_sin(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
636        unary_basic::launch::<_>(tensor, |_| BasicFloatUnaryKind::Sin)
637    }
638
639    fn float_tan(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
640        unary_basic::launch::<_>(tensor, |_| BasicFloatUnaryKind::Tan)
641    }
642
643    fn float_cosh(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
644        unary_basic::launch::<_>(tensor, |_| BasicFloatUnaryKind::Cosh)
645    }
646
647    fn float_sinh(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
648        unary_basic::launch::<_>(tensor, |_| BasicFloatUnaryKind::Sinh)
649    }
650
651    fn float_tanh(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
652        unary_basic::launch::<_>(tensor, |_| BasicFloatUnaryKind::Tanh)
653    }
654
655    fn float_acos(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
656        unary_basic::launch::<_>(tensor, |_| BasicFloatUnaryKind::ArcCos)
657    }
658
659    fn float_acosh(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
660        unary_basic::launch::<_>(tensor, |_| BasicFloatUnaryKind::ArcCosh)
661    }
662
663    fn float_asin(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
664        unary_basic::launch::<_>(tensor, |_| BasicFloatUnaryKind::ArcSin)
665    }
666
667    fn float_asinh(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
668        unary_basic::launch::<_>(tensor, |_| BasicFloatUnaryKind::ArcSinh)
669    }
670
671    fn float_atan(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
672        unary_basic::launch::<_>(tensor, |_| BasicFloatUnaryKind::ArcTan)
673    }
674
675    fn float_atanh(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
676        unary_basic::launch::<_>(tensor, |_| BasicFloatUnaryKind::ArcTanh)
677    }
678
679    fn float_atan2(lhs: FloatTensor<Self>, rhs: FloatTensor<Self>) -> FloatTensor<Self> {
680        crate::kernel::atan2(lhs, rhs)
681    }
682
683    fn float_round(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
684        unary_basic::launch::<_>(tensor, |_| BasicFloatUnaryKind::Round)
685    }
686
687    fn float_floor(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
688        unary_basic::launch::<_>(tensor, |_| BasicFloatUnaryKind::Floor)
689    }
690
691    fn float_ceil(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
692        unary_basic::launch::<_>(tensor, |_| BasicFloatUnaryKind::Ceil)
693    }
694
695    fn float_trunc(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
696        unary_basic::launch::<_>(tensor, |_| BasicFloatUnaryKind::Trunc)
697    }
698
699    fn float_erf(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
700        unary_basic::launch::<_>(tensor, |_| BasicFloatUnaryKind::Erf)
701    }
702
703    fn float_argmax(tensor: FloatTensor<Self>, dim: usize, out_dtype: IntDType) -> IntTensor<Self> {
704        reduce::reduce_dim(
705            tensor,
706            Some(out_dtype.into()),
707            dim,
708            Default::default(),
709            ReduceOperationConfig::ArgMax,
710        )
711        .unwrap()
712    }
713
714    fn float_argtopk(
715        tensor: FloatTensor<Self>,
716        dim: usize,
717        k: usize,
718        out_dtype: IntDType,
719    ) -> IntTensor<Self> {
720        reduce::reduce_dim(
721            tensor,
722            Some(out_dtype.into()),
723            dim,
724            Default::default(),
725            ReduceOperationConfig::ArgTopK(k),
726        )
727        .unwrap()
728    }
729
730    fn float_topk(tensor: FloatTensor<Self>, dim: usize, k: usize) -> FloatTensor<Self> {
731        reduce::reduce_dim(
732            tensor,
733            None,
734            dim,
735            Default::default(),
736            ReduceOperationConfig::TopK(k),
737        )
738        .unwrap()
739    }
740
741    fn float_topk_with_indices(
742        tensor: FloatTensor<Self>,
743        dim: usize,
744        k: usize,
745        out_dtype: IntDType,
746    ) -> (FloatTensor<Self>, IntTensor<Self>) {
747        // One pass, rather than the default's TopK followed by ArgTopK: the reduction
748        // already carries both halves, and these kernels are memory bound.
749        reduce::reduce_dim_with_indices(
750            tensor,
751            out_dtype.into(),
752            dim,
753            Default::default(),
754            ReduceOperationConfig::TopK(k),
755        )
756        .unwrap()
757    }
758
759    fn float_max_dim_with_indices(
760        tensor: FloatTensor<Self>,
761        dim: usize,
762        indices_dtype: IntDType,
763    ) -> (FloatTensor<Self>, IntTensor<Self>) {
764        // One pass, rather than the default's ArgMax followed by a gather.
765        reduce::reduce_dim_with_indices(
766            tensor,
767            indices_dtype.into(),
768            dim,
769            Default::default(),
770            ReduceOperationConfig::Max,
771        )
772        .unwrap()
773    }
774
775    fn float_min_dim_with_indices(
776        tensor: FloatTensor<Self>,
777        dim: usize,
778        indices_dtype: IntDType,
779    ) -> (FloatTensor<Self>, IntTensor<Self>) {
780        // One pass, rather than the default's ArgMin followed by a gather.
781        reduce::reduce_dim_with_indices(
782            tensor,
783            indices_dtype.into(),
784            dim,
785            Default::default(),
786            ReduceOperationConfig::Min,
787        )
788        .unwrap()
789    }
790
791    fn float_argmin(tensor: FloatTensor<Self>, dim: usize, out_dtype: IntDType) -> IntTensor<Self> {
792        reduce::reduce_dim(
793            tensor,
794            Some(out_dtype.into()),
795            dim,
796            Default::default(),
797            ReduceOperationConfig::ArgMin,
798        )
799        .unwrap()
800    }
801
802    fn float_into_int(tensor: FloatTensor<Self>, out_dtype: IntDType) -> IntTensor<Self> {
803        kernel::cast(tensor, out_dtype.into())
804    }
805
806    fn float_clamp(tensor: FloatTensor<Self>, min: Scalar, max: Scalar) -> FloatTensor<Self> {
807        let dtype = tensor.dtype;
808        kernel::clamp_float(
809            tensor,
810            InputScalar::new(min, dtype_to_storage_type(dtype)),
811            InputScalar::new(max, dtype_to_storage_type(dtype)),
812        )
813    }
814
815    fn float_recip(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
816        unary_basic::launch::<_>(tensor, |_| BasicFloatUnaryKind::Recip)
817    }
818
819    fn float_repeat_dim(tensor: FloatTensor<Self>, dim: usize, times: usize) -> FloatTensor<Self> {
820        kernel::repeat_dim(tensor, dim, times)
821    }
822
823    fn float_powf(lhs: FloatTensor<Self>, rhs: FloatTensor<Self>) -> FloatTensor<Self> {
824        numeric::pow(lhs, rhs)
825    }
826
827    fn float_permute(tensor: FloatTensor<Self>, axes: &[usize]) -> FloatTensor<Self> {
828        permute(tensor, axes)
829    }
830
831    fn float_expand(tensor: FloatTensor<Self>, shape: Shape) -> FloatTensor<Self> {
832        expand(tensor, shape)
833    }
834
835    fn float_flip(tensor: FloatTensor<Self>, axes: &[usize]) -> FloatTensor<Self> {
836        let bool_dtype = get_or_init_device_settings::<Self>(&tensor.device).bool_dtype;
837        kernel::flip(tensor, axes, bool_dtype.into())
838    }
839
840    fn float_cast(tensor: FloatTensor<Self>, dtype: FloatDType) -> FloatTensor<Self> {
841        kernel::cast(tensor, dtype.into())
842    }
843
844    fn float_unfold(
845        tensor: FloatTensor<Self>,
846        dim: usize,
847        size: usize,
848        step: usize,
849    ) -> FloatTensor<Self> {
850        unfold(tensor, dim, size, step)
851    }
852
853    fn float_is_nan(tensor: FloatTensor<Self>, out_dtype: BoolDType) -> BoolTensor<Self> {
854        kernel::is_nan(tensor, out_dtype.into())
855    }
856
857    fn float_is_inf(tensor: FloatTensor<Self>, out_dtype: BoolDType) -> BoolTensor<Self> {
858        kernel::is_inf(tensor, out_dtype.into())
859    }
860
861    fn float_grid_sample_2d(
862        tensor: FloatTensor<Self>,
863        grid: FloatTensor<Self>,
864        options: GridSampleOptions,
865    ) -> FloatTensor<Self> {
866        kernel::grid_sample::grid_sample(tensor, grid, options)
867    }
868}