Solve One or a System of Equations Numerically¶
Use SymPy to numerically solve a system of one or more equations. For example, numerically solving \(\cos(x) = x \) returns \( x \approx 0.739085133215161\).
Solving numerically is useful if:
You only need a numeric solution, not a symbolic one
A closed-form solution is not available or is overly complicated; refer to When You Might Prefer a Numeric Solution
solve()
and solveset()
will not try to find a numeric solution,
only a mathematically-exact symbolic solution. So if you want a numeric
solution, use nsolve()
.
SymPy is designed for symbolic mathematics. If you do not need to do symbolic operations, then for numerical operations you can use another free and open-source package such as NumPy or SciPy which will be faster, work with arrays, and have more algorithms implemented. The main reasons to use SymPy (or its dependency mpmath) for numerical calculations are:
to do a simple numerical calculation within the context of a symbolic calculation using SymPy
if you need the arbitrary precision capabilities to get more digits of precision than you would get from float64.
Alternatives to Consider¶
SciPy’s
scipy.optimize.fsolve()
can solve a system of (non-linear) equationsNumPy’s
numpy.linalg.solve()
can solve a system of linear scalar equationsmpmath’s
findroot()
, whichnsolve()
calls and can pass parameters to
Example of Numerically Solving an Equation¶
Here is an example of numerically solving one equation:
>>> from sympy import cos, nsolve, Symbol
>>> x = Symbol('x')
>>> nsolve(cos(x) - x, x, 1)
0.739085133215161
Guidance¶
Overdetermined systems of equations are supported.
Find Complex Roots of a Real Function¶
To solve for complex roots of real functions, specify a nonreal (either purely imaginary, or complex) initial point:
>>> from sympy import nsolve
>>> from sympy.abc import x
>>> nsolve(x**2 + 2, 1) # Real initial point returns no root
Traceback (most recent call last):
...
ValueError: Could not find root within given tolerance. (4.18466446988997098217 > 2.16840434497100886801e-19)
Try another starting point or tweak arguments.
>>> from sympy import I
>>> nsolve(x**2 + 2, I) # Imaginary initial point returns a complex root
1.4142135623731*I
>>> nsolve(x**2 + 2, 1 + I) # Complex initial point returns a complex root
1.4142135623731*I
Ensure the Root Found is in a Given Interval¶
It is not guaranteed that nsolve()
will find the root closest to the
initial point. Here, even though the root -1
is closer to the initial point of
-0.1
, nsolve()
finds the root 1
:
>>> from sympy import nsolve
>>> from sympy.abc import x
>>> nsolve(x**2 - 1, -0.1)
1.00000000000000
You can ensure the root found is in a given interval, if such a root exists,
using solver='bisect'
by specifying the interval in a tuple. Here, specifying
the interval (-10, 0)
ensures that the root -1
is found:
>>> from sympy import nsolve
>>> from sympy.abc import x
>>> nsolve(x**2 - 1, (-10, 0), solver='bisect')
-1.00000000000000
Solve a System of Equations Numerically¶
To solve a system of multidimensional functions, supply a tuple of
functions
(f1, f2)
variables to solve for
(x1, x2)
starting values
(-1, 1)
>>> from sympy import Symbol, nsolve
>>> x1 = Symbol('x1')
>>> x2 = Symbol('x2')
>>> f1 = 3 * x1**2 - 2 * x2**2 - 1
>>> f2 = x1**2 - 2 * x1 + x2**2 + 2 * x2 - 8
>>> print(nsolve((f1, f2), (x1, x2), (-1, 1)))
Matrix([[-1.19287309935246], [1.27844411169911]])
Increase Precision of the Solution¶
You can increase the precision of the solution using prec
:
>>> from sympy import Symbol, nsolve
>>> x1 = Symbol('x1')
>>> x2 = Symbol('x2')
>>> f1 = 3 * x1**2 - 2 * x2**2 - 1
>>> f2 = x1**2 - 2 * x1 + x2**2 + 2 * x2 - 8
>>> print(nsolve((f1, f2), (x1, x2), (-1, 1), prec=25))
Matrix([[-1.192873099352460791205211], [1.278444111699106966687122]])
Create a Function That Can Be Solved With SciPy¶
As noted above, SymPy focuses on symbolic computation and is not optimized for
numerical calculations. If you need to make many calls to a numerical solver, it
can be much faster to use a solver optimized for numerical calculations such as
SciPy’s root_scalar()
. A recommended workflow is:
use SymPy to generate (by symbolically simplifying or solving an equation) the mathematical expression
convert it to a lambda function using
lambdify()
use a numerical library such as SciPy to generate numerical solutions
>>> from sympy import simplify, cos, sin, lambdify
>>> from sympy.abc import x, y
>>> from scipy.optimize import root_scalar
>>> expr = cos(x * (x + x**2)/(x*sin(y)**2 + x*cos(y)**2 + x))
>>> simplify(expr) # 1. symbolically simplify expression
cos(x*(x + 1)/2)
>>> lam_f = lambdify(x, cos(x*(x + 1)/2)) # 2. lambdify
>>> sol = root_scalar(lam_f, bracket=[0, 2]) # 3. numerically solve using SciPy
>>> sol.root
1.3416277185114782
Use the Solution Result¶
Substitute the Result Into an Expression¶
The best practice is to use evalf()
to substitute numerical
values into expressions. The following code demonstrates that the numerical
value is not an exact root because substituting it back into the expression
produces a result slightly different from zero:
>>> from sympy import cos, nsolve, Symbol
>>> x = Symbol('x')
>>> f = cos(x) - x
>>> x_value = nsolve(f, x, 1); x_value
0.739085133215161
>>> f.evalf(subs={x: x_value})
-5.12757857962640e-17
Using subs
can give an incorrect result due to
precision errors, here effectively rounding -5.12757857962640e-17
to zero:
>>> f.subs(x, x_value)
0
When substituting in values, you can also leave some symbols as variables:
>>> from sympy import cos, nsolve, Symbol
>>> x = Symbol('x')
>>> f = cos(x) - x
>>> x_value = nsolve(f, x, 1); x_value
0.739085133215161
>>> y = Symbol('y')
>>> z = Symbol('z')
>>> g = x * y**2
>>> values = {x: x_value, y: 1}
>>> (x + y - z).evalf(subs=values)
1.73908513321516 - z
Not all Equations Can be Solved¶
nsolve()
is a numerical solving function, so it can often provide a
solution for equations which cannot be solved algebraically.
Equations With no Solution¶
Some equations have no solution, in which case SymPy may return an error. For
example, the equation \(e^x = 0\) (exp(x)
in SymPy) has no solution:
>>> from sympy import nsolve, exp
>>> from sympy.abc import x
>>> nsolve(exp(x), x, 1, prec=20)
Traceback (most recent call last):
...
ValueError: Could not find root within given tolerance. (5.4877893607115270300540019e-18 > 1.6543612251060553497428174e-24)
Try another starting point or tweak arguments.
Report a Bug¶
If you find a bug with nsolve()
, please post the problem on the SymPy mailing
list. Until the issue is resolved, you can
use a different method listed in Alternatives to Consider.