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
17.491604555909646
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)
Pick[wrap /@ eigenvectors, Im@eigenvalues, 0.]; // RepeatedTiming
is 10x faster. I believe this may have to do with the fact thatPick
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$