Connects to: All preceding analytical results
When analytical solutions exist, we use them. The Solow model has a closed-form transition path; the log-utility Diamond OLG has an algebraic steady state; the scalar NK MSV solution is a single formula. But these are the exceptions. In medium-scale DSGE models with ten or more state variables, financial frictions, occasionally-binding constraints, and heterogeneous agents, closed-form solutions are impossible. Numerical methods are not a fallback — they are the primary tool.
This part covers the five fundamental numerical techniques that underlie every computational macroeconomics software package, from Dynare to QuantEcon. Chapter 22 develops root-finding: bisection, Newton–Raphson (with a proof of quadratic convergence), and quasi-Newton methods for finding DSGE steady states. Chapter 23 covers numerical integration — trapezoidal rules, Gaussian quadrature, and Monte Carlo — for the expected-utility integrals that appear in dynamic programming and option pricing. Chapter 24 develops numerical optimization, from gradient descent through BFGS, applied to the central bank’s optimal policy problem. Chapter 25 addresses linear system solution: LU decomposition, sparse matrix methods, and QR — the linear algebra backbone of the Kalman filter (Chapter 20), the DSGE solution (Chapter 28), and the Leontief model (Chapter 2). Chapter 26 synthesizes everything into Monte Carlo simulation of macroeconomic models: the Tauchen discretization, Markov chain simulation, bootstrap confidence bands, and the particle filter for nonlinear state-space models.
The mathematical depth of this part is intentionally applied: every algorithm is derived from first principles (not just stated), and every derivation connects to a specific step in the macroeconomic workflow. The reader who works through these five chapters will understand not just how to call a numerical solver but what it is doing inside — and when it will fail.
Chapter 22: Solving Nonlinear Equations¶
Newton–Raphson for Steady-State Calculations
“Newton’s method is the most important algorithm in numerical analysis. Everything else is a variation on it.”
Cross-reference: Principles Ch. 5.2 (Solow steady state as a root); Ch. 5.3 (RCK modified Golden Rule); Ch. 23 (Taylor rule fixed point) [P:Ch.5.2, P:Ch.5.3, P:Ch.23]
22.1 The Steady State as a Root-Finding Problem¶
Every dynamic model has a steady state — a fixed point of the system’s law of motion where all variables remain constant over time. Finding the steady state is the first step in any DSGE analysis: the log-linearization (Chapter 27) is performed around the steady state, and the Blanchard–Kahn solution (Chapter 28) requires knowing it precisely.
In simple models, steady states have closed-form solutions. The Solow model gives ; the log-utility Diamond OLG gives a direct algebraic expression. But in medium-scale DSGE models with multiple nonlinear equilibrium conditions — price-setting optimization, labor market search-and-matching, financial frictions — no closed form exists. We must solve a system of nonlinear equations:
The methods of this chapter — bisection, Newton–Raphson, quasi-Newton — address this problem with different trade-offs between reliability and speed.
22.2 The Bisection Method¶
Algorithm 22.1 (Bisection).
Input: Continuous function ; bracket with ; tolerance .
Set , .
For :
(midpoint).
If or : return .
If : set , .
Else: set , .
Theorem 22.1 (Bisection Convergence). The bisection method converges to a root with:
The number of iterations to achieve tolerance is .
Proof. At each step the bracket halves in length: . The root lies in the bracket, so .
Convergence rate: The error halves at each step — linear convergence with rate . To gain one decimal digit of accuracy requires about iterations. For 10-digit accuracy from a bracket of width 1: iterations. Bisection is slow but guaranteed to converge whenever a bracket exists.
When to use bisection: When the function is continuous but not differentiable; when a bracket is available but convergence of faster methods is uncertain; as a fallback when Newton–Raphson fails to converge.
22.3 Newton–Raphson for Scalar Equations¶
Newton–Raphson is the workhorse of root-finding. It exploits derivative information to converge far faster than bisection.
Algorithm 22.2 (Newton–Raphson, Scalar).
Input: Differentiable ; initial guess ; tolerance .
For :
Compute .
If : return .
Derivation from Taylor expansion. Expand around :
The update is the -intercept of the tangent line to at . Geometrically, Newton–Raphson replaces the nonlinear with its linear approximation and solves that.
Theorem 22.2 (Quadratic Convergence of Newton–Raphson). Suppose is twice continuously differentiable, , , and the initial guess is sufficiently close to . Then:
The error squares at each step — quadratic convergence. If , then after one step ; after two steps ; after three steps . Three iterations from 1-digit accuracy gives 8-digit accuracy.
Proof. Let . Taylor-expand around :
for some between and . The Newton step:
Since and :
Taking absolute values: with .
In APL, Newton–Raphson via the ⍣≡ fixed-point operator:
⎕IO←0 ⋄ ⎕ML←1
⍝ 1. Parameters
alpha ← 1÷3 ⋄ s ← 0.25 ⋄ mu ← 0.08
⍝ 2. Solow Model Functions
⍝ Note: Added parentheses around (alpha-1) to ensure correct order
f ← {(s × ⍵*alpha) - mu × ⍵} ⍝ Goal: f(k) = 0
df ← {(s × alpha × ⍵*alpha-1) - mu} ⍝ f'(k)
⍝ 3. Newton-Raphson Step: x → x - f(x)/f'(x)
nr_step ← {⍵ - (f ⍵) ÷ df ⍵}
⍝ 4. Iterate and then take the Real Part (9○) to clean up floating-point noise
kstar ← 9○ nr_step ⍣ ≡ 1
⍝ 5. Print Results
'Steady State Capital (k*):' ⋄ kstar
'Residual check (should be 0):' ⋄ f kstar22.4 Newton–Raphson for Systems¶
For a system with , Newton–Raphson generalizes by replacing the scalar derivative with the Jacobian matrix.
Definition 22.1 (Jacobian). The Jacobian of at is the matrix:
Algorithm 22.3 (Newton–Raphson, System).
Input: ; initial guess ; tolerance .
For :
Compute .
Solve the linear system for .
Set .
If or : return .
The key step is solving — a linear system in . This is the step that uses LU decomposition (Chapter 25). In APL, it is delta_x ← ((-F_x) ⌹ J_x).
Theorem 22.3 (Quadratic Convergence — System Newton). If is twice continuously differentiable in a neighborhood of , is invertible, and is sufficiently close to , then:
with depending on the second derivatives of and .
Sufficient condition for convergence (Kantorovich theorem): Let where is the Lipschitz constant of . If , Newton–Raphson converges from .
22.5 Quasi-Newton Methods: Broyden’s Method¶
Newton–Raphson requires computing the Jacobian at each iteration — expensive when is costly to evaluate or when has no closed form. Broyden’s method maintains an approximate Jacobian and updates it cheaply using only function evaluations.
Algorithm 22.4 (Broyden’s Method).
Input: ; initial guess ; initial Jacobian approximation (often or a finite-difference Jacobian).
For :
Solve ; update .
Compute .
Broyden update: .
The Broyden update is the rank-1 correction that enforces the secant condition , while changing as little as possible (in Frobenius norm). This generalizes the scalar secant method to systems.
Convergence: Broyden’s method achieves superlinear convergence — faster than linear but slower than quadratic. It requires only one function evaluation per iteration (vs. for finite-difference Jacobian Newton), making it much faster per iteration for large systems.
22.6 Worked Example: Steady State of a Medium-Scale NK Model¶
Cross-reference: Principles Ch. 23 (NK steady state) [P:Ch.23]
A medium-scale NK model has 7 steady-state conditions in 7 unknowns :
plus optimality conditions from household and price-setting behavior. Newton–Raphson solves this in 3–5 iterations from a reasonable starting point.
Python¶
import numpy as np
from scipy.optimize import fsolve
# Medium-scale NK steady state
alpha, delta, beta, theta_p = 0.36, 0.025, 0.99, 0.75
pi_bar, phi_pi, phi_y = 1.005, 1.5, 0.5 # gross inflation target
def nk_steady_state(x):
C, K, Y, w, r, pi, mc = x
n = 1/3 # fixed labor (for simplicity)
F = np.zeros(7)
F[0] = Y - K**alpha * n**(1-alpha) # production
F[1] = 1 - beta*(1+r) # Euler equation (r* = 1/β - 1)
F[2] = r - (alpha*Y/K - delta) # MPK = r + δ
F[3] = w - (1-alpha)*Y/n # MPL = w
F[4] = mc - w/((1-alpha)*Y/n) # MC = w/MPL
F[5] = pi - pi_bar # inflation target
F[6] = C - (Y - delta*K) # goods market clearing
return F
# Initial guess from approximate values
r_star = 1/beta - 1
K_star_approx = (alpha/(r_star+delta))**(1/(1-alpha)) * (1/3)**(1-alpha/(1-alpha))
x0 = [0.7, K_star_approx, 1.0, 2.0, r_star, pi_bar, 1.0]
x_star = fsolve(nk_steady_state, x0, full_output=False)
C_star, K_star, Y_star, w_star, r_star_sol, pi_star, mc_star = x_star
print(f"Steady state:")
print(f" Y* = {Y_star:.4f}, K* = {K_star:.4f}, C* = {C_star:.4f}")
print(f" r* = {r_star_sol*100:.2f}% (quarterly), mc* = {mc_star:.4f}")
print(f" Residuals: {np.max(np.abs(nk_steady_state(x_star))):.2e}")
# Newton-Raphson from scratch (educational)
def newton_system(F, x0, tol=1e-12, max_iter=50):
x = np.array(x0, dtype=float)
for i in range(max_iter):
Fx = F(x)
if np.max(np.abs(Fx)) < tol: return x, i
# Finite-difference Jacobian
h = 1e-7
J = np.zeros((len(x), len(x)))
for j in range(len(x)):
xp = x.copy(); xp[j] += h
J[:,j] = (np.array(F(xp)) - Fx) / h
dx = np.linalg.solve(J, -Fx)
x = x + dx
return x, max_iter
x_nr, iters = newton_system(nk_steady_state, x0)
print(f"\nNewton-Raphson converged in {iters} iterations")
print(f"Max residual: {np.max(np.abs(nk_steady_state(x_nr))):.2e}")Julia¶
using NLsolve, LinearAlgebra
alpha, delta, beta = 0.36, 0.025, 0.99
pi_bar = 1.005; n = 1/3
function nk_ss!(F, x)
C, K, Y, w, r, pi_, mc = x
F[1] = Y - K^alpha * n^(1-alpha)
F[2] = 1 - beta*(1+r)
F[3] = r - (alpha*Y/K - delta)
F[4] = w - (1-alpha)*Y/n
F[5] = mc - w/((1-alpha)*Y/n)
F[6] = pi_ - pi_bar
F[7] = C - (Y - delta*K)
end
r_app = 1/beta - 1
K_app = (alpha/(r_app+delta))^(1/(1-alpha))*(n)^((1-alpha)/(1-alpha))
x0 = [0.7, K_app, 1.0, 2.0, r_app, pi_bar, 1.0]
sol = nlsolve(nk_ss!, x0, method=:newton, ftol=1e-12)
println("Converged: $(sol.f_converged), iterations: $(sol.iterations)")
println("Y*=$(round(sol.zero[3],digits=4)), K*=$(round(sol.zero[2],digits=4))")library(nleqslv)
alpha<-0.36; delta<-0.025; beta<-0.99; n<-1/3; pi_bar<-1.005
nk_ss <- function(x) {
C<-x[1]; K<-x[2]; Y<-x[3]; w<-x[4]; r<-x[5]; pi_<-x[6]; mc<-x[7]
c(Y - K^alpha*n^(1-alpha), 1 - beta*(1+r), r-(alpha*Y/K-delta),
w-(1-alpha)*Y/n, mc-w/((1-alpha)*Y/n), pi_-pi_bar, C-(Y-delta*K))
}
r_app<-1/beta-1; K_app<-(alpha/(r_app+delta))^(1/(1-alpha))
x0<-c(0.7, K_app, 1.0, 2.0, r_app, pi_bar, 1.0)
sol<-nleqslv(x0, nk_ss, method="Newton")
cat(sprintf("Y*=%.4f, K*=%.4f, residual=%.2e\n",sol$x[3],sol$x[2],max(abs(sol$fvec))))22.7 Programming Exercises¶
Exercise 22.1 (APL — Broyden’s Method)¶
Implement Broyden’s method in APL as a dfn broyden ← {F B0 x0 ← ⍵ ⋄ ...}. The update step uses the rank-1 formula: B ← B + ((dF - B+.×dx) ∘.× dx) ÷ dx+.×dx. Test on the 2-equation Solow/RCK system and compare iteration counts to Newton–Raphson. Show that Broyden requires fewer function evaluations per solution.
Exercise 22.2 (Python — Convergence Rate Measurement)¶
For Newton–Raphson applied to starting from : (a) run 10 iterations recording ; (b) compute the convergence ratio ; (c) verify this ratio converges to ; (d) plot vs. and verify the slope approximately doubles each step on the log scale.
Exercise 22.3 (Julia — Ill-Conditioning)¶
# Effect of ill-conditioning on Newton convergence
using LinearAlgebra
function ill_conditioned_system(x, kappa=1000.0)
# Near-singular system: condition number ≈ kappa
A = [1.0 1.0; 1.0 1.0+1/kappa]
b = [2.0, 2.0+1/kappa]
A*x - b # F(x) = Ax - b, solution x* = [1, 1]
end
for kappa in [1, 10, 100, 1000, 1e6]
F!(f,x) = (f .= ill_conditioned_system(x, kappa))
sol = nlsolve(F!, [0.5, 0.5], method=:newton)
cond_J = cond([1.0 1.0; 1.0 1.0+1/kappa])
println("κ=$(kappa): cond(J)=$(round(cond_J,digits=1)), " *
"iters=$(sol.iterations), max_err=$(round(maximum(abs.(sol.zero .- 1)),digits=8))")
endExercise 22.4 — RCK Steady State ()¶
The RCK model steady state satisfies: (1) ; (2) . With these have analytical solutions. Now use (CES). (a) Write the two conditions as a system . (b) Apply Newton–Raphson with finite-difference Jacobian. (c) Compare the CES steady state to the Cobb–Douglas approximation for .
22.8 Chapter Summary¶
Key results:
Bisection: guaranteed convergence at linear rate ; needs iterations; requires a bracket with .
Newton–Raphson (scalar): ; quadratic convergence proved in Theorem 22.2; error satisfies near .
Newton–Raphson (system): solve at each step via LU; quadratic convergence (Theorem 22.3); Kantorovich theorem gives sufficient conditions.
Broyden’s method: rank-1 Jacobian update ; superlinear convergence; one function evaluation per iteration.
In APL: Newton–Raphson is
{⍵ - (f ⍵) ÷ df ⍵}⍣≡; the system version is{x - ((-F_x) ⌹ J_x)}⍣≡.
Next: Chapter 23 — Numerical Integration and Differentiation