0

I get ValueError: math domain error when trying to compute the angle between two vectors using math.acos:

import numpy as np
from math import acos, sqrt

x1 = np.array([12.001, 13.001, 14.001])
x2 = np.array([-12.001, -13.001, -14.001])

mom1 = sqrt(x1.dot(x1))
mom2 = sqrt(x2.dot(x2))
son = x1.dot(x2)

c = son / mom1 / mom2

theta_rad = acos(c)

And I get:

ValueError: math domain error
Stef
  • 13,242
  • 2
  • 17
  • 28
jason
  • 11
  • 1
  • When you take a real number `x` and apply function `cos` to it, the result is always going to be inside interval [-1, +1]. For this reason, function `acos`, which is the inverse of `cos` on [-pi, +pi], can only be applied to numbers in interval [-1, +1]. – Stef Jan 10 '23 at 10:42
  • Also note that `mom1=np.sqrt(x1.dot(x1))` can be equivalently rewritten as `mom1 = np.linalg.norm(x1)`, or just `mom1 = norm(x1)` if you put `from numpy.linalg import norm` at the top of your file. – Stef Jan 10 '23 at 10:43
  • Looking at your code it looks like there shouldn't be a problem, since `x1.dot(x2) / (norm(x1) * norm(x2))` should always be in interval [-1, +1]. Can you please try wrapping the `math.acos` call into a `try ... except ...` block to catch the ValueError and print the values of x1 and x2 that result in the math domain error? – Stef Jan 10 '23 at 10:47
  • 1
    Your intended formula `cos(x1^x2) = x1 . x2 / |x1| * |x2|`seems correct, but float operations lose precision, so at some point theta becomes something like `-1.0000000000000002`. – bereal Jan 10 '23 at 10:47
  • Perhaps you can write `theta=son/mom1/mom2; theta = max(-1, min(theta, 1))` to force theta to remain between -1 and +1. – Stef Jan 10 '23 at 10:48
  • Welcome to Stack Overflow. Please read [ask] and [research] and [mre] and https://ericlippert.com/2014/03/05/how-to-debug-small-programs/. We don't provide a debugging service. When code raises an error, please first **read and try to understand** the error message. Look to the line of code: `theta_rad=math.acos(theta)`. So, could there be some reason, why `theta` cannot be passed to `math.acos`? Look to the type of error: `ValueError`. Do you understand what this means? – Karl Knechtel Jan 10 '23 at 11:00
  • Finally, there is a description, "math domain error". Before asking on Stack Overflow, [please try](https://meta.stackoverflow.com/questions/261592) to look up things like this, for example by [using a search engine](https://duckduckgo.com/?q=python+math+domain+error). – Karl Knechtel Jan 10 '23 at 11:01
  • I would also like to make another comment. The fact that your variables are named `theta = son/mom1/mom2` and `theta_rad = acos(theta)` is misleading and hints that maybe you don't understand the relation between these two variables `theta` and `theta_rad`. The name `theta` is most often used to refer to an angle, but you named `theta` the cosine instead of the angle, and theta_rad the angle. I strongly suggest changing their names to better reflect their meaning. For instance, I suggest `cosine_similarity = x1.dot(x2) / (norm(x1) * norm(x2))` and `theta_rad = acos(cosine_similarity)`. – Stef Jan 10 '23 at 11:05
  • Finally, regarding KarlKnechtel's comment about [mre]: Your question contains **a lot** of code, which is very discouraging for anyone reading your question, including people who might know the answer. In particular, all the code about reading and parsing file `model.txt` is completely irrelevant to the question. Instead it would have been much preferable to focus your question by only showing us the small snippet of code reproducing the error. – Stef Jan 10 '23 at 11:11
  • For instance you could have only showed the following code: `import numpy as np; from math import acos, sqrt; x1 = np.array([12.001, 13.001, 14.001]) ; x2 = -x1; mom1 = sqrt(x1.dot(x1)); mom2 = sqrt(x2.dot(x2)); son = x1.dot(x2); c = son / mom1 / mom2; theta = acos(c)` which results in `c = -1.0000000000000002` and `acos(c)` produces `ValueError: math domain error` – Stef Jan 10 '23 at 11:12

1 Answers1

1

Your intended formula cos(theta) = x1 . x2 / (|x1| * |x2|) seems correct, but float operations lose precision, so at some point theta becomes something like -1.0000000000000002. You could handle those situations with e.g.

theta = min(1, max(theta, -1))
bereal
  • 32,519
  • 6
  • 58
  • 104
  • 1
    An alternative with numpy is `theta = numpy.clip(theta, -1, 1)` but I actually prefer the `min(max(-1, theta), 1)` solution because it throws an error if `theta` is not a scalar, which is a good thing, whereas `numpy.clip` would accept any array without throwing an error. – Stef Jan 10 '23 at 13:56