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_round(tensor: FloatTensor<Host>) -> FloatTensor<Host> {
504        unary::round(tensor)
505    }
506
507    fn float_floor(tensor: FloatTensor<Host>) -> FloatTensor<Host> {
508        unary::floor(tensor)
509    }
510
511    fn float_ceil(tensor: FloatTensor<Host>) -> FloatTensor<Host> {
512        unary::ceil(tensor)
513    }
514
515    fn float_trunc(tensor: FloatTensor<Host>) -> FloatTensor<Host> {
516        unary::trunc(tensor)
517    }
518
519    fn float_erf(tensor: FloatTensor<Host>) -> FloatTensor<Host> {
520        unary::erf(tensor)
521    }
522
523    fn float_argmax(
524        tensor: FloatTensor<Host>,
525        dim: usize,
526        out_dtype: ruda_tensor::IntDType,
527    ) -> IntTensor<Host> {
528        ruprim_host::reduce::dispatch::float_argmax(tensor, dim, out_dtype)
529    }
530
531    fn float_argtopk(
532        tensor: FloatTensor<Host>,
533        dim: usize,
534        k: usize,
535        out_dtype: ruda_tensor::IntDType,
536    ) -> IntTensor<Host> {
537        ruprim_host::sort::dispatch::float_argtopk(tensor, dim, k, out_dtype)
538    }
539
540    fn float_argmin(
541        tensor: FloatTensor<Host>,
542        dim: usize,
543        out_dtype: ruda_tensor::IntDType,
544    ) -> IntTensor<Host> {
545        ruprim_host::reduce::dispatch::float_argmin(tensor, dim, out_dtype)
546    }
547
548    fn float_expand(tensor: FloatTensor<Host>, shape: Shape) -> FloatTensor<Host> {
549        crate::ops::expand::expand(tensor, shape)
550    }
551
552    fn float_unfold(
553        tensor: FloatTensor<Host>,
554        dim: usize,
555        size: usize,
556        step: usize,
557    ) -> FloatTensor<Host> {
558        // unfold is now type-agnostic (zero-copy strided view)
559        crate::ops::unfold::unfold(tensor, dim, size, step)
560    }
561
562    fn float_grid_sample_2d(
563        tensor: FloatTensor<Host>,
564        grid: FloatTensor<Host>,
565        options: GridSampleOptions,
566    ) -> FloatTensor<Host> {
567        crate::ops::grid_sample::grid_sample_2d(tensor, grid, options)
568    }
569
570    fn float_zeros(shape: Shape, _device: &Device<Host>, dtype: FloatDType) -> FloatTensor<Host> {
571        HostTensor::zeros(shape, dtype.into())
572    }
573
574    fn float_ones(shape: Shape, _device: &Device<Host>, dtype: FloatDType) -> FloatTensor<Host> {
575        ruprim_host::fill::float_ones(shape, dtype)
576    }
577
578    fn float_full(
579        shape: Shape,
580        fill_value: Scalar,
581        _device: &Device<Host>,
582        dtype: FloatDType,
583    ) -> FloatTensor<Host> {
584        ruprim_host::fill::float_full(shape, fill_value, dtype)
585    }
586
587    fn float_transpose(tensor: FloatTensor<Host>) -> FloatTensor<Host> {
588        let ndims = tensor.layout().num_dims();
589        if ndims < 2 {
590            return tensor;
591        }
592        tensor.transpose(ndims - 2, ndims - 1)
593    }
594
595    fn float_repeat_dim(tensor: FloatTensor<Host>, dim: usize, times: usize) -> FloatTensor<Host> {
596        crate::ops::repeat_dim::repeat_dim(tensor, dim, times)
597    }
598
599    fn float_sort(tensor: FloatTensor<Host>, dim: usize, descending: bool) -> FloatTensor<Host> {
600        crate::ops::sort::sort(tensor, dim, descending)
601    }
602
603    fn float_sort_with_indices(
604        tensor: FloatTensor<Host>,
605        dim: usize,
606        descending: bool,
607        indices_dtype: ruda_tensor::IntDType,
608    ) -> (FloatTensor<Host>, IntTensor<Host>) {
609        ruprim_host::sort::dispatch::float_sort_with_indices(tensor, dim, descending, indices_dtype)
610    }
611
612    fn float_argsort(
613        tensor: FloatTensor<Host>,
614        dim: usize,
615        descending: bool,
616        out_dtype: ruda_tensor::IntDType,
617    ) -> IntTensor<Host> {
618        ruprim_host::sort::dispatch::float_argsort(tensor, dim, descending, out_dtype)
619    }
620
621    fn float_powi(lhs: FloatTensor<Host>, rhs: IntTensor<Host>) -> FloatTensor<Host> {
622        ruprim_host::binary::dispatch_float::float_powi(lhs, rhs)
623    }
624
625    fn float_powi_scalar(lhs: FloatTensor<Host>, rhs: Scalar) -> FloatTensor<Host> {
626        ruprim_host::binary::dispatch_float::float_powi_scalar(lhs, rhs)
627    }
628
629    fn float_powi_scalar_impl(lhs: FloatTensor<Host>, rhs: Scalar) -> FloatTensor<Host> {
630        ruprim_host::binary::dispatch_float::float_powi_scalar(lhs, rhs)
631    }
632
633    fn float_powf_scalar(tensor: FloatTensor<Host>, value: Scalar) -> FloatTensor<Host> {
634        ruprim_host::binary::dispatch_float::float_powf_scalar(tensor, value)
635    }
636
637    fn float_max_abs(tensor: FloatTensor<Host>) -> FloatTensor<Host> {
638        ruprim_host::reduce::dispatch::float_max_abs(tensor)
639    }
640
641    fn float_max_abs_dim(tensor: FloatTensor<Host>, dim: usize) -> FloatTensor<Host> {
642        ruprim_host::reduce::dispatch::float_max_abs_dim(tensor, dim)
643    }
644
645    fn float_is_nan(tensor: FloatTensor<Host>, out_dtype: ruda_tensor::BoolDType) -> BoolTensor<Host> {
646        ruprim_host::unary::dispatch_float::float_is_nan(tensor, out_dtype)
647    }
648
649    fn float_is_inf(tensor: FloatTensor<Host>, out_dtype: ruda_tensor::BoolDType) -> BoolTensor<Host> {
650        ruprim_host::unary::dispatch_float::float_is_inf(tensor, out_dtype)
651    }
652}
653
654// Tests kept here exercise flex-specific behavior: direct `Host::`
655// backend-op calls with explicit IntDType/FloatDType to pin dtype storage
656// selection (U8/I32/I64, F16/F64). Plain arithmetic, math, cast, cross,
657// unfold, and random smoke tests have been dropped in favor of the
658// equivalent coverage in ruda-backend-tests, which exercises every backend.
659// When adding new tests, keep them here only if they probe flex dtype
660// storage or flex internals; otherwise add them to
661// crates/ruda-backend-tests/tests/tensor/float/ops/.
662#[cfg(test)]
663mod tests {
664    use ruda_tensor::TensorData;
665
666    use crate::Host;
667
668    #[test]
669    fn test_float_into_int_i32() {
670        use ruda_tensor::ops::FloatTensorOps;
671        use ruda_tensor::IntDType;
672
673        let t = crate::HostTensor::from_data(TensorData::from([1.5f32, -2.7, 0.0, 255.9]));
674        let result = Host::float_into_int(t, IntDType::I32);
675        assert_eq!(result.dtype(), ruda_tensor::DType::I32);
676        let data: Vec<i32> = result.into_data().to_vec().unwrap();
677        assert_eq!(data, vec![1, -2, 0, 255]);
678    }
679
680    #[test]
681    fn test_float_into_int_u8() {
682        use ruda_tensor::ops::FloatTensorOps;
683        use ruda_tensor::IntDType;
684
685        let t = crate::HostTensor::from_data(TensorData::from([0.0f32, 1.9, 127.5, 255.0]));
686        let result = Host::float_into_int(t, IntDType::U8);
687        assert_eq!(result.dtype(), ruda_tensor::DType::U8);
688        let data: Vec<u8> = result.into_data().to_vec().unwrap();
689        assert_eq!(data, vec![0, 1, 127, 255]);
690    }
691
692    #[test]
693    fn test_float_argmax_i32_out_dtype() {
694        use ruda_tensor::ops::FloatTensorOps;
695        use ruda_tensor::IntDType;
696
697        let t = crate::HostTensor::from_data(TensorData::from([[1.0f32, 3.0, 2.0]]));
698        let result = Host::float_argmax(t, 1, IntDType::I32);
699        assert_eq!(result.dtype(), ruda_tensor::DType::I32);
700        let data: Vec<i32> = result.into_data().to_vec().unwrap();
701        assert_eq!(data, vec![1]);
702    }
703
704    #[test]
705    fn test_float_argmin_i32_out_dtype() {
706        use ruda_tensor::ops::FloatTensorOps;
707        use ruda_tensor::IntDType;
708
709        let t = crate::HostTensor::from_data(TensorData::from([[3.0f32, 1.0, 2.0]]));
710        let result = Host::float_argmin(t, 1, IntDType::I32);
711        assert_eq!(result.dtype(), ruda_tensor::DType::I32);
712        let data: Vec<i32> = result.into_data().to_vec().unwrap();
713        assert_eq!(data, vec![1]);
714    }
715
716    #[test]
717    fn test_float_argmax_i64_out_dtype() {
718        use ruda_tensor::ops::FloatTensorOps;
719        use ruda_tensor::IntDType;
720
721        let t = crate::HostTensor::from_data(TensorData::from([[1.0f32, 3.0, 2.0]]));
722        let result = Host::float_argmax(t, 1, IntDType::I64);
723        assert_eq!(result.dtype(), ruda_tensor::DType::I64);
724        let data: Vec<i64> = result.into_data().to_vec().unwrap();
725        assert_eq!(data, vec![1]);
726    }
727
728    #[test]
729    fn test_float_max_dim_with_indices_i32() {
730        use ruda_tensor::ops::FloatTensorOps;
731        use ruda_tensor::IntDType;
732
733        let t = crate::HostTensor::from_data(TensorData::from([[1.0f32, 5.0], [3.0, 2.0]]));
734        let (values, indices) = Host::float_max_dim_with_indices(t, 1, IntDType::I32);
735        assert_eq!(indices.dtype(), ruda_tensor::DType::I32);
736        let idx: Vec<i32> = indices.into_data().to_vec().unwrap();
737        assert_eq!(idx, vec![1, 0]);
738        let vals: Vec<f32> = values.into_data().to_vec().unwrap();
739        assert_eq!(vals, vec![5.0, 3.0]);
740    }
741
742    #[test]
743    fn test_float_min_dim_with_indices_i32() {
744        use ruda_tensor::ops::FloatTensorOps;
745        use ruda_tensor::IntDType;
746
747        let t = crate::HostTensor::from_data(TensorData::from([[1.0f32, 5.0], [3.0, 2.0]]));
748        let (values, indices) = Host::float_min_dim_with_indices(t, 1, IntDType::I32);
749        assert_eq!(indices.dtype(), ruda_tensor::DType::I32);
750        let idx: Vec<i32> = indices.into_data().to_vec().unwrap();
751        assert_eq!(idx, vec![0, 1]);
752        let vals: Vec<f32> = values.into_data().to_vec().unwrap();
753        assert_eq!(vals, vec![1.0, 2.0]);
754    }
755
756    #[test]
757    fn test_float_random_f64() {
758        use ruda_tensor::{DType, FloatDType, ops::FloatTensorOps};
759
760        let shape = ruda_tensor::Shape::from(vec![100]);
761        let dist = ruda_tensor::Distribution::Uniform(0.0, 1.0);
762        let device = crate::HostDevice;
763        let t = Host::float_random(shape, dist, &device, FloatDType::F64);
764        assert_eq!(t.dtype(), DType::F64);
765        let data: Vec<f64> = t.into_data().to_vec().unwrap();
766        assert!(data.iter().all(|&v| (0.0..=1.0).contains(&v)));
767    }
768
769    #[test]
770    fn test_float_random_f16() {
771        use ruda_tensor::{DType, FloatDType, ops::FloatTensorOps};
772
773        let shape = ruda_tensor::Shape::from(vec![100]);
774        let dist = ruda_tensor::Distribution::Uniform(0.0, 1.0);
775        let device = crate::HostDevice;
776        let t = Host::float_random(shape, dist, &device, FloatDType::F16);
777        assert_eq!(t.dtype(), DType::F16);
778    }
779}