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_device_settings};
6use crate::{ExecutionError, Scalar};
7use alloc::vec::Vec;
8use burn_std::{BoolDType, FloatDType, 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    /// Gather elements from the tensor at the given indices.
191    ///
192    /// # Arguments
193    ///
194    /// * `dim` - The dimension to gather from.
195    /// * `tensor` - The tensor.
196    /// * `indices` - The indices.
197    fn bool_gather(dim: usize, tensor: BoolTensor<B>, indices: IntTensor<B>) -> BoolTensor<B>;
198
199    /// Scatter a given value to the tensor at the given indices using boolean or reduction.
200    ///
201    /// # Arguments
202    ///
203    /// * `dim` - The dimension to scatter to.
204    /// * `tensor` - The tensor.
205    /// * `indices` - The indices.
206    /// * `value` - The value.
207    ///
208    /// # Returns
209    ///
210    /// The tensor with the values scattered.
211    fn bool_scatter_or(
212        dim: usize,
213        tensor: BoolTensor<B>,
214        indices: IntTensor<B>,
215        value: BoolTensor<B>,
216    ) -> BoolTensor<B>;
217
218    /// Select tensor elements along the given dimension corresponding to the given indices.
219    ///
220    /// # Arguments
221    ///
222    /// * `tensor` - The tensor to select from.
223    /// * `dim` - The dimension to select from.
224    /// * `indices` - The indices of the elements to select.
225    ///
226    /// # Returns
227    ///
228    /// The tensor with the selected elements.
229    fn bool_select(tensor: BoolTensor<B>, dim: usize, indices: IntTensor<B>) -> BoolTensor<B>;
230
231    /// Assign the selected elements along the given dimension corresponding to the given indices
232    /// to the given value using sum reduction.
233    ///
234    /// # Arguments
235    ///
236    /// * `tensor` - The tensor to assign the values to.
237    /// * `dim` - The dimension to select from.
238    /// * `indices` - The indices of the elements to assign.
239    /// * `value` - The values to assign.
240    ///
241    /// # Returns
242    ///
243    /// The tensor with the assigned values.
244    fn bool_select_or(
245        tensor: BoolTensor<B>,
246        dim: usize,
247        indices: IntTensor<B>,
248        value: BoolTensor<B>,
249    ) -> BoolTensor<B>;
250
251    /// Repeats one dimension of the tensor a given number of times along that dimension.
252    ///
253    /// # Arguments
254    ///
255    /// * `tensor` - The tensor.
256    /// * `dim` - The dimension to repeat.
257    /// * `times` - The number of times to repeat the dimension.
258    ///
259    /// # Returns
260    ///
261    /// The tensor with the dimension repeated.
262    fn bool_repeat_dim(tensor: BoolTensor<B>, dim: usize, times: usize) -> BoolTensor<B> {
263        let device = tensor.device();
264        repeat_with_slice_assign::<B, _, _, _>(
265            tensor,
266            dim,
267            times,
268            device,
269            |shape, device, dtype| B::bool_empty(shape, device, dtype.into()),
270            B::bool_slice_assign,
271        )
272    }
273
274    /// Concatenates the tensors along the given dimension.
275    ///
276    /// # Arguments
277    ///
278    /// * `tensors` - The tensors to concatenate.
279    /// * `dim` - The dimension to concatenate along.
280    ///
281    /// # Returns
282    ///
283    /// The tensor with the tensors concatenated along the given dimension.
284    ///
285    /// # Note
286    ///
287    /// Empty tensors (where the concatenation dimension has size 0) are filtered out at the
288    /// high-level tensor API and will not be passed to this method. Backend implementations do
289    /// not need to handle empty tensors.
290    fn bool_cat(tensors: Vec<BoolTensor<B>>, dim: usize) -> BoolTensor<B> {
291        let first_tensor = tensors.first().expect("Tensors should not be empty");
292        let device = first_tensor.device();
293        cat_with_slice_assign::<B, _, _, _>(
294            tensors,
295            dim,
296            device,
297            |shape, device, dtype| B::bool_empty(shape, device, dtype.into()),
298            B::bool_slice_assign,
299        )
300    }
301
302    /// Equates the two tensors.
303    ///
304    /// # Arguments
305    ///
306    /// * `lhs` - The left hand side tensor.
307    /// * `rhs` - The right hand side tensor.
308    ///
309    /// # Returns
310    ///
311    /// The tensor with the result of the equate.
312    fn bool_equal(lhs: BoolTensor<B>, rhs: BoolTensor<B>) -> BoolTensor<B>;
313
314    /// Element-wise non-equality comparison.
315    ///
316    /// # Arguments
317    ///
318    /// * `lhs` - The left hand side tensor.
319    /// * `rhs` - The right hand side tensor.
320    ///
321    /// # Returns
322    ///
323    /// The tensor with the result of the comparison.
324    fn bool_not_equal(lhs: BoolTensor<B>, rhs: BoolTensor<B>) -> BoolTensor<B> {
325        let equal_tensor = B::bool_equal(lhs, rhs);
326        B::bool_not(equal_tensor)
327    }
328
329    /// Element-wise equality comparison with a scalar.
330    ///
331    /// # Arguments
332    ///
333    /// * `lhs` - The left-hand side tensor.
334    /// * `rhs` - The right-hand side scalar.
335    ///
336    /// # Returns
337    ///
338    /// The boolean tensor with the result of the comparison.
339    fn bool_equal_elem(lhs: BoolTensor<B>, rhs: Scalar) -> BoolTensor<B>;
340
341    /// Element-wise non-equality comparison with a scalar.
342    ///
343    /// # Arguments
344    ///
345    /// * `lhs` - The left-hand side tensor.
346    /// * `rhs` - The right-hand side scalar.
347    ///
348    /// # Returns
349    ///
350    /// The boolean tensor with the result of the comparison.
351    fn bool_not_equal_elem(lhs: BoolTensor<B>, rhs: Scalar) -> BoolTensor<B> {
352        let equal_tensor = B::bool_equal_elem(lhs, rhs);
353        B::bool_not(equal_tensor)
354    }
355
356    /// Inverses boolean values.
357    ///
358    /// # Arguments
359    ///
360    /// * `tensor` - The tensor.
361    ///
362    /// # Returns
363    ///
364    /// The tensor with the result of the negation.
365    fn bool_not(tensor: BoolTensor<B>) -> BoolTensor<B>;
366
367    /// Executes the logical and (`&&`) operation on two boolean tensors.
368    ///
369    /// # Arguments
370    ///
371    /// * `lhs` - The left hand side tensor.
372    /// * `rhs` - The right hand side tensor.
373    ///
374    /// # Returns
375    ///
376    /// The tensor with the result of the logical and.
377    fn bool_and(lhs: BoolTensor<B>, rhs: BoolTensor<B>) -> BoolTensor<B>;
378
379    /// Executes the logical or (`||`) operation on two boolean tensors.
380    ///
381    /// # Arguments
382    ///
383    /// * `lhs` - The left hand side tensor.
384    /// * `rhs` - The right hand side tensor.
385    ///
386    /// # Returns
387    ///
388    /// The tensor with the result of the logical or.
389    fn bool_or(lhs: BoolTensor<B>, rhs: BoolTensor<B>) -> BoolTensor<B>;
390
391    /// Element-wise exclusive or.
392    ///
393    /// # Arguments
394    ///
395    /// * `lhs` - The left hand side tensor.
396    /// * `rhs` - The right hand side tensor.
397    ///
398    /// # Returns
399    ///
400    /// The tensor with the result of the comparison.
401    fn bool_xor(lhs: BoolTensor<B>, rhs: BoolTensor<B>) -> BoolTensor<B> {
402        Self::bool_not_equal(lhs, rhs)
403    }
404
405    /// Transposes a bool tensor.
406    ///
407    /// # Arguments
408    ///
409    /// * `tensor` - The tensor to transpose.
410    ///
411    /// # Returns
412    ///
413    /// The transposed tensor.
414    fn bool_transpose(tensor: BoolTensor<B>) -> BoolTensor<B> {
415        let ndims = tensor.shape().num_dims();
416        Self::bool_swap_dims(tensor, ndims - 2, ndims - 1)
417    }
418
419    /// Swaps two dimensions of a bool tensor.
420    ///
421    /// # Arguments
422    ///
423    /// * `tensor` - The tensor to swap the dimensions of.
424    /// * `dim1` - The first dimension to swap.
425    /// * `dim2` - The second dimension to swap.
426    ///
427    /// # Returns
428    ///
429    /// The tensor with the dimensions swapped.
430    fn bool_swap_dims(tensor: BoolTensor<B>, dim1: usize, dim2: usize) -> BoolTensor<B>;
431
432    /// Permutes the dimensions of a tensor.
433    ///
434    /// # Arguments
435    ///
436    /// * `tensor` - The tensor to permute the dimensions of.
437    /// * `axes` - The new order of the dimensions.
438    /// # Returns
439    ///
440    /// The tensor with the dimensions permuted.
441    fn bool_permute(tensor: BoolTensor<B>, axes: &[usize]) -> BoolTensor<B>;
442
443    /// Reverse the order of elements in a tensor along the given axes.
444    ///
445    /// # Arguments
446    ///
447    /// * `tensor` - The tensor to reverse.
448    /// * `axes` - The axes to reverse.
449    ///
450    /// The tensor with the elements reversed.
451    fn bool_flip(tensor: BoolTensor<B>, axes: &[usize]) -> BoolTensor<B>;
452
453    /// Tests if any element in the boolean `tensor` evaluates to True.
454    ///
455    /// # Arguments
456    ///
457    /// * `tensor` - The tensor to test.
458    ///
459    /// # Returns
460    ///
461    /// A boolean tensor with a single element, True if any element in the tensor is True, False otherwise.
462    fn bool_any(tensor: BoolTensor<B>) -> BoolTensor<B> {
463        let dtype = tensor.dtype();
464        let int_dtype = get_device_settings::<B>(&tensor.device()).int_dtype;
465        let sum = B::int_sum(B::bool_into_int(tensor, int_dtype));
466        B::int_greater_elem(sum, 0.into(), dtype.into())
467    }
468
469    /// Tests if any element in the boolean `tensor` evaluates to True along a given dimension `dim`.
470    ///
471    /// # Arguments
472    ///
473    /// * `tensor` - The tensor to test.
474    /// * `dim` - The axis along which to test.
475    ///
476    /// # Returns
477    ///
478    /// A boolean tensor `Tensor<B, D, Bool>` with the same size as input `tensor`, except in the `dim` axis
479    /// where the size is 1. The elem in the `dim` axis is True if any element along this dim in the input
480    /// evaluates to True, False otherwise.
481    fn bool_any_dim(tensor: BoolTensor<B>, dim: usize) -> BoolTensor<B> {
482        let dtype = tensor.dtype();
483        let int_dtype = get_device_settings::<B>(&tensor.device()).int_dtype;
484        let sum = B::int_sum_dim(B::bool_into_int(tensor, int_dtype), dim);
485        B::int_greater_elem(sum, 0.into(), dtype.into())
486    }
487
488    /// Tests if all elements in the boolean `tensor` evaluate to True.
489    ///
490    /// # Arguments
491    ///
492    /// * `tensor` - The tensor to test.
493    ///
494    /// # Returns
495    ///
496    /// A boolean tensor `Tensor<B, 1, Bool>` with a single element, True if all elements in the input tensor
497    /// evaluate to True, False otherwise.
498    fn bool_all(tensor: BoolTensor<B>) -> BoolTensor<B> {
499        let dtype = tensor.dtype();
500        let int_dtype = get_device_settings::<B>(&tensor.device()).int_dtype;
501        let num_elems = tensor.shape().num_elements() as i64;
502        let sum = B::int_sum(B::bool_into_int(tensor, int_dtype));
503        B::int_equal_elem(sum, num_elems.into(), dtype.into())
504    }
505
506    /// Tests if all elements in the boolean `tensor` evaluate to True along a given dimension `dim`.
507    ///
508    /// # Arguments
509    ///
510    /// * `tensor` - The tensor to test.
511    /// * `dim` - The axis along which to test.
512    ///
513    /// # Returns
514    ///
515    /// A boolean tensor `Tensor<B, D, Bool>` with the same size as input `tensor`, except in the `dim` axis
516    /// where the size is 1. The elem in the `dim` axis is True if all elements along this dim in the input
517    /// evaluates to True, False otherwise.
518    fn bool_all_dim(tensor: BoolTensor<B>, dim: usize) -> BoolTensor<B> {
519        let dtype = tensor.dtype();
520        let int_dtype = get_device_settings::<B>(&tensor.device()).int_dtype;
521        let num_elems = tensor.shape()[dim] as i64;
522        let sum = B::int_sum_dim(B::bool_into_int(tensor, int_dtype), dim);
523        B::int_equal_elem(sum, num_elems.into(), dtype.into())
524    }
525
526    /// Compute the indices of the elements that are non-zero, grouped by element.
527    ///
528    /// # Arguments
529    ///
530    /// * `tensor` - The input tensor.
531    /// * `out_dtype` - The output tensor dtype.
532    ///
533    /// # Returns
534    ///
535    /// A 2D tensor containing the indices of all non-zero elements of the given tensor.
536    /// Each row contains the indices of a non-zero element.
537    fn bool_argwhere(
538        tensor: BoolTensor<B>,
539        out_dtype: IntDType,
540    ) -> impl Future<Output = IntTensor<B>> + 'static + Send {
541        async move {
542            // Size of each output tensor is variable (= number of nonzero elements in the tensor).
543            // Reading the data to count the number of truth values might cause sync but is required.
544            let device = &tensor.device();
545            let data = B::bool_into_data(tensor)
546                .await
547                .expect("Can read the data without error");
548            argwhere_data::<B>(data, device, out_dtype)
549        }
550    }
551
552    /// Broadcasts the bool `tensor` to the given `shape`.
553    fn bool_expand(tensor: BoolTensor<B>, shape: Shape) -> BoolTensor<B>;
554
555    /// Unfold windows along a dimension.
556    ///
557    /// Returns a view of the tensor with all complete windows of size `size` in dimension `dim`;
558    /// where windows are advanced by `step` at each index.
559    ///
560    /// The number of windows is `max(0, (shape[dim] - size).ceil_div(step))`.
561    ///
562    /// # Arguments
563    ///
564    /// * `tensor` - The input tensor to unfold; of shape ``[pre=..., dim shape, post=...]``
565    /// * `dim` - the selected dim.
566    /// * `size` - the size of each unfolded window.
567    /// * `step` - the step between each window.
568    ///
569    /// # Returns
570    ///
571    /// A tensor view with shape ``[pre=..., windows, size, post=...]``.
572    fn bool_unfold(tensor: BoolTensor<B>, dim: usize, size: usize, step: usize) -> BoolTensor<B>;
573}