Skip to main content

ruda_tensor_host/ops/
float.rs

1//! Float tensor operations for the Host backend.
2
3use alloc::vec::Vec;
4use ruda_tensor::{
5    Distribution, ExecutionError, FloatDType, Scalar, TensorData,
6    ops::{FloatTensorOps, GridSampleOptions},
7    tensor::{BoolTensor, Device, FloatTensor, IntTensor},
8};
9use ruda_tensor::{Shape, Slice};
10
11
12use crate::ops::matmul;
13use crate::ops::unary;
14use crate::{Host, HostTensor};
15
16impl FloatTensorOps<Host> for Host {
17    fn float_from_data(data: TensorData, _device: &Device<Host>) -> FloatTensor<Host> {
18        HostTensor::from_data(data)
19    }
20
21    fn float_random(
22        shape: Shape,
23        distribution: Distribution,
24        _device: &Device<Host>,
25        dtype: FloatDType,
26    ) -> FloatTensor<Host> {
27        rurand_host::float_random(shape, distribution, dtype)
28    }
29
30    async fn float_into_data(tensor: FloatTensor<Host>) -> Result<TensorData, ExecutionError> {
31        Ok(tensor.into_data())
32    }
33
34    fn float_device(_tensor: &FloatTensor<Host>) -> Device<Host> {
35        // CPU backend: all tensors are on the default device
36        Default::default()
37    }
38
39    fn float_to_device(tensor: FloatTensor<Host>, _device: &Device<Host>) -> FloatTensor<Host> {
40        // CPU backend: no-op, tensors are always on CPU
41        tensor
42    }
43
44    fn float_detach(tensor: FloatTensor<Host>) -> FloatTensor<Host> {
45        tensor
46    }
47
48    fn float_into_int(tensor: FloatTensor<Host>, out_dtype: ruda_tensor::IntDType) -> IntTensor<Host> {
49        ruprim_host::cast::float_into_int(tensor, out_dtype)
50    }
51
52    fn float_empty(shape: Shape, _device: &Device<Host>, dtype: FloatDType) -> FloatTensor<Host> {
53        HostTensor::empty(shape, dtype.into())
54    }
55
56    fn float_add(lhs: FloatTensor<Host>, rhs: FloatTensor<Host>) -> FloatTensor<Host> {
57        ruprim_host::binary::dispatch_float::float_add(lhs, rhs)
58    }
59
60    fn float_add_scalar(lhs: FloatTensor<Host>, rhs: Scalar) -> FloatTensor<Host> {
61        ruprim_host::binary::dispatch_float::float_add_scalar(lhs, rhs)
62    }
63
64    fn float_sub(lhs: FloatTensor<Host>, rhs: FloatTensor<Host>) -> FloatTensor<Host> {
65        ruprim_host::binary::dispatch_float::float_sub(lhs, rhs)
66    }
67
68    fn float_sub_scalar(lhs: FloatTensor<Host>, rhs: Scalar) -> FloatTensor<Host> {
69        ruprim_host::binary::dispatch_float::float_sub_scalar(lhs, rhs)
70    }
71
72    fn float_mul(lhs: FloatTensor<Host>, rhs: FloatTensor<Host>) -> FloatTensor<Host> {
73        ruprim_host::binary::dispatch_float::float_mul(lhs, rhs)
74    }
75
76    fn float_mul_scalar(lhs: FloatTensor<Host>, rhs: Scalar) -> FloatTensor<Host> {
77        ruprim_host::binary::dispatch_float::float_mul_scalar(lhs, rhs)
78    }
79
80    fn float_div(lhs: FloatTensor<Host>, rhs: FloatTensor<Host>) -> FloatTensor<Host> {
81        ruprim_host::binary::dispatch_float::float_div(lhs, rhs)
82    }
83
84    fn float_div_scalar(lhs: FloatTensor<Host>, rhs: Scalar) -> FloatTensor<Host> {
85        ruprim_host::binary::dispatch_float::float_div_scalar(lhs, rhs)
86    }
87
88    fn float_remainder(lhs: FloatTensor<Host>, rhs: FloatTensor<Host>) -> FloatTensor<Host> {
89        ruprim_host::binary::dispatch_float::float_remainder(lhs, rhs)
90    }
91
92    fn float_remainder_scalar(lhs: FloatTensor<Host>, rhs: Scalar) -> FloatTensor<Host> {
93        ruprim_host::binary::dispatch_float::float_remainder_scalar(lhs, rhs)
94    }
95
96    fn float_matmul(lhs: FloatTensor<Host>, rhs: FloatTensor<Host>) -> FloatTensor<Host> {
97        matmul::matmul(lhs, rhs)
98    }
99
100    fn float_cross(
101        lhs: FloatTensor<Host>,
102        rhs: FloatTensor<Host>,
103        dim: usize,
104    ) -> FloatTensor<Host> {
105        rublas_host::cross(lhs, rhs, dim)
106    }
107
108    fn float_recip(tensor: FloatTensor<Host>) -> FloatTensor<Host> {
109        unary::recip(tensor)
110    }
111
112    fn float_swap_dims(tensor: FloatTensor<Host>, dim1: usize, dim2: usize) -> FloatTensor<Host> {
113        tensor.transpose(dim1, dim2)
114    }
115
116    fn float_permute(tensor: FloatTensor<Host>, axes: &[usize]) -> FloatTensor<Host> {
117        tensor.permute(axes)
118    }
119
120    fn float_flip(tensor: FloatTensor<Host>, axes: &[usize]) -> FloatTensor<Host> {
121        crate::ops::flip::flip(tensor, axes)
122    }
123
124    fn float_cat(tensors: Vec<FloatTensor<Host>>, dim: usize) -> FloatTensor<Host> {
125        crate::ops::cat::cat(tensors, dim)
126    }
127
128    fn float_reshape(tensor: FloatTensor<Host>, shape: Shape) -> FloatTensor<Host> {
129        tensor.reshape(shape)
130    }
131
132    fn float_gather(
133        dim: usize,
134        tensor: FloatTensor<Host>,
135        indices: IntTensor<Host>,
136    ) -> FloatTensor<Host> {
137        ruprim_host::gather_scatter::dispatch_float::float_gather(dim, tensor, indices)
138    }
139
140    fn float_scatter_add(
141        dim: usize,
142        tensor: FloatTensor<Host>,
143        indices: IntTensor<Host>,
144        value: FloatTensor<Host>,
145    ) -> FloatTensor<Host> {
146        ruprim_host::gather_scatter::dispatch_float::float_scatter_add(dim, tensor, indices, value)
147    }
148
149    fn float_scatter_nd(
150        data: FloatTensor<Host>,
151        indices: IntTensor<Host>,
152        values: FloatTensor<Host>,
153        reduction: ruda_tensor::tensor::IndexingUpdateOp,
154    ) -> FloatTensor<Host> {
155        ruprim_host::gather_scatter::dispatch_float::float_scatter_nd(data, indices, values, reduction)
156    }
157
158    fn float_gather_nd(data: FloatTensor<Host>, indices: IntTensor<Host>) -> FloatTensor<Host> {
159        ruprim_host::gather_scatter::dispatch_float::float_gather_nd(data, indices)
160    }
161
162    fn float_select(
163        tensor: FloatTensor<Host>,
164        dim: usize,
165        indices: IntTensor<Host>,
166    ) -> FloatTensor<Host> {
167        ruprim_host::gather_scatter::dispatch_float::float_select(tensor, dim, indices)
168    }
169
170    fn float_select_add(
171        tensor: FloatTensor<Host>,
172        dim: usize,
173        indices: IntTensor<Host>,
174        value: FloatTensor<Host>,
175    ) -> FloatTensor<Host> {
176        ruprim_host::gather_scatter::dispatch_float::float_select_add(tensor, dim, indices, value)
177    }
178
179    fn float_slice(tensor: FloatTensor<Host>, slices: &[Slice]) -> FloatTensor<Host> {
180        crate::ops::slice::slice(tensor, slices)
181    }
182
183    fn float_slice_assign(
184        tensor: FloatTensor<Host>,
185        slices: &[Slice],
186        value: FloatTensor<Host>,
187    ) -> FloatTensor<Host> {
188        crate::ops::slice::slice_assign(tensor, slices, value)
189    }
190
191    fn float_mask_where(
192        tensor: FloatTensor<Host>,
193        mask: BoolTensor<Host>,
194        value: FloatTensor<Host>,
195    ) -> FloatTensor<Host> {
196        ruprim_host::mask::dispatch::float_mask_where(tensor, mask, value)
197    }
198
199    fn float_mask_fill(
200        tensor: FloatTensor<Host>,
201        mask: BoolTensor<Host>,
202        value: Scalar,
203    ) -> FloatTensor<Host> {
204        ruprim_host::mask::dispatch::float_mask_fill(tensor, mask, value)
205    }
206
207    fn float_equal(
208        lhs: FloatTensor<Host>,
209        rhs: FloatTensor<Host>,
210        out_dtype: ruda_tensor::BoolDType,
211    ) -> BoolTensor<Host> {
212        crate::ops::comparison::equal(lhs, rhs, out_dtype)
213    }
214
215    fn float_equal_elem(
216        lhs: FloatTensor<Host>,
217        rhs: Scalar,
218        out_dtype: ruda_tensor::BoolDType,
219    ) -> BoolTensor<Host> {
220        ruprim_host::comparison::dispatch::float_equal_elem(lhs, rhs, out_dtype)
221    }
222
223    fn float_greater(
224        lhs: FloatTensor<Host>,
225        rhs: FloatTensor<Host>,
226        out_dtype: ruda_tensor::BoolDType,
227    ) -> BoolTensor<Host> {
228        crate::ops::comparison::greater(lhs, rhs, out_dtype)
229    }
230
231    fn float_greater_elem(
232        lhs: FloatTensor<Host>,
233        rhs: Scalar,
234        out_dtype: ruda_tensor::BoolDType,
235    ) -> BoolTensor<Host> {
236        ruprim_host::comparison::dispatch::float_greater_elem(lhs, rhs, out_dtype)
237    }
238
239    fn float_greater_equal(
240        lhs: FloatTensor<Host>,
241        rhs: FloatTensor<Host>,
242        out_dtype: ruda_tensor::BoolDType,
243    ) -> BoolTensor<Host> {
244        crate::ops::comparison::greater_equal(lhs, rhs, out_dtype)
245    }
246
247    fn float_greater_equal_elem(
248        lhs: FloatTensor<Host>,
249        rhs: Scalar,
250        out_dtype: ruda_tensor::BoolDType,
251    ) -> BoolTensor<Host> {
252        ruprim_host::comparison::dispatch::float_greater_equal_elem(lhs, rhs, out_dtype)
253    }
254
255    fn float_lower(
256        lhs: FloatTensor<Host>,
257        rhs: FloatTensor<Host>,
258        out_dtype: ruda_tensor::BoolDType,
259    ) -> BoolTensor<Host> {
260        crate::ops::comparison::lower(lhs, rhs, out_dtype)
261    }
262
263    fn float_lower_elem(
264        lhs: FloatTensor<Host>,
265        rhs: Scalar,
266        out_dtype: ruda_tensor::BoolDType,
267    ) -> BoolTensor<Host> {
268        ruprim_host::comparison::dispatch::float_lower_elem(lhs, rhs, out_dtype)
269    }
270
271    fn float_lower_equal(
272        lhs: FloatTensor<Host>,
273        rhs: FloatTensor<Host>,
274        out_dtype: ruda_tensor::BoolDType,
275    ) -> BoolTensor<Host> {
276        crate::ops::comparison::lower_equal(lhs, rhs, out_dtype)
277    }
278
279    fn float_lower_equal_elem(
280        lhs: FloatTensor<Host>,
281        rhs: Scalar,
282        out_dtype: ruda_tensor::BoolDType,
283    ) -> BoolTensor<Host> {
284        ruprim_host::comparison::dispatch::float_lower_equal_elem(lhs, rhs, out_dtype)
285    }
286
287    fn float_not_equal(
288        lhs: FloatTensor<Host>,
289        rhs: FloatTensor<Host>,
290        out_dtype: ruda_tensor::BoolDType,
291    ) -> BoolTensor<Host> {
292        crate::ops::comparison::not_equal(lhs, rhs, out_dtype)
293    }
294
295    fn float_not_equal_elem(
296        lhs: FloatTensor<Host>,
297        rhs: Scalar,
298        out_dtype: ruda_tensor::BoolDType,
299    ) -> BoolTensor<Host> {
300        ruprim_host::comparison::dispatch::float_not_equal_elem(lhs, rhs, out_dtype)
301    }
302
303    fn float_neg(tensor: FloatTensor<Host>) -> FloatTensor<Host> {
304        ruprim_host::unary::dispatch_float::float_neg(tensor)
305    }
306
307    fn float_clamp(tensor: FloatTensor<Host>, min: Scalar, max: Scalar) -> FloatTensor<Host> {
308        ruprim_host::unary::dispatch_float::float_clamp(tensor, min, max)
309    }
310
311    fn float_clamp_min(tensor: FloatTensor<Host>, min: Scalar) -> FloatTensor<Host> {
312        ruprim_host::unary::dispatch_float::float_clamp_min(tensor, min)
313    }
314
315    fn float_clamp_max(tensor: FloatTensor<Host>, max: Scalar) -> FloatTensor<Host> {
316        ruprim_host::unary::dispatch_float::float_clamp_max(tensor, max)
317    }
318
319    fn float_sign(tensor: FloatTensor<Host>) -> FloatTensor<Host> {
320        ruprim_host::unary::dispatch_float::float_sign(tensor)
321    }
322
323    fn float_mean(tensor: FloatTensor<Host>) -> FloatTensor<Host> {
324        crate::ops::reduce::mean(tensor)
325    }
326
327    fn float_max(tensor: FloatTensor<Host>) -> FloatTensor<Host> {
328        crate::ops::reduce::max(tensor)
329    }
330
331    fn float_max_dim(tensor: FloatTensor<Host>, dim: usize) -> FloatTensor<Host> {
332        crate::ops::reduce::max_dim(tensor, dim)
333    }
334
335    fn float_min(tensor: FloatTensor<Host>) -> FloatTensor<Host> {
336        crate::ops::reduce::min(tensor)
337    }
338
339    fn float_min_dim(tensor: FloatTensor<Host>, dim: usize) -> FloatTensor<Host> {
340        crate::ops::reduce::min_dim(tensor, dim)
341    }
342
343    fn float_max_dim_with_indices(
344        tensor: FloatTensor<Host>,
345        dim: usize,
346        indices_dtype: ruda_tensor::IntDType,
347    ) -> (FloatTensor<Host>, IntTensor<Host>) {
348        ruprim_host::reduce::dispatch::float_max_dim_with_indices(tensor, dim, indices_dtype)
349    }
350
351    fn float_min_dim_with_indices(
352        tensor: FloatTensor<Host>,
353        dim: usize,
354        indices_dtype: ruda_tensor::IntDType,
355    ) -> (FloatTensor<Host>, IntTensor<Host>) {
356        ruprim_host::reduce::dispatch::float_min_dim_with_indices(tensor, dim, indices_dtype)
357    }
358
359    fn float_any(tensor: FloatTensor<Host>, out_dtype: ruda_tensor::BoolDType) -> BoolTensor<Host> {
360        crate::ops::comparison::any_float(tensor, out_dtype)
361    }
362
363    fn float_any_dim(
364        tensor: FloatTensor<Host>,
365        dim: usize,
366        out_dtype: ruda_tensor::BoolDType,
367    ) -> BoolTensor<Host> {
368        crate::ops::comparison::any_float_dim(tensor, dim, out_dtype)
369    }
370
371    fn float_all(tensor: FloatTensor<Host>, out_dtype: ruda_tensor::BoolDType) -> BoolTensor<Host> {
372        crate::ops::comparison::all_float(tensor, out_dtype)
373    }
374
375    fn float_all_dim(
376        tensor: FloatTensor<Host>,
377        dim: usize,
378        out_dtype: ruda_tensor::BoolDType,
379    ) -> BoolTensor<Host> {
380        crate::ops::comparison::all_float_dim(tensor, dim, out_dtype)
381    }
382
383    fn float_sum(tensor: FloatTensor<Host>) -> FloatTensor<Host> {
384        crate::ops::reduce::sum(tensor)
385    }
386
387    fn float_sum_dim(tensor: FloatTensor<Host>, dim: usize) -> FloatTensor<Host> {
388        crate::ops::reduce::sum_dim(tensor, dim)
389    }
390
391    fn float_mean_dim(tensor: FloatTensor<Host>, dim: usize) -> FloatTensor<Host> {
392        crate::ops::reduce::mean_dim(tensor, dim)
393    }
394
395    fn float_prod(tensor: FloatTensor<Host>) -> FloatTensor<Host> {
396        crate::ops::reduce::prod(tensor)
397    }
398
399    fn float_prod_dim(tensor: FloatTensor<Host>, dim: usize) -> FloatTensor<Host> {
400        crate::ops::reduce::prod_dim(tensor, dim)
401    }
402
403    fn float_cumsum(tensor: FloatTensor<Host>, dim: usize) -> FloatTensor<Host> {
404        ruprim_host::cumulative::dispatch::float_cumsum(tensor, dim)
405    }
406
407    fn float_cumprod(tensor: FloatTensor<Host>, dim: usize) -> FloatTensor<Host> {
408        ruprim_host::cumulative::dispatch::float_cumprod(tensor, dim)
409    }
410
411    fn float_cummin(tensor: FloatTensor<Host>, dim: usize) -> FloatTensor<Host> {
412        ruprim_host::cumulative::dispatch::float_cummin(tensor, dim)
413    }
414
415    fn float_cummax(tensor: FloatTensor<Host>, dim: usize) -> FloatTensor<Host> {
416        ruprim_host::cumulative::dispatch::float_cummax(tensor, dim)
417    }
418
419    fn float_cast(tensor: FloatTensor<Host>, dtype: FloatDType) -> FloatTensor<Host> {
420        ruprim_host::cast::float_cast(tensor, dtype)
421    }
422
423    fn float_exp(tensor: FloatTensor<Host>) -> FloatTensor<Host> {
424        unary::exp(tensor)
425    }
426
427    fn float_log(tensor: FloatTensor<Host>) -> FloatTensor<Host> {
428        unary::log(tensor)
429    }
430
431    fn float_log1p(tensor: FloatTensor<Host>) -> FloatTensor<Host> {
432        unary::log1p(tensor)
433    }
434
435    fn float_powf(lhs: FloatTensor<Host>, rhs: FloatTensor<Host>) -> FloatTensor<Host> {
436        ruprim_host::binary::dispatch_float::float_powf(lhs, rhs)
437    }
438
439    fn float_powf_scalar_impl(tensor: FloatTensor<Host>, value: Scalar) -> FloatTensor<Host> {
440        ruprim_host::binary::dispatch_float::float_powf_scalar_impl(tensor, value)
441    }
442
443    fn float_sqrt(tensor: FloatTensor<Host>) -> FloatTensor<Host> {
444        unary::sqrt(tensor)
445    }
446
447    fn float_abs(tensor: FloatTensor<Host>) -> FloatTensor<Host> {
448        unary::abs(tensor)
449    }
450
451    fn float_cos(tensor: FloatTensor<Host>) -> FloatTensor<Host> {
452        unary::cos(tensor)
453    }
454
455    fn float_sin(tensor: FloatTensor<Host>) -> FloatTensor<Host> {
456        unary::sin(tensor)
457    }
458
459    fn float_tan(tensor: FloatTensor<Host>) -> FloatTensor<Host> {
460        unary::tan(tensor)
461    }
462
463    fn float_cosh(tensor: FloatTensor<Host>) -> FloatTensor<Host> {
464        unary::cosh(tensor)
465    }
466
467    fn float_sinh(tensor: FloatTensor<Host>) -> FloatTensor<Host> {
468        unary::sinh(tensor)
469    }
470
471    fn float_tanh(tensor: FloatTensor<Host>) -> FloatTensor<Host> {
472        unary::tanh(tensor)
473    }
474
475    fn float_acos(tensor: FloatTensor<Host>) -> FloatTensor<Host> {
476        unary::acos(tensor)
477    }
478
479    fn float_acosh(tensor: FloatTensor<Host>) -> FloatTensor<Host> {
480        unary::acosh(tensor)
481    }
482
483    fn float_asin(tensor: FloatTensor<Host>) -> FloatTensor<Host> {
484        unary::asin(tensor)
485    }
486
487    fn float_asinh(tensor: FloatTensor<Host>) -> FloatTensor<Host> {
488        unary::asinh(tensor)
489    }
490
491    fn float_atan(tensor: FloatTensor<Host>) -> FloatTensor<Host> {
492        unary::atan(tensor)
493    }
494
495    fn float_atanh(tensor: FloatTensor<Host>) -> FloatTensor<Host> {
496        unary::atanh(tensor)
497    }
498
499    fn float_atan2(lhs: FloatTensor<Host>, rhs: FloatTensor<Host>) -> FloatTensor<Host> {
500        ruprim_host::binary::dispatch_float::float_atan2(lhs, rhs)
501    }
502
503    fn float_hypot(lhs: FloatTensor<Host>, rhs: FloatTensor<Host>) -> FloatTensor<Host> {
504        ruprim_host::binary::dispatch_float::float_hypot(lhs, rhs)
505    }
506
507    fn float_round(tensor: FloatTensor<Host>) -> FloatTensor<Host> {
508        unary::round(tensor)
509    }
510
511    fn float_floor(tensor: FloatTensor<Host>) -> FloatTensor<Host> {
512        unary::floor(tensor)
513    }
514
515    fn float_ceil(tensor: FloatTensor<Host>) -> FloatTensor<Host> {
516        unary::ceil(tensor)
517    }
518
519    fn float_trunc(tensor: FloatTensor<Host>) -> FloatTensor<Host> {
520        unary::trunc(tensor)
521    }
522
523    fn float_erf(tensor: FloatTensor<Host>) -> FloatTensor<Host> {
524        unary::erf(tensor)
525    }
526
527    fn float_argmax(
528        tensor: FloatTensor<Host>,
529        dim: usize,
530        out_dtype: ruda_tensor::IntDType,
531    ) -> IntTensor<Host> {
532        ruprim_host::reduce::dispatch::float_argmax(tensor, dim, out_dtype)
533    }
534
535    fn float_argtopk(
536        tensor: FloatTensor<Host>,
537        dim: usize,
538        k: usize,
539        out_dtype: ruda_tensor::IntDType,
540    ) -> IntTensor<Host> {
541        ruprim_host::sort::dispatch::float_argtopk(tensor, dim, k, out_dtype)
542    }
543
544    fn float_argmin(
545        tensor: FloatTensor<Host>,
546        dim: usize,
547        out_dtype: ruda_tensor::IntDType,
548    ) -> IntTensor<Host> {
549        ruprim_host::reduce::dispatch::float_argmin(tensor, dim, out_dtype)
550    }
551
552    fn float_expand(tensor: FloatTensor<Host>, shape: Shape) -> FloatTensor<Host> {
553        crate::ops::expand::expand(tensor, shape)
554    }
555
556    fn float_unfold(
557        tensor: FloatTensor<Host>,
558        dim: usize,
559        size: usize,
560        step: usize,
561    ) -> FloatTensor<Host> {
562        // unfold is now type-agnostic (zero-copy strided view)
563        crate::ops::unfold::unfold(tensor, dim, size, step)
564    }
565
566    fn float_grid_sample_2d(
567        tensor: FloatTensor<Host>,
568        grid: FloatTensor<Host>,
569        options: GridSampleOptions,
570    ) -> FloatTensor<Host> {
571        crate::ops::grid_sample::grid_sample_2d(tensor, grid, options)
572    }
573
574    fn float_zeros(shape: Shape, _device: &Device<Host>, dtype: FloatDType) -> FloatTensor<Host> {
575        HostTensor::zeros(shape, dtype.into())
576    }
577
578    fn float_ones(shape: Shape, _device: &Device<Host>, dtype: FloatDType) -> FloatTensor<Host> {
579        ruprim_host::fill::float_ones(shape, dtype)
580    }
581
582    fn float_full(
583        shape: Shape,
584        fill_value: Scalar,
585        _device: &Device<Host>,
586        dtype: FloatDType,
587    ) -> FloatTensor<Host> {
588        ruprim_host::fill::float_full(shape, fill_value, dtype)
589    }
590
591    fn float_transpose(tensor: FloatTensor<Host>) -> FloatTensor<Host> {
592        let ndims = tensor.layout().num_dims();
593        if ndims < 2 {
594            return tensor;
595        }
596        tensor.transpose(ndims - 2, ndims - 1)
597    }
598
599    fn float_repeat_dim(tensor: FloatTensor<Host>, dim: usize, times: usize) -> FloatTensor<Host> {
600        crate::ops::repeat_dim::repeat_dim(tensor, dim, times)
601    }
602
603    fn float_sort(tensor: FloatTensor<Host>, dim: usize, descending: bool) -> FloatTensor<Host> {
604        crate::ops::sort::sort(tensor, dim, descending)
605    }
606
607    fn float_sort_with_indices(
608        tensor: FloatTensor<Host>,
609        dim: usize,
610        descending: bool,
611        indices_dtype: ruda_tensor::IntDType,
612    ) -> (FloatTensor<Host>, IntTensor<Host>) {
613        ruprim_host::sort::dispatch::float_sort_with_indices(tensor, dim, descending, indices_dtype)
614    }
615
616    fn float_argsort(
617        tensor: FloatTensor<Host>,
618        dim: usize,
619        descending: bool,
620        out_dtype: ruda_tensor::IntDType,
621    ) -> IntTensor<Host> {
622        ruprim_host::sort::dispatch::float_argsort(tensor, dim, descending, out_dtype)
623    }
624
625    fn float_powi(lhs: FloatTensor<Host>, rhs: IntTensor<Host>) -> FloatTensor<Host> {
626        ruprim_host::binary::dispatch_float::float_powi(lhs, rhs)
627    }
628
629    fn float_powi_scalar(lhs: FloatTensor<Host>, rhs: Scalar) -> FloatTensor<Host> {
630        ruprim_host::binary::dispatch_float::float_powi_scalar(lhs, rhs)
631    }
632
633    fn float_powi_scalar_impl(lhs: FloatTensor<Host>, rhs: Scalar) -> FloatTensor<Host> {
634        ruprim_host::binary::dispatch_float::float_powi_scalar(lhs, rhs)
635    }
636
637    fn float_powf_scalar(tensor: FloatTensor<Host>, value: Scalar) -> FloatTensor<Host> {
638        ruprim_host::binary::dispatch_float::float_powf_scalar(tensor, value)
639    }
640
641    fn float_max_abs(tensor: FloatTensor<Host>) -> FloatTensor<Host> {
642        ruprim_host::reduce::dispatch::float_max_abs(tensor)
643    }
644
645    fn float_max_abs_dim(tensor: FloatTensor<Host>, dim: usize) -> FloatTensor<Host> {
646        ruprim_host::reduce::dispatch::float_max_abs_dim(tensor, dim)
647    }
648
649    fn float_is_nan(tensor: FloatTensor<Host>, out_dtype: ruda_tensor::BoolDType) -> BoolTensor<Host> {
650        ruprim_host::unary::dispatch_float::float_is_nan(tensor, out_dtype)
651    }
652
653    fn float_is_inf(tensor: FloatTensor<Host>, out_dtype: ruda_tensor::BoolDType) -> BoolTensor<Host> {
654        ruprim_host::unary::dispatch_float::float_is_inf(tensor, out_dtype)
655    }
656}
657
658// Tests kept here exercise flex-specific behavior: direct `Host::`
659// backend-op calls with explicit IntDType/FloatDType to pin dtype storage
660// selection (U8/I32/I64, F16/F64). Plain arithmetic, math, cast, cross,
661// unfold, and random smoke tests have been dropped in favor of the
662// equivalent coverage in ruda-backend-tests, which exercises every backend.
663// When adding new tests, keep them here only if they probe flex dtype
664// storage or flex internals; otherwise add them to
665// crates/ruda-backend-tests/tests/tensor/float/ops/.
666#[cfg(test)]
667mod tests {
668    use ruda_tensor::TensorData;
669
670    use crate::Host;
671
672    #[test]
673    fn test_float_into_int_i32() {
674        use ruda_tensor::ops::FloatTensorOps;
675        use ruda_tensor::IntDType;
676
677        let t = crate::HostTensor::from_data(TensorData::from([1.5f32, -2.7, 0.0, 255.9]));
678        let result = Host::float_into_int(t, IntDType::I32);
679        assert_eq!(result.dtype(), ruda_tensor::DType::I32);
680        let data: Vec<i32> = result.into_data().to_vec().unwrap();
681        assert_eq!(data, vec![1, -2, 0, 255]);
682    }
683
684    #[test]
685    fn test_float_into_int_u8() {
686        use ruda_tensor::ops::FloatTensorOps;
687        use ruda_tensor::IntDType;
688
689        let t = crate::HostTensor::from_data(TensorData::from([0.0f32, 1.9, 127.5, 255.0]));
690        let result = Host::float_into_int(t, IntDType::U8);
691        assert_eq!(result.dtype(), ruda_tensor::DType::U8);
692        let data: Vec<u8> = result.into_data().to_vec().unwrap();
693        assert_eq!(data, vec![0, 1, 127, 255]);
694    }
695
696    #[test]
697    fn test_float_argmax_i32_out_dtype() {
698        use ruda_tensor::ops::FloatTensorOps;
699        use ruda_tensor::IntDType;
700
701        let t = crate::HostTensor::from_data(TensorData::from([[1.0f32, 3.0, 2.0]]));
702        let result = Host::float_argmax(t, 1, IntDType::I32);
703        assert_eq!(result.dtype(), ruda_tensor::DType::I32);
704        let data: Vec<i32> = result.into_data().to_vec().unwrap();
705        assert_eq!(data, vec![1]);
706    }
707
708    #[test]
709    fn test_float_argmin_i32_out_dtype() {
710        use ruda_tensor::ops::FloatTensorOps;
711        use ruda_tensor::IntDType;
712
713        let t = crate::HostTensor::from_data(TensorData::from([[3.0f32, 1.0, 2.0]]));
714        let result = Host::float_argmin(t, 1, IntDType::I32);
715        assert_eq!(result.dtype(), ruda_tensor::DType::I32);
716        let data: Vec<i32> = result.into_data().to_vec().unwrap();
717        assert_eq!(data, vec![1]);
718    }
719
720    #[test]
721    fn test_float_argmax_i64_out_dtype() {
722        use ruda_tensor::ops::FloatTensorOps;
723        use ruda_tensor::IntDType;
724
725        let t = crate::HostTensor::from_data(TensorData::from([[1.0f32, 3.0, 2.0]]));
726        let result = Host::float_argmax(t, 1, IntDType::I64);
727        assert_eq!(result.dtype(), ruda_tensor::DType::I64);
728        let data: Vec<i64> = result.into_data().to_vec().unwrap();
729        assert_eq!(data, vec![1]);
730    }
731
732    #[test]
733    fn test_float_max_dim_with_indices_i32() {
734        use ruda_tensor::ops::FloatTensorOps;
735        use ruda_tensor::IntDType;
736
737        let t = crate::HostTensor::from_data(TensorData::from([[1.0f32, 5.0], [3.0, 2.0]]));
738        let (values, indices) = Host::float_max_dim_with_indices(t, 1, IntDType::I32);
739        assert_eq!(indices.dtype(), ruda_tensor::DType::I32);
740        let idx: Vec<i32> = indices.into_data().to_vec().unwrap();
741        assert_eq!(idx, vec![1, 0]);
742        let vals: Vec<f32> = values.into_data().to_vec().unwrap();
743        assert_eq!(vals, vec![5.0, 3.0]);
744    }
745
746    #[test]
747    fn test_float_min_dim_with_indices_i32() {
748        use ruda_tensor::ops::FloatTensorOps;
749        use ruda_tensor::IntDType;
750
751        let t = crate::HostTensor::from_data(TensorData::from([[1.0f32, 5.0], [3.0, 2.0]]));
752        let (values, indices) = Host::float_min_dim_with_indices(t, 1, IntDType::I32);
753        assert_eq!(indices.dtype(), ruda_tensor::DType::I32);
754        let idx: Vec<i32> = indices.into_data().to_vec().unwrap();
755        assert_eq!(idx, vec![0, 1]);
756        let vals: Vec<f32> = values.into_data().to_vec().unwrap();
757        assert_eq!(vals, vec![1.0, 2.0]);
758    }
759
760    #[test]
761    fn test_float_random_f64() {
762        use ruda_tensor::{DType, FloatDType, ops::FloatTensorOps};
763
764        let shape = ruda_tensor::Shape::from(vec![100]);
765        let dist = ruda_tensor::Distribution::Uniform(0.0, 1.0);
766        let device = crate::HostDevice;
767        let t = Host::float_random(shape, dist, &device, FloatDType::F64);
768        assert_eq!(t.dtype(), DType::F64);
769        let data: Vec<f64> = t.into_data().to_vec().unwrap();
770        assert!(data.iter().all(|&v| (0.0..=1.0).contains(&v)));
771    }
772
773    #[test]
774    fn test_float_random_f16() {
775        use ruda_tensor::{DType, FloatDType, ops::FloatTensorOps};
776
777        let shape = ruda_tensor::Shape::from(vec![100]);
778        let dist = ruda_tensor::Distribution::Uniform(0.0, 1.0);
779        let device = crate::HostDevice;
780        let t = Host::float_random(shape, dist, &device, FloatDType::F16);
781        assert_eq!(t.dtype(), DType::F16);
782    }
783}