简体   繁体   中英

SymPy unable to solve a system of trigonometric equations

I'm trying to get SymPy to solve a system of equations but it gives me an error saying:

NotImplementedError: could not solve 3*sin(3*t0/2)*tan(t0) + 2*cos(3*t0/2) - 4

Is there another way for me to be able to solve the system of equations:

sin(x)+(y-x)cos(x)           = 0

-1.5(y-x)sin(1.5x)+cos(1.5x) = 2

I used :

from sympy import *
solve([sin(x)+(y-x)cos(x), -1.5(y-x)sin(1.5x)+cos(1.5x)-2], x, y)

SymPy could do better with this equation, but ultimately it's equivalent to some 10th degree polynomial the roots of which can only be represented abstractly. I'll describe the steps one can take and show how far SymPy can go. It's a semi-manual solution process which should be more automatic.

First of all, don't put 1.5, or other floating point numbers, in the equations. Instead, introduce a coefficient a = Rational(3, 2) and use that:

eq = [sin(x) + (y-x)*cos(x), -a*(y-x)*sin(a*x) + cos(a*x) - 2]

Variable y can be eliminated using the first equation: y=x-tan(x) , which is easy for us to see, but SymPy sometimes misses the opportunity. Let's help it:

eq1 = eq[1].subs(y, x-tan(x))   #   3*sin(3*x/2)*tan(x)/2 + cos(3*x/2) - 2

As is, solve and solveset (an alternative SymPy solver) give up on the equation because of this mix of trigonometric functions of different arguments. Some of us remember from school days that trigonometric functions can be expressed as rational functions of the tangent of half-argument, so let's do that: rewrite the equation in terms of tan .

eq2 = eq1.rewrite(tan)   #   (-tan(3*x/4)**2 + 1)/(tan(3*x/4)**2 + 1) - 2 + 3*tan(3*x/4)*tan(x)/(tan(3*x/4)**2 + 1)

As mentioned, this halves the argument. Having fractions like x/4 in trig functions is bad. Introduce a new symbol, var('u') , and make u = x/4:

eq3 = eq2.subs(x, 4*u)   #   (-tan(3*u)**2 + 1)/(tan(3*u)**2 + 1) - 2 + 3*tan(3*u)*tan(4*u)/(tan(3*u)**2 + 1)

Now we can expand all these tangents in terms of tan(u) , using expand_trig . The equation gets longer:

eq4 = expand_trig(eq3)  #  (1 - (-tan(u)**3 + 3*tan(u))**2/(-3*tan(u)**2 + 1)**2)/(1 + (-tan(u)**3 + 3*tan(u))**2/(-3*tan(u)**2 + 1)**2) - 2 + 3*(-4*tan(u)**3 + 4*tan(u))*(-tan(u)**3 + 3*tan(u))/((1 + (-tan(u)**3 + 3*tan(u))**2/(-3*tan(u)**2 + 1)**2)*(-3*tan(u)**2 + 1)*(tan(u)**4 - 6*tan(u)**2 + 1))

But it's also simpler because tan(u) can be treated as another unknown, say v .

eq5 = eq4.subs(tan(u), v)  #  (1 - (-v**3 + 3*v)**2/(-3*v**2 + 1)**2)/(1 + (-v**3 + 3*v)**2/(-3*v**2 + 1)**2) - 2 + 3*(-4*v**3 + 4*v)*(-v**3 + 3*v)/((1 + (-v**3 + 3*v)**2/(-3*v**2 + 1)**2)*(-3*v**2 + 1)*(v**4 - 6*v**2 + 1))

Great, now we have a rational function. It can be handled with solveset(eq5, x) . By default solveset gives all complex solutions and we need only real roots among them, so let's specify the domain as Reals:

vsol = list(solveset(eq5, v, domain=S.Reals))

There is no algebraic formula for these, so they are recorded somewhat abstractly but these are actual numbers we can work with:

[CRootOf(3*v**10 + 9*v**8 - 78*v**6 + 22*v**4 - 21*v**2 + 1, 0),
 CRootOf(3*v**10 + 9*v**8 - 78*v**6 + 22*v**4 - 21*v**2 + 1, 1),
 CRootOf(3*v**10 + 9*v**8 - 78*v**6 + 22*v**4 - 21*v**2 + 1, 2),
 CRootOf(3*v**10 + 9*v**8 - 78*v**6 + 22*v**4 - 21*v**2 + 1, 3)]

For example, we can go back to x and y now, and evaluate the solutions:

xsol = [4*atan(v) for v in vsol] 
ysol = [x - tan(x) for x in xsol]
numsol = [(N(x), N(y)) for x, y in zip(xsol, ysol)]

Numeric values are

[(-4.35962510714700, -1.64344290066272),
 (-0.877886785847899, 0.326585146723377),
 (0.877886785847899, -0.326585146723377),
 (4.35962510714700, 1.64344290066272)]

Of course there are infinitely more because the tangent is periodic. Finally, let's check these actually work:

residuals = [[e.subs({x: xv, y: yv}) for e in eq] for xv, yv in numsol]

These are a bunch of numbers of order 1e-15 or less, so yes, the equations hold within machine precision.

Unlike a purely numeric solution we'd get from SciPy or other numeric solvers, these can be evaluated with any accuracy without repeating the process. For example, 50 digits of the first x-solution:

xsol[0].evalf(50)  #   -4.3596251071470021258397061103704574594477338857831

Just for the fun of it here is a manual solution that only needs solving a polynomial of degree 5:

Write t = x/2 , a = yx , s = sin t , c = cos t , S = sin x and C = cos x .

The the given equations can be rewritten

(1)  2 sc + a (c^2 - s^2) = 0
(2)  3 a s^3 - 9 a c^2 s - 6 c s^2 + 2 c^3 = 4

Multiplying (1) by 3 s and adding to (2):

(3)  -6 a c^2 s + 2 c^3 = 4

Next we substitute a = -S / C and use S = 2sc and s^2 = 1 - c^2 :

(4)  12 c^3 (1 - c^2) / C + 2 c^3 = 4

Multiply with C = 2 c^2 - 1 :

(5)  c^3 (12 - 12 c^2 + 4 c^2 - 2) = 8 c^2 - 4

Finally,

(6)   4 c^5 - 5 c^3 + 4 c^2 - 2 = 0

This has a pair of complex solutions, one real solution outside the domain of the cosine and another two solutions which give the four principal solutions for x.

(7)   c_1/2 = 0.90520121, -0.57206084
(8)   x_1/2/3/4 = +/- 2 arccos(x_1/2)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM