Solving Guidance¶
These guidelines apply to many types of solving.
Numeric Solutions¶
Equations With no Closed-Form Solution¶
The vast majority of arbitrary nonlinear equations have no closed-form solution. The classes of equations that are solvable are basically:
Linear equations
Polynomials, except where limited by the Abel-Ruffini theorem (learn more about solving polynomials using a
GroebnerBasis
)Equations that can be solved by inverting some transcendental functions
Problems that can be transformed into the cases above (e.g., by turning trigonometric functions into polynomials)
A few other special cases that can be solved with something like the
Lambert W function
Equations that you can
decompose()
via any of the above
SymPy may reflect that your equation has no solutions that can be expressed
algebraically (symbolically), or that SymPy lacks an algorithm to find a
closed-form solution that does exist, by returning an error such as
NotImplementedError
:
>>> from sympy import solve, cos
>>> from sympy.abc import x
>>> solve(cos(x) - x, x, dict=True)
Traceback (most recent call last):
...
NotImplementedError: multiple generators [x, cos(x)]
No algorithms are implemented to solve equation -x + cos(x)
so you may have to solve your equation numerically instead, for example using
nsolve()
>>> from sympy import nsolve, cos
>>> from sympy.abc import x
>>> nsolve(cos(x) - x, x, 2)
0.739085133215161
If you receive non-closed-form solutions such as CRootOf()
(which represents an indexed complex
root of a polynomial), you can evaluate them numerically using
evalf()
:
>>> from sympy import solve
>>> from sympy.abc import x
>>> solutions = solve(x**5 - x - 1, x, dict=True)
>>> solutions
[{x: CRootOf(x**5 - x - 1, 0)}, {x: CRootOf(x**5 - x - 1, 1)}, {x: CRootOf(x**5 - x - 1, 2)}, {x: CRootOf(x**5 - x - 1, 3)}, {x: CRootOf(x**5 - x - 1, 4)}]
>>> [solution[x].evalf(3) for solution in solutions]
[1.17, -0.765 - 0.352*I, -0.765 + 0.352*I, 0.181 - 1.08*I, 0.181 + 1.08*I]
When You Might Prefer a Numeric Solution¶
Even if your problem has a closed-form solution, you might prefer a numeric solution.
Solving functions such as solve()
and solveset()
will not try to
find a numeric solution, only a mathematically-exact symbolic solution. So if
you want a numeric solution, consider nsolve()
.
In some situations, even though a closed-form solution is available, it may be
too cumbersome to be desirable. In that case, you can use
evalf()
instead if a numerical solution is acceptable. For
example, the following solution set contains more than 40 terms total when
expressed exactly (scroll horizontally in the code block below if you want to
view them all), compared to eight when expressed numerically:
>>> from sympy import symbols, solve
>>> x = symbols('x')
>>> solutions = solve(x**4 + 10*x**2 + x + 1, x, dict=True)
>>> solutions
[{x: -sqrt(-20/3 + 56/(9*(1307/432 + sqrt(434607)*I/144)**(1/3)) + 2*(1307/432 + sqrt(434607)*I/144)**(1/3))/2 - sqrt(-40/3 - 2*(1307/432 + sqrt(434607)*I/144)**(1/3) + 2/sqrt(-20/3 + 56/(9*(1307/432 + sqrt(434607)*I/144)**(1/3)) + 2*(1307/432 + sqrt(434607)*I/144)**(1/3)) - 56/(9*(1307/432 + sqrt(434607)*I/144)**(1/3)))/2}, {x: sqrt(-20/3 + 56/(9*(1307/432 + sqrt(434607)*I/144)**(1/3)) + 2*(1307/432 + sqrt(434607)*I/144)**(1/3))/2 - sqrt(-40/3 - 2*(1307/432 + sqrt(434607)*I/144)**(1/3) - 2/sqrt(-20/3 + 56/(9*(1307/432 + sqrt(434607)*I/144)**(1/3)) + 2*(1307/432 + sqrt(434607)*I/144)**(1/3)) - 56/(9*(1307/432 + sqrt(434607)*I/144)**(1/3)))/2}, {x: sqrt(-40/3 - 2*(1307/432 + sqrt(434607)*I/144)**(1/3) - 2/sqrt(-20/3 + 56/(9*(1307/432 + sqrt(434607)*I/144)**(1/3)) + 2*(1307/432 + sqrt(434607)*I/144)**(1/3)) - 56/(9*(1307/432 + sqrt(434607)*I/144)**(1/3)))/2 + sqrt(-20/3 + 56/(9*(1307/432 + sqrt(434607)*I/144)**(1/3)) + 2*(1307/432 + sqrt(434607)*I/144)**(1/3))/2}, {x: sqrt(-40/3 - 2*(1307/432 + sqrt(434607)*I/144)**(1/3) + 2/sqrt(-20/3 + 56/(9*(1307/432 + sqrt(434607)*I/144)**(1/3)) + 2*(1307/432 + sqrt(434607)*I/144)**(1/3)) - 56/(9*(1307/432 + sqrt(434607)*I/144)**(1/3)))/2 - sqrt(-20/3 + 56/(9*(1307/432 + sqrt(434607)*I/144)**(1/3)) + 2*(1307/432 + sqrt(434607)*I/144)**(1/3))/2}]
>>> for solution in solutions:
... solution[x].evalf()
-0.0509758447494279 + 0.313552108895239*I
0.0509758447494279 + 3.14751999969868*I
0.0509758447494279 - 3.14751999969868*I
-0.0509758447494279 - 0.313552108895239*I
In other situations, even if the exact solution has few terms, you may want a numeric solution so you know its approximate numerical value. For example, it may be difficult to estimate that \(\sqrt{2} e^{\pi}/2\) is approximately \(16\):
>>> from sympy import pi, sqrt, exp, solve, evalf
>>> shorter = solve(sqrt(2)*x - exp(pi), x, dict=True)
>>> shorter
[{x: sqrt(2)*exp(pi)/2}]
>>> [solution[x].evalf(3) for solution in shorter]
[16.4]
Use Exact Values¶
If you want to preserve the exact mathematical values of symbols such as
transcendental numbers and square roots, define them so
that SymPy can interpret them symbolically, for example use SymPy’s
Pi
:
>>> from sympy import symbols, solve, pi
>>> x = symbols('x')
>>> solve(x**2 - pi, x, dict=True)
[{x: -sqrt(pi)}, {x: sqrt(pi)}]
If you use the standard Python math version of \(\pi\), Python will pass that inexact value to SymPy, leading to an inexact, numerical solution:
>>> from sympy import symbols, solve
>>> from math import pi
>>> x = symbols('x')
>>> solve(x**2 - pi, x, dict=True)
[{x: -1.77245385090552}, {x: 1.77245385090552}]
To use exact values for numbers such as \(6.2\) or \(1/2\), refer to Python numbers vs. SymPy Numbers.
In certain cases, using an inexact value will prevent SymPy from finding a result. For example, this exact equation can be solved:
>>> from sympy import symbols, solve, sqrt
>>> x = symbols('x')
>>> eq = x**sqrt(2) - 2
>>> solve(eq, x, dict=True)
[{x: 2**(sqrt(2)/2)}]
but if you use the inexact equation eq = x**1.4142135623730951 - 2
, SymPy will
not return a result despite attempting for a long time.
Include the Variable to be Solved for in the Function Call¶
We recommend you include the variable to be solved for as the second argument
for solving functions including solve()
and solveset()
. While
this is optional for univariate equations, it is a good practice because it
ensures SymPy will solve for the desired symbol. For example, you might be
interested in a solution for \(x\), but SymPy solves for \(y\):
>>> from sympy.abc import x, y
>>> from sympy import solve
>>> solve(x**2 - y, dict=True)
[{y: x**2}]
Specifying the variable to solve for ensures that SymPy solves for it:
>>> from sympy.abc import x, y
>>> from sympy import solve
>>> solve(x**2 - y, x, dict=True)
[{x: -sqrt(y)}, {x: sqrt(y)}]
Ensure Consistent Formatting From solve()
¶
solve()
produces a variety of output as explained in
Solve Output by Type. Using dict=True
will give a consistent output format
which is especially important when extracting information about the solution
programmatically.
To extract the solutions, you can iterate through the list of dictionaries:
>>> from sympy import parse_expr, solve, solveset
>>> from sympy.abc import x
>>> expr = "x^2 = y"
>>> parsed = parse_expr(expr, transformations="all")
>>> parsed
Eq(x**2, y)
>>> solutions = solve(parsed, x, dict=True)
>>> [solution[x] for solution in solutions]
[-sqrt(y), sqrt(y)]
>>> solveset(parsed, x)
{-sqrt(y), sqrt(y)}
Options That Can Speed up solve()
¶
Include Solutions Making Any Denominator Zero¶
Normally, solve()
checks whether any solutions make any denominator
zero, and automatically excludes them. If you want to include those solutions,
and speed up solve()
(at the risk of obtaining invalid solutions), set
check=False
:
>>> from sympy import Symbol, sin, solve
>>> x = Symbol("x")
>>> solve(sin(x)/x, x, dict=True) # 0 is excluded
[{x: pi}]
>>> solve(sin(x)/x, x, dict=True, check=False) # 0 is not excluded
[{x: 0}, {x: pi}]
Do Not Simplify Solutions¶
Normally, solve()
simplifies many results before returning them and (if
check
is not False) uses the general simplify()
function on the solutions and the expression obtained when they are substituted
into the function which should be zero. If you do not want the solutions
simplified, and want to speed up solve()
, use simplify=False
.
>>> from sympy import solve
>>> from sympy.abc import x, y
>>> expr = x**2 - (y**5 - 3*y**3 + y**2 - 3)
>>> solve(expr, x, dict=True)
[{x: -sqrt(y**5 - 3*y**3 + y**2 - 3)}, {x: sqrt(y**5 - 3*y**3 + y**2 - 3)}]
>>> solve(expr, x, dict=True, simplify=False)
[{x: -sqrt((y + 1)*(y**2 - 3)*(y**2 - y + 1))}, {x: sqrt((y + 1)*(y**2 - 3)*(y**2 - y + 1))}]
Parse a String Representing the Equation¶
If you are creating the expression yourself, we advise against using string parsing to create expressions. But if you are programmatically reading in a string, this approach is convenient.
You can parse a string representing the equation into a form that SymPy can
understand (for example, Eq
form), then solve
the parsed expression. Parsing an equation from a string requires you to use
transformations
for SymPy to
interpret equals signs
create symbols from your variables
use more mathematical (rather than standard Python) notation, for example the exponent operator can be parsed from
^
rather than having to use Python’s**
.
If you already have the equation in Eq
(equation) form, you can parse that string:
>>> from sympy import parse_expr, solve, solveset
>>> from sympy.abc import x
>>> expr = "Eq(x^2, y)"
>>> parsed = parse_expr(expr, transformations="all")
>>> parsed
Eq(x**2, y)
SymPy can also parse LaTeX into expressions
using parse_latex()
.
Report a Bug¶
If you find a bug with these commands, please post the problem on the SymPy mailing list.