Initial Guess#
The perfect foresight solver requires an initial guess X0 — a (T, n) array whose rows are the starting estimates for each period’s endogenous variables. A good initial guess reduces Newton iterations and avoids convergence failure, especially for large shocks.
make_initial_guess#
- pyperfectforesight.make_initial_guess(T, ss_initial, ss_terminal, method='linear', decay=0.9)[source]
Generate an initial guess path for the perfect foresight solver.
Replicates the spirit of Dynare’s
perfect_foresight_setuppath initialisation and adds an exponential option that better matches the saddle-path dynamics typical of DSGE models.- Parameters:
T (int) – Number of periods (rows of the returned array).
ss_initial (array-like, shape (n,)) – Starting values — typically the initial steady state or the period-0 values you expect the path to begin near.
ss_terminal (array-like, shape (n,)) – Terminal values — typically the terminal steady state
ss.method ({'linear', 'exponential', 'constant'}, default 'linear') –
How to interpolate between
ss_initialandss_terminal:'linear'— evenly spaced fromss_initial(t=0) toss_terminal(t=T-1). Matches Dynare’s defaultperfect_foresight_setupbehaviour when bothinitvalandendvalare supplied.'exponential'— geometric convergencex(t) = ss_terminal + (ss_initial - ss_terminal) * decay**t. The path closes most of the gap in early periods and flattens nearss_terminal, mimicking saddle-path dynamics. The gap never reaches exactly zero; usedecayto control the speed.'constant'—ss_terminalrepeated for all periods. Equivalent tonp.tile(ss_terminal, (T, 1)).
decay (float, default 0.9) – Decay rate for
method='exponential'. Must be in(0, 1). Smaller values converge faster (e.g.0.5closes half the gap each period); values near 1 converge slowly and approach linear. Ignored for other methods.
- Returns:
X0 – Initial guess array, ready to pass as
X0tosolve_perfect_foresightorsolve_perfect_foresight_expectation_errors.- Return type:
ndarray, shape (T, n)
Examples
Linear interpolation (Dynare default):
>>> X0 = make_initial_guess(T, ss_initial=ss, ss_terminal=ss_new)
Exponential interpolation with faster convergence:
>>> X0 = make_initial_guess(T, ss_initial=ss, ss_terminal=ss_new, ... method='exponential', decay=0.85)
Methods#
make_initial_guess supports three interpolation methods between ss_initial (the starting point) and ss_terminal (the terminal steady state):
linear(default)Linearly interpolates from
ss_initialat \(t=0\) toss_terminalat \(t=T-1\). This matches Dynare’s defaultperfect_foresight_setupbehaviour when bothinitvalandendvalare supplied.exponentialGeometric convergence: \(x(t) = \texttt{ss\_terminal} + (\texttt{ss\_initial} - \texttt{ss\_terminal}) \cdot \texttt{decay}^t\). The path closes most of the gap early and flattens near
ss_terminal, mimicking the saddle-path dynamics typical of DSGE models. Thedecayparameter (default0.9) controls the convergence speed — smaller values (e.g.0.5) close the gap faster.constantReturns
ss_terminalrepeated for allTperiods. Equivalent to the common idiomnp.tile(ss, (T, 1)).
Usage examples#
Replacing np.tile#
The simplest starting point is a constant path at the terminal steady state. make_initial_guess with method='constant' is a drop-in replacement:
import numpy as np
from pyperfectforesight import make_initial_guess
# Old idiom
X0 = np.tile(ss, (T, 1))
# Equivalent with make_initial_guess
X0 = make_initial_guess(T, ss_initial=ss, ss_terminal=ss, method='constant')
Linear interpolation (transition between two steady states)#
When you know the economy starts at ss_old and ends at ss_new, a linear interpolation is a natural warm start:
X0 = make_initial_guess(T, ss_initial=ss_old, ss_terminal=ss_new)
# method='linear' is the default
Exponential interpolation for saddle-path models#
For models with saddle-path dynamics, the exponential method often gives a better warm start because the true solution also closes most of the gap early:
X0 = make_initial_guess(
T,
ss_initial=ss,
ss_terminal=ss,
method='exponential',
decay=0.85, # faster convergence than default 0.9
)
Combining with solve_perfect_foresight#
import numpy as np
from pyperfectforesight import (
v, process_model, solve_perfect_foresight, make_initial_guess,
)
ALPHA, BETA = 0.36, 0.99
eq_euler = v("c", 0)**(-1) - BETA * ALPHA * v("k", 0)**(ALPHA-1) * v("c", 1)**(-1)
eq_kacc = v("k", 0) - v("k", -1)**ALPHA + v("c", 0)
vars_dyn = ["c", "k"]
model_funcs = process_model([eq_euler, eq_kacc], vars_dyn)
K_SS = (ALPHA * BETA) ** (1 / (1 - ALPHA))
C_SS = K_SS**ALPHA - K_SS
ss = np.array([C_SS, K_SS])
T = 100
k_neg1 = np.array([K_SS * 1.1])
# Build initial guess: exponential path from perturbed SS back to SS
ss_perturbed = np.array([C_SS, K_SS * 1.1]) # approximate period-0 values
X0 = make_initial_guess(T, ss_initial=ss_perturbed, ss_terminal=ss,
method='exponential', decay=0.9)
sol = solve_perfect_foresight(
T, {}, ss, model_funcs, vars_dyn, X0,
initial_state=k_neg1,
stock_var_indices=[1],
)
print(f"Converged: {sol.success}")
The decay parameter#
The decay parameter only affects method='exponential'. It must be in \((0, 1)\):
|
Convergence speed |
Notes |
|---|---|---|
|
Fast — half the gap closed each period |
Good for very persistent models |
|
Medium-fast |
Reasonable default for most DSGE models |
|
Medium (default) |
Matches typical AR(1) persistence |
|
Slow — nearly linear |
Use |
Values outside \((0, 1)\) raise ValueError.