I am calculating signed vector angles (limited to {-Pi,Pi}), but the twist is that these calculations are being done in the context of a 'landscape' array. I have a point on a 2D landscape and a direction expressed as a vector. Imagine an animal standing one the landscape and looking along that vector. What I need to calculate is the signed vector angle between that 'looking angle' and every location on the array, so the end result is an array of signed angles. And of course I need to do it as fast as possible. It is simple enough to generate an {x,y} coordinate for each element of the array and subtract the location of the animal to turn these into vectors. Functionally then, the task is to map a 'signed angle' function over that array of vectors. But to make it fast, we want to be able to use Listable functions and vectorize calcuations as much as possible. And maybe compile it as well! Here is my current code:

signedVectorAnglesC = 
 Compile[{{vec1Array, _Real, 3}, {vec2, _Real, 1}}, 
  Module[{a2, a1Array, aArray, piArray, piNegArray, piPosArray},
   a2 = ArcTan[vec2[[1]], vec2[[2]]];
   a1Array = 
    ArcTan[vec1Array[[All, All, 1]], vec1Array[[All, All, 2]]];
   aArray = a1Array - a2;
   piArray = IntegerPart[aArray/Pi];
   piNegArray = N[-Clip[piArray, {-1, -1}, {0, 0}]];
   piPosArray = N[Clip[piArray, {1, 1}, {0, 0}]];
   aArray + piNegArray*2*Pi - piPosArray*2*Pi

Here is an example for input

landscapeDimensions = {800, 500};
arrayCoordinates = 
   Reverse[Table[{i, j}, {i, 1, landscapeDimensions[[2]]}, {j, 1, 
       landscapeDimensions[[1]]}] - 0.5], {2}];
animalLoc = {327.2, 100.7};
animalVec = {1.1, 0.4};
arrayVectors = Map[# - animalLoc &, arrayCoordinates, {2}];

It works, and is reasonably fast

In[1306]:= Timing[
 arrayAngles = signedVectorAnglesC[arrayVectors, animalVec];]

Out[1306]= {0.060664, Null}

But I want it to be as fast as possible, because I need to do it repeatedly in the context of a simulation. I found the challenge to be the correction factor to bring the ArcTan differences into the range {-Pi,Pi}: the last four lines of the function do that in a way that is hacky but quite fast in the array context. But perhaps I have missed something simpler? Anyone up for a speed challenge?

  • $\begingroup$ Mod[x,2 Pi, -Pi] $\endgroup$
    – mikado
    Aug 24 '20 at 14:29
  • $\begingroup$ Yes! Thanks -- I didn't know about the offset option in Mod. $\endgroup$ Aug 24 '20 at 14:45

I also discovered that sending the array of vectors as separate x and y components, eliminating the need to separate them within the function, also speeds things up. In fact both that and the Mod suggestion independently halve the time, so now I have this:

signedVectorAnglesC = 
 Compile[{{vecXArray, _Real, 2}, {vecYArray, _Real, 2}, {vec2, _Real, 
  Mod[ArcTan[vecXArray, vecYArray] - ArcTan[vec2[[1]], vec2[[2]]], 
   2*Pi, -Pi]

Resulting in

In[1399]:= Timing[
 arrayAngles = 
   signedVectorAnglesC[arraXVectors, arraYVectors, animalVec];]

Out[1399]= {0.018517, Null}

Hard to see how this can get any faster!


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.