SymPy returns no solution for a simple set of trigonometric equations
Question:
I’m trying to solve the following equations:
cos(a) – cos(b) – 1 = 0
and
sin(a) – sin(b) = 0
Using SymPy I get no solutions for (a,b)
.
from sympy import symbols, sin, cos, solve, Eq
a, b = symbols("a b")
eq1 = Eq(cos(a) - cos(b) - 1, 0)
eq2 = Eq(sin(a) - sin(b), 0)
solve([eq1, eq2], [a, b])
However, there exists at least one solution:
a = 60°
b = 120°
Why can’t SymPy solve the set of trigonometric equations?
Answers:
I think solve
is not the best tool for this job. It probably fails because there could be many solutions. You can try to use nsolve
to numerically solve the system. However, in your case it is really "finicky" on the initial guess. For example:
from sympy import *
a, b = symbols("a b")
eq1 = Eq(cos(a) - cos(b) - 1, 0)
eq2 = Eq(sin(a) - sin(b), 0)
nsolve((cos(a) - cos(b) - 1, sin(a) - sin(b)), [a, b], (0.5, 0.5))
# ZeroDivisionError: matrix is numerically singular
# one solution
nsolve((cos(a) - cos(b) - 1, sin(a) - sin(b)), [a, b], (0.5, 0.6))
# out: Matrix([[7.33038285837618], [8.37758040957278]]) # radians
# the solution you are looking for
nsolve((cos(a) - cos(b) - 1, sin(a) - sin(b)), [a, b], (0.5, 0.95))
# out: Matrix([[1.04719755119660], [2.09439510239320]])
EDIT to clarify comment:
Can you please explain what is the (0.5, 0.5) or (0.5,0.6) and (0.5, 0.95)? How did you arrive at those sets?
Those are the initial guesses that I provided to nsolve
. If you have two equations (like in this example), one way to figure them out is to plot the zero-level contours of the equations and look for the intersection of the curves. For example:
import numpy as np
import matplotlib.pyplot as plt
aa, bb = np.mgrid[0:2*np.pi:100j, 0:2*np.pi:100j]
# NOTE: eq1.rewrite(Add) = eq1.lhs - eq1.rhs
f1 = lambdify([a, b], eq1.rewrite(Add))
f2 = lambdify([a, b], eq2.rewrite(Add))
plt.figure()
plt.contour(aa, bb, f1(aa, bb), levels=[0], cmap="hsv")
plt.contour(aa, bb, f2(aa, bb), levels=[0], cmap="Blues_r")
plt.xlabel("a")
plt.xlabel("b")
plt.grid()
plt.show()
With this method you can chose initial guesses very close to the actual solutions.
If you rewrite in terms of exp
— and I thought solve
did this automatically, but maybe only for a single equation — you get the solution:
>>> solve([i.rewrite(exp) for i in (cos(a) - cos(b) - 1,sin(a) - sin(b))])
[{a: pi/3, b: 2*pi/3}, {a: -I*log(-(-1)**(2/3)), b: -2*pi/3}]
I’m trying to solve the following equations:
cos(a) – cos(b) – 1 = 0
and
sin(a) – sin(b) = 0
Using SymPy I get no solutions for (a,b)
.
from sympy import symbols, sin, cos, solve, Eq
a, b = symbols("a b")
eq1 = Eq(cos(a) - cos(b) - 1, 0)
eq2 = Eq(sin(a) - sin(b), 0)
solve([eq1, eq2], [a, b])
However, there exists at least one solution:
a = 60°
b = 120°
Why can’t SymPy solve the set of trigonometric equations?
I think solve
is not the best tool for this job. It probably fails because there could be many solutions. You can try to use nsolve
to numerically solve the system. However, in your case it is really "finicky" on the initial guess. For example:
from sympy import *
a, b = symbols("a b")
eq1 = Eq(cos(a) - cos(b) - 1, 0)
eq2 = Eq(sin(a) - sin(b), 0)
nsolve((cos(a) - cos(b) - 1, sin(a) - sin(b)), [a, b], (0.5, 0.5))
# ZeroDivisionError: matrix is numerically singular
# one solution
nsolve((cos(a) - cos(b) - 1, sin(a) - sin(b)), [a, b], (0.5, 0.6))
# out: Matrix([[7.33038285837618], [8.37758040957278]]) # radians
# the solution you are looking for
nsolve((cos(a) - cos(b) - 1, sin(a) - sin(b)), [a, b], (0.5, 0.95))
# out: Matrix([[1.04719755119660], [2.09439510239320]])
EDIT to clarify comment:
Can you please explain what is the (0.5, 0.5) or (0.5,0.6) and (0.5, 0.95)? How did you arrive at those sets?
Those are the initial guesses that I provided to nsolve
. If you have two equations (like in this example), one way to figure them out is to plot the zero-level contours of the equations and look for the intersection of the curves. For example:
import numpy as np
import matplotlib.pyplot as plt
aa, bb = np.mgrid[0:2*np.pi:100j, 0:2*np.pi:100j]
# NOTE: eq1.rewrite(Add) = eq1.lhs - eq1.rhs
f1 = lambdify([a, b], eq1.rewrite(Add))
f2 = lambdify([a, b], eq2.rewrite(Add))
plt.figure()
plt.contour(aa, bb, f1(aa, bb), levels=[0], cmap="hsv")
plt.contour(aa, bb, f2(aa, bb), levels=[0], cmap="Blues_r")
plt.xlabel("a")
plt.xlabel("b")
plt.grid()
plt.show()
With this method you can chose initial guesses very close to the actual solutions.
If you rewrite in terms of exp
— and I thought solve
did this automatically, but maybe only for a single equation — you get the solution:
>>> solve([i.rewrite(exp) for i in (cos(a) - cos(b) - 1,sin(a) - sin(b))])
[{a: pi/3, b: 2*pi/3}, {a: -I*log(-(-1)**(2/3)), b: -2*pi/3}]