1

I am trying to calculate the angle between some vectors in python2.7. I am using the following identity to find the angle.

theta = acos(v . w / |v||w|)

For a particular instance my code is:

v = numpy.array([1.0, 1.0, 1.0])
w = numpy.array([1.0, 1.0, 1.0])
a = numpy.dot(v, w) / (numpy.linalg.norm(v) * numpy.linalg.norm(w))
theta = math.acos(a)

When I run this I get the error ValueError: math domain error

I assume this is because acos is only defined on the domain [-1,1] and my value 'a' is a float which is very close to 1 but actually a little bit bigger. I can confirm this with print Decimal(a) and I get 1.0000000000000002220446...

What is the best way to work around this problem?

All I can think of is to check for any values of 'a' being bigger than 1 (or less than -1) and round them to precisely 1. This seems like a tacky work around. Is there a neater / more conventional way to solve this problem?

Jemma
  • 112
  • 6

1 Answers1

1

numpy.clip: was used in Angles between two n-dimensional vectors in Python

numpy.nan_to_num: also looks like a good patch if you re-arrange the math

and could be used with your code modified with my theta = atan2(b,a) formulation to avoid 1 + eps trouble with acos (which was my my 1st pass with: b = np.nan_to_num(np.sqrt(1 - a ** 2)))

But I have issues with the near universal use of the dot product alone with acos for the angle between vectors problem, particularly in 2 and 3 D where we have a np.cross product

I prefer forming the cross product b "sine" term, passing both the unnormalized a "cosine" term and my b to atan2:

import numpy as np
v = np.array([1.0, 1.0, 1.0])
w = np.array([1.0, 1.0, 1.0])  

a = np.dot(v, w)
c = np.cross(v, w)
b = np.sqrt(np.dot(c,c))

theta = np.arctan2(b,a)

the atan2(b, a) formulation won't throw an exception with 1 + eps float errors from linalg.norm floating point tolerance if you use the normed args - and atan2 doesn't need normed args anyway

I believe it is more numerically robust with the cross product b term and atan2 giving better accuracy overall than just using the information in the a dot product "cosine" term with acos

edit: (a bit of a Math explaination, not the same a, b, c as in the code above, somewhat mixed up vector math typed in text since MathJax doesn't seem to be enabled on this forum )

a * b = |a||b| cos(w)

c = a x b = |a||b| sin(w) c_unit_vector

sqrt(c * c) = |a||b| sin(w) since c_unit_vector * c_unit_vector = 1

so we end up with atan( |a||b| sin(w), |a||b| cos(w) ) and the |a||b| cancel out in the ratio calculation internal to atan

Community
  • 1
  • 1
f5r5e5d
  • 3,656
  • 3
  • 14
  • 18
  • I have implemented this tan solution. It works well (thank you), but how does it work? It looks like it would take the angle between the projection of one vector onto the other, and an orthogonal. – Jemma Jan 24 '17 at 00:24