1use alloc::vec;
4use alloc::vec::Vec;
5use burn_backend::{
6 DType, ExecutionError, TensorData,
7 ops::{BoolTensorOps, IntTensorOps},
8 tensor::{BoolTensor, Device, FloatTensor, IntTensor},
9};
10use burn_std::{Bytes, FloatDType, IntDType, Shape, Slice, bf16, f16};
11
12use crate::{Flex, FlexTensor, Layout};
13
14impl BoolTensorOps<Flex> for Flex {
15 fn bool_from_data(data: TensorData, _device: &Device<Flex>) -> BoolTensor<Flex> {
16 FlexTensor::from_data(data)
17 }
18
19 async fn bool_into_data(tensor: BoolTensor<Flex>) -> Result<TensorData, ExecutionError> {
20 Ok(tensor.into_data())
21 }
22
23 fn bool_to_device(tensor: BoolTensor<Flex>, _device: &Device<Flex>) -> BoolTensor<Flex> {
24 tensor
25 }
26
27 fn bool_cat(tensors: Vec<BoolTensor<Flex>>, dim: usize) -> BoolTensor<Flex> {
28 crate::ops::cat::cat(tensors, dim)
29 }
30
31 fn bool_reshape(tensor: BoolTensor<Flex>, shape: Shape) -> BoolTensor<Flex> {
32 tensor.reshape(shape)
33 }
34
35 fn bool_slice(tensor: BoolTensor<Flex>, slices: &[Slice]) -> BoolTensor<Flex> {
36 crate::ops::slice::slice(tensor, slices)
37 }
38
39 fn bool_empty(
40 shape: Shape,
41 _device: &Device<Flex>,
42 dtype: burn_std::BoolDType,
43 ) -> BoolTensor<Flex> {
44 FlexTensor::empty(shape, DType::from(dtype))
45 }
46
47 fn bool_slice_assign(
48 tensor: BoolTensor<Flex>,
49 slices: &[Slice],
50 value: BoolTensor<Flex>,
51 ) -> BoolTensor<Flex> {
52 crate::ops::slice::slice_assign(tensor, slices, value)
53 }
54
55 fn bool_into_int(tensor: BoolTensor<Flex>, out_dtype: burn_std::IntDType) -> IntTensor<Flex> {
56 let tensor = tensor.to_contiguous();
57 let shape = tensor.layout().shape().clone();
58 let out_dt = DType::from(out_dtype);
59 let bools = tensor.bytes();
60
61 macro_rules! convert {
62 ($int_ty:ty) => {{
63 let data: Vec<$int_ty> =
64 bools.iter().map(|&x| if x != 0 { 1 } else { 0 }).collect();
65 FlexTensor::new(Bytes::from_elems(data), Layout::contiguous(shape), out_dt)
66 }};
67 }
68
69 match out_dtype {
70 IntDType::I64 => convert!(i64),
71 IntDType::I32 => convert!(i32),
72 IntDType::I16 => convert!(i16),
73 IntDType::I8 => convert!(i8),
74 IntDType::U64 => convert!(u64),
75 IntDType::U32 => convert!(u32),
76 IntDType::U16 => convert!(u16),
77 IntDType::U8 => convert!(u8),
78 }
79 }
80
81 fn bool_into_float(
82 tensor: BoolTensor<Flex>,
83 out_dtype: burn_std::FloatDType,
84 ) -> FloatTensor<Flex> {
85 let tensor = tensor.to_contiguous();
86 let shape = tensor.layout().shape().clone();
87 let out_dt = DType::from(out_dtype);
88 let bools = tensor.bytes();
89
90 match out_dtype {
91 FloatDType::F64 => {
92 let data: Vec<f64> = bools
93 .iter()
94 .map(|&x| if x != 0 { 1.0 } else { 0.0 })
95 .collect();
96 FlexTensor::new(Bytes::from_elems(data), Layout::contiguous(shape), out_dt)
97 }
98 FloatDType::F32 | FloatDType::Flex32 => {
99 let data: Vec<f32> = bools
100 .iter()
101 .map(|&x| if x != 0 { 1.0 } else { 0.0 })
102 .collect();
103 FlexTensor::new(Bytes::from_elems(data), Layout::contiguous(shape), out_dt)
104 }
105 FloatDType::F16 => {
106 let one = f16::from_f32(1.0);
107 let zero = f16::from_f32(0.0);
108 let data: Vec<f16> = bools
109 .iter()
110 .map(|&x| if x != 0 { one } else { zero })
111 .collect();
112 FlexTensor::new(Bytes::from_elems(data), Layout::contiguous(shape), out_dt)
113 }
114 FloatDType::BF16 => {
115 let one = bf16::from_f32(1.0);
116 let zero = bf16::from_f32(0.0);
117 let data: Vec<bf16> = bools
118 .iter()
119 .map(|&x| if x != 0 { one } else { zero })
120 .collect();
121 FlexTensor::new(Bytes::from_elems(data), Layout::contiguous(shape), out_dt)
122 }
123 }
124 }
125
126 fn bool_swap_dims(tensor: BoolTensor<Flex>, dim1: usize, dim2: usize) -> BoolTensor<Flex> {
127 tensor.transpose(dim1, dim2)
128 }
129
130 fn bool_permute(tensor: BoolTensor<Flex>, axes: &[usize]) -> BoolTensor<Flex> {
131 tensor.permute(axes)
132 }
133
134 fn bool_flip(tensor: BoolTensor<Flex>, axes: &[usize]) -> BoolTensor<Flex> {
135 crate::ops::flip::flip(tensor, axes)
136 }
137
138 fn bool_equal(lhs: BoolTensor<Flex>, rhs: BoolTensor<Flex>) -> BoolTensor<Flex> {
139 use crate::strided_index::StridedIter;
140
141 let (lhs, rhs) = crate::ops::expand::broadcast_binary(lhs, rhs);
147
148 let out_dtype = burn_std::BoolDType::from(lhs.dtype());
149 let shape = lhs.layout().shape().clone();
150 let lhs_storage: &[u8] = lhs.bytes();
151 let rhs_storage: &[u8] = rhs.bytes();
152
153 let result: Vec<u8> = match (
154 lhs.layout().contiguous_offsets(),
155 rhs.layout().contiguous_offsets(),
156 ) {
157 (Some((l_start, l_end)), Some((r_start, r_end))) => {
158 let l_slice = &lhs_storage[l_start..l_end];
159 let r_slice = &rhs_storage[r_start..r_end];
160 l_slice
161 .iter()
162 .zip(r_slice)
163 .map(|(&a, &b)| (a == b) as u8)
164 .collect()
165 }
166 _ => {
167 let lhs_iter = StridedIter::new(lhs.layout());
168 let rhs_iter = StridedIter::new(rhs.layout());
169 lhs_iter
170 .zip(rhs_iter)
171 .map(|(li, ri)| (lhs_storage[li] == rhs_storage[ri]) as u8)
172 .collect()
173 }
174 };
175
176 crate::ops::comparison::make_bool_tensor(result, shape, out_dtype)
177 }
178
179 fn bool_not(mut tensor: BoolTensor<Flex>) -> BoolTensor<Flex> {
180 use crate::strided_index::StridedIter;
181
182 debug_assert!(
183 matches!(
184 tensor.dtype(),
185 DType::Bool(burn_std::BoolStore::Native | burn_std::BoolStore::U8)
186 ),
187 "bool_not: only Bool(Native) and Bool(U8) are supported, got {:?}",
188 tensor.dtype()
189 );
190
191 if tensor.is_unique()
195 && tensor.layout().is_contiguous()
196 && tensor.layout().start_offset() == 0
197 {
198 let storage = tensor.storage_mut::<u8>();
199 crate::simd::bool_not_inplace_u8(storage);
200 return tensor;
201 }
202
203 let out_dtype = burn_std::BoolDType::from(tensor.dtype());
206 let shape = tensor.layout().shape().clone();
207 let storage: &[u8] = tensor.bytes();
208
209 let result: Vec<u8> = match tensor.layout().contiguous_offsets() {
210 Some((start, end)) => {
211 let slice = &storage[start..end];
212 let mut out = vec![0u8; slice.len()];
213 crate::simd::bool_not_u8(slice, &mut out);
214 out
215 }
216 None => StridedIter::new(tensor.layout())
217 .map(|idx| (storage[idx] == 0) as u8)
218 .collect(),
219 };
220
221 crate::ops::comparison::make_bool_tensor(result, shape, out_dtype)
222 }
223
224 fn bool_and(lhs: BoolTensor<Flex>, rhs: BoolTensor<Flex>) -> BoolTensor<Flex> {
225 bool_binary_op_simd(lhs, rhs, BoolBinaryOp::And)
226 }
227
228 fn bool_or(lhs: BoolTensor<Flex>, rhs: BoolTensor<Flex>) -> BoolTensor<Flex> {
229 bool_binary_op_simd(lhs, rhs, BoolBinaryOp::Or)
230 }
231
232 fn bool_xor(lhs: BoolTensor<Flex>, rhs: BoolTensor<Flex>) -> BoolTensor<Flex> {
233 bool_binary_op_simd(lhs, rhs, BoolBinaryOp::Xor)
234 }
235
236 fn bool_expand(tensor: BoolTensor<Flex>, shape: Shape) -> BoolTensor<Flex> {
237 crate::ops::expand::expand(tensor, shape)
238 }
239
240 fn bool_zeros(
242 shape: Shape,
243 device: &Device<Flex>,
244 dtype: burn_std::BoolDType,
245 ) -> BoolTensor<Flex> {
246 Self::bool_empty(shape, device, dtype)
247 }
248
249 fn bool_ones(
250 shape: Shape,
251 _device: &Device<Flex>,
252 dtype: burn_std::BoolDType,
253 ) -> BoolTensor<Flex> {
254 let num_elements = shape.num_elements();
255 let data = vec![1u8; num_elements];
256 crate::ops::comparison::make_bool_tensor(data, shape, dtype)
257 }
258
259 fn bool_mask_where(
260 tensor: BoolTensor<Flex>,
261 mask: BoolTensor<Flex>,
262 value: BoolTensor<Flex>,
263 ) -> BoolTensor<Flex> {
264 crate::ops::mask::mask_where_bool(tensor, mask, value)
265 }
266
267 fn bool_mask_fill(
268 tensor: BoolTensor<Flex>,
269 mask: BoolTensor<Flex>,
270 value: burn_backend::Scalar,
271 ) -> BoolTensor<Flex> {
272 let value: bool = value.elem();
273 crate::ops::mask::mask_fill_bool(tensor, mask, value)
274 }
275
276 fn bool_gather(
277 dim: usize,
278 tensor: BoolTensor<Flex>,
279 indices: IntTensor<Flex>,
280 ) -> BoolTensor<Flex> {
281 crate::ops::gather_scatter::gather_bool(tensor, dim, indices)
282 }
283
284 fn bool_scatter_or(
285 dim: usize,
286 tensor: BoolTensor<Flex>,
287 indices: IntTensor<Flex>,
288 value: BoolTensor<Flex>,
289 ) -> BoolTensor<Flex> {
290 crate::ops::gather_scatter::scatter_or(tensor, dim, indices, value)
291 }
292
293 fn bool_equal_elem(lhs: BoolTensor<Flex>, rhs: burn_backend::Scalar) -> BoolTensor<Flex> {
294 use crate::strided_index::StridedIter;
295
296 let out_dtype = burn_std::BoolDType::from(lhs.dtype());
297 let shape = lhs.layout().shape().clone();
298 let storage: &[u8] = lhs.bytes();
299 let rhs_bool: bool = rhs.elem();
300 let rhs_val = rhs_bool as u8;
301
302 let result: Vec<u8> = match lhs.layout().contiguous_offsets() {
303 Some((start, end)) => storage[start..end]
304 .iter()
305 .map(|&v| (v == rhs_val) as u8)
306 .collect(),
307 None => StridedIter::new(lhs.layout())
308 .map(|idx| (storage[idx] == rhs_val) as u8)
309 .collect(),
310 };
311
312 crate::ops::comparison::make_bool_tensor(result, shape, out_dtype)
313 }
314
315 fn bool_unfold(
316 tensor: BoolTensor<Flex>,
317 dim: usize,
318 size: usize,
319 step: usize,
320 ) -> BoolTensor<Flex> {
321 crate::ops::unfold::unfold_bool(tensor, dim, size, step)
322 }
323
324 fn bool_not_equal(lhs: BoolTensor<Flex>, rhs: BoolTensor<Flex>) -> BoolTensor<Flex> {
325 let out_dtype = burn_std::BoolDType::from(lhs.dtype());
326 crate::ops::comparison::bool_not_equal(lhs, rhs, out_dtype)
327 }
328
329 fn bool_not_equal_elem(lhs: BoolTensor<Flex>, rhs: burn_backend::Scalar) -> BoolTensor<Flex> {
330 let out_dtype = burn_std::BoolDType::from(lhs.dtype());
331 let rhs: bool = rhs.elem();
332 crate::ops::comparison::bool_not_equal_elem(lhs, rhs, out_dtype)
333 }
334
335 fn bool_any(tensor: BoolTensor<Flex>) -> BoolTensor<Flex> {
336 let out_dtype = burn_std::BoolDType::from(tensor.dtype());
337 crate::ops::comparison::any_bool(tensor, out_dtype)
338 }
339
340 fn bool_any_dim(tensor: BoolTensor<Flex>, dim: usize) -> BoolTensor<Flex> {
341 let out_dtype = burn_std::BoolDType::from(tensor.dtype());
342 crate::ops::comparison::any_bool_dim(tensor, dim, out_dtype)
343 }
344
345 fn bool_all(tensor: BoolTensor<Flex>) -> BoolTensor<Flex> {
346 let out_dtype = burn_std::BoolDType::from(tensor.dtype());
347 crate::ops::comparison::all_bool(tensor, out_dtype)
348 }
349
350 fn bool_all_dim(tensor: BoolTensor<Flex>, dim: usize) -> BoolTensor<Flex> {
351 let out_dtype = burn_std::BoolDType::from(tensor.dtype());
352 crate::ops::comparison::all_bool_dim(tensor, dim, out_dtype)
353 }
354
355 fn bool_select(
356 tensor: BoolTensor<Flex>,
357 dim: usize,
358 indices: IntTensor<Flex>,
359 ) -> BoolTensor<Flex> {
360 crate::ops::gather_scatter::select::<u8>(tensor, dim, indices)
361 }
362
363 fn bool_select_or(
364 tensor: BoolTensor<Flex>,
365 dim: usize,
366 indices: IntTensor<Flex>,
367 value: BoolTensor<Flex>,
368 ) -> BoolTensor<Flex> {
369 let mut result = crate::ops::gather_scatter::select_add::<u8>(tensor, dim, indices, value);
370 let storage: &mut [u8] = result.storage_mut();
372 for v in storage.iter_mut() {
373 if *v > 1 {
374 *v = 1;
375 }
376 }
377 result
378 }
379
380 fn bool_transpose(tensor: BoolTensor<Flex>) -> BoolTensor<Flex> {
381 let ndims = tensor.layout().num_dims();
382 if ndims < 2 {
383 return tensor;
384 }
385 tensor.transpose(ndims - 2, ndims - 1)
386 }
387
388 fn bool_repeat_dim(tensor: BoolTensor<Flex>, dim: usize, times: usize) -> BoolTensor<Flex> {
389 crate::ops::repeat_dim::repeat_dim(tensor, dim, times)
390 }
391
392 async fn bool_argwhere(tensor: BoolTensor<Flex>, out_dtype: IntDType) -> IntTensor<Flex> {
393 let tensor = tensor.to_contiguous();
394 let shape = tensor.layout().shape().clone();
395 let ndims = shape.num_dims();
396 let data: &[u8] = tensor.storage();
397 let n = shape.num_elements();
398
399 let count = data[..n].iter().filter(|&&v| v != 0).count();
400 let mut coords: Vec<isize> = Vec::with_capacity(count * ndims);
401 let strides = crate::layout::contiguous_strides_usize(&shape);
402
403 for (flat_idx, &val) in data[..n].iter().enumerate() {
404 if val != 0 {
405 let mut remaining = flat_idx;
406 for &s in &strides {
407 coords.push((remaining / s) as isize);
408 remaining %= s;
409 }
410 }
411 }
412
413 let out_shape = Shape::from(vec![count, ndims]);
414 let result = FlexTensor::new(
415 Bytes::from_elems(coords),
416 Layout::contiguous(out_shape),
417 crate::ops::INDEX_DTYPE,
418 );
419 if result.dtype() != DType::from(out_dtype) {
420 Flex::int_cast(result, out_dtype)
421 } else {
422 result
423 }
424 }
425}
426
427#[derive(Clone, Copy)]
429enum BoolBinaryOp {
430 And,
431 Or,
432 Xor,
433}
434
435fn bool_binary_op_simd(lhs: FlexTensor, rhs: FlexTensor, op: BoolBinaryOp) -> FlexTensor {
436 use crate::strided_index::StridedIter;
437
438 debug_assert_eq!(lhs.dtype(), rhs.dtype(), "bool_binary_op: dtype mismatch");
439
440 let (mut lhs, mut rhs) = crate::ops::expand::broadcast_binary(lhs, rhs);
444
445 let out_dtype = burn_std::BoolDType::from(lhs.dtype());
448 let shape = lhs.layout().shape().clone();
449 let l_offsets = lhs.layout().contiguous_offsets();
450 let r_offsets = rhs.layout().contiguous_offsets();
451
452 if lhs.is_unique()
454 && let (Some((0, l_end)), Some((r_start, r_end))) = (l_offsets, r_offsets)
455 {
456 let rhs_storage: &[u8] = rhs.bytes();
457 let r_slice = &rhs_storage[r_start..r_end];
458 let lhs_storage: &mut [u8] = lhs.storage_mut();
459 let l_slice = &mut lhs_storage[..l_end];
460
461 match op {
462 BoolBinaryOp::And => crate::simd::bool_and_inplace_u8(l_slice, r_slice),
463 BoolBinaryOp::Or => crate::simd::bool_or_inplace_u8(l_slice, r_slice),
464 BoolBinaryOp::Xor => crate::simd::bool_xor_inplace_u8(l_slice, r_slice),
465 }
466 return lhs;
467 }
468
469 if rhs.is_unique()
472 && let (Some((l_start, l_end)), Some((0, r_end))) = (l_offsets, r_offsets)
473 {
474 let lhs_storage: &[u8] = lhs.bytes();
475 let l_slice = &lhs_storage[l_start..l_end];
476 let rhs_storage: &mut [u8] = rhs.storage_mut();
477 let r_slice = &mut rhs_storage[..r_end];
478
479 match op {
480 BoolBinaryOp::And => crate::simd::bool_and_inplace_u8(r_slice, l_slice),
481 BoolBinaryOp::Or => crate::simd::bool_or_inplace_u8(r_slice, l_slice),
482 BoolBinaryOp::Xor => crate::simd::bool_xor_inplace_u8(r_slice, l_slice),
483 }
484 return rhs;
485 }
486
487 let lhs_storage: &[u8] = lhs.bytes();
489 let rhs_storage: &[u8] = rhs.bytes();
490
491 let result: Vec<u8> = match (l_offsets, r_offsets) {
492 (Some((l_start, l_end)), Some((r_start, r_end))) => {
493 let l_slice = &lhs_storage[l_start..l_end];
494 let r_slice = &rhs_storage[r_start..r_end];
495 let mut out = vec![0u8; l_slice.len()];
496 match op {
497 BoolBinaryOp::And => crate::simd::bool_and_u8(l_slice, r_slice, &mut out),
498 BoolBinaryOp::Or => crate::simd::bool_or_u8(l_slice, r_slice, &mut out),
499 BoolBinaryOp::Xor => crate::simd::bool_xor_u8(l_slice, r_slice, &mut out),
500 }
501 out
502 }
503 _ => {
504 let lhs_iter = StridedIter::new(lhs.layout());
505 let rhs_iter = StridedIter::new(rhs.layout());
506 match op {
507 BoolBinaryOp::And => lhs_iter
508 .zip(rhs_iter)
509 .map(|(li, ri)| lhs_storage[li] & rhs_storage[ri])
510 .collect(),
511 BoolBinaryOp::Or => lhs_iter
512 .zip(rhs_iter)
513 .map(|(li, ri)| lhs_storage[li] | rhs_storage[ri])
514 .collect(),
515 BoolBinaryOp::Xor => lhs_iter
516 .zip(rhs_iter)
517 .map(|(li, ri)| lhs_storage[li] ^ rhs_storage[ri])
518 .collect(),
519 }
520 }
521 };
522
523 crate::ops::comparison::make_bool_tensor(result, shape, out_dtype)
524}
525
526#[cfg(test)]
534mod tests {
535 use alloc::vec;
536 use burn_backend::TensorData;
537 use burn_backend::ops::BoolTensorOps;
538 use burn_std::{FloatDType, IntDType};
539
540 use crate::{Flex, FlexTensor};
541
542 #[test]
543 fn test_bool_into_int_u8() {
544 let t = FlexTensor::from_data(TensorData::from([true, false, true]));
545 let result = Flex::bool_into_int(t, IntDType::U8);
546 assert_eq!(result.dtype(), burn_backend::DType::U8);
547 let data: Vec<u8> = result.into_data().to_vec().unwrap();
548 assert_eq!(data, vec![1u8, 0, 1]);
549 }
550
551 #[test]
552 fn test_bool_into_float_f64() {
553 let t = FlexTensor::from_data(TensorData::from([true, false, true]));
554 let result = Flex::bool_into_float(t, FloatDType::F64);
555 assert_eq!(result.dtype(), burn_backend::DType::F64);
556 let data: Vec<f64> = result.into_data().to_vec().unwrap();
557 assert_eq!(data, vec![1.0f64, 0.0, 1.0]);
558 }
559}