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_setup path 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_initial and ss_terminal:

    • 'linear' — evenly spaced from ss_initial (t=0) to ss_terminal (t=T-1). Matches Dynare’s default perfect_foresight_setup behaviour when both initval and endval are supplied.

    • 'exponential' — geometric convergence x(t) = ss_terminal + (ss_initial - ss_terminal) * decay**t. The path closes most of the gap in early periods and flattens near ss_terminal, mimicking saddle-path dynamics. The gap never reaches exactly zero; use decay to control the speed.

    • 'constant'ss_terminal repeated for all periods. Equivalent to np.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.5 closes 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 X0 to solve_perfect_foresight or solve_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_initial at \(t=0\) to ss_terminal at \(t=T-1\). This matches Dynare’s default perfect_foresight_setup behaviour when both initval and endval are supplied.

exponential

Geometric 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. The decay parameter (default 0.9) controls the convergence speed — smaller values (e.g. 0.5) close the gap faster.

constant

Returns ss_terminal repeated for all T periods. Equivalent to the common idiom np.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)\):

decay

Convergence speed

Notes

0.5

Fast — half the gap closed each period

Good for very persistent models

0.85

Medium-fast

Reasonable default for most DSGE models

0.9

Medium (default)

Matches typical AR(1) persistence

0.99

Slow — nearly linear

Use method='linear' instead

Values outside \((0, 1)\) raise ValueError.