Skip to main content

burn_backend/backend/ops/
bool_tensor.rs

1use super::{
2    argwhere::argwhere_data, cat::cat_with_slice_assign, repeat_dim::repeat_with_slice_assign,
3};
4use crate::tensor::{BoolTensor, Device, FloatTensor, IntTensor};
5use crate::{Backend, TensorData, TensorMetadata, get_or_init_device_settings};
6use crate::{ExecutionError, Scalar};
7use alloc::vec::Vec;
8use burn_std::{BoolDType, FloatDType, IndexingUpdateOp, IntDType, Shape, Slice};
9use core::future::Future;
10
11/// Bool Tensor API for basic operations, see
12#[cfg_attr(doc, doc = crate::doc_tensor!())]
13#[cfg_attr(not(doc), doc = "`Tensor`")]
14/// for documentation on each function.
15pub trait BoolTensorOps<B: Backend> {
16    /// Creates a new bool tensor.
17    ///
18    /// # Arguments
19    ///
20    /// * `shape` - The shape of the tensor.
21    /// * `device` - The device to create the tensor on.
22    /// * `dtype` - The target data type.
23    ///
24    /// # Returns
25    ///
26    /// The boolean tensor with the given shape.
27    fn bool_empty(shape: Shape, device: &Device<B>, dtype: BoolDType) -> BoolTensor<B>;
28
29    /// Creates a new bool tensor filled false.
30    ///
31    /// # Arguments
32    ///
33    /// * `shape` - The shape of the tensor.
34    /// * `device` - The device to create the tensor on.
35    /// * `dtype` - The target data type.
36    ///
37    /// # Returns
38    ///
39    /// The boolean tensor filled with false.
40    fn bool_zeros(shape: Shape, device: &Device<B>, dtype: BoolDType) -> BoolTensor<B>;
41
42    /// Creates a new bool tensor filled true.
43    ///
44    /// # Arguments
45    ///
46    /// * `shape` - The shape of the tensor.
47    /// * `device` - The device to create the tensor on.
48    /// * `dtype` - The target data type.
49    ///
50    /// # Returns
51    ///
52    /// The boolean tensor filled with true.
53    fn bool_ones(shape: Shape, device: &Device<B>, dtype: BoolDType) -> BoolTensor<B>;
54
55    /// Converts the tensor to a data structure.
56    ///
57    /// # Arguments
58    ///
59    /// * `tensor` - The tensor.
60    ///
61    /// # Returns
62    ///
63    /// The data structure with the tensor's data.
64    fn bool_into_data(
65        tensor: BoolTensor<B>,
66    ) -> impl Future<Output = Result<TensorData, ExecutionError>> + Send;
67
68    /// Creates a tensor from the data structure.
69    ///
70    /// # Arguments
71    ///
72    /// * `data` - The data structure.
73    /// * `device` - The device to create the tensor on.
74    ///
75    /// # Returns
76    ///
77    /// The tensor with the data.
78    fn bool_from_data(data: TensorData, device: &Device<B>) -> BoolTensor<B>;
79
80    /// Converts bool tensor to int tensor.
81    ///
82    /// # Arguments
83    ///
84    /// * `tensor` - The tensor.
85    /// * `out_dtype` - The output tensor dtype.
86    ///
87    /// # Returns
88    ///
89    /// The int tensor with the same data as the bool tensor.
90    fn bool_into_int(tensor: BoolTensor<B>, out_dtype: IntDType) -> IntTensor<B>;
91
92    /// Converts bool tensor to float tensor.
93    ///
94    /// # Arguments
95    ///
96    /// * `tensor` - The tensor.
97    /// * `out_dtype` - The output tensor dtype.
98    ///
99    /// # Returns
100    ///
101    /// The float tensor with the same data as the bool tensor.
102    fn bool_into_float(tensor: BoolTensor<B>, out_dtype: FloatDType) -> FloatTensor<B>;
103
104    /// Moves the tensor to the device.
105    fn bool_to_device(tensor: BoolTensor<B>, device: &Device<B>) -> BoolTensor<B>;
106
107    /// Reshapes the tensor.
108    ///
109    /// # Arguments
110    ///
111    /// * `tensor` - The tensor.
112    /// * `shape` - The new shape.
113    ///
114    /// # Returns
115    ///
116    /// The tensor with the new shape.
117    fn bool_reshape(tensor: BoolTensor<B>, shape: Shape) -> BoolTensor<B>;
118
119    /// Gets the values from the tensor for the given ranges.
120    ///
121    /// # Arguments
122    ///
123    /// * `tensor` - The tensor.
124    /// * `slices` - The slices specifying ranges and steps for each dimension.
125    ///
126    /// # Returns
127    ///
128    /// The tensor with the values for the given slices.
129    ///
130    /// # Note
131    ///
132    /// Empty slices (where start >= end) are handled at the high-level tensor API and will not
133    /// be passed to this method. Backend implementations do not need to handle empty slices.
134    fn bool_slice(tensor: BoolTensor<B>, slices: &[Slice]) -> BoolTensor<B>;
135
136    /// Sets the values in the tensor for the given ranges.
137    ///
138    /// # Arguments
139    ///
140    /// * `tensor` - The tensor.
141    /// * `ranges` - The ranges to set the values for.
142    /// * `value` - The values to set.
143    ///
144    /// # Returns
145    ///
146    /// The tensor with the values set for the given ranges.
147    ///
148    /// # Note
149    ///
150    /// Empty slice assignments (where any slice range produces 0 elements) are handled at the
151    /// high-level tensor API and will not be passed to this method. Backend implementations do
152    /// not need to handle empty slice assignments.
153    fn bool_slice_assign(
154        tensor: BoolTensor<B>,
155        slices: &[Slice],
156        value: BoolTensor<B>,
157    ) -> BoolTensor<B>;
158
159    /// Fills the tensor with values from the value tensor if the mask is true at the given
160    /// indices.
161    ///
162    /// # Arguments
163    ///
164    /// * `tensor` - The tensor.
165    /// * `mask` - The mask.
166    /// * `value` - The value tensor.
167    ///
168    /// # Returns
169    ///
170    /// The tensor with the values filled.
171    fn bool_mask_where(
172        tensor: BoolTensor<B>,
173        mask: BoolTensor<B>,
174        value: BoolTensor<B>,
175    ) -> BoolTensor<B>;
176
177    /// Fills the tensor with the given value if the mask is true at the given indices.
178    ///
179    /// # Arguments
180    ///
181    /// * `tensor` - The tensor.
182    /// * `mask` - The mask.
183    /// * `value` - The value.
184    ///
185    /// # Returns
186    ///
187    /// The tensor with the values filled.
188    fn bool_mask_fill(tensor: BoolTensor<B>, mask: BoolTensor<B>, value: Scalar) -> BoolTensor<B>;
189
190    /// Selects the elements of the tensor where the mask is true, returned as a 1D tensor.
191    ///
192    /// The elements are collected in row-major order. Because the number of selected elements
193    /// depends on the mask values, the output shape is data-dependent: computing it may require
194    /// synchronizing with the device, which is why this operation is asynchronous.
195    ///
196    /// # Arguments
197    ///
198    /// * `tensor` - The tensor to select from.
199    /// * `mask` - The boolean mask, with the same shape as the tensor.
200    ///
201    /// # Returns
202    ///
203    /// A 1D tensor containing the selected elements.
204    fn bool_mask_select(
205        tensor: BoolTensor<B>,
206        mask: BoolTensor<B>,
207    ) -> impl Future<Output = BoolTensor<B>> + 'static + Send {
208        async move {
209            // Data-dependent output length, so we defer to `bool_argwhere` (the only pre-existing
210            // data-dependent op) to collect the flat indices of the true mask values, then select.
211            let n = mask.shape().num_elements();
212            let int_dtype = get_or_init_device_settings::<B>(&mask.device()).int_dtype;
213            let mask = B::bool_reshape(mask, Shape::new([n]));
214            let indices = B::bool_argwhere(mask, int_dtype).await; // [count, 1]
215            let count = indices.shape()[0];
216            let indices = B::int_reshape(indices, Shape::new([count])); // squeeze to [count]
217            let tensor = B::bool_reshape(tensor, Shape::new([n]));
218            B::bool_select(tensor, 0, indices)
219        }
220    }
221
222    /// Gather elements from the tensor at the given indices.
223    ///
224    /// # Arguments
225    ///
226    /// * `dim` - The dimension to gather from.
227    /// * `tensor` - The tensor.
228    /// * `indices` - The indices.
229    fn bool_gather(dim: usize, tensor: BoolTensor<B>, indices: IntTensor<B>) -> BoolTensor<B>;
230
231    /// Scatter elements into a tensor using the specified update operation.
232    ///
233    /// `Assign` overwrites, `Add` and `Max` combine with logical or, and `Mul` and `Min`
234    /// combine with logical and.
235    ///
236    /// # Arguments
237    ///
238    /// * `dim` - The dimension to scatter to.
239    /// * `tensor` - The tensor.
240    /// * `indices` - The indices.
241    /// * `value` - The value.
242    /// * `update` - The update operation.
243    ///
244    /// # Returns
245    ///
246    /// The tensor with the values scattered.
247    fn bool_scatter(
248        dim: usize,
249        tensor: BoolTensor<B>,
250        indices: IntTensor<B>,
251        value: BoolTensor<B>,
252        update: IndexingUpdateOp,
253    ) -> BoolTensor<B>;
254
255    /// Multi-dimensional scatter for bool tensors.
256    ///
257    /// `Assign` overwrites, `Add` and `Max` combine with logical or, and `Mul` and `Min`
258    /// combine with logical and.
259    fn bool_scatter_nd(
260        data: BoolTensor<B>,
261        indices: IntTensor<B>,
262        values: BoolTensor<B>,
263        reduction: IndexingUpdateOp,
264    ) -> BoolTensor<B>;
265
266    /// Multi-dimensional gather for bool tensors.
267    fn bool_gather_nd(data: BoolTensor<B>, indices: IntTensor<B>) -> BoolTensor<B>;
268
269    /// Select tensor elements along the given dimension corresponding to the given indices.
270    ///
271    /// # Arguments
272    ///
273    /// * `tensor` - The tensor to select from.
274    /// * `dim` - The dimension to select from.
275    /// * `indices` - The indices of the elements to select.
276    ///
277    /// # Returns
278    ///
279    /// The tensor with the selected elements.
280    fn bool_select(tensor: BoolTensor<B>, dim: usize, indices: IntTensor<B>) -> BoolTensor<B>;
281
282    /// Assign selected elements along a dimension using the specified update operation.
283    ///
284    /// `Assign` overwrites, `Add` and `Max` combine with logical or, and `Mul` and `Min`
285    /// combine with logical and.
286    ///
287    /// # Arguments
288    ///
289    /// * `tensor` - The tensor to assign the values to.
290    /// * `dim` - The dimension to select from.
291    /// * `indices` - The indices of the elements to assign.
292    /// * `value` - The values to assign.
293    /// * `update` - The update operation.
294    ///
295    /// # Returns
296    ///
297    /// The tensor with the assigned values.
298    fn bool_select_assign(
299        tensor: BoolTensor<B>,
300        dim: usize,
301        indices: IntTensor<B>,
302        value: BoolTensor<B>,
303        update: IndexingUpdateOp,
304    ) -> BoolTensor<B>;
305
306    /// Repeats one dimension of the tensor a given number of times along that dimension.
307    ///
308    /// # Arguments
309    ///
310    /// * `tensor` - The tensor.
311    /// * `dim` - The dimension to repeat.
312    /// * `times` - The number of times to repeat the dimension.
313    ///
314    /// # Returns
315    ///
316    /// The tensor with the dimension repeated.
317    fn bool_repeat_dim(tensor: BoolTensor<B>, dim: usize, times: usize) -> BoolTensor<B> {
318        let device = tensor.device();
319        repeat_with_slice_assign::<B, _, _, _>(
320            tensor,
321            dim,
322            times,
323            device,
324            |shape, device, dtype| B::bool_empty(shape, device, dtype.into()),
325            B::bool_slice_assign,
326        )
327    }
328
329    /// Concatenates the tensors along the given dimension.
330    ///
331    /// # Arguments
332    ///
333    /// * `tensors` - The tensors to concatenate.
334    /// * `dim` - The dimension to concatenate along.
335    ///
336    /// # Returns
337    ///
338    /// The tensor with the tensors concatenated along the given dimension.
339    ///
340    /// # Note
341    ///
342    /// Empty tensors (where the concatenation dimension has size 0) are filtered out at the
343    /// high-level tensor API and will not be passed to this method. Backend implementations do
344    /// not need to handle empty tensors.
345    fn bool_cat(tensors: Vec<BoolTensor<B>>, dim: usize) -> BoolTensor<B> {
346        let first_tensor = tensors.first().expect("Tensors should not be empty");
347        let device = first_tensor.device();
348        cat_with_slice_assign::<B, _, _, _>(
349            tensors,
350            dim,
351            device,
352            |shape, device, dtype| B::bool_empty(shape, device, dtype.into()),
353            B::bool_slice_assign,
354        )
355    }
356
357    /// Equates the two tensors.
358    ///
359    /// # Arguments
360    ///
361    /// * `lhs` - The left hand side tensor.
362    /// * `rhs` - The right hand side tensor.
363    ///
364    /// # Returns
365    ///
366    /// The tensor with the result of the equate.
367    fn bool_equal(lhs: BoolTensor<B>, rhs: BoolTensor<B>) -> BoolTensor<B>;
368
369    /// Element-wise non-equality comparison.
370    ///
371    /// # Arguments
372    ///
373    /// * `lhs` - The left hand side tensor.
374    /// * `rhs` - The right hand side tensor.
375    ///
376    /// # Returns
377    ///
378    /// The tensor with the result of the comparison.
379    fn bool_not_equal(lhs: BoolTensor<B>, rhs: BoolTensor<B>) -> BoolTensor<B> {
380        let equal_tensor = B::bool_equal(lhs, rhs);
381        B::bool_not(equal_tensor)
382    }
383
384    /// Element-wise equality comparison with a scalar.
385    ///
386    /// # Arguments
387    ///
388    /// * `lhs` - The left-hand side tensor.
389    /// * `rhs` - The right-hand side scalar.
390    ///
391    /// # Returns
392    ///
393    /// The boolean tensor with the result of the comparison.
394    fn bool_equal_elem(lhs: BoolTensor<B>, rhs: Scalar) -> BoolTensor<B>;
395
396    /// Element-wise non-equality comparison with a scalar.
397    ///
398    /// # Arguments
399    ///
400    /// * `lhs` - The left-hand side tensor.
401    /// * `rhs` - The right-hand side scalar.
402    ///
403    /// # Returns
404    ///
405    /// The boolean tensor with the result of the comparison.
406    fn bool_not_equal_elem(lhs: BoolTensor<B>, rhs: Scalar) -> BoolTensor<B> {
407        let equal_tensor = B::bool_equal_elem(lhs, rhs);
408        B::bool_not(equal_tensor)
409    }
410
411    /// Inverses boolean values.
412    ///
413    /// # Arguments
414    ///
415    /// * `tensor` - The tensor.
416    ///
417    /// # Returns
418    ///
419    /// The tensor with the result of the negation.
420    fn bool_not(tensor: BoolTensor<B>) -> BoolTensor<B>;
421
422    /// Executes the logical and (`&&`) operation on two boolean tensors.
423    ///
424    /// # Arguments
425    ///
426    /// * `lhs` - The left hand side tensor.
427    /// * `rhs` - The right hand side tensor.
428    ///
429    /// # Returns
430    ///
431    /// The tensor with the result of the logical and.
432    fn bool_and(lhs: BoolTensor<B>, rhs: BoolTensor<B>) -> BoolTensor<B>;
433
434    /// Executes the logical or (`||`) operation on two boolean tensors.
435    ///
436    /// # Arguments
437    ///
438    /// * `lhs` - The left hand side tensor.
439    /// * `rhs` - The right hand side tensor.
440    ///
441    /// # Returns
442    ///
443    /// The tensor with the result of the logical or.
444    fn bool_or(lhs: BoolTensor<B>, rhs: BoolTensor<B>) -> BoolTensor<B>;
445
446    /// Element-wise exclusive or.
447    ///
448    /// # Arguments
449    ///
450    /// * `lhs` - The left hand side tensor.
451    /// * `rhs` - The right hand side tensor.
452    ///
453    /// # Returns
454    ///
455    /// The tensor with the result of the comparison.
456    fn bool_xor(lhs: BoolTensor<B>, rhs: BoolTensor<B>) -> BoolTensor<B> {
457        Self::bool_not_equal(lhs, rhs)
458    }
459
460    /// Transposes a bool tensor.
461    ///
462    /// # Arguments
463    ///
464    /// * `tensor` - The tensor to transpose.
465    ///
466    /// # Returns
467    ///
468    /// The transposed tensor.
469    fn bool_transpose(tensor: BoolTensor<B>) -> BoolTensor<B> {
470        let ndims = tensor.shape().num_dims();
471        Self::bool_swap_dims(tensor, ndims - 2, ndims - 1)
472    }
473
474    /// Swaps two dimensions of a bool tensor.
475    ///
476    /// # Arguments
477    ///
478    /// * `tensor` - The tensor to swap the dimensions of.
479    /// * `dim1` - The first dimension to swap.
480    /// * `dim2` - The second dimension to swap.
481    ///
482    /// # Returns
483    ///
484    /// The tensor with the dimensions swapped.
485    fn bool_swap_dims(tensor: BoolTensor<B>, dim1: usize, dim2: usize) -> BoolTensor<B>;
486
487    /// Permutes the dimensions of a tensor.
488    ///
489    /// # Arguments
490    ///
491    /// * `tensor` - The tensor to permute the dimensions of.
492    /// * `axes` - The new order of the dimensions.
493    /// # Returns
494    ///
495    /// The tensor with the dimensions permuted.
496    fn bool_permute(tensor: BoolTensor<B>, axes: &[usize]) -> BoolTensor<B>;
497
498    /// Reverse the order of elements in a tensor along the given axes.
499    ///
500    /// # Arguments
501    ///
502    /// * `tensor` - The tensor to reverse.
503    /// * `axes` - The axes to reverse.
504    ///
505    /// The tensor with the elements reversed.
506    fn bool_flip(tensor: BoolTensor<B>, axes: &[usize]) -> BoolTensor<B>;
507
508    /// Tests if any element in the boolean `tensor` evaluates to True.
509    ///
510    /// # Arguments
511    ///
512    /// * `tensor` - The tensor to test.
513    ///
514    /// # Returns
515    ///
516    /// A boolean tensor with a single element, True if any element in the tensor is True, False otherwise.
517    fn bool_any(tensor: BoolTensor<B>) -> BoolTensor<B> {
518        let dtype = tensor.dtype();
519        let int_dtype = get_or_init_device_settings::<B>(&tensor.device()).int_dtype;
520        let sum = B::int_sum(B::bool_into_int(tensor, int_dtype));
521        B::int_greater_elem(sum, 0.into(), dtype.into())
522    }
523
524    /// Tests if any element in the boolean `tensor` evaluates to True along a given dimension `dim`.
525    ///
526    /// # Arguments
527    ///
528    /// * `tensor` - The tensor to test.
529    /// * `dim` - The axis along which to test.
530    ///
531    /// # Returns
532    ///
533    /// A boolean tensor primitive with the same size as input `tensor`, except in the `dim` axis
534    /// where the size is 1. The elem in the `dim` axis is True if any element along this dim in the input
535    /// evaluates to True, False otherwise.
536    fn bool_any_dim(tensor: BoolTensor<B>, dim: usize) -> BoolTensor<B> {
537        let dtype = tensor.dtype();
538        let int_dtype = get_or_init_device_settings::<B>(&tensor.device()).int_dtype;
539        let sum = B::int_sum_dim(B::bool_into_int(tensor, int_dtype), dim);
540        B::int_greater_elem(sum, 0.into(), dtype.into())
541    }
542
543    /// Tests if all elements in the boolean `tensor` evaluate to True.
544    ///
545    /// # Arguments
546    ///
547    /// * `tensor` - The tensor to test.
548    ///
549    /// # Returns
550    ///
551    /// A boolean tensor primitive with a single element, True if all elements in the input tensor
552    /// evaluate to True, False otherwise.
553    fn bool_all(tensor: BoolTensor<B>) -> BoolTensor<B> {
554        let dtype = tensor.dtype();
555        let int_dtype = get_or_init_device_settings::<B>(&tensor.device()).int_dtype;
556        let num_elems = tensor.shape().num_elements() as i64;
557        let sum = B::int_sum(B::bool_into_int(tensor, int_dtype));
558        B::int_equal_elem(sum, num_elems.into(), dtype.into())
559    }
560
561    /// Tests if all elements in the boolean `tensor` evaluate to True along a given dimension `dim`.
562    ///
563    /// # Arguments
564    ///
565    /// * `tensor` - The tensor to test.
566    /// * `dim` - The axis along which to test.
567    ///
568    /// # Returns
569    ///
570    /// A boolean tensor primitive with the same size as input `tensor`, except in the `dim` axis
571    /// where the size is 1. The elem in the `dim` axis is True if all elements along this dim in the input
572    /// evaluates to True, False otherwise.
573    fn bool_all_dim(tensor: BoolTensor<B>, dim: usize) -> BoolTensor<B> {
574        let dtype = tensor.dtype();
575        let int_dtype = get_or_init_device_settings::<B>(&tensor.device()).int_dtype;
576        let num_elems = tensor.shape()[dim] as i64;
577        let sum = B::int_sum_dim(B::bool_into_int(tensor, int_dtype), dim);
578        B::int_equal_elem(sum, num_elems.into(), dtype.into())
579    }
580
581    /// Compute the indices of the elements that are non-zero, grouped by element.
582    ///
583    /// # Arguments
584    ///
585    /// * `tensor` - The input tensor.
586    /// * `out_dtype` - The output tensor dtype.
587    ///
588    /// # Returns
589    ///
590    /// A 2D tensor containing the indices of all non-zero elements of the given tensor.
591    /// Each row contains the indices of a non-zero element.
592    fn bool_argwhere(
593        tensor: BoolTensor<B>,
594        out_dtype: IntDType,
595    ) -> impl Future<Output = IntTensor<B>> + 'static + Send {
596        async move {
597            // Size of each output tensor is variable (= number of nonzero elements in the tensor).
598            // Reading the data to count the number of truth values might cause sync but is required.
599            let device = &tensor.device();
600            let data = B::bool_into_data(tensor)
601                .await
602                .expect("Can read the data without error");
603            argwhere_data::<B>(data, device, out_dtype)
604        }
605    }
606
607    /// Broadcasts the bool `tensor` to the given `shape`.
608    fn bool_expand(tensor: BoolTensor<B>, shape: Shape) -> BoolTensor<B>;
609
610    /// Unfold windows along a dimension.
611    ///
612    /// Returns a view of the tensor with all complete windows of size `size` in dimension `dim`;
613    /// where windows are advanced by `step` at each index.
614    ///
615    /// The number of windows is `0` when `shape[dim] < size`, and otherwise
616    /// `(shape[dim] - size) / step + 1`.
617    ///
618    /// # Arguments
619    ///
620    /// * `tensor` - The input tensor to unfold; of shape ``[pre=..., dim shape, post=...]``
621    /// * `dim` - the selected dim.
622    /// * `size` - the size of each unfolded window.
623    /// * `step` - the step between each window.
624    ///
625    /// # Returns
626    ///
627    /// A tensor view with shape ``[pre=..., windows, size, post=...]``.
628    fn bool_unfold(tensor: BoolTensor<B>, dim: usize, size: usize, step: usize) -> BoolTensor<B>;
629}