Skip to main content

ruda_tensor_host/ops/
bool.rs

1//! Bool tensor operations for the Host backend.
2
3use alloc::vec::Vec;
4use ruda_tensor::{
5    DType, ExecutionError, TensorData,
6    ops::BoolTensorOps,
7    tensor::{BoolTensor, Device, FloatTensor, IntTensor},
8};
9use ruda_tensor::{IntDType, Shape, Slice};
10
11use crate::{Host, HostTensor};
12
13impl BoolTensorOps<Host> for Host {
14    fn bool_from_data(data: TensorData, _device: &Device<Host>) -> BoolTensor<Host> {
15        HostTensor::from_data(data)
16    }
17
18    async fn bool_into_data(tensor: BoolTensor<Host>) -> Result<TensorData, ExecutionError> {
19        Ok(tensor.into_data())
20    }
21
22    fn bool_device(_tensor: &BoolTensor<Host>) -> Device<Host> {
23        Default::default()
24    }
25
26    fn bool_to_device(tensor: BoolTensor<Host>, _device: &Device<Host>) -> BoolTensor<Host> {
27        tensor
28    }
29
30    fn bool_cat(tensors: Vec<BoolTensor<Host>>, dim: usize) -> BoolTensor<Host> {
31        crate::ops::cat::cat(tensors, dim)
32    }
33
34    fn bool_reshape(tensor: BoolTensor<Host>, shape: Shape) -> BoolTensor<Host> {
35        tensor.reshape(shape)
36    }
37
38    fn bool_slice(tensor: BoolTensor<Host>, slices: &[Slice]) -> BoolTensor<Host> {
39        crate::ops::slice::slice(tensor, slices)
40    }
41
42    fn bool_empty(
43        shape: Shape,
44        _device: &Device<Host>,
45        dtype: ruda_tensor::BoolDType,
46    ) -> BoolTensor<Host> {
47        HostTensor::empty(shape, DType::from(dtype))
48    }
49
50    fn bool_slice_assign(
51        tensor: BoolTensor<Host>,
52        slices: &[Slice],
53        value: BoolTensor<Host>,
54    ) -> BoolTensor<Host> {
55        crate::ops::slice::slice_assign(tensor, slices, value)
56    }
57
58    fn bool_into_int(tensor: BoolTensor<Host>, out_dtype: ruda_tensor::IntDType) -> IntTensor<Host> {
59        ruprim_host::cast::bool_into_int(tensor, out_dtype)
60    }
61
62    fn bool_into_float(
63        tensor: BoolTensor<Host>,
64        out_dtype: ruda_tensor::FloatDType,
65    ) -> FloatTensor<Host> {
66        ruprim_host::cast::bool_into_float(tensor, out_dtype)
67    }
68
69    fn bool_swap_dims(tensor: BoolTensor<Host>, dim1: usize, dim2: usize) -> BoolTensor<Host> {
70        tensor.transpose(dim1, dim2)
71    }
72
73    fn bool_permute(tensor: BoolTensor<Host>, axes: &[usize]) -> BoolTensor<Host> {
74        tensor.permute(axes)
75    }
76
77    fn bool_flip(tensor: BoolTensor<Host>, axes: &[usize]) -> BoolTensor<Host> {
78        crate::ops::flip::flip(tensor, axes)
79    }
80
81    fn bool_equal(lhs: BoolTensor<Host>, rhs: BoolTensor<Host>) -> BoolTensor<Host> {
82        ruprim_host::boolean::bool_equal(lhs, rhs)
83    }
84
85    fn bool_not(mut tensor: BoolTensor<Host>) -> BoolTensor<Host> {
86        ruprim_host::boolean::bool_not(tensor)
87    }
88
89    fn bool_and(lhs: BoolTensor<Host>, rhs: BoolTensor<Host>) -> BoolTensor<Host> {
90        ruprim_host::boolean::bool_and(lhs, rhs)
91    }
92
93    fn bool_or(lhs: BoolTensor<Host>, rhs: BoolTensor<Host>) -> BoolTensor<Host> {
94        ruprim_host::boolean::bool_or(lhs, rhs)
95    }
96
97    fn bool_xor(lhs: BoolTensor<Host>, rhs: BoolTensor<Host>) -> BoolTensor<Host> {
98        ruprim_host::boolean::bool_xor(lhs, rhs)
99    }
100
101    fn bool_expand(tensor: BoolTensor<Host>, shape: Shape) -> BoolTensor<Host> {
102        crate::ops::expand::expand(tensor, shape)
103    }
104
105    // Missing methods
106    fn bool_zeros(
107        shape: Shape,
108        device: &Device<Host>,
109        dtype: ruda_tensor::BoolDType,
110    ) -> BoolTensor<Host> {
111        Self::bool_empty(shape, device, dtype)
112    }
113
114    fn bool_ones(
115        shape: Shape,
116        _device: &Device<Host>,
117        dtype: ruda_tensor::BoolDType,
118    ) -> BoolTensor<Host> {
119        ruprim_host::boolean::bool_ones(shape, dtype)
120    }
121
122    fn bool_mask_where(
123        tensor: BoolTensor<Host>,
124        mask: BoolTensor<Host>,
125        value: BoolTensor<Host>,
126    ) -> BoolTensor<Host> {
127        crate::ops::mask::mask_where_bool(tensor, mask, value)
128    }
129
130    fn bool_mask_fill(
131        tensor: BoolTensor<Host>,
132        mask: BoolTensor<Host>,
133        value: ruda_tensor::Scalar,
134    ) -> BoolTensor<Host> {
135        let value: bool = value.elem();
136        crate::ops::mask::mask_fill_bool(tensor, mask, value)
137    }
138
139    fn bool_gather(
140        dim: usize,
141        tensor: BoolTensor<Host>,
142        indices: IntTensor<Host>,
143    ) -> BoolTensor<Host> {
144        crate::ops::gather_scatter::gather_bool(tensor, dim, indices)
145    }
146
147    fn bool_scatter_or(
148        dim: usize,
149        tensor: BoolTensor<Host>,
150        indices: IntTensor<Host>,
151        value: BoolTensor<Host>,
152    ) -> BoolTensor<Host> {
153        crate::ops::gather_scatter::scatter_or(tensor, dim, indices, value)
154    }
155
156    fn bool_equal_elem(lhs: BoolTensor<Host>, rhs: ruda_tensor::Scalar) -> BoolTensor<Host> {
157        ruprim_host::boolean::bool_equal_elem(lhs, rhs)
158    }
159
160    fn bool_unfold(
161        tensor: BoolTensor<Host>,
162        dim: usize,
163        size: usize,
164        step: usize,
165    ) -> BoolTensor<Host> {
166        crate::ops::unfold::unfold_bool(tensor, dim, size, step)
167    }
168
169    fn bool_not_equal(lhs: BoolTensor<Host>, rhs: BoolTensor<Host>) -> BoolTensor<Host> {
170        let out_dtype = ruda_tensor::BoolDType::from(lhs.dtype());
171        crate::ops::comparison::bool_not_equal(lhs, rhs, out_dtype)
172    }
173
174    fn bool_not_equal_elem(lhs: BoolTensor<Host>, rhs: ruda_tensor::Scalar) -> BoolTensor<Host> {
175        let out_dtype = ruda_tensor::BoolDType::from(lhs.dtype());
176        let rhs: bool = rhs.elem();
177        crate::ops::comparison::bool_not_equal_elem(lhs, rhs, out_dtype)
178    }
179
180    fn bool_any(tensor: BoolTensor<Host>) -> BoolTensor<Host> {
181        let out_dtype = ruda_tensor::BoolDType::from(tensor.dtype());
182        crate::ops::comparison::any_bool(tensor, out_dtype)
183    }
184
185    fn bool_any_dim(tensor: BoolTensor<Host>, dim: usize) -> BoolTensor<Host> {
186        let out_dtype = ruda_tensor::BoolDType::from(tensor.dtype());
187        crate::ops::comparison::any_bool_dim(tensor, dim, out_dtype)
188    }
189
190    fn bool_all(tensor: BoolTensor<Host>) -> BoolTensor<Host> {
191        let out_dtype = ruda_tensor::BoolDType::from(tensor.dtype());
192        crate::ops::comparison::all_bool(tensor, out_dtype)
193    }
194
195    fn bool_all_dim(tensor: BoolTensor<Host>, dim: usize) -> BoolTensor<Host> {
196        let out_dtype = ruda_tensor::BoolDType::from(tensor.dtype());
197        crate::ops::comparison::all_bool_dim(tensor, dim, out_dtype)
198    }
199
200    fn bool_select(
201        tensor: BoolTensor<Host>,
202        dim: usize,
203        indices: IntTensor<Host>,
204    ) -> BoolTensor<Host> {
205        crate::ops::gather_scatter::select::<u8>(tensor, dim, indices)
206    }
207
208    fn bool_select_or(
209        tensor: BoolTensor<Host>,
210        dim: usize,
211        indices: IntTensor<Host>,
212        value: BoolTensor<Host>,
213    ) -> BoolTensor<Host> {
214        ruprim_host::boolean::bool_select_or(tensor, dim, indices, value)
215    }
216
217    fn bool_transpose(tensor: BoolTensor<Host>) -> BoolTensor<Host> {
218        let ndims = tensor.layout().num_dims();
219        if ndims < 2 {
220            return tensor;
221        }
222        tensor.transpose(ndims - 2, ndims - 1)
223    }
224
225    fn bool_repeat_dim(tensor: BoolTensor<Host>, dim: usize, times: usize) -> BoolTensor<Host> {
226        crate::ops::repeat_dim::repeat_dim(tensor, dim, times)
227    }
228
229    async fn bool_argwhere(tensor: BoolTensor<Host>, out_dtype: IntDType) -> IntTensor<Host> {
230        ruprim_host::boolean::bool_argwhere(tensor, out_dtype).await
231    }
232}
233
234// Tests kept here exercise flex-specific dtype storage selection via
235// explicit IntDType/FloatDType. Plain bool ops, bool-to-int/float
236// casts, and negative-stride (flipped) bool coverage have been migrated
237// to crates/ruda-backend-tests/tests/tensor/bool/ops/{logical,cast}.rs
238// so they run against every backend. When adding new tests, keep them
239// here only if they probe flex dtype dispatch; otherwise add them
240// there.
241#[cfg(test)]
242mod tests {
243    use alloc::vec;
244    use ruda_tensor::TensorData;
245    use ruda_tensor::ops::BoolTensorOps;
246    use ruda_tensor::{FloatDType, IntDType};
247
248    use crate::{Host, HostTensor};
249
250    #[test]
251    fn test_bool_into_int_u8() {
252        let t = HostTensor::from_data(TensorData::from([true, false, true]));
253        let result = Host::bool_into_int(t, IntDType::U8);
254        assert_eq!(result.dtype(), ruda_tensor::DType::U8);
255        let data: Vec<u8> = result.into_data().to_vec().unwrap();
256        assert_eq!(data, vec![1u8, 0, 1]);
257    }
258
259    #[test]
260    fn test_bool_into_float_f64() {
261        let t = HostTensor::from_data(TensorData::from([true, false, true]));
262        let result = Host::bool_into_float(t, FloatDType::F64);
263        assert_eq!(result.dtype(), ruda_tensor::DType::F64);
264        let data: Vec<f64> = result.into_data().to_vec().unwrap();
265        assert_eq!(data, vec![1.0f64, 0.0, 1.0]);
266    }
267}