I'm trying to figure out how to use Pick most efficiently. I use it already in a number of places, but only ever in the context of UnitStep and 0 selector specs. When working on this answer though I hit some odd performance results, though. Here's the setup:

A = BlockRandom[SeedRandom[0]; RandomReal[{-1, 1}, {1000, 1000}]];
{eigenvalues, eigenvectors} = Eigensystem[A];

And now say I want to pick the eigenvectors corresponding to real eigenvalues, here's what I thought to do:

Pick[eigenvectors, Im@eigenvalues, 0.]; // RepeatedTiming

{0.15, Null}

This is bizarre. That's terrible performance for Pick. So then check out this:

Pick[eigenvectors, Unitize@Im@eigenvalues, 0]; // RepeatedTiming

{0.000047, Null}

That's much more in line with expectation.

Okay, then, let's try picking for non-real eigenvalues:

Pick[eigenvectors, Unitize@Im@eigenvalues, 1]; // RepeatedTiming

{0.0047, Null}

Much, much slower, but this could simply be because we're returning a much larger set of results, requiring more memory to be copied. I could believe it.

Now, just for fun I wanted to see what happens if I numericize everything:

Pick[eigenvectors, N@Unitize@Im@eigenvalues, 0.]; // RepeatedTiming

{0.15, Null}

Pick[eigenvectors, N@Unitize@Im@eigenvalues, 1.]; // RepeatedTiming

{0.0061, Null}

All of a sudden the performance has flipped (and quite dramatically). We're back to the original performance for the 0. case, but not for the 1. case. There we have almost the same performance as before. So then I thought that 0. might be special, so I decided to try a no-zero case in the original setup:

Im@eigenvalues // First


Pick[eigenvectors, Im@eigenvalues, 
   17.491604555909646]; // RepeatedTiming

{0.15, Null}

And so that's clearly not what's going on...so what is? What explains these wildly divergent performance characteristics of Pick (and how can I use this to make sure I'm always doing things most efficiently)

  • 7
    $\begingroup$ Hint: Pick[wrap /@ eigenvectors, Im@eigenvalues, 0.]; // RepeatedTiming is 10x faster. I believe this may have to do with the fact that Pick does not only operate at level 1, and may scan all elements of the first argument (even though the second one clearly indicates that level 1 is sufficient). $\endgroup$
    – Szabolcs
    Dec 27, 2018 at 17:30


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.