5

I am a beginner in Mathematica. I have this in Mathematica v12:

A = {1, 1, 1, 1}
B = {2, 2, 2, 2}
A+B
(* 3,3,3,3 *)

I'd like to experiment with evaluating arbitrary expressions on arrays, like this:

C = {4, 2, 4, 2}
(A+B)<C
(* I want this output: *)
(* True, False, True, False *)

In the example, above:

  • {1,1,1,1} + {2,2,2,2} evaluates to {3,3,3,3} which is an element-wise operation.
  • {3,3,3,3} < {4,2,4,2} should evaluate to { True, False, True, False }, but it does not do an element-wise operation (which seems logically inconsistent!).

What I have tried

Tried about 30 combinations and permutations of existing functions, none of them appeared to work. Tried the top 6 answers that were vaguely relevant on SO. Browsed through Mathematica documentation to try and find anything relevant.

Edmund
  • 42,267
  • 3
  • 51
  • 143
Contango
  • 463
  • 1
  • 3
  • 10

4 Answers4

6

You may exploit that UnitStep step is vectorized like this:

a = {1, 1, 1, 1};
b = {2, 2, 2, 2};
c = {4, 2, 4, 2};
{False, True}[[2 - UnitStep[(a + b) - c]]]

{True, False, True, False}

If you can life with 0 and 1 instead of False and True, the the following will serve your needs and is more efficient (for large lists):

Subtract[1, UnitStep[Subtract[(a + b), c]]]

In particular, it is two orders of magnitude faster than Thread:

n = 1000000;
a = RandomInteger[{0, 1000}, {n}];
b = RandomInteger[{0, 1000}, {n}];
c = RandomInteger[{0, 1000}, {n}];

r1 = Thread[(a + b) < c]; // RepeatedTiming // First r2 = Subtract[1, UnitStep[Subtract[(a + b), c]]]; // RepeatedTiming // First And @@ ({False, True}[[r2 + 1]] == r1)

0.34

0.0025

True

But of course, Thread is more convenient and also more readible.

Szabolcs wrote a nice packaged that gives you the best of both worlds: It's called BoolEval.

Henrik Schumacher
  • 106,770
  • 7
  • 179
  • 309
5

Update

Can generalize this to account for scalars using ReplaceRepeated and some pattern matching.

bolVectorEval[exp_] := 
 exp //. h_[params : OrderlessPatternSequence[_List .., _?NumericQ ...]] :> 
   Thread[h[params]]

then

bolVectorEval[(a > c) || (4.3 < (b + c))]
{True, False, True, False}

OP

You may use Thread.

Thread[(a + b) < c]
{True, False, True, False}

Hope this helps.

Edmund
  • 42,267
  • 3
  • 51
  • 143
  • Thanks! The first answer works perfectly to evaluate expressions with logical operators such as || or &&, e.g. bolVectorEval[((a + b) > (c + a)) || (a > c)]. @Henrik Schumacher also recommended BoolEval by Szabolcz, that works perfectly as well, and claims to be optimised for efficiency. Example: BoolEval[((a + b) > (c + a)) || (a > c)]. – Contango Mar 18 '21 at 13:43
4
vecLess = Negative @* Subtract;

vecLess[a + b, c]
{True, False, True, False}
a + b < c /. Less -> vecLess
{True, False, True, False}
kglr
  • 394,356
  • 18
  • 477
  • 896
4

Here's a very idiosyncratic way of handling the problem. This would be my goto method if I just needed to get things done and didn't care about performance. It's not the easiest to read, but there's a lot going on that someone new to MMA can learn from:

a = {1, 1, 1, 1};
b = {2, 2, 2, 2};
c = {4, 2, 4, 2};
#1+#2<#3&@@@Transpose[{a,b,c}]

{True, False, True, False}

This post is a great place to start if you're just getting into Mathematica.

N.J.Evans
  • 5,093
  • 19
  • 25