I have a several boolean matrices, and I want a resulting matrix that indicates if any of the elements in that position of those matrices are true. Is there a single function in the Julia language that would allow me to obtain an elementwise OR over an arbitrary number of matrices?
# My data
a = Bool[1 0; 1 1]
b = Bool[0 0; 1 1]
c = Bool[0 0; 0 0]
d = Bool[0 0; 1 1]
# Arrays of Bool Arrays
z1 = [a]
z2 = [a, b]
z3 = [b, c, d]
z4 = [a, b, c, d]
z100 = [rand(Bool, 2, 2) for i in 1:100]
# Expected
julia> some_function(z1)
2×2 BitMatrix:
1 0
1 1
julia> some_function(z2)
2×2 BitMatrix:
1 0
1 1
julia> some_function(z3)
2×2 BitMatrix:
0 0
1 1
julia> some_function(z4)
2×2 BitMatrix:
1 0
1 1
julia> some_function(z100)
2×2 BitMatrix:
1 1
1 1
This question was originally asked on Julia Slack.
The straightforward approach to this in Julia uses broadcasting. The OR operator in Julia is |
. To broadcast an operator like this, we can prefix it with a dot as follows.
julia> .|(a)
2×2 BitMatrix:
1 0
1 1
julia> .|(a,b)
2×2 BitMatrix:
1 0
1 1
julia> .|(a,b,c)
2×2 BitMatrix:
1 0
1 1
julia> .|(a,b,c,d)
2×2 BitMatrix:
1 0
1 1
Indicating each matrix manually is tedious. To avoid this, we can use the splat operator which will take elements in an iterator and turn them each into a distinct argument for the function being called.
julia> .|(z1...)
2×2 BitMatrix:
1 0
1 1
julia> .|(z2...)
2×2 BitMatrix:
1 0
1 1
julia> .|(z3...)
2×2 BitMatrix:
0 0
1 1
julia> .|(z4...)
2×2 BitMatrix:
1 0
1 1
julia> .|(z100...)
2×2 BitMatrix:
1 1
1 1
Note that broadcasting allows for expansion of some of the arguments so all the arguments do not need to be the same shape.
julia> .|(z4..., [1 0])
2×2 Matrix{Int64}:
1 0
1 1
julia> .|(z4..., [0 1])
2×2 Matrix{Int64}:
1 1
1 1
julia> .|(z4..., [0, 1])
2×2 Matrix{Int64}:
1 0
1 1
julia> .|(z4..., [1, 0])
2×2 Matrix{Int64}:
1 1
1 1
julia> .|(z4..., 0)
2×2 Matrix{Int64}:
1 0
1 1
julia> .|(z4..., 1)
2×2 Matrix{Int64}:
1 1
1 1
Since the above solutions use broadcasting, they are quite general. If we constrain the problem such that all the boolean matrices must be the same size, then we can take advantage of short circuit evaluation. Once we find a 1
or true
value in any position, we do not need to check the elements in the same position of subsequent matrices. To implement this we will use the any
function along with an array comprehension.
julia> short_circuit_or(z...) = short_circuit_or(z)
short_circuit_or (generic function with 1 method)
julia> short_circuit_or(z::Tuple) = [
any(x->x[ind], z) for ind in CartesianIndices(first(z))
]
short_circuit_or (generic function with 2 methods)
julia> short_circuit_or(a,b,c)
2×2 Matrix{Bool}:
1 0
1 1
julia> short_circuit_or(z4...)
2×2 Matrix{Bool}:
1 0
1 1
julia> short_circuit_or(z1)
2×2 Matrix{Bool}:
1 0
1 1
julia> short_circuit_or(z2)
2×2 Matrix{Bool}:
1 0
1 1
julia> short_circuit_or(z3)
2×2 Matrix{Bool}:
0 0
1 1
julia> short_circuit_or(z4)
2×2 Matrix{Bool}:
1 0
1 1
julia> short_circuit_or(z100)
2×2 Matrix{Bool}:
1 1
1 1
As demonstrated by these benchmarks, short circuit evaluation can save time.
julia> using BenchmarkTools
julia> @btime .|($z100...)
3.032 ms (24099 allocations: 1.91 MiB)
2×2 BitMatrix:
1 1
1 1
julia> @btime short_circuit_or($z100)
76.413 ns (1 allocation: 96 bytes)
2×2 Matrix{Bool}:
1 1
1 1
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With