Skip to main content

burn_ndarray/ops/
tensor.rs

1// Language
2use alloc::vec::Vec;
3use burn_backend::backend::ExecutionError;
4use burn_backend::ops::GridSampleOptions;
5use burn_backend::tensor::FloatTensor;
6use burn_backend::{TensorMetadata, element::cast::ToElement};
7use burn_std::{BoolDType, IntDType};
8
9// Current crate
10use super::{
11    NdArrayMathOps, NdArrayOps,
12    matmul::{cross, matmul},
13};
14use crate::{
15    NdArray, cast_to_dtype, cat_with_dtype, execute_with_int_dtype, tensor::NdArrayTensor,
16};
17use crate::{NdArrayDevice, SEED, execute_with_float_out_dtype, execute_with_int_out_dtype, slice};
18use crate::{SharedArray, element::ExpElement};
19use crate::{execute_with_float_dtype, ops::grid_sample::grid_sample_2d};
20
21// Workspace crates
22use crate::rand::get_seeded_rng;
23use burn_backend::{Distribution, FloatDType, Scalar};
24use burn_backend::{ElementConversion, Shape, TensorData, ops::FloatTensorOps};
25
26#[cfg(not(feature = "std"))]
27#[allow(unused_imports)]
28use num_traits::Float;
29
30use libm::erf;
31
32#[cfg(feature = "std")]
33#[allow(dead_code)]
34fn round_ties_even_wrapper(x: f64) -> f64 {
35    x.round_ties_even()
36}
37
38#[cfg(not(feature = "std"))]
39#[allow(dead_code)]
40fn round_ties_even_wrapper(x: f64) -> f64 {
41    if (x - x.floor()) == 0.5 {
42        (x * 0.5).round() * 2.0
43    } else {
44        x.round()
45    }
46}
47
48impl FloatTensorOps<Self> for NdArray {
49    fn float_from_data(data: TensorData, _device: &NdArrayDevice) -> FloatTensor<Self> {
50        NdArrayTensor::from_data(data)
51    }
52
53    fn float_random(
54        shape: Shape,
55        distribution: Distribution,
56        device: &NdArrayDevice,
57        dtype: FloatDType,
58    ) -> FloatTensor<Self> {
59        let mut seed = SEED.lock();
60        let mut rng = seed.take().unwrap_or_else(get_seeded_rng);
61        let tensor = execute_with_float_out_dtype!(
62            dtype,
63            E,
64            Self::float_from_data(
65                TensorData::random::<E, _, _>(shape, distribution, &mut rng),
66                device,
67            )
68        );
69
70        *seed = Some(rng);
71        tensor
72    }
73
74    async fn float_into_data(tensor: FloatTensor<Self>) -> Result<TensorData, ExecutionError> {
75        Ok(tensor.into_data())
76    }
77
78    fn float_to_device(tensor: FloatTensor<Self>, _device: &NdArrayDevice) -> FloatTensor<Self> {
79        tensor
80    }
81
82    fn float_empty(shape: Shape, device: &NdArrayDevice, dtype: FloatDType) -> FloatTensor<Self> {
83        Self::float_zeros(shape, device, dtype)
84    }
85
86    fn float_add(lhs: FloatTensor<Self>, rhs: FloatTensor<Self>) -> FloatTensor<Self> {
87        execute_with_float_dtype!((lhs, rhs), NdArrayMathOps::add)
88    }
89
90    fn float_add_scalar(lhs: FloatTensor<Self>, rhs: Scalar) -> FloatTensor<Self> {
91        execute_with_float_dtype!(lhs, FloatElem, |array: SharedArray<FloatElem>| {
92            NdArrayMathOps::add_scalar(array, rhs.elem())
93        })
94    }
95
96    fn float_sub(lhs: FloatTensor<Self>, rhs: FloatTensor<Self>) -> FloatTensor<Self> {
97        execute_with_float_dtype!((lhs, rhs), NdArrayMathOps::sub)
98    }
99
100    fn float_sub_scalar(lhs: FloatTensor<Self>, rhs: Scalar) -> FloatTensor<Self> {
101        execute_with_float_dtype!(lhs, FloatElem, |array: SharedArray<FloatElem>| {
102            NdArrayMathOps::sub_scalar(array, rhs.elem())
103        })
104    }
105
106    fn float_mul(lhs: FloatTensor<Self>, rhs: FloatTensor<Self>) -> FloatTensor<Self> {
107        execute_with_float_dtype!((lhs, rhs), NdArrayMathOps::mul)
108    }
109
110    fn float_mul_scalar(lhs: FloatTensor<Self>, rhs: Scalar) -> FloatTensor<Self> {
111        execute_with_float_dtype!(lhs, FloatElem, |array: SharedArray<FloatElem>| {
112            NdArrayMathOps::mul_scalar(array, rhs.elem())
113        })
114    }
115
116    fn float_div(lhs: FloatTensor<Self>, rhs: FloatTensor<Self>) -> FloatTensor<Self> {
117        execute_with_float_dtype!((lhs, rhs), NdArrayMathOps::div)
118    }
119
120    fn float_div_scalar(lhs: FloatTensor<Self>, rhs: Scalar) -> FloatTensor<Self> {
121        execute_with_float_dtype!(lhs, FloatElem, |array: SharedArray<FloatElem>| {
122            NdArrayMathOps::div_scalar(array, rhs.elem())
123        })
124    }
125
126    fn float_remainder(lhs: FloatTensor<Self>, rhs: FloatTensor<Self>) -> FloatTensor<Self> {
127        execute_with_float_dtype!((lhs, rhs), NdArrayMathOps::remainder)
128    }
129
130    fn float_remainder_scalar(lhs: FloatTensor<Self>, rhs: Scalar) -> FloatTensor<Self> {
131        execute_with_float_dtype!(lhs, FloatElem, |array: SharedArray<FloatElem>| {
132            NdArrayMathOps::remainder_scalar(array, rhs.elem())
133        })
134    }
135
136    fn float_matmul(lhs: FloatTensor<Self>, rhs: FloatTensor<Self>) -> FloatTensor<Self> {
137        execute_with_float_dtype!((lhs, rhs), matmul)
138    }
139
140    fn float_cross(
141        lhs: FloatTensor<Self>,
142        rhs: FloatTensor<Self>,
143        dim: usize,
144    ) -> FloatTensor<Self> {
145        execute_with_float_dtype!((lhs, rhs), |lhs, rhs| cross(lhs, rhs, dim))
146    }
147
148    fn float_recip(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
149        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
150            NdArrayMathOps::recip(array)
151        })
152    }
153
154    fn float_swap_dims(tensor: FloatTensor<Self>, dim1: usize, dim2: usize) -> FloatTensor<Self> {
155        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
156            NdArrayOps::swap_dims(array, dim1, dim2)
157        })
158    }
159
160    fn float_reshape(tensor: FloatTensor<Self>, shape: Shape) -> FloatTensor<Self> {
161        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
162            NdArrayOps::reshape(array, shape)
163        })
164    }
165
166    fn float_gather(
167        dim: usize,
168        tensor: FloatTensor<Self>,
169        indices: NdArrayTensor,
170    ) -> FloatTensor<Self> {
171        execute_with_int_dtype!(
172            indices,
173            IntElem,
174            |idx_array: SharedArray<IntElem>| -> NdArrayTensor {
175                execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
176                    NdArrayOps::gather(dim, array, idx_array)
177                })
178            }
179        )
180    }
181
182    fn float_scatter(
183        dim: usize,
184        tensor: FloatTensor<Self>,
185        indices: NdArrayTensor,
186        value: FloatTensor<Self>,
187        update: burn_backend::tensor::IndexingUpdateOp,
188    ) -> FloatTensor<Self> {
189        match update {
190            burn_backend::tensor::IndexingUpdateOp::Add => {
191                execute_with_int_dtype!(
192                    indices,
193                    IntElem,
194                    |idx_array: SharedArray<IntElem>| -> NdArrayTensor {
195                        execute_with_float_dtype!((tensor, value), |tensor, value| {
196                            NdArrayOps::scatter(dim, tensor, idx_array, value)
197                        })
198                    }
199                )
200            }
201            burn_backend::tensor::IndexingUpdateOp::Assign => {
202                execute_with_int_dtype!(
203                    indices,
204                    IntElem,
205                    |idx_array: SharedArray<IntElem>| -> NdArrayTensor {
206                        execute_with_float_dtype!((tensor, value), |tensor, value| {
207                            NdArrayOps::scatter_assign(dim, tensor, idx_array, value)
208                        })
209                    }
210                )
211            }
212            burn_backend::tensor::IndexingUpdateOp::Mul => {
213                execute_with_int_dtype!(
214                    indices,
215                    IntElem,
216                    |idx_array: SharedArray<IntElem>| -> NdArrayTensor {
217                        execute_with_float_dtype!((tensor, value), |tensor, value| {
218                            NdArrayOps::scatter_mul(dim, tensor, idx_array, value)
219                        })
220                    }
221                )
222            }
223            burn_backend::tensor::IndexingUpdateOp::Min => {
224                execute_with_int_dtype!(
225                    indices,
226                    IntElem,
227                    |idx_array: SharedArray<IntElem>| -> NdArrayTensor {
228                        execute_with_float_dtype!((tensor, value), |tensor, value| {
229                            NdArrayOps::scatter_min(dim, tensor, idx_array, value)
230                        })
231                    }
232                )
233            }
234            burn_backend::tensor::IndexingUpdateOp::Max => {
235                execute_with_int_dtype!(
236                    indices,
237                    IntElem,
238                    |idx_array: SharedArray<IntElem>| -> NdArrayTensor {
239                        execute_with_float_dtype!((tensor, value), |tensor, value| {
240                            NdArrayOps::scatter_max(dim, tensor, idx_array, value)
241                        })
242                    }
243                )
244            }
245        }
246    }
247
248    fn float_scatter_nd(
249        data: FloatTensor<Self>,
250        indices: NdArrayTensor,
251        values: FloatTensor<Self>,
252        reduction: burn_backend::tensor::IndexingUpdateOp,
253    ) -> FloatTensor<Self> {
254        execute_with_int_dtype!(
255            indices,
256            IntElem,
257            |idx_array: SharedArray<IntElem>| -> NdArrayTensor {
258                execute_with_float_dtype!((data, values), |data, values| NdArrayOps::scatter_nd(
259                    data, idx_array, values, reduction
260                ))
261            }
262        )
263    }
264
265    fn float_gather_nd(data: FloatTensor<Self>, indices: NdArrayTensor) -> FloatTensor<Self> {
266        execute_with_int_dtype!(
267            indices,
268            IntElem,
269            |idx_array: SharedArray<IntElem>| -> NdArrayTensor {
270                execute_with_float_dtype!(data, FloatElem, |array: SharedArray<FloatElem>| {
271                    NdArrayOps::gather_nd(array, idx_array)
272                })
273            }
274        )
275    }
276
277    fn float_select(
278        tensor: FloatTensor<Self>,
279        dim: usize,
280        indices: NdArrayTensor,
281    ) -> FloatTensor<Self> {
282        execute_with_int_dtype!(
283            indices,
284            IntElem,
285            |idx_array: SharedArray<IntElem>| -> NdArrayTensor {
286                execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
287                    NdArrayMathOps::select(array, dim, idx_array)
288                })
289            }
290        )
291    }
292
293    fn float_select_assign(
294        tensor: FloatTensor<Self>,
295        dim: usize,
296        indices: NdArrayTensor,
297        value: FloatTensor<Self>,
298        update: burn_backend::tensor::IndexingUpdateOp,
299    ) -> FloatTensor<Self> {
300        match update {
301            burn_backend::tensor::IndexingUpdateOp::Add => {
302                execute_with_int_dtype!(
303                    indices,
304                    IntElem,
305                    |idx_array: SharedArray<IntElem>| -> NdArrayTensor {
306                        execute_with_float_dtype!((tensor, value), |tensor, value| {
307                            NdArrayMathOps::select_assign(tensor, dim, idx_array, value)
308                        })
309                    }
310                )
311            }
312            burn_backend::tensor::IndexingUpdateOp::Assign => {
313                execute_with_int_dtype!(
314                    indices,
315                    IntElem,
316                    |idx_array: SharedArray<IntElem>| -> NdArrayTensor {
317                        execute_with_float_dtype!((tensor, value), |tensor, value| {
318                            NdArrayMathOps::select_assign_replace(tensor, dim, idx_array, value)
319                        })
320                    }
321                )
322            }
323            burn_backend::tensor::IndexingUpdateOp::Mul => {
324                execute_with_int_dtype!(
325                    indices,
326                    IntElem,
327                    |idx_array: SharedArray<IntElem>| -> NdArrayTensor {
328                        execute_with_float_dtype!((tensor, value), |tensor, value| {
329                            NdArrayMathOps::select_assign_mul(tensor, dim, idx_array, value)
330                        })
331                    }
332                )
333            }
334            burn_backend::tensor::IndexingUpdateOp::Min => {
335                execute_with_int_dtype!(
336                    indices,
337                    IntElem,
338                    |idx_array: SharedArray<IntElem>| -> NdArrayTensor {
339                        execute_with_float_dtype!((tensor, value), |tensor, value| {
340                            NdArrayMathOps::select_assign_min(tensor, dim, idx_array, value)
341                        })
342                    }
343                )
344            }
345            burn_backend::tensor::IndexingUpdateOp::Max => {
346                execute_with_int_dtype!(
347                    indices,
348                    IntElem,
349                    |idx_array: SharedArray<IntElem>| -> NdArrayTensor {
350                        execute_with_float_dtype!((tensor, value), |tensor, value| {
351                            NdArrayMathOps::select_assign_max(tensor, dim, idx_array, value)
352                        })
353                    }
354                )
355            }
356        }
357    }
358
359    fn float_slice(tensor: FloatTensor<Self>, slices: &[burn_backend::Slice]) -> FloatTensor<Self> {
360        slice!(tensor, slices)
361    }
362
363    fn float_slice_assign(
364        tensor: FloatTensor<Self>,
365        slices: &[burn_backend::Slice],
366        value: FloatTensor<Self>,
367    ) -> FloatTensor<Self> {
368        execute_with_float_dtype!((tensor, value), |tensor, value| {
369            NdArrayOps::slice_assign(tensor, slices, value)
370        })
371    }
372
373    fn float_mask_where(
374        tensor: FloatTensor<Self>,
375        mask: NdArrayTensor,
376        value: FloatTensor<Self>,
377    ) -> FloatTensor<Self> {
378        execute_with_float_dtype!((tensor, value), |tensor, value| {
379            NdArrayOps::mask_where(tensor, mask.bool(), value)
380        })
381    }
382
383    fn float_mask_fill(
384        tensor: FloatTensor<Self>,
385        mask: NdArrayTensor,
386        value: Scalar,
387    ) -> FloatTensor<Self> {
388        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
389            NdArrayOps::mask_fill(array, mask.bool(), value.elem())
390        })
391    }
392
393    fn float_equal(
394        lhs: FloatTensor<Self>,
395        rhs: FloatTensor<Self>,
396        _out_dtype: BoolDType,
397    ) -> NdArrayTensor {
398        execute_with_float_dtype!((lhs, rhs), |lhs, rhs| { NdArrayMathOps::equal(lhs, rhs) })
399    }
400
401    fn float_equal_elem(
402        lhs: FloatTensor<Self>,
403        rhs: Scalar,
404        _out_dtype: BoolDType,
405    ) -> NdArrayTensor {
406        execute_with_float_dtype!(lhs, FloatElem, |array: SharedArray<FloatElem>| {
407            NdArrayMathOps::equal_elem(array, rhs.elem())
408        })
409    }
410
411    fn float_greater(
412        lhs: FloatTensor<Self>,
413        rhs: FloatTensor<Self>,
414        _out_dtype: BoolDType,
415    ) -> NdArrayTensor {
416        execute_with_float_dtype!((lhs, rhs), |lhs, rhs| { NdArrayMathOps::greater(lhs, rhs) })
417    }
418
419    fn float_greater_elem(
420        lhs: FloatTensor<Self>,
421        rhs: Scalar,
422        _out_dtype: BoolDType,
423    ) -> NdArrayTensor {
424        execute_with_float_dtype!(lhs, FloatElem, |array: SharedArray<FloatElem>| {
425            NdArrayMathOps::greater_elem(array, rhs.elem())
426        })
427    }
428
429    fn float_greater_equal(
430        lhs: FloatTensor<Self>,
431        rhs: FloatTensor<Self>,
432        _out_dtype: BoolDType,
433    ) -> NdArrayTensor {
434        execute_with_float_dtype!((lhs, rhs), |lhs, rhs| {
435            NdArrayMathOps::greater_equal(lhs, rhs)
436        })
437    }
438
439    fn float_greater_equal_elem(
440        lhs: FloatTensor<Self>,
441        rhs: Scalar,
442        _out_dtype: BoolDType,
443    ) -> NdArrayTensor {
444        execute_with_float_dtype!(lhs, FloatElem, |array: SharedArray<FloatElem>| {
445            NdArrayMathOps::greater_equal_elem(array, rhs.elem())
446        })
447    }
448
449    fn float_lower(
450        lhs: FloatTensor<Self>,
451        rhs: FloatTensor<Self>,
452        _out_dtype: BoolDType,
453    ) -> NdArrayTensor {
454        execute_with_float_dtype!((lhs, rhs), |lhs, rhs| { NdArrayMathOps::lower(lhs, rhs) })
455    }
456
457    fn float_lower_elem(
458        lhs: FloatTensor<Self>,
459        rhs: Scalar,
460        _out_dtype: BoolDType,
461    ) -> NdArrayTensor {
462        execute_with_float_dtype!(lhs, FloatElem, |array: SharedArray<FloatElem>| {
463            NdArrayMathOps::lower_elem(array, rhs.elem())
464        })
465    }
466
467    fn float_lower_equal(
468        lhs: FloatTensor<Self>,
469        rhs: FloatTensor<Self>,
470        _out_dtype: BoolDType,
471    ) -> NdArrayTensor {
472        execute_with_float_dtype!((lhs, rhs), |lhs, rhs| {
473            NdArrayMathOps::lower_equal(lhs, rhs)
474        })
475    }
476
477    fn float_lower_equal_elem(
478        lhs: FloatTensor<Self>,
479        rhs: Scalar,
480        _out_dtype: BoolDType,
481    ) -> NdArrayTensor {
482        execute_with_float_dtype!(lhs, FloatElem, |array: SharedArray<FloatElem>| {
483            NdArrayMathOps::lower_equal_elem(array, rhs.elem())
484        })
485    }
486
487    fn float_detach(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
488        tensor
489    }
490
491    fn float_mean(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
492        // Use view() for zero-copy on borrowed storage
493        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
494            NdArrayMathOps::mean_view(array.view())
495        })
496    }
497
498    fn float_sum(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
499        // Use view() for zero-copy on borrowed storage
500        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
501            NdArrayMathOps::sum_view(array.view())
502        })
503    }
504
505    fn float_mean_dim(tensor: FloatTensor<Self>, dim: usize) -> FloatTensor<Self> {
506        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
507            NdArrayMathOps::mean_dim(array, dim)
508        })
509    }
510
511    fn float_cumsum(tensor: FloatTensor<Self>, dim: usize) -> FloatTensor<Self> {
512        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
513            NdArrayMathOps::cumsum(array, dim)
514        })
515    }
516
517    fn float_cumprod(tensor: FloatTensor<Self>, dim: usize) -> FloatTensor<Self> {
518        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
519            NdArrayMathOps::cumprod(array, dim)
520        })
521    }
522
523    fn float_cummin(tensor: FloatTensor<Self>, dim: usize) -> FloatTensor<Self> {
524        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
525            NdArrayMathOps::cummin(array, dim)
526        })
527    }
528
529    fn float_cummax(tensor: FloatTensor<Self>, dim: usize) -> FloatTensor<Self> {
530        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
531            NdArrayMathOps::cummax(array, dim)
532        })
533    }
534
535    fn float_sum_dim(tensor: FloatTensor<Self>, dim: usize) -> FloatTensor<Self> {
536        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
537            NdArrayMathOps::sum_dim(array, dim)
538        })
539    }
540
541    fn float_argmax(tensor: FloatTensor<Self>, dim: usize, out_dtype: IntDType) -> NdArrayTensor {
542        // Use view() for zero-copy on borrowed storage
543        execute_with_int_out_dtype!(out_dtype, I, {
544            execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
545                NdArrayMathOps::argmax_view::<I>(array.view(), dim)
546            })
547        })
548    }
549
550    fn float_argmin(tensor: FloatTensor<Self>, dim: usize, out_dtype: IntDType) -> NdArrayTensor {
551        // Use view() for zero-copy on borrowed storage
552        execute_with_int_out_dtype!(out_dtype, I, {
553            execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
554                NdArrayMathOps::argmin_view::<I>(array.view(), dim)
555            })
556        })
557    }
558
559    fn float_exp(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
560        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
561            array.mapv_into(|a: FloatElem| a.exp_elem()).into_shared()
562        })
563    }
564
565    fn float_log(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
566        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
567            array.mapv_into(|a: FloatElem| a.log_elem()).into_shared()
568        })
569    }
570
571    fn float_prod(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
572        // Use view() for zero-copy on borrowed storage
573        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
574            NdArrayMathOps::prod_view(array.view())
575        })
576    }
577
578    fn float_prod_dim(tensor: FloatTensor<Self>, dim: usize) -> FloatTensor<Self> {
579        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
580            NdArrayMathOps::prod_dim(array, dim)
581        })
582    }
583
584    fn float_max(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
585        // Use view() for zero-copy on borrowed storage
586        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
587            NdArrayMathOps::max_float_view(array.view())
588        })
589    }
590
591    fn float_min(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
592        // Use view() for zero-copy on borrowed storage
593        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
594            NdArrayMathOps::min_float_view(array.view())
595        })
596    }
597
598    fn float_log1p(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
599        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
600            array.mapv_into(|a: FloatElem| a.log1p_elem()).into_shared()
601        })
602    }
603
604    fn float_powf_scalar_impl(tensor: FloatTensor<Self>, value: Scalar) -> FloatTensor<Self> {
605        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
606            array
607                .mapv_into(|a: FloatElem| a.powf_elem(value.elem()))
608                .into_shared()
609        })
610    }
611
612    fn float_sqrt(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
613        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
614            array.mapv_into(|a: FloatElem| a.sqrt_elem()).into_shared()
615        })
616    }
617
618    fn float_abs(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
619        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
620            NdArrayMathOps::abs(array)
621        })
622    }
623
624    fn float_cos(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
625        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
626            array
627                .mapv_into(|a: FloatElem| (a.to_f64()).cos().elem())
628                .into_shared()
629        })
630    }
631
632    fn float_cosh(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
633        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
634            array
635                .mapv_into(|a: FloatElem| (a.to_f64()).cosh().elem())
636                .into_shared()
637        })
638    }
639
640    fn float_sin(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
641        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
642            array
643                .mapv_into(|a: FloatElem| (a.to_f64()).sin().elem())
644                .into_shared()
645        })
646    }
647
648    fn float_sinh(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
649        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
650            array
651                .mapv_into(|a: FloatElem| (a.to_f64()).sinh().elem())
652                .into_shared()
653        })
654    }
655
656    fn float_tan(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
657        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
658            array
659                .mapv_into(|a: FloatElem| (a.to_f64()).tan().elem())
660                .into_shared()
661        })
662    }
663
664    fn float_tanh(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
665        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
666            array
667                .mapv_into(|a: FloatElem| (a.to_f64()).tanh().elem())
668                .into_shared()
669        })
670    }
671
672    fn float_acos(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
673        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
674            array
675                .mapv_into(|a: FloatElem| (a.to_f64()).acos().elem())
676                .into_shared()
677        })
678    }
679
680    fn float_acosh(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
681        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
682            array
683                .mapv_into(|a: FloatElem| (a.to_f64()).acosh().elem())
684                .into_shared()
685        })
686    }
687
688    fn float_asin(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
689        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
690            array
691                .mapv_into(|a: FloatElem| (a.to_f64()).asin().elem())
692                .into_shared()
693        })
694    }
695
696    fn float_asinh(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
697        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
698            array
699                .mapv_into(|a: FloatElem| (a.to_f64()).asinh().elem())
700                .into_shared()
701        })
702    }
703
704    fn float_atan(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
705        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
706            array
707                .mapv_into(|a: FloatElem| (a.to_f64()).atan().elem())
708                .into_shared()
709        })
710    }
711
712    fn float_atanh(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
713        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
714            array
715                .mapv_into(|a: FloatElem| (a.to_f64()).atanh().elem())
716                .into_shared()
717        })
718    }
719
720    fn float_atan2(lhs: FloatTensor<Self>, rhs: FloatTensor<Self>) -> FloatTensor<Self> {
721        execute_with_float_dtype!((lhs, rhs), FloatElem, |lhs, rhs| {
722            NdArrayMathOps::elementwise_op(lhs, rhs, |a: &FloatElem, b: &FloatElem| a.atan2(*b))
723        })
724    }
725
726    fn float_round(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
727        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
728            array
729                .mapv_into(|a: FloatElem| round_ties_even_wrapper(a.to_f64()).elem())
730                .into_shared()
731        })
732    }
733
734    fn float_floor(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
735        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
736            array
737                .mapv_into(|a: FloatElem| (a.to_f64()).floor().elem())
738                .into_shared()
739        })
740    }
741
742    fn float_ceil(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
743        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
744            array
745                .mapv_into(|a: FloatElem| (a.to_f64()).ceil().elem())
746                .into_shared()
747        })
748    }
749
750    fn float_trunc(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
751        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
752            array
753                .mapv_into(|a: FloatElem| (a.to_f64()).trunc().elem())
754                .into_shared()
755        })
756    }
757
758    fn float_erf(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
759        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
760            array
761                .mapv_into(|a: FloatElem| erf(a.to_f64()).elem())
762                .into_shared()
763        })
764    }
765
766    fn float_cat(tensors: Vec<FloatTensor<Self>>, dim: usize) -> FloatTensor<Self> {
767        cat_with_dtype!(tensors, dim, [F64, F32])
768    }
769
770    fn float_clamp_min(tensor: FloatTensor<Self>, min: Scalar) -> FloatTensor<Self> {
771        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
772            NdArrayMathOps::clamp_min(array, min.elem())
773        })
774    }
775
776    fn float_clamp_max(tensor: FloatTensor<Self>, max: Scalar) -> FloatTensor<Self> {
777        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
778            NdArrayMathOps::clamp_max(array, max.elem())
779        })
780    }
781
782    fn float_clamp(tensor: FloatTensor<Self>, min: Scalar, max: Scalar) -> FloatTensor<Self> {
783        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
784            NdArrayMathOps::clamp(array, min.elem(), max.elem())
785        })
786    }
787
788    fn float_into_int(tensor: FloatTensor<Self>, out_dtype: IntDType) -> NdArrayTensor {
789        execute_with_int_out_dtype!(out_dtype, I, {
790            execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
791                array.mapv(|a: FloatElem| a.elem::<I>()).into_shared()
792            })
793        })
794    }
795
796    fn float_powf(lhs: FloatTensor<Self>, rhs: FloatTensor<Self>) -> FloatTensor<Self> {
797        execute_with_float_dtype!((lhs, rhs), FloatElem, |lhs, rhs| {
798            NdArrayMathOps::elementwise_op(lhs, rhs, |a: &FloatElem, b: &FloatElem| a.powf(*b))
799        })
800    }
801
802    fn float_permute(tensor: FloatTensor<Self>, axes: &[usize]) -> FloatTensor<Self> {
803        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
804            NdArrayOps::permute(array, axes)
805        })
806    }
807
808    fn float_flip(tensor: FloatTensor<Self>, axes: &[usize]) -> FloatTensor<Self> {
809        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
810            NdArrayOps::flip(array, axes)
811        })
812    }
813
814    fn float_sign(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
815        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
816            NdArrayMathOps::sign_op(array)
817        })
818    }
819
820    fn float_expand(tensor: FloatTensor<Self>, shape: Shape) -> FloatTensor<Self> {
821        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
822            NdArrayOps::expand(array, shape)
823        })
824    }
825
826    fn float_cast(tensor: FloatTensor<Self>, dtype: FloatDType) -> FloatTensor<Self> {
827        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
828            cast_to_dtype(array, dtype.into())
829        })
830    }
831
832    fn float_grid_sample_2d(
833        tensor: FloatTensor<Self>,
834        grid: FloatTensor<Self>,
835        options: GridSampleOptions,
836    ) -> FloatTensor<Self> {
837        execute_with_float_dtype!((tensor, grid), |tensor, grid| grid_sample_2d(
838            tensor, grid, options
839        ))
840    }
841
842    fn float_unfold(
843        tensor: FloatTensor<Self>,
844        dim: usize,
845        size: usize,
846        step: usize,
847    ) -> FloatTensor<Self> {
848        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
849            NdArrayOps::unfold(array, dim, size, step)
850        })
851    }
852
853    fn float_hypot(lhs: FloatTensor<Self>, rhs: FloatTensor<Self>) -> FloatTensor<Self> {
854        execute_with_float_dtype!((lhs, rhs), FloatElem, |lhs, rhs| {
855            NdArrayMathOps::elementwise_op(lhs, rhs, |a: &FloatElem, b: &FloatElem| a.hypot(*b))
856        })
857    }
858}