From 02072dde399c58e840b4e12be7bbbef390c79d39 Mon Sep 17 00:00:00 2001 From: Marco Ratto Date: Mon, 17 May 2021 21:24:25 +0200 Subject: [PATCH] Add Occbin routines Syntax is not yet finalized (see preprocessor#68). Documentation still to be done. Ref. #569 --- license.txt | 26 ++ matlab/+occbin/DSGE_smoother.m | 391 ++++++++++++++++ matlab/+occbin/IVF_core.m | 129 ++++++ matlab/+occbin/IVF_posterior.m | 198 ++++++++ matlab/+occbin/check_regimes.m | 146 ++++++ matlab/+occbin/dynare_resolve.m | 72 +++ matlab/+occbin/get_deriv.m | 69 +++ matlab/+occbin/get_info.m | 57 +++ matlab/+occbin/get_pq.m | 32 ++ matlab/+occbin/graph.m | 156 +++++++ matlab/+occbin/initialize.m | 75 +++ matlab/+occbin/kalman_update_algo_1.m | 427 ++++++++++++++++++ matlab/+occbin/kalman_update_algo_3.m | 392 ++++++++++++++++ matlab/+occbin/make_chart.m | 67 +++ matlab/+occbin/map_regime.m | 49 ++ matlab/+occbin/match_function.m | 56 +++ matlab/+occbin/mkdata.m | 76 ++++ .../mkdatap_anticipated_2constraints_dyn.m | 169 +++++++ matlab/+occbin/mkdatap_anticipated_dyn.m | 151 +++++++ matlab/+occbin/process_constraint.m | 81 ++++ matlab/+occbin/process_error_constraint.m | 35 ++ matlab/+occbin/set_default_options.m | 206 +++++++++ matlab/+occbin/set_option.m | 34 ++ matlab/+occbin/setup.m | 52 +++ matlab/+occbin/shock_decomposition.m | 266 +++++++++++ matlab/+occbin/solve_one_constraint.m | 297 ++++++++++++ matlab/+occbin/solve_two_constraints.m | 321 +++++++++++++ matlab/+occbin/solver.m | 83 ++++ matlab/+occbin/tokenize.m | 66 +++ matlab/+occbin/unpack_simulations.m | 38 ++ matlab/+occbin/write_regimes_to_xls.m | 64 +++ matlab/DsgeSmoother.m | 378 +++++++++++----- matlab/default_option_values.m | 3 + matlab/dsge_likelihood.m | 102 ++++- matlab/dynare_estimation_1.m | 44 +- matlab/dynare_estimation_init.m | 11 + matlab/evaluate_likelihood.m | 6 +- matlab/evaluate_smoother.m | 25 +- matlab/get_error_message.m | 16 + matlab/initial_estimation_checks.m | 12 + .../missing_observations_kalman_filter.m | 151 +++++-- matlab/list_of_functions_to_be_cleared.m | 3 +- matlab/missing_DiffuseKalmanSmootherH3_Z.m | 334 +++++++++++--- matlab/prior_posterior_statistics_core.m | 9 +- matlab/store_smoother_results.m | 14 + matlab/varlist_indices.m | 52 ++- preprocessor | 2 +- scripts/dynare.el | 8 +- tests/.gitignore | 2 + tests/Makefile.am | 19 + tests/occbin/filter/NKM.mod | 342 ++++++++++++++ tests/occbin/filter/NKM_0_std_shocks.mod | 345 ++++++++++++++ tests/occbin/filter/NKM_mh_mode_saved.mat | Bin 0 -> 972 bytes tests/occbin/filter/dataobsfile.mat | Bin 0 -> 6568 bytes tests/occbin/model_borrcon/borrcon.mod | 53 +++ .../model_borrcon/borrcon_0_std_shocks.mod | 58 +++ tests/occbin/model_borrcon/borrcon_common.inc | 38 ++ .../model_irrcap_twoconstraints/dynrbc.mod | 47 ++ .../dynrbc_0_std_shocks.mod | 64 +++ .../dynrbc_common.inc | 116 +++++ .../dynrbc_token_xfail.mod | 80 ++++ 61 files changed, 6364 insertions(+), 251 deletions(-) create mode 100644 matlab/+occbin/DSGE_smoother.m create mode 100644 matlab/+occbin/IVF_core.m create mode 100644 matlab/+occbin/IVF_posterior.m create mode 100644 matlab/+occbin/check_regimes.m create mode 100644 matlab/+occbin/dynare_resolve.m create mode 100644 matlab/+occbin/get_deriv.m create mode 100644 matlab/+occbin/get_info.m create mode 100644 matlab/+occbin/get_pq.m create mode 100644 matlab/+occbin/graph.m create mode 100644 matlab/+occbin/initialize.m create mode 100644 matlab/+occbin/kalman_update_algo_1.m create mode 100644 matlab/+occbin/kalman_update_algo_3.m create mode 100644 matlab/+occbin/make_chart.m create mode 100644 matlab/+occbin/map_regime.m create mode 100644 matlab/+occbin/match_function.m create mode 100644 matlab/+occbin/mkdata.m create mode 100644 matlab/+occbin/mkdatap_anticipated_2constraints_dyn.m create mode 100644 matlab/+occbin/mkdatap_anticipated_dyn.m create mode 100644 matlab/+occbin/process_constraint.m create mode 100644 matlab/+occbin/process_error_constraint.m create mode 100644 matlab/+occbin/set_default_options.m create mode 100644 matlab/+occbin/set_option.m create mode 100644 matlab/+occbin/setup.m create mode 100644 matlab/+occbin/shock_decomposition.m create mode 100644 matlab/+occbin/solve_one_constraint.m create mode 100644 matlab/+occbin/solve_two_constraints.m create mode 100644 matlab/+occbin/solver.m create mode 100644 matlab/+occbin/tokenize.m create mode 100644 matlab/+occbin/unpack_simulations.m create mode 100644 matlab/+occbin/write_regimes_to_xls.m create mode 100644 tests/occbin/filter/NKM.mod create mode 100644 tests/occbin/filter/NKM_0_std_shocks.mod create mode 100644 tests/occbin/filter/NKM_mh_mode_saved.mat create mode 100644 tests/occbin/filter/dataobsfile.mat create mode 100644 tests/occbin/model_borrcon/borrcon.mod create mode 100644 tests/occbin/model_borrcon/borrcon_0_std_shocks.mod create mode 100644 tests/occbin/model_borrcon/borrcon_common.inc create mode 100755 tests/occbin/model_irrcap_twoconstraints/dynrbc.mod create mode 100644 tests/occbin/model_irrcap_twoconstraints/dynrbc_0_std_shocks.mod create mode 100644 tests/occbin/model_irrcap_twoconstraints/dynrbc_common.inc create mode 100644 tests/occbin/model_irrcap_twoconstraints/dynrbc_token_xfail.mod diff --git a/license.txt b/license.txt index 73b1f5e2a..5af6f7c5b 100644 --- a/license.txt +++ b/license.txt @@ -25,6 +25,32 @@ Files: * Copyright: 1996-2021 Dynare Team License: GPL-3+ +Files: matlab/+occbin/IVF_core.m + matlab/+occbin/make_chart.m + matlab/+occbin/map_regime.m + matlab/+occbin/match_function.m + matlab/+occbin/mkdata.m + matlab/+occbin/mkdatap_anticipated_2constraints_dyn.m + matlab/+occbin/mkdatap_anticipated_dyn.m + matlab/+occbin/process_constraint.m + matlab/+occbin/solve_one_constraint.m + matlab/+occbin/solve_two_constraints.m + matlab/+occbin/tokenize.m +Copyright: none +License: public-domain-occbin + Original authors: Luca Guerrieri and Matteo Iacoviello + Original file downloaded from: + https://www.matteoiacoviello.com/research_files/occbin_20140630.zip + Adapted for Dynare by Dynare Team. + . + This code is in the public domain and may be used freely. + However the authors would appreciate acknowledgement of the source by + citation of any of the following papers: + . + Luca Guerrieri and Matteo Iacoviello (2015): "OccBin: A toolkit for solving + dynamic models with occasionally binding constraints easily" + Journal of Monetary Economics 70, 22-38 + Files: matlab/AIM/SP* Copyright: none License: public-domain-aim diff --git a/matlab/+occbin/DSGE_smoother.m b/matlab/+occbin/DSGE_smoother.m new file mode 100644 index 000000000..9ab36a10d --- /dev/null +++ b/matlab/+occbin/DSGE_smoother.m @@ -0,0 +1,391 @@ +function [alphahat,etahat,epsilonhat,ahat0,SteadyState,trend_coeff,aKK,T0,R0,P,PKK,decomp,Trend,state_uncertainty,M_,oo_,bayestopt_] = DSGE_smoother(xparam1,gend,Y,data_index,missing_value,M_,oo_,options_,bayestopt_,estim_params_,dataset_, dataset_info) +%function [alphahat,etahat,epsilonhat,ahat0,SteadyState,trend_coeff,aKK,T0,R0,P,PKK,decomp,Trend,state_uncertainty,M_,oo_,bayestopt_] = DSGE_smoother(xparam1,gend,Y,data_index,missing_value,M_,oo_,options_,bayestopt_,estim_params_,dataset_, dataset_info) +% Runs a DSGE smoother with occasionally binding constraints +% +% INPUTS +% - xparam1 [double] (p*1) vector of (estimated) parameters. +% - gend [integer] scalar specifying the number of observations +% - Y [double] (n*T) matrix of data. +% - data_index [cell] 1*smpl cell of column vectors of indices. +% - missing_value [boolean] 1 if missing values, 0 otherwise +% - M_ [structure] Matlab's structure describing the model (M_). +% - oo_ [structure] Matlab's structure containing the results (oo_). +% - options_ [structure] Matlab's structure describing the current options (options_). +% - bayestopt_ [structure] describing the priors +% - estim_params_ [structure] characterizing parameters to be estimated +% - dataset_ [structure] the dataset after required transformation +% - dataset_info [structure] Various informations about the dataset (descriptive statistics and missing observations) +% +% OUTPUTS +% - alphahat [double] (m*T) matrix, smoothed endogenous variables (a_{t|T}) (decision-rule order) +% - etahat [double] (r*T) matrix, smoothed structural shocks (r>=n is the number of shocks). +% - epsilonhat [double] (n*T) matrix, smoothed measurement errors. +% - ahat0 [double] (m*T) matrix, updated (endogenous) variables (a_{t|t}) (decision-rule order) +% - SteadyState [double] (m*1) vector specifying the steady state level of each endogenous variable (declaration order) +% - trend_coeff [double] (n*1) vector, parameters specifying the slope of the trend associated to each observed variable. +% - aKK [double] (K,n,T+K) array, k (k=1,...,K) steps ahead +% filtered (endogenous) variables (decision-rule order) +% - T0 and R0 [double] Matrices defining the state equation (T is the (m*m) transition matrix). +% - P: (m*m*(T+1)) 3D array of one-step ahead forecast error variance +% matrices (decision-rule order) +% - PKK (K*m*m*(T+K)) 4D array of k-step ahead forecast error variance +% matrices (meaningless for periods 1:d) (decision-rule order) +% - decomp (K*m*r*(T+K)) 4D array of shock decomposition of k-step ahead +% filtered variables (decision-rule order) +% - Trend [double] (n*T) pure trend component; stored in options_.varobs order +% - state_uncertainty [double] (K,K,T) array, storing the uncertainty +% about the smoothed state (decision-rule order) +% - M_ [structure] decribing the model +% - oo_ [structure] storing the results +% - options_ [structure] describing the options +% - bayestopt_ [structure] describing the priors + +% Copyright (C) 2021 Dynare Team +% +% This file is part of Dynare. +% +% Dynare is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% Dynare is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with Dynare. If not, see . + +smoother_field_list = {'SmoothedVariables', 'UpdatedVariables', 'SmoothedShocks'}; + +regime_history=[]; +if options_.occbin.smoother.linear_smoother && nargin==12 + %% linear smoother + options_.occbin.smoother.status=false; + [alphahat,etahat,epsilonhat,ahat,SteadyState,trend_coeff,aK,T0,R0,P,PK,decomp,Trend,state_uncertainty,M_,oo_,bayestopt_] = DsgeSmoother(xparam1,gend,Y,data_index,missing_value,M_,oo_,options_,bayestopt_,estim_params_); + tmp_smoother=store_smoother_results(M_,oo_,options_,bayestopt_,dataset_,dataset_info,alphahat,etahat,epsilonhat,ahat,SteadyState,trend_coeff,aK,P,PK,decomp,Trend,state_uncertainty); + for jf=1:length(smoother_field_list) + oo_.occbin.linear_smoother.(smoother_field_list{jf}) = tmp_smoother.(smoother_field_list{jf}); + end + oo_.occbin.linear_smoother.alphahat=alphahat; + oo_.occbin.linear_smoother.etahat=etahat; + oo_.occbin.linear_smoother.epsilonhat=epsilonhat; + oo_.occbin.linear_smoother.ahat=ahat; + oo_.occbin.linear_smoother.SteadyState=SteadyState; + oo_.occbin.linear_smoother.trend_coeff=trend_coeff; + oo_.occbin.linear_smoother.aK=aK; + oo_.occbin.linear_smoother.T0=T0; + oo_.occbin.linear_smoother.R0=R0; + oo_.occbin.linear_smoother.decomp=decomp; + + fprintf('\nOccbin: linear smoother done.\n') + options_.occbin.smoother.status=true; +end +% if init_mode +%% keep these commented lines for the moment, should some nasty problem in the future call back for this option +% shocks1 = etahat(:,2:end)'; +% clear regime_history ; +% +% for ii = 1:gend-1 +% +% fprintf('Period number %d out of %d \n',ii,gend-1) +% opts_simul.SHOCKS = shocks1(ii,:); +% opts_simul.endo_init = alphahat(oo_.dr.inv_order_var,ii); +% out = occbin.runsim_fn(opts_simul, M_, oo_, options_); +% regime_history(ii) = out.regime_history; +% end +% +% [TT,RR] = dynare_resolve(M_,options_,oo_); +% CC = zeros([size(RR,1),gend ]); +% else + +opts_simul = options_.occbin.simul; +opts_simul.curb_retrench = options_.occbin.smoother.curb_retrench; +opts_simul.maxit = options_.occbin.smoother.maxit; +opts_simul.waitbar = options_.occbin.smoother.waitbar; +opts_simul.periods = options_.occbin.smoother.periods; +opts_simul.check_ahead_periods = options_.occbin.smoother.check_ahead_periods; +opts_simul.full_output = options_.occbin.smoother.full_output; +opts_simul.piecewise_only = options_.occbin.smoother.piecewise_only; +constraints = M_.occbin.constraint; +% init_mode = options_.occbin.smoother.init_mode; % 0 = standard; 1 = unconditional frcsts zero shocks+smoothed states in each period +% init_mode = 0; +occbin_options = struct(); + +occbin_options.constraints = constraints; +occbin_options.first_period_occbin_update = options_.occbin.smoother.first_period_occbin_update; +occbin_options.opts_regime = opts_simul; % this builds the opts_simul options field needed by occbin.solver +occbin_options.opts_regime.binding_indicator = options_.occbin.likelihood.init_binding_indicator; +occbin_options.opts_regime.regime_history=options_.occbin.likelihood.init_regime_history; +[alphahat,etahat,epsilonhat,ahat,SteadyState,trend_coeff,aK,T0,R0,P,PK,decomp,Trend,state_uncertainty,M_,oo_,bayestopt_] = DsgeSmoother(xparam1,gend,Y,data_index,missing_value,M_,oo_,options_,bayestopt_,estim_params_,occbin_options);% T1=TT; + +oo_.occbin.smoother.realtime_regime_history = oo_.occbin.smoother.regime_history; +regime_history = oo_.occbin.smoother.regime_history; +opts_regime.regime_history = oo_.occbin.smoother.regime_history; + +ahat0 = ahat; +aKK=aK; +PKK=PK; +clear aK PK; + +occbin_options.first_period_occbin_update = inf; + +opts_regime.binding_indicator=[]; +regime_history0 = regime_history; + +fprintf('Occbin smoother iteration 1.\n') +opts_simul.SHOCKS = [etahat(:,2:end)'; zeros(1,M_.exo_nbr)]; +opts_simul.exo_pos = 1:M_.exo_nbr; +opts_simul.endo_init = alphahat(oo_.dr.inv_order_var,1); +opts_simul.init_regime=regime_history; % use realtime regime for guess, to avoid multiple solution issues! +options_.occbin.simul=opts_simul; +[~, out, ss] = occbin.solver(M_,oo_,options_); +regime_history = out.regime_history; +if options_.smoother_redux + occbin_options.opts_simul.restrict_state_space =1; + oo_.occbin.linear_smoother.T0=ss.T(oo_.dr.order_var,oo_.dr.order_var,1); + oo_.occbin.linear_smoother.R0=ss.R(oo_.dr.order_var,:,1); +end +TT = ss.T(oo_.dr.order_var,oo_.dr.order_var,:); +RR = ss.R(oo_.dr.order_var,:,:); +CC = ss.C(oo_.dr.order_var,:); +TT = cat(3,TT(:,:,1),TT); +RR = cat(3,RR(:,:,1),RR); +CC = cat(2,CC(:,1),CC); + +opts_regime.regime_history = regime_history; +opts_regime.binding_indicator = []; +[TT, RR, CC, regime_history] = occbin.check_regimes(TT, RR, CC, opts_regime, M_, oo_, options_); +is_changed = ~isequal(regime_history0,regime_history); +if isempty(regime_history0) + regime_history0 = regime_history; +end +iter=1; + +is_periodic = 0; +is_changed_start = 0; +maxiter = options_.occbin.smoother.max_number_of_iterations; +occbin_smoother_fast = options_.occbin.smoother.fast; +occbin_smoother_debug=options_.occbin.smoother.debug; + +sto_alphahat=alphahat; +sto_etahat={etahat}; +sto_CC = CC; +sto_RR = RR; +sto_TT = TT; +for k=1:size(TT,3) + sto_eee(:,k) = eig(TT(:,:,k)); +end + + +while is_changed && maxiter>iter && ~is_periodic + iter=iter+1; + fprintf('Occbin smoother iteration %u.\n', iter) + occbin_options.opts_regime.regime_history=regime_history; + [alphahat,etahat,epsilonhat,ahat,SteadyState,trend_coeff,aK,T0,R0,P,PK,decomp,Trend,state_uncertainty,M_,oo_,bayestopt_] = DsgeSmoother(xparam1,gend,Y,data_index,missing_value,M_,oo_,options_,bayestopt_,estim_params_,occbin_options,TT,RR,CC);% T1=TT; + sto_etahat(iter)={etahat}; + regime_history0(iter,:) = regime_history; + save('info1','regime_history0'); + + sto_CC = CC; + sto_RR = RR; + sto_TT = TT; + + opts_simul.SHOCKS = [etahat(:,2:end)'; zeros(1,M_.exo_nbr)]; + opts_simul.endo_init = alphahat(oo_.dr.inv_order_var,1); + options_.occbin.simul=opts_simul; + [~, out, ss] = occbin.solver(M_,oo_,options_); + regime_history = out.regime_history; + TT = ss.T(oo_.dr.order_var,oo_.dr.order_var,:); + RR = ss.R(oo_.dr.order_var,:,:); + CC = ss.C(oo_.dr.order_var,:); + TT = cat(3,TT(:,:,1),TT); + RR = cat(3,RR(:,:,1),RR); + CC = cat(2,CC(:,1),CC); + + opts_regime.regime_history = regime_history; + [TT, RR, CC, regime_history] = occbin.check_regimes(TT, RR, CC, opts_regime, M_, oo_, options_); + is_changed = ~isequal(regime_history0(iter,:),regime_history); + if length(constraints)==2 + for k=1:size(regime_history0,2) + isdiff_regime(k,1) = ~isequal(regime_history0(end,k).regime1,regime_history(k).regime1); + isdiff_start(k,1) = ~isequal(regime_history0(end,k).regimestart1,regime_history(k).regimestart1); + isdiff_(k,1) = isdiff_regime(k,1) || isdiff_start(k,1); + isdiff_regime(k,2) = ~isequal(regime_history0(end,k).regime2,regime_history(k).regime2); + isdiff_start(k,2) = ~isequal(regime_history0(end,k).regimestart2,regime_history(k).regimestart2); + isdiff_(k,2) = isdiff_regime(k,2) || isdiff_start(k,2); + end + is_changed_regime = ~isempty(find(isdiff_regime(:,1))) || ~isempty(find(isdiff_regime(:,2))); + is_changed_start = ~isempty(find(isdiff_start(:,1))) || ~isempty(find(isdiff_start(:,2))); + else + for k=1:size(regime_history0,2) + isdiff_regime(k,1) = ~isequal(regime_history0(end,k).regime,regime_history(k).regime); + isdiff_start(k,1) = ~isequal(regime_history0(end,k).regimestart,regime_history(k).regimestart); + isdiff_(k,1) = isdiff_regime(k,1) || isdiff_start(k,1); + end + is_changed_regime = ~isempty(find(isdiff_regime(:,1))); + is_changed_start = ~isempty(find(isdiff_start(:,1))); + end + if occbin_smoother_fast + is_changed = is_changed_regime; + end + if iter>1 + for kiter=1:iter-1 + is_tmp = isequal(regime_history0(kiter,:),regime_history); + if is_tmp + break + end + end + is_periodic = (is_changed && is_tmp); + end + + if is_changed + for k=1:size(TT,3) + eee(:,k) = eig(TT(:,:,k)); + end + err_eig(iter-1) = max(max(abs(sort(eee)-sort(sto_eee)))); + err_alphahat(iter-1) = max(max(max(abs(alphahat-sto_alphahat)))); + err_etahat(iter-1) = max(max(max(abs(etahat-sto_etahat{iter-1})))); + err_CC(iter-1) = max(max(max(abs(CC-sto_CC)))); + err_RR(iter-1) = max(max(max(abs(RR-sto_RR)))); + err_TT(iter-1) = max(max(max(abs(TT-sto_TT)))); + end + + if occbin_smoother_debug + regime_ = cell(0); + regime_new = regime_; + start_ = regime_; + start_new = regime_; + if length(constraints)==2 + indx_init_1 = find(isdiff_(:,1)); + if ~isempty(indx_init_1) + qq={regime_history0(end,indx_init_1).regime1}'; + for j=1:length(qq), regime_(j,1) = {int2str(qq{j})}; end + qq={regime_history0(end,indx_init_1).regimestart1}'; + for j=1:length(qq), start_(j,1) = {int2str(qq{j})}; end + qq={regime_history(indx_init_1).regime1}'; + for j=1:length(qq), regime_new(j,1) = {int2str(qq{j})}; end + qq={regime_history(indx_init_1).regimestart1}'; + for j=1:length(qq), start_new(j,1) = {int2str(qq{j})}; end + disp('Time points where regime 1 differs') + disp(table(indx_init_1, regime_, start_, regime_new, start_new)) + end + + indx_init_2 = find(isdiff_(:,2)); + if ~isempty(indx_init_2) + regime_ = cell(0); + regime_new = regime_; + start_ = regime_; + start_new = regime_; + qq={regime_history0(end,indx_init_2).regime2}'; + for j=1:length(qq), regime_(j,1) = {int2str(qq{j})}; end + qq={regime_history0(end,indx_init_2).regimestart2}'; + for j=1:length(qq), start_(j,1) = {int2str(qq{j})}; end + qq={regime_history(indx_init_2).regime2}'; + for j=1:length(qq), regime_new(j,1) = {int2str(qq{j})}; end + qq={regime_history(indx_init_2).regimestart2}'; + for j=1:length(qq), start_new(j,1) = {int2str(qq{j})}; end + disp('Time points where regime 2 differs ') + disp(table(indx_init_2, regime_, start_, regime_new, start_new)) + end + else + indx_init_1 = find(isdiff_(:,1)); + if ~isempty(indx_init_1) + qq={regime_history0(end,indx_init_1).regime}'; + for j=1:length(qq), regime_(j,1) = {int2str(qq{j})}; end + qq={regime_history0(end,indx_init_1).regimestart}'; + for j=1:length(qq), start_(j,1) = {int2str(qq{j})}; end + qq={regime_history(indx_init_1).regime}'; + for j=1:length(qq), regime_new(j,1) = {int2str(qq{j})}; end + qq={regime_history(indx_init_1).regimestart}'; + for j=1:length(qq), start_new(j,1) = {int2str(qq{j})}; end + disp('Time points where regime differs') + disp(table(indx_init_1, regime_, start_, regime_new, start_new)) + end + + end + end + if is_changed + sto_alphahat=alphahat; + sto_eee = eee; + end +end +regime_history0(max(iter+1,1),:) = regime_history; +oo_.occbin.smoother.regime_history=regime_history0(end,:); +oo_.occbin.smoother.regime_history_iter=regime_history0; +save('info1','regime_history0') + +if (maxiter==iter && is_changed) || is_periodic + disp(['Occbin smoother did not converge.']) + if is_periodic + disp(['Occbin smoother algo loops between two solutions.']) + end +else + disp(['Occbin smoother converged.']) + if occbin_smoother_fast && is_changed_start + disp('WARNING: fast algo is used, regime(s) duration(s) was not forced to converge') + end +end +if (~is_changed || occbin_smoother_debug) && nargin==12 + if is_changed + CC = sto_CC; + RR = sto_RR; + TT = sto_TT; + oo_.occbin.smoother.regime_history=regime_history0(end-1,:); + end + tmp_smoother=store_smoother_results(M_,oo_,options_,bayestopt_,dataset_,dataset_info,alphahat,etahat,epsilonhat,ahat0,SteadyState,trend_coeff,aKK,P,PKK,decomp,Trend,state_uncertainty); + for jf=1:length(smoother_field_list) + oo_.occbin.smoother.(smoother_field_list{jf}) = tmp_smoother.(smoother_field_list{jf}); + end + oo_.occbin.smoother.alphahat=alphahat; + oo_.occbin.smoother.etahat=etahat; + oo_.occbin.smoother.epsilonhat=epsilonhat; + oo_.occbin.smoother.ahat=ahat0; + oo_.occbin.smoother.SteadyState=SteadyState; + oo_.occbin.smoother.trend_coeff=trend_coeff; + oo_.occbin.smoother.aK=aKK; + oo_.occbin.smoother.T0=TT; + oo_.occbin.smoother.R0=RR; + oo_.occbin.smoother.C0=CC; + if options_.occbin.smoother.plot + GraphDirectoryName = CheckPath('graphs',M_.fname); + j1=0; + ifig=0; + for j=1:M_.exo_nbr + if M_.Sigma_e(j,j) + j1=j1+1; + if mod(j1,9)==1 + hfig = dyn_figure(options_.nodisplay,'name','Occbin smoothed shocks'); + ifig=ifig+1; + isub=0; + end + isub=isub+1; + subplot(3,3,isub) + if options_.occbin.smoother.linear_smoother + plot(oo_.occbin.linear_smoother.etahat(j,:)','linewidth',2) + hold on, + end + plot(oo_.occbin.smoother.etahat(j,:)','r--','linewidth',2) + hold on, plot([0 options_.nobs],[0 0],'k--') + set(gca,'xlim',[0 options_.nobs]) + title(deblank(M_.exo_names(j,:)),'interpreter','none') + if mod(j1,9)==0 + if options_.occbin.smoother.linear_smoother + annotation('textbox', [0.1,0,0.35,0.05],'String', 'Linear','Color','Blue','horizontalalignment','center','interpreter','none'); + end + annotation('textbox', [0.55,0,0.35,0.05],'String', 'Piecewise','Color','Red','horizontalalignment','center','interpreter','none'); + dyn_saveas(gcf,[GraphDirectoryName filesep M_.fname,'_smoothedshocks_occbin',int2str(ifig)],options_.nodisplay,options_.graph_format); + end + end + + if mod(j1,9)~=0 && j==M_.exo_nbr + annotation('textbox', [0.1,0,0.35,0.05],'String', 'Linear','Color','Blue','horizontalalignment','center','interpreter','none'); + annotation('textbox', [0.55,0,0.35,0.05],'String', 'Piecewise','Color','Red','horizontalalignment','center','interpreter','none'); + dyn_saveas(hfig,[GraphDirectoryName filesep M_.fname,'_smoothedshocks_occbin',int2str(ifig)],options_.nodisplay,options_.graph_format); + end + end + end + +end + diff --git a/matlab/+occbin/IVF_core.m b/matlab/+occbin/IVF_core.m new file mode 100644 index 000000000..8ca5d16be --- /dev/null +++ b/matlab/+occbin/IVF_core.m @@ -0,0 +1,129 @@ +function [filtered_errs, resids, Emat, stateval, error_code] = IVF_core(M_,oo_,options_,err_index,filtered_errs_init,my_obs_list,obs,init_val) +% function [filtered_errs, resids, Emat, stateval] = IVF_core(M_,oo_,options_,err_index,filtered_errs_init,my_obs_list,obs,init_val) +% Computes thre +% +% Outputs: +% - filtered_errs [T by N_obs] filtered shocks +% - resids [T by N_obs] residuals +% - Emat [N by N_obs by T] response matrix of endogenous variables to shocks at each point in time +% - stateval [T by N] vector of endogenous variables +% - error_code [4 by 1] error code +% +% Inputs +% - M_ [structure] Matlab's structure describing the model (M_). +% - oo_ [structure] Matlab's structure containing the results (oo_). +% - options_ [structure] Matlab's structure describing the current options (options_). +% - err_index [double] index of shocks with strictly positive variance in M_.exo_names +% - filtered_errs_init [T by N_obs] initial values for the shocks +% - my_obs_list [cell] names of observables +% - obs [T by N_obs] observed data +% - init_val [N by 1] initial value of endogenous variables + +% Original authors: Pablo Cuba-Borda, Luca Guerrieri, Matteo Iacoviello and Molin Zhong +% Original file downloaded from: +% http://www.lguerrieri.com/jae-replication.zip +% Adapted for Dynare by Dynare Team. +% +% This code is in the public domain and may be used freely. +% However the authors would appreciate acknowledgement of the source by +% citation of any of the following papers: +% +% Pablo Cuba-Borda, Luca Guerrieri, and Matteo Iacoviello (2019): "Likelihood evaluation of models +% with occasionally binding constraints", Journal of Applied Econometrics, +% 34(7), 1073-1085 + + +%------------------------------------- +% Filter shocks +%------------------------------------- + +[sample_length, n_obs]= size(obs); +nerrs = size(err_index,1); +if nargin<8 + init_val = zeros(M_.endo_nbr,1); +end + +resids = zeros(sample_length,nerrs); +stateval = zeros(sample_length,M_.endo_nbr); +Emat = zeros(M_.endo_nbr,nerrs,sample_length); +error_code = zeros(4,1); +%solver options (set locally) +options_.solve_algo = options_.occbin.solver.solve_algo; +options_.solve_tolf = options_.occbin.solver.solve_tolf; +options_.solve_tolx = options_.occbin.solver.solve_tolx; +options_.options.steady.maxit = options_.occbin.solver.maxit; +options_.jacobian_flag=1; + +opts_simul = options_.occbin.simul; +opts_simul.curb_retrench = options_.occbin.likelihood.curb_retrench; +opts_simul.maxit = options_.occbin.likelihood.maxit; +opts_simul.waitbar = false; +opts_simul.periods = options_.occbin.likelihood.periods; +opts_simul.check_ahead_periods = options_.occbin.likelihood.check_ahead_periods; +opts_simul.periodic_solution = options_.occbin.likelihood.periodic_solution; +opts_simul.restrict_state_space = options_.occbin.likelihood.restrict_state_space; +opts_simul.piecewise_only = 1; + +filtered_errs=zeros(sample_length,n_obs); + +if options_.occbin.likelihood.waitbar + hh = dyn_waitbar(0,'IVF_core: Filtering the shocks'); + set(hh,'Name','IVF_core: Filtering the shocks.'); +end + +for this_period=1:sample_length + if options_.occbin.likelihood.waitbar + dyn_waitbar(this_period/sample_length, hh, sprintf('Period %u of %u', this_period,sample_length)); + end + current_obs = obs(this_period,:); + init_val_old = init_val; + + inan = ~isnan(current_obs); + current_obs = current_obs(inan); + obs_list = my_obs_list(inan); + opts_simul.varobs_id=options_.varobs_id(inan)'; + opts_simul.exo_pos=err_index(inan); %err_index is predefined mapping from observables to shocks + opts_simul.endo_init = init_val_old; + opts_simul.SHOCKS = filtered_errs_init(this_period,inan); +% [ err_vals_out, exitflag ] = csolve(@(err_vals) occbin.match_function(... +% err_vals, obs_list,current_obs, opts_simul, M_,oo_,options_),... +% err0',@(err_vals) occbin.match_function(... +% err_vals, obs_list,current_obs, opts_simul, M_,oo_,options_),options_.solve_tolf,options_.occbin.solver.maxit); + [err_vals_out, exitflag] = dynare_solve(@occbin.match_function, filtered_errs_init(this_period,inan)', options_, obs_list,current_obs, opts_simul, M_,oo_,options_); + + if exitflag + filtered_errs=NaN; + error_code(1) = 304; + error_code(4) = 1000; + if options_.occbin.likelihood.waitbar; dyn_waitbar_close(hh); end + return + end + filtered_errs(this_period,inan)=err_vals_out'; + + opts_simul.SHOCKS = err_vals_out; + + [ resids(this_period,inan), ~, stateval(this_period,:), Emat(:,inan,this_period), M_] = occbin.match_function(... + err_vals_out,obs_list,current_obs,opts_simul, M_,oo_,options_); + init_val = stateval(this_period,:); %update + if max(abs(err_vals_out))>1e8 + error_code(1) = 306; + error_code(4) = max(abs(err_vals_out))/1000; + filtered_errs=NaN; + if options_.occbin.likelihood.waitbar; dyn_waitbar_close(hh); end + return + end + if max(abs(resids(this_period,:)))>0.001 + disp_verbose('IVF_core: I am stopping because match_function could not find the shocks that',options_.verbosity) + disp_verbose('IVF_core: solve for the model''s observed variables',options_.verbosity) + filtered_errs=NaN; + error_code(1) = 303; + error_code(4) = max(abs(resids(this_period,:)))*100; + if options_.occbin.likelihood.waitbar; dyn_waitbar_close(hh); end + return + end +end +if options_.occbin.likelihood.waitbar + dyn_waitbar_close(hh); +end + +end \ No newline at end of file diff --git a/matlab/+occbin/IVF_posterior.m b/matlab/+occbin/IVF_posterior.m new file mode 100644 index 000000000..edb28041d --- /dev/null +++ b/matlab/+occbin/IVF_posterior.m @@ -0,0 +1,198 @@ +function [fval,info,exit_flag,DLIK,Hess,SteadyState,trend_coeff,Model,DynareOptions,BayesInfo,DynareResults, atT, innov] = IVF_posterior(xparam1,... + dataset_,obs_info,DynareOptions,Model,EstimatedParameters,BayesInfo,BoundsInfo,DynareResults) +% function [fval,info,exit_flag,DLIK,Hess,SteadyState,trend_coeff,Model,DynareOptions,BayesInfo,DynareResults, atT, innov] = IVF_posterior(xparam1,... +% dataset_,obs_info,DynareOptions,Model,EstimatedParameters,BayesInfo,BoundsInfo,DynareResults) +% Computes Likelihood with inversion filter +% +% INPUTS +% - xparam1 [double] current values for the estimated parameters. +% - dataset_ [structure] dataset after transformations +% - DynareOptions [structure] Matlab's structure describing the current options (options_). +% - Model [structure] Matlab's structure describing the model (M_). +% - EstimatedParameters [structure] characterizing parameters to be estimated +% - BayesInfo [structure] describing the priors +% - BoundsInfo [structure] containing prior bounds +% - DynareResults [structure] Matlab's structure containing the results (oo_). +% +% OUTPUTS +% - fval [double] scalar, value of the likelihood or posterior kernel. +% - info [integer] 4×1 vector, informations resolution of the model and evaluation of the likelihood. +% - exit_flag [integer] scalar, equal to 1 (no issues when evaluating the likelihood) or 0 (not able to evaluate the likelihood). +% - DLIK [double] Empty array. +% - Hess [double] Empty array. +% - SteadyState [double] Empty array. +% - trend [double] Empty array. +% - Model [struct] Updated Model structure described in INPUTS section. +% - DynareOptions [struct] Updated DynareOptions structure described in INPUTS section. +% - BayesInfo [struct] See INPUTS section. +% - DynareResults [struct] Updated DynareResults structure described in INPUTS section. +% - atT [double] (m*T) matrix, smoothed endogenous variables (a_{t|T}) (decision-rule order) +% - innov [double] (r*T) matrix, smoothed structural shocks (r>n is the umber of shocks). + +% Copyright (C) 2021 Dynare Team +% +% This file is part of Dynare. +% +% Dynare is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% Dynare is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with Dynare. If not, see . + + +DLIK=[]; +Hess=[]; +trend_coeff = []; +obs = obs_info.rawdata; +obs_list = DynareOptions.varobs(:); +exit_flag = 1; + + +if size(xparam1,1)1e-3 + disp_verbose('Penalize failure of residuals to be zero',options_.verbosity) + fval = Inf; + info(1) = 303; + info(4) = sum(resids(:).^2); + exit_flag = 0; + return +end + +if ~isempty(xparam1) + prior = -priordens(xparam1,BayesInfo.pshape,BayesInfo.p6,BayesInfo.p7,BayesInfo.p3,BayesInfo.p4); +else + prior = 0; +end +if prior == Inf + % If parameters outside prior bound, minus prior density is very large + fval = Inf; + info(4) = 1000; + exit_flag = 0; + return +end + +%--------------------------------------------- +% Calculate posterior +%--------------------------------------------- + +% remember that the likelihood has already been multiplied by -1 +% hence, posterior is -1 times the log of the prior +fval = like+prior; +atT = stateval(:,DynareResults.dr.order_var)'; +innov = zeros(Model.exo_nbr,sample_length); +innov(diag(Model.Sigma_e)~=0,:)=filtered_errs'; +updated_variables = atT*nan; +BayesInfo.mf = BayesInfo.smoother_var_list(BayesInfo.smoother_mf); + + +initDynareOptions=DynareOptions; +DynareOptions.nk=[]; %unset options_.nk and reset it later +[DynareResults]=store_smoother_results(Model,DynareResults,DynareOptions,BayesInfo,dataset_,obs_info,atT,innov,[],updated_variables,DynareResults.dr.ys,zeros(length(DynareOptions.varobs_id),1)); +DynareOptions=initDynareOptions; diff --git a/matlab/+occbin/check_regimes.m b/matlab/+occbin/check_regimes.m new file mode 100644 index 000000000..7e49a4a78 --- /dev/null +++ b/matlab/+occbin/check_regimes.m @@ -0,0 +1,146 @@ +function [TT, RR, CC, regime_history] = check_regimes(TT, RR, CC, opts_regime, M_, oo_, options_) +%-function function [TT, RR, CC, regime_history] = check_regimes(TT, RR, CC, opts_regime, M_, oo_, options_) +% +% INPUTS +% - TT [N by N] transition matrix of state space +% - RR [N by N_exo] shock impact matrix of state space +% - CC [N by 1] constant of state space +% - opts_regime_ [structure] structure describing the regime +% - M_ [structure] Matlab's structure describing the model +% - oo_ [structure] Matlab's structure containing the results +% - options_ [structure] Matlab's structure describing the current options +% +% OUTPUTS +% - TT [N by N] transition matrix of state space for each period +% - RR [N by N_exo by T] shock impact matrix of state space for each period +% - CC [N by N_exo by T] constant of state space for each period +% - regime_history [structure] contains the regime history + + +% Copyright (C) 2021 Dynare Team +% +% This file is part of Dynare. +% +% Dynare is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% Dynare is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with Dynare. If not, see . + +binding_indicator = opts_regime.binding_indicator; +regime_history = opts_regime.regime_history; +gend = size(binding_indicator,1); +if gend ==0 + gend = length(regime_history)+1; +end +number_of_constraints = length(M_.occbin.constraint); + +if number_of_constraints==1 + base_regime.regime = 0; + base_regime.regimestart = 1; +else + base_regime.regime1 = 0; + base_regime.regimestart1 = 1; + base_regime.regime2 = 0; + base_regime.regimestart2 = 1; +end + +if isempty(regime_history) + % set default unconstrained regimes + regime_history = base_regime; + regime_history(2:gend-1) = base_regime; +end +init_=0; +if ndim(TT)==2 + CC = zeros([size(RR,1),gend ]); + TT = repmat(TT, [1 1 gend]); + RR = repmat(RR, [1 1 gend]); + init_=1; +end +opts_simul = options_.occbin.simul; +opts_simul.full_output=0; +opts_simul.piecewise_only=1; + +for tp=1:gend-1 + change_it = 0; + if ~isempty(binding_indicator) + if number_of_constraints==1 + if regime_history(tp).regime(1) > binding_indicator(tp+1,1) + regime_history(tp).regime = [0 regime_history(tp).regime]; + regime_history(tp).regimestart = [1 regime_history(tp).regimestart+1]; + change_it = 1; + end + if regime_history(tp).regime(1) < binding_indicator(tp+1,1) + regime_history(tp).regime = [1 regime_history(tp).regime]; + regime_history(tp).regimestart = [1 regime_history(tp).regimestart+1]; + change_it = 1; + end + + else + if regime_history(tp).regime1(1) > binding_indicator(tp+1,1) + regime_history(tp).regime1 = [0 regime_history(tp).regime1]; + regime_history(tp).regimestart1 = [1 regime_history(tp).regimestart1+1]; + change_it = 1; + end + if regime_history(tp).regime1(1) < binding_indicator(tp+1,1) + regime_history(tp).regime1 = [1 regime_history(tp).regime1]; + regime_history(tp).regimestart1 = [1 regime_history(tp).regimestart1+1]; + change_it = 1; + end + if regime_history(tp).regime2(1) > binding_indicator(tp+1,2) + regime_history(tp).regime2 = [0 regime_history(tp).regime2]; + regime_history(tp).regimestart2 = [1 regime_history(tp).regimestart2+1]; + change_it = 1; + end + if regime_history(tp).regime2(1) < binding_indicator(tp+1,2) + regime_history(tp).regime2 = [1 regime_history(tp).regime2]; + regime_history(tp).regimestart2 = [1 regime_history(tp).regimestart2+1]; + change_it = 1; + end + end + end + if change_it || init_ + check_it = 0; + jt=1; + while jt<=tp-1 && check_it==0 + check_it = isequal(regime_history(tp),regime_history(jt)); + jt = jt+1; + end + if ~check_it && isequal(regime_history(tp),base_regime) && init_ + check_it = true; + jt=1; + end + + if check_it %|| tp==1 % the matrices for regime have been already computed + TT(:,:,tp+1) = TT(:,:,jt); + RR(:,:,tp+1) = RR(:,:,jt); + CC(:,tp+1) = CC(:,jt); + else + if number_of_constraints==1 + nperi = max(regime_history(tp).regimestart); + else + nperi = max([regime_history(tp).regimestart1 regime_history(tp).regimestart2]); + end + + opts_simul.endo_init_=zeros(size(RR,1),1); + opts_simul.init_binding_indicator=[]; + opts_simul.init_regime=regime_history(tp); + opts_simul.SHOCKS=zeros(1,size(RR,2)); + opts_simul.maxit=1; + opts_simul.periods=nperi; + options_.occbin.simul=opts_simul; + [~, ~, ss] = occbin.solver(M_,oo_,options_); + TT(:,:,tp+1) = ss.T(oo_.dr.order_var,oo_.dr.order_var); + RR(:,:,tp+1) = ss.R(oo_.dr.order_var,:); + CC(:,tp+1) = ss.C(oo_.dr.order_var); + end + + end +end diff --git a/matlab/+occbin/dynare_resolve.m b/matlab/+occbin/dynare_resolve.m new file mode 100644 index 000000000..ee9fbfcfc --- /dev/null +++ b/matlab/+occbin/dynare_resolve.m @@ -0,0 +1,72 @@ +function [A,B,ys,info,M_,oo_,TT, RR, CC, A0, B0] ... + = dynare_resolve(M_,options_,oo_,regime_history, reduced_state_space, A, B) +% function [A,B,ys,info,M_,options_,oo_,TT, RR, CC, A0, B0] ... +% = dynare_resolve(M_,options_,oo_,regime_history, reduced_state_space, A, B) +% Computes the linear approximation and the matrices A and B of the +% transition equation. Mirrors dynare_resolve +% +% Inputs: +% - M_ [structure] Matlab's structure describing the model +% - options_ [structure] Matlab's structure containing the options +% - oo_ [structure] Matlab's structure containing the results +% - reduced_state_space [string] +% - A [double] State transition matrix +% - B [double] shock impact matrix +% Outputs: +% - A [double] State transition matrix (potentially for restricted state space) +% - B [double] shock impact matrix (potentially for restricted state space) +% - ys [double] vector of steady state values +% - info [double] 4 by 1 vector with exit flag and information +% - M_ [structure] Matlab's structure describing the model +% - options_ [structure] Matlab's structure containing the options +% - oo_ [structure] Matlab's structure containing the results +% - TT [N by N] transition matrix of state space for each period +% - RR [N by N_exo by T] shock impact matrix of state space for each period +% - CC [N by N_exo by T] constant of state space for each period +% - A0 [double] State transition matrix (unrestricted state space) +% - B0 [double] shock impact matrix (unrestricted state space) + +% Copyright (C) 2001-2021 Dynare Team +% +% This file is part of Dynare. +% +% Dynare is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% Dynare is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with Dynare. If not, see . + +if nargin<6 + [A,B,ys,info,M_,oo_] = dynare_resolve(M_,options_,oo_); +else + ys = oo_.dr.ys; + info = 0; +end +if ~info(1) && nargin>4 && ~isempty(regime_history) + opts_regime.regime_history=regime_history; + opts_regime.binding_indicator=[]; + [TT, RR, CC] = ... + occbin.check_regimes(A, B, [], opts_regime, M_,oo_,options_); +else + TT=A; + RR=B; + CC=zeros(size(A,1),1); +end + +A0=A; +B0=B; +if ~info(1) && nargin>4 && ischar(reduced_state_space) && ~isempty(reduced_state_space) + iv = oo_.dr.restrict_var_list; + A=A(iv,iv); + B=B(iv,:); + TT=TT(iv,iv,:); + RR=RR(iv,:,:); + CC=CC(iv,:); +end \ No newline at end of file diff --git a/matlab/+occbin/get_deriv.m b/matlab/+occbin/get_deriv.m new file mode 100644 index 000000000..4a3f6fd9b --- /dev/null +++ b/matlab/+occbin/get_deriv.m @@ -0,0 +1,69 @@ +function [h_minus_1, h, h_plus_1, h_exo, resid] = get_deriv(M_, ys_) +% function [h_minus_1, h, h_plus_1, h_exo, resid] = get_deriv(M_, ys_) +% Computes dynamic Jacobian and writes it to conformable matrices +% +% INPUTS +% - M_ [struct] Definition of the model. +% - ys vector steady state +% +% OUTPUTS +% - h_minus_1 [N by N] derivative matrix with respect to lagged endogenous variables +% - h [N by N] derivative matrix with respect to contemporanous endogenous variables +% - h_plus_1 [N by N] derivative matrix with respect to leaded endogenous variables +% - h_exo [N by N_exo] derivative matrix with respect to exogenous variables +% - resid [N by 1] vector of residuals + +% Copyright (C) 2021 Dynare Team +% +% This file is part of Dynare. +% +% Dynare is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% Dynare is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with Dynare. If not, see . + +x = zeros(M_.maximum_lag + M_.maximum_lead + 1,M_.exo_nbr); + +iyv = M_.lead_lag_incidence'; +iyr0 = find(iyv(:)) ; +z=repmat(ys_,1,M_.maximum_lag + M_.maximum_lead + 1); + +[resid,g1]=feval([M_.fname,'.dynamic'],z(iyr0),x, M_.params, ys_, M_.maximum_exo_lag+1); + +% Initialize matrices +h_minus_1=zeros(M_.endo_nbr); +h = h_minus_1; +h_plus_1 = h_minus_1; + +% build h_minus_1 +if M_.maximum_lag + lag_columns=find(iyv(:,1)); + n_lag_columns=length(lag_columns); + h_minus_1(:,lag_columns) = g1(:,1:n_lag_columns); +else + n_lag_columns=0; +end + +% build h +contemporaneous_columns=find(iyv(:,M_.maximum_lag+1)); +n_contemporaneous_columns = length(contemporaneous_columns); +h(:,contemporaneous_columns) = g1(:,1+n_lag_columns:n_lag_columns+n_contemporaneous_columns); + +%build h_plus_1 +if M_.maximum_lead + lead_columns=find(iyv(:,end)); + n_lead_columns = length(lead_columns); + h_plus_1(:,lead_columns) = g1(:,n_lag_columns+n_contemporaneous_columns+1:n_lag_columns+n_contemporaneous_columns+n_lead_columns); +else + n_lead_columns=0; +end + +h_exo =g1(:,n_lag_columns+n_contemporaneous_columns+n_lead_columns+1:end); diff --git a/matlab/+occbin/get_info.m b/matlab/+occbin/get_info.m new file mode 100644 index 000000000..05317299e --- /dev/null +++ b/matlab/+occbin/get_info.m @@ -0,0 +1,57 @@ +function M_ = get_info(M_) +%function M_ = get_info(M_) +% Parses constraint to clean spaces and provide information on endogenous variables +% involved in constraints +% +% INPUTS +% - M_ [struct] Definition of the model. +% +% OUTPUTS +% - M_ [struct] Definition of the model. + +% Copyright (C) 2021 Dynare Team +% +% This file is part of Dynare. +% +% Dynare is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% Dynare is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with Dynare. If not, see . + +delimiters = char(',',';','(',')','+','-','^','*','/','>','<','='); +iwish=0; +indx_=[]; +wish_list=''; +M_.occbin.constraint_nbr=length(M_.occbin.constraint); +for k=1:M_.occbin.constraint_nbr + %get rid of redundant spaces; + M_.occbin.constraint(k).bind=strrep(M_.occbin.constraint(k).bind,' ',''); + M_.occbin.constraint(k).relax=strrep(M_.occbin.constraint(k).relax,' ',''); + bind_clean = occbin.tokenize(M_.occbin.constraint(k).bind,delimiters); + relax_clean = occbin.tokenize(M_.occbin.constraint(k).relax,delimiters); + for j=1:M_.endo_nbr + tmp=ismember(M_.endo_names{j},bind_clean); + tmp1=ismember(M_.endo_names{j},relax_clean); + if tmp || tmp1 + iwish = iwish+1; + indx_(iwish)=j; + if iwish>1 + wish_list = char(wish_list,M_.endo_names{j}); + else + wish_list = M_.endo_names{j}; + end + end + end +end + +M_.occbin.wish_list.endo = wish_list; +M_.occbin.wish_list.iendo = indx_; +end \ No newline at end of file diff --git a/matlab/+occbin/get_pq.m b/matlab/+occbin/get_pq.m new file mode 100644 index 000000000..c3d0c40d0 --- /dev/null +++ b/matlab/+occbin/get_pq.m @@ -0,0 +1,32 @@ +function [p,q]=get_pq(dr) +% function [p,q]=get_pq(dr) +% Output decision rule matrices p and q in declaration order +% +% INPUTS +% - dr [struct] decision rules +% +% OUTPUTS +% - p [N by N] transition matrix ghu in declaration order +% - q [N by N_exo] shock response matrix ghx in declaration order + +% Copyright (C) 2021 Dynare Team +% +% This file is part of Dynare. +% +% Dynare is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% Dynare is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with Dynare. If not, see . + +nvars = size(dr.ghx,1); +p = zeros(nvars); +p(dr.order_var,dr.state_var) = dr.ghx; +q = dr.ghu(dr.inv_order_var,:); diff --git a/matlab/+occbin/graph.m b/matlab/+occbin/graph.m new file mode 100644 index 000000000..d2a96e9ef --- /dev/null +++ b/matlab/+occbin/graph.m @@ -0,0 +1,156 @@ +function graph(M_, options_, options_occbin_, oo_, var_list) +% function graph(M_, options_, options_occbin_, oo_, var_list) +% +% Inputs: +% - M_ [structure] Matlab's structure describing the model +% - options_ [structure] Matlab's structure containing the options +% - options_occbin_ [structure] Matlab's structure containing Occbin options +% - oo_ [structure] Matlab's structure containing the results +% - var_list [char] list of the variables to plot + +% Copyright (C) 2021 Dynare Team +% +% This file is part of Dynare. +% +% Dynare is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% Dynare is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with Dynare. If not, see . + +options_ = occbin.set_option(options_,options_occbin_,'graph.steady_state'); + +if ~exist([M_.dname '/graphs'],'dir') + mkdir(M_.dname,'graphs'); +end + +fidTeX=write_latex_header(M_,options_); + +if isempty(var_list) + var_list = M_.endo_names(1:M_.orig_endo_nbr); +end + +%get endogenous variables +[i_var, number_of_plots_to_draw_endo, index_uniques] = varlist_indices(var_list, M_.endo_names, 1); +var_list_plots=var_list(index_uniques); +var_list_TeX = M_.endo_names_tex(i_var); + +data_to_plot(:,:,1)=oo_.occbin.piecewise(:,i_var); +if isfield(oo_.occbin,'linear') + data_to_plot(:,:,2)=oo_.occbin.linear(:,i_var); + legend_list = {'Piecewise Linear','Linear'}; +else + legend_list = {'Piecewise Linear'}; +end + +nperiods=size(data_to_plot,1); +ndim=size(data_to_plot,3); + +if ~options_.occbin.graph.steady_state + data_to_plot=data_to_plot-repmat(oo_.occbin.ys(i_var)',nperiods,1,ndim); +end + +%get exogenous variables +[i_var_exo, number_of_plots_to_draw_exo, index_uniques] = varlist_indices(var_list, M_.exo_names, 1); +var_list_plots=[var_list_plots; var_list(index_uniques)]; +var_list_TeX = [var_list_TeX; M_.exo_names_tex(i_var_exo)]; + +if number_of_plots_to_draw_exo>0 + exo_index=NaN(number_of_plots_to_draw_exo); + for ii=1:length(i_var_exo) + temp_index=find(oo_.occbin.exo_pos==i_var_exo(ii)); + if ~isempty(temp_index) + exo_index(ii)=temp_index; + else + error('%s was not part of the shocks for Occbin.', var_list{i_var_exo(ii)}); + end + end + data_to_plot(:,end+1:end+number_of_plots_to_draw_exo,1)=[oo_.occbin.shocks_sequence(:,exo_index); zeros(nperiods-size(oo_.occbin.shocks_sequence,1),number_of_plots_to_draw_exo)]; + data_to_plot(:,end+1:end+number_of_plots_to_draw_exo,2)=NaN; +end + +[nbplt,nr,nc,lr,lc,nstar] = pltorg(number_of_plots_to_draw_endo+number_of_plots_to_draw_exo); + +for fig = 1:nbplt + hh = dyn_figure(options_.nodisplay,'Name',['Occbin simulated paths, figure ' int2str(fig)]); + for plt = 1:nstar + if fig==nbplt && ~lr==0 + subplot(lr,lc,plt); + else + subplot(nr,nc,plt); + end + h_zero=plot([1 nperiods],[0 0],'--k','linewidth',0.5); + hold on + h1=plot(1:nperiods,data_to_plot(:,(fig-1)*nstar+plt,1),'b-','linewidth',2); + if ndim==2 && (fig-1)*nstar+plt<=number_of_plots_to_draw_endo + h2=plot(1:nperiods,data_to_plot(:,(fig-1)*nstar+plt,2),'r--','linewidth',2); hold on + end + hold off + + max_y = max(max(data_to_plot(:,(fig-1)*nstar+plt,:))); + min_y = min(min(data_to_plot(:,(fig-1)*nstar+plt,:))); + + y_bottom = min_y - .01*abs(min_y); + + y_top = max_y + 0.01*abs(max_y); + if y_bottom==y_top + y_top=y_bottom+1; + end + axis([1 nperiods y_bottom y_top]) + remove_fractional_xticks + if plt==1 + if ndim==2 + legend([h1,h2],legend_list,'box','off') + else + legend([h1],legend_list,'box','off') + end + end + if options_.TeX + title(['$' var_list_TeX{(fig-1)*nstar+plt,:} '$'],'Interpreter','latex'); + else + title(deblank(var_list_plots((fig-1)*nstar+plt,:)),'Interpreter','none'); + end + if number_of_plots_to_draw_endo+number_of_plots_to_draw_exo==(fig-1)*nstar+plt + break + end + end + dyn_saveas(hh,[M_.dname, '/graphs/' M_.fname '_occbin_' int2str(fig)],options_.nodisplay,options_.graph_format); + if options_.TeX && any(strcmp('eps',cellstr(options_.graph_format))) + fprintf(fidTeX,'\\begin{figure}[H]\n'); + fprintf(fidTeX,'\\centering \n'); + if fig==nbplt + fprintf(fidTeX,'\\includegraphics[width=%2.2f\\textwidth]{%s_occbin_%s}\n',options_.figures.textwidth*min(plt/nc,1),[M_.dname, '/graphs/' M_.fname],int2str(fig)); + else + fprintf(fidTeX,'\\includegraphics[width=%2.2f\\textwidth]{%s_occbin_%s}\n',options_.figures.textwidth*min(plt/lc,1),[M_.dname, '/graphs/' M_.fname],int2str(fig)); + end + fprintf(fidTeX,'\\caption{Simulated time paths over time. Blue solid line: taking occasionally binding constraint into account. Red dashed line: ignoring constraint.}\n'); + fprintf(fidTeX,'\\label{Fig:occbin:%s}\n', int2str(fig)); + fprintf(fidTeX,'\\end{figure}\n'); + fprintf(fidTeX,' \n'); + end +end + +if options_.TeX && any(strcmp('eps',cellstr(options_.graph_format))) + fprintf(fidTeX,' \n'); + fprintf(fidTeX,'%% End Of TeX file. \n'); + fclose(fidTeX); +end + + +function fidTeX=write_latex_header(M_,options_) + +if options_.TeX && any(strcmp('eps',cellstr(options_.graph_format))) + fidTeX = fopen([M_.dname, '/graphs/' M_.fname '_occbin.tex'],'w'); + fprintf(fidTeX,'%% TeX eps-loader file generated by occbin.make_chart.m (Dynare).\n'); + fprintf(fidTeX,['%% ' datestr(now,0) '\n']); + fprintf(fidTeX,' \n'); +else + fidTeX =[]; +end diff --git a/matlab/+occbin/initialize.m b/matlab/+occbin/initialize.m new file mode 100644 index 000000000..bc938d16b --- /dev/null +++ b/matlab/+occbin/initialize.m @@ -0,0 +1,75 @@ +function [M_, oo_, options_] = initialize(M_,oo_,options_) +% function [M_, oo_, options_] = initialize(M_,oo_,options_) +% Initializes run of Occbin: sets default options, parses constraints, creates +% eval_difference-file +% +% INPUT: +% - M_ [structure] Matlab's structure describing the model +% - oo_ [structure] Matlab's structure containing the results +% - options_ [structure] Matlab's structure containing the options +% +% OUTPUT: +% - M_ [structure] Matlab's structure describing the model +% - oo_ [structure] Matlab's structure containing the results +% - options_ [structure] Matlab's structure containing the options + +% Copyright (C) 2021 Dynare Team +% +% This file is part of Dynare. +% +% Dynare is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% Dynare is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with Dynare. If not, see . + +options_.occbin = struct(); +options_.occbin = occbin.set_default_options(options_.occbin, M_); + +oo_.dr=set_state_space(oo_.dr,M_,options_); + +M_ = occbin.get_info(M_); + +if M_.occbin.constraint_nbr>2 + error('Occbin: Only up to two constraints are supported') +end +for k=1:M_.occbin.constraint_nbr + M_.occbin.constraint(k).bind_difference = occbin.process_constraint(M_.occbin.constraint(k).bind,'_difference',M_.endo_names,false,M_.param_names); + M_.occbin.constraint(k).bind_error = occbin.process_error_constraint(M_.occbin.constraint(k).bind_difference); + if isempty(M_.occbin.constraint(k).relax) + M_.occbin.constraint(k).relax_difference = ['~binding.constraint_',int2str(k)]; + else + M_.occbin.constraint(k).relax_difference = occbin.process_constraint(M_.occbin.constraint(k).relax,'_difference',M_.endo_names,false,M_.param_names); + end + M_.occbin.constraint(k).relax_error = occbin.process_error_constraint(M_.occbin.constraint(k).relax_difference); + M_.occbin.constraint(k).pswitch_index = strmatch(M_.occbin.constraint(k).pswitch,M_.param_names); +end +nwishes_ = size(M_.occbin.wish_list.endo,1); +fid = fopen(['+' M_.fname '/eval_difference.m'],'w+'); +fprintf(fid,'function [binding, relax, err]=eval_difference(zdatalinear_,M_,ys);\n'); +for i_indx_=1:nwishes_ + fprintf(fid,'%s_difference=zdatalinear_(:,%i);\r\n',deblank(M_.occbin.wish_list.endo(i_indx_,:)),M_.occbin.wish_list.iendo(i_indx_)); +end +for k=1:M_.occbin.constraint_nbr + fprintf(fid,'binding.constraint_%i = %s;\r\n',k,M_.occbin.constraint(k).bind_difference); + fprintf(fid,'relax.constraint_%i = %s;\r\n',k,M_.occbin.constraint(k).relax_difference); +% fprintf(fid,'try\r\n'); + + fprintf(fid,' err.binding_constraint_%i = abs(%s);\r\n',k,M_.occbin.constraint(k).bind_error); + fprintf(fid,' err.relax_constraint_%i = abs(%s);\r\n',k,M_.occbin.constraint(k).relax_error); + +% fprintf(fid,'catch\r\n'); +% +% fprintf(fid,' err.newviolvecbool_%i = nan(size(err.newviolvecbool_%i));\r\n',k,k); +% fprintf(fid,' err.relaxconstraint_%i = nan(size(err.relaxconstraint_%i));\r\n',k,k); + +% fprintf(fid,'end\r\n'); +end +fclose(fid); diff --git a/matlab/+occbin/kalman_update_algo_1.m b/matlab/+occbin/kalman_update_algo_1.m new file mode 100644 index 000000000..c0310dfa5 --- /dev/null +++ b/matlab/+occbin/kalman_update_algo_1.m @@ -0,0 +1,427 @@ +function [a, a1, P, P1, v, T, R, C, regimes_, error_flag, M_, lik, etahat] = kalman_update_algo_1(a,a1,P,P1,data_index,Z,v,Y,H,QQQ,T0,R0,TT,RR,CC,regimes0,M_,oo_,options_,occbin_options) +% function [a, a1, P, P1, v, T, R, C, regimes_, info, M_, lik, etahat] = kalman_update_algo_1(a,a1,P,P1,data_index,Z,v,Y,H,QQQ,T0,R0,TT,RR,CC,regimes0,M_,oo_,options_,occbin_options) +% INPUTS +% - a [N by 1] t-1's state estimate +% - a1 [N by 2] state predictions made at t-1:t +% - P [N by N] t-1's covariance of states +% - P1 [N by N by 2] one-step ahead forecast error variance at t-1:t +% - data_index: [cell] 1*2 cell of column vectors of indices. +% - Z [N_obs ny N] Selector matrix +% - v [N_obs by 2] prediction error on observables at t-1:t +% - Y: [N_obs by 2] observations at t-1:t +% - H [N_obs by 1] vector of measurement error +% - QQQ [N_exo by N_exo by 3] covariance matrix of shocks at t-1:t+1 +% - T0 [N by N] initial state transition matrix +% - R0 [N by N_exo] initial shock impact transition matrix +% - TT [N by N by 2] state transition matrix at t-1:t +% - RR [N by N_exo by 2] shock impact matrix at t-1:t +% - CC [N by 2] state space constant state transition matrix at t-1:t +% - regimes0 [structure] regime info at t-1:t +% - M_ [structure] Matlab's structure describing the model (M_). +% - oo_ [structure] Matlab's structure containing the results (oo_). +% - options_ [structure] Matlab's structure describing the current options (options_). +% - occbin_options_ [structure] Matlab's structure describing the Occbin options. +% - kalman_tol [double] tolerance for reciprocal condition number +% +% Outputs +% - a [N by 2] t-1's state estimate +% - a1 [N by 2] state predictions made at t-1:t +% - P [N by N by 2] t-1's covariance of states +% - P1 [N by N by 2] one-step ahead forecast error variance at t-1:t +% - v [N_obs by 2] prediction error on observables at t-1:t +% - T [N by N by 2] state transition matrix at t-1:t +% - R [N by N_exo by 2] shock impact matrix at t-1:t +% - C [N by 2] state space constant state transition matrix at t-1:t +% - regimes_ [structure] regime info at t-1:t +% - error_flag [integer] error code +% - M_ [structure] Matlab's structure describing the model (M_). +% - lik [double] likelihood +% - etahat: smoothed shocks +% +% Notes: The algorithm and implementation is based on Massimo Giovannini, +% Philipp Pfeiffer, Marco Ratto (2021), Efficient and robust inference of models with occasionally binding +% constraints, Working Papers 2021-03, Joint Research Centre, European Commission + +% Copyright (C) 2021 Dynare Team +% +% This file is part of Dynare. +% +% Dynare is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% Dynare is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with Dynare. If not, see . + +warning off + +sto.a=a; +sto.a1=a1; +sto.P=P; +sto.P1=P1; + +number_constr = length(M_.occbin.constraint); +base_regime = struct(); +if number_constr==1 + base_regime.regime = 0; + base_regime.regimestart = 1; +else + base_regime.regime1 = 0; + base_regime.regimestart1 = 1; + base_regime.regime2 = 0; + base_regime.regimestart2 = 1; +end + +mm=size(a,1); +%% store info in t=1 +t=1; +di = data_index{t}; +T = TT(:,:,t); +ZZ = Z(di,:); +di = data_index{t}; +F = ZZ*P1(:,:,t)*ZZ' + H(di,di); +Fi(di,di,t)=F; +sig=sqrt(diag(F)); +iF(di,di,t) = inv(F./(sig*sig'))./(sig*sig'); +PZI = P1(:,:,t)*ZZ'*iF(di,di,t); +% K(:,di,t) = T*PZI; +% L(:,:,t) = T-K(:,di,t)*ZZ; +L(:,:,t) = eye(mm)-PZI*ZZ; + +if ~options_.occbin.filter.use_relaxation + [a, a1, P, P1, v, alphahat, etahat, lik] = occbin_kalman_update0(a,a1,P,P1,data_index,Z,v,Y,H,QQQ,TT,RR,CC,iF,L,mm, options_.rescale_prediction_error_covariance, options_.occbin.likelihood.IF_likelihood); +else + [~,~,~,~,~,~, TTx, RRx, CCx] ... + = occbin.dynare_resolve(M_,options_,oo_, base_regime,'reduced_state_space',T0,R0); + TT(:,:,2) = TTx(:,:,end); + RR(:,:,2) = RRx(:,:,end); + CC(:,2) = CCx(:,end); + [a, a1, P, P1, v, alphahat, etahat, lik] = occbin_kalman_update0(a,a1,P,P1,data_index,Z,v,Y,H,QQQ,TT,RR,CC,iF,L,mm, options_.rescale_prediction_error_covariance, options_.occbin.likelihood.IF_likelihood); + regimes0(1)=base_regime; +end + + +%% run here the occbin simul +opts_simul = occbin_options.opts_simul; +opts_simul.SHOCKS = zeros(3,M_.exo_nbr); +opts_simul.exo_pos=1:M_.exo_nbr; +opts_simul.SHOCKS(1,:) = etahat(:,2)'; +if opts_simul.restrict_state_space + tmp=zeros(M_.endo_nbr,1); + tmp(oo_.dr.restrict_var_list,1)=alphahat(:,1); + opts_simul.endo_init = tmp(oo_.dr.inv_order_var,1); + my_order_var = oo_.dr.order_var(oo_.dr.restrict_var_list); +else + opts_simul.endo_init = alphahat(oo_.dr.inv_order_var,1); + my_order_var = oo_.dr.order_var; +end +options_.occbin.simul=opts_simul; +[~, out, ss] = occbin.solver(M_,oo_,options_); + +regimes_ = out.regime_history; +if number_constr==1 + myregime = [regimes_.regime]; +else + myregime = [regimes_.regime1 regimes_.regime2]; +end +etahat_hist = {etahat}; +regime_hist = {regimes0(1)}; +if number_constr==1 + regime_end = regimes0(1).regimestart(end); +end +lik_hist=lik; +niter=1; +is_periodic=0; +if options_.occbin.filter.use_relaxation || isequal(regimes0(1),base_regime) + nguess=1; +else + nguess=0; +end +newguess=0; + +if any(myregime) || ~isequal(regimes_(1),regimes0(1)) + while ~isequal(regimes_(1),regimes0(1)) && ~is_periodic && ~out.error_flag && niter<=options_.occbin.likelihood.max_number_of_iterations + niter=niter+1; + oldstart=1; + if number_constr==1 && length(regimes0(1).regimestart)>1 + oldstart = regimes0(1).regimestart(end); + end + newstart=1; + if number_constr==1 && length(regimes_(1).regimestart)>1 + newstart = regimes_(1).regimestart(end); + end + if number_constr==1 && (newstart-oldstart)>2 && options_.occbin.filter.use_relaxation + regimestart = max(oldstart+2,round(0.5*(newstart+oldstart))); + regimestart = min(regimestart,oldstart+4); + if regimestart<=regimes_(1).regimestart(end-1) + if length(regimes_(1).regimestart)<=3 + regimestart = max(regimestart, min(regimes_(1).regimestart(end-1)+2,newstart)); + else + regimes_(1).regime = regimes_(1).regime(1:end-2); + regimes_(1).regimestart = regimes_(1).regimestart(1:end-2); + regimestart = max(regimestart, regimes_(1).regimestart(end-1)+1); + end + end + regimes_(1).regimestart(end)=regimestart; + [~,~,~,~,~,~, TTx, RRx, CCx] ... + = occbin.dynare_resolve(M_,options_,oo_, [base_regime regimes_(1)],'reduced_state_space', T0, R0); + TT(:,:,2) = TTx(:,:,end); + RR(:,:,2) = RRx(:,:,end); + CC(:,2) = CCx(:,end); + elseif newguess==0 + TT(:,:,2)=ss.T(my_order_var,my_order_var,1); + RR(:,:,2)=ss.R(my_order_var,:,1); + CC(:,2)=ss.C(my_order_var,1); + end + newguess=0; + regime_hist(niter) = {regimes_(1)}; + if number_constr==1 + regime_end(niter) = regimes_(1).regimestart(end); + end + [a, a1, P, P1, v, alphahat, etahat, lik] = occbin_kalman_update0(a,a1,P,P1,data_index,Z,v,Y,H,QQQ,TT,RR,CC,iF,L,mm, options_.rescale_prediction_error_covariance, options_.occbin.likelihood.IF_likelihood); + etahat_hist(niter) = {etahat}; + lik_hist(niter) = lik; + opts_simul.SHOCKS(1,:) = etahat(:,2)'; + if opts_simul.restrict_state_space + tmp=zeros(M_.endo_nbr,1); + tmp(oo_.dr.restrict_var_list,1)=alphahat(:,1); + opts_simul.endo_init = tmp(oo_.dr.inv_order_var,1); + else + opts_simul.endo_init = alphahat(oo_.dr.inv_order_var,1); + end +% opts_simul.init_regime=regimes_(1); + if number_constr==1 + myregimestart = [regimes_.regimestart]; + else + myregimestart = [regimes_.regimestart1 regimes_.regimestart2]; + end + opts_simul.periods = max(opts_simul.periods,max(myregimestart)); + options_.occbin.simul=opts_simul; + [~, out, ss] = occbin.solver(M_,oo_,options_); + regimes0=regimes_; + regimes_ = out.regime_history; + if niter>1 + for kiter=1:niter-1 + is_periodic(kiter) = isequal(regime_hist{kiter}, regimes_(1)); + end + is_periodic = any(is_periodic); + if is_periodic + if nguess<3 && number_constr==1 + newguess=1; + is_periodic=0; + nguess=nguess+1; + if nguess==1 + % change starting regime + regimes_(1).regime=0; + regimes_(1).regimestart=1; + elseif nguess==2 + % change starting regime + regimes_(1).regime=[0 1 0]; + regimes_(1).regimestart=[1 2 3]; + else + regimes_(1).regime=[1 0]; + regimes_(1).regimestart=[1 2]; + end + [~,~,~,~,~,~, TTx, RRx, CCx] ... + = occbin.dynare_resolve(M_,options_,oo_, [base_regime regimes_(1)],'reduced_state_space',T0,R0); + TT(:,:,2) = TTx(:,:,end); + RR(:,:,2) = RRx(:,:,end); + CC(:,2) = CCx(:,end); + regime_hist = regime_hist(1); + niter=1; + else + % re-set to previous regime + regimes_ = regimes0; + % force projection conditional on previous regime + opts_simul.init_regime=regimes0(1); + if number_constr==1 + myregimestart = [regimes0.regimestart]; + else + myregimestart = [regimes0.regimestart1 regimes0.regimestart2]; + end + opts_simul.periods = max(opts_simul.periods,max(myregimestart)); + opts_simul.maxit=1; + options_.occbin.simul=opts_simul; + [~, out, ss] = occbin.solver(M_,oo_,options_); + end + end + end + end +end + +error_flag = out.error_flag; +if ~error_flag && niter>options_.occbin.likelihood.max_number_of_iterations && ~isequal(regimes_(1),regimes0(1)) + error_flag = 1; + if number_constr==1 % try some other regime + [ll, il]=sort(lik_hist); + [ll, il]=sort(regime_end); + rr=regime_hist(il(2:3)); + newstart=1; + if length(rr{1}.regimestart)>1 + newstart = rr{1}.regimestart(end)-rr{1}.regimestart(end-1)+1; + end + oldstart=1; + if length(rr{2}.regimestart)>1 + oldstart = rr{2}.regimestart(end)-rr{2}.regimestart(end-1)+1; + end + nstart=sort([newstart oldstart]); + regimes_=rr{1}(1); + for k=(nstart(1)+1):(nstart(2)-1) + niter=niter+1; + regimes_(1).regimestart(end)=k; + + [~,~,~,~,~,~, TTx, RRx, CCx] ... + = occbin.dynare_resolve(M_,options_,oo_, [base_regime regimes_(1)],'reduced_state_space',T0,R0); + TT(:,:,2) = TTx(:,:,end); + RR(:,:,2) = RRx(:,:,end); + CC(:,2) = CCx(:,end); + [a, a1, P, P1, v, alphahat, etahat, lik] = occbin_kalman_update0(a,a1,P,P1,data_index,Z,v,Y,H,QQQ,TT,RR,CC,iF,L,mm, options_.rescale_prediction_error_covariance, options_.occbin.likelihood.IF_likelihood); + etahat_hist(niter) = {etahat}; + lik_hist(niter) = lik; + regime_hist(niter) = {regimes_(1)}; + opts_simul.SHOCKS(1,:) = etahat(:,2)'; + if opts_simul.restrict_state_space + tmp=zeros(M_.endo_nbr,1); + tmp(oo_.dr.restrict_var_list,1)=alphahat(:,1); + opts_simul.endo_init = tmp(oo_.dr.inv_order_var,1); + else + opts_simul.endo_init = alphahat(oo_.dr.inv_order_var,1); + end + % opts_simul.init_regime=regimes_(1); + if number_constr==1 + myregimestart = [regimes_.regimestart]; + else + myregimestart = [regimes_.regimestart1 regimes_.regimestart2]; + end + opts_simul.periods = max(opts_simul.periods,max(myregimestart)); + options_.occbin.simul=opts_simul; + [~, out, ss] = occbin.solver(M_,oo_,options_); + if isequal(out.regime_history(1),regimes_(1)) + error_flag=0; + break + end + end + regimes_ = out.regime_history; + end +end + +a = out.piecewise(1:2,my_order_var)' - repmat(out.ys(my_order_var),1,2); +T = ss.T(my_order_var,my_order_var,1:2); +R = ss.R(my_order_var,:,1:2); +C = ss.C(my_order_var,1:2); +QQ = R(:,:,2)*QQQ(:,:,3)*transpose(R(:,:,2)); +P(:,:,1) = P(:,:,2); +P(:,:,2) = T(:,:,2)*P(:,:,1)*transpose(T(:,:,2))+QQ; +% P = cat(3,P(:,:,2),P2); +regimes_=regimes_(1:3); +etahat=etahat(:,2); + +warning on +end + +function [a, a1, P, P1, v, alphahat, etahat, lik] = occbin_kalman_update0(a,a1,P,P1,data_index,Z,v,Y,H,QQQ,TT,RR,CC,iF,L,mm, rescale_prediction_error_covariance, IF_likelihood) +warning off +if nargin<18 + IF_likelihood=0; +end +t=2; +%% forward pass +% given updated variables and covarnace in t=1, we make the step to t=2 +T = TT(:,:,t); +R = RR(:,:,t); +C = CC(:,t); +Q=QQQ(:,:,t); +QQ = R*Q*transpose(R); +a1(:,t) = T*a(:,t-1)+C; +a(:,t) = a1(:,t); +P1(:,:,t) = T*P(:,:,t-1)*T' + QQ; %transition according to (6.14) in DK (2012) +P(:,:,t) = P1(:,:,t); + +di = data_index{t}; +if isempty(di) + a(:,t) = a1(:,t); + L(:,:,t) = eye(mm); + P1(:,:,t+1) = T*P(:,:,t)*T' + QQ; %p. 111, DK(2012) +else + ZZ = Z(di,:); + v(di,t) = Y(di,t) - ZZ*a(:,t); + F = ZZ*P(:,:,t)*ZZ' + H(di,di); + sig=sqrt(diag(F)); + if rank(F) 1 + t = t-1; + di = data_index{t}; + if isempty(di) + % in this case, L is simply T due to Z=0, so that DK (2012), eq. 4.93 obtains + r(:,t) = L(:,:,t)'*r(:,t+1); %compute r_{t-1}, DK (2012), eq. 4.38 with Z=0 + else + ZZ = Z(di,:); + r(:,t) = ZZ'*iF(di,di,t)*v(di,t) + L(:,:,t)'*r(:,t+1); %compute r_{t-1}, DK (2012), eq. 4.38 + end + Q=QQQ(:,:,t); + QRt = Q*transpose(RR(:,:,t)); + T = TT(:,:,t); + alphahat(:,t) = a1(:,t) + P1(:,:,t)*r(:,t); %DK (2012), eq. 4.35 + etahat(:,t) = QRt*r(:,t); %DK (2012), eq. 4.63 + r(:,t) = T'*r(:,t); % KD (2003), eq. (23), equation for r_{t-1,p_{t-1}} + + if IF_likelihood && t==2 && not(isempty(di)) + ishocks = any(ZZ*RR(:,:,t)); + ishocks(find(etahat(ishocks,t)==0))=false; + Gmat1 = ZZ*RR(:,ishocks,t); + if size(Gmat1,1) == size(Gmat1,2) + log_det_jacobian = log(det(Q(ishocks,ishocks))) + 2*log(abs(det(Gmat1))); + trace_term = etahat(ishocks,t)'*(Q(ishocks,ishocks)\etahat(ishocks,t)); + + lik = log_det_jacobian + trace_term + length(di)*log(2*pi); + else + lik = inf; + end + + end +end + +warning on +end diff --git a/matlab/+occbin/kalman_update_algo_3.m b/matlab/+occbin/kalman_update_algo_3.m new file mode 100644 index 000000000..83de87ced --- /dev/null +++ b/matlab/+occbin/kalman_update_algo_3.m @@ -0,0 +1,392 @@ +function [a, a1, P, P1, v, Fi, Ki, T, R, C, regimes_, error_flag, M_, alphahat, etahat, TT, RR, CC] = kalman_update_algo_3(a,a1,P,P1,data_index,Z,v,Fi,Ki,Y,H,QQQ,T0,R0,TT,RR,CC,regimes0,M_,oo_,options_,occbin_options,kalman_tol,nk) +% function [a, a1, P, P1, v, Fi, Ki, T, R, C, regimes_, error_flag, M_, alphahat, etahat, TT, RR, CC] = kalman_update_algo_3(a,a1,P,P1,data_index,Z,v,Fi,Ki,Y,H,QQQ,T0,R0,TT,RR,CC,regimes0,M_,oo_,options_,occbin_options,kalman_tol,nk) +% +% INPUTS +% - a [N by 1] t-1's state estimate +% - a1 [N by N by 2] state predictions made at t-1:t +% - P [N by N] t-1's covariance of states +% - P1 [N by N by 2] one-step ahead forecast error variance at t-1:t +% - data_index: [cell] 1*2 cell of column vectors of indices. +% - Z [N_obs ny N] Selector matrix +% - v [N_obs by 2] prediction error on observables at t-1:t +% - Fi [N_obs by 1] F_i matrix +% - Ki [N by N_obs] Kalman gain matrix +% - Y: [N_obs by 2] observations at t-1:t +% - H [N_obs by 1] vector of measurement error +% - QQQ [N_exo by N_exo by 3] covariance matrix of shocks at t-1:t+1 +% - T0 [N by N] initial state transition matrix +% - R0 [N by N_exo] initial shock impact transition matrix +% - TT [N by N by 2] state transition matrix at t-1:t +% - RR [N by N_exo by 2] shock impact matrix at t-1:t +% - CC [N by 2] state space constant state transition matrix at t-1:t +% - regimes0 [structure] regime info at t-1:t +% - M_ [structure] Matlab's structure describing the model (M_). +% - options_ [structure] Matlab's structure describing the current options (options_). +% - oo_ [structure] Matlab's structure containing the results (oo_). +% - occbin_options_ [structure] Matlab's structure describing the Occbin options. +% - kalman_tol [double] tolerance for reciprocal condition number +% - nk [double] number of forecasting periods +% +% Outputs +% - a [N by 2] t-1's state estimate +% - a1 [N by N by 2] state predictions made at t-1:t +% - P [N by N by 2] t-1's covariance of states +% - P1 [N by N by 2] one-step ahead forecast error variance at t-1:t +% - v [N_obs by 2] prediction error on observables at t-1:t +% - Fi [N_obs by 2] F_i matrix +% - Ki [N by N_obs by 2] Kalman gain matrix +% - TT [N by N by 2] state transition matrix at t-1:t +% - RR [N by N_exo by 2] shock impact matrix at t-1:t +% - CC [N by 2] state space constant state transition matrix at t-1:t +% - regimes_ [structure] regime info at t-1:t +% - error_flag [structure] error flag +% - M_ [structure] Matlab's structure describing the model (M_). +% - alphahat: smoothed variables (a_{t|T}) +% - etahat: smoothed shocks +% - TT [N by N by 2] state transition matrix at t-1:t +% - RR [N by N_exo by 2] shock impact matrix at t-1:t +% - CC [N by 2] state space constant state transition matrix at t-1:t +% +% Notes: The algorithm and implementation is based on Massimo Giovannini, +% Philipp Pfeiffer, Marco Ratto (2021), Efficient and robust inference of models with occasionally binding +% constraints, Working Papers 2021-03, Joint Research Centre, European Commission + + +% Copyright (C) 2021 Dynare Team +% +% This file is part of Dynare. +% +% Dynare is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% Dynare is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with Dynare. If not, see . + +if isempty(nk) + nk=1; +end +nk=max(nk,1); + +number_constr = length(M_.occbin.constraint); +opts_simul = occbin_options.opts_regime;base_regime = struct(); +if number_constr==1 + base_regime.regime = 0; + base_regime.regimestart = 1; +else + base_regime.regime1 = 0; + base_regime.regimestart1 = 1; + base_regime.regime2 = 0; + base_regime.regimestart2 = 1; +end +myrestrict=[]; +if options_.smoother_redux + opts_simul.restrict_state_space =1; + myrestrict='restrict'; +end + +mm=size(a,1); +if ~options_.occbin.filter.use_relaxation + [a, a1, P, P1, v, Fi, Ki, alphahat, etahat] = occbin_kalman_update(a,a1,P,P1,data_index,Z,v,Y,H,QQQ,TT,RR,CC,Ki,Fi,mm,kalman_tol); +else + [~,~,~,~,~,~, TTx, RRx, CCx] ... + = occbin.dynare_resolve(M_,options_,oo_, base_regime,myrestrict,T0,R0); + TT(:,:,2) = TTx(:,:,end); + RR(:,:,2) = RRx(:,:,end); + CC(:,2) = CCx(:,end); + [a, a1, P, P1, v, Fi, Ki, alphahat, etahat] = occbin_kalman_update(a,a1,P,P1,data_index,Z,v,Y,H,QQQ,TT,RR,CC,Ki,Fi,mm,kalman_tol); + regimes0(1)=base_regime; +end + + +%% run here the occbin simul +opts_simul.SHOCKS = zeros(max(3,1+nk),M_.exo_nbr); +opts_simul.SHOCKS(1,:) = etahat(:,2)'; +opts_simul.exo_pos=1:M_.exo_nbr; + +if opts_simul.restrict_state_space + tmp=zeros(M_.endo_nbr,1); + tmp(oo_.dr.restrict_var_list,1)=alphahat(:,1); + opts_simul.endo_init = tmp(oo_.dr.inv_order_var,1); + my_order_var = oo_.dr.order_var(oo_.dr.restrict_var_list); +else + opts_simul.endo_init = alphahat(oo_.dr.inv_order_var,1); + my_order_var = oo_.dr.order_var; +end + +options_.occbin.simul=opts_simul; +[~, out, ss] = occbin.solver(M_,oo_,options_); + +regimes_ = out.regime_history; +if number_constr==1 + myregime = [regimes_.regime]; +else + myregime = [regimes_.regime1 regimes_.regime2]; +end +regime_hist = {regimes0}; +if number_constr==1 + regime_end = regimes0(1).regimestart(end); +end +niter=1; +is_periodic=0; +if options_.occbin.filter.use_relaxation || isequal(regimes0(1),base_regime) + nguess=1; +else + nguess=0; +end +newguess=0; + +if any(myregime) || ~isequal(regimes_(1),regimes0(1)) + while ~isequal(regimes_(1),regimes0(1)) && ~is_periodic && ~out.error_flag && niter<=options_.occbin.likelihood.max_number_of_iterations + niter=niter+1; + newstart=1; + if number_constr==1 && length(regimes_(1).regimestart)>1 + newstart = regimes_(1).regimestart(end); + end + oldstart=1; + if number_constr==1 && length(regimes0(1).regimestart)>1 + oldstart = regimes0(1).regimestart(end); + end + if number_constr==1 && (newstart-oldstart)>2 && options_.occbin.filter.use_relaxation + regimestart = max(oldstart+2,round(0.5*(newstart+oldstart))); + regimestart = min(regimestart,oldstart+4); + if regimestart<=regimes_(1).regimestart(end-1) + if length(regimes_(1).regimestart)<=3 + regimestart = max(regimestart, min(regimes_(1).regimestart(end-1)+2,newstart)); + else + regimes_(1).regime = regimes_(1).regime(1:end-2); + regimes_(1).regimestart = regimes_(1).regimestart(1:end-2); + regimestart = max(regimestart, regimes_(1).regimestart(end-1)+1); + end + end + + % % if (newstart-oldstart)>3 + % % regimestart = regimes_(1).regimestart(end-1)+oldstart+2; + % % % regimestart = regimes_(1).regimestart(end-1)+round(0.5*(newstart+oldstart))-1; + regimes_(1).regimestart(end)=regimestart; + [~,~,~,~,~,~, TTx, RRx, CCx] ... + = occbin.dynare_resolve(M_,options_,oo_, [base_regime regimes_(1)],myrestrict,T0,R0); + TT(:,:,2) = TTx(:,:,end); + RR(:,:,2) = RRx(:,:,end); + CC(:,2) = CCx(:,end); + elseif newguess==0 + TT(:,:,2)=ss.T(my_order_var,my_order_var,1); + RR(:,:,2)=ss.R(my_order_var,:,1); + CC(:,2)=ss.C(my_order_var,1); + end + newguess=0; + regime_hist(niter) = {regimes_}; + if number_constr==1 + regime_end(niter) = regimes_(1).regimestart(end); + end + [a, a1, P, P1, v, Fi, Ki, alphahat, etahat] = occbin_kalman_update(a,a1,P,P1,data_index,Z,v,Y,H,QQQ,TT,RR,CC,Ki,Fi,mm,kalman_tol); + opts_simul.SHOCKS(1,:) = etahat(:,2)'; + % if opts_simul.restrict_state_space + % tmp=zeros(M_.endo_nbr,1); + % tmp(oo_.dr.restrict_var_list,1)=alphahat(:,1); + % opts_simul.endo_init = tmp(oo_.dr.inv_order_var,1); + % else + if opts_simul.restrict_state_space + tmp=zeros(M_.endo_nbr,1); + tmp(oo_.dr.restrict_var_list,1)=alphahat(:,1); + opts_simul.endo_init = tmp(oo_.dr.inv_order_var,1); + else + opts_simul.endo_init = alphahat(oo_.dr.inv_order_var,1); + end + % end + % opts_simul.init_regime=regimes_(1); %% why don't we use this ??? + if number_constr==1 + myregimestart = [regimes_.regimestart]; + else + myregimestart = [regimes_.regimestart1 regimes_.regimestart2]; + end + opts_simul.periods = max(opts_simul.periods,max(myregimestart)); + options_.occbin.simul=opts_simul; + [~, out, ss] = occbin.solver(M_,oo_,options_); + regimes0=regimes_; + regimes_ = out.regime_history; + if niter>1 + for kiter=1:niter-1 + is_periodic(kiter) = isequal(regime_hist{kiter}, regimes_); + end + is_periodic = any(is_periodic); + if is_periodic + if nguess<3 && number_constr==1 + newguess=1; + is_periodic=0; + nguess=nguess+1; + if nguess==1 + % change starting regime + regimes_(1).regime=0; + regimes_(1).regimestart=1; + elseif nguess==2 + % change starting regime + regimes_(1).regime=[0 1 0]; + regimes_(1).regimestart=[1 2 3]; + else + regimes_(1).regime=[1 0]; + regimes_(1).regimestart=[1 2]; + end + [~,~,~,~,~,~, TTx, RRx, CCx] ... + = occbin.dynare_resolve(M_,options_,oo_, [base_regime regimes_(1)],myrestrict,T0,R0); + TT(:,:,2) = TTx(:,:,end); + RR(:,:,2) = RRx(:,:,end); + CC(:,2) = CCx(:,end); + regime_hist = regime_hist(1); + niter=1; + else + % re-set to previous regime + regimes_ = regimes0; + % force projection conditional on previous regime + opts_simul.init_regime=regimes0(1); + if number_constr==1 + myregimestart = [regimes0.regimestart]; + else + myregimestart = [regimes0.regimestart1 regimes0.regimestart2]; + end + opts_simul.periods = max(opts_simul.periods,max(myregimestart)); + opts_simul.maxit=1; + options_.occbin.simul=opts_simul; + [~, out, ss] = occbin.solver(M_,oo_,options_); + end + end + end + end +end + +error_flag = out.error_flag; +if error_flag==0 && niter>options_.occbin.likelihood.max_number_of_iterations && ~isequal(regimes_(1),regimes0(1)) %fixed point algorithm did not converge + error_flag = 1; + + if number_constr==1 + % try some other regime before giving up + [ll, il]=sort(regime_end); + rr=regime_hist(il(2:3)); + newstart=1; + if length(rr{1}(1).regimestart)>1 + newstart = rr{1}(1).regimestart(end)-rr{1}(1).regimestart(end-1)+1; + end + oldstart=1; + if length(rr{2}(1).regimestart)>1 + oldstart = rr{2}(1).regimestart(end)-rr{2}(1).regimestart(end-1)+1; + end + nstart=sort([newstart oldstart]); + regimes_=rr{1}(1); + for k=(nstart(1)+1):(nstart(2)-1) + niter=niter+1; + regimes_(1).regimestart(end)=k; + + [~,~,~,~,~,~, TTx, RRx, CCx] ... + = occbin.dynare_resolve(M_,options_,oo_, [base_regime regimes_(1)],myrestrict,T0,R0); + TT(:,:,2) = TTx(:,:,end); + RR(:,:,2) = RRx(:,:,end); + CC(:,2) = CCx(:,end); + [a, a1, P, P1, v, Fi, Ki, alphahat, etahat] = occbin_kalman_update(a,a1,P,P1,data_index,Z,v,Y,H,QQQ,TT,RR,CC,Ki,Fi,mm,kalman_tol); + opts_simul.SHOCKS(1,:) = etahat(:,2)'; + opts_simul.endo_init = alphahat(oo_.dr.inv_order_var,1); + if number_constr==1 + myregimestart = [regimes_.regimestart]; + else + myregimestart = [regimes_.regimestart1 regimes_.regimestart2]; + end + opts_simul.periods = max(opts_simul.periods,max(myregimestart)); + options_.occbin.simul=opts_simul; + [~, out, ss] = occbin.solver(M_,oo_,options_); + if isequal(out.regime_history(1),regimes_(1)) + error_flag=0; + break + end + end + regimes_ = out.regime_history; + end +end + +regimes_=regimes_(1:3); +a = out.piecewise(1:nk+1,my_order_var)' - repmat(out.ys(my_order_var),1,nk+1); +T = ss.T(my_order_var,my_order_var,:); +R = ss.R(my_order_var,:,:); +C = ss.C(my_order_var,:); +TT = ss.T(oo_.dr.order_var,oo_.dr.order_var,1); +RR = ss.R(oo_.dr.order_var,:,1); +CC = ss.C(oo_.dr.order_var,1); +QQ = R(:,:,2)*QQQ(:,:,3)*transpose(R(:,:,2)); +P(:,:,1) = P(:,:,2); +for j=1:nk + P(:,:,j+1) = T(:,:,j+1)*P(:,:,j)*transpose(T(:,:,j+1))+QQ; +end +% P = cat(3,P(:,:,2),P2); + + +end + +function [a, a1, P, P1, v, Fi, Ki, alphahat, etahat] = occbin_kalman_update(a,a1,P,P1,data_index,Z,v,Y,H,QQQ,TT,RR,CC,Ki,Fi,mm,kalman_tol) +% [a, a1, P, P1, v, Fi, Ki, alphahat, etahat] = occbin_kalman_update(a,a1,P,P1,data_index,Z,v,Y,H,QQQ,TT,RR,CC,Ki,Fi,mm,kalman_tol) +% - a +% - a1 +% - P +% - P1 +% - v +% - Fi +% - Ki + + +t=2; + +%% forward pass +% given updated variables and covariance in t=1, we make the step to t=2 +T = TT(:,:,t); +R = RR(:,:,t); +C = CC(:,t); +Q=QQQ(:,:,t); +QQ = R*Q*transpose(R); +a1(:,t) = T*a(:,t-1)+C; +a(:,t) = a1(:,t); +P1(:,:,t) = T*P(:,:,t-1)*T' + QQ; %transition according to (6.14) in DK (2012) +P(:,:,t) = P1(:,:,t); +di = data_index{t}'; +if isempty(di) + Fi(:,t) = 0; + Ki(:,:,t) = 0; +end +for i=di + Zi = Z(i,:); + v(i,t) = Y(i,t) - Zi*a(:,t); % nu_{t,i} in 6.13 in DK (2012) + Fi(i,t) = Zi*P(:,:,t)*Zi' + H(i); % F_{t,i} in 6.13 in DK (2012), relies on H being diagonal + Ki(:,i,t) = P(:,:,t)*Zi'; % K_{t,i}*F_(i,t) in 6.13 in DK (2012) + if Fi(i,t) > kalman_tol + a(:,t) = a(:,t) + Ki(:,i,t)*v(i,t)/Fi(i,t); %filtering according to (6.13) in DK (2012) + P(:,:,t) = P(:,:,t) - Ki(:,i,t)*Ki(:,i,t)'/Fi(i,t); %filtering according to (6.13) in DK (2012) + else + % do nothing as a_{t,i+1}=a_{t,i} and P_{t,i+1}=P_{t,i}, see + % p. 157, DK (2012) + end +end + +%% do backward pass +ri=zeros(mm,1); +t = t+1; +while t > 1 + t = t-1; + di = flipud(data_index{t})'; + for i = di + if Fi(i,t) > kalman_tol + Li = eye(mm)-Ki(:,i,t)*Z(i,:)/Fi(i,t); + ri = Z(i,:)'/Fi(i,t)*v(i,t)+Li'*ri; % DK (2012), 6.15, equation for r_{t,i-1} + end + end + r(:,t) = ri; % DK (2012), below 6.15, r_{t-1}=r_{t,0} + alphahat(:,t) = a1(:,t) + P1(:,:,t)*r(:,t); + Q=QQQ(:,:,t); + QRt = Q*transpose(RR(:,:,t)); + T = TT(:,:,t); + etahat(:,t) = QRt*r(:,t); + ri = T'*ri; % KD (2003), eq. (23), equation for r_{t-1,p_{t-1}} +end + +end \ No newline at end of file diff --git a/matlab/+occbin/make_chart.m b/matlab/+occbin/make_chart.m new file mode 100644 index 000000000..cc1848ccc --- /dev/null +++ b/matlab/+occbin/make_chart.m @@ -0,0 +1,67 @@ +function make_chart(titlelist,legendlist,figlabel,ylabels,data_series) +% function make_chart(titlelist,legendlist,figlabel,ylabels,zdata) + +% Original authors: Luca Guerrieri and Matteo Iacoviello +% Original file downloaded from: +% https://www.matteoiacoviello.com/research_files/occbin_20140630.zip +% Adapted for Dynare by Dynare Team. +% +% This code is in the public domain and may be used freely. +% However the authors would appreciate acknowledgement of the source by +% citation of any of the following papers: +% +% Luca Guerrieri and Matteo Iacoviello (2015): "OccBin: A toolkit for solving +% dynamic models with occasionally binding constraints easily" +% Journal of Monetary Economics 70, 22-38 + +titlelist = char(strrep(cellstr(titlelist),'_','.')); + +ndsets=size(data_series,3); % default, changed below as applicable +nperiods = size(data_series,1); + +xvalues = (1:nperiods)'; +nvars = size(titlelist,1); + +[nbplt,nr,nc,lr,lc,nstar] = pltorg(nvars); + +style_cell={'k','r','b','g','c','y'}; + +for fig = 1:nbplt + figure('Name',[figlabel, ', Figure ' int2str(fig)]); + for plt = 1:nstar + h1=NaN(ndsets); + if fig==nbplt && ~lr==0 + subplot(lr,lc,plt); + else + subplot(nr,nc,plt); + end + for data_set_iter=1:ndsets + h1(data_set_iter)=plot(xvalues,data_series(:,(fig-1)*nstar+plt,data_set_iter),style_cell{1+mod(data_set_iter-1,length(style_cell))},'linewidth',2); + hold on + end + grid on + + max_y = max(max(data_series(:,(fig-1)*nstar+plt,:))); + min_y = min(min(data_series(:,(fig-1)*nstar+plt,:))); + + y_bottom = min_y - .01*abs(min_y); + + y_top = max_y + 0.01*abs(max_y); + if y_bottom==y_top + y_top=y_bottom+1; + end + + axis([1 nperiods y_bottom y_top]) + if plt==1 + if numel(strvcat(legendlist(1,:))) + h=legend(legendlist,'Location','Northwest','Fontsize',8); + end + end + + title(titlelist(plt,:),'Fontsize',11); + ylabel(ylabels(plt,:)) + if nvars==(fig-1)*nstar+plt + break + end + end +end \ No newline at end of file diff --git a/matlab/+occbin/map_regime.m b/matlab/+occbin/map_regime.m new file mode 100644 index 000000000..0a8cb651f --- /dev/null +++ b/matlab/+occbin/map_regime.m @@ -0,0 +1,49 @@ +function [regime, regime_start, error_flag]=map_regime(binding_indicator,debug_switch) +% function [regime, regime_start, error_flag]=map_regime(binding_indicator) +% Map regime indicator into information +% +% Inputs: +% - binding_indicator [integer] [nperiods by 1] vector of regime indices +% - debug_switch [boolean] indicator for printing warnings +% +% Outputs: +% - regime [integer] [1 by n_transitions] vector of regime number indices +% - regime_start [integer] [1 by n_transitions] vectors with period numbers in which regime starts +% - error_flag [boolean] 1 if regime never leaves 1 or is still there at the end of nperiods +% 0 otherwise + +% Original authors: Luca Guerrieri and Matteo Iacoviello +% Original file downloaded from: +% https://www.matteoiacoviello.com/research_files/occbin_20140630.zip +% Adapted for Dynare by Dynare Team. +% +% This code is in the public domain and may be used freely. +% However the authors would appreciate acknowledgement of the source by +% citation of any of the following papers: +% +% Luca Guerrieri and Matteo Iacoviello (2015): "OccBin: A toolkit for solving +% dynamic models with occasionally binding constraints easily" +% Journal of Monetary Economics 70, 22-38 + +error_flag=0; +% analyse violvec and isolate contiguous periods in the other regime. +regime(1) = binding_indicator(1); +regime_index = 1; +regime_start(1) = 1; +for i=2:length(binding_indicator) + if binding_indicator(i)~=regime(regime_index) + regime_index=regime_index+1; + regime(regime_index) = binding_indicator(i); + regime_start(regime_index)=i; + end +end + +if (regime(1) == 1 && length(regime_start)==1) + disp_verbose('map_regime: Binding regime was never left. nperiods needs to be increased.',debug_switch); + error_flag=1; +end + +if (regime(end)==1) + disp_verbose('map_regime: Constraint(s) are binding at the end of the sample. nperiods needs to be increased.',debug_switch); + error_flag=1; +end diff --git a/matlab/+occbin/match_function.m b/matlab/+occbin/match_function.m new file mode 100644 index 000000000..316c24d5e --- /dev/null +++ b/matlab/+occbin/match_function.m @@ -0,0 +1,56 @@ +function [resids, grad, state_out, E, M_, out] = match_function(err_0, obs_list,current_obs, opts_simul,... + M_, oo_, options_) +% function [resids, grad, stateout, E, M_, out] = match_function(err_0, obs_list,current_obs, opts_simul,... +% M_, oo_, options_) +% Outputs: +% - resids [double] [n_exo by 1] vector of residuals +% - grad [double] [n by n_exo] gradient (response of observables to shocks) +% - state_out [double] [ny by 1] value of endogenous variables +% - E [double] response of endogenous variables to shocks +% - M_ [structure] Matlab's structure describing the model (M_). +% - out [structure] Occbin's results structure +% +% Inputs +% - err_ [double] value of shocks +% - obs_list [cell] names of observables +% - current_obs [double] [1 by n_obs] current value of observables +% - opts_simul [structure] Structure with simulation options +% - M_ [structure] Matlab's structure describing the model (M_). +% - oo_ [structure] Matlab's structure containing the results (oo_). +% - options_ [structure] Matlab's structure describing the current options (options_). + +% Original authors: Pablo Cuba-Borda, Luca Guerrieri, Matteo Iacoviello, and Molin Zhong +% Original file downloaded from: +% http://www.lguerrieri.com/jae-replication.zip +% Adapted for Dynare by Dynare Team. +% +% This code is in the public domain and may be used freely. +% However the authors would appreciate acknowledgement of the source by +% citation of any of the following papers: +% +% Pablo Cuba-Borda, Luca Guerrieri, and Matteo Iacoviello (2019): "Likelihood evaluation of models +% with occasionally binding constraints", Journal of Applied Econometrics, +% 34(7), 1073-1085 + +opts_simul.SHOCKS = err_0'; +options_.occbin.simul=opts_simul; +options_.occbin.simul.full_output=1; +options_.noprint = 1; +[~, out, ss] = occbin.solver(M_,oo_,options_); +state_out= out.piecewise(1,:)' - out.ys; + +E = ss.R(:,opts_simul.exo_pos); +grad = ss.R(opts_simul.varobs_id,opts_simul.exo_pos); + +nobs = size(obs_list,1); +resids = zeros(nobs,1); + +if ~out.error_flag + % -- add observation block in model ---% + % % put in model file + resids = (out.piecewise(1,opts_simul.varobs_id)-current_obs)'; %-out.endo_ss.(obs_list{this_obs}); +else + resids = resids+100; +end + +end diff --git a/matlab/+occbin/mkdata.m b/matlab/+occbin/mkdata.m new file mode 100644 index 000000000..017095f54 --- /dev/null +++ b/matlab/+occbin/mkdata.m @@ -0,0 +1,76 @@ +function [data_mat]=make_linear_model_data(n_periods,dr_A,dr_B,endo_names,exo_names,wish_list,shock_pos,shock_size_vec,var_init) +% function [data_mat]=make_linear_model_data(n_periods,dr_A,dr_B,endo_names,exo_names,wish_list,shock_pos,shock_size_vec,var_init) +% Inputs: +% - n_periods [integer] number of simulation periods +% - dr_A [double] [n by n] transition matrix +% - dr_B [double] [n by nexo] shock response matrix +% - endo_names [cell] name of endogenous variables +% - exo_names [cell] name of exogenous variables +% - wish_list [cell] name of requested variables for output +% - shock_pos [integer] index of shocks +% - shock_size_vec [double] [shock periods by 1] vector of +% - var_init [double] [n by 1] vector of initial values (incl. states) +% +% Outputs: +% - data_mat [double] [n_periods by n] vector of regime number indices + +% Original authors: Luca Guerrieri and Matteo Iacoviello +% Original file downloaded from: +% https://www.matteoiacoviello.com/research_files/occbin_20140630.zip +% Adapted for Dynare by Dynare Team. +% +% This code is in the public domain and may be used freely. +% However the authors would appreciate acknowledgement of the source by +% citation of any of the following papers: +% +% Luca Guerrieri and Matteo Iacoviello (2015): "OccBin: A toolkit for solving +% dynamic models with occasionally binding constraints easily" +% Journal of Monetary Economics 70, 22-38 + +% given decision rule +neqs = size(endo_names,1); + +if nargin<9 + var_init = zeros(neqs,1); +end + +if nargin<8 + shock_size_vec=1; +end + +if nargin<7 + error('Not enough inputs') +end + +history = zeros(neqs,n_periods+1); + +% generate data +% history will contain data, the state vector at each period in time will +% be stored columnwise. +history(:,1)= var_init; + +lengthshock = size(shock_size_vec,1); + +err_vec = zeros(size(exo_names,1),1); + +for i = 2:n_periods+1 + if i<=(lengthshock+1) + err_vec(shock_pos) = shock_size_vec(i-1,:); + history(:,i) = dr_A * history(:,i-1)+dr_B*err_vec; + else + % update endogenous variables + history(:,i) = dr_A * history(:,i-1); + end +end + +% extract desired variables +if ~isempty(wish_list) + n_wish=size(wish_list,1); + wish_pos = zeros(n_wish,1); + for i=1:n_wish + wish_pos(i) = strmatch(wish_list(i,:),endo_names,'exact'); + end + data_mat = history(wish_pos,2:end)'; +else + data_mat = history(:,2:end)'; +end diff --git a/matlab/+occbin/mkdatap_anticipated_2constraints_dyn.m b/matlab/+occbin/mkdatap_anticipated_2constraints_dyn.m new file mode 100644 index 000000000..379380cbc --- /dev/null +++ b/matlab/+occbin/mkdatap_anticipated_2constraints_dyn.m @@ -0,0 +1,169 @@ +function [zdata, T, R, CONST, ss, update_flag]=mkdatap_anticipated_2constraints_dyn(n_periods,DM,T_max,... + binding_indicator,irfshock_pos,scalefactor_mod,init,update_flag) +% function [zdata, T, R, CONST, ss, update_flag]=mkdatap_anticipated_2constraints_dyn(n_periods,DM,T_max,... +% binding_indicator,irfshock_pos,scalefactor_mod,init,update_flag) +% +% Inputs: +% - n_periods [double] number for periods for simulation +% - DM [structure] Dynamic model +% - T_max [Tmax] last period where constraints bind +% - binding_indicator [T+1] indicator for constraint violations +% - irfshock_pos [double] shock position +% - scalefactor_mod [double] shock values +% - init [double] [N by 1] initial value of endogenous variables +% - update_flag [boolean] flag whether to update results +% +% Output: +% - zdata [double] T+1 by N matrix of simulated data +% - T [N by N] transition matrix of state space +% - R [N by N_exo] shock impact matrix of state space +% - CONST [N by 1] constant of state space +% - ss [structure] state space system +% - update_flag [boolean] flag that results have been updated +% Original authors: Luca Guerrieri and Matteo Iacoviello +% Original file downloaded from: +% https://www.matteoiacoviello.com/research_files/occbin_20140630.zip +% Adapted for Dynare by Dynare Team. +% +% This code is in the public domain and may be used freely. +% However the authors would appreciate acknowledgement of the source by +% citation of any of the following papers: +% +% Luca Guerrieri and Matteo Iacoviello (2015): "OccBin: A toolkit for solving +% dynamic models with occasionally binding constraints easily" +% Journal of Monetary Economics 70, 22-38 + +persistent dictionary + +if update_flag + dictionary=[]; + update_flag=false; +end + +n_vars = DM.n_vars; +T = DM.decrulea; +CONST = zeros(n_vars,1); +R = DM.decruleb; + +if nargin<7 + init=zeros(n_vars,1); +end + +if nargin<6 + scalefactor_mod=1; +end + +n_exo=DM.n_exo; + +% Tmax = max([regimestart1(nregimes1) regimestart2(nregimes2)])-1; % Tmax is the position of the last period +% when the constraint binds + +if ~isempty(dictionary) + if (length(binding_indicator(:))>size(dictionary.binding_indicator,1)) + nviol_old = size(dictionary.binding_indicator,1)/2; + tmp = zeros(length(binding_indicator)-nviol_old,size(dictionary.binding_indicator,2)); + dictionary.binding_indicator = [dictionary.binding_indicator(1:nviol_old,:); tmp; dictionary.binding_indicator(1+nviol_old:2*nviol_old,:); tmp]; + end + if (length(binding_indicator(:)) 0 + + if isempty(dictionary) + tmp = [binding_indicator(T_max,:); zeros(n_periods,2)]; + dictionary.binding_indicator(:,1) = tmp(:); + if (binding_indicator(T_max,1) && ~binding_indicator(T_max,2)) + temp = -(DM.Abarmat10*DM.decrulea+DM.Bbarmat10)\[DM.Cbarmat10 DM.Jbarmat10 DM.Dbarmat10]; + dictionary.ss(1).T = temp(:,1:n_vars); + dictionary.ss(1).R = temp(:,n_vars+1:n_vars+n_exo); + dictionary.ss(1).C = temp(:,n_vars+n_exo+1:end); + elseif (binding_indicator(T_max,1) && binding_indicator(T_max,2)) + temp = -(DM.Abarmat11*DM.decrulea+DM.Bbarmat11)\[DM.Cbarmat11 DM.Jbarmat11 DM.Dbarmat11]; + dictionary.ss(1).T = temp(:,1:n_vars); + dictionary.ss(1).R = temp(:,n_vars+1:n_vars+n_exo); + dictionary.ss(1).C = temp(:,n_vars+n_exo+1:end); + else + temp = -(DM.Abarmat01*DM.decrulea+DM.Bbarmat01)\[DM.Cbarmat01 DM.Jbarmat01 DM.Dbarmat01]; + dictionary.ss(1).T = temp(:,1:n_vars); + dictionary.ss(1).R = temp(:,n_vars+1:n_vars+n_exo); + dictionary.ss(1).C = temp(:,n_vars+n_exo+1:end); + end + end + ireg(T_max)=1; + + icount=length(dictionary.ss); + + for i = T_max-1:-1:1 + tmp = 0*binding_indicator; + tmp(1:end-i+1,:) = binding_indicator(i:end,:); + itmp = find(~any(dictionary.binding_indicator(1:length(tmp)*2,:)-tmp(:))); + if ~isempty(itmp) + ireg(i) = itmp; + else + icount=icount+1; + ireg(i) = icount; + dictionary.binding_indicator(1:length(tmp)*2,icount) = tmp(:); + if (binding_indicator(i,1) && ~binding_indicator(i,2)) + temp = -(DM.Bbarmat10+DM.Abarmat10*dictionary.ss(ireg(i+1)).T)\[DM.Cbarmat10 DM.Jbarmat10 DM.Abarmat10*dictionary.ss(ireg(i+1)).C+DM.Dbarmat10]; + dictionary.ss(icount).T = temp(:,1:n_vars); + dictionary.ss(icount).R = temp(:,n_vars+1:n_vars+n_exo); + dictionary.ss(icount).C = temp(:,n_vars+n_exo+1:end); + elseif (~binding_indicator(i,1) && binding_indicator(i,2)) + temp = -(DM.Bbarmat01+DM.Abarmat01*dictionary.ss(ireg(i+1)).T)\[DM.Cbarmat01 DM.Jbarmat01 DM.Abarmat01*dictionary.ss(ireg(i+1)).C+DM.Dbarmat01]; + dictionary.ss(icount).T = temp(:,1:n_vars); + dictionary.ss(icount).R = temp(:,n_vars+1:n_vars+n_exo); + dictionary.ss(icount).C = temp(:,n_vars+n_exo+1:end); + elseif (binding_indicator(i,1) && binding_indicator(i,2)) + temp = -(DM.Bbarmat11+DM.Abarmat11*dictionary.ss(ireg(i+1)).T)\[DM.Cbarmat11 DM.Jbarmat11 DM.Abarmat11*dictionary.ss(ireg(i+1)).C+DM.Dbarmat11]; + dictionary.ss(icount).T = temp(:,1:n_vars); + dictionary.ss(icount).R = temp(:,n_vars+1:n_vars+n_exo); + dictionary.ss(icount).C = temp(:,n_vars+n_exo+1:end); + else + temp = -(DM.Bbarmat+DM.Abarmat*dictionary.ss(ireg(i+1)).T)\[DM.Cbarmat DM.Jbarmat DM.Abarmat*dictionary.ss(ireg(i+1)).C]; + dictionary.ss(icount).T = temp(:,1:n_vars); + dictionary.ss(icount).R = temp(:,n_vars+1:n_vars+n_exo); + dictionary.ss(icount).C = temp(:,n_vars+n_exo+1:end); + end + end + end + + E = dictionary.ss(ireg(1)).R; + ss = dictionary.ss(ireg(1:T_max)); +else + ss = []; +end + +% generate data +% history will contain data, the state vector at each period in time will +% be stored columnwise. +history = zeros(n_vars,n_periods+1); +history(:,1) = init; +errvec = zeros(n_exo,1); + +errvec(irfshock_pos) = scalefactor_mod; + +% deal with shocks +irfpos =1; +if irfpos <=T_max + history(:,irfpos+1) = dictionary.ss(ireg(irfpos)).T* history(:,irfpos)+... + dictionary.ss(ireg(irfpos)).C + E*errvec; + T = dictionary.ss(ireg(irfpos)).T; + CONST = dictionary.ss(ireg(irfpos)).C; + R = E; +else + history(:,irfpos+1) = DM.decrulea*history(:,irfpos)+DM.decruleb*errvec; +end + +% all other periods +for irfpos=2:n_periods+1 + if irfpos <=T_max + history(:,irfpos+1) = dictionary.ss(ireg(irfpos)).T* history(:,irfpos)+... + dictionary.ss(ireg(irfpos)).C; + else + history(:,irfpos+1) = DM.decrulea*history(:,irfpos); + end +end + +zdata = history(:,2:end)'; \ No newline at end of file diff --git a/matlab/+occbin/mkdatap_anticipated_dyn.m b/matlab/+occbin/mkdatap_anticipated_dyn.m new file mode 100644 index 000000000..24740421c --- /dev/null +++ b/matlab/+occbin/mkdatap_anticipated_dyn.m @@ -0,0 +1,151 @@ +function [zdata, T, R, CONST, ss, update_flag]=mkdatap_anticipated_dyn(n_periods,DM,... + T_max,binding_indicator,irfshock_pos,scalefactor_mod,init,update_flag) +% function [zdata, T, R, CONST, ss]=mkdatap_anticipated_dyn(nperiods,DM,... +% Tmax,binding_indicator,irfshockpos,scalefactormod,init,update_flag) +% +% Inputs: +% - n_periods [double] number for periods for simulation +% - DM [structure] Dynamic model +% - T_max [Tmax] last period where constraints bind +% - binding_indicator [T+1] indicator for constraint violations +% - irfshock_pos [double] shock position +% - scalefactor_mod [double] shock values +% - init [double] [N by 1] initial value of endogenous variables +% - update_flag [boolean] flag whether to update results +% +% Output: +% - zdata [double] T+1 by N matrix of simulated data +% - T [N by N] transition matrix of state space +% - R [N by N_exo] shock impact matrix of state space +% - CONST [N by 1] constant of state space +% - ss [structure] state space system +% - update_flag [boolean] flag that results have been updated +% +% Original authors: Luca Guerrieri and Matteo Iacoviello +% Original file downloaded from: +% https://www.matteoiacoviello.com/research_files/occbin_20140630.zip +% Adapted for Dynare by Dynare Team. +% +% This code is in the public domain and may be used freely. +% However the authors would appreciate acknowledgement of the source by +% citation of any of the following papers: +% +% Luca Guerrieri and Matteo Iacoviello (2015): "OccBin: A toolkit for solving +% dynamic models with occasionally binding constraints easily" +% Journal of Monetary Economics 70, 22-38 + +persistent dictionary + +if update_flag + dictionary=[]; + update_flag=false; +end + +%Initialize outputs +n_vars = DM.n_vars; +n_exo = DM.n_exo; +T = DM.decrulea; +CONST = zeros(n_vars,1); +R = DM.decruleb; + +if nargin<7 + init=zeros(n_vars,1); +end + +if nargin<6 + scalefactor_mod=1; +end + + +% % get the time-dependent decision rules + +if ~isempty(dictionary) + if (length(binding_indicator)>size(dictionary.binding_indicator,1)) + dictionary.binding_indicator = [dictionary.binding_indicator; zeros(length(binding_indicator)-size(dictionary.binding_indicator,1),size(dictionary.binding_indicator,2))]; + end + if (length(binding_indicator(:)) 0 + if isempty(dictionary) + temp = -(DM.Astarbarmat*DM.decrulea+DM.Bstarbarmat)\[DM.Cstarbarmat DM.Jstarbarmat DM.Dstarbarmat]; + dictionary.binding_indicator(:,1) = [1; zeros(n_periods,1)]; + dictionary.ss(1).T = temp(:,1:n_vars); + dictionary.ss(1).R = temp(:,n_vars+1:n_vars+n_exo); + dictionary.ss(1).C = temp(:,n_vars+n_exo+1:end); + end + ireg(T_max)=1; + + % equivalent to pre-multiplying by the inverse above if the target + % matrix is invertible. Otherwise it yields the minimum state solution + %P(:,:,Tmax) = -(Astarbarmat*decrulea+Bstarbarmat)\Cstarbarmat; + %D(:,Tmax) = -(Astarbarmat*decrulea+Bstarbarmat)\Dstarbarmat; + + icount=length(dictionary.ss); + + for i = T_max-1:-1:1 + + tmp = 0*binding_indicator; + tmp(1:end-i+1) = binding_indicator(i:end); + itmp = find(~any(dictionary.binding_indicator-tmp)); + if ~isempty(itmp) + ireg(i) = itmp; + else + icount=icount+1; + ireg(i) = icount; + dictionary.binding_indicator(1:length(tmp),icount) = tmp; + if binding_indicator(i) + temp = -(DM.Bstarbarmat+DM.Astarbarmat*dictionary.ss(ireg(i+1)).T)\[DM.Cstarbarmat DM.Jstarbarmat DM.Astarbarmat*dictionary.ss(ireg(i+1)).C+DM.Dstarbarmat]; + dictionary.ss(icount).T = temp(:,1:n_vars); + dictionary.ss(icount).R = temp(:,n_vars+1:n_vars+n_exo); + dictionary.ss(icount).C = temp(:,n_vars+n_exo+1:end); + else + temp = -(DM.Bbarmat+DM.Abarmat*dictionary.ss(ireg(i+1)).T)\[DM.Cbarmat DM.Jbarmat (DM.Abarmat*dictionary.ss(ireg(i+1)).C)]; + dictionary.ss(icount).T = temp(:,1:n_vars); + dictionary.ss(icount).R = temp(:,n_vars+1:n_vars+n_exo); + dictionary.ss(icount).C = temp(:,n_vars+n_exo+1:end); + end + end + end + + E = dictionary.ss(ireg(1)).R; + ss = dictionary.ss(ireg(1:T_max)); +else + ss = []; +end + +% generate data +% history will contain data, the state vector at each period in time will +% be stored columnwise. +history = zeros(n_vars,n_periods+1); +history(:,1) = init; +errvec = zeros(n_exo,1); + +% deal with predetermined conditions +errvec(irfshock_pos) = scalefactor_mod; + +% deal with shocks +irfpos =1; +if irfpos <=T_max + history(:,irfpos+1) = dictionary.ss(ireg(irfpos)).T* history(:,irfpos)+... + dictionary.ss(ireg(irfpos)).C + E*errvec; + T = dictionary.ss(ireg(irfpos)).T; + CONST = dictionary.ss(ireg(irfpos)).C; + R = E; +else + history(:,irfpos+1) = DM.decrulea*history(:,irfpos)+DM.decruleb*errvec; +end + +% all other periods +for irfpos=2:n_periods+1 + if irfpos <=T_max + history(:,irfpos+1) = dictionary.ss(ireg(irfpos)).T* history(:,irfpos)+... + dictionary.ss(ireg(irfpos)).C; + else + history(:,irfpos+1) = DM.decrulea*history(:,irfpos); + end +end + +zdata = history(:,2:end)'; \ No newline at end of file diff --git a/matlab/+occbin/process_constraint.m b/matlab/+occbin/process_constraint.m new file mode 100644 index 000000000..2f308eef6 --- /dev/null +++ b/matlab/+occbin/process_constraint.m @@ -0,0 +1,81 @@ +function constraint_parsed = process_constraint(constraint,suffix,endo_names,invert_switch,param_names) +%function constraint_parsed = process_constraint(constraint,suffix,endo_names,invert_switch,param_names) +% Processes constraints for use in Occbin, appending endogenous variables +% with suffix and replacing parameters by their value in M_.params +% +% INPUTS +% - constraint [char] constraint to be parsed +% - suffix [char] suffix to be appended +% - endo_names [cell] names of endogenous variables +% - invert_switch [bool] if true, invert direction of the inequality constraint +% - param_names [cell] names of parameters +% +% OUTPUTS +% - constraint_parsed [char] parsed constraint + +% Original authors: Luca Guerrieri and Matteo Iacoviello +% Original file downloaded from: +% https://www.matteoiacoviello.com/research_files/occbin_20140630.zip +% Adapted for Dynare by Dynare Team. +% +% This code is in the public domain and may be used freely. +% However the authors would appreciate acknowledgement of the source by +% citation of any of the following papers: +% +% Luca Guerrieri and Matteo Iacoviello (2015): "OccBin: A toolkit for solving +% dynamic models with occasionally binding constraints easily" +% Journal of Monetary Economics 70, 22-38 + +% create a list of delimiters that can separate parameters and endogenoous +% variables in the string that expresses the constraint +delimiters = char(',',';','(',')','+','-','^','*','/','>','<','='); + +% split the string that holds the constraint into tokens +tokens = occbin.tokenize(constraint,delimiters); + +ntokens = length(tokens); + +endo_ss=strcat(endo_names,repmat('_ss',length(endo_names),1)); + +% search for tokens that match the list of endogenous variables +for i=1:ntokens + valid_token=0; + if ~isempty(find(strcmp(tokens(i),delimiters))) + % if the invert_switch is true + % reverse the direction of the inequality + if invert_switch + if strcmp(tokens(i),cellstr('>')) + tokens(i) = cellstr('<'); + elseif strcmp(tokens(i),cellstr('<')) + tokens(i) = cellstr('>'); + end + end + valid_token=1; + continue; + end + + if ~isempty(find(strcmp(tokens(i),endo_names))) + % when there is a match with an endogenous variable append the suffix + tokens(i) = cellstr([char(tokens(i)),suffix]); + valid_token=1; + continue; + end + par_index=find(strcmp(tokens(i),param_names)); + if ~isempty(par_index) + tokens(i) = {['M_.params(',num2str(par_index),')']}; + valid_token=1; + continue; + end + ss_index=find(strcmp(tokens(i),endo_ss)); + if ~isempty(ss_index) + tokens(i) = {['ys(',num2str(ss_index),')']}; + valid_token=1; + continue; + end + if isnan(str2double(tokens(i))) + error('Occbin: Constraint %s contains the uninterpretable token %s', constraint, tokens{i}); + end +end + +% reassemble the tokens to create a string that expresses the constraint +constraint_parsed = regexprep(strjoin(tokens),' ',''); \ No newline at end of file diff --git a/matlab/+occbin/process_error_constraint.m b/matlab/+occbin/process_error_constraint.m new file mode 100644 index 000000000..9ae8d012a --- /dev/null +++ b/matlab/+occbin/process_error_constraint.m @@ -0,0 +1,35 @@ +function error_parsed = process_error_constraint(constraint) +% function constraint1 = process_error_constraint(constraint) +% Constructs a string with the constraint error, i.e. by how much it is violated +% INPUTS +% - constraint [char] constraint to be parsed +% +% OUTPUTS +% - error_parsed [char] parsed constraint + +% Copyright (C) 2021 Dynare Team +% +% This file is part of Dynare. +% +% Dynare is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% Dynare is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with Dynare. If not, see . + +error_parsed = constraint; +error_parsed=regexprep(error_parsed,'=',''); +num = length(regexp(error_parsed,'>'))+length(regexp(error_parsed,'<')); +error_parsed=regexprep(error_parsed,'>','-('); +error_parsed=regexprep(error_parsed,'<','-('); +for k=1:num + error_parsed=[error_parsed ')']; +end + diff --git a/matlab/+occbin/set_default_options.m b/matlab/+occbin/set_default_options.m new file mode 100644 index 000000000..a3b437cd1 --- /dev/null +++ b/matlab/+occbin/set_default_options.m @@ -0,0 +1,206 @@ +function options_occbin_ = set_default_options(options_occbin_,M_,flag) +% function options_occbin_ = set_default_options(options_occbin_,M_,flag) +% Sets default options for Occbin +% +% INPUTS +% - options_occbin_ [structure] Matlab's structure describing the current options +% - M_ [structure] Matlab's structure describing the model +% - flag [cell] govern what/how much to initialize +% +% OUTPUTS +% - options_occbin_ [structure] Matlab's structure describing the current options + +% Copyright (C) 2021 Dynare Team +% +% This file is part of Dynare. +% +% Dynare is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% Dynare is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with Dynare. If not, see . + +if nargin<3 + flag='all'; +end + +if ismember(flag,{'all'}) + options_occbin_.solver.solve_algo=3; %solver for match_function: csolve + options_occbin_.solver.solve_tolx=1e-10; + options_occbin_.solver.solve_tolf=1e-5; + options_occbin_.solver.maxit=10; + options_occbin_.write_regimes.periods=[]; + options_occbin_.write_regimes.filename=[M_.fname '_occbin_regimes']; +end + +if ismember(flag,{'filter','all'}) + options_occbin_.filter.use_relaxation = false; +end + +if ismember(flag,{'forecast','all'}) + options_occbin_.forecast.debug_flag=false; + options_occbin_.forecast.frcst_regimes=[]; + options_occbin_.forecast.maxit=30; + options_occbin_.forecast.periods=30; + options_occbin_.forecast.qmc=0; + options_occbin_.forecast.replic=0; + options_occbin_.forecast.SHOCKS0=[]; + options_occbin_.forecast.treepath=1; % number of branches +end + +if ismember(flag,{'irf','all'}) + options_occbin_.irf.init_regime=[]; + options_occbin_.irf.maxit=30; + options_occbin_.irf.threshold = 10^-6; +% options_occbin_.irf.periods=options_.irf; + options_occbin_.irf.shocksize=[]; + options_occbin_.irf.shocksigns = {'1','_1'}; +end + +if ismember(flag,{'likelihood','all'}) + options_occbin_.likelihood.curb_retrench = false; + options_occbin_.likelihood.first_period_occbin_update = true; + options_occbin_.likelihood.full_output = false; + options_occbin_.likelihood.IF_likelihood = false; + options_occbin_.likelihood.init_regime_history = []; + options_occbin_.likelihood.init_binding_indicator = false(0); + options_occbin_.likelihood.inversion_filter = false; + options_occbin_.likelihood.IVF_shock_observable_mapping = []; + options_occbin_.likelihood.maxit = 50; % this is for occbin solver algo + options_occbin_.likelihood.max_number_of_iterations = 10; % this is for occbin_kalman_update loop + options_occbin_.likelihood.periods = 100; + options_occbin_.likelihood.check_ahead_periods=200; + options_occbin_.likelihood.periodic_solution=true; + options_occbin_.likelihood.piecewise_only = true; + options_occbin_.likelihood.restrict_state_space = true; + options_occbin_.likelihood.status=true; + options_occbin_.likelihood.use_updated_regime = true; + options_occbin_.likelihood.waitbar=false; +end + +if ismember(flag,{'plot_shock_decomp','all'}) + options_occbin_.plot_shock_decomp.add_steadystate = false; + options_occbin_.plot_shock_decomp.add_zero_line = false; + options_occbin_.plot_shock_decomp.decomp_type='qoq'; + options_occbin_.plot_shock_decomp.figure_size = [200 100 650 850]; + options_occbin_.plot_shock_decomp.grid = false; + options_occbin_.plot_shock_decomp.graph_line=false; + options_occbin_.plot_shock_decomp.graph_regime=false; + options_occbin_.plot_shock_decomp.graph_simul=char('total','linear'); + options_occbin_.plot_shock_decomp.graph_simul_zoom=char('total','piecewise','linear'); + options_occbin_.plot_shock_decomp.graph_zoom=false; + options_occbin_.plot_shock_decomp.init_names_=[]; + options_occbin_.plot_shock_decomp.lw=2; + options_occbin_.plot_shock_decomp.mystyles = {':','--',':','-.'}; + options_occbin_.plot_shock_decomp.ncol = 3; + options_occbin_.plot_shock_decomp.no_legend = false; + options_occbin_.plot_shock_decomp.no_others = false; + options_occbin_.plot_shock_decomp.T0 = []; % initial date in plot (must be >= TINIT) + options_occbin_.plot_shock_decomp.TINIT = dates(); % date of initialized states for shock decomp +% options_occbin_.plot_shock_decomp.use_shock_groups=options_.plot_shock_decomp.use_shock_groups; +end + +if ismember(flag,{'plot_simul','all'}) + options_occbin_.plot_simul.add_steadystate = false; + options_occbin_.plot_simul.add_vertical_line = false; + options_occbin_.plot_simul.cutoff = false; + options_occbin_.plot_simul.figure_size = [50 50 850 850]; + options_occbin_.plot_simul.labels = {'Linear','Piecewise','Simulation 3','Simulation 4'}; + options_occbin_.plot_simul.legend = true; + options_occbin_.plot_simul.length_simul = []; + options_occbin_.plot_simul.linewidth = 2; + options_occbin_.plot_simul.log_normalize_graph = false; + options_occbin_.plot_simul.marg_h(1) = 0.08; + options_occbin_.plot_simul.marg_h(2) = 0.055; + options_occbin_.plot_simul.mycolors = get(groot,'DefaultAxesColorOrder'); + options_occbin_.plot_simul.my_dir = 'OccBinSimul'; + options_occbin_.plot_simul.mystyles = {'-','--',':','-.'}; + options_occbin_.plot_simul.mystst_simul_pos = false; + options_occbin_.plot_simul.ncols = 3; + options_occbin_.plot_simul.normalization_point = 1; + options_occbin_.plot_simul.nrows = 3; + options_occbin_.plot_simul.print_emf = false; + options_occbin_.plot_simul.scale_y = false; + options_occbin_.plot_simul.scale_y1 = true; + options_occbin_.plot_simul.scale_y2 = true; + options_occbin_.plot_simul.simulname = 'occbin_simul'; + options_occbin_.plot_simul.subplot_gap = 0.07; + options_occbin_.plot_simul.threshold = 10^-6; + options_occbin_.plot_simul.timeaxis = []; + options_occbin_.plot_simul.use_grid = true; + +end + +if ismember(flag,{'shock_decomp','all'}) + options_occbin_.shock_decomp.additive=false; + options_occbin_.shock_decomp.curb_retrench=false; + options_occbin_.shock_decomp.debug=false; + options_occbin_.shock_decomp.init_in_others=false; + options_occbin_.shock_decomp.init_names_=[]; + options_occbin_.shock_decomp.init_total=false; + options_occbin_.shock_decomp.init2shocks= false; + options_occbin_.shock_decomp.main_effect=false; + options_occbin_.shock_decomp.main_effect_init=false; + options_occbin_.shock_decomp.maxit = 100; + options_occbin_.shock_decomp.nfrcst=0; + options_occbin_.shock_decomp.periods = 60; + options_occbin_.shock_decomp.check_ahead_periods=200; + options_occbin_.shock_decomp.shocks_only=false; + options_occbin_.shock_decomp.total_effect=false; + options_occbin_.shock_decomp.conditional_only=true; + options_occbin_.shock_decomp.TINIT = dates(); % date to initialize states for shock decomp +% options_occbin_.shock_decomp.use_shock_groups=options_.plot_shock_decomp.use_shock_groups; +end + +if ismember(flag,{'simul','all'}) + options_occbin_.simul.debug = false; + options_occbin_.simul.curb_retrench=false; + options_occbin_.simul.endo_init=zeros(M_.endo_nbr,1); + options_occbin_.simul.full_output=true; + options_occbin_.simul.init_regime=[]; + options_occbin_.simul.init_binding_indicator=false(0); + options_occbin_.simul.exo_pos=1:M_.exo_nbr; + options_occbin_.simul.local=true; + options_occbin_.simul.maxit=10; + options_occbin_.simul.max_periods=inf; + options_occbin_.simul.periods=30; + options_occbin_.simul.check_ahead_periods=200; + options_occbin_.simul.periodic_solution=true; + options_occbin_.simul.piecewise_only = false; + options_occbin_.simul.restrict_state_space=false; + options_occbin_.simul.SHOCKS=zeros(1,M_.exo_nbr); + options_occbin_.simul.waitbar=true; +end + +if ismember(flag,{'smoother','all'}) + options_occbin_.smoother.curb_retrench = false; + options_occbin_.smoother.debug = false; + options_occbin_.smoother.fast = false; + options_occbin_.smoother.first_period_occbin_update = true; + options_occbin_.smoother.full_output = false; +% options.occbin.smoother.init_mode = 1; % 0 = standard; 1 = unconditional frcsts zero shocks+smoothed states in each period + options_occbin_.smoother.init_regime_history = []; + options_occbin_.smoother.init_binding_indicator = false(0); + options_occbin_.smoother.inversion_filter = false; + options_occbin_.smoother.linear_smoother = true; + options_occbin_.smoother.maxit = 30; % this is for occbin solver algo + options_occbin_.smoother.max_number_of_iterations = 10; % this is for smoother loop + options_occbin_.smoother.periods = 100; + options_occbin_.smoother.check_ahead_periods=200; + options_occbin_.smoother.piecewise_only = true; + options_occbin_.smoother.plot = true; + options_occbin_.smoother.status=true; + options_occbin_.smoother.waitbar=true; +% options.occbin.smoother.restrict_state_space = 1; +end + +if ismember(flag,{'graph','all'}) + options_occbin_.graph.steady_state=true; +end \ No newline at end of file diff --git a/matlab/+occbin/set_option.m b/matlab/+occbin/set_option.m new file mode 100644 index 000000000..ea6070ce0 --- /dev/null +++ b/matlab/+occbin/set_option.m @@ -0,0 +1,34 @@ +function options_=set_option(options_,options_occbin_,fieldname) +% function options_=set_option(options_,options_occbin_,fieldname) +% Set local option for Occbin +% +% Inputs: +% - options_ [structure] Matlab's structure containing the options +% - options_occbin_ [structure] Matlab's structure containing Occbin options +% - fieldname [string] name of the options field to set +% +% Outputs: +% - options_ [structure] Matlab's structure containing the options + +% Copyright (C) 2021 Dynare Team +% +% This file is part of Dynare. +% +% Dynare is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% Dynare is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with Dynare. If not, see . + +dot_pos = strfind(fieldname,'.'); + +if isfield(options_occbin_,fieldname(1:dot_pos-1)) && isfield(options_occbin_.(fieldname(1:dot_pos-1)),fieldname(dot_pos+1:end)) + options_.occbin.(fieldname(1:dot_pos-1)).(fieldname(dot_pos+1:end)) = options_occbin_.(fieldname(1:dot_pos-1)).(fieldname(dot_pos+1:end)); +end \ No newline at end of file diff --git a/matlab/+occbin/setup.m b/matlab/+occbin/setup.m new file mode 100644 index 000000000..71d6b326d --- /dev/null +++ b/matlab/+occbin/setup.m @@ -0,0 +1,52 @@ +function [M_, options_] = setup(M_,options_, options_occbin_) +% function [M_, options_] = setup(M_, options_, options_occbin_) +% Sets up run of Occbin: creates shock matrix, sets options +% +% INPUT: +% - M_ [structure] Matlab's structure describing the model +% - options_ [structure] Matlab's structure containing the options +% - options_occbin_ [structure] Matlab's structure containing Occbin options +% +% OUTPUT: +% - M_ [structure] Matlab's structure describing the model +% - options_occbin_ [structure] Matlab's structure containing Occbin options + +% Copyright (C) 2021 Dynare Team +% +% This file is part of Dynare. +% +% Dynare is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% Dynare is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with Dynare. If not, see . + +options_ = occbin.set_option(options_,options_occbin_,'simul.periods'); +options_ = occbin.set_option(options_,options_occbin_,'simul.curb_retrench'); +options_ = occbin.set_option(options_,options_occbin_,'simul.maxit'); +options_ = occbin.set_option(options_,options_occbin_,'simul.check_ahead_periods'); +options_ = occbin.set_option(options_,options_occbin_,'smoother.periods'); +options_ = occbin.set_option(options_,options_occbin_,'smoother.curb_retrench'); +options_ = occbin.set_option(options_,options_occbin_,'smoother.maxit'); +options_ = occbin.set_option(options_,options_occbin_,'smoother.check_ahead_periods'); +options_ = occbin.set_option(options_,options_occbin_,'filter.use_relaxation'); +options_ = occbin.set_option(options_,options_occbin_,'likelihood.inversion_filter'); +options_ = occbin.set_option(options_,options_occbin_,'smoother.inversion_filter'); + +if isfield(M_,'surprise_shocks') && ~isempty(M_.surprise_shocks) + temp=zeros(max(cat(2,M_.surprise_shocks.periods)),M_.exo_nbr); + for ii = 1:length(M_.surprise_shocks) + ivar = M_.surprise_shocks(ii).exo_id; + temp(M_.surprise_shocks(ii).periods,ivar) = M_.surprise_shocks(ii).value; + end + shock_index=~all(temp==0); + options_.occbin.simul.SHOCKS=temp(:,shock_index); + options_.occbin.simul.exo_pos=find(shock_index); +end diff --git a/matlab/+occbin/shock_decomposition.m b/matlab/+occbin/shock_decomposition.m new file mode 100644 index 000000000..f25f6b504 --- /dev/null +++ b/matlab/+occbin/shock_decomposition.m @@ -0,0 +1,266 @@ +function oo_ = shock_decomposition(oo_, M_, options_, vname) +% function oo_ = shock_decomposition(oo_, M_, options_, vname) +% +% INPUTS +% - oo_ [structure] Matlab's structure containing the results (oo_). +% - M_ [structure] Matlab's structure describing the model (M_). +% - options_ [structure] Matlab's structure describing the current options (options_). +% - vname [cell] array of variable names +% OUTPUT +% - oo_ [structure] Matlab's structure containing the results (oo_). + +% Copyright (C) 2021 Dynare Team +% +% This file is part of Dynare. +% +% Dynare is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% Dynare is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with Dynare. If not, see . + + +% list of options +% T0, use_shock_groups, vname, file_name, nfrcst, init_names_ + +shock_decomp_options = options_.occbin.shock_decomp; + +if nargin<4 || isempty(vname) + vname=cellstr(M_.endo_names(1:M_.orig_endo_nbr,:)); +end + +opts_simul = options_.occbin.simul; +opts_simul.periods = shock_decomp_options.periods; +opts_simul.check_ahead_periods = shock_decomp_options.check_ahead_periods; +opts_simul.maxit = shock_decomp_options.maxit; +use_shock_groups = shock_decomp_options.use_shock_groups; + +init_names_ = shock_decomp_options.init_names_; +nfrcst = shock_decomp_options.nfrcst; + +%% new dynare grouping +if isempty(use_shock_groups) + use_shock_groups = 'ALL'; + ngroups = M_.exo_nbr; + ex_names_ = cell(ngroups,1); + for i=1:ngroups + ex_names_{i} = M_.exo_names(i); + end + shock_decomp_options.main_effect=0; + shock_decomp_options.additive = 0; +else + shock_groups = M_.shock_groups.(use_shock_groups); + shock_ind = fieldnames(shock_groups); + ngroups = length(shock_ind); + ex_names_ = shock_ind; + for i=1:ngroups + ex_names_{i} = shock_groups.(shock_ind{i}).shocks; + end +end +%% + +if iscell(vname{1}) + vname0=vname; + clear vname + vname = cell(1,length(vname0)); + decomp_type = cell(1,length(vname0)); + gtrend = cell(1,length(vname0)); + var_type = nan(1,length(vname0)); + for j=1:length(vname0) + vname{j}=vname0{j}{1}; + decomp_type{j}=vname0{j}{2}; + if strcmpi(decomp_type{j},'aoa') + gtrend{j}=vname0{j}{3}; + if strcmpi(vname0{j}{4},'flow') + var_type(j)=1; + elseif strcmpi(vname0{j}{4},'deflator') + var_type(j)=2; + elseif strcmpi(vname0{j}{4},'stock') + var_type(j)=0; + else + error('wrong var type for aoa decomp') + end + end + end + clear vname0 +else + decomp_type = cell(1,length(vname)); + for j=1:length(vname) + decomp_type{j}='qoq'; + end +end + +% set time-varying state space matrices +TT = oo_.occbin.smoother.T0; +RR = oo_.occbin.smoother.R0; +CC = oo_.occbin.smoother.C0; + +load (options_.datafile, 'T'); + +indx_init=zeros(length(init_names_),1); +for j=1:length(init_names_) + indx_init(j)=strmatch(init_names_{j},M_.endo_names(oo_.dr.order_var,:),'exact'); +end + +if shock_decomp_options.debug + % re-compute smoothed variables from T, R and smoothed shocks etahat + a1=oo_.occbin.smoother.alphahat(:,1); + as=a1; + as0=zeros(length(a1),1); + as0(indx_init)=a1(indx_init); + etahat=[oo_.occbin.smoother.etahat zeros(size(oo_.occbin.smoother.etahat,1),nfrcst)]; + for j=1:size(etahat,2)-1 + TM = TT(:,:,j+1); + RM = RR(:,:,j+1); + CONST = CC(:,j+1); + as(:,j+1)=TM*as(:,j)+RM*etahat(:,j+1)+CONST; + as0(:,j+1)=TM*as0(:,j)+CONST; + end + err = max(max(abs(oo_.occbin.smoother.alphahat-as))); + if err>1e-8 + disp('WARNING CHECK SMOOTHER:') + disp(['simulated model with smoothed shocks differs from stored smoother by ' num2str(err)]) + else + disp('CHECK SMOOTHER OK:') + disp(['simulated model with smoothed shocks differs from stored smoother by less than 1e-8 (err=' num2str(err) ')']) + end +else + etahat=[oo_.occbin.smoother.etahat zeros(size(oo_.occbin.smoother.etahat,1),nfrcst)]; + as = oo_.occbin.smoother.alphahat; +end +gend=size(etahat,2); + +%% +TT= 0:0.25:ceil(gend/4+1); +TT=TT(1:gend); +TT1= dates('0Q1'):(dates('0Q1')+gend-1); +if exist('T','var') + TT=T(options_.first_obs)+TT; + TT1=TT1+T(options_.first_obs)*4; +end + +shock_decomp_options = set_default_option(shock_decomp_options,'TINIT',TT1(1)); +tinit = max([1,find(TT1==shock_decomp_options.TINIT)]); +TINIT = char(TT1(tinit)); +shock_decomp_options = set_default_option(shock_decomp_options,'file_name',['_' use_shock_groups '_' TINIT]); +file_name = shock_decomp_options.file_name; + +%% + +%%%%%%%%%%%%%%%%%%%% LINEAR shock decomp +as_lin=zeros(M_.endo_nbr,length(tinit:gend)); % linear smoother reconstructed +att=zeros(M_.endo_nbr,length(tinit:gend)); % linear initial condition effect +inn=zeros(M_.endo_nbr,length(tinit:gend)); % linear aggreage shocks effect without att, i.e. as_lin = inn+att; +deco=zeros(M_.endo_nbr,M_.exo_nbr,length(tinit:gend)); % full decomposition into individual shocks + +att(:,1)=oo_.occbin.linear_smoother.alphahat(:,tinit); +as_lin(:,1)=oo_.occbin.linear_smoother.alphahat(:,tinit); +TM = oo_.occbin.linear_smoother.T0; +RM= oo_.occbin.linear_smoother.R0; +for j=2:length(tinit:gend) + as_lin(:,j) = TM*as_lin(:,j-1)+RM*oo_.occbin.linear_smoother.etahat(:,j+tinit-1); + att(:,j) = TM*att(:,j-1); + inn(:,j) = RM*oo_.occbin.linear_smoother.etahat(:,j+tinit-1); + if j>1 + inn(:,j) = inn(:,j) + TM*inn(:,j-1); + end + for iexo=1:M_.exo_nbr + deco(:,iexo,j) = RM(:,iexo)*oo_.occbin.linear_smoother.etahat(iexo,j+tinit-1); + if j>1 + deco(:,iexo,j) = deco(:,iexo,j) + TM*deco(:,iexo,j-1); + end + + end +end +as_lin=as_lin(oo_.dr.inv_order_var,:); % linear smoother reconstructed +att=att(oo_.dr.inv_order_var,:); % linear initial condition effect +inn=inn(oo_.dr.inv_order_var,:); % linear aggreage shocks effect without att +deco=deco(oo_.dr.inv_order_var,:,:); % full decomposition into individual shocks +deco(:,M_.exo_nbr+1,:)=att; +deco(:,M_.exo_nbr+2,:)=as_lin; +oo_.occbin.linear_smoother.decomp=deco; +%%%%%%%%%%%%%%%%%%%% END LINEAR shock decomp + +%% + +%%%%%%%%%%%%%%%%%%%% piecewise COONDITIONAL shock decomp +as_p=zeros(M_.endo_nbr,length(tinit:gend)); % smoother reconstructed +att_p=zeros(M_.endo_nbr,length(tinit:gend)); % initial condition effect +inn_p=zeros(M_.endo_nbr,length(tinit:gend)); % aggreage shocks effect without att, i.e. as_lin = inn+att; +deco_p=zeros(M_.endo_nbr,M_.exo_nbr,length(tinit:gend)); % full decomposition into individual shocks +reg_p=zeros(M_.endo_nbr,length(tinit:gend)); % pure regime effect (CONST 'shocks') + +att_p(:,1)=oo_.occbin.smoother.alphahat(:,tinit); +as_p(:,1)=oo_.occbin.smoother.alphahat(:,tinit); +TT = oo_.occbin.smoother.T0; +RR= oo_.occbin.smoother.R0; +CC= oo_.occbin.smoother.C0; + +for j=2:length(tinit:gend) + TM = TT(:,:,j+tinit-1); + RM = RR(:,:,j+tinit-1); + CONST = CC(:,j+tinit-1); + as_p(:,j) = TM*as_p(:,j-1)+RM*oo_.occbin.smoother.etahat(:,j+tinit-1)+CONST; + att_p(:,j) = TM*att_p(:,j-1); + inn_p(:,j) = RM*oo_.occbin.smoother.etahat(:,j+tinit-1); + reg_p(:,j) = TM*reg_p(:,j-1)+CONST; + if j>1 + inn_p(:,j) = inn_p(:,j) + TM*inn_p(:,j-1) ; + end + for iexo=1:M_.exo_nbr + deco_p(:,iexo,j) = RM(:,iexo)*oo_.occbin.smoother.etahat(iexo,j+tinit-1); + if j>1 + deco_p(:,iexo,j) = deco_p(:,iexo,j) + TM*deco_p(:,iexo,j-1); + end + + end +end +as_p=as_p(oo_.dr.inv_order_var,:); % occbin smoother reconstructed +att_p=att_p(oo_.dr.inv_order_var,:); % occbin initial condition effect +inn_p=inn_p(oo_.dr.inv_order_var,:); % occbin aggregage shocks effect without att +deco_p=deco_p(oo_.dr.inv_order_var,:,:); % occbin full decomposition into individual shocks +reg_p=reg_p(oo_.dr.inv_order_var,:); % occbin pure regime effect (CONST 'shocks') +i_reg=strmatch('EPS_REGIME',M_.exo_names,'exact'); +if ~isempty(i_reg) +deco_p(:,i_reg,:)=reg_p; +end +deco_p(:,M_.exo_nbr+1,:)=att_p; +deco_p(:,M_.exo_nbr+2,:)=as_p; +%deco_p(:,M_.exo_nbr+3,:)=as_p-reg_p; +oo_.occbin.smoother.decomp=deco_p; +rr=abs(deco_p(:,1:end-1,:)); +if isequal(use_shock_groups,'ALL') && ~isempty(i_reg) + rr(:,i_reg,:)=0; +end +ww=zeros(size(rr)); +for k=1:size(rr,3) + tmp=sum(rr(:,:,k),2); + tmp(tmp<1.e-10)=1; + for g=1:size(rr,2) + ww(:,g,k) = rr(:,g,k)./tmp; + end +end +wdeco_p=deco_p; +if ~isempty(i_reg) + wdeco_p(:,i_reg,:)=0; +end +for k=1:size(rr,3) + if any(any(reg_p(:,k))) + for g=1:size(rr,2) + wdeco_p(:,g,k) = wdeco_p(:,g,k)+reg_p(:,k).*ww(:,g,k); + end + end +end +oo_.occbin.smoother.wdecomp=wdeco_p; +%%%%%%%%%%%%%%%%%%%% END CONDITIONAL shock decomp +%% add here other fetures when ready +% if shock_decomp_options.conditional_only ==1 +% return +% end diff --git a/matlab/+occbin/solve_one_constraint.m b/matlab/+occbin/solve_one_constraint.m new file mode 100644 index 000000000..83025117d --- /dev/null +++ b/matlab/+occbin/solve_one_constraint.m @@ -0,0 +1,297 @@ +function [data, SS_out, error_flag ] = solve_one_constraint(M_,dr, opts_simul_, solve_DM) +%function [data, SS_out, error_flag ] = solve_one_constraint(M_,dr, opts_simul_, solve_DM) +% +% INPUT: +% - M_ [structure] Matlab's structure describing the model (M_). +% - dr [structure] decision rules for the model +% - opts_simul [structure] Matlab's structure containing the Occbin options (opts_simul). +% - solve_DM [double] indicator on whether to recompute decision rules +% +% OUTPUT: +% - data [structure] simulation result containing fields: +% - linear: paths for endogenous variables ignoring OBC (linear solution) +% - piecewise: paths for endogenous variables satisfying the OBC (occbin/piecewise solution) +% - ys: vector of steady state values +% - regime_history: information on number and time of regime transitions +% - SS_out [structure] State space solution +% - T: [n_vars by n_vars by n_shock_period] array of transition matrices +% - R: [n_vars by n_exo by n_shock_period] array of shock response matrices +% - C: [n_vars by n_shock_period] array of constants +% - error_flag [integer] 1 if a problem was encoutered, 0 otherwise + +% Original authors: Luca Guerrieri and Matteo Iacoviello +% Original file downloaded from: +% https://www.matteoiacoviello.com/research_files/occbin_20140630.zip +% Adapted for Dynare by Dynare Team. +% +% This code is in the public domain and may be used freely. +% However the authors would appreciate acknowledgement of the source by +% citation of any of the following papers: +% +% Luca Guerrieri and Matteo Iacoviello (2015): "OccBin: A toolkit for solving +% dynamic models with occasionally binding constraints easily" +% Journal of Monetary Economics 70, 22-38 + +persistent DM + +if isempty(DM) + solve_DM=true; +end + +data.shocks_sequence = opts_simul_.SHOCKS; % sequence of unforeseen shocks under which one wants to solve the model +n_periods = opts_simul_.periods; % simulation horizon (can be longer than the sequence of shocks defined in shockssequence; must be long enough to ensure convergence back to the reference model at the end of the simulation horizon and may need to be varied depending on the sequence of shocks). +curb_retrench = opts_simul_.curb_retrench; % 0: updates guess based on previous iteration; 1: updates similar to Gauss-Jacobi scheme, slowing iterations down by updating guess only one period at a time +max_iter = opts_simul_.maxit; % maximum number of iterations allowed for the solution algorithm +endo_init = opts_simul_.endo_init; % initial condition for state variables, in deviation from steady state in declaration order +binding_indicator = opts_simul_.init_binding_indicator; % initial guess for constraint violations +regime_history_guess = opts_simul_.init_regime; % initial guess for constraint violations +periodic_solution = opts_simul_.periodic_solution; + +data.exo_pos = opts_simul_.exo_pos; + +n_shocks_periods = size(data.shocks_sequence,1); + +if n_periods < n_shocks_periods + n_periods = n_shocks_periods; +end +nperiods_0 = max(opts_simul_.check_ahead_periods,n_periods-n_shocks_periods); + +error_flag=0; + +M_base = M_; +dr_base = dr; + +% ensure that the two models have the same parameters +% use the parameters for the base model. +%Mstar_.params = Mbase_.params; + +data.ys = dr_base.ys; + +if solve_DM + DM.n_vars = M_base.endo_nbr; + DM.n_exo = M_base.exo_nbr; + + % get the matrices holding the first derivatives for the model + % each regime is treated separately + [DM.Cbarmat, DM.Bbarmat, DM.Abarmat, DM.Jbarmat] = occbin.get_deriv(M_base,data.ys); + + Mstar_= M_base; + Mstar_.params(M_.occbin.constraint(1).pswitch_index)= 1; + [DM.Cstarbarmat, DM.Bstarbarmat, DM.Astarbarmat, DM.Jstarbarmat, DM.Dstarbarmat] = occbin.get_deriv(Mstar_,data.ys); + + [DM.decrulea,DM.decruleb]=occbin.get_pq(dr_base); + + update_flag=true; +else + update_flag=false; +end + +endo_names = M_base.endo_names; +exo_names = M_base.exo_names; + + +% set some initial conditions and loop through the shocks +% period by period +init_orig_ = endo_init; +zdatapiecewise_ = zeros(n_periods,DM.n_vars); +% nwishes_ = size(wishlist_,1); +if ~exist('binding_indicator','var') + binding_indicator = false(nperiods_0+1,1); +else + if length(binding_indicator)<(nperiods_0+1) + binding_indicator = [binding_indicator; false(nperiods_0+1-length(binding_indicator),1)]; + end +end +SS_out.T = NaN(DM.n_vars,DM.n_vars,n_shocks_periods); +SS_out.R = NaN(DM.n_vars,DM.n_exo,n_shocks_periods); +SS_out.C = nan(DM.n_vars,n_shocks_periods); +if ~exist('regime_history_','var') || isempty(regime_history_guess) + regime_history = struct(); + guess_history = false; +else + guess_history = true; + regime_history = regime_history_guess; +end + +if opts_simul_.waitbar + hh = dyn_waitbar(0,'Occbin: Solving the model'); + set(hh,'Name','Occbin: Solving the model.'); +end + +for shock_period = 1:n_shocks_periods + if opts_simul_.waitbar + dyn_waitbar(shock_period/n_shocks_periods, hh, sprintf('Period %u of %u', shock_period,n_shocks_periods)); + end + + regime_change_this_iteration=true; + iter = 0; + guess_history_it = false; + if guess_history && (shock_period<=length(regime_history_guess)) %beyond guess regime history + guess_history_it = true; + end + + is_periodic=false; + binding_indicator_history={}; + max_err = NaN(max_iter,1); + + while (regime_change_this_iteration && iter1 && any(data.shocks_sequence(shock_period,:)) + [zdatalinear_, SS_out.T(:,:,shock_period), SS_out.R(:,:,shock_period), SS_out.C(:,shock_period), SS, update_flag]=occbin.mkdatap_anticipated_dyn(nperiods_0,DM,... + regime_start(end)-1,binding_indicator,... + data.exo_pos,data.shocks_sequence(shock_period,:),endo_init,update_flag); + + [binding, relax, err]=feval([M_.fname,'.eval_difference'],zdatalinear_,M_,dr_base.ys); + + % check if changes to the hypothesis of the duration for each + % regime + if any(binding.constraint_1 & ~binding_indicator) || any(relax.constraint_1 & binding_indicator) + err_viol = err.binding_constraint_1(binding.constraint_1 & ~binding_indicator); + err_relax = err.relax_constraint_1(relax.constraint_1 & binding_indicator); + max_err(iter) = max(abs([err_viol;err_relax])); + regime_change_this_iteration = true; + else + regime_change_this_iteration = false; + max_err(iter) = 0; + end + + if curb_retrench % apply Gauss-Seidel idea of slowing down the change in the guess + % for the constraint -- only relax one + % period at a time starting from the last + % one when each of the constraints is true. + retrench = false(numel(binding_indicator),1); + max_relax_constraint_1=find(relax.constraint_1 & binding_indicator,1,'last'); + if ~isempty(max_relax_constraint_1) && find(relax.constraint_1,1,'last')>=find(binding_indicator,1,'last') + retrench(max_relax_constraint_1) = true; + end + binding_indicator = (binding_indicator | binding.constraint_1) & ~ retrench; + else + binding_indicator= (binding_indicator | binding.constraint_1) & ~(binding_indicator & relax.constraint_1); + end + + if iter>1 && regime_change_this_iteration + for kiter=1:iter-1 + vvv = [binding_indicator_history{kiter}; false(size(binding_indicator,1)- size(binding_indicator_history{kiter},1), 1)]; + is_periodic(kiter) = isequal(vvv, binding_indicator); + end + is_periodic_all =is_periodic; + is_periodic = any(is_periodic); + if is_periodic && periodic_solution + [merr,imerr]=min(max_err(find(is_periodic_all,1):end)); + inx = find(is_periodic_all,1):iter; + inx = inx(imerr); + binding_indicator=binding_indicator_history{inx}; + if inx1 + SS=SS(2:end); + else + SS=[]; + end + if isempty(SS) + SS_out.T(:,:,shock_period)= DM.decrulea; + SS_out.R(:,:,shock_period)= DM.decruleb; + SS_out.C(:,shock_period)= 0; + else + SS_out.T(:,:,shock_period)= SS(1).T; + SS_out.R(:,:,shock_period)= SS(1).R; + SS_out.C(:,shock_period)= SS(1).C; + end + binding_indicator_history{iter}=binding_indicator; + end + + end + + if regime_change_this_iteration ==1 && max_iter>1 + disp_verbose(['occbin solver:: period ' int2str(shock_period) '::'],opts_simul_.debug) + if is_periodic + disp_verbose('Occbin solver loops between two regimes.',opts_simul_.debug) + if periodic_solution + disp_verbose(['Max error:' num2str(merr) '.'],opts_simul_.debug) + else + if opts_simul_.waitbar; dyn_waitbar_close(hh); end + error_flag = 310; + return + end + else + disp_verbose('Did not converge -- increase maxit.',opts_simul_.debug) + if opts_simul_.waitbar; dyn_waitbar_close(hh); end + error_flag = 311; + return + end + end + if any(error_code_period) + disp_verbose('Increase nperiods.',opts_simul_.debug) + if opts_simul_.waitbar; dyn_waitbar_close(hh); end + error_flag = 312; + return + end + + endo_init = zdatalinear_(1,:); + zdatapiecewise_(shock_period,:)=endo_init; + endo_init= endo_init'; + + % reset binding_indicator for next period's shock -- this resetting is + % consistent with expecting no additional shocks + binding_indicator=[binding_indicator(2:end); false]; + +end + +% if necessary, fill in the rest of the path with the remainder of the +% last IRF computed. +zdatapiecewise_(n_shocks_periods+1:end,:)=zdatalinear_(2:n_periods-n_shocks_periods+1,:); + +data.piecewise=zdatapiecewise_; +data.regime_history=regime_history; + +if ~opts_simul_.piecewise_only + % get the linear responses + data.linear = occbin.mkdata(max(n_periods,size(data.shocks_sequence,1)),... + DM.decrulea,DM.decruleb,endo_names,exo_names,... + [],data.exo_pos,data.shocks_sequence,init_orig_); +end + +if opts_simul_.waitbar + dyn_waitbar_close(hh); +end \ No newline at end of file diff --git a/matlab/+occbin/solve_two_constraints.m b/matlab/+occbin/solve_two_constraints.m new file mode 100644 index 000000000..3b3d267d7 --- /dev/null +++ b/matlab/+occbin/solve_two_constraints.m @@ -0,0 +1,321 @@ +function [ data, SS_out, error_flag] = solve_two_constraints(M_,dr, opts_simul_, solve_DM) +% function [ data, SS_out, error_flag] = solve_two_constraints(M_,dr, opts_simul_, solve_DM) +% +% INPUT: +% - M_ [structure] Matlab's structure describing the model (M_). +% - dr [structure] decision rules for the model +% - opts_simul [structure] Matlab's structure containing the Occbin options (opts_simul). +% - solve_DM [double] indicator on whether to recompute decision rules +% +% OUTPUT: +% - data [structure] simulation result containing fields: +% - linear: paths for endogenous variables ignoring OBC (linear solution) +% - piecewise: paths for endogenous variables satisfying the OBC (occbin/piecewise solution) +% - ys: vector of steady state values +% - regime_history: information on number and time of regime transitions +% - SS_out [structure] State space solution +% - T: [n_vars by n_vars by n_shock_period] array of transition matrices +% - R: [n_vars by n_exo by n_shock_period] array of shock response matrices +% - C: [n_vars by n_shock_period] array of constants +% - error_flag [integer] 1 if a problem was encoutered, 0 otherwise + +% Original authors: Luca Guerrieri and Matteo Iacoviello +% Original file downloaded from: +% https://www.matteoiacoviello.com/research_files/occbin_20140630.zip +% Adapted for Dynare by Dynare Team. +% +% This code is in the public domain and may be used freely. +% However the authors would appreciate acknowledgement of the source by +% citation of any of the following papers: +% +% Luca Guerrieri and Matteo Iacoviello (2015): "OccBin: A toolkit for solving +% dynamic models with occasionally binding constraints easily" +% Journal of Monetary Economics 70, 22-38 + +persistent DM + +if isempty(DM) + solve_DM=true; +end + +data.shocks_sequence= opts_simul_.SHOCKS; % sequence of unforeseen shocks under which one wants to solve the model +n_periods = opts_simul_.periods; % simulation horizon (can be longer than the sequence of shocks defined in shockssequence; must be long enough to ensure convergence back to the reference model at the end of the simulation horizon and may need to be varied depending on the sequence of shocks). +curb_retrench = opts_simul_.curb_retrench; % 0: updates guess based on previous iteration; 1: updates similar to Gauss-Jacobi scheme, slowing iterations down by updating guess only one period at a time +max_iter = opts_simul_.maxit; % maximum number of iterations allowed for the solution algorithm +endo_init = opts_simul_.endo_init; % initial condition for state variables, in deviation from steady state in declaration order +binding_indicator = opts_simul_.init_binding_indicator; % initial guess for constraint violations +regime_history_guess = opts_simul_.init_regime; % initial guess for constraint violations +periodic_solution = opts_simul_.periodic_solution; +data.exo_pos = opts_simul_.exo_pos; + +n_shocks_periods = size(data.shocks_sequence,1); + +if n_periods < n_shocks_periods + n_periods = n_shocks_periods; +end +nperiods_0 = max(opts_simul_.check_ahead_periods,n_periods-n_shocks_periods); + +error_flag=0; + +M00_ = M_; + +% ensure that all models have the same parameters +% use the parameters for the base model. + +%keep the correct auxiliary regime specific parameter values + +data.ys = dr.ys; + +if solve_DM %recompute solution matrices + [DM.Cbarmat ,DM.Bbarmat, DM.Abarmat, DM.Jbarmat] = occbin.get_deriv(M00_,data.ys); + + M10_ = M00_; + M10_.params(strmatch(M_.occbin.constraint(1).pswitch,M00_.param_names,'exact'))= 1; + [DM.Cbarmat10, DM.Bbarmat10, DM.Abarmat10, DM.Jbarmat10, DM.Dbarmat10] = occbin.get_deriv(M10_,data.ys); + + M01_ = M00_; + M01_.params(strmatch(M_.occbin.constraint(2).pswitch,M00_.param_names,'exact'))= 1; + [DM.Cbarmat01, DM.Bbarmat01, DM.Abarmat01, DM.Jbarmat01, DM.Dbarmat01] = occbin.get_deriv(M01_,data.ys); + + M11_ = M00_; + M11_.params(M_.occbin.constraint(1).pswitch_index)= 1; + M11_.params(M_.occbin.constraint(2).pswitch_index)= 1; + [DM.Cbarmat11, DM.Bbarmat11, DM.Abarmat11, DM.Jbarmat11, DM.Dbarmat11] = occbin.get_deriv(M11_,data.ys); + + [DM.decrulea,DM.decruleb]=occbin.get_pq(dr); + update_flag=true; + DM.n_vars = M00_.endo_nbr; + DM.n_exo = M00_.exo_nbr; +else + update_flag=false; +end + +endo_names = M00_.endo_names; +exo_names = M00_.exo_names; + +init_orig_ = endo_init; + +zdatapiecewise_ = zeros(n_periods,DM.n_vars); + +if ~exist('binding_indicator','var') + binding_indicator = false(nperiods_0+1,2); % This sets the first guess for when + % the constraints are going to hold. + % The variable is a boolean with two columns. The first column refers to + % constrain1_; the second to constrain2_. + % Each row is a period in time. + % If the boolean is true it indicates the relevant constraint is expected + % to evaluate to true. + % The default initial guess is consistent with the base model always + % holding -- equivalent to the linear solution. +else + if size(binding_indicator,1)<(nperiods_0+1) + binding_indicator = [binding_indicator; false(nperiods_0+1-size(binding_indicator,1),2)]; + end +end +SS_out.T = NaN(DM.n_vars,DM.n_vars,n_shocks_periods); +SS_out.R = NaN(DM.n_vars,DM.n_exo,n_shocks_periods); +SS_out.C = NaN(DM.n_vars,n_shocks_periods); +if ~exist('regime_history_','var') || isempty(regime_history_guess) + regime_history = struct(); + guess_history = false; +else + guess_history = true; %previous information exists + regime_history = regime_history_guess; +end + +if opts_simul_.waitbar + hh = dyn_waitbar(0,'Occbin: Solving the model'); + set(hh,'Name','Occbin: Solving the model.'); +end + +for shock_period = 1:n_shocks_periods + if opts_simul_.waitbar + dyn_waitbar(shock_period/n_shocks_periods, hh, sprintf('Period %u of %u', shock_period,n_shocks_periods)); + end + regime_change_this_iteration=true; + iter = 0; + guess_history_it = false; + if guess_history && (shock_period<=length(regime_history_guess)) %beyond guess regime history + guess_history_it = true; + end + is_periodic=false; + binding_indicator_history={}; + max_err = NaN(max_iter,1); + + while (regime_change_this_iteration && iter1 && any(data.shocks_sequence(shock_period,:)) % first period or shock happening + Tmax=max([regime_start_1(end) regime_start_2(end)])-1; + [zdatalinear_, SS_out.T(:,:,shock_period), SS_out.R(:,:,shock_period), SS_out.C(:,shock_period), SS, update_flag]=occbin.mkdatap_anticipated_2constraints_dyn(nperiods_0,... + DM,Tmax,... + binding_indicator,... + data.exo_pos,data.shocks_sequence(shock_period,:),endo_init, update_flag); + + [binding, relax, err]=feval([M_.fname,'.eval_difference'],zdatalinear_,M_,dr.ys); + binding_constraint_new=[binding.constraint_1;binding.constraint_2]; + relaxed_constraint_new = [relax.constraint_1;relax.constraint_2]; + + err_binding_constraint_new = [err.binding_constraint_1; err.binding_constraint_2]; + err_relaxed_constraint_new = [err.relax_constraint_1; err.relax_constraint_2]; + + % check if changes_ + if any(binding_constraint_new & ~binding_indicator(:)) || any(relaxed_constraint_new & binding_indicator(:)) + err_violation = err_binding_constraint_new(binding_constraint_new & ~binding_indicator(:)); + err_relax = err_relaxed_constraint_new(relaxed_constraint_new & binding_indicator(:)); + max_err(iter) = max(abs([err_violation;err_relax])); + regime_change_this_iteration = true; + else + regime_change_this_iteration = false; + max_err(iter) = 0; + end + + if curb_retrench % apply Gauss-Seidel idea of slowing down the change in the guess + % for the constraint -- only relax one + % period at a time starting from the last + % one when each of the constraints is true. + retrench = false(numel(binding_indicator),1); + max_relax_constraint_1=find(relax.constraint_1 & binding_indicator(:,1),1,'last'); + if ~isempty(max_relax_constraint_1) && find(relax.constraint_1,1,'last')>=find(binding_indicator(:,1),1,'last') + retrench(max_relax_constraint_1) = true; + end + max_relax_constraint_2=find(relax.constraint_2 & binding_indicator(:,2),1,'last'); + if ~isempty(max_relax_constraint_2) && find(relax.constraint_2,1,'last')>=find(binding_indicator(:,2),1,'last') + retrench(max_relax_constraint_2+nperiods_0+1) = true; + end + binding_indicator = (binding_indicator(:) | binding_constraint_new) & ~ retrench; + else + binding_indicator= (binding_indicator(:) | binding_constraint_new) & ~(binding_indicator(:) & relaxed_constraint_new); + end + binding_indicator = reshape(binding_indicator,nperiods_0+1,2); + + if iter>1 && regime_change_this_iteration + is_periodic=false(1,iter-1); + for kiter=1:iter-1 + vvv = [binding_indicator_history{kiter}; false(size(binding_indicator,1)- size(binding_indicator_history{kiter},1), 2)]; + is_periodic(kiter) = isequal(vvv, binding_indicator); + end + is_periodic_all = is_periodic; + is_periodic = any(is_periodic); + if is_periodic && periodic_solution + [min_err,index_min_err]=min(max_err(find(is_periodic_all,1):end)); + inx = find(is_periodic_all,1):iter; + inx = inx(index_min_err); + binding_indicator=binding_indicator_history{inx}; %select regime history with same result, but smallest error + if inx1 + SS=SS(2:end); + else + SS=[]; + end + if isempty(SS) + SS_out.T(:,:,shock_period)= DM.decrulea; + SS_out.R(:,:,shock_period)= DM.decruleb; + SS_out.C(:,shock_period)= 0; + else + SS_out.T(:,:,shock_period)= SS(1).T; + SS_out.R(:,:,shock_period)= SS(1).R; + SS_out.C(:,shock_period)= SS(1).C; + end + binding_indicator_history{iter}=binding_indicator; + end + end + if regime_change_this_iteration && max_iter>1 + disp_verbose(['occbin solver: period ' int2str(shock_period) ':'],opts_simul_.debug) + if is_periodic + disp_verbose('Occbin solver loops between two regimes.',opts_simul_.debug) + if periodic_solution + disp_verbose(['Max error:' num2str(min_err) '.'],opts_simul_.debug) + else + error_flag = 310; + if opts_simul_.waitbar; dyn_waitbar_close(hh); end + return; + end + else + disp_verbose('Did not converge -- increase maxit.',opts_simul_.debug) + error_flag = 311; + if opts_simul_.waitbar; dyn_waitbar_close(hh); end + return; + end + end + if any(error_code_period) + disp_verbose('Increase nperiods.',opts_simul_.debug) + error_flag = 312; + if opts_simul_.waitbar; dyn_waitbar_close(hh); end + return; + end + + endo_init = zdatalinear_(1,:); + zdatapiecewise_(shock_period,:)=endo_init; + endo_init= endo_init'; + + % update the guess for constraint violations for next period + % update is consistent with expecting no additional shocks next period + binding_indicator=[binding_indicator(2:end,:); false(1,2)]; + +end + +zdatapiecewise_(shock_period+1:end,:)=zdatalinear_(2:n_periods-shock_period+1,:); + +data.piecewise=zdatapiecewise_; +data.regime_history=regime_history; + +if ~opts_simul_.piecewise_only + % get the linear responses + data.linear = occbin.mkdata(n_periods,DM.decrulea,DM.decruleb,endo_names,exo_names,[],data.exo_pos,data.shocks_sequence,init_orig_); +end + +if opts_simul_.waitbar + dyn_waitbar_close(hh); +end \ No newline at end of file diff --git a/matlab/+occbin/solver.m b/matlab/+occbin/solver.m new file mode 100644 index 000000000..4877125fa --- /dev/null +++ b/matlab/+occbin/solver.m @@ -0,0 +1,83 @@ +function [oo_, out, ss] = solver(M_,oo_,options_) +% function [oo_, out, ss] = solver(M_,oo_,options_,opts_simul) +% Solves the model with an OBC and produces simulations/IRFs +% +% INPUT: +% - opts_simul [structure] Occbin simulation options +% - M_ [structure] Matlab's structure describing the model +% - options_ [structure] Matlab's structure containing the options +% +% OUTPUT: +% - oo_ [structure] Matlab's structure containing the results +% - out [structure] simulation result containing fields: +% - linear: paths for endogenous variables ignoring OBC (linear solution) +% - piecewise: paths for endogenous variables satisfying the OBC (occbin/piecewise solution) +% - ys: vector of steady state values +% - regime_history: information on number and time of regime transitions +% - ss [structure] State space solution +% - T: [n_vars by n_vars by n_shock_period] array of transition matrices +% - R: [n_vars by n_exo by n_shock_period] array of shock response matrices +% - C: [n_vars by n_shock_period] array of constants + +% Copyright (C) 2021 Dynare Team +% +% This file is part of Dynare. +% +% Dynare is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% Dynare is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with Dynare. If not, see . + +persistent sto_M sto_dr + +% check dr +solve_dr=0; +if isempty(sto_M) || isempty(sto_dr) + solve_dr=1; +else + inan = find(~isnan(M_.params)); + inan0 = find(~isnan(sto_M.params)); + if ~isequal(inan,inan0) || ~isequal(sto_M.params(inan),M_.params(inan)) + solve_dr=1; + end +end + +if solve_dr + [dr,error_flag,M_,oo_] = resol(0,M_,options_,oo_); + oo_.dr = dr; + sto_dr=dr; + sto_M=M_; +else + oo_.dr=sto_dr; +end + +if M_.occbin.constraint_nbr==1 + [out, ss, error_flag ] = occbin.solve_one_constraint(M_,oo_.dr,options_.occbin.simul,solve_dr); +elseif M_.occbin.constraint_nbr==2 + [out, ss, error_flag ] = occbin.solve_two_constraints(M_,oo_.dr,options_.occbin.simul,solve_dr); +end + +out.error_flag=error_flag; +if error_flag + print_info(error_flag, options_.noprint, options_) + out=[]; + return; +end + +% add back steady state +if ~options_.occbin.simul.piecewise_only + out.linear = out.linear + out.ys'; +end +out.piecewise = out.piecewise+ out.ys'; +out.exo_simul = options_.occbin.simul.SHOCKS; +out.exo_pos = options_.occbin.simul.exo_pos; + +oo_.occbin=out; diff --git a/matlab/+occbin/tokenize.m b/matlab/+occbin/tokenize.m new file mode 100644 index 000000000..df87c6a13 --- /dev/null +++ b/matlab/+occbin/tokenize.m @@ -0,0 +1,66 @@ +function tokens = tokenize(source,delimiter) +%function tokens = tokenize(source,delimiter) +% Breaks down strings into its components (tokens) +% INPUTS +% - source [string] string to be broken into tokens +% - delimiter [char array] single character delimiters +% +% OUTPUTS: +% -tokens [cell] array containing the tokens + +% Original authors: Luca Guerrieri and Matteo Iacoviello +% Original file downloaded from: +% https://www.matteoiacoviello.com/research_files/occbin_20140630.zip +% Adapted for Dynare by Dynare Team. +% +% This code is in the public domain and may be used freely. +% However the authors would appreciate acknowledgement of the source by +% citation of any of the following papers: +% +% Luca Guerrieri and Matteo Iacoviello (2015): "OccBin: A toolkit for solving +% dynamic models with occasionally binding constraints easily" +% Journal of Monetary Economics 70, 22-38 + +posdelims = []; + +% assumes that delimiter cannot be in the first position or the last position +ndelimiters = size(delimiter,1); +for i=1:ndelimiters + newpositions = strfind(source,delimiter(i,:)); + if ~isempty(newpositions) + posdelims =[posdelims, newpositions]; + end +end + +% reorder posdelims in ascending order +posdelims = sort(posdelims); + +if isempty(posdelims) + tokens = cellstr(source); +else + ndelims = length(posdelims); + % build positions for substrings + delims = zeros(ndelims+1,2); + for i=1:ndelims+1 + if i==1 + if posdelims(1) == 1 + tokens = cellstr(source(1)); + else + delims(i,:) = [1,posdelims(i)-1]; + tokens = cellstr(source([delims(i,1):delims(i,2)])); + tokens = [tokens, source(posdelims(i))]; + end + elseif i==ndelims+1 + if (posdelims(i-1) < length(source)) + delims(i,:) = [posdelims(i-1)+1,length(source)]; + tokens = [tokens, cellstr(source([delims(i,1):delims(i,2)]))]; + end + else + if posdelims(i)>posdelims(i-1)+1 + delims(i,:) = [posdelims(i-1)+1,posdelims(i)-1]; + tokens = [tokens, cellstr(source([delims(i,1):delims(i,2)]))]; + end + tokens = [tokens, source(posdelims(i))]; + end + end +end \ No newline at end of file diff --git a/matlab/+occbin/unpack_simulations.m b/matlab/+occbin/unpack_simulations.m new file mode 100644 index 000000000..4f102690c --- /dev/null +++ b/matlab/+occbin/unpack_simulations.m @@ -0,0 +1,38 @@ +function oo_=unpack_simulations(M_,oo_,options_) +% function oo_=unpack_simulations(M_,oo_,options_) +% Writes Occbin simulations from matrix to structure +% +% Inputs +% - M_ [structure] Matlab's structure describing the model +% - oo_ [structure] Matlab's structure containing the results +% - options_ [structure] Matlab's structure containing the options +% +% Outputs +% - oo_ [structure] Matlab's structure containing the results + +% Copyright (C) 2021 Dynare Team +% +% This file is part of Dynare. +% +% Dynare is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% Dynare is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with Dynare. If not, see . + +for i=1:M_.endo_nbr + % unpack the IRFs + oo_.occbin.endo_linear.(M_.endo_names{i})= oo_.occbin.linear(:,i); + oo_.occbin.endo_piecewise.(M_.endo_names{i})=oo_.occbin.piecewise(:,i); + oo_.occbin.endo_ss.(M_.endo_names{i})=oo_.occbin.ys(i); +end +for i=1:length(oo_.occbin.exo_pos) + oo_.occbin.exo.(M_.exo_names{i})=options_.occbin.simul.SHOCKS(:,i); +end \ No newline at end of file diff --git a/matlab/+occbin/write_regimes_to_xls.m b/matlab/+occbin/write_regimes_to_xls.m new file mode 100644 index 000000000..c74131fc5 --- /dev/null +++ b/matlab/+occbin/write_regimes_to_xls.m @@ -0,0 +1,64 @@ +function write_regimes_to_xls(regime_history,M_,options_) +% function write_regimes_to_xls(regime_history,M_,options_) +% writes regime results to Excel-file +% +% INPUTS +% - regime_history [struct] information on the regimes +% - M_ [struct] Matlab's structure describing the model +% - options_ [struct] Matlab's structure describing the current options + +% Copyright (C) 2021 Dynare Team +% +% This file is part of Dynare. +% +% Dynare is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% Dynare is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with Dynare. If not, see . + +OutputDirectoryName = CheckPath('Output',M_.dname); + +if isempty(options_.occbin.write_regimes.periods) + T=1:length(regime_history); +else + T=options_.occbin.write_regimes.periods; +end + +xls_filename = options_.occbin.write_regimes.filename; + +if isfield(regime_history,'regime') + Header = {'time', 'regime sequence', 'starting period of regime'}; + for tp=1:length(T) + xlsmat{tp,1}=T(tp); + xlsmat{tp,2}=int2str(regime_history(tp).regime); + xlsmat{tp,3}=int2str(regime_history(tp).regimestart); + end +else + Header = {'time', 'regime sequence 1', 'starting period of regime 1', 'regime sequence 2', 'starting period of regime 2'}; + for tp=1:length(T) + xlsmat{tp,1}=T(tp); + xlsmat{tp,2}=int2str(regime_history(tp).regime1); + xlsmat{tp,3}=int2str(regime_history(tp).regimestart1); + xlsmat{tp,4}=int2str(regime_history(tp).regime2); + xlsmat{tp,5}=int2str(regime_history(tp).regimestart2); + end +end +filename=[OutputDirectoryName filesep xls_filename '.xls']; +if matlab_ver_less_than('9.3') + if exist(filename,'file') + delete(filename) + end +else + if isfile(filename) + delete(filename) + end +end +writetable(array2table(xlsmat,'VariableNames',Header), filename, 'Sheet', 'Regimes'); diff --git a/matlab/DsgeSmoother.m b/matlab/DsgeSmoother.m index 8b3063f6f..180300c51 100644 --- a/matlab/DsgeSmoother.m +++ b/matlab/DsgeSmoother.m @@ -1,4 +1,4 @@ -function [alphahat,etahat,epsilonhat,ahat,SteadyState,trend_coeff,aK,T,R,P,PK,decomp,trend_addition,state_uncertainty,M_,oo_,bayestopt_] = DsgeSmoother(xparam1,gend,Y,data_index,missing_value,M_,oo_,options_,bayestopt_,estim_params_) +function [alphahat,etahat,epsilonhat,ahat,SteadyState,trend_coeff,aK,T,R,P,PK,decomp,trend_addition,state_uncertainty,M_,oo_,bayestopt_] = DsgeSmoother(xparam1,gend,Y,data_index,missing_value,M_,oo_,options_,bayestopt_,estim_params_,varargin) % Estimation of the smoothed variables and innovations. % % INPUTS @@ -96,6 +96,7 @@ end %------------------------------------------------------------------------------ % 2. call model setup & reduction program %------------------------------------------------------------------------------ +length_varargin=length(varargin); if ~options_.smoother_redux %store old setting of restricted var_list @@ -111,9 +112,22 @@ if ~options_.smoother_redux bayestopt_.mf = bayestopt_.smoother_var_list(bayestopt_.smoother_mf); else - [T,R,SteadyState,info,M_,oo_] = dynare_resolve(M_,options_,oo_,'restrict'); + if ~options_.occbin.smoother.status + [T,R,SteadyState,info,M_,oo_] = dynare_resolve(M_,options_,oo_,'restrict'); + else + [T,R,SteadyState,info,M_,oo_,~,~,~, T0, R0] = ... + occbin.dynare_resolve(M_,options_,oo_,[],'restrict'); + varargin{length_varargin+1}=T0; + varargin{length_varargin+2}=R0; + end bayestopt_.mf = bayestopt_.mf1; end +if options_.occbin.smoother.status + occbin_info.status = true; + occbin_info.info= [{options_,oo_,M_} varargin]; +else + occbin_info.status = false; +end if info~=0 print_info(info,options_.noprint, options_); @@ -179,7 +193,7 @@ elseif options_.lik_init == 3 % Diffuse Kalman filter kalman_algo = 3; else if ~all(all(abs(H-diag(diag(H)))<1e-14))% ie, the covariance matrix is not diagonal... - %Augment state vector (follows Section 6.4.3 of DK (2012)) + %Augment state vector (follows Section 6.4.3 of DK (2012)) expanded_state_vector_for_univariate_filter=1; T = blkdiag(T,zeros(vobs)); np = size(T,1); @@ -236,14 +250,23 @@ if options_.heteroskedastic_filter Q=get_Qvec_heteroskedastic_filter(Q,smpl,M_); end +if options_.occbin.smoother.status + if kalman_algo == 1 + kalman_algo = 2; + end + if kalman_algo == 3 + kalman_algo = 4; + end +end + if kalman_algo == 1 || kalman_algo == 3 a_initial = zeros(np,1); a_initial=set_Kalman_smoother_starting_values(a_initial,M_,oo_,options_); - a_initial=T*a_initial; %set state prediction for first Kalman step; + a_initial=T*a_initial; %set state prediction for first Kalman step; [alphahat,epsilonhat,etahat,ahat,P,aK,PK,decomp,state_uncertainty, aahat, eehat, d] = missing_DiffuseKalmanSmootherH1_Z(a_initial,ST, ... - Z,R1,Q,H,Pinf,Pstar, ... - data1,vobs,np,smpl,data_index, ... - options_.nk,kalman_tol,diffuse_kalman_tol,options_.filter_decomposition,options_.smoothed_state_uncertainty,options_.filter_covariance,options_.smoother_redux); + Z,R1,Q,H,Pinf,Pstar, ... + data1,vobs,np,smpl,data_index, ... + options_.nk,kalman_tol,diffuse_kalman_tol,options_.filter_decomposition,options_.smoothed_state_uncertainty,options_.filter_covariance,options_.smoother_redux); if isinf(alphahat) if kalman_algo == 1 fprintf('\nDsgeSmoother: Switching to univariate filter. This may be a sign of stochastic singularity.\n') @@ -292,16 +315,18 @@ if kalman_algo == 2 || kalman_algo == 4 %do nothing, state vector was already expanded end end - + a_initial = zeros(np,1); a_initial=set_Kalman_smoother_starting_values(a_initial,M_,oo_,options_); a_initial=ST*a_initial; %set state prediction for first Kalman step; - [alphahat,epsilonhat,etahat,ahat,P,aK,PK,decomp,state_uncertainty, aahat, eehat, d] = missing_DiffuseKalmanSmootherH3_Z(a_initial,ST, ... - Z,R1,Q,diag(H), ... - Pinf,Pstar,data1,vobs,np,smpl,data_index, ... - options_.nk,kalman_tol,diffuse_kalman_tol, ... - options_.filter_decomposition,options_.smoothed_state_uncertainty,options_.filter_covariance,options_.smoother_redux); - + [alphahat,epsilonhat,etahat,ahat,P,aK,PK,decomp,state_uncertainty, aahat, eehat, d, regimes_,TT,RR,CC] = missing_DiffuseKalmanSmootherH3_Z(a_initial,ST, ... + Z,R1,Q,diag(H), ... + Pinf,Pstar,data1,vobs,np,smpl,data_index, ... + options_.nk,kalman_tol,diffuse_kalman_tol, ... + options_.filter_decomposition,options_.smoothed_state_uncertainty,options_.filter_covariance,options_.smoother_redux,occbin_info); + if options_.occbin.smoother.status + oo_.occbin.smoother.regime_history = regimes_; + end end if expanded_state_vector_for_univariate_filter && (kalman_algo == 2 || kalman_algo == 4) @@ -327,7 +352,7 @@ if expanded_state_vector_for_univariate_filter && (kalman_algo == 2 || kalman_al end end -if ~options_.smoother_redux +if ~options_.smoother_redux %reset old setting of restricted var_list oo_.dr.restrict_var_list = oldoo.restrict_var_list; oo_.dr.restrict_columns = oldoo.restrict_columns; @@ -338,107 +363,238 @@ else ic = oo_.dr.restrict_columns; end - [A,B] = kalman_transition_matrix(oo_.dr,(1:M_.endo_nbr)',ic,M_.exo_nbr); - iT = pinv(T); - Tstar = A(~ismember(1:M_.endo_nbr,oo_.dr.restrict_var_list),oo_.dr.restrict_var_list); - Rstar = B(~ismember(1:M_.endo_nbr,oo_.dr.restrict_var_list),:); - C = Tstar*iT; - D = Rstar-C*R; - static_var_list = ~ismember(1:M_.endo_nbr,oo_.dr.restrict_var_list); - ilagged = any(abs(C*T-Tstar)'>1.e-12); - static_var_list0 = static_var_list; - static_var_list0(static_var_list) = ilagged; - static_var_list(static_var_list) = ~ilagged; - % reconstruct smoothed variables - aaa=zeros(M_.endo_nbr,gend); - aaa(oo_.dr.restrict_var_list,:)=alphahat; - for k=1:gend - aaa(static_var_list,k) = C(~ilagged,:)*alphahat(:,k)+D(~ilagged,:)*etahat(:,k); - end - if any(ilagged) + if options_.occbin.smoother.status + % reconstruct occbin smoother + if length_varargin>0 + isoccbin=1; + else + isoccbin=0; + end + if length_varargin>1 + TT=varargin{2}; + RR=varargin{3}; + CC=varargin{4}; + if size(TT,3)<(smpl+1) + TT=repmat(T,1,1,smpl+1); + RR=repmat(R,1,1,smpl+1); + CC=repmat(zeros(mm,1),1,smpl+1); + end + end + if isoccbin==0 + [A,B] = kalman_transition_matrix(oo_.dr,(1:M_.endo_nbr)',ic,M_.exo_nbr); + else + opts_simul = options_.occbin.simul; + end + aaa=zeros(M_.endo_nbr,gend); + aaa(oo_.dr.restrict_var_list,:)=alphahat; for k=2:gend - aaa(static_var_list0,k) = Tstar(ilagged,:)*alphahat(:,k-1)+Rstar(ilagged,:)*etahat(:,k); + if isoccbin + A = TT(:,:,k); + B = RR(:,:,k); + C = CC(:,k); + else + C=0; + end + aaa(:,k) = C+A*aaa(:,k-1)+B*etahat(:,k); end - end - alphahat=aaa; - - % reconstruct updated variables - aaa=zeros(M_.endo_nbr,gend); - aaa(oo_.dr.restrict_var_list,:)=ahat; - for k=1:gend - aaa(static_var_list,k) = C(~ilagged,:)*ahat(:,k)+D(~ilagged,:)*eehat(:,k); - end - if any(ilagged) + alphahat=aaa; + aaa=zeros(M_.endo_nbr,gend); + bbb=zeros(M_.endo_nbr,gend); + bbb(oo_.dr.restrict_var_list,:)=ahat; + aaa(oo_.dr.restrict_var_list,:)=aahat; for k=d+2:gend - aaa(static_var_list0,k) = Tstar(ilagged,:)*aahat(:,k-1)+Rstar(ilagged,:)*eehat(:,k); - end - end - ahat1=aaa; - % reconstruct aK - aaa = zeros(options_.nk,M_.endo_nbr,gend+options_.nk); - aaa(:,oo_.dr.restrict_var_list,:)=aK; - for k=1:gend - for jnk=1:options_.nk - aaa(jnk,static_var_list,k+jnk) = C(~ilagged,:)*dynare_squeeze(aK(jnk,:,k+jnk)); - end - end - if any(ilagged) - for k=1:gend - aaa(1,static_var_list0,k+1) = Tstar(ilagged,:)*ahat(:,k); - for jnk=2:options_.nk - aaa(jnk,static_var_list0,k+jnk) = Tstar(ilagged,:)*dynare_squeeze(aK(jnk-1,:,k+jnk-1)); + if isoccbin + A = TT(:,:,k); + B = RR(:,:,k); + C = CC(:,k); + bbb(:,k) = C+A*aaa(:,k-1)+B*eehat(:,k); + else + opts_simul.curb_retrench = options_.occbin.smoother.curb_retrench; + opts_simul.waitbar = options_.occbin.smoother.waitbar; + opts_simul.maxit = options_.occbin.smoother.maxit; + opts_simul.periods = options_.occbin.smoother.periods; + opts_simul.check_ahead_periods = options_.occbin.smoother.check_ahead_periods; + opts_simul.full_output = options_.occbin.smoother.full_output; + opts_simul.piecewise_only = options_.occbin.smoother.piecewise_only; + opts_simul.SHOCKS = zeros(options_.nk,M_.exo_nbr); + opts_simul.SHOCKS(1,:) = eehat(:,k); + tmp=zeros(M_.endo_nbr,1); + tmp(oo_.dr.restrict_var_list,1)=aahat(:,k-1); + opts_simul.endo_init = tmp(oo_.dr.inv_order_var,1); + opts_simul.init_regime = []; %regimes_(k); + options_.occbin.simul=opts_simul; + [~, out] = occbin.solver(M_,oo_,options_); + % regime in out should be identical to regimes_(k-2) moved one + % period ahead (so if regimestart was [1 5] it should be [1 4] + % in out + % end + bbb(oo_.dr.inv_order_var,k) = out.zpiece(1,:); end end - end - aK=aaa; - ahat=ahat1; - - % reconstruct P - if ~isempty(P) - PP=zeros(M_.endo_nbr,M_.endo_nbr,gend+1); - PP(oo_.dr.restrict_var_list,oo_.dr.restrict_var_list,:)=P; - DQD=D(~ilagged,:)*Q*transpose(D(~ilagged,:))+C(~ilagged,:)*R*Q*transpose(D(~ilagged,:))+D(~ilagged,:)*Q*transpose(C(~ilagged,:)*R); - DQR=D(~ilagged,:)*Q*transpose(R); - for k=1:gend+1 - PP(static_var_list,static_var_list,k)=C(~ilagged,:)*P(:,:,k)*C(~ilagged,:)'+DQD; - PP(static_var_list,oo_.dr.restrict_var_list,k)=C(~ilagged,:)*P(:,:,k)+DQR; - PP(oo_.dr.restrict_var_list,static_var_list,k)=transpose(PP(static_var_list,oo_.dr.restrict_var_list,k)); + ahat0=ahat; + ahat=bbb; + if ~isempty(P) + PP=zeros(M_.endo_nbr,M_.endo_nbr,gend+1); + PP(oo_.dr.restrict_var_list,oo_.dr.restrict_var_list,:)=P; + P=PP; + clear PP end - P=PP; - clear('PP'); - end - - % reconstruct state_uncertainty - if ~isempty(state_uncertainty) - mm=size(T,1); - ss=length(find(static_var_list)); - sstate_uncertainty=zeros(M_.endo_nbr,M_.endo_nbr,gend); - sstate_uncertainty(oo_.dr.restrict_var_list,oo_.dr.restrict_var_list,:)=state_uncertainty(1:mm,1:mm,:); - for k=1:gend - sstate_uncertainty(static_var_list,static_var_list,k)=[C(~ilagged,:) D(~ilagged,:)]*state_uncertainty(:,:,k)*[C(~ilagged,:) D(~ilagged,:)]'; - tmp = [C(~ilagged,:) D(~ilagged,:)]*state_uncertainty(:,:,k); - sstate_uncertainty(static_var_list,oo_.dr.restrict_var_list,k)=tmp(1:ss,1:mm); - sstate_uncertainty(oo_.dr.restrict_var_list,static_var_list,k)=transpose(sstate_uncertainty(static_var_list,oo_.dr.restrict_var_list,k)); - end - state_uncertainty=sstate_uncertainty; - clear('sstate_uncertainty'); - end - % reconstruct PK TO DO!! - if ~isempty(PK) - PP = zeros(options_.nk,M_.endo_nbr,M_.endo_nbr,gend+options_.nk); - PP(:,oo_.dr.restrict_var_list,oo_.dr.restrict_var_list,:) = PK; - DQD=D(~ilagged,:)*Q*transpose(D(~ilagged,:))+C(~ilagged,:)*R*Q*transpose(D(~ilagged,:))+D(~ilagged,:)*Q*transpose(C(~ilagged,:)*R); - DQR=D(~ilagged,:)*Q*transpose(R); - for f=1:options_.nk - for k=1:gend - PP(f,static_var_list,static_var_list,k+f)=C(~ilagged,:)*squeeze(PK(f,:,:,k+f))*C(~ilagged,:)'+DQD; - PP(f,static_var_list,oo_.dr.restrict_var_list,k+f)=C(~ilagged,:)*squeeze(PK(f,:,:,k+f))+DQR; - PP(f,oo_.dr.restrict_var_list,static_var_list,k+f)=transpose(squeeze(PP(f,static_var_list,oo_.dr.restrict_var_list,k+f))); + if ~isempty(state_uncertainty) + sstate_uncertainty=zeros(M_.endo_nbr,M_.endo_nbr,gend); + sstate_uncertainty(oo_.dr.restrict_var_list,oo_.dr.restrict_var_list,:)=state_uncertainty; + state_uncertainty=sstate_uncertainty; + clear sstate_uncertainty + end + + aaa = zeros(options_.nk,M_.endo_nbr,gend+options_.nk); + aaa(:,oo_.dr.restrict_var_list,:)=aK; + + for k=2:gend+1 + opts_simul.curb_retrench = options_.occbin.smoother.curb_retrench; + opts_simul.waitbar = options_.occbin.smoother.waitbar; + opts_simul.maxit = options_.occbin.smoother.maxit; + opts_simul.periods = options_.occbin.smoother.periods; + opts_simul.check_ahead_periods = options_.occbin.smoother.check_ahead_periods; + opts_simul.full_output = options_.occbin.smoother.full_output; + opts_simul.piecewise_only = options_.occbin.smoother.piecewise_only; + opts_simul.SHOCKS = zeros(options_.nk,M_.exo_nbr); + tmp=zeros(M_.endo_nbr,1); + tmp(oo_.dr.restrict_var_list,1)=ahat0(:,k-1); + opts_simul.endo_init = tmp(oo_.dr.inv_order_var,1); + opts_simul.init_regime = []; %regimes_(k); + options_.occbin.simul=opts_simul; + [~, out] = occbin.solver(M_,oo_,options_); + % regime in out should be identical to regimes_(k-2) moved one + % period ahead (so if regimestart was [1 5] it should be [1 4] + % in out + % end + for jnk=1:options_.nk + aaa(jnk,oo_.dr.inv_order_var,k+jnk-1) = out.zpiece(jnk,:); end end - PK=PP; - clear('PP'); + aK=aaa; + + if ~isempty(PK) + PP = zeros(options_.nk,M_.endo_nbr,M_.endo_nbr,gend+options_.nk); + PP(:,oo_.dr.restrict_var_list,oo_.dr.restrict_var_list,:) = PK; + PK=PP; + clear PP + end + else + % reconstruct smoother + [A,B] = kalman_transition_matrix(oo_.dr,(1:M_.endo_nbr)',ic,M_.exo_nbr); + iT = pinv(T); + Tstar = A(~ismember(1:M_.endo_nbr,oo_.dr.restrict_var_list),oo_.dr.restrict_var_list); + Rstar = B(~ismember(1:M_.endo_nbr,oo_.dr.restrict_var_list),:); + C = Tstar*iT; + D = Rstar-C*R; + static_var_list = ~ismember(1:M_.endo_nbr,oo_.dr.restrict_var_list); + ilagged = any(abs(C*T-Tstar)'>1.e-12); + static_var_list0 = static_var_list; + static_var_list0(static_var_list) = ilagged; + static_var_list(static_var_list) = ~ilagged; + % reconstruct smoothed variables + aaa=zeros(M_.endo_nbr,gend); + aaa(oo_.dr.restrict_var_list,:)=alphahat; + for k=1:gend + aaa(static_var_list,k) = C(~ilagged,:)*alphahat(:,k)+D(~ilagged,:)*etahat(:,k); + end + if any(ilagged) + for k=2:gend + aaa(static_var_list0,k) = Tstar(ilagged,:)*alphahat(:,k-1)+Rstar(ilagged,:)*etahat(:,k); + end + end + alphahat=aaa; + + % reconstruct updated variables + aaa=zeros(M_.endo_nbr,gend); + aaa(oo_.dr.restrict_var_list,:)=ahat; + for k=1:gend + aaa(static_var_list,k) = C(~ilagged,:)*ahat(:,k)+D(~ilagged,:)*eehat(:,k); + end + if any(ilagged) + % bbb=zeros(M_.endo_nbr,gend); + % bbb(oo_.dr.restrict_var_list,:)=aahat; + for k=d+2:gend + aaa(static_var_list0,k) = Tstar(ilagged,:)*aahat(:,k-1)+Rstar(ilagged,:)*eehat(:,k); + end + end + ahat1=aaa; + % reconstruct aK + aaa = zeros(options_.nk,M_.endo_nbr,gend+options_.nk); + aaa(:,oo_.dr.restrict_var_list,:)=aK; + for k=1:gend + for jnk=1:options_.nk + aaa(jnk,static_var_list,k+jnk) = C(~ilagged,:)*dynare_squeeze(aK(jnk,:,k+jnk)); + end + end + if any(ilagged) + for k=1:gend + aaa(1,static_var_list0,k+1) = Tstar(ilagged,:)*ahat(:,k); + for jnk=2:options_.nk + aaa(jnk,static_var_list0,k+jnk) = Tstar(ilagged,:)*dynare_squeeze(aK(jnk-1,:,k+jnk-1)); + end + end + end + aK=aaa; + ahat=ahat1; + + % reconstruct P + if ~isempty(P) + PP=zeros(M_.endo_nbr,M_.endo_nbr,gend+1); + PP(oo_.dr.restrict_var_list,oo_.dr.restrict_var_list,:)=P; + if ~options_.heteroskedastic_filter + DQD=D(~ilagged,:)*Q*transpose(D(~ilagged,:))+C(~ilagged,:)*R*Q*transpose(D(~ilagged,:))+D(~ilagged,:)*Q*transpose(C(~ilagged,:)*R); + DQR=D(~ilagged,:)*Q*transpose(R); + end + for k=1:gend+1 + if options_.heteroskedastic_filter + DQD=D(~ilagged,:)*Q(:,:,k)*transpose(D(~ilagged,:))+C(~ilagged,:)*R*Q(:,:,k)*transpose(D(~ilagged,:))+D(~ilagged,:)*Q(:,:,k)*transpose(C(~ilagged,:)*R); + DQR=D(~ilagged,:)*Q(:,:,k)*transpose(R); + end + PP(static_var_list,static_var_list,k)=C(~ilagged,:)*P(:,:,k)*C(~ilagged,:)'+DQD; + PP(static_var_list,oo_.dr.restrict_var_list,k)=C(~ilagged,:)*P(:,:,k)+DQR; + PP(oo_.dr.restrict_var_list,static_var_list,k)=transpose(PP(static_var_list,oo_.dr.restrict_var_list,k)); + end + P=PP; + clear PP + end + + % reconstruct state_uncertainty + if ~isempty(state_uncertainty) + mm=size(T,1); + ss=length(find(static_var_list)); + sstate_uncertainty=zeros(M_.endo_nbr,M_.endo_nbr,gend); + sstate_uncertainty(oo_.dr.restrict_var_list,oo_.dr.restrict_var_list,:)=state_uncertainty(1:mm,1:mm,:); + for k=1:gend + sstate_uncertainty(static_var_list,static_var_list,k)=[C(~ilagged,:) D(~ilagged,:)]*state_uncertainty(:,:,k)*[C(~ilagged,:) D(~ilagged,:)]'; + tmp = [C(~ilagged,:) D(~ilagged,:)]*state_uncertainty(:,:,k); + sstate_uncertainty(static_var_list,oo_.dr.restrict_var_list,k)=tmp(1:ss,1:mm); + sstate_uncertainty(oo_.dr.restrict_var_list,static_var_list,k)=transpose(sstate_uncertainty(static_var_list,oo_.dr.restrict_var_list,k)); + end + state_uncertainty=sstate_uncertainty; + clear sstate_uncertainty + end + + % reconstruct PK + if ~isempty(PK) + PP = zeros(options_.nk,M_.endo_nbr,M_.endo_nbr,gend+options_.nk); + PP(:,oo_.dr.restrict_var_list,oo_.dr.restrict_var_list,:) = PK; + if ~options_.heteroskedastic_filter + DQD=D(~ilagged,:)*Q*transpose(D(~ilagged,:))+C(~ilagged,:)*R*Q*transpose(D(~ilagged,:))+D(~ilagged,:)*Q*transpose(C(~ilagged,:)*R); + DQR=D(~ilagged,:)*Q*transpose(R); + for f=1:options_.nk + for k=1:gend + PP(f,static_var_list,static_var_list,k+f)=C(~ilagged,:)*squeeze(PK(f,:,:,k+f))*C(~ilagged,:)'+DQD; + PP(f,static_var_list,oo_.dr.restrict_var_list,k+f)=C(~ilagged,:)*squeeze(PK(f,:,:,k+f))+DQR; + PP(f,oo_.dr.restrict_var_list,static_var_list,k+f)=transpose(squeeze(PP(f,static_var_list,oo_.dr.restrict_var_list,k+f))); + end + end + end + PK=PP; + clear PP + end end bayestopt_.mf = bayestopt_.smoother_var_list(bayestopt_.smoother_mf); @@ -446,14 +602,14 @@ end function a=set_Kalman_smoother_starting_values(a,M_,oo_,options_) % function a=set_Kalman_smoother_starting_values(a,M_,oo_,options_) -% Sets initial states guess for Kalman filter/smoother based on M_.filter_initial_state -% -% INPUTS +% Sets initial states guess for Kalman filter/smoother based on M_.filter_initial_state +% +% INPUTS % o a [double] (p*1) vector of states % o M_ [structure] decribing the model % o oo_ [structure] storing the results % o options_ [structure] describing the options -% +% % OUTPUTS % o a [double] (p*1) vector of set initial states diff --git a/matlab/default_option_values.m b/matlab/default_option_values.m index 1f8eb8395..4c529625d 100644 --- a/matlab/default_option_values.m +++ b/matlab/default_option_values.m @@ -412,6 +412,9 @@ options_.recursive_estimation_restart = 0; options_.MCMC_jumping_covariance='hessian'; options_.use_calibration_initialization = 0; options_.endo_vars_for_moment_computations_in_estimation=[]; +% occbin options +options_.occbin.likelihood.status=false; +options_.occbin.smoother.status=false; % Run optimizer silently options_.silent_optimizer = false; diff --git a/matlab/dsge_likelihood.m b/matlab/dsge_likelihood.m index 76f825311..85b4cc3ea 100644 --- a/matlab/dsge_likelihood.m +++ b/matlab/dsge_likelihood.m @@ -183,9 +183,34 @@ end %------------------------------------------------------------------------------ % 2. call model setup & reduction program %------------------------------------------------------------------------------ +is_restrict_state_space = true; +if DynareOptions.occbin.likelihood.status + occbin_options = set_occbin_options(DynareOptions, Model); + if occbin_options.opts_simul.restrict_state_space + [T,R,SteadyState,info,Model,DynareResults,TTx,RRx,CCx, T0, R0] = ... + occbin.dynare_resolve(Model,DynareOptions,DynareResults,[],'restrict'); + else + is_restrict_state_space = false; + oldoo.restrict_var_list = DynareResults.dr.restrict_var_list; + oldoo.restrict_columns = DynareResults.dr.restrict_columns; + DynareResults.dr.restrict_var_list = BayesInfo.smoother_var_list; + DynareResults.dr.restrict_columns = BayesInfo.smoother_restrict_columns; + + % Linearize the model around the deterministic steady state and extract the matrices of the state equation (T and R). + [T,R,SteadyState,info,Model,DynareOptions,DynareResults,TTx,RRx,CCx, T0, R0] = ... + occbin.dynare_resolve(Model,DynareOptions,DynareResults); -% Linearize the model around the deterministic steady state and extract the matrices of the state equation (T and R). -[T,R,SteadyState,info,Model,DynareResults] = dynare_resolve(Model,DynareOptions,DynareResults,'restrict'); + DynareResults.dr.restrict_var_list = oldoo.restrict_var_list; + DynareResults.dr.restrict_columns = oldoo.restrict_columns; + + end + occbin_.status = true; + occbin_.info= {DynareOptions, DynareResults, Model, occbin_options, TTx, RRx, CCx,T0,R0}; +else + % Linearize the model around the deterministic steady state and extract the matrices of the state equation (T and R). + [T,R,SteadyState,info,Model,DynareResults] = dynare_resolve(Model,DynareOptions,DynareResults,'restrict'); + occbin_.status = false; +end % Return, with endogenous penalty when possible, if dynare_resolve issues an error code (defined in resol). if info(1) @@ -223,8 +248,14 @@ if info(1) return end -% Define a vector of indices for the observed variables. Is this really usefull?... -BayesInfo.mf = BayesInfo.mf1; +if is_restrict_state_space +%% Define a vector of indices for the observed variables. Is this really usefull?... + BayesInfo.mf = BayesInfo.mf1; +else +%get location of observed variables and requested smoothed variables in +%decision rules + BayesInfo.mf = BayesInfo.smoother_var_list(BayesInfo.smoother_mf); +end % Define the constant vector of the measurement equation. if DynareOptions.noconstant @@ -283,7 +314,16 @@ switch DynareOptions.lik_init a = zeros(mm,1); a=set_Kalman_starting_values(a,Model,DynareResults,DynareOptions,BayesInfo); a_0_given_tm1=T*a; %set state prediction for first Kalman step; - Zflag = 0; + + if DynareOptions.occbin.likelihood.status + Z =zeros(length(BayesInfo.mf),size(T,1)); + for i = 1:length(BayesInfo.mf) + Z(i,BayesInfo.mf(i))=1; + end + Zflag = 1; + else + Zflag = 0; + end case 2% Initialization with large numbers on the diagonal of the covariance matrix if the states (for non stationary models). if kalman_algo ~= 2 % Use standard kalman filter except if the univariate filter is explicitely choosen. @@ -294,7 +334,15 @@ switch DynareOptions.lik_init a = zeros(mm,1); a = set_Kalman_starting_values(a,Model,DynareResults,DynareOptions,BayesInfo); a_0_given_tm1 = T*a; %set state prediction for first Kalman step; - Zflag = 0; + if DynareOptions.occbin.likelihood.status + Z =zeros(length(BayesInfo.mf),size(T,1)); + for i = 1:length(BayesInfo.mf) + Z(i,BayesInfo.mf(i))=1; + end + Zflag = 1; + else + Zflag = 0; + end case 3% Diffuse Kalman filter (Durbin and Koopman) % Use standard kalman filter except if the univariate filter is explicitely choosen. if kalman_algo == 0 @@ -421,7 +469,15 @@ switch DynareOptions.lik_init a = zeros(mm,1); a = set_Kalman_starting_values(a,Model,DynareResults,DynareOptions,BayesInfo); a_0_given_tm1 = T*a; - Zflag = 0; + if DynareOptions.occbin.likelihood.status + Z =zeros(length(BayesInfo.mf),size(T,1)); + for i = 1:length(BayesInfo.mf) + Z(i,BayesInfo.mf(i))=1; + end + Zflag = 1; + else + Zflag = 0; + end case 5 % Old diffuse Kalman filter only for the non stationary variables [eigenvect, eigenv] = eig(T); eigenv = diag(eigenv); @@ -443,7 +499,15 @@ switch DynareOptions.lik_init a = zeros(mm,1); a = set_Kalman_starting_values(a,Model,DynareResults,DynareOptions,BayesInfo); a_0_given_tm1 = T*a; - Zflag = 0; + if DynareOptions.occbin.likelihood.status + Z =zeros(length(BayesInfo.mf),size(T,1)); + for i = 1:length(BayesInfo.mf) + Z(i,BayesInfo.mf(i))=1; + end + Zflag = 1; + else + Zflag = 0; + end otherwise error('dsge_likelihood:: Unknown initialization approach for the Kalman filter!') end @@ -604,7 +668,7 @@ end singularity_has_been_detected = false; % First test multivariate filter if specified; potentially abort and use univariate filter instead if ((kalman_algo==1) || (kalman_algo==3))% Multivariate Kalman Filter - if no_missing_data_flag + if no_missing_data_flag && ~DynareOptions.occbin.likelihood.status if DynareOptions.block LIK = block_kalman_filter(T,R,Q,H,Pstar,Y,start,Z,kalman_tol,riccati_tol, Model.nz_state_var, Model.n_diag, Model.nobs_non_statevar); elseif DynareOptions.fast_kalman_filter @@ -643,7 +707,7 @@ if ((kalman_algo==1) || (kalman_algo==3))% Multivariate Kalman Filter kalman_tol, DynareOptions.riccati_tol, ... DynareOptions.rescale_prediction_error_covariance, ... DynareOptions.presample, ... - T,Q,R,H,Z,mm,pp,rr,Zflag,diffuse_periods); + T,Q,R,H,Z,mm,pp,rr,Zflag,diffuse_periods, occbin_); end end if analytic_derivation @@ -895,3 +959,21 @@ if isfield(M_,'filter_initial_state') && ~isempty(M_.filter_initial_state) end end +function occbin_options = set_occbin_options(DynareOptions, Model) + +% this builds the opts_simul options field needed by occbin.solver +occbin_options.opts_simul = DynareOptions.occbin.simul; +occbin_options.opts_simul.curb_retrench = DynareOptions.occbin.likelihood.curb_retrench; +occbin_options.opts_simul.maxit = DynareOptions.occbin.likelihood.maxit; +occbin_options.opts_simul.periods = DynareOptions.occbin.likelihood.periods; +occbin_options.opts_simul.check_ahead_periods = DynareOptions.occbin.likelihood.check_ahead_periods; +occbin_options.opts_simul.periodic_solution = DynareOptions.occbin.likelihood.periodic_solution; +occbin_options.opts_simul.restrict_state_space = DynareOptions.occbin.likelihood.restrict_state_space; +occbin_options.constraints = Model.occbin.constraint; + +occbin_options.opts_simul.full_output = DynareOptions.occbin.likelihood.full_output; +occbin_options.opts_simul.piecewise_only = DynareOptions.occbin.likelihood.piecewise_only; +if ~isempty(DynareOptions.occbin.smoother.init_binding_indicator) + occbin_options.opts_simul.init_binding_indicator = DynareOptions.occbin.likelihood.init_binding_indicator; + occbin_options.opts_simul.init_regime_history=DynareOptions.occbin.likelihood.init_regime_history; +end diff --git a/matlab/dynare_estimation_1.m b/matlab/dynare_estimation_1.m index 0efdf40c6..c44f9ecdf 100644 --- a/matlab/dynare_estimation_1.m +++ b/matlab/dynare_estimation_1.m @@ -41,7 +41,6 @@ else reset_options_related_to_estimation = false; end - %store qz_criterium qz_criterium_old=options_.qz_criterium; if isnan(options_.first_obs) @@ -102,7 +101,11 @@ if ~options_.dsge_var error(['Estimation: Unknown filter ' options_.particle.filter_algorithm]) end else - objective_function = str2func('dsge_likelihood'); + if options_.occbin.likelihood.status && options_.occbin.likelihood.inversion_filter + objective_function = str2func('occbin.IVF_posterior'); + else + objective_function = str2func('dsge_likelihood'); + end end else objective_function = str2func('dsge_var_likelihood'); @@ -176,8 +179,22 @@ end if isequal(options_.mode_compute,0) && isempty(options_.mode_file) && options_.mh_posterior_mode_estimation==0 if options_.order==1 && ~options_.particle.status if options_.smoother - [atT,innov,measurement_error,updated_variables,ys,trend_coeff,aK,T,R,P,PK,decomp,Trend,state_uncertainty,M_,oo_,bayestopt_] = DsgeSmoother(xparam1,gend,transpose(data),data_index,missing_value,M_,oo_,options_,bayestopt_,estim_params_); - [oo_]=store_smoother_results(M_,oo_,options_,bayestopt_,dataset_,dataset_info,atT,innov,measurement_error,updated_variables,ys,trend_coeff,aK,P,PK,decomp,Trend,state_uncertainty); + if options_.occbin.smoother.status && options_.occbin.smoother.inversion_filter + [~, ~, ~, ~, ~, ~, ~, ~, ~, ~, oo_, atT, innov] = occbin.IVF_posterior(xparam1,dataset_,dataset_info,options_,M_,estim_params_,bayestopt_,prior_bounds(bayestopt_,options_.prior_trunc),oo_); + updated_variables = atT*nan; + measurement_error=[]; + ys = oo_.dr.ys; + trend_coeff = zeros(length(options_.varobs_id),1); + bayestopt_.mf = bayestopt_.smoother_var_list(bayestopt_.smoother_mf); + [oo_]=store_smoother_results(M_,oo_,options_,bayestopt_,dataset_,dataset_info,atT,innov,measurement_error,updated_variables,ys,trend_coeff); + else + if options_.occbin.smoother.status + [atT,innov,measurement_error,updated_variables,ys,trend_coeff,aK,T,R,P,PK,decomp,Trend,state_uncertainty,M_,oo_,bayestopt_] = occbin.DSGE_smoother(xparam1,gend,transpose(data),data_index,missing_value,M_,oo_,options_,bayestopt_,estim_params_,dataset_,dataset_info); + else + [atT,innov,measurement_error,updated_variables,ys,trend_coeff,aK,T,R,P,PK,decomp,Trend,state_uncertainty,M_,oo_,bayestopt_] = DsgeSmoother(xparam1,gend,transpose(data),data_index,missing_value,M_,oo_,options_,bayestopt_,estim_params_); + end + [oo_]=store_smoother_results(M_,oo_,options_,bayestopt_,dataset_,dataset_info,atT,innov,measurement_error,updated_variables,ys,trend_coeff,aK,P,PK,decomp,Trend,state_uncertainty); + end if options_.forecast > 0 oo_.forecast = dyn_forecast(var_list_,M_,options_,oo_,'smoother',dataset_info); end @@ -554,9 +571,22 @@ end if (~((any(bayestopt_.pshape > 0) && options_.mh_replic) || (any(bayestopt_.pshape> 0) && options_.load_mh_file)) ... || ~options_.smoother ) && ~options_.partial_information % to be fixed %% ML estimation, or posterior mode without Metropolis-Hastings or Metropolis without Bayesian smoothes variables - [atT,innov,measurement_error,updated_variables,ys,trend_coeff,aK,T,R,P,PK,decomp,Trend,state_uncertainty,M_,oo_,bayestopt_] = DsgeSmoother(xparam1,dataset_.nobs,transpose(dataset_.data),dataset_info.missing.aindex,dataset_info.missing.state,M_,oo_,options_,bayestopt_,estim_params_); - [oo_,yf]=store_smoother_results(M_,oo_,options_,bayestopt_,dataset_,dataset_info,atT,innov,measurement_error,updated_variables,ys,trend_coeff,aK,P,PK,decomp,Trend,state_uncertainty); - + if options_.occbin.smoother.status && options_.occbin.smoother.inversion_filter + [~, ~, ~, ~, ~, ~, ~, ~, ~, ~, oo_, atT, innov] = occbin.IVF_posterior(xparam1,dataset_,dataset_info,options_,M_,estim_params_,bayestopt_,prior_bounds(bayestopt_,options_.prior_trunc),oo_); + updated_variables = atT*nan; + measurement_error=[]; + ys = oo_.dr.ys; + trend_coeff = zeros(length(options_.varobs_id),1); + bayestopt_.mf = bayestopt_.smoother_var_list(bayestopt_.smoother_mf); + [oo_, yf]=store_smoother_results(M_,oo_,options_,bayestopt_,dataset_,dataset_info,atT,innov,measurement_error,updated_variables,ys,trend_coeff); + else + if options_.occbin.smoother.status + [atT,innov,measurement_error,updated_variables,ys,trend_coeff,aK,T,R,P,PK,decomp,Trend,state_uncertainty,M_,oo_,bayestopt_] = occbin.DSGE_smoother(xparam1,dataset_.nobs,transpose(dataset_.data),dataset_info.missing.aindex,dataset_info.missing.state,M_,oo_,options_,bayestopt_,estim_params_,dataset_,dataset_info); + else + [atT,innov,measurement_error,updated_variables,ys,trend_coeff,aK,T,R,P,PK,decomp,Trend,state_uncertainty,M_,oo_,bayestopt_] = DsgeSmoother(xparam1,dataset_.nobs,transpose(dataset_.data),dataset_info.missing.aindex,dataset_info.missing.state,M_,oo_,options_,bayestopt_,estim_params_); + end + [oo_,yf]=store_smoother_results(M_,oo_,options_,bayestopt_,dataset_,dataset_info,atT,innov,measurement_error,updated_variables,ys,trend_coeff,aK,P,PK,decomp,Trend,state_uncertainty); + end if ~options_.nograph [nbplt,nr,nc,lr,lc,nstar] = pltorg(M_.exo_nbr); if ~exist([M_.dname '/graphs'],'dir') diff --git a/matlab/dynare_estimation_init.m b/matlab/dynare_estimation_init.m index 2958c74ed..c4e3f70df 100644 --- a/matlab/dynare_estimation_init.m +++ b/matlab/dynare_estimation_init.m @@ -685,3 +685,14 @@ if options_.heteroskedastic_filter error('Scale and value defined for the same shock in the same period with "heteroskedastic_shocks".') end end + +if options_.occbin.likelihood.status && options_.occbin.likelihood.inversion_filter + if isempty(options_.occbin.likelihood.IVF_shock_observable_mapping) + options_.occbin.likelihood.IVF_shock_observable_mapping=find(diag(M.Sigma_e)~=0); + else + zero_var_shocks=find(diag(M.Sigma_e)==0); + if any(ismember(options_.occbin.likelihood.IVF_shock_observable_mapping,zero_var_shocks)) + error('IVF-filter: an observable is mapped to a zero variance shock.') + end + end +end \ No newline at end of file diff --git a/matlab/evaluate_likelihood.m b/matlab/evaluate_likelihood.m index 5775e2ee0..f48385a84 100644 --- a/matlab/evaluate_likelihood.m +++ b/matlab/evaluate_likelihood.m @@ -73,6 +73,10 @@ if isempty(dataset) end options_=select_qz_criterium_value(options_); -llik = -dsge_likelihood(parameters,dataset,dataset_info,options_,M_,estim_params_,bayestopt_,prior_bounds(bayestopt_,options_.prior_trunc),oo_); +if options_.occbin.likelihood.status && options_.occbin.likelihood.inversion_filter + llik = -occbin.IVF_posterior(parameters,dataset,dataset_info,options_,M_,estim_params_,bayestopt_,prior_bounds(bayestopt_,options_.prior_trunc),oo_); +else + llik = -dsge_likelihood(parameters,dataset,dataset_info,options_,M_,estim_params_,bayestopt_,prior_bounds(bayestopt_,options_.prior_trunc),oo_); +end ldens = evaluate_prior(parameters,M_,estim_params_,oo_,options_,bayestopt_); llik = llik - ldens; \ No newline at end of file diff --git a/matlab/evaluate_smoother.m b/matlab/evaluate_smoother.m index 699a590f8..c5fac04d1 100644 --- a/matlab/evaluate_smoother.m +++ b/matlab/evaluate_smoother.m @@ -101,10 +101,27 @@ if ischar(parameters) end end -[atT,innov,measurement_error,updated_variables,ys,trend_coeff,aK,T,R,P,PK,decomp,Trend,state_uncertainty,M_,oo_,bayestopt_] = ... - DsgeSmoother(parameters,dataset_.nobs,transpose(dataset_.data),dataset_info.missing.aindex,dataset_info.missing.state,M_,oo_,options_,bayestopt_,estim_params_); -[oo_]=store_smoother_results(M_,oo_,options_,bayestopt_,dataset_,dataset_info,atT,innov,measurement_error,updated_variables,ys,trend_coeff,aK,P,PK,decomp,Trend,state_uncertainty); - +if options_.occbin.smoother.status + if options_.occbin.smoother.inversion_filter + [~, ~, ~, ~, ~, ~, ~, ~, ~, ~, oo_, atT, innov] = occbin.IVF_posterior(parameters,dataset_,dataset_info,options_,M_,estim_params_,bayestopt_,prior_bounds(bayestopt_,options_.prior_trunc),oo_); + updated_variables = atT*nan; + measurement_error=[]; + ys = oo_.dr.ys; + trend_coeff = zeros(length(options_.varobs_id),1); + bayestopt_.mf = bayestopt_.smoother_var_list(bayestopt_.smoother_mf); + else + [atT,innov,measurement_error,updated_variables,ys,trend_coeff,aK,T,R,P,PK,decomp,Trend,state_uncertainty,M_,oo_,bayestopt_] = ... + occbin.DSGE_smoother(parameters,dataset_.nobs,transpose(dataset_.data),dataset_info.missing.aindex,dataset_info.missing.state,M_,oo_,options_,bayestopt_,estim_params_,dataset_,dataset_info); + end +else + [atT,innov,measurement_error,updated_variables,ys,trend_coeff,aK,T,R,P,PK,decomp,Trend,state_uncertainty,M_,oo_,bayestopt_] = ... + DsgeSmoother(parameters,dataset_.nobs,transpose(dataset_.data),dataset_info.missing.aindex,dataset_info.missing.state,M_,oo_,options_,bayestopt_,estim_params_); +end +if ~(options_.occbin.smoother.status && options_.occbin.smoother.inversion_filter) + [oo_]=store_smoother_results(M_,oo_,options_,bayestopt_,dataset_,dataset_info,atT,innov,measurement_error,updated_variables,ys,trend_coeff,aK,P,PK,decomp,Trend,state_uncertainty); +else + [oo_]=store_smoother_results(M_,oo_,options_,bayestopt_,dataset_,dataset_info,atT,innov,measurement_error,updated_variables,ys,trend_coeff); +end if nargout>4 Smoothed_variables_declaration_order_deviation_form=atT(oo_.dr.inv_order_var(bayestopt_.smoother_var_list),:); end diff --git a/matlab/get_error_message.m b/matlab/get_error_message.m index fc1424b57..a0baf2b4d 100644 --- a/matlab/get_error_message.m +++ b/matlab/get_error_message.m @@ -170,6 +170,22 @@ switch info(1) message = 'Particle Filter: Initial covariance of the states is not positive definite. Try a different nonlinear_filter_initialization'; case 202 message = 'Particle Filter: Initial covariance of the states based on simulation resulted in NaN/Inf. Use pruning or try a different nonlinear_filter_initialization'; + case 301 + message = 'IVF: The likelihood is Inf.'; + case 302 + message = 'IVF: The likelihood is NaN.'; + case 303 + message = 'IVF: The residuals are not 0.'; + case 304 + message = 'IVF: The solver returned with an error code.'; + case 305 + message = 'IVF: The returned shocks are bigger than 1e8.'; + case 310 + message = 'Occbin: Simulation terminated with periodic solution (no convergence).'; + case 311 + message = 'Occbin: Simulation did not converge, increase maxit or check_ahead_periods.'; + case 312 + message = 'Occbin: Constraint(s) are binding at the end of the sample.'; otherwise message = 'This case shouldn''t happen. Contact the authors of Dynare'; end \ No newline at end of file diff --git a/matlab/initial_estimation_checks.m b/matlab/initial_estimation_checks.m index fac3cb875..2df6dd3eb 100644 --- a/matlab/initial_estimation_checks.m +++ b/matlab/initial_estimation_checks.m @@ -76,6 +76,18 @@ if DynareOptions.order>1 end end +if (DynareOptions.occbin.likelihood.status && DynareOptions.occbin.likelihood.inversion_filter) || (DynareOptions.occbin.smoother.status && DynareOptions.occbin.smoother.inversion_filter) + err_index= find(diag(Model.Sigma_e)~=0); + if length(err_index)~=length(DynareOptions.varobs) + fprintf('initial_estimation_checks:: The IVF requires exactly as many shocks as observables.') + end + var_index=find(any(isnan(DynareDataset.data))); + if ~isempty(var_index) + fprintf('initial_estimation_checks:: The IVF requires exactly as many shocks as observables.\n') + fprintf('initial_estimation_checks:: The data series %s contains NaN, I am therefore dropping shock %s for these time points.\n',... + DynareOptions.varobs{var_index},Model.exo_names{DynareOptions.occbin.likelihood.IVF_shock_observable_mapping(var_index)}) + end +end if DynareOptions.order>1 || (DynareOptions.order==1 && ~ischar(DynareOptions.mode_compute) && DynareOptions.mode_compute==11) if DynareOptions.order==1 && DynareOptions.mode_compute==11 diff --git a/matlab/kalman/likelihood/missing_observations_kalman_filter.m b/matlab/kalman/likelihood/missing_observations_kalman_filter.m index fa8b98990..d90dd66cf 100644 --- a/matlab/kalman/likelihood/missing_observations_kalman_filter.m +++ b/matlab/kalman/likelihood/missing_observations_kalman_filter.m @@ -1,4 +1,4 @@ -function [LIK, lik, a, P] = missing_observations_kalman_filter(data_index,number_of_observations,no_more_missing_observations,Y,start,last,a,P,kalman_tol,riccati_tol,rescale_prediction_error_covariance,presample,T,Q,R,H,Z,mm,pp,rr,Zflag,diffuse_periods) +function [LIK, lik, a, P] = missing_observations_kalman_filter(data_index,number_of_observations,no_more_missing_observations,Y,start,last,a,P,kalman_tol,riccati_tol,rescale_prediction_error_covariance,presample,T,Q,R,H,Z,mm,pp,rr,Zflag,diffuse_periods,occbin_) % Computes the likelihood of a state space model in the case with missing observations. % % INPUTS @@ -91,8 +91,57 @@ notsteady = 1; F_singular = true; s = 0; rescale_prediction_error_covariance0=rescale_prediction_error_covariance; +if occbin_.status + Qt = repmat(Q,[1 1 3]); + a0 = zeros(mm,last); + a1 = zeros(mm,last); + P0 = zeros(mm,mm,last); + P1 = zeros(mm,mm,last); + vv = zeros(pp,last); + + options_=occbin_.info{1}; + oo_=occbin_.info{2}; + M_=occbin_.info{3}; + occbin_options=occbin_.info{4}; + opts_regime.regime_history = occbin_options.opts_simul.init_regime; + opts_regime.binding_indicator = occbin_options.opts_simul.init_binding_indicator; + first_period_occbin_update = max(t+1,options_.occbin.likelihood.first_period_occbin_update); + if isempty(opts_regime.binding_indicator) && isempty(opts_regime.regime_history) + opts_regime.binding_indicator=zeros(last+2,length(M_.occbin.constraint)); + end + [~, ~, ~, regimes_] = occbin.check_regimes([], [], [], opts_regime, M_, oo_, options_); + if length(occbin_.info)>4 + TT=occbin_.info{5}; + RR=occbin_.info{6}; + CC=occbin_.info{7}; + T0=occbin_.info{8}; + R0=occbin_.info{9}; + TT = cat(3,TT,T); + RR = cat(3,RR,R); + CC = cat(2,CC,zeros(mm,1)); + if size(TT,3)<(last+1) + TT=repmat(T,1,1,last+1); + RR=repmat(R,1,1,last+1); + CC=repmat(zeros(mm,1),1,last+1); + end + + end +else + first_period_occbin_update = inf; + C=0; +end while notsteady && t<=last + if occbin_.status + a1(:,t) = a; + P1(:,:,t) = P; + C = CC(:,t+1); + R = RR(:,:,t+1); + T = TT(:,:,t+1); + if ~(isqvec) + QQ = R*Q*transpose(R); % Variance of R times the vector of structural innovations. + end + end s = t-start+1; d_index = data_index{t}; if isqvec @@ -129,39 +178,79 @@ while notsteady && t<=last % badly_conditioned_F = true; end end - if badly_conditioned_F - if ~all(abs(F(:))=no_more_missing_observations && ~isqvec - notsteady = max(abs(K(:)-oldK))>riccati_tol; - oldK = K(:); + F_singular = false; + if rescale_prediction_error_covariance + log_dF = log(det(F./(sig*sig')))+2*sum(log(sig)); + iF = inv(F./(sig*sig'))./(sig*sig'); + rescale_prediction_error_covariance=rescale_prediction_error_covariance0; + else + log_dF = log(det(F)); + iF = inv(F); + end + lik(s) = log_dF + transpose(v)*iF*v + length(d_index)*log(2*pi); + if t=no_more_missing_observations && ~isqvec && ~occbin_.status + notsteady = max(abs(K(:)-oldK))>riccati_tol; + oldK = K(:); + end + end end end end + if occbin_.status && t>=first_period_occbin_update + + if isqvec + Qt = Qvec(:,:,t-1:t+1); + end + occbin_options.opts_simul.waitbar=0; + [ax, a1x, Px, P1x, vx, Tx, Rx, Cx, regimes_(t:t+2), info, M_, likx, etax(t,:)] = occbin.kalman_update_algo_1(a0(:,t-1),a1(:,t-1:t),P0(:,:,t-1),P1(:,:,t-1:t),data_index(t-1:t),Z,vv(:,t-1:t),Y(:,t-1:t),H,Qt,T0,R0,TT(:,:,t-1:t),RR(:,:,t-1:t),CC(:,t-1:t),regimes_(t:t+1),M_,oo_,options_,occbin_options); + if info + return + end + if options_.occbin.likelihood.use_updated_regime + lik(s) = likx; + end + a0(:,t) = ax(:,1); + a1(:,t) = a1x(:,2); + a = ax(:,2); + vv(d_index,t) = vx(d_index,2); + TT(:,:,t:t+1) = Tx; + RR(:,:,t:t+1) = Rx; + CC(:,t:t+1) = Cx; + P0(:,:,t) = Px(:,:,1); + P1(:,:,t) = P1x(:,:,2); + P = Px(:,:,2); + + end t = t+1; end @@ -182,4 +271,4 @@ if presample>=diffuse_periods LIK = sum(lik(1+presample-diffuse_periods:end)); else LIK = sum(lik); -end \ No newline at end of file +end diff --git a/matlab/list_of_functions_to_be_cleared.m b/matlab/list_of_functions_to_be_cleared.m index 82ee94b5a..495d0e86b 100644 --- a/matlab/list_of_functions_to_be_cleared.m +++ b/matlab/list_of_functions_to_be_cleared.m @@ -1 +1,2 @@ -list_of_functions = {'discretionary_policy_1', 'dsge_var_likelihood', 'dyn_first_order_solver', 'dyn_waitbar', 'ep_residuals', 'evaluate_likelihood', 'prior_draw_gsa', 'identification_analysis', 'computeDLIK', 'univariate_computeDLIK', 'metropolis_draw', 'flag_implicit_skip_nan', 'moment_function', 'mr_hessian', 'masterParallel', 'auxiliary_initialization', 'auxiliary_particle_filter', 'conditional_filter_proposal', 'conditional_particle_filter', 'gaussian_filter', 'gaussian_filter_bank', 'gaussian_mixture_filter', 'gaussian_mixture_filter_bank', 'Kalman_filter', 'online_auxiliary_filter', 'pruned_state_space_system', 'sequential_importance_particle_filter', 'solve_model_for_online_filter', 'perfect_foresight_simulation', 'prior_draw', 'priordens'}; +list_of_functions = {'discretionary_policy_1', 'dsge_var_likelihood', 'dyn_first_order_solver', 'dyn_waitbar', 'ep_residuals', 'evaluate_likelihood', 'prior_draw_gsa', 'identification_analysis', 'computeDLIK', 'univariate_computeDLIK', 'metropolis_draw', 'flag_implicit_skip_nan', 'moment_function', 'mr_hessian', 'masterParallel', 'auxiliary_initialization', 'auxiliary_particle_filter', 'conditional_filter_proposal', 'conditional_particle_filter', 'gaussian_filter', 'gaussian_filter_bank', 'gaussian_mixture_filter', 'gaussian_mixture_filter_bank', 'Kalman_filter', 'online_auxiliary_filter', 'pruned_state_space_system', 'sequential_importance_particle_filter', 'solve_model_for_online_filter', 'perfect_foresight_simulation', 'prior_draw', 'priordens',... + '+occbin/solver.m','+occbin/mkdatap_anticipated_dyn.m','+occbin/mkdatap_anticipated_2constraints_dyn.m','+occbin/match_function.m','+occbin/solve_one_constraint.m','+occbin/solve_two_constraint.m','+occbin/plot/shock_decomposition.m'}; \ No newline at end of file diff --git a/matlab/missing_DiffuseKalmanSmootherH3_Z.m b/matlab/missing_DiffuseKalmanSmootherH3_Z.m index 9e9c8a811..d6b2c2ebc 100644 --- a/matlab/missing_DiffuseKalmanSmootherH3_Z.m +++ b/matlab/missing_DiffuseKalmanSmootherH3_Z.m @@ -1,5 +1,5 @@ -function [alphahat,epsilonhat,etahat,a,P1,aK,PK,decomp,V, aalphahat,eetahat,d] = missing_DiffuseKalmanSmootherH3_Z(a_initial,T,Z,R,Q,H,Pinf1,Pstar1,Y,pp,mm,smpl,data_index,nk,kalman_tol,diffuse_kalman_tol,decomp_flag,state_uncertainty_flag, filter_covariance_flag, smoother_redux) -% function [alphahat,epsilonhat,etahat,a,P1,aK,PK,decomp,V, aalphahat,eetahat,d] = missing_DiffuseKalmanSmootherH3_Z(a_initial,T,Z,R,Q,H,Pinf1,Pstar1,Y,pp,mm,smpl,data_index,nk,kalman_tol,diffuse_kalman_tol,decomp_flag,state_uncertainty_flag, filter_covariance_flag, smoother_redux) +function [alphahat,epsilonhat,etahat,a,P1,aK,PK,decomp,V, aalphahat,eetahat,d,varargout] = missing_DiffuseKalmanSmootherH3_Z(a_initial,T,Z,R,Q,H,Pinf1,Pstar1,Y,pp,mm,smpl,data_index,nk,kalman_tol,diffuse_kalman_tol,decomp_flag,state_uncertainty_flag, filter_covariance_flag, smoother_redux, occbin_) +% function [alphahat,epsilonhat,etahat,a,P1,aK,PK,decomp,V, aalphahat,eetahat,d] = missing_DiffuseKalmanSmootherH3_Z(a_initial,T,Z,R,Q,H,Pinf1,Pstar1,Y,pp,mm,smpl,data_index,nk,kalman_tol,diffuse_kalman_tol,decomp_flag,state_uncertainty_flag, filter_covariance_flag, smoother_redux, occbin_) % Computes the diffuse kalman smoother in the case of a singular var-cov matrix. % Univariate treatment of multivariate time series. % @@ -150,6 +150,71 @@ else V=[]; end +if ~occbin_.status + isoccbin = 0; + C=0; + TT=[]; + RR=[]; + CC=[]; +else + isoccbin = 1; + Qt = repmat(Q,[1 1 3]); + options_=occbin_.info{1}; + oo_=occbin_.info{2}; + M_=occbin_.info{3}; + occbin_options=occbin_.info{4}; + opts_regime = occbin_options.opts_regime; + % first_period_occbin_update = inf; + if isfield(opts_regime,'regime_history') && ~isempty(opts_regime.regime_history) + opts_regime.regime_history=[opts_regime.regime_history(1) opts_regime.regime_history]; + else + opts_regime.binding_indicator=zeros(smpl+2,length(M_.occbin.constraint)); + end + occbin_options.opts_regime = opts_regime; + [~, ~, ~, regimes_] = occbin.check_regimes([], [], [], opts_regime, M_, oo_, options_); + if length(occbin_.info)>4 + if length(occbin_.info)==6 && options_.smoother_redux + TT=repmat(T,1,1,smpl+1); + RR=repmat(R,1,1,smpl+1); + CC=repmat(zeros(mm,1),1,smpl+1); + T0=occbin_.info{5}; + R0=occbin_.info{6}; + else + + TT=occbin_.info{5}; + RR=occbin_.info{6}; + CC=occbin_.info{7}; + % TT = cat(3,TT,T); + % RR = cat(3,RR,R); + % CC = cat(2,CC,zeros(mm,1)); + if options_.smoother_redux + my_order_var = oo_.dr.restrict_var_list; + CC = CC(my_order_var,:); + RR = RR(my_order_var,:,:); + TT = TT(my_order_var,my_order_var,:); + T0=occbin_.info{8}; + R0=occbin_.info{9}; + end + if size(TT,3)<(smpl+1) + TT=repmat(T,1,1,smpl+1); + RR=repmat(R,1,1,smpl+1); + CC=repmat(zeros(mm,1),1,smpl+1); + end + end + + else + TT=repmat(T,1,1,smpl+1); + RR=repmat(R,1,1,smpl+1); + CC=repmat(zeros(mm,1),1,smpl+1); + end + if ~smoother_redux + T0=T; + R0=R; + + end + +end + t = 0; icc=0; if ~isempty(Pinf(:,:,1)) @@ -182,8 +247,8 @@ while newRank && t < smpl elseif Fstar(i,t) > kalman_tol a(:,t) = a(:,t) + Kstar(:,i,t)*v(i,t)/Fstar(i,t); % KD (2000), eq. (17) Pstar(:,:,t) = Pstar(:,:,t) - Kstar(:,i,t)*Kstar(:,i,t)'/Fstar(i,t); % KD (2000), eq. (17) - % Pinf is passed through unaltered, see eq. (17) of - % Koopman/Durbin (2000) + % Pinf is passed through unaltered, see eq. (17) of + % Koopman/Durbin (2000) else % do nothing as a_{t,i+1}=a_{t,i} and P_{t,i+1}=P_{t,i}, see % p. 157, DK (2012) @@ -198,6 +263,10 @@ while newRank && t < smpl else oldRank = 0; end + if isoccbin, + TT(:,:,t+1)= T; + RR(:,:,t+1)= R; + end a1(:,t+1) = T*a(:,t); aK(1,:,t+1) = a1(:,t+1); for jnk=2:nk @@ -221,7 +290,19 @@ while newRank && t < smpl end end - +if isoccbin + first_period_occbin_update = max(t+2,occbin_options.first_period_occbin_update); + if occbin_options.opts_regime.waitbar + hh = dyn_waitbar(0,'Occbin: Piecewise Kalman Filter'); + set(hh,'Name','Occbin: Piecewise Kalman Filter.'); + waitbar_indicator=1; + else + waitbar_indicator=0; + end +else + first_period_occbin_update = inf; + waitbar_indicator=0; +end d = t; P(:,:,d+1) = Pstar(:,:,d+1); Fstar = Fstar(:,1:d); @@ -237,63 +318,166 @@ while notsteady && t kalman_tol - a(:,t) = a(:,t) + Ki(:,i,t)*v(i,t)/Fi(i,t); %filtering according to (6.13) in DK (2012) - P(:,:,t) = P(:,:,t) - Ki(:,i,t)*Ki(:,i,t)'/Fi(i,t); %filtering according to (6.13) in DK (2012) + if t>=first_period_occbin_update + if waitbar_indicator + dyn_waitbar(t/smpl, hh, sprintf('Period %u of %u', t,smpl)); + end + if isqvec + Qt = Qvec(:,:,t-1:t+1); + end + occbin_options.opts_regime.waitbar=0; + [ax, a1x, Px, P1x, vx, Fix, Kix, Tx, Rx, Cx, tmp, error_flag, M_, aha, etaha,TTx,RRx,CCx] = occbin.kalman_update_algo_3(a(:,t-1),a1(:,t-1:t),P(:,:,t-1),P1(:,:,t-1:t),data_index(t-1:t),Z,v(:,t-1:t),Fi(:,t-1),Ki(:,:,t-1),Y(:,t-1:t),H,Qt,T0,R0,TT(:,:,t-1:t),RR(:,:,t-1:t),CC(:,t-1:t),regimes_(t:t+1),M_,oo_,options_,occbin_options,kalman_tol,nk); + if ~error_flag + regimes_(t:t+2)=tmp; else - % do nothing as a_{t,i+1}=a_{t,i} and P_{t,i+1}=P_{t,i}, see - % p. 157, DK (2012) + varargout{1} = []; + varargout{2} = []; + varargout{3} = []; + varargout{4} = []; + return end - end - if smoother_redux - ri=zeros(mm,1); - for st=t:-1:max(d+1,t-1) - di = flipud(data_index{st})'; - for i = di - if Fi(i,st) > kalman_tol - Li = eye(mm)-Ki(:,i,st)*Z(i,:)/Fi(i,st); - ri = Z(i,:)'/Fi(i,st)*v(i,st)+Li'*ri; % DK (2012), 6.15, equation for r_{t,i-1} - end - end - if st==t-1 - aalphahat(:,st) = a1(:,st) + P1(:,:,st)*ri; + + if smoother_redux + aalphahat(:,t-1) = aha(:,1); + eetahat(:,t) = etaha(:,2); + end + a(:,t) = ax(:,1); + a1(:,t) = a1x(:,2); + a1(:,t+1) = ax(:,2); + v(di,t) = vx(di,2); + Fi(di,t) = Fix(di,2); + Ki(:,di,t) = Kix(:,di,2); + TT(:,:,t:t+1) = Tx(:,:,1:2); + RR(:,:,t:t+1) = Rx(:,:,1:2); + CC(:,t:t+1) = Cx(:,1:2); + TTT(:,:,t)=TTx; + RRR(:,:,t)=RRx; + CCC(:,t)=CCx; + P(:,:,t) = Px(:,:,1); + P1(:,:,t) = P1x(:,:,2); + P(:,:,t+1) = Px(:,:,2); + for jnk=1:nk + PK(jnk,:,:,t+jnk) = Px(:,:,1+jnk); + aK(jnk,:,t+jnk) = ax(:,1+jnk); + end + else + for i=di + Zi = Z(i,:); + v(i,t) = Y(i,t) - Zi*a(:,t); % nu_{t,i} in 6.13 in DK (2012) + Fi(i,t) = Zi*P(:,:,t)*Zi' + H(i); % F_{t,i} in 6.13 in DK (2012), relies on H being diagonal + Ki(:,i,t) = P(:,:,t)*Zi'; % K_{t,i}*F_(i,t) in 6.13 in DK (2012) + if Fi(i,t) > kalman_tol + a(:,t) = a(:,t) + Ki(:,i,t)*v(i,t)/Fi(i,t); %filtering according to (6.13) in DK (2012) + P(:,:,t) = P(:,:,t) - Ki(:,i,t)*Ki(:,i,t)'/Fi(i,t); %filtering according to (6.13) in DK (2012) else - eetahat(:,st) = QRt*ri; + % do nothing as a_{t,i+1}=a_{t,i} and P_{t,i+1}=P_{t,i}, see + % p. 157, DK (2012) end - ri = T'*ri; % KD (2003), eq. (23), equation for r_{t-1,p_{t-1}} end - end - if isqvec - QQ = R*Qvec(:,:,t+1)*transpose(R); - end - a1(:,t+1) = T*a(:,t); %transition according to (6.14) in DK (2012) - P(:,:,t+1) = T*P(:,:,t)*T' + QQ; %transition according to (6.14) in DK (2012) - if filter_covariance_flag - Pf = P(:,:,t+1); - end - aK(1,:,t+1) = a1(:,t+1); - for jnk=1:nk + if isqvec + QQ = R*Qvec(:,:,t)*transpose(R); + end + if smoother_redux + ri=zeros(mm,1); + for st=t:-1:max(d+1,t-1) + di = flipud(data_index{st})'; + for i = di + if Fi(i,st) > kalman_tol + Li = eye(mm)-Ki(:,i,st)*Z(i,:)/Fi(i,st); + ri = Z(i,:)'/Fi(i,st)*v(i,st)+Li'*ri; % DK (2012), 6.15, equation for r_{t,i-1} + end + end + if st==t-1 + aalphahat(:,st) = a1(:,st) + P1(:,:,st)*ri; + else + if isoccbin + if isqvec + QRt = Qvec(:,:,st)*transpose(RR(:,:,st)); + else + QRt = Q*transpose(RR(:,:,st)); + end + T = TT(:,:,st); + else + if isqvec + QRt = Qvec(:,:,st)*transpose(R); + end + end + eetahat(:,st) = QRt*ri; + end + ri = T'*ri; % KD (2003), eq. (23), equation for r_{t-1,p_{t-1}} + end + end + if isoccbin + if isqvec + QQ = RR(:,:,t+1)*Qvec(:,:,t+1)*transpose(RR(:,:,t+1)); + else + QQ = RR(:,:,t+1)*Q*transpose(RR(:,:,t+1)); + end + T = TT(:,:,t+1); + C = CC(:,t+1); + else + if isqvec + QQ = R*Qvec(:,:,t+1)*transpose(R); + end + end + a1(:,t+1) = T*a(:,t)+C; %transition according to (6.14) in DK (2012) + P(:,:,t+1) = T*P(:,:,t)*T' + QQ; %transition according to (6.14) in DK (2012) if filter_covariance_flag - if jnk>1 - Pf = T*Pf*T' + QQ; - end - PK(jnk,:,:,t+jnk) = Pf; + Pf = P(:,:,t+1); end - if jnk>1 - aK(jnk,:,t+jnk) = T*dynare_squeeze(aK(jnk-1,:,t+jnk-1)); + aK(1,:,t+1) = a1(:,t+1); + if ~isempty(nk) && nk>1 && isoccbin + opts_simul = occbin_options.opts_regime; + opts_simul.SHOCKS = zeros(nk,M_.exo_nbr); + if smoother_redux + tmp=zeros(M_.endo_nbr,1); + tmp(oo_.dr.restrict_var_list)=a(:,t); + opts_simul.endo_init = tmp(oo_.dr.inv_order_var); + else + opts_simul.endo_init = a(oo_.dr.inv_order_var,t); + end + opts_simul.init_regime = []; %regimes_(t); + options_.occbin.simul=opts_simul; + [~, out, ss] = occbin.solver(M_,oo_,options_); + end + for jnk=1:nk + if filter_covariance_flag + if jnk>1 + Pf = T*Pf*T' + QQ; + end + PK(jnk,:,:,t+jnk) = Pf; + end + if isoccbin + if smoother_redux + aK(jnk,:,t+jnk) = out.zpiece(jnk,oo_.dr.order_var(oo_.dr.restrict_var_list)); + else + aK(jnk,oo_.dr.inv_order_var,t+jnk) = out.zpiece(jnk,:); + end + elseif jnk>1 + aK(jnk,:,t+jnk) = T*dynare_squeeze(aK(jnk-1,:,t+jnk-1)); + end end end - % notsteady = ~(max(max(abs(P(:,:,t+1)-P(:,:,t)))) d+1 end r(:,t) = ri; % DK (2012), below 6.15, r_{t-1}=r_{t,0} alphahat(:,t) = a1(:,t) + P1(:,:,t)*r(:,t); - if isqvec - QRt = Qvec(:,:,t)*transpose(R); + if isoccbin + if isqvec + QRt = Qvec(:,:,t)*transpose(RR(:,:,t)); + else + QRt = Q*transpose(RR(:,:,t)); + end + R = RR(:,:,t); + T = TT(:,:,t); + else + if isqvec + QRt = Qvec(:,:,t)*transpose(R); + end end etahat(:,t) = QRt*r(:,t); ri = T'*ri; % KD (2003), eq. (23), equation for r_{t-1,p_{t-1}} @@ -383,8 +577,8 @@ if d Linf = eye(mm) - Kinf(:,i,t)*Z(i,:)/Finf(i,t); L0 = (Kinf(:,i,t)*(Fstar(i,t)/Finf(i,t))-Kstar(:,i,t))*Z(i,:)/Finf(i,t); r1(:,t) = Z(i,:)'*v(i,t)/Finf(i,t) + ... - L0'*r0(:,t) + ... - Linf'*r1(:,t); % KD (2000), eq. (25) for r_1 + L0'*r0(:,t) + ... + Linf'*r1(:,t); % KD (2000), eq. (25) for r_1 r0(:,t) = Linf'*r0(:,t); % KD (2000), eq. (25) for r_0 if state_uncertainty_flag N_2(:,:,t)=Z(i,:)'/Finf(i,t)^2*Z(i,:)*Fstar(i,t) ... @@ -406,8 +600,18 @@ if d end alphahat(:,t) = a1(:,t) + Pstar1(:,:,t)*r0(:,t) + Pinf1(:,:,t)*r1(:,t); % KD (2000), eq. (26) r(:,t) = r0(:,t); - if isqvec - QRt = Qvec(:,:,t)*transpose(R); + if isoccbin + if isqvec + QRt = Qvec(:,:,t)*transpose(RR(:,:,t)); + else + QRt = Q*transpose(RR(:,:,t)); + end + R = RR(:,:,t); + T = TT(:,:,t); + else + if isqvec + QRt = Qvec(:,:,t)*transpose(R); + end end etahat(:,t) = QRt*r(:,t); % KD (2000), eq. (27) if state_uncertainty_flag @@ -439,6 +643,8 @@ if d end end end +else + alphahat0 = 0*a1(:,1) + P1(:,:,1)*ri; end if decomp_flag @@ -452,10 +658,20 @@ if decomp_flag ri_d = Z(i,:)'/Fi(i,t)*v(i,t)+ri_d-Ki(:,i,t)'*ri_d/Fi(i,t)*Z(i,:)'; end end - + % calculate eta_tm1t - if isqvec - QRt = Qvec(:,:,t)*transpose(R); + if isoccbin + if isqvec + QRt = Qvec(:,:,t)*transpose(RR(:,:,t)); + else + QRt = Q*transpose(RR(:,:,t)); + end + R = RR(:,:,t); + T = TT(:,:,t); + else + if isqvec + QRt = Qvec(:,:,t)*transpose(R); + end end eta_tm1t = QRt*ri_d; % calculate decomposition diff --git a/matlab/prior_posterior_statistics_core.m b/matlab/prior_posterior_statistics_core.m index a98405d57..38862e06c 100644 --- a/matlab/prior_posterior_statistics_core.m +++ b/matlab/prior_posterior_statistics_core.m @@ -223,8 +223,13 @@ for b=fpar:B fprintf('\nprior_posterior_statistics: One of the draws failed with the error:\n%s\n',message) fprintf('prior_posterior_statistics: This should not happen. Please contact the developers.\n',message) end - [alphahat,etahat,epsilonhat,alphatilde,SteadyState,trend_coeff,aK,~,~,P,~,~,trend_addition,state_uncertainty,M_,oo_,bayestopt_] = ... - DsgeSmoother(deep,gend,Y,data_index,missing_value,M_,oo_,opts_local,bayestopt_,estim_params_); + if options_.occbin.smoother.status + [alphahat,etahat,epsilonhat,alphatilde,SteadyState,trend_coeff,aK,~,~,P,~,~,trend_addition,state_uncertainty,M_,oo_,bayestopt_] = ... + occbin.DSGE_smoother(deep,gend,Y,data_index,missing_value,M_,oo_,opts_local,bayestopt_,estim_params_); + else + [alphahat,etahat,epsilonhat,alphatilde,SteadyState,trend_coeff,aK,~,~,P,~,~,trend_addition,state_uncertainty,M_,oo_,bayestopt_] = ... + DsgeSmoother(deep,gend,Y,data_index,missing_value,M_,oo_,opts_local,bayestopt_,estim_params_); + end stock_trend_coeff(options_.varobs_id,irun(9))=trend_coeff; stock_smoothed_trend(IdObs,:,irun(11))=trend_addition; diff --git a/matlab/store_smoother_results.m b/matlab/store_smoother_results.m index a187e93b3..d75d47509 100644 --- a/matlab/store_smoother_results.m +++ b/matlab/store_smoother_results.m @@ -72,6 +72,20 @@ function [oo_, yf]=store_smoother_results(M_,oo_,options_,bayestopt_,dataset_,da % You should have received a copy of the GNU General Public License % along with Dynare. If not, see . +%make sure there are no stale results +field_names={'Smoother','SmoothedVariables','UpdatedVariables','FilteredVariables','FilteredVariablesKStepAhead','FilteredVariablesShockDecomposition','FilteredVariablesKStepAheadVariances','SmoothedShocks','SmoothedMeasurementErrors'}; +for field_iter=1:length(field_names) + if isfield(oo_,field_names(field_iter)) + oo_=rmfield(oo_,field_names(field_iter)); + end +end + +if options_.occbin.smoother.status + oo_.Smoother.occbin = true; +else + oo_.Smoother.occbin = false; +end + gend=dataset_.nobs; if nargin<16 Trend=zeros(options_.number_of_observed_variables,gend); diff --git a/matlab/varlist_indices.m b/matlab/varlist_indices.m index 047759551..c28c00fc6 100644 --- a/matlab/varlist_indices.m +++ b/matlab/varlist_indices.m @@ -1,20 +1,20 @@ -function [i_var, nvar, index_uniques] = varlist_indices(sublist, list) +function [i_var, nvar, index_unique_present] = varlist_indices(sublist, list, nocheck_dummy) -% returns the indices of a list of endogenous variables +% returns the indices of a list of variables % % INPUT % sublist [cell of char arrays] sublist of variables % list [cell of char arrays] list of variables % % OUTPUT -% i_var variable indices in M_.endo_names -% nvar number of variables in varlist -% index_uniques indices of unique elements in varlist +% i_var variable indices in list +% nvar number of unique variables contained in list +% index_uniques indices of unique and present elements in sublist % % SPECIAL REQUIREMENTS % none -% Copyright (C) 2010-2018 Dynare Team +% Copyright (C) 2010-2021 Dynare Team % % This file is part of Dynare. % @@ -31,6 +31,9 @@ function [i_var, nvar, index_uniques] = varlist_indices(sublist, list) % You should have received a copy of the GNU General Public License % along with Dynare. If not, see . +if nargin<3 + nocheck_dummy=0; +end if isempty(sublist) check = []; i_var = []; @@ -38,27 +41,34 @@ else [check, i_var] = ismember(sublist, list); end +indices_not_present=[]; if ~all(check) - k = find(~check); - str = 'The following symbols are not endogenous variables:'; - for ii = 1:length(k) - str = sprintf('%s %s', str, sublist{k(ii)}); + k_not_present = find(~check); + if ~nocheck_dummy + str = 'The following symbols are not endogenous variables:'; + for ii = 1:length(k_not_present) + str = sprintf('%s %s', str, sublist{k_not_present(ii)}); + end + error(str) + else + indices_not_present=find(i_var==0); end - error(str) end -nvar = length(i_var); -[~, index_uniques, ~] = unique(i_var, 'first'); -index_uniques = sort(index_uniques); -i_var_unique = i_var(index_uniques); +nvar_present = length(i_var(check)); +[~, index_unique, ~] = unique(i_var, 'first'); +index_unique_present = index_unique(~ismember(index_unique,indices_not_present)); +index_unique_present = sort(index_unique_present); +i_var_unique_present = i_var(index_unique_present); -if length(i_var_unique)~=nvar - k = find(~ismember(1:nvar,index_uniques)); +if length(i_var_unique_present)~=nvar_present + k = find(~ismember((1:length(i_var))',index_unique_present) & i_var~=0); str = 'The following symbols are specified twice in the variable list and are considered only once:'; for ii = 1:length(k) - str = sprintf('%s %s', str, sublist{k(ii)}); + str = sprintf('%s %s', str, sublist{i_var(k(ii))}); end warning('%s\n', str) - i_var = i_var_unique; - nvar = length(i_var); -end \ No newline at end of file +end + +i_var = i_var_unique_present; +nvar = length(i_var); diff --git a/preprocessor b/preprocessor index 6b9d94405..08ac455fc 160000 --- a/preprocessor +++ b/preprocessor @@ -1 +1 @@ -Subproject commit 6b9d94405c44fe2533a2d8bc377173a2a70f3a54 +Subproject commit 08ac455fcddd032058738669ed6a84fda9c76e05 diff --git a/scripts/dynare.el b/scripts/dynare.el index 10cf8278d..f26ce8e7c 100644 --- a/scripts/dynare.el +++ b/scripts/dynare.el @@ -64,7 +64,8 @@ "write_latex_steady_state_model" "steady" "check" "simul" "stoch_simul" "var_model" "trend_component_model" "var_expectation_model" "pac_model" "dsample" "Sigma_e" "planner_objective" "ramsey_model" "ramsey_policy" - "evaluate_planner_objective" + "evaluate_planner_objective" "occbin_setup" "occbin_solver" + "occbin_write_regimes" "occbin_graph" "discretionary_policy" "identification" "bvar_density" "bvar_forecast" "dynare_sensitivity" "initval_file" "histval_file" "forecast" "shock_decomposition" "realtime_shock_decomposition" @@ -84,7 +85,8 @@ ;; Keywords that may appear in blocks, and that begin a statement which will be ;; closed by a semicolon (defvar dynare-statements-like - '("stderr" "values" "scales" "restriction" "exclusion" "upper_cholesky" "lower_cholesky") + '("stderr" "values" "scales" "restriction" "exclusion" "upper_cholesky" "lower_cholesky" + "bind" "relax" "error_bind" "error_relax") "Dynare statements-like keywords.") ;; Those keywords that makes the lexer enter the DYNARE_BLOCK start condition @@ -99,7 +101,7 @@ "observation_trends" "deterministic_trends" "optim_weights" "homotopy_setup" "conditional_forecast_paths" "svar_identification" "moment_calibration" "irf_calibration" "ramsey_constraints" "generate_irfs" - "matched_moments" "verbatim") + "matched_moments" "occbin_constraints" "verbatim") "Dynare block keywords.")) ;; Mathematical functions and operators used in model equations (see "hand_side" in Bison file) diff --git a/tests/.gitignore b/tests/.gitignore index cad54782c..79788679e 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -120,6 +120,8 @@ wsOct !/ms-sbvar/data.m !/objectives/sgu_ex1.mat !/observation_trends_and_prefiltering/generate_trend_stationary_AR1.m +!/occbin/filter/dataobsfile.mat +!/occbin/filter/NKM_mh_mode_saved.mat !/optimal_policy/Ramsey/find_c.m !/optimal_policy/Ramsey/oo_ramsey_policy_initval.mat !/optimizers/optimizer_function_wrapper.m diff --git a/tests/Makefile.am b/tests/Makefile.am index 34922721b..aa84d1cf0 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1,5 +1,11 @@ MODFILES = \ walsh.mod \ + occbin/model_irrcap_twoconstraints/dynrbc.mod \ + occbin/model_irrcap_twoconstraints/dynrbc_0_std_shocks.mod \ + occbin/model_borrcon/borrcon.mod \ + occbin/model_borrcon/borrcon_0_std_shocks.mod \ + occbin/filter/NKM.mod \ + occbin/filter/NKM_0_std_shocks.mod \ optimizers/fs2000_6.mod \ moments/example1_hp_test.mod \ moments/fs2000_post_moments.mod \ @@ -594,6 +600,7 @@ XFAIL_MODFILES = ramst_xfail.mod \ identification/LindeTrabandt/LindeTrabandt2019_xfail.mod \ steady_state/Linear_steady_state_xfail.mod \ optimal_policy/Ramsey/ramsey_histval_xfail.mod \ + occbin/model_irrcap_twoconstraints/dynrbc_token_xfail.mod \ particle/first_spec_xfail_0.mod \ particle/first_spec_xfail_1.mod \ kalman_initial_state/fs2000_kalman_initial_xfail.mod \ @@ -617,6 +624,12 @@ optimal_policy/neo_growth_ramsey_foresight.o.trs: optimal_policy/neo_growth_fore optimal_policy/neo_growth_ramsey_k_order.m.trs: optimal_policy/neo_growth_k_order.m.trs optimal_policy/neo_growth_ramsey_k_order.o.trs: optimal_policy/neo_growth_k_order.o.trs +occbin/model_irrcap_twoconstraints/dynrbc_0_std_shocks.m.trs: occbin/model_irrcap_twoconstraints/dynrbc.m.trs +occbin/model_irrcap_twoconstraints/dynrbc_0_std_shocks.o.trs: occbin/model_irrcap_twoconstraints/dynrbc.o.trs + +occbin/model_borrcon/borrcon_0_std_shocks.m.trs: occbin/model_borrcon/borrcon.m.trs +occbin/model_borrcon/borrcon_0_std_shocks.o.trs: occbin/model_borrcon/borrcon.o.trs + example1_use_dll.m.trs: example1.m.trs example1_use_dll.o.trs: example1.o.trs @@ -1245,6 +1258,10 @@ EXTRA_DIST = \ observation_trends_and_prefiltering/Trend_model_calib_no_prefilter_common.inc \ observation_trends_and_prefiltering/Trend_load_data_common.inc \ observation_trends_and_prefiltering/Trend_no_prefilter_conditional_forecast.inc \ + occbin/model_borrcon/borrcon_common.inc \ + occbin/model_irrcap_twoconstraints/dynrbc_common.inc \ + occbin/filter/dataobsfile.mat \ + occbin/filter/NKM_mh_mode_saved.mat \ optimal_policy/neo_growth_common.inc \ optimal_policy/neo_growth_ramsey_common.inc \ optimal_policy/Ramsey/oo_ramsey_policy_initval.mat \ @@ -1479,6 +1496,8 @@ clean-local: kalman/likelihood_from_dynare/fs_ns_dat_simul_uncorr_ME.m \ kalman/likelihood_from_dynare/fs_ns_dat_simul_uncorr_ME_missing.m + rm -f occbin/filter/dataobsfile2.mat + find . -name "*.tex" -type f -delete find . -name "*.aux" -type f -delete find . -name "*.log" -type f -delete diff --git a/tests/occbin/filter/NKM.mod b/tests/occbin/filter/NKM.mod new file mode 100644 index 000000000..112e73a76 --- /dev/null +++ b/tests/occbin/filter/NKM.mod @@ -0,0 +1,342 @@ +//Tests Occbin estimation with IVF and PKF with 1 constraints + +// this file implements the model in: +// Atkinson, T., A. W. Richter, and N. A. Throckmorton (2019). +// The zero lower bound andestimation accuracy.Journal of Monetary Economics +// original codes provided by Alexander Richter +// adapted for dynare implementation +// ------------------------- Settings -----------------------------------------// + +@#ifndef small_model + @#define small_model = 0 +@#endif + +// if ~exist('run_ivf','var') +run_ivf=0; +// end + +// ----------------- Defintions -----------------------------------------// +var + c //1 Consumption + n //2 Labor + y //5 Output + yf //6 Final goods + yg //11 Output growth gap + w //12 Real wage rate + wf //13 Flexible real wage + pigap //15 Inflation rate -> pi(t)/pibar = pigap + inom //16 Nominal interest rate + inomnot //17 Notional interest rate + mc //19 Real marginal cost + lam //20 Inverse marginal utility of wealth + g //21 Growth shock + s //22 Risk premium shock + mp //23 Monetary policy shock + pi //24 Observed inflation + @#if !(small_model) + x //3 Investment + k //4 Capital + u //7 Utilization cost + ups //8 Utilization choice + wg //9 Real wage growth gap + xg //10 Investment growth + rk //14 Real rental rate + q //18 Tobins q + @#endif +; +varexo + epsg // Productivity growth shock + epsi // Notional interest rate shock + epss // Risk premium shock +; +parameters + // Calibrated Parameters + beta // Discount factor + chi // Labor disutility scale + thetap // Elasticity of subs. between intermediate goods + thetaw // Elasticity of subs. between labor types + nbar // Steady state labor + eta // Inverse frish elasticity of labor supply + delta // Depreciation + alpha // Capital share + gbar // Mean growth rate + pibar // Inflation target + inombar // Steady gross nom interest rate + inomlb // Effective lower bound on gross nominal interest rate + sbar // Average risk premium + // Parameters for DGP and Estimated parameters + varphip // Rotemberg price adjustment cost + varphiw // Rotemberg wage adjustment cost + h // Habit persistence + rhos // Persistence + rhoi // Persistence + sigz // Standard deviation technology + sigs // Standard deviation risk premia + sigi // Standard deviation mon pol + phipi // Inflation responsiveness + phiy // Output responsiveness + nu // Investment adjustment cost + sigups // Utilization + + // Switching parameters + zlb + ; + + +// ---------------- Calibration -----------------------------------------// + +beta = 0.9949; // Discount factor +thetap = 6; // Elasticity of subs. between intermediate goods +thetaw = 6; // Elasticity of subs. between labor types +nbar = 1/3; // Steady state labor +eta = 1/3; // Inverse frish elasticity of labor supply +delta = 0.025; // Depreciation +alpha = 0.35; // Capital share +gbar = 1.0034; // Mean growth rate +pibar = 1.0053; // Inflation target +sbar = 1.0058; // Average risk premium +rkbar = 1/beta; +varphiw = 100; // Rotemberg wage adjustment cost +nu = 4; // Investment adjustment cost +sigups = 5; // Utilization +varphip = 100; // Rotemberg price adjustment cost +h = 0.80; // Habit persistence +rhos = 0.80; // Persistence +rhoi = 0.80; // Persistence +sigz = 0.005; // Standard deviation +sigs = 0.005; // Standard deviation +sigi = 0.002; // Standard deviation +phipi = 2.0; // Inflation responsiveness +phiy = 0.5; // Output responsiveness +zlb = 0 ; // ZLB dummy +inomlb = 1 ; // Inom LB + +// ---------------- Model -----------------------------------------------// +model; + + @#if !(small_model) + [name = 'HH FOC utilization (1)'] + rk = steady_state(rk)*exp(sigups*(ups-1)); + + [name = 'Utilization definition (3)'] + u = steady_state(rk)*(exp(sigups*(ups-1))-1)/sigups; + + [name = 'Firm FOC capital (4)'] + rk = mc*alpha*g*yf/(ups*k(-1)); + + [name = 'HH FOC capital (17)'] + q = beta*(lam/lam(+1))*(rk(+1)*ups(+1)-u(+1)+(1-delta)*q(+1))/g(+1); + + [name = 'HH FOC investment (18)'] + 1 = q*(1-nu*(xg-1)^2/2-nu*(xg-1)*xg)+beta*nu*gbar*q(+1)*(lam/lam(+1))*xg(+1)^2*(xg(+1)-1)/g(+1); + + [name = 'Wage Phillips Curve (20)'] + varphiw*(wg-1)*wg = ((1-thetaw)*w+thetaw*wf)*n/yf + beta*varphiw*(lam/lam(+1))*(wg(+1)-1)*wg(+1)*(yf(+1)/yf) ; + + [name = 'Real wage growth gap (6)'] + wg = pigap*g*w/(gbar*w(-1)); + + [name = 'Law of motion for capital (15)'] + k = (1-delta)*(k(-1)/g)+x*(1-nu*(xg-1)^2/2); + + [name = 'Investment growth gap (14)'] + xg = g*x/(gbar*x(-1)); + @#endif + + + @#if small_model + [name = 'Production function (2)'] + yf = n; + + [name = 'Firm FOC labor (5)'] + w = mc*yf/n; + + [name = 'Output definition (7)'] + y = (1-varphip*(pigap-1)^2/2)*yf; + + [name = 'ARC (13)'] + c = y; + + [name = 'Household labpur supply equals flex wage'] + w = wf; + @#else + [name = 'Production function (2)'] + yf = (ups*k(-1)/g)^alpha*n^(1-alpha); + + [name = 'Firm FOC labor (5)'] + w = (1-alpha)*mc*yf/n; + + [name = 'Output definition (7)'] + y = (1-varphip*(pigap-1)^2/2-varphiw*(wg-1)^2/2)*yf - u*k(-1)/g; + + [name = 'ARC (13)'] + x = y-c; + @#endif + + [name = 'Output growth gap (8)'] + yg = g*y/(gbar*y(-1)); + + [name = 'Notional Interest Rate (9)'] + inomnot = inomnot(-1)^rhoi*(inombar*pigap^phipi*yg^phiy)^(1-rhoi)*exp(mp); + + [name = 'Nominal Interest Rate (10)'] + inom = (inomnot*(1-zlb)+zlb*inomlb); + + [name = 'Inverse MUC (11)'] + lam = c-h*c(-1)/g; + + [name = 'Flexible real wage definition (12)'] + wf = chi*n^eta*lam; + + [name = 'HH FOC bond (16)'] + 1 = beta*(lam/lam(+1))*s*inom/(g(+1)*pibar*pigap(+1)); + + [name = 'Price Phillips Curve (19)'] + varphip*(pigap-1)*pigap = 1-thetap+thetap*mc+beta*varphip*(lam/lam(+1))*(pigap(+1)-1)*pigap(+1)*(yf(+1)/yf); + + [name = 'Stochastic productivity growth (21)'] + g = gbar+ sigz*epsg; + + [name = 'Risk premium shock (22)'] + s = (1-rhos)*sbar + rhos*s(-1)+sigs*epss ; + + [name = 'Notional interest rate shock (23)'] + mp = sigi*epsi; + + [name = 'Observed inflation (24)'] + pi = pigap*pibar; + +end; + +occbin_constraints; +name 'zlb'; bind inom+inombar <= inomlb; relax inom+inombar > inomlb; +end; + +// ---------------- Steady state -----------------------------------------// +steady_state_model; + mp = 0; + xg = 1; + s = sbar; + n = nbar; + ups =1; + u =0; + q =1; + g = gbar; + yg = 1; + pigap = 1; + wg =1; + pi = pigap*pibar; + // FOC bond + inom = gbar*pibar/(beta*s); + inomnot = inom; + inombar = inom; + // Firm pricing + mc = (thetap-1)/thetap; + // FOC capital + rk = gbar/beta+delta-1; + // Marginal cost definition + @#if small_model + alpha = 0; + thetaw=1; + @#endif + w = (mc*(1-alpha)^(1-alpha)*alpha^alpha/rk^alpha)^(1/(1-alpha)); + @#if small_model + wf = w; + @#else + wf = (thetaw-1)*w/thetaw; + @#endif + // Consolidated FOC firm + k = w*n*gbar*alpha/(rk*(1-alpha)); + // Law of motion for capital + x = (1-(1-delta)/gbar)*k; + // Production function + yf = (k/gbar)^alpha*n^(1-alpha); + // Real GDP + y = yf; + // Aggregate resouce constraint + c = y-x; + // FOC labor + lam = (1-h/gbar)*c; + chi = wf/(n^eta*lam); + + // try log observables +end; + +// ---------------- Checks -----------------------------------------// +steady; +check; + +// ---------------- Simulation -----------------------------------------// +shocks; + var epsi = 1; + var epss = 1; + var epsg = 1; +end; + +steady; +check; + +// ---------------- Estimation -----------------------------------------// + +varobs yg inom pi; + estimated_params; + // PARAM NAME, INITVAL, LB, UB, PRIOR_SHAPE, PRIOR_P1, PRIOR_P2, PRIOR_P3, PRIOR_P4, JSCALE + // PRIOR_SHAPE: BETA_PDF, GAMMA_PDF, NORMAL_PDF, INV_GAMMA_PDF + varphip,,0,inf,NORMAL_PDF,100,25; + phipi,,,,NORMAL_PDF,2,0.25; + phiy,,0,inf,NORMAL_PDF,0.5,0.25; + h,,,,BETA_PDF,0.8,0.1; + rhos,,,,BETA_PDF,0.8,0.1; + rhoi,,,,BETA_PDF,0.8,0.1; + sigz,,,,INV_GAMMA_PDF,0.005,0.005; + sigs,,,,INV_GAMMA_PDF,0.005,0.005; + sigi,,,,INV_GAMMA_PDF,0.002,0.002; + end; + + +// dataloading_jme_beta(1,'sims.txt',30); + load('dataobsfile','inom') + // check if inom is at lb and remove data + associated shock + verbatim; + inom(inom==1)=NaN; + end; + inx = strmatch('epsi',M_.exo_names); + if any(isnan(inom)) + M_.heteroskedastic_shocks.Qscale_orig.periods=find(isnan(inom)); + M_.heteroskedastic_shocks.Qscale_orig.exo_id=inx; + M_.heteroskedastic_shocks.Qscale_orig.scale=0; + else + options_.heteroskedastic_filter=false; + end + + copyfile dataobsfile.mat dataobsfile2.mat + save dataobsfile2 inom -append + // -----------------Occbin ----------------------------------------------// + options_.occbin.smoother.debug=1; + occbin_setup(filter_use_relaxation,likelihood_piecewise_kalman_filter); + // use PKF + estimation( + datafile=dataobsfile2, mode_file=NKM_mh_mode_saved, + mode_compute=0, nobs=120, first_obs=1, + mh_replic=0, plot_priors=0, smoother, + graph_format=(fig), nodisplay,consider_all_endogenous,heteroskedastic_filter); + + oo0=oo_; + // use inversion filter (note that IF provides smoother together with likelihood) + occbin_setup(likelihood_inversion_filter,smoother_inversion_filter); + + estimation( + datafile=dataobsfile2, mode_file=NKM_mh_mode_saved, + mode_compute=0, nobs=120, first_obs=1, + mh_replic=0, plot_priors=0, smoother, + graph_format=(fig), nodisplay, consider_all_endogenous,heteroskedastic_filter); + + // show initial condition effect of IF + figure, + subplot(221) + plot([oo0.SmoothedShocks.epsg oo_.SmoothedShocks.epsg]), title('epsg') + subplot(222) + plot([oo0.SmoothedShocks.epsi oo_.SmoothedShocks.epsi]), title('epsi') + subplot(223) + plot([oo0.SmoothedShocks.epss oo_.SmoothedShocks.epss]), title('epss') + legend('PKF','IF') diff --git a/tests/occbin/filter/NKM_0_std_shocks.mod b/tests/occbin/filter/NKM_0_std_shocks.mod new file mode 100644 index 000000000..c45e4c8e9 --- /dev/null +++ b/tests/occbin/filter/NKM_0_std_shocks.mod @@ -0,0 +1,345 @@ +//Tests Occbin estimation with IVF and PKF with 1 constraints and redundant shocks + +// this file implements the model in: +// Atkinson, T., A. W. Richter, and N. A. Throckmorton (2019). +// The zero lower bound andestimation accuracy.Journal of Monetary Economics +// original codes provided by Alexander Richter +// adapted for dynare implementation +// ------------------------- Settings -----------------------------------------// + +@#ifndef small_model + @#define small_model = 0 +@#endif + +// if ~exist('run_ivf','var') +run_ivf=0; +// end + +// ----------------- Defintions -----------------------------------------// +var + c //1 Consumption + n //2 Labor + y //5 Output + yf //6 Final goods + yg //11 Output growth gap + w //12 Real wage rate + wf //13 Flexible real wage + pigap //15 Inflation rate -> pi(t)/pibar = pigap + inom //16 Nominal interest rate + inomnot //17 Notional interest rate + mc //19 Real marginal cost + lam //20 Inverse marginal utility of wealth + g //21 Growth shock + s //22 Risk premium shock + mp //23 Monetary policy shock + pi //24 Observed inflation + @#if !(small_model) + x //3 Investment + k //4 Capital + u //7 Utilization cost + ups //8 Utilization choice + wg //9 Real wage growth gap + xg //10 Investment growth + rk //14 Real rental rate + q //18 Tobins q + @#endif +; +varexo + junk1 + epsg // Productivity growth shock + epsi // Notional interest rate shock + epss // Risk premium shock + junk2 +; +parameters + // Calibrated Parameters + beta // Discount factor + chi // Labor disutility scale + thetap // Elasticity of subs. between intermediate goods + thetaw // Elasticity of subs. between labor types + nbar // Steady state labor + eta // Inverse frish elasticity of labor supply + delta // Depreciation + alpha // Capital share + gbar // Mean growth rate + pibar // Inflation target + inombar // Steady gross nom interest rate + inomlb // Effective lower bound on gross nominal interest rate + sbar // Average risk premium + // Parameters for DGP and Estimated parameters + varphip // Rotemberg price adjustment cost + varphiw // Rotemberg wage adjustment cost + h // Habit persistence + rhos // Persistence + rhoi // Persistence + sigz // Standard deviation technology + sigs // Standard deviation risk premia + sigi // Standard deviation mon pol + phipi // Inflation responsiveness + phiy // Output responsiveness + nu // Investment adjustment cost + sigups // Utilization + + // Switching parameters + zlb + ; + + +// ---------------- Calibration -----------------------------------------// + +beta = 0.9949; // Discount factor +thetap = 6; // Elasticity of subs. between intermediate goods +thetaw = 6; // Elasticity of subs. between labor types +nbar = 1/3; // Steady state labor +eta = 1/3; // Inverse frish elasticity of labor supply +delta = 0.025; // Depreciation +alpha = 0.35; // Capital share +gbar = 1.0034; // Mean growth rate +pibar = 1.0053; // Inflation target +sbar = 1.0058; // Average risk premium +rkbar = 1/beta; +varphiw = 100; // Rotemberg wage adjustment cost +nu = 4; // Investment adjustment cost +sigups = 5; // Utilization +varphip = 100; // Rotemberg price adjustment cost +h = 0.80; // Habit persistence +rhos = 0.80; // Persistence +rhoi = 0.80; // Persistence +sigz = 0.005; // Standard deviation +sigs = 0.005; // Standard deviation +sigi = 0.002; // Standard deviation +phipi = 2.0; // Inflation responsiveness +phiy = 0.5; // Output responsiveness +zlb = 0 ; // ZLB dummy +inomlb = 1 ; // Inom LB + +// ---------------- Model -----------------------------------------------// +model; + + @#if !(small_model) + [name = 'HH FOC utilization (1)'] + rk = steady_state(rk)*exp(sigups*(ups-1)); + + [name = 'Utilization definition (3)'] + u = steady_state(rk)*(exp(sigups*(ups-1))-1)/sigups; + + [name = 'Firm FOC capital (4)'] + rk = mc*alpha*g*yf/(ups*k(-1)); + + [name = 'HH FOC capital (17)'] + q = beta*(lam/lam(+1))*(rk(+1)*ups(+1)-u(+1)+(1-delta)*q(+1))/g(+1); + + [name = 'HH FOC investment (18)'] + 1 = q*(1-nu*(xg-1)^2/2-nu*(xg-1)*xg)+beta*nu*gbar*q(+1)*(lam/lam(+1))*xg(+1)^2*(xg(+1)-1)/g(+1); + + [name = 'Wage Phillips Curve (20)'] + varphiw*(wg-1)*wg = ((1-thetaw)*w+thetaw*wf)*n/yf + beta*varphiw*(lam/lam(+1))*(wg(+1)-1)*wg(+1)*(yf(+1)/yf) ; + + [name = 'Real wage growth gap (6)'] + wg = pigap*g*w/(gbar*w(-1)); + + [name = 'Law of motion for capital (15)'] + k = (1-delta)*(k(-1)/g)+x*(1-nu*(xg-1)^2/2); + + [name = 'Investment growth gap (14)'] + xg = g*x/(gbar*x(-1)); + @#endif + + + @#if small_model + [name = 'Production function (2)'] + yf = n; + + [name = 'Firm FOC labor (5)'] + w = mc*yf/n; + + [name = 'Output definition (7)'] + y = (1-varphip*(pigap-1)^2/2)*yf; + + [name = 'ARC (13)'] + c = y; + + [name = 'Household labpur supply equals flex wage'] + w = wf; + @#else + [name = 'Production function (2)'] + yf = (ups*k(-1)/g)^alpha*n^(1-alpha); + + [name = 'Firm FOC labor (5)'] + w = (1-alpha)*mc*yf/n; + + [name = 'Output definition (7)'] + y = (1-varphip*(pigap-1)^2/2-varphiw*(wg-1)^2/2)*yf - u*k(-1)/g; + + [name = 'ARC (13)'] + x = y-c; + @#endif + + [name = 'Output growth gap (8)'] + yg = g*y/(gbar*y(-1)) + junk1 + junk2; + + [name = 'Notional Interest Rate (9)'] + inomnot = inomnot(-1)^rhoi*(inombar*pigap^phipi*yg^phiy)^(1-rhoi)*exp(mp); + + [name = 'Nominal Interest Rate (10)'] + inom = (inomnot*(1-zlb)+zlb*inomlb); + + [name = 'Inverse MUC (11)'] + lam = c-h*c(-1)/g; + + [name = 'Flexible real wage definition (12)'] + wf = chi*n^eta*lam; + + [name = 'HH FOC bond (16)'] + 1 = beta*(lam/lam(+1))*s*inom/(g(+1)*pibar*pigap(+1)); + + [name = 'Price Phillips Curve (19)'] + varphip*(pigap-1)*pigap = 1-thetap+thetap*mc+beta*varphip*(lam/lam(+1))*(pigap(+1)-1)*pigap(+1)*(yf(+1)/yf); + + [name = 'Stochastic productivity growth (21)'] + g = gbar+ sigz*epsg; + + [name = 'Risk premium shock (22)'] + s = (1-rhos)*sbar + rhos*s(-1)+sigs*epss ; + + [name = 'Notional interest rate shock (23)'] + mp = sigi*epsi; + + [name = 'Observed inflation (24)'] + pi = pigap*pibar; + +end; + +occbin_constraints; +name 'zlb'; bind inom+inombar <= inomlb; relax inom+inombar > inomlb; +end; + +// ---------------- Steady state -----------------------------------------// +steady_state_model; + mp = 0; + xg = 1; + s = sbar; + n = nbar; + ups =1; + u =0; + q =1; + g = gbar; + yg = 1; + pigap = 1; + wg =1; + pi = pigap*pibar; + // FOC bond + inom = gbar*pibar/(beta*s); + inomnot = inom; + inombar = inom; + // Firm pricing + mc = (thetap-1)/thetap; + // FOC capital + rk = gbar/beta+delta-1; + // Marginal cost definition + @#if small_model + alpha = 0; + thetaw=1; + @#endif + w = (mc*(1-alpha)^(1-alpha)*alpha^alpha/rk^alpha)^(1/(1-alpha)); + @#if small_model + wf = w; + @#else + wf = (thetaw-1)*w/thetaw; + @#endif + // Consolidated FOC firm + k = w*n*gbar*alpha/(rk*(1-alpha)); + // Law of motion for capital + x = (1-(1-delta)/gbar)*k; + // Production function + yf = (k/gbar)^alpha*n^(1-alpha); + // Real GDP + y = yf; + // Aggregate resouce constraint + c = y-x; + // FOC labor + lam = (1-h/gbar)*c; + chi = wf/(n^eta*lam); + + // try log observables +end; + +// ---------------- Checks -----------------------------------------// +steady; +check; + +// ---------------- Simulation -----------------------------------------// +shocks; + var epsi = 1; + var epss = 1; + var epsg = 1; +end; + +steady; +check; + +// ---------------- Estimation -----------------------------------------// + +varobs yg inom pi; + estimated_params; + // PARAM NAME, INITVAL, LB, UB, PRIOR_SHAPE, PRIOR_P1, PRIOR_P2, PRIOR_P3, PRIOR_P4, JSCALE + // PRIOR_SHAPE: BETA_PDF, GAMMA_PDF, NORMAL_PDF, INV_GAMMA_PDF + varphip,,0,inf,NORMAL_PDF,100,25; + phipi,,,,NORMAL_PDF,2,0.25; + phiy,,0,inf,NORMAL_PDF,0.5,0.25; + h,,,,BETA_PDF,0.8,0.1; + rhos,,,,BETA_PDF,0.8,0.1; + rhoi,,,,BETA_PDF,0.8,0.1; + sigz,,,,INV_GAMMA_PDF,0.005,0.005; + sigs,,,,INV_GAMMA_PDF,0.005,0.005; + sigi,,,,INV_GAMMA_PDF,0.002,0.002; + end; + + +// dataloading_jme_beta(1,'sims.txt',30); + load('dataobsfile','inom') + // check if inom is at lb and remove data + associated shock + verbatim; + inom(inom==1)=NaN; + end; + inx = strmatch('epsi',M_.exo_names); + if any(isnan(inom)) + M_.heteroskedastic_shocks.Qscale_orig.periods=find(isnan(inom)); + M_.heteroskedastic_shocks.Qscale_orig.exo_id=inx; + M_.heteroskedastic_shocks.Qscale_orig.scale=0; + else + options_.heteroskedastic_filter=false; + end + + copyfile dataobsfile.mat dataobsfile2.mat + save dataobsfile2 inom -append + // -----------------Occbin ----------------------------------------------// + options_.occbin.filter.use_relaxation=true; + // use PKF + estimation( + datafile=dataobsfile2, mode_file=NKM_mh_mode_saved, + mode_compute=0, nobs=120, first_obs=1, + mh_replic=0, plot_priors=0, smoother, + graph_format=(fig), nodisplay,consider_all_endogenous,heteroskedastic_filter); + + oo0=oo_; + + // use inversion filter (note that IF provides smoother together with likelihood) + options_.occbin.likelihood.inversion_filter = 1; + options_.occbin.smoother.inversion_filter = 1; + + estimation( + datafile=dataobsfile2, mode_file=NKM_mh_mode_saved, + mode_compute=0, nobs=120, first_obs=1, + mh_replic=0, plot_priors=0, smoother, + graph_format=(fig), nodisplay, consider_all_endogenous,heteroskedastic_filter); + + // show initial condition effect of IF + figure, + subplot(221) + plot([oo0.SmoothedShocks.epsg oo_.SmoothedShocks.epsg]), title('epsg') + subplot(222) + plot([oo0.SmoothedShocks.epsi oo_.SmoothedShocks.epsi]), title('epsi') + subplot(223) + plot([oo0.SmoothedShocks.epss oo_.SmoothedShocks.epss]), title('epss') + legend('PKF','IF') diff --git a/tests/occbin/filter/NKM_mh_mode_saved.mat b/tests/occbin/filter/NKM_mh_mode_saved.mat new file mode 100644 index 0000000000000000000000000000000000000000..80764521cd915b366960d91fe4c93cdbbf481a72 GIT binary patch literal 972 zcmeZu4DoSvQZUssQ1EpO(M`+DN!3vZ$Vn_o%P-2cQV4Jk_w+L}(NSTPGBC9=HdZh)Ffvpi5-`93qo*%Fko}R7fuUl~d8wZaZ zIKZ~VQbNImV}sMrIRR%*95^yzB4c7X!_{s%6YtrE^^5)OM4>MrinS^oF)CD{_|wU5J=y2}(4+LYGk72j-3SR3DaQ9Vt*W0l6-d&Mg*C4Rr! zJA3YuDA{L`tJl_)KR(9zo$ub2NwcKc6TTh(vii;b-S4!L_Z^n~+wn$qx|Hmp^m|`D zbZ0zFP+4E5T&cBBT+LphZ{Z6Z{tl_8p zv$`374gOBE|9dO`NNl;q_lx&V*=9a(EH*B)H{SK(z`NO!z=p6ODwXOEqmNNo>~WXi4ZDHG#;mhJN?`XZXXEPUe1 zew}iruc=H7+p47Y^8uY<0mN)T%mJl=6ay=i4@{m65X{1mR+gB<2^HUyV(24uZH5C# ze*+NX(hpJ(GlLz-t|&+>O3XE60Ev45@r0J0Ei#(14uLQHs&@%8IAmS;zkA2ar}iH0 za+jCie{NqgU*#ot^(*_Q3-p>Z@4d25Ns+zsA+6Yc#&IRCj(Y|6)8_2%yseRJ4*q<% literal 0 HcmV?d00001 diff --git a/tests/occbin/filter/dataobsfile.mat b/tests/occbin/filter/dataobsfile.mat new file mode 100644 index 0000000000000000000000000000000000000000..93b6334ad748cab20233647ef9a951975b26b9aa GIT binary patch literal 6568 zcmeHKdt8m#8s6>F4c&FqWlKp{?JiO#tIL!cx(t;JGu^RCQ7WD4NNE(gQ%pnXP9$=P zQm81omSkMgMTC5d5i!(x_UzpsXMX4S=lo&jjIUq&{nlFVde^(&_j#YSK6eN2nGTK| zYf}r3yMwpM0$y0CF~=(`aK(a%$fdUEb8?$(Y0Pno3=Lcn8p4SPx8=-^4(GTB#&Imn zI9zjE3v*j5F2~%=oXc_j+Qo_!6}%{PX&{GdYG%eU<^0EoHTqQWW_BMye;DiBriN- zDgTWx`E!Qb!+4!2Q2VdHA}opD@mnJ%P!VAXW{9u_7KZJt6k7OWTNKFW_sMezOMqQ_ zmgPv6<&dag!U**8$Js)IlB`R=kBG8rL?aWnFEo~-L?$Ks>ChNTRL;FJaOq;my-O`( zDa{NCGPd$f8coSrZka>U2ZmfrR5efUV2EdN>U>pwN`f=2CybV*#C2o*s{{*5q&8mM zQZj;)J9E7DOJLr2zg~@cGRTnitCu$!j-q6``T^|@HYL|o$ycr{=xgov+OA58|KrvY z`F@7HpEh{3_cBBF%$@RZwkRbFC7kMH;m@1jJf(KlGsOO(iED)lB`MU(e=B30=TnB9X_!>3 zew87-df$Pj0*2(Yr;XnYJ0-D_4#hGI$(XumYSDRyTrAd{=&_6;+IRC)?OGVZwLaiD z;S56@MszH3QA0eB`BYU)Q!*hWexe83Kfm|R2s4Afb(S_c=9Ki;WEt9XC~5gMbyTwg zB}L~CRb4S<$PXcQCAvI@DD0YeyVTg=!jAO_ULmCdK$$B7OKKExuWf@Rn z-rm|Rp-su`PT33I(joV9xZk(hi}<@-s=WOk`7gC8&)36#w%)p=7jqAGdxKxFJ~AYCK+<~=^n=>umK-c$ z$Yu7fg~uN;B>G0Z#uE`rL=Rdn)x&;l!WR6PZAyu;i^`I%*uUX|_!^H9l)P7q7^eqZ zR(OlA)q)@M&R*@%|CJ%JtsL%X;L+yMwQ*eZTVL6cs0DpV&A6#voyecsHOb?siwpKo zZ;VI$PMsc~=nOx-lKUS!G%`eD<>oXA_*pY5A=>c?Lv&xJtEK@TwFlpCy4VEVEx+RU z4RGJFy(#;Z2_C&$QlHina3G7t z5%S-swg0@MpYvP@)?3@%2mG0rJk?H60{U(94CTY|UBP8QtQu1Luw>2YDT z=+CX_36?^=kl1$vA>b>q-7_>#%2TpoPR6qU;PrKSY3(iW_R!J9tSaz`_`{UQkHGuN z-nJ{!z_Hwc20cC0-D2q!YddA&-NKYP0bZ%B%4CniIx)I^g{!cxx1O@;Thzqo&4p4H{?7qPDR+zzAN;*^}|X}4VbmLbymujSY;8KNaG_K(&&hBVISSi7M|0Bpjh5rw zT;A)cDTnjTX?=Cb()$egZ9{&Y&qIdnvv5qQ?O}-C)pZJy;8i=7?$~+Y@55W%Qaqny zJnQSE&F>iUa?X-nIvE3RhTLHVJVWX|rtQS>QtPXT#Jp{P}uD^ZejnUi^{pRGbTU@D0VV1?SN57>{+z)ri_5V;ae221 zuJB`jbEB9u*5^%545e5{nnczX0ynKZnVuiv&&}(jMI_+Q{Ggn7H8=+f`&1&Ig4e~1 zGt+|6PH@N!oeMklh1@ks@Hgj3oWVrYm;DDz+e*Zz(-uihBrUN-);YYX@*peyO8#)-J+#_dpN1HVV)s$C8P7k$yn znH#|)_cu2O?J=W7WBShv{HIWI^2y20HU~=9WcQeCvZmygX2{Sl=9Fxa8FYDJL5YDJ zr(iw$GYzy8?*UIi_cI!^U}s-*l8GYj1w+OoET^Hr_q;7V1AoY@EZ-zc+^@W(7ans0 z4_l-wr`W+xXJc{>)~!)Xuqm;kq`fCPGMXow?FR+7kIE%^4bUgBOWBG ziDJLoGBZxL0B6VA%X-vNS69Prw8sKJ1rLv1NWuQz?#R8Jf_f61jsrt~7il{E2R|v? z=hi3;ndhQTSG{Q{Nkx61DyVlJ4OuFC!_5KT6HlU6hHpW>Zr;kR{>cdZA?7u<9C-_U z_|{D4Y|JEu`$auEe?U z(|oSx_B$BQyO|{S6L1pWzgUM2Ke;FG9pr!4#Tw@AnGPJ5)V^hz0mr_o7avZ6e~wkI zr=G&jx8+h@alm0#&f9NZBR*Daw=wlt*K4nNOcDH(i`PH90sa_q#&+*08K6v9afc`)7*$2gj&wkJS!e@U~J{#t> T|9|;~&;A;G<`?n#zuW%>{-afr literal 0 HcmV?d00001 diff --git a/tests/occbin/model_borrcon/borrcon.mod b/tests/occbin/model_borrcon/borrcon.mod new file mode 100644 index 000000000..b2535a2d5 --- /dev/null +++ b/tests/occbin/model_borrcon/borrcon.mod @@ -0,0 +1,53 @@ +// --+ options: nostrict +-- +//Test Occbin with 1 constraint and redundant shocks; also checks whether defaults for error_* are correct + +var b ${b}$ (long_name='borrowing') + c ${c}$ (long_name='vonsumption') + ec ${E(c_t)}$ (long_name='expected consumption') + lb ${\lambda}$ (long_name='Lagrange multiplier') + y ${y}$ (long_name='Output') + c_hat ${\hat c}$ + b_hat ${\hat c}$ + y_hat ${\hat y}$ + ; +varexo u $u$; + +parameters RHO ${\rho}$, BETA ${\beta}$, M $M$, R $R$, SIGMA ${\sigma}$, GAMMAC $\gamma_c$, relax_borrcon ; + +model; +ec = c(1); +c = y + b - R*b(-1) ; +(1-relax_borrcon)*(b - M*y) + relax_borrcon*lb = 0; +lb = 1/c^GAMMAC - BETA*R/c(+1)^GAMMAC ; +log(y) = RHO*log(y(-1)) + u ; +c_hat = log(c) - log(steady_state(c)); +b_hat = log(b) - log(steady_state(b)); +y_hat = log(y) - log(steady_state(y)); +end; + +occbin_constraints; +% name 'relax_borrcon'; bind lb<-lb_ss; relax b>M*y; error_bind abs(lb+lb_ss); error_relax abs(b-M*y); +name 'relax_borrcon'; bind lb<-lb_ss; relax b>M*y; +end; + +steady_state_model; +b=M; +c=1+M-R*M; +ec=c; +lb=(1-BETA*R)/c^GAMMAC; +y=1; +end; + +R = 1.05; +BETA = 0.945; +RHO = 0.9; +SIGMA = 0.05; +M = 1; +GAMMAC = 1; +relax_borrcon = 0; + +shocks; +var u; stderr SIGMA; +end; + +@#include "borrcon_common.inc" \ No newline at end of file diff --git a/tests/occbin/model_borrcon/borrcon_0_std_shocks.mod b/tests/occbin/model_borrcon/borrcon_0_std_shocks.mod new file mode 100644 index 000000000..364918e9d --- /dev/null +++ b/tests/occbin/model_borrcon/borrcon_0_std_shocks.mod @@ -0,0 +1,58 @@ +// --+ options: nostrict +-- +//Test Occbin with 1 constraint and redundant shocks; also checks whether defaults for error_* are correct + +var b ${b}$ (long_name='borrowing') + c ${c}$ (long_name='vonsumption') + ec ${E(c_t)}$ (long_name='expected consumption') + lb ${\lambda}$ (long_name='Lagrange multiplier') + y ${y}$ (long_name='Output') + c_hat ${\hat c}$ + b_hat ${\hat c}$ + y_hat ${\hat y}$ +; + +varexo junk1 u junk2 ; + +parameters RHO ${\rho}$, BETA ${\beta}$, M $M$, R $R$, SIGMA ${\sigma}$, GAMMAC $\gamma_c$, relax_borrcon ; + +model; +ec = c(1); +c = y + b - R*b(-1) ; +(1-relax_borrcon)*(b - M*y) + relax_borrcon*lb = 0; +lb = 1/c^GAMMAC - BETA*R/c(+1)^GAMMAC +junk1 + junk2; +log(y) = RHO*log(y(-1)) + u ; +c_hat = log(c) - log(steady_state(c)); +b_hat = log(b) - log(steady_state(b)); +y_hat = log(y) - log(steady_state(y)); +end; + +occbin_constraints; +name 'relax_borrcon'; bind lb<-lb_ss; relax b>M*y; error_bind abs(lb+lb_ss); error_relax abs(b-M*y); +end; + +steady_state_model; +b=M; +c=1+M-R*M; +ec=c; +lb=(1-BETA*R)/c^GAMMAC; +y=1; +end; + +R = 1.05; +BETA = 0.945; +RHO = 0.9; +SIGMA = 0.05; +M = 1; +GAMMAC = 1; +relax_borrcon = 0; + +shocks; +var u; stderr SIGMA; +end; + +@#include "borrcon_common.inc" + +orig_results=load('borrcon_results.mat'); +if max(max(abs(oo_.occbin.piecewise-orig_results.oo_.occbin.piecewise)))>1e-10 + error('Results do not match') +end \ No newline at end of file diff --git a/tests/occbin/model_borrcon/borrcon_common.inc b/tests/occbin/model_borrcon/borrcon_common.inc new file mode 100644 index 000000000..b4d12a463 --- /dev/null +++ b/tests/occbin/model_borrcon/borrcon_common.inc @@ -0,0 +1,38 @@ +steady; +check; + +shocks(surprise,overwrite); +var u; +periods 10, 30; +values 0.03, -0.03; +end; + +occbin_setup(simul_periods=80); +occbin_solver(simul_maxit=11,simul_curb_retrench); +% +titlelist = char('c (consumption)','b (borrowing)','y (income)','lb (multiplier)'); +percent = 'Percent'; +level = 'Level'; +ylabels = char(percent,percent,percent,level); +figtitle = 'Simulated variables'; +legendlist = cellstr(char('Piecewise Linear','Linear')); + +options_.TeX=1; +occbin_graph; +occbin_graph c_hat b_hat y_hat; + +write_latex_original_model(write_equation_tags); +//collect_latex_files; + +oo_= occbin.unpack_simulations(M_,oo_,options_); + +line1=100*[(oo_.occbin.endo_piecewise.c-oo_.occbin.endo_ss.c)/oo_.occbin.endo_ss.c, ... + (oo_.occbin.endo_piecewise.b-oo_.occbin.endo_ss.b)/oo_.occbin.endo_ss.b, ... + (oo_.occbin.endo_piecewise.y-oo_.occbin.endo_ss.y)/oo_.occbin.endo_ss.y, ... + oo_.occbin.endo_piecewise.lb/100]; +line2=100*[(oo_.occbin.endo_linear.c-oo_.occbin.endo_ss.c)/oo_.occbin.endo_ss.c, ... + (oo_.occbin.endo_linear.b-oo_.occbin.endo_ss.b)/oo_.occbin.endo_ss.b, ... + (oo_.occbin.endo_linear.y-oo_.occbin.endo_ss.y)/oo_.occbin.endo_ss.y, ... + oo_.occbin.endo_linear.lb/100]; + +occbin.make_chart(titlelist,legendlist,figtitle,ylabels,cat(3,line1,line2)); \ No newline at end of file diff --git a/tests/occbin/model_irrcap_twoconstraints/dynrbc.mod b/tests/occbin/model_irrcap_twoconstraints/dynrbc.mod new file mode 100755 index 000000000..0249f337e --- /dev/null +++ b/tests/occbin/model_irrcap_twoconstraints/dynrbc.mod @@ -0,0 +1,47 @@ +//Tests Occbin with 2 constraints + +// variables +var a, c, i, k, lambdak; + +// innovations to shock processes +varexo erra; + + +// parameters +parameters ALPHA, DELTAK, BETA, GAMMAC, RHOA, PHI, PSI, PSINEG, INEG, IRR; + +model; + +# zkss = ((1/BETA-1+DELTAK)/ALPHA)^(1/(ALPHA-1)); +# zcss = -DELTAK*zkss + zkss^ALPHA; +# ziss = DELTAK*zkss; +# zuss = (zcss^(1-GAMMAC)-1)/(1-GAMMAC); +# zvss = zuss/(1-BETA); + +///////////////////////////////////////////////////////////////// +// 1. +-exp(c)^(-GAMMAC)*(1+2*INEG*PSI*(exp(k)/exp(k(-1))-1)/exp(k(-1))) ++ BETA*exp(c(1))^(-GAMMAC)*((1-DELTAK)-2*INEG*PSI*(exp(k(1))/exp(k)-1)* + (-exp(k(1))/exp(k)^2)+ALPHA*exp(a(1))*exp(k)^(ALPHA-1))= + -lambdak+BETA*(1-DELTAK)*lambdak(1); + +// 2. +exp(c)+exp(k)-(1-DELTAK)*exp(k(-1))+ +INEG*PSI*(exp(k)/exp(k(-1))-1)^2=exp(a)*exp(k(-1))^(ALPHA); + +// 3. +exp(i) = exp(k)-(1-DELTAK)*exp(k(-1)); + +// 4. +lambdak*(1-IRR) + IRR*(i - log(PHI*ziss)) = 0; + +// 5. +a = RHOA*a(-1)+erra; +end; + +occbin_constraints; +name 'IRR'; bind i-0.000001; error_bind abs(i-0.000001); error_relax abs(i-0.000001); +end; + +@#include "dynrbc_common.inc" + +orig_results=load('dynrbc_results.mat'); +if max(max(abs(oo_.occbin.piecewise-orig_results.oo_.occbin.piecewise)))>1e-10 + error('Results do not match') +end + +if max(max(abs(oo_.SmoothedShocks.erra-orig_results.oo_.SmoothedShocks.erra)))>1e-10 + error('SmoothedShocks do not match') +end + +if max(max(abs(struct2array(oo_.SmoothedVariables)-struct2array(orig_results.oo_.SmoothedVariables))))>1e-10 + error('SmoothedShocks do not match') +end + +if max(max(abs(oo_.Smoother.SteadyState-orig_results.oo_.Smoother.SteadyState)))>1e-10 + error('SmoothedShocks do not match') +end \ No newline at end of file diff --git a/tests/occbin/model_irrcap_twoconstraints/dynrbc_common.inc b/tests/occbin/model_irrcap_twoconstraints/dynrbc_common.inc new file mode 100644 index 000000000..41fb6b6d9 --- /dev/null +++ b/tests/occbin/model_irrcap_twoconstraints/dynrbc_common.inc @@ -0,0 +1,116 @@ +steady_state_model; +kss = ((1/BETA-1+DELTAK)/ALPHA)^(1/(ALPHA-1)); +css = -DELTAK*kss +kss^ALPHA; +iss = DELTAK*kss; + + +k = log(kss); +c = log(css); +i = log(iss); +lambdak = 0; +a=0; +end; + +BETA=0.96; +ALPHA=0.33; +DELTAK=0.10; +GAMMAC=2; +RHOA = 0.9; +PHI = 0.975; +PSI = 5; % adjustment cost for capital if investment is negative +INEG = 0; +IRR = 0; + +shocks; + var erra; stderr 0.015; +end; + +steady; + +// run occbin simulations +% Option=1: impulse responses +% Option=2: random simulation + +@#for option_val in [1, 2] + +option=@{option_val}; + +%%%%%%%%%%%%%%%% Inputs stop here %%%%%%%%%%%%%%%%%%%%% + +if option==1 + shocks(surprise,overwrite); + var erra; + periods 1:9, 10, 50, 90, 130, 131:169; + values -0.0001, -0.01,-0.02, 0.01, 0.02, 0; + end; + + +elseif option==2 + nperiods = 100; + randn('seed',1); + shockssequence = 1*randn(nperiods,1)*0.02 ; + + shocks(surprise,overwrite); + var erra; + periods 1:100; + values (shockssequence); + end; + +end + +% set inputs + +occbin_setup; +options_.occbin.smoother.debug=1; +occbin_solver(simul_periods=200,simul_maxit=200,simul_curb_retrench,simul_check_ahead_periods=200); +occbin_write_regimes(filename='test',periods=[1:100]); + +%% Modify to plot IRFs +titlelist = char('c','lambdak','k','i','a'); +percent = 'Percent'; +value = 'value'; +ylabels = char(percent,value,percent,percent,percent); +figtitle = 'Simulated variables'; +legendlist = cellstr(char('Piecewise Linear','Linear')); + +oo_= occbin.unpack_simulations(M_,oo_,options_); + +line1=100*[oo_.occbin.endo_piecewise.c-oo_.occbin.endo_ss.c,oo_.occbin.endo_piecewise.lambdak/100,oo_.occbin.endo_piecewise.k-oo_.occbin.endo_ss.k,oo_.occbin.endo_piecewise.i-oo_.occbin.endo_ss.i,oo_.occbin.endo_piecewise.a-oo_.occbin.endo_ss.a]; +line2=100*[oo_.occbin.endo_linear.c-oo_.occbin.endo_ss.c,oo_.occbin.endo_linear.lambdak/100,oo_.occbin.endo_linear.k-oo_.occbin.endo_ss.k,oo_.occbin.endo_linear.i-oo_.occbin.endo_ss.i,oo_.occbin.endo_linear.a-oo_.occbin.endo_ss.a]; + +occbin.make_chart(titlelist,legendlist,figtitle,ylabels,cat(3,line1,line2)); + +occbin_graph(noconstant) c erra lambdak k i a k; + +@#if option_val==1 + verbatim; + c=oo_.occbin.endo_piecewise.c; + end; + save('datasim','c'); + varobs c; + + occbin_solver(simul_periods=200,simul_maxit=200,simul_curb_retrench,simul_check_ahead_periods=200); + occbin_setup(smoother_periods=200,smoother_maxit=200,smoother_curb_retrench,smoother_check_ahead_periods=200); + calib_smoother(datafile=datasim); + oo_= occbin.unpack_simulations(M_,oo_,options_); + + titlelist = char('c','lambdak','k','i','a','erra'); + percent = 'Percent'; + value = 'value'; + ylabels = char(percent,value,percent,percent,percent, value); + figtitle = 'Smoothed variables (piecewise)'; + legendlist = cellstr(char('Simulated','Piecewise smoother')); + + shock_vector=[oo_.occbin.shocks_sequence./100; zeros(length(oo_.occbin.endo_piecewise.c)-size(oo_.occbin.shocks_sequence,1),size(oo_.occbin.shocks_sequence,2))]; + line1=100*[oo_.occbin.endo_piecewise.c-oo_.occbin.endo_ss.c,oo_.occbin.endo_piecewise.lambdak/100,oo_.occbin.endo_piecewise.k-oo_.occbin.endo_ss.k,oo_.occbin.endo_piecewise.i-oo_.occbin.endo_ss.i,oo_.occbin.endo_piecewise.a-oo_.occbin.endo_ss.a, shock_vector]; + line2=100*[oo_.occbin.smoother.SmoothedVariables.c-oo_.occbin.endo_ss.c,oo_.occbin.smoother.SmoothedVariables.lambdak/100,oo_.occbin.smoother.SmoothedVariables.k-oo_.occbin.endo_ss.k,oo_.occbin.smoother.SmoothedVariables.i-oo_.occbin.endo_ss.i,oo_.occbin.smoother.SmoothedVariables.a-oo_.occbin.endo_ss.a, oo_.occbin.smoother.SmoothedShocks.erra/100]; + occbin.make_chart(titlelist,legendlist,figtitle,ylabels,cat(3,line1,line2)); + + figtitle = 'Smoothed variables (linear)'; + legendlist = cellstr(char('Simulated','Linear smoother')); + + line1=100*[oo_.occbin.endo_piecewise.c-oo_.occbin.endo_ss.c,oo_.occbin.endo_piecewise.lambdak/100,oo_.occbin.endo_piecewise.k-oo_.occbin.endo_ss.k,oo_.occbin.endo_piecewise.i-oo_.occbin.endo_ss.i,oo_.occbin.endo_piecewise.a-oo_.occbin.endo_ss.a, shock_vector]; + line2=100*[oo_.occbin.linear_smoother.SmoothedVariables.c-oo_.occbin.endo_ss.c,oo_.occbin.linear_smoother.SmoothedVariables.lambdak/100,oo_.occbin.linear_smoother.SmoothedVariables.k-oo_.occbin.endo_ss.k,oo_.occbin.linear_smoother.SmoothedVariables.i-oo_.occbin.endo_ss.i,oo_.occbin.linear_smoother.SmoothedVariables.a-oo_.occbin.endo_ss.a, oo_.occbin.linear_smoother.SmoothedShocks.erra/100]; + occbin.make_chart(titlelist,legendlist,figtitle,ylabels,cat(3,line1,line2)); +@#endif +@#endfor \ No newline at end of file diff --git a/tests/occbin/model_irrcap_twoconstraints/dynrbc_token_xfail.mod b/tests/occbin/model_irrcap_twoconstraints/dynrbc_token_xfail.mod new file mode 100644 index 000000000..484b3831c --- /dev/null +++ b/tests/occbin/model_irrcap_twoconstraints/dynrbc_token_xfail.mod @@ -0,0 +1,80 @@ +% check for correct error message if token cannot be interpreted +// variables +var a, c, i, k, lambdak; + +// innovations to shock processes +varexo erra; + + +// parameters +parameters ALPHA, DELTAK, BETA, GAMMAC, RHOA, PHI, PSI, PSINEG, INEG, IRR; + +model(occbin); + +# zkss = ((1/BETA-1+DELTAK)/ALPHA)^(1/(ALPHA-1)); +# zcss = -DELTAK*zkss + zkss^ALPHA; +# ziss = DELTAK*zkss; +# zuss = (zcss^(1-GAMMAC)-1)/(1-GAMMAC); +# zvss = zuss/(1-BETA); + +///////////////////////////////////////////////////////////////// +// 1. +-exp(c)^(-GAMMAC)*(1+2*INEG*PSI*(exp(k)/exp(k(-1))-1)/exp(k(-1))) ++ BETA*exp(c(1))^(-GAMMAC)*((1-DELTAK)-2*INEG*PSI*(exp(k(1))/exp(k)-1)* + (-exp(k(1))/exp(k)^2)+ALPHA*exp(a(1))*exp(k)^(ALPHA-1))= + -lambdak+BETA*(1-DELTAK)*lambdak(1); + +// 2. +exp(c)+exp(k)-(1-DELTAK)*exp(k(-1))+ +INEG*PSI*(exp(k)/exp(k(-1))-1)^2=exp(a)*exp(k(-1))^(ALPHA); + +// 3. +[pswitch = 'INEG', +// bind = 'exp(i+i_ss)<-0.000001', +// relax = 'exp(i+i_ss)>-0.000001' ] + bind = 'i<-b', + relax = 'i>-0.000001' ] +exp(i) = exp(k)-(1-DELTAK)*exp(k(-1)); + +// 4. +[pswitch = 'IRR', + bind = 'i