Spaces:
Sleeping
Sleeping
| # 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 | |
| [](solving-guidance.md#when-you-might-prefer-a-numeric-solution) | |
| {func}`~.solve` and {func}`~.solveset` will not try to find a numeric solution, | |
| only a mathematically-exact symbolic solution. So if you want a numeric | |
| solution, use {func}`~.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](https://mpmath.org/)) 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 {external:func}`scipy.optimize.fsolve` can solve a system of | |
| (non-linear) equations | |
| - NumPy's {external:func}`numpy.linalg.solve` can solve a system of linear | |
| scalar equations | |
| - mpmath's {external:func}`~mpmath.findroot`, which {func}`~.nsolve` calls and | |
| can pass parameters to | |
| ## Example of Numerically Solving an Equation | |
| Here is an example of numerically solving one equation: | |
| ```py | |
| >>> 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: | |
| ```py | |
| >>> 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 {func}`~.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`, {func}`~.nsolve` finds the root `1`: | |
| ```py | |
| >>> 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: | |
| ```py | |
| >>> 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)` | |
| ```py | |
| >>> 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`: | |
| ```py | |
| >>> 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 {external:func}`~scipy.optimize.root_scalar`. A recommended workflow is: | |
| 1. use SymPy to generate (by symbolically simplifying or solving an equation) | |
| the mathematical expression | |
| 2. convert it to a lambda function using {func}`~.lambdify` | |
| 3. use a numerical library such as SciPy to generate numerical solutions | |
| ```py | |
| >>> 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 {func}`~sympy.core.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: | |
| ```py | |
| >>> 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`](sympy.core.basic.Basic.subs) can give an incorrect result due to | |
| precision errors, here effectively rounding `-5.12757857962640e-17` to zero: | |
| ```py | |
| >>> f.subs(x, x_value) | |
| 0 | |
| ``` | |
| When substituting in values, you can also leave some symbols as variables: | |
| ```py | |
| >>> 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 | |
| {func}`~.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: | |
| ```py | |
| >>> 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 {func}`~.nsolve`, please post the problem on the [SymPy mailing | |
| list](https://groups.google.com/g/sympy). Until the issue is resolved, you can | |
| use a different method listed in [](#alternatives-to-consider). | |