It's been out for quite a while now, but for those who don't know about it, I would like to announce the BoolEval package.
Note: You can try a preview of the main function from the Wolfram Function Repository.
The package provides a fast vectorized way to evaluate array comparisons (equalities or inequalities, possibly joined with logical operations).
Suppose we want to select the numbers greater than 3 from the list {1, 2, ..., 10}
.
In[1]:= Needs["BoolEval`"]
In[2]:= mask = BoolEval[Range[10] > 3]
Out[2]= {0, 0, 0, 1, 1, 1, 1, 1, 1, 1}
BoolEval
returns an array which contains 1s where the condition is true and 0 elsewhere.
We can use this result for various things, such as masking the array: mask*Range[10]
; counting the number of elements that satisfy the condition: Total[mask]
; or Pick
ing them out. BoolEval includes convenient helper functions for this:
In[3]:= arr = Range[10];
BoolPick[arr, arr > 3]
Out[4]= {4, 5, 6, 7, 8, 9, 10}
In[5]:= BoolCount[arr > 3]
Out[5]= 7
Those who use basically any other scientific computing system (such as MATLAB, numpy, R, Julia, etc.) will immediately recognize this way of working. Indeed, the original idea for BoolEval came from a StackExchange thread asking for this feature back in 2012: Does Mathematica have advanced indexing?
Of course, the very same operations can be performed with Select
, Cases
and Count
. Why do we need BoolEval then? The answer: BoolEval is much, much faster.
In[6]:= arr = RandomReal[1, 1000000];
In[7]:= res1 = Select[arr, # < 0.3 &]; // RepeatedTiming
Out[7]= {0.490, Null}
In[8]:= res2 = Cases[arr, x_ /; x < 0.3]; // RepeatedTiming
Out[8]= {0.4638, Null}
In[9]:= res3 = BoolPick[arr, arr < 0.3]; // RepeatedTiming
Out[9]= {0.017, Null}
In this case, it's faster by a factor of 30. It achieved this amazing speed by converting the comparisons to arithmetic. E.g. arr >= 0
can be computed as UnitStep[arr]
. To see how BoolEval converts a complex expression into arithmetic, just pass a symbolic expression to it:
In[11]:= BoolEval[3 < a < 5 || a^2 < 1]
Out[11]= Unitize[1 + (1 - UnitStep[3 - a]) (1 - UnitStep[-5 + a]) - UnitStep[-1 + a^2]]
Advanced Mathematica users will often use the UnitStep
trick to get fast results, but as you can see, the complexity of the arithmetic transcription will quickly get out of hand ... It's difficult not to make a mistake. BoolEval
does the conversion automatically, and behind the scenes. It basically allows one to use a convenient, human-readable notation while still getting the performance boost.
BoolEval works with any expression that Mathematica can do arithmetic on, including images. E.g., we can select image levels between 0.75 and 0.85 intensity:
im = ColorConvert[ExampleData[{"AerialImage", "Oakland2"}], "Grayscale"]
BoolEval[0.75 < im < 0.85]
I am preparing to release a new version of BoolEval in the coming days. I made this post to collect feedback. Please let me know if you have any suggestion, especially if you have any ideas about how to improve the documentation or what cool examples to include. Please be sure to read the tutorial included in the documentation.