Various improvements to perfect foresight homotopy

– new option “endval_steady” to pf_setup command to recompute terminal
  steady state in the homotopy loop

– new options “homotopy_linearization_fallback” and
  “homotopy_marginal_linearization_fallback” to pf_solver and pfwee_solver
  commands, to get an approximate solution when homotopy fails to go to 100%

– new options “homotopy_initial_step_size”, “homotopy_min_step_size”,
  “homotopy_step_size_increase_success_count” and “homotopy_max_completion_share”
  to pf_solver and pfwee_solver commands to fine tune the homotopy behaviour

– removed option “homotopy_alt_starting_point” to pf_solver command, not really
  useful

– new options “steady_solve_algo”, “steady_tolf”, “steady_tolx”,
  “steady_maxit”, “steady_markowitz” to pf_solver and pfwee_solver commands, to
  control the computation of the terminal steady state (and remove the
  equivalent options which previously had different names in pfwee_solver command)
remove-submodule
Sébastien Villemot 2023-06-15 16:59:37 +02:00
parent 3a789ca780
commit d5a3a8e16a
No known key found for this signature in database
GPG Key ID: 2CECE9350ECEBE4A
19 changed files with 633 additions and 282 deletions

View File

@ -3701,21 +3701,69 @@ speed-up on large models.
This option tells Dynare to not try a homotopy technique (as described
above) if the problem cannot be solved directly.
.. option:: homotopy_alt_starting_point
.. option:: homotopy_initial_step_size = DOUBLE
When the homotopy technique is tried (as described above), Dynare first
tries to reduce the size of the shock in order to get a successful
simulation on which to build upon for simulating a shock of the true
size. However, if an ``endval`` block is present (*i.e.* if the terminal
state differs from the initial state), there are two ways of reducing
the size of the shock. By default, Dynare will perform this reduction by
computing a simulation whose initial state is closer to the target
terminal state; in other words, it will implicitly modify the contents
of the ``initval`` and ``shocks`` blocks to make them closer to the the
contents of the ``endval`` block. If this option is set, Dynare will do
the opposite: it will implicitly modify the contents of the ``endval``
and ``shocks`` blocks to make them closer to the contents of the
``initval`` block.
Specifies which share of the shock should be applied in the first
iteration of the homotopy procedure. This option is useful when it is
known that immediately trying 100% of the shock will fail, so as to save
computing time. Must be between ``0`` and ``1``. Default: ``1``.
.. option:: homotopy_min_step_size = DOUBLE
The homotopy procedure halves the size of the step whenever there is
a failure. This option specifies the minimum step size under which the
homotopy procedure is considered to have failed. Default: ``0.001``.
.. option:: homotopy_step_size_increase_success_count = INTEGER
Specifies after how many consecutive successful iterations the homotopy
procedure should double the size of the step. A zero value means that
the step size should never be increased. Default: ``3``.
.. option:: homotopy_linearization_fallback
Whenever the homotopy procedure is not able to find a solution for 100%
of the shock, but is able to find one for a smaller share, instructs
Dynare to compute an approximate solution by rescaling the solution
obtained for a fraction of the shock, as if the reaction of the model to
the shock was a linear function of the size of that shock. More
formally, if :math:`s` is the share of the shock applied (between
:math:`0` and :math:`1`), :math:`y(s)` is the value of a given
endogenous variable at a given period as a function of :math:`s` (in
particular, :math:`y(1)` corresponds to the exact solution of the
problem), and :math:`s^*` is the greatest share of the shock for which
the homotopy procedure has been able to find a solution, then the approximate
solution returned is :math:`\frac{y(s^*)-y(0)}{s^*}`.
.. option:: homotopy_marginal_linearization_fallback [= DOUBLE]
Whenever the homotopy procedure is not able to find a solution for 100%
of the shock, but is able to find one for a smaller share, instructs
Dynare to compute an approximate solution obtained by rescaling the
solution obtained for a fraction of the shock, obtained as if the
reaction of the model to the shock was, at the margin, a linear function
of the size of that shock. More formally, if :math:`s` is the share of
the shock applied (between :math:`0` and :math:`1`), :math:`y(s)` is the
value of a given endogenous variable at a given period as a function of
:math:`s` (in particular, :math:`y(1)` corresponds to the exact solution
of the problem), :math:`s^*` is the greatest share of the shock for
which the homotopy procedure has been able to find a solution, and
:math:`\epsilon` is a small step size, then the approximate solution
returned is
:math:`y(s^*)+(1-s^*)\frac{y(s^*)-y(s^*-\epsilon)}{\epsilon}`. The value
of :math:`\epsilon` is ``0.01`` by default, but can be modified by
passing some other value to the option.
.. option:: homotopy_max_completion_share = DOUBLE
Instructs Dynare, within the homotopy procedure, to not try to compute
the solution for a greater share than the one given as the option value.
This option only makes sense when used in conjunction with either the
``homotopy_linearization_fallback`` or the
``homotopy_marginal_linearization_fallback`` option. It is typically
used in situations where it is known that homotopy will fail to go
beyond a certain point, so as to save computing time, while at the same
time getting an approximate solution.
.. option:: markowitz = DOUBLE
@ -3827,6 +3875,50 @@ speed-up on large models.
``endval`` or a subsequent ``steady``. Only available with option
``stack_solve_algo==0`` or ``stack_solve_algo==7``.
.. option:: endval_steady
In scenarios with a permanent shock, specifies that the terminal
condition is a steady state, even if the ``steady`` command has not been
called after the ``endval`` block. As a consequence, the
``perfect_foresight_solver`` command will compute the terminal steady
state itself (given the value of the exogenous variables given in the
``endval`` block). In practice, this option is useful when the permanent
shock is very large, in which case the homotopy procedure inside
``perfect_foresight_solver`` will find both the terminal steady state
and the transitional dynamics within the same loop (which is less costly
than first computing the terminal steady state by homotopy, then
computing the transitional dynamics by homotopy).
.. option:: steady_solve_algo = INTEGER
See :ref:`solve_algo <solvalg>`. Used when computing the terminal steady
state when option ``endval_steady`` has been specified to the
``perfect_foresight_setup`` command.
.. option:: steady_tolf = DOUBLE
See :ref:`tolf <steady_tolf>`. Used when computing the terminal steady
state when option ``endval_steady`` has been specified to the
``perfect_foresight_setup`` command.
.. option:: steady_tolx = DOUBLE
See :ref:`tolx <steady_tolx>`. Used when computing the terminal steady
state when option ``endval_steady`` has been specified to the
``perfect_foresight_setup`` command.
.. option:: steady_maxit = INTEGER
See :ref:`maxit <steady_maxit>`. Used when computing the terminal steady
state when option ``endval_steady`` has been specified to the
``perfect_foresight_setup`` command.
.. option:: steady_markowitz = DOUBLE
See :ref:`markowitz <steady_markowitz>`. Used when computing the
terminal steady state when option ``endval_steady`` has been specified
to the ``perfect_foresight_setup`` command.
*Output*
The simulated endogenous variables are available in global matrix
@ -4019,7 +4111,9 @@ and ``endval`` blocks which are given a special ``learnt_in`` option.
always a steady state. Hence, it will recompute the terminal steady state
as many times as the anticipation about the terminal condition changes. In
particular, the information about endogenous variables that may be given in
the ``endval`` block is ignored.
the ``endval`` block is ignored. Said otherwise, the equivalent of option
``endval_steady`` of the ``perfect_foresight_setup`` command is always
implicitly enabled.
*Options*
@ -4057,33 +4151,12 @@ and ``endval`` blocks which are given a special ``learnt_in`` option.
the name of the variable on the first line and ``s`` on the second
line. Of course, values in cells corresponding to ``t<s`` are ignored.
.. option:: solve_algo = INTEGER
See :ref:`solve_algo <solvalg>`. Used when computing the terminal steady state.
.. option:: tolf = DOUBLE
See :ref:`tolf <steady_tolf>`. Used when computing the terminal steady state.
.. option:: tolx = DOUBLE
See :ref:`tolx <steady_tolx>`. Used when computing the terminal steady state.
.. option:: maxit = INTEGER
See :ref:`maxit <steady_maxit>`. Used when computing the terminal steady state.
.. option:: markowitz = DOUBLE
See :ref:`markowitz <steady_markowitz>`. Used when computing the terminal steady state.
*Output*
``oo_.exo_simul`` and ``oo_.endo_simul`` are initialized before the
simulation. Temporary shocks are stored in ``oo_.pfwee.shocks_info``,
terminal conditions for exogenous variables are stored in
``oo_.pfwee.terminal_info``, and terminal steady states are stored in
``oo_.pfwee.terminal_steady_state``.
``oo_.pfwee.terminal_info``.
*Example*
@ -4189,7 +4262,9 @@ and ``endval`` blocks which are given a special ``learnt_in`` option.
*Output*
The simulated paths of endogenous variables are available in
``oo_.endo_simul``.
``oo_.endo_simul``. The terminal steady state values corresponding to the
last period of the information set are available in ``oo_.steady_state``
and ``oo_.exo_steady_state``.
.. matvar:: oo_.pfwee.shocks_info
@ -4212,17 +4287,6 @@ and ``endval`` blocks which are given a special ``learnt_in`` option.
the terminal condition for exogenous indexed ``k``, as anticipated from
period ``s``, is stored in ``oo_.pfwee.terminal_info(k,s)``.
.. matvar:: oo_.pfwee.terminal_steady_state
|br| This variable stores the terminal steady states for endogenous
variables used during perfect foresight simulations with expectation
errors, after :comm:`perfect_foresight_with_expectation_errors_setup` has
been run. It is a matrix, whose lines correspond to endogenous variables
(in declaration order), and whose columns correspond to informational time.
In other words, the terminal steady state for endogenous indexed ``k``, as
anticipated from period ``s``, is stored in
``oo_.pfwee.terminal_steady_state(k,s)``.
.. _stoch-sol:
Stochastic solution and simulation

View File

@ -49,6 +49,7 @@ options_.schur_vec_tol = 1e-11; % used to find nonstationary variables in Schur
options_.qz_criterium = [];
options_.qz_zero_threshold = 1e-6;
options_.lyapunov_complex_threshold = 1e-15;
options_.solve_algo = 4;
options_.solve_tolf = eps^(1/3);
options_.solve_tolx = eps^(2/3);
options_.trust_region_initial_step_bound_factor = 1;
@ -326,7 +327,22 @@ options_.markowitz = 0.5;
options_.minimal_solving_periods = 1;
options_.endogenous_terminal_period = false;
options_.no_homotopy = false;
options_.homotopy_alt_starting_point = false;
options_.simul.endval_steady = false;
options_.simul.homotopy_max_completion_share = 1;
options_.simul.homotopy_min_step_size = 1e-3;
options_.simul.homotopy_step_size_increase_success_count = 3;
options_.simul.homotopy_initial_step_size = 1;
options_.simul.homotopy_linearization_fallback = false;
options_.simul.homotopy_marginal_linearization_fallback = 0; % Size of the step used for the marginal linearization; 0 means disabled
% Options used by perfect_foresight_* commands when they compute the steady
% state corresponding to a terminal condition
options_.simul.steady_solve_algo = options_.solve_algo;
options_.simul.steady_maxit = options_.steady.maxit;
options_.simul.steady_tolf = options_.solve_tolf;
options_.simul.steady_tolx = options_.solve_tolx;
options_.simul.steady_markowitz = options_.markowitz;
% Perfect foresight with expectation errors
options_.pfwee.constant_simulation_length = false;
@ -334,7 +350,6 @@ options_.pfwee.constant_simulation_length = false;
% Solution
options_.order = 2;
options_.pruning = false;
options_.solve_algo = 4;
options_.replic = 50;
options_.simul_replic = 1;
options_.drop = 100;

View File

@ -394,7 +394,9 @@ elseif options.bytecode
ys = bytecode('static', ys_init, exo_ss, params);
end
catch ME
disp(ME.message);
if options.verbosity >= 1
disp(ME.message);
end
ys = ys_init;
check = 1;
end
@ -420,7 +422,9 @@ elseif options.bytecode
[~, ~, ys, T] = bytecode(ys, exo_ss, params, ys, 1, ys, T, 'evaluate', 'static', ...
'block_decomposed', ['block = ' int2str(b)]);
catch ME
disp(ME.message);
if options.verbosity >= 1
disp(ME.message);
end
check = 1;
break
end

View File

@ -12,7 +12,7 @@ function perfect_foresight_setup()
% SPECIAL REQUIREMENTS
% none
% Copyright © 1996-2021 Dynare Team
% Copyright © 1996-2023 Dynare Team
%
% This file is part of Dynare.
%
@ -64,5 +64,9 @@ if ~isempty(M_.det_shocks) && options_.periods<max([M_.det_shocks.periods])
error('PERFECT_FORESIGHT_SETUP: Please check the declaration of the shocks or increase the value of the periods option.')
end
if options_.simul.endval_steady && M_.maximum_lead == 0
error('PERFECT_FORESIGHT_SETUP: Option endval_steady cannot be used on a purely backward or static model.')
end
oo_ = make_ex_(M_,options_,oo_);
oo_ = make_y_(M_,options_,oo_);
oo_ = make_y_(M_,options_,oo_);

View File

@ -51,162 +51,318 @@ if options_.debug
end
end
% Various sanity checks
if options_.no_homotopy && (options_.simul.homotopy_initial_step_size ~= 1 ...
|| options_.simul.homotopy_max_completion_share ~= 1 ...
|| options_.simul.homotopy_linearization_fallback ...
|| options_.simul.homotopy_marginal_linearization_fallback ~= 0)
error('perfect_foresight_solver: the no_homotopy option is incompatible with homotopy_initial_step_size, homotopy_max_completion_share, homotopy_linearization_fallback and homotopy_marginal_linearization_fallback options')
end
if options_.simul.homotopy_initial_step_size > 1 || options_.simul.homotopy_initial_step_size < 0
error('perfect_foresight_solver: The value given to homotopy_initial_step_size option must be in the [0,1] interval')
end
if options_.simul.homotopy_min_step_size > 1 || options_.simul.homotopy_min_step_size < 0
error('perfect_foresight_solver: The value given to homotopy_min_step_size option must be in the [0,1] interval')
end
if options_.simul.homotopy_max_completion_share > 1 || options_.simul.homotopy_max_completion_share < 0
error('perfect_foresight_solver: The value given to homotopy_max_completion_share option must be in the [0,1] interval')
end
if options_.simul.homotopy_marginal_linearization_fallback > 1 || options_.simul.homotopy_marginal_linearization_fallback < 0
error('perfect_foresight_solver: The value given to homotopy_marginal_linearization_fallback option must be in the [0,1] interval')
end
if options_.simul.homotopy_initial_step_size < options_.simul.homotopy_min_step_size
error('perfect_foresight_solver: The value given to homotopy_initial_step_size option must be greater or equal to that given to homotopy_min_step_size option')
end
if options_.simul.homotopy_linearization_fallback && options_.simul.homotopy_marginal_linearization_fallback > 0
error('perfect_foresight_solver: Options homotopy_linearization_fallback and homotopy_marginal_linearization_fallback cannot be used together')
end
if options_.simul.homotopy_max_completion_share < 1 && ~options_.simul.homotopy_linearization_fallback && options_.simul.homotopy_marginal_linearization_fallback == 0
error('perfect_foresight_solver: Option homotopy_max_completion_share has a value less than 1, so you must also specify either homotopy_linearization_fallback or homotopy_marginal_linearization_fallback')
end
initperiods = 1:M_.maximum_lag;
lastperiods = (M_.maximum_lag+periods+1):(M_.maximum_lag+periods+M_.maximum_lead);
simperiods = M_.maximum_lag+(1:periods);
lastperiods = M_.maximum_lag+periods+(1:M_.maximum_lead);
[oo_.endo_simul, success, maxerror, solver_iter, per_block_status] = perfect_foresight_solver_core(M_,options_,oo_);
% Create base scenario for homotopy, which corresponds to the initial steady
% state (i.e. a known solution to the perfect foresight problem, assuming that
% oo_.steady_state/ys0_ effectively contains a steady state)
if isempty(ys0_)
endobase = repmat(oo_.steady_state, 1,M_.maximum_lag+periods+M_.maximum_lead);
exobase = repmat(oo_.exo_steady_state',M_.maximum_lag+periods+M_.maximum_lead,1);
else
endobase = repmat(ys0_, 1, M_.maximum_lag+periods+M_.maximum_lead);
exobase = repmat(ex0_', M_.maximum_lag+periods+M_.maximum_lead, 1);
end
% If simulation failed try homotopy.
if ~success && ~options_.no_homotopy
if ~options_.noprint
fprintf('\nSimulation of the perfect foresight model failed!\n')
fprintf('Switching to a homotopy method...\n')
% Determine whether the terminal condition is not a steady state (typically
% because steady was not called after endval)
if ~options_.simul.endval_steady && ~isempty(ys0_)
terminal_condition_is_a_steady_state = true;
for j = lastperiods
endval_resid = evaluate_static_model(oo_.endo_simul(:,j), oo_.exo_simul(j,:), M_.params, M_, options_);
if norm(endval_resid, 'Inf') > options_.simul.steady_tolf
terminal_condition_is_a_steady_state = false;
break
end
end
end
% Disable warnings if homotopy
warning_old_state = warning;
warning off all
% Do not print anything
oldverbositylevel = options_.verbosity;
options_.verbosity = 0;
% Copy the paths for the exogenous and endogenous variables, as given by perfect_foresight_setup
exoorig = oo_.exo_simul;
endoorig = oo_.endo_simul;
% Set initial paths for the endogenous and exogenous variables.
if ~options_.homotopy_alt_starting_point
endoinit = repmat(oo_.steady_state, 1,M_.maximum_lag+periods+M_.maximum_lead);
exoinit = repmat(oo_.exo_steady_state',M_.maximum_lag+periods+M_.maximum_lead,1);
current_share = 0; % Share of shock successfully completed so far
step = min(options_.simul.homotopy_initial_step_size, options_.simul.homotopy_max_completion_share);
success_counter = 0;
iteration = 0;
function local_success = create_scenario(share)
% For a given share, updates the exogenous path and also the initial and
% terminal conditions for the endogenous path (but do not modify the initial
% guess for endogenous)
% Compute convex combination for the path of exogenous
oo_.exo_simul = exoorig*share + exobase*(1-share);
% Compute convex combination for the initial condition
% In most cases, the initial condition is a steady state and this does nothing
% This is for cases when the initial condition is out of equilibrium
oo_.endo_simul(:, initperiods) = share*endoorig(:, initperiods)+(1-share)*endobase(:, initperiods);
% If there is a permanent shock, ensure that the rescaled terminal condition is
% a steady state (if the user asked for this recomputation, or if the original
% terminal condition is a steady state)
local_success = true;
if options_.simul.endval_steady || (~isempty(ys0_) && terminal_condition_is_a_steady_state)
% Set “local” options for steady state computation (after saving the global values)
saved_steady_solve_algo = options_.solve_algo;
options_.solve_algo = options_.simul.steady_solve_algo;
saved_steady_maxit = options_.steady.maxit;
options_.steady.maxit = options_.simul.steady_maxit;
saved_steady_tolf = options_.solve_tolf;
options_.solve_tolf = options_.simul.steady_tolf;
saved_steady_tolx = options_.solve_tolx;
options_.solve_tolx = options_.simul.steady_tolx;
saved_steady_markowitz = options_.markowitz;
options_.markowitz = options_.simul.steady_markowitz;
saved_ss = oo_.endo_simul(:, lastperiods);
% Effectively compute the terminal steady state
for j = lastperiods
% First use the terminal steady of the previous homotopy iteration as guess value (or the contents of the endval block if this is the first iteration)
[oo_.endo_simul(:, j), ~, info] = evaluate_steady_state(oo_.endo_simul(:, j), oo_.exo_simul(j, :), M_, options_, true);
if info(1)
% If this fails, then try again using the initial steady state as guess value
if isempty(ys0_)
guess_value = oo_.steady_state;
else
guess_value = ys0_;
end
[oo_.endo_simul(:, j), ~, info] = evaluate_steady_state(guess_value, oo_.exo_simul(j, :), M_, options_, true);
if info(1)
% If this fails again, give up and restore last periods in oo_.endo_simul
oo_.endo_simul(:, lastperiods) = saved_ss;
local_success = false;
break;
end
end
end
% The following is needed for the STEADY_STATE() operator to work properly
oo_.steady_state = oo_.endo_simul(:, end);
options_.solve_algo = saved_steady_solve_algo;
options_.steady.maxit = saved_steady_maxit;
options_.solve_tolf = saved_steady_tolf;
options_.solve_tolx = saved_steady_tolx;
options_.markowitz = saved_steady_markowitz;
else
if isempty(ys0_) || isempty(ex0_)
error('The homotopy_alt_starting_point option cannot be used without an endval block');
end
endoinit = repmat(ys0_, 1, M_.maximum_lag+periods+M_.maximum_lead);
exoinit = repmat(ex0_', M_.maximum_lag+periods+M_.maximum_lead, 1);
% The terminal condition is not a steady state, compute a convex combination
oo_.endo_simul(:, lastperiods) = share*endoorig(:, lastperiods)+(1-share)*endobase(:, lastperiods);
end
end
while step > options_.simul.homotopy_min_step_size
iteration = iteration+1;
saved_endo_simul = oo_.endo_simul;
new_share = current_share + step; % Try this share, and see if it succeeds
if new_share > options_.simul.homotopy_max_completion_share
new_share = options_.simul.homotopy_max_completion_share; % Don't go beyond target point
step = new_share - current_share;
end
% Copy the current paths for the exogenous and endogenous variables.
exosim = oo_.exo_simul;
endosim = oo_.endo_simul;
steady_success = create_scenario(new_share);
current_weight = 0; % Current weight of target point in convex combination.
step = .5; % Set default step size.
success_counter = 0;
iteration = 0;
if ~options_.noprint
fprintf('Iter. \t | Lambda \t | status \t | Max. residual\n')
fprintf('++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n')
end
while (step > options_.dynatol.x)
if ~isequal(step,1)
options_.verbosity = 0;
if steady_success
% At the first iteration, use the initial guess given by
% perfect_foresight_setup or the user (but only if new_share=1, otherwise it
% does not make much sense). Afterwards, until a converging iteration has been obtained,
% use the rescaled terminal condition (or, if there is no lead, the base
% scenario / initial steady state).
if current_share == 0
if iteration == 1 && new_share == 1
oo_.endo_simul(:, simperiods) = endoorig(:, simperiods);
elseif M_.maximum_lead > 0
oo_.endo_simul(:, simperiods) = repmat(oo_.endo_simul(:, lastperiods(1)), 1, options_.periods);
else
oo_.endo_simul(:, simperiods) = endobase(:, simperiods);
end
end
iteration = iteration+1;
new_weight = current_weight + step; % Try this weight, and see if it succeeds
if new_weight >= 1
new_weight = 1; % Don't go beyond target point
step = new_weight - current_weight;
end
% Compute convex combination for exo path and initial/terminal endo conditions
% But take care of not overwriting the computed part of oo_.endo_simul
oo_.exo_simul = exosim*new_weight + exoinit*(1-new_weight);
oo_.endo_simul(:,[initperiods, lastperiods]) = new_weight*endosim(:,[initperiods, lastperiods])+(1-new_weight)*endoinit(:,[initperiods, lastperiods]);
% Detect Nans or complex numbers in the solution.
path_with_nans = any(any(isnan(oo_.endo_simul)));
path_with_cplx = any(any(~isreal(oo_.endo_simul)));
if isequal(iteration, 1)
% First iteration, same initial guess as in the first call to perfect_foresight_solver_core routine.
oo_.endo_simul(:,M_.maximum_lag+1:end-M_.maximum_lead) = endoinit(:,1:periods);
elseif path_with_nans || path_with_cplx
% If solver failed with NaNs or complex number, use previous solution as an initial guess.
oo_.endo_simul(:,M_.maximum_lag+1:end-M_.maximum_lead) = saved_endo_simul(:,1+M_.maximum_lag:end-M_.maximum_lead);
end
% Make a copy of the paths.
saved_endo_simul = oo_.endo_simul;
% Solve for the paths of the endogenous variables.
[oo_.endo_simul, success, maxerror, solver_iter, per_block_status] = perfect_foresight_solver_core(M_,options_,oo_);
[oo_.endo_simul, success, maxerror, solver_iter, per_block_status] = perfect_foresight_solver_core(M_, options_, oo_);
else
success = false;
maxerror = NaN;
solver_iter = [];
per_block_status = [];
end
if options_.no_homotopy || (iteration == 1 && success && new_share == 1)
% Skip homotopy
if success
current_weight = new_weight;
if current_weight >= 1
if ~options_.noprint
fprintf('%i \t | %1.5f \t | %s \t | %e\n', iteration, new_weight, 'succeeded', maxerror)
end
break
end
success_counter = success_counter + 1;
if success_counter >= 3
success_counter = 0;
step = step * 2;
end
if ~options_.noprint
fprintf('%i \t | %1.5f \t | %s \t | %e\n', iteration, new_weight, 'succeeded', maxerror)
end
else
% If solver failed, then go back.
oo_.endo_simul = saved_endo_simul;
current_share = new_share;
end
did_homotopy = false;
break
end
if iteration == 1
% First iteration failed, so we enter homotopy
did_homotopy = true;
if ~options_.noprint
fprintf('\nEntering the homotopy method iterations...\n')
fprintf('\nIter. \t | Share \t | Status \t | Max. residual\n')
fprintf('++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n')
end
% Disable warnings if homotopy
warning_old_state = warning;
warning off all
% Do not print anything
oldverbositylevel = options_.verbosity;
options_.verbosity = 0;
end
if success
% Successful step
if ~options_.noprint
fprintf('%i \t | %1.5f \t | %s \t | %e\n', iteration, new_share, 'succeeded', maxerror)
end
current_share = new_share;
if current_share >= options_.simul.homotopy_max_completion_share
break
end
success_counter = success_counter + 1;
if options_.simul.homotopy_step_size_increase_success_count > 0 ...
&& success_counter >= options_.simul.homotopy_step_size_increase_success_count
success_counter = 0;
step = step / 2;
if ~options_.noprint
if isreal(maxerror)
fprintf('%i \t | %1.5f \t | %s \t | %e\n', iteration, new_weight, 'failed', maxerror)
else
fprintf('%i \t | %1.5f \t | %s \t | %s\n', iteration, new_weight, 'failed', 'Complex')
end
step = step * 2;
end
else
oo_.endo_simul = saved_endo_simul;
success_counter = 0;
step = step / 2;
if ~options_.noprint
if ~steady_success
fprintf('%i \t | %1.5f \t | %s\n', iteration, new_share, 'failed (in endval steady)')
elseif isreal(maxerror)
fprintf('%i \t | %1.5f \t | %s \t | %e\n', iteration, new_share, 'failed', maxerror)
else
fprintf('%i \t | %1.5f \t | %s \t | %s\n', iteration, new_share, 'failed', 'Complex')
end
end
end
if ~options_.noprint
fprintf('++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n\n')
end
options_.verbosity = oldverbositylevel;
warning(warning_old_state);
end
if did_homotopy && ~options_.noprint
fprintf('++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n\n')
end
%If simulated paths are complex, take real part and recompute the residuals to check whether this is actually a solution
if ~isreal(oo_.endo_simul(:)) % cannot happen with bytecode or the perfect_foresight_problem DLL
ny = size(oo_.endo_simul, 1);
if M_.maximum_lag > 0
y0 = real(oo_.endo_simul(:, M_.maximum_lag));
real_simul = real(oo_.endo_simul);
real_maxerror = recompute_maxerror(real_simul, oo_, M_, options_);
if real_maxerror <= options_.dynatol.f
oo_.endo_simul = real_simul;
maxerror = real_maxerror;
else
y0 = NaN(ny, 1);
end
if M_.maximum_lead > 0
yT = real(oo_.endo_simul(:, M_.maximum_lag+periods+1));
else
yT = NaN(ny, 1);
end
yy = real(oo_.endo_simul(:,M_.maximum_lag+(1:periods)));
residuals = perfect_foresight_problem(yy(:), y0, yT, oo_.exo_simul, M_.params, oo_.steady_state, periods, M_, options_);
maxerror = max(max(abs(residuals)))
if maxerror < options_.dynatol.f
success = true;
oo_.endo_simul=real(oo_.endo_simul);
else
success = false;
current_share = 0;
disp('Simulation terminated with imaginary parts in the residuals or endogenous variables.')
end
end
if success
% Put solver status information in oo_, and do linearization if needed and requested
if current_share == 1
if ~options_.noprint
fprintf('Perfect foresight solution found.\n\n')
end
oo_.deterministic_simulation.status = true;
elseif options_.simul.homotopy_linearization_fallback && current_share > 0
oo_.endo_simul = endobase + (oo_.endo_simul - endobase)/current_share;
oo_.exo_simul = exoorig;
if options_.simul.endval_steady
% The following is needed for the STEADY_STATE() operator to work properly,
% and thus must come before computing the maximum error.
% This is not a true steady state, but it is the closest we can get to
oo_.steady_state = oo_.endo_simul(:, end);
end
maxerror = recompute_maxerror(oo_.endo_simul, oo_, M_, options_);
if ~options_.noprint
fprintf('Perfect foresight solution found for %.1f%% of the shock, then extrapolation was performed using linearization\n\n', current_share*100)
end
oo_.deterministic_simulation.status = true;
oo_.deterministic_simulation.homotopy_linearization = true;
elseif options_.simul.homotopy_marginal_linearization_fallback > 0 && current_share > options_.simul.homotopy_marginal_linearization_fallback
saved_endo_simul = oo_.endo_simul;
new_share = current_share - options_.simul.homotopy_marginal_linearization_fallback;
new_success = create_scenario(new_share);
if new_success
[oo_.endo_simul, new_success] = perfect_foresight_solver_core(M_, options_, oo_);
end
if new_success
oo_.endo_simul = saved_endo_simul + (saved_endo_simul - oo_.endo_simul)*(1-current_share)/options_.simul.homotopy_marginal_linearization_fallback;
oo_.exo_simul = exoorig;
if options_.simul.endval_steady
% The following is needed for the STEADY_STATE() operator to work properly,
% and thus must come before computing the maximum error.
% This is not a true steady state, but it is the closest we can get to
oo_.steady_state = oo_.endo_simul(:, end);
end
maxerror = recompute_maxerror(oo_.endo_simul, oo_, M_, options_);
if ~options_.noprint
fprintf('Perfect foresight solution found for %.1f%% of the shock, then extrapolation was performed using marginal linearization\n\n', current_share*100)
end
oo_.deterministic_simulation.homotopy_marginal_linearization = true;
else
fprintf('perfect_foresight_solver: marginal linearization failed, unable to find solution for %.1f%% of the shock. Try to modify the value of homotopy_marginal_linearization_fallback option\n\n', new_share*100)
end
oo_.deterministic_simulation.status = new_success;
else
fprintf('Failed to solve perfect foresight model\n\n')
oo_.deterministic_simulation.status = false;
end
% Put solver status information in oo_
oo_.deterministic_simulation.status = success;
oo_.deterministic_simulation.error = maxerror;
oo_.deterministic_simulation.homotopy_completion_share = current_share;
oo_.deterministic_simulation.homotopy_iterations = iteration;
if ~isempty(solver_iter)
oo_.deterministic_simulation.iterations = solver_iter;
end
@ -214,6 +370,12 @@ if ~isempty(per_block_status)
oo_.deterministic_simulation.block = per_block_status;
end
% Must come after marginal linearization
if did_homotopy
options_.verbosity = oldverbositylevel;
warning(warning_old_state);
end
dyn2vec(M_, oo_, options_);
if isfield(oo_, 'initval_series') && ~isempty(oo_.initval_series)
@ -236,3 +398,29 @@ assignin('base', 'Simulated_time_series', ts);
if success
oo_.gui.ran_perfect_foresight = true;
end
end
function maxerror = recompute_maxerror(endo_simul, oo_, M_, options_)
% Computes ∞-norm of residuals for a given path of endogenous,
% given the exogenous path and parameters in oo_ and M_
if options_.bytecode
residuals = bytecode('dynamic', 'evaluate', endo_simul, oo_.exo_simul, M_.params, oo_.steady_state, 1);
else
ny = size(endo_simul, 1);
periods = size(endo_simul, 2) - M_.maximum_lag - M_.maximum_lead;
if M_.maximum_lag > 0
y0 = endo_simul(:, M_.maximum_lag);
else
y0 = NaN(ny, 1);
end
if M_.maximum_lead > 0
yT = endo_simul(:, M_.maximum_lag+periods+1);
else
yT = NaN(ny, 1);
end
yy = endo_simul(:,M_.maximum_lag+(1:periods));
residuals = perfect_foresight_problem(yy(:), y0, yT, oo_.exo_simul, M_.params, oo_.steady_state, periods, M_, options_);
end
maxerror = norm(vec(residuals), 'Inf');
end

View File

@ -68,9 +68,6 @@ if options_.block
if options_.verbosity >= 1
disp(ME.message)
end
if options_.no_homotopy
error('Error in bytecode')
end
y = oo_.endo_simul; % Set something for y, need for computing maxerror
success = false;
end
@ -86,9 +83,6 @@ else
if options_.verbosity >= 1
disp(ME.message)
end
if options_.no_homotopy
error('Error in bytecode')
end
y = oo_.endo_simul; % Set something for y, need for computing maxerror
success = false;
end

View File

@ -125,21 +125,6 @@ else
end
end
%% Compute the terminal steady state for all informational periods
oo_.pfwee.terminal_steady_state = NaN(M_.endo_nbr, periods);
for p = 1:periods
if p > 1 && all(oo_.pfwee.terminal_info(:, p) == oo_.pfwee.terminal_info(:, p-1))
oo_.pfwee.terminal_steady_state(:, p) = oo_.pfwee.terminal_steady_state(:, p-1);
else
if p == 1
init = oo_.steady_state;
else
init = oo_.pfwee.terminal_steady_state(:, p-1);
end
oo_.pfwee.terminal_steady_state(:, p) = evaluate_steady_state(init, oo_.pfwee.terminal_info(:, p), M_, options_, true);
end
end
% Build initial paths for endos and exos (only initial conditions are set, the rest is NaN)
if isempty(ys0_)
oo_.endo_simul = repmat(oo_.steady_state, 1, M_.maximum_lag+periods+M_.maximum_lead);

View File

@ -19,18 +19,23 @@ function perfect_foresight_with_expectation_errors_solver
global M_ oo_ options_ ys0_
% Save original steady state, for restoring it at the end
orig_steady_state = oo_.steady_state;
orig_exo_steady_state = oo_.exo_steady_state;
% Same for periods (it will be modified before calling perfect_foresight_solver if constants_simulation_length option is false)
periods = options_.periods;
% Save some options
orig_homotopy_max_completion_share = options_.simul.homotopy_max_completion_share;
orig_endval_steady = options_.simul.endval_steady;
% Retrieve initial paths built by pfwee_setup
% (the versions in oo_ will be truncated before calling perfect_foresight_solver)
endo_simul = oo_.endo_simul;
exo_simul = oo_.exo_simul;
% Enforce the endval steady option in pf_solver
if M_.maximum_lead > 0
options_.simul.endval_steady = true;
end
% Start main loop around informational periods
info_period = 1;
increment = 0;
@ -41,10 +46,11 @@ while info_period <= periods
% Compute terminal steady state as anticipated
oo_.exo_steady_state = oo_.pfwee.terminal_info(:, info_period);
oo_.steady_state = oo_.pfwee.terminal_steady_state(:, info_period);
% oo_.steady_state will be updated by pf_solver (since endval_steady=true)
if options_.pfwee.constant_simulation_length && increment > 0
endo_simul = [ endo_simul NaN(M_.endo_nbr, increment)];
% Use previous terminal steady state as guess value for: simulation periods that dont yet have an initial guess (i.e. are NaNs at this point); and also for the terminal steady state
endo_simul = [ endo_simul repmat(oo_.steady_state, 1, increment)];
exo_simul = [ exo_simul; NaN(increment, M_.exo_nbr)];
end
@ -54,11 +60,7 @@ while info_period <= periods
else
sim_length = periods - info_period + 1;
end
if options_.pfwee.constant_simulation_length && increment > M_.maximum_lead
% Use terminal steady state as guess value for simulation periods that dont yet have an initial guess (i.e. are NaNs at this point)
oo_.endo_simul(:, M_.maximum_lag+periods-(0:increment-M_.maximum_lead-1)) = repmat(oo_.steady_state, 1, increment-M_.maximum_lead);
end
oo_.endo_simul(:, end-M_.maximum_lead+1:end) = repmat(oo_.steady_state, 1, M_.maximum_lead);
oo_.exo_simul = exo_simul(info_period:end, :);
oo_.exo_simul(M_.maximum_lag+(1:periods-info_period+1), :) = oo_.pfwee.shocks_info(:, info_period:end, info_period)';
oo_.exo_simul(M_.maximum_lag+periods-info_period+2:end, :) = repmat(oo_.exo_steady_state', sim_length+M_.maximum_lead-(periods-info_period+1), 1);
@ -71,6 +73,13 @@ while info_period <= periods
error('perfect_foresight_with_expectation_errors_solver: failed to compute solution for information available at period %d\n', info_period)
end
if info_period == 1
homotopy_completion_share = oo_.deterministic_simulation.homotopy_completion_share;
options_.simul.homotopy_max_completion_share = homotopy_completion_share;
elseif oo_.deterministic_simulation.homotopy_completion_share ~= homotopy_completion_share
error('perfect_foresight_solver_with_expectation_errors: could not find a solution for information available at period %d with the same homotopy completion share as period 1\n', info_period)
end
endo_simul(:, info_period:end) = oo_.endo_simul;
exo_simul(info_period:end, :) = oo_.exo_simul;
@ -88,7 +97,7 @@ end
oo_.endo_simul = endo_simul;
oo_.exo_simul = exo_simul;
% Restore some values
oo_.steady_state = orig_steady_state;
oo_.exo_steady_state = orig_exo_steady_state;
% Restore some options
options_.periods = periods;
options_.simul.homotopy_max_completion_share = orig_homotopy_max_completion_share;
options_.simul.endval_steady = orig_endval_steady;

@ -1 +1 @@
Subproject commit a1b8602760e77895ee065de4dadb0d532c4e926e
Subproject commit fa5c4c5191600143f2203ae8cc1e87b709ff6921

View File

@ -372,8 +372,9 @@ MODFILES = \
deterministic_simulations/rbc_det5.mod \
deterministic_simulations/rbc_det6.mod \
deterministic_simulations/homotopy.mod \
deterministic_simulations/homotopy_alt_starting_point.mod \
deterministic_simulations/homotopy_histval.mod \
deterministic_simulations/homotopy_endval_steady_linearization.mod \
deterministic_simulations/homotopy_marginal_linearization.mod \
deterministic_simulations/rbc_det_exo_lag_2a.mod \
deterministic_simulations/rbc_det_exo_lag_2b.mod \
deterministic_simulations/rbc_det_exo_lag_2c.mod \
@ -397,6 +398,7 @@ MODFILES = \
deterministic_simulations/pfwee_constant_sim_length.mod \
deterministic_simulations/pfwee_learnt_in.mod \
deterministic_simulations/pfwee_multiple_shocks.mod \
deterministic_simulations/pfwee_homotopy.mod \
lmmcp/rbc.mod \
lmmcp/sw_lmmcp.mod \
lmmcp/sw_newton.mod \

View File

@ -1,4 +1,5 @@
// Example that triggers homotopy in perfect foresight simulation.
// Simulation with a permanent shock.
var Consumption, Capital, LoggedProductivity;
@ -34,12 +35,12 @@ set_time(1Q1);
initval;
LoggedProductivityInnovation = 0;
LoggedProductivity = 10;
Capital = 1;
end;
steady;
endval;
LoggedProductivityInnovation = 0;
LoggedProductivityInnovation = 1;
end;
steady;

View File

@ -1,50 +0,0 @@
// Test for the homotopy_alt_starting_point option of perfect_foresight_solver
var Consumption, Capital, LoggedProductivity;
varexo LoggedProductivityInnovation;
parameters beta, alpha, delta, rho;
beta = .985;
alpha = 1/3;
delta = alpha/10;
rho = .9;
model;
[name='Euler equation']
1/Consumption = beta/Consumption(1)*(alpha*exp(LoggedProductivity(1))*Capital^(alpha-1)+1-delta);
[name='Physical capital stock law of motion']
Capital = exp(LoggedProductivity)*Capital(-1)^alpha+(1-delta)*Capital(-1)-Consumption;
[name='Logged productivity law of motion']
LoggedProductivity = rho*LoggedProductivity(-1)+LoggedProductivityInnovation;
end;
steady_state_model;
LoggedProductivity = LoggedProductivityInnovation/(1-rho);
Capital = (exp(LoggedProductivity)*alpha/(1/beta-1+delta))^(1/(1-alpha));
Consumption = exp(LoggedProductivity)*Capital^alpha-delta*Capital;
end;
initval;
LoggedProductivityInnovation = 0;
end;
steady;
endval;
LoggedProductivityInnovation = 0.4;
end;
steady;
perfect_foresight_setup(periods=200);
perfect_foresight_solver(homotopy_alt_starting_point);
if ~oo_.deterministic_simulation.status
error('Perfect foresight simulation failed')
end

View File

@ -0,0 +1,43 @@
// Example that triggers homotopy in perfect foresight simulation.
// Tests the endval_steady, homotopy_linearization_fallback options, and a few more.
var Consumption, Capital, LoggedProductivity;
varexo LoggedProductivityInnovation;
parameters beta, alpha, delta, rho;
beta = .985;
alpha = 1/3;
delta = alpha/10;
rho = .9;
model;
1/Consumption = beta/Consumption(1)*(alpha*exp(LoggedProductivity(1))*Capital^(alpha-1)+1-delta);
Capital = exp(LoggedProductivity)*Capital(-1)^alpha+(1-delta)*Capital(-1)-Consumption;
LoggedProductivity = rho*LoggedProductivity(-1)+LoggedProductivityInnovation;
end;
initval;
LoggedProductivityInnovation = 0;
end;
steady;
endval;
LoggedProductivityInnovation = 1;
Consumption = 0.1;
Capital = 1;
end;
perfect_foresight_setup(periods=200, endval_steady);
perfect_foresight_solver(homotopy_initial_step_size = 0.5,
homotopy_max_completion_share = 0.6,
homotopy_min_step_size = 0.0001,
homotopy_linearization_fallback,
steady_solve_algo = 13, steady_tolf=1e-7);
if ~oo_.deterministic_simulation.status
error('Perfect foresight simulation failed')
end

View File

@ -1,4 +1,5 @@
// Example that triggers homotopy in perfect foresight simulation.
// Simulation starts out of steady state and returns to it.
var Consumption, Capital, LoggedProductivity;
@ -24,11 +25,11 @@ LoggedProductivity = rho*LoggedProductivity(-1)+LoggedProductivityInnovation;
end;
% steady_state_model;
% LoggedProductivity = LoggedProductivityInnovation/(1-rho);
% Capital = (exp(LoggedProductivity)*alpha/(1/beta-1+delta))^(1/(1-alpha));
% Consumption = exp(LoggedProductivity)*Capital^alpha-delta*Capital;
% end;
steady_state_model;
LoggedProductivity = LoggedProductivityInnovation/(1-rho);
Capital = (exp(LoggedProductivity)*alpha/(1/beta-1+delta))^(1/(1-alpha));
Consumption = exp(LoggedProductivity)*Capital^alpha-delta*Capital;
end;
set_time(1Q1);
@ -45,12 +46,6 @@ histval;
LoggedProductivity(0)=10;
end;
// endval;
// LoggedProductivityInnovation = 0;
// end;
// steady;
perfect_foresight_setup(periods=200);
perfect_foresight_solver;

View File

@ -0,0 +1,42 @@
// Example that triggers homotopy in perfect foresight simulation.
// Tests the homotopy_marginal_linearization_fallback and homotopy_step_size_increase_success_count options
var Consumption, Capital, LoggedProductivity;
varexo LoggedProductivityInnovation;
parameters beta, alpha, delta, rho;
beta = .985;
alpha = 1/3;
delta = alpha/10;
rho = .9;
model;
1/Consumption = beta/Consumption(1)*(alpha*exp(LoggedProductivity(1))*Capital^(alpha-1)+1-delta);
Capital = exp(LoggedProductivity)*Capital(-1)^alpha+(1-delta)*Capital(-1)-Consumption;
LoggedProductivity = rho*LoggedProductivity(-1)+LoggedProductivityInnovation;
end;
initval;
LoggedProductivityInnovation = 0;
end;
steady;
endval;
LoggedProductivityInnovation = 1;
Consumption = 0.1;
Capital = 1;
end;
perfect_foresight_setup(periods=200, endval_steady);
perfect_foresight_solver(homotopy_max_completion_share = 0.7,
homotopy_step_size_increase_success_count = 3,
homotopy_marginal_linearization_fallback,
steady_solve_algo = 13);
if ~oo_.deterministic_simulation.status
error('Perfect foresight simulation failed')
end

View File

@ -27,13 +27,18 @@ steady;
check;
// Save initial steady state (it will be modified by pfwee)
orig_steady_state = oo_.steady_state;
orig_exo_steady_state = oo_.exo_steady_state;
perfect_foresight_with_expectation_errors_setup(periods = 7, datafile = 'pfwee.csv');
// First simulation with default options
perfect_foresight_with_expectation_errors_solver;
pfwee_simul = oo_.endo_simul;
// Now compute the solution by hand to verify the results
oo_.steady_state = orig_steady_state;
oo_.exo_steady_state = orig_exo_steady_state;
perfect_foresight_setup;

View File

@ -26,17 +26,21 @@ steady;
check;
// First simulation with default options
// Save initial steady state (it will be modified by pfwee)
orig_steady_state = oo_.steady_state;
orig_exo_steady_state = oo_.exo_steady_state;
perfect_foresight_with_expectation_errors_setup(periods = 7, datafile = 'pfwee.csv');
perfect_foresight_with_expectation_errors_solver(constant_simulation_length);
pfwee_simul = oo_.endo_simul;
// Now compute the solution by hand to verify the results
oo_.steady_state = orig_steady_state;
oo_.exo_steady_state = orig_exo_steady_state;
perfect_foresight_setup;
initial_steady_state = oo_.steady_state;
// Information arriving in period 1 (temp shock now)
oo_.exo_simul(2,1) = 1.2;
perfect_foresight_solver;
@ -74,8 +78,9 @@ oo_.exo_simul(7,1) = 1.1;
oo_.exo_simul(8,1) = 1.1;
oo_.exo_steady_state = 1.1;
oo_.exo_simul(9:14, 1) = repmat(oo_.exo_steady_state', 6, 1);
oo_.endo_simul(:, 12:13) = repmat(oo_.steady_state, 1, 2);
oo_.steady_state = evaluate_steady_state(oo_.steady_state, oo_.exo_steady_state, M_, options_, true);
oo_.endo_simul(:, 12:14) = repmat(oo_.steady_state, 1, 3);
oo_.endo_simul(:, 14) = oo_.steady_state;
saved_endo = oo_.endo_simul(:, 1:5);
saved_exo = oo_.exo_simul(1:5, :);
oo_.endo_simul = oo_.endo_simul(:, 6:end);

View File

@ -0,0 +1,40 @@
/* Example that triggers homotopy in perfect foresight simulation with
expectation errors. */
var Consumption, Capital, LoggedProductivity;
varexo LoggedProductivityInnovation;
parameters beta, alpha, delta, rho;
beta = .985;
alpha = 1/3;
delta = alpha/10;
rho = .9;
model;
1/Consumption = beta/Consumption(1)*(alpha*exp(LoggedProductivity(1))*Capital^(alpha-1)+1-delta);
Capital = exp(LoggedProductivity)*Capital(-1)^alpha+(1-delta)*Capital(-1)-Consumption;
LoggedProductivity = rho*LoggedProductivity(-1)+LoggedProductivityInnovation;
end;
initval;
LoggedProductivityInnovation = 0;
end;
steady;
endval;
LoggedProductivityInnovation = 0.6;
end;
endval(learnt_in = 5);
LoggedProductivityInnovation = 1;
end;
perfect_foresight_with_expectation_errors_setup(periods=200);
perfect_foresight_with_expectation_errors_solver(homotopy_max_completion_share = 0.8, homotopy_linearization_fallback, steady_solve_algo = 13);
if ~oo_.deterministic_simulation.status
error('Perfect foresight simulation failed')
end

View File

@ -70,13 +70,18 @@ endval(learnt_in = 6);
x *= 0.75;
end;
// Save initial steady state (it will be modified by pfwee)
orig_steady_state = oo_.steady_state;
orig_exo_steady_state = oo_.exo_steady_state;
perfect_foresight_with_expectation_errors_setup(periods = 7);
// First simulation with default options
perfect_foresight_with_expectation_errors_solver;
pfwee_simul = oo_.endo_simul;
// Now compute the solution by hand to verify the results
oo_.steady_state = orig_steady_state;
oo_.exo_steady_state = orig_exo_steady_state;
perfect_foresight_setup;