Root finding and solving equations

SciPy · Practical · 10 min

Break-even points, implied rates and equilibrium conditions are equations without closed-form solutions.

A root of f is an x where f(x) = 0. To solve g(x) = c, find the root of g(x) - c.

For one variable, the safest approach is a bracketing method: find a, b where f(a) and f(b) have opposite signs, and optimize.brentq(f, a, b) is guaranteed to converge to a root inside. optimize.root_scalar(f, bracket=[a, b]) is the general interface; it returns a result with root, converged and iterations. Without a bracket, root_scalar(f, x0=..., fprime=...) uses Newton's method (or the secant method if no derivative is given), which is fast but can diverge from a poor start.

For systems of equations, optimize.root(fun, x0) takes a function returning a vector of residuals, one per equation, and solves them simultaneously. Methods include "hybr" (default) and "lm". Check res.success. fsolve is the older interface to the same algorithm.

Tips:

  • Plot or tabulate f first to find brackets and see how many roots exist.
  • Root finders return one root; to find several, bracket each separately.
  • For polynomials, np.roots returns all roots at once.
  • Minimisation is different: minimize looks for flat points of a function, not zeros.

Example

import numpy as np
from scipy import optimize

# Interest rate r such that 1000 now grows to 1500 in 7 years
f = lambda r: 1000 * (1 + r) ** 7 - 1500
r = optimize.brentq(f, 0.0, 1.0)          # f(0) < 0 < f(1)
print(round(r, 5))                         # 0.05963

sol = optimize.root_scalar(np.cos, bracket=[0, 3])
print(sol.converged, round(sol.root, 6))   # True 1.570796

def system(v):                             # x + y = 3 and x*y = 2
    x, y = v
    return [x + y - 3, x * y - 2]

res = optimize.root(system, x0=[0.5, 3.0])
print(res.success, res.x.round(6))         # True [1. 2.]

Remember

  • Bracketing (brentq, root_scalar(bracket=...)) is robust when signs differ at the ends
  • Newton/secant are fast but need a good start
  • optimize.root solves systems: return one residual per equation

Watch out for

  • Expecting a root finder to return all roots instead of the one it converged to.

Check yourself

  1. Why is brentq guaranteed to converge while Newton's method is not?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: docs.scipy.org/doc/scipy/reference/optimize.html#root-finding