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_add(
183        dim: usize,
184        tensor: FloatTensor<Self>,
185        indices: NdArrayTensor,
186        value: FloatTensor<Self>,
187    ) -> FloatTensor<Self> {
188        execute_with_int_dtype!(
189            indices,
190            IntElem,
191            |idx_array: SharedArray<IntElem>| -> NdArrayTensor {
192                execute_with_float_dtype!((tensor, value), |tensor, value| NdArrayOps::scatter(
193                    dim, tensor, idx_array, value
194                ))
195            }
196        )
197    }
198
199    fn float_scatter(
200        dim: usize,
201        tensor: FloatTensor<Self>,
202        indices: NdArrayTensor,
203        value: FloatTensor<Self>,
204        update: burn_backend::tensor::IndexingUpdateOp,
205    ) -> FloatTensor<Self> {
206        match update {
207            burn_backend::tensor::IndexingUpdateOp::Add => {
208                Self::float_scatter_add(dim, tensor, indices, value)
209            }
210            burn_backend::tensor::IndexingUpdateOp::Assign => {
211                execute_with_int_dtype!(
212                    indices,
213                    IntElem,
214                    |idx_array: SharedArray<IntElem>| -> NdArrayTensor {
215                        execute_with_float_dtype!((tensor, value), |tensor, value| {
216                            NdArrayOps::scatter_assign(dim, tensor, idx_array, value)
217                        })
218                    }
219                )
220            }
221            other => unimplemented!("float_scatter with {other:?} update is not implemented"),
222        }
223    }
224
225    fn float_scatter_nd(
226        data: FloatTensor<Self>,
227        indices: NdArrayTensor,
228        values: FloatTensor<Self>,
229        reduction: burn_backend::tensor::IndexingUpdateOp,
230    ) -> FloatTensor<Self> {
231        execute_with_int_dtype!(
232            indices,
233            IntElem,
234            |idx_array: SharedArray<IntElem>| -> NdArrayTensor {
235                execute_with_float_dtype!((data, values), |data, values| NdArrayOps::scatter_nd(
236                    data, idx_array, values, reduction
237                ))
238            }
239        )
240    }
241
242    fn float_gather_nd(data: FloatTensor<Self>, indices: NdArrayTensor) -> FloatTensor<Self> {
243        execute_with_int_dtype!(
244            indices,
245            IntElem,
246            |idx_array: SharedArray<IntElem>| -> NdArrayTensor {
247                execute_with_float_dtype!(data, FloatElem, |array: SharedArray<FloatElem>| {
248                    NdArrayOps::gather_nd(array, idx_array)
249                })
250            }
251        )
252    }
253
254    fn float_select(
255        tensor: FloatTensor<Self>,
256        dim: usize,
257        indices: NdArrayTensor,
258    ) -> FloatTensor<Self> {
259        execute_with_int_dtype!(
260            indices,
261            IntElem,
262            |idx_array: SharedArray<IntElem>| -> NdArrayTensor {
263                execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
264                    NdArrayMathOps::select(array, dim, idx_array)
265                })
266            }
267        )
268    }
269
270    fn float_select_add(
271        tensor: FloatTensor<Self>,
272        dim: usize,
273        indices: NdArrayTensor,
274        value: FloatTensor<Self>,
275    ) -> FloatTensor<Self> {
276        execute_with_int_dtype!(
277            indices,
278            IntElem,
279            |idx_array: SharedArray<IntElem>| -> NdArrayTensor {
280                execute_with_float_dtype!((tensor, value), |tensor, value| {
281                    NdArrayMathOps::select_assign(tensor, dim, idx_array, value)
282                })
283            }
284        )
285    }
286
287    fn float_select_assign(
288        tensor: FloatTensor<Self>,
289        dim: usize,
290        indices: NdArrayTensor,
291        value: FloatTensor<Self>,
292        update: burn_backend::tensor::IndexingUpdateOp,
293    ) -> FloatTensor<Self> {
294        match update {
295            burn_backend::tensor::IndexingUpdateOp::Add => {
296                Self::float_select_add(tensor, dim, indices, value)
297            }
298            burn_backend::tensor::IndexingUpdateOp::Assign => {
299                execute_with_int_dtype!(
300                    indices,
301                    IntElem,
302                    |idx_array: SharedArray<IntElem>| -> NdArrayTensor {
303                        execute_with_float_dtype!((tensor, value), |tensor, value| {
304                            NdArrayMathOps::select_assign_replace(tensor, dim, idx_array, value)
305                        })
306                    }
307                )
308            }
309            other => {
310                unimplemented!("float_select_assign with {other:?} update is not implemented")
311            }
312        }
313    }
314
315    fn float_slice(tensor: FloatTensor<Self>, slices: &[burn_backend::Slice]) -> FloatTensor<Self> {
316        slice!(tensor, slices)
317    }
318
319    fn float_slice_assign(
320        tensor: FloatTensor<Self>,
321        slices: &[burn_backend::Slice],
322        value: FloatTensor<Self>,
323    ) -> FloatTensor<Self> {
324        execute_with_float_dtype!((tensor, value), |tensor, value| {
325            NdArrayOps::slice_assign(tensor, slices, value)
326        })
327    }
328
329    fn float_mask_where(
330        tensor: FloatTensor<Self>,
331        mask: NdArrayTensor,
332        value: FloatTensor<Self>,
333    ) -> FloatTensor<Self> {
334        execute_with_float_dtype!((tensor, value), |tensor, value| {
335            NdArrayOps::mask_where(tensor, mask.bool(), value)
336        })
337    }
338
339    fn float_mask_fill(
340        tensor: FloatTensor<Self>,
341        mask: NdArrayTensor,
342        value: Scalar,
343    ) -> FloatTensor<Self> {
344        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
345            NdArrayOps::mask_fill(array, mask.bool(), value.elem())
346        })
347    }
348
349    fn float_equal(
350        lhs: FloatTensor<Self>,
351        rhs: FloatTensor<Self>,
352        _out_dtype: BoolDType,
353    ) -> NdArrayTensor {
354        execute_with_float_dtype!((lhs, rhs), |lhs, rhs| { NdArrayMathOps::equal(lhs, rhs) })
355    }
356
357    fn float_equal_elem(
358        lhs: FloatTensor<Self>,
359        rhs: Scalar,
360        _out_dtype: BoolDType,
361    ) -> NdArrayTensor {
362        execute_with_float_dtype!(lhs, FloatElem, |array: SharedArray<FloatElem>| {
363            NdArrayMathOps::equal_elem(array, rhs.elem())
364        })
365    }
366
367    fn float_greater(
368        lhs: FloatTensor<Self>,
369        rhs: FloatTensor<Self>,
370        _out_dtype: BoolDType,
371    ) -> NdArrayTensor {
372        execute_with_float_dtype!((lhs, rhs), |lhs, rhs| { NdArrayMathOps::greater(lhs, rhs) })
373    }
374
375    fn float_greater_elem(
376        lhs: FloatTensor<Self>,
377        rhs: Scalar,
378        _out_dtype: BoolDType,
379    ) -> NdArrayTensor {
380        execute_with_float_dtype!(lhs, FloatElem, |array: SharedArray<FloatElem>| {
381            NdArrayMathOps::greater_elem(array, rhs.elem())
382        })
383    }
384
385    fn float_greater_equal(
386        lhs: FloatTensor<Self>,
387        rhs: FloatTensor<Self>,
388        _out_dtype: BoolDType,
389    ) -> NdArrayTensor {
390        execute_with_float_dtype!((lhs, rhs), |lhs, rhs| {
391            NdArrayMathOps::greater_equal(lhs, rhs)
392        })
393    }
394
395    fn float_greater_equal_elem(
396        lhs: FloatTensor<Self>,
397        rhs: Scalar,
398        _out_dtype: BoolDType,
399    ) -> NdArrayTensor {
400        execute_with_float_dtype!(lhs, FloatElem, |array: SharedArray<FloatElem>| {
401            NdArrayMathOps::greater_equal_elem(array, rhs.elem())
402        })
403    }
404
405    fn float_lower(
406        lhs: FloatTensor<Self>,
407        rhs: FloatTensor<Self>,
408        _out_dtype: BoolDType,
409    ) -> NdArrayTensor {
410        execute_with_float_dtype!((lhs, rhs), |lhs, rhs| { NdArrayMathOps::lower(lhs, rhs) })
411    }
412
413    fn float_lower_elem(
414        lhs: FloatTensor<Self>,
415        rhs: Scalar,
416        _out_dtype: BoolDType,
417    ) -> NdArrayTensor {
418        execute_with_float_dtype!(lhs, FloatElem, |array: SharedArray<FloatElem>| {
419            NdArrayMathOps::lower_elem(array, rhs.elem())
420        })
421    }
422
423    fn float_lower_equal(
424        lhs: FloatTensor<Self>,
425        rhs: FloatTensor<Self>,
426        _out_dtype: BoolDType,
427    ) -> NdArrayTensor {
428        execute_with_float_dtype!((lhs, rhs), |lhs, rhs| {
429            NdArrayMathOps::lower_equal(lhs, rhs)
430        })
431    }
432
433    fn float_lower_equal_elem(
434        lhs: FloatTensor<Self>,
435        rhs: Scalar,
436        _out_dtype: BoolDType,
437    ) -> NdArrayTensor {
438        execute_with_float_dtype!(lhs, FloatElem, |array: SharedArray<FloatElem>| {
439            NdArrayMathOps::lower_equal_elem(array, rhs.elem())
440        })
441    }
442
443    fn float_detach(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
444        tensor
445    }
446
447    fn float_mean(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
448        // Use view() for zero-copy on borrowed storage
449        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
450            NdArrayMathOps::mean_view(array.view())
451        })
452    }
453
454    fn float_sum(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
455        // Use view() for zero-copy on borrowed storage
456        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
457            NdArrayMathOps::sum_view(array.view())
458        })
459    }
460
461    fn float_mean_dim(tensor: FloatTensor<Self>, dim: usize) -> FloatTensor<Self> {
462        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
463            NdArrayMathOps::mean_dim(array, dim)
464        })
465    }
466
467    fn float_cumsum(tensor: FloatTensor<Self>, dim: usize) -> FloatTensor<Self> {
468        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
469            NdArrayMathOps::cumsum(array, dim)
470        })
471    }
472
473    fn float_cumprod(tensor: FloatTensor<Self>, dim: usize) -> FloatTensor<Self> {
474        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
475            NdArrayMathOps::cumprod(array, dim)
476        })
477    }
478
479    fn float_cummin(tensor: FloatTensor<Self>, dim: usize) -> FloatTensor<Self> {
480        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
481            NdArrayMathOps::cummin(array, dim)
482        })
483    }
484
485    fn float_cummax(tensor: FloatTensor<Self>, dim: usize) -> FloatTensor<Self> {
486        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
487            NdArrayMathOps::cummax(array, dim)
488        })
489    }
490
491    fn float_sum_dim(tensor: FloatTensor<Self>, dim: usize) -> FloatTensor<Self> {
492        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
493            NdArrayMathOps::sum_dim(array, dim)
494        })
495    }
496
497    fn float_argmax(tensor: FloatTensor<Self>, dim: usize, out_dtype: IntDType) -> NdArrayTensor {
498        // Use view() for zero-copy on borrowed storage
499        execute_with_int_out_dtype!(out_dtype, I, {
500            execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
501                NdArrayMathOps::argmax_view::<I>(array.view(), dim)
502            })
503        })
504    }
505
506    fn float_argmin(tensor: FloatTensor<Self>, dim: usize, out_dtype: IntDType) -> NdArrayTensor {
507        // Use view() for zero-copy on borrowed storage
508        execute_with_int_out_dtype!(out_dtype, I, {
509            execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
510                NdArrayMathOps::argmin_view::<I>(array.view(), dim)
511            })
512        })
513    }
514
515    fn float_exp(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
516        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
517            array.mapv_into(|a: FloatElem| a.exp_elem()).into_shared()
518        })
519    }
520
521    fn float_log(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
522        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
523            array.mapv_into(|a: FloatElem| a.log_elem()).into_shared()
524        })
525    }
526
527    fn float_prod(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
528        // Use view() for zero-copy on borrowed storage
529        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
530            NdArrayMathOps::prod_view(array.view())
531        })
532    }
533
534    fn float_prod_dim(tensor: FloatTensor<Self>, dim: usize) -> FloatTensor<Self> {
535        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
536            NdArrayMathOps::prod_dim(array, dim)
537        })
538    }
539
540    fn float_max(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
541        // Use view() for zero-copy on borrowed storage
542        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
543            NdArrayMathOps::max_float_view(array.view())
544        })
545    }
546
547    fn float_min(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
548        // Use view() for zero-copy on borrowed storage
549        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
550            NdArrayMathOps::min_float_view(array.view())
551        })
552    }
553
554    fn float_log1p(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
555        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
556            array.mapv_into(|a: FloatElem| a.log1p_elem()).into_shared()
557        })
558    }
559
560    fn float_powf_scalar_impl(tensor: FloatTensor<Self>, value: Scalar) -> FloatTensor<Self> {
561        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
562            array
563                .mapv_into(|a: FloatElem| a.powf_elem(value.elem()))
564                .into_shared()
565        })
566    }
567
568    fn float_sqrt(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
569        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
570            array.mapv_into(|a: FloatElem| a.sqrt_elem()).into_shared()
571        })
572    }
573
574    fn float_abs(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
575        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
576            NdArrayMathOps::abs(array)
577        })
578    }
579
580    fn float_cos(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
581        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
582            array
583                .mapv_into(|a: FloatElem| (a.to_f64()).cos().elem())
584                .into_shared()
585        })
586    }
587
588    fn float_cosh(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
589        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
590            array
591                .mapv_into(|a: FloatElem| (a.to_f64()).cosh().elem())
592                .into_shared()
593        })
594    }
595
596    fn float_sin(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
597        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
598            array
599                .mapv_into(|a: FloatElem| (a.to_f64()).sin().elem())
600                .into_shared()
601        })
602    }
603
604    fn float_sinh(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
605        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
606            array
607                .mapv_into(|a: FloatElem| (a.to_f64()).sinh().elem())
608                .into_shared()
609        })
610    }
611
612    fn float_tan(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
613        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
614            array
615                .mapv_into(|a: FloatElem| (a.to_f64()).tan().elem())
616                .into_shared()
617        })
618    }
619
620    fn float_tanh(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
621        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
622            array
623                .mapv_into(|a: FloatElem| (a.to_f64()).tanh().elem())
624                .into_shared()
625        })
626    }
627
628    fn float_acos(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
629        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
630            array
631                .mapv_into(|a: FloatElem| (a.to_f64()).acos().elem())
632                .into_shared()
633        })
634    }
635
636    fn float_acosh(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
637        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
638            array
639                .mapv_into(|a: FloatElem| (a.to_f64()).acosh().elem())
640                .into_shared()
641        })
642    }
643
644    fn float_asin(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
645        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
646            array
647                .mapv_into(|a: FloatElem| (a.to_f64()).asin().elem())
648                .into_shared()
649        })
650    }
651
652    fn float_asinh(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
653        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
654            array
655                .mapv_into(|a: FloatElem| (a.to_f64()).asinh().elem())
656                .into_shared()
657        })
658    }
659
660    fn float_atan(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
661        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
662            array
663                .mapv_into(|a: FloatElem| (a.to_f64()).atan().elem())
664                .into_shared()
665        })
666    }
667
668    fn float_atanh(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
669        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
670            array
671                .mapv_into(|a: FloatElem| (a.to_f64()).atanh().elem())
672                .into_shared()
673        })
674    }
675
676    fn float_atan2(lhs: FloatTensor<Self>, rhs: FloatTensor<Self>) -> FloatTensor<Self> {
677        execute_with_float_dtype!((lhs, rhs), FloatElem, |lhs, rhs| {
678            NdArrayMathOps::elementwise_op(lhs, rhs, |a: &FloatElem, b: &FloatElem| a.atan2(*b))
679        })
680    }
681
682    fn float_round(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
683        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
684            array
685                .mapv_into(|a: FloatElem| round_ties_even_wrapper(a.to_f64()).elem())
686                .into_shared()
687        })
688    }
689
690    fn float_floor(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
691        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
692            array
693                .mapv_into(|a: FloatElem| (a.to_f64()).floor().elem())
694                .into_shared()
695        })
696    }
697
698    fn float_ceil(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
699        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
700            array
701                .mapv_into(|a: FloatElem| (a.to_f64()).ceil().elem())
702                .into_shared()
703        })
704    }
705
706    fn float_trunc(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
707        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
708            array
709                .mapv_into(|a: FloatElem| (a.to_f64()).trunc().elem())
710                .into_shared()
711        })
712    }
713
714    fn float_erf(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
715        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
716            array
717                .mapv_into(|a: FloatElem| erf(a.to_f64()).elem())
718                .into_shared()
719        })
720    }
721
722    fn float_cat(tensors: Vec<FloatTensor<Self>>, dim: usize) -> FloatTensor<Self> {
723        cat_with_dtype!(tensors, dim, [F64, F32])
724    }
725
726    fn float_clamp_min(tensor: FloatTensor<Self>, min: Scalar) -> FloatTensor<Self> {
727        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
728            NdArrayMathOps::clamp_min(array, min.elem())
729        })
730    }
731
732    fn float_clamp_max(tensor: FloatTensor<Self>, max: Scalar) -> FloatTensor<Self> {
733        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
734            NdArrayMathOps::clamp_max(array, max.elem())
735        })
736    }
737
738    fn float_clamp(tensor: FloatTensor<Self>, min: Scalar, max: Scalar) -> FloatTensor<Self> {
739        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
740            NdArrayMathOps::clamp(array, min.elem(), max.elem())
741        })
742    }
743
744    fn float_into_int(tensor: FloatTensor<Self>, out_dtype: IntDType) -> NdArrayTensor {
745        execute_with_int_out_dtype!(out_dtype, I, {
746            execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
747                array.mapv(|a: FloatElem| a.elem::<I>()).into_shared()
748            })
749        })
750    }
751
752    fn float_powf(lhs: FloatTensor<Self>, rhs: FloatTensor<Self>) -> FloatTensor<Self> {
753        execute_with_float_dtype!((lhs, rhs), FloatElem, |lhs, rhs| {
754            NdArrayMathOps::elementwise_op(lhs, rhs, |a: &FloatElem, b: &FloatElem| a.powf(*b))
755        })
756    }
757
758    fn float_permute(tensor: FloatTensor<Self>, axes: &[usize]) -> FloatTensor<Self> {
759        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
760            NdArrayOps::permute(array, axes)
761        })
762    }
763
764    fn float_flip(tensor: FloatTensor<Self>, axes: &[usize]) -> FloatTensor<Self> {
765        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
766            NdArrayOps::flip(array, axes)
767        })
768    }
769
770    fn float_sign(tensor: FloatTensor<Self>) -> FloatTensor<Self> {
771        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
772            NdArrayMathOps::sign_op(array)
773        })
774    }
775
776    fn float_expand(tensor: FloatTensor<Self>, shape: Shape) -> FloatTensor<Self> {
777        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
778            NdArrayOps::expand(array, shape)
779        })
780    }
781
782    fn float_cast(tensor: FloatTensor<Self>, dtype: FloatDType) -> FloatTensor<Self> {
783        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
784            cast_to_dtype(array, dtype.into())
785        })
786    }
787
788    fn float_grid_sample_2d(
789        tensor: FloatTensor<Self>,
790        grid: FloatTensor<Self>,
791        options: GridSampleOptions,
792    ) -> FloatTensor<Self> {
793        execute_with_float_dtype!((tensor, grid), |tensor, grid| grid_sample_2d(
794            tensor, grid, options
795        ))
796    }
797
798    fn float_unfold(
799        tensor: FloatTensor<Self>,
800        dim: usize,
801        size: usize,
802        step: usize,
803    ) -> FloatTensor<Self> {
804        execute_with_float_dtype!(tensor, FloatElem, |array: SharedArray<FloatElem>| {
805            NdArrayOps::unfold(array, dim, size, step)
806        })
807    }
808
809    fn float_hypot(lhs: FloatTensor<Self>, rhs: FloatTensor<Self>) -> FloatTensor<Self> {
810        execute_with_float_dtype!((lhs, rhs), FloatElem, |lhs, rhs| {
811            NdArrayMathOps::elementwise_op(lhs, rhs, |a: &FloatElem, b: &FloatElem| a.hypot(*b))
812        })
813    }
814}