From 5fa6265944426ee5c50531eb9d59c2c11fbfe707 Mon Sep 17 00:00:00 2001 From: Marco Ratto <marco.ratto@ec.europa.eu> Date: Tue, 4 May 2021 09:01:27 +0200 Subject: [PATCH] Implement new option smoother_redux, to allow fast smoother for very large models. It runs smoother only for state variables and computes the remaining ones ex-post Contains improvements, in order to recover as much as possible static unobserved (filtered, smoothed, updated, k-step ahead), Variance, State_uncertainty, k-step ahead variances trying to map lagged states onto current ones using pinv(T). This has exceptions (namely lagged shocks which are ONLY used to recover static NON observed variables). this exception is also trapped. For such extensions we can only recover smoothed variables starting from d+1. Variances CANNOT be recovered for such variables (the smoother gives ZERO.) --- doc/manual/source/the-model-file.rst | 32 +++ matlab/DsgeSmoother.m | 159 +++++++++++-- matlab/default_option_values.m | 1 + matlab/missing_DiffuseKalmanSmootherH1_Z.m | 131 +++++++--- matlab/missing_DiffuseKalmanSmootherH3_Z.m | 105 ++++++-- preprocessor | 2 +- tests/Makefile.am | 1 + .../fs2000_smoother_redux.mod | 225 ++++++++++++++++++ 8 files changed, 584 insertions(+), 72 deletions(-) create mode 100644 tests/kalman_filter_smoother/fs2000_smoother_redux.mod diff --git a/doc/manual/source/the-model-file.rst b/doc/manual/source/the-model-file.rst index 970fc5b20e..9bd4a1c63b 100644 --- a/doc/manual/source/the-model-file.rst +++ b/doc/manual/source/the-model-file.rst @@ -6330,6 +6330,26 @@ block decomposition of the model (see :opt:`block`). (:math:`E_{t}{y_t}`). See below for a description of all these variables. + .. option:: smoother_redux + + Triggers a faster computation of the smoothed endogenous variables and shocks for large models. + It runs the smoother only for the state variables (i.e. with the same representation used for + likelihood computations) and computes the remaining variables ex-post. + Static unobserved objects (filtered, smoothed, updated, k-step ahead) are recovered, but there are + exceptions to a full recovery, depending on how static unobserved variables depend on the restricted + state space adopted. For example, lagged shocks which are ONLY used to recover NON-observed static + variables will not be recovered). + For such exceptions, only the following output is provided: + + ``FilteredVariablesKStepAhead``: will be fully recovered + + ``SmoothedVariables``, ``FilteredVariables``, ``UpdatedVariables``: recovered for all periods beyond period ``d+1``, + where ``d`` denotes the number of diffuse filtering steps. + + ``FilteredVariablesKStepAheadVariances``, ``Variance``, and ``State_uncertainty`` cannot be recovered, and ZERO is provided as output. + + If you need variances for those variables, either do not set the option, or declare the variable as observed, using NaNs as data points. + .. option:: forecast = INTEGER Computes the posterior distribution of a forecast on INTEGER @@ -8417,6 +8437,18 @@ Dynare can also run the smoother on a calibrated model: See :opt:`filter_decomposition`. + .. option:: filter_covariance + + See :opt:`filter_covariance`. + + .. option:: smoother_redux + + See :opt:`smoother_redux`. + + .. option:: kalman_algo = INTEGER + + See :opt:`kalman_algo <kalman_algo = INTEGER>`. + .. option:: diffuse_filter = INTEGER See :opt:`diffuse_filter`. diff --git a/matlab/DsgeSmoother.m b/matlab/DsgeSmoother.m index 112c1b3f62..8dbee6f4df 100644 --- a/matlab/DsgeSmoother.m +++ b/matlab/DsgeSmoother.m @@ -97,22 +97,30 @@ end %------------------------------------------------------------------------------ % 2. call model setup & reduction program %------------------------------------------------------------------------------ -%store old setting of restricted var_list -oldoo.restrict_var_list = oo_.dr.restrict_var_list; -oldoo.restrict_columns = oo_.dr.restrict_columns; -oo_.dr.restrict_var_list = bayestopt_.smoother_var_list; -oo_.dr.restrict_columns = bayestopt_.smoother_restrict_columns; - -[T,R,SteadyState,info,M_,options_,oo_] = dynare_resolve(M_,options_,oo_); +if ~options_.smoother_redux + + %store old setting of restricted var_list + oldoo.restrict_var_list = oo_.dr.restrict_var_list; + oldoo.restrict_columns = oo_.dr.restrict_columns; + oo_.dr.restrict_var_list = bayestopt_.smoother_var_list; + oo_.dr.restrict_columns = bayestopt_.smoother_restrict_columns; + + [T,R,SteadyState,info,M_,options_,oo_] = dynare_resolve(M_,options_,oo_); + + %get location of observed variables and requested smoothed variables in + %decision rules + bayestopt_.mf = bayestopt_.smoother_var_list(bayestopt_.smoother_mf); + +else + [T,R,SteadyState,info,M_,options_,oo_] = dynare_resolve(M_,options_,oo_,'restrict'); + bayestopt_.mf = bayestopt_.mf1; +end if info~=0 print_info(info,options_.noprint, options_); return end -%get location of observed variables and requested smoothed variables in -%decision rules -bayestopt_.mf = bayestopt_.smoother_var_list(bayestopt_.smoother_mf); if options_.noconstant constant = zeros(vobs,1); else @@ -233,10 +241,10 @@ 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; - [alphahat,epsilonhat,etahat,ahat,P,aK,PK,decomp,state_uncertainty] = missing_DiffuseKalmanSmootherH1_Z(a_initial,ST, ... + [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_.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') @@ -289,14 +297,14 @@ if kalman_algo == 2 || kalman_algo == 4 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] = missing_DiffuseKalmanSmootherH3_Z(a_initial,ST, ... + [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_decomposition,options_.smoothed_state_uncertainty,options_.filter_covariance,options_.smoother_redux); + end - if expanded_state_vector_for_univariate_filter && (kalman_algo == 2 || kalman_algo == 4) % extracting measurement errors % removing observed variables from the state vector @@ -320,10 +328,122 @@ if expanded_state_vector_for_univariate_filter && (kalman_algo == 2 || kalman_al end end -%reset old setting of restricted var_list -oo_.dr.restrict_var_list = oldoo.restrict_var_list; -oo_.dr.restrict_columns = oldoo.restrict_columns; - +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; +else + if options_.block == 0 + ic = [ M_.nstatic+(1:M_.nspred) M_.endo_nbr+(1:size(oo_.dr.ghx,2)-M_.nspred) ]'; + 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) + 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) + 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; + 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)); + 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))); + end + end + PK=PP; + clear('PP'); + end + + bayestopt_.mf = bayestopt_.smoother_var_list(bayestopt_.smoother_mf); +end function a=set_Kalman_smoother_starting_values(a,M_,oo_,options_) % function a=set_Kalman_smoother_starting_values(a,M_,oo_,options_) @@ -352,3 +472,4 @@ if isfield(M_,'filter_initial_state') && ~isempty(M_.filter_initial_state) end end end + diff --git a/matlab/default_option_values.m b/matlab/default_option_values.m index 86ef05dd51..195622de22 100644 --- a/matlab/default_option_values.m +++ b/matlab/default_option_values.m @@ -428,6 +428,7 @@ options_.prefilter = 0; options_.presample = 0; options_.prior_trunc = 1e-10; options_.smoother = false; +options_.smoother_redux = false; options_.posterior_max_subsample_draws = 1200; options_.sub_draws = []; options_.ME_plot_tol=1e-6; diff --git a/matlab/missing_DiffuseKalmanSmootherH1_Z.m b/matlab/missing_DiffuseKalmanSmootherH1_Z.m index cda2b91233..e6c9335ee3 100644 --- a/matlab/missing_DiffuseKalmanSmootherH1_Z.m +++ b/matlab/missing_DiffuseKalmanSmootherH1_Z.m @@ -1,6 +1,6 @@ -function [alphahat,epsilonhat,etahat,atilde,P,aK,PK,decomp,V] = missing_DiffuseKalmanSmootherH1_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) +function [alphahat,epsilonhat,etahat,atilde,P,aK,PK,decomp,V,aalphahat,eetahat,d] = missing_DiffuseKalmanSmootherH1_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,aK,PK,decomp] = DiffuseKalmanSmoother1(T,Z,R,Q,H,Pinf1,Pstar1,Y,pp,mm,smpl,data_index,nk,kalman_tol,diffuse_kalman_tol,decomp_flag,state_uncertainty_flag) +% function [alphahat,epsilonhat,etahat,atilde,P,aK,PK,decomp,V,aalphahat,eetahat,d] = missing_DiffuseKalmanSmootherH1_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) % Computes the diffuse kalman smoother without measurement error, in the case of a non-singular var-cov matrix. % % INPUTS @@ -16,27 +16,34 @@ function [alphahat,epsilonhat,etahat,atilde,P,aK,PK,decomp,V] = missing_DiffuseK % pp: number of observed variables % mm: number of state variables % smpl: sample size -% data_index [cell] 1*smpl cell of column vectors of indices. -% nk number of forecasting periods -% kalman_tol tolerance for reciprocal condition number -% diffuse_kalman_tol tolerance for reciprocal condition number (for Finf) and the rank of Pinf -% decomp_flag if true, compute filter decomposition -% state_uncertainty_flag if true, compute uncertainty about smoothed +% data_index: [cell] 1*smpl cell of column vectors of indices. +% nk: number of forecasting periods +% kalman_tol: tolerance for reciprocal condition number +% diffuse_kalman_tol: tolerance for reciprocal condition number (for Finf) and the rank of Pinf +% decomp_flag: if true, compute filter decomposition +% state_uncertainty_flag: if true, compute uncertainty about smoothed % state estimate +% decomp_flag: if true, compute filter decomposition +% filter_covariance_flag: if true, compute filter covariance +% smoother_redux: if true, compute smoother on restricted +% state space, recover static variables from this % % OUTPUTS -% alphahat: smoothed variables (a_{t|T}) -% epsilonhat:smoothed measurement errors -% etahat: smoothed shocks -% atilde: matrix of updated variables (a_{t|t}) -% aK: 3D array of k step ahead filtered state variables (a_{t+k|t) -% (meaningless for periods 1:d) -% P: 3D array of one-step ahead forecast error variance -% matrices -% PK: 4D array of k-step ahead forecast error variance -% matrices (meaningless for periods 1:d) -% decomp: decomposition of the effect of shocks on filtered values -% V: 3D array of state uncertainty matrices +% alphahat: smoothed variables (a_{t|T}) +% epsilonhat: smoothed measurement errors +% etahat: smoothed shocks +% atilde: matrix of updated variables (a_{t|t}) +% P: 3D array of one-step ahead forecast error variance +% matrices +% aK: 3D array of k step ahead filtered state variables (a_{t+k|t) +% (meaningless for periods 1:d) +% PK: 4D array of k-step ahead forecast error variance +% matrices (meaningless for periods 1:d) +% decomp: decomposition of the effect of shocks on filtered values +% V: 3D array of state uncertainty matrices +% aalphahat: filtered states in t-1|t +% eetahat: updated shocks in t|t +% d: number of diffuse periods % % Notes: % Outputs are stored in decision-rule order, i.e. to get variables in order of declaration @@ -50,7 +57,7 @@ function [alphahat,epsilonhat,etahat,atilde,P,aK,PK,decomp,V] = missing_DiffuseK % Durbin/Koopman (2012): "Time Series Analysis by State Space Methods", Oxford University Press, % Second Edition, Ch. 5 -% Copyright (C) 2004-2018 Dynare Team +% Copyright (C) 2004-2021 Dynare Team % % This file is part of Dynare. % @@ -82,7 +89,11 @@ a = zeros(mm,smpl+1); a(:,1) = a_initial; atilde = zeros(mm,smpl); aK = zeros(nk,mm,smpl+nk); -PK = zeros(nk,mm,mm,smpl+nk); +if filter_covariance_flag + PK = zeros(nk,mm,mm,smpl+nk); +else + PK = []; +end iF = zeros(pp,pp,smpl); Fstar = zeros(pp,pp,smpl); iFstar = zeros(pp,pp,smpl); @@ -109,11 +120,22 @@ QQ = R*Q*transpose(R); QRt = Q*transpose(R); alphahat = zeros(mm,smpl); etahat = zeros(rr,smpl); +if smoother_redux + aalphahat = alphahat; + eetahat = etahat; +else + aalphahat = []; + eetahat = []; +end epsilonhat = zeros(rr,smpl); r = zeros(mm,smpl+1); Finf_singular = zeros(1,smpl); if state_uncertainty_flag - V = zeros(mm,mm,smpl); + if smoother_redux + V = zeros(mm+rr,mm+rr,smpl); + else + V = zeros(mm,mm,smpl); + end N = zeros(mm,mm,smpl+1); else V=[]; @@ -222,20 +244,45 @@ while notsteady && t<smpl L(:,:,t) = T-K(:,di,t)*ZZ; P(:,:,t+1) = T*P(:,:,t)*L(:,:,t)' + QQ; end + if smoother_redux + ri=zeros(mm,1); + for st=t:-1:max(d+1,t-1) + di = data_index{st}; + if isempty(di) + % in this case, L is simply T due to Z=0, so that DK (2012), eq. 4.93 obtains + ri = L(:,:,t)'*ri; %compute r_{t-1}, DK (2012), eq. 4.38 with Z=0 + else + ZZ = Z(di,:); + ri = ZZ'*iF(di,di,st)*v(di,st) + L(:,:,st)'*ri; %compute r_{t-1}, DK (2012), eq. 4.38 + end + if st==t-1 + % get states in t-1|t + aalphahat(:,st) = a(:,st) + P(:,:,st)*ri; %DK (2012), eq. 4.35 + else + % get shocks in t|t + eetahat(:,st) = QRt*ri; %DK (2012), eq. 4.63 + end + end + end a(:,t+1) = T*atilde(:,t); - Pf = P(:,:,t+1); + if filter_covariance_flag + Pf = P(:,:,t+1); + end aK(1,:,t+1) = a(:,t+1); for jnk=1:nk - if jnk>1 - Pf = T*Pf*T' + QQ; + if filter_covariance_flag + if jnk>1 + Pf = T*Pf*T' + QQ; + end + PK(jnk,:,:,t+jnk) = Pf; end - PK(jnk,:,:,t+jnk) = Pf; if jnk>1 aK(jnk,:,t+jnk) = T*dynare_squeeze(aK(jnk-1,:,t+jnk-1)); end end % notsteady = ~(max(max(abs(P(:,:,t+1)-P(:,:,t))))<kalman_tol); end + % $$$ if t<smpl % $$$ PZI_s = PZI; % $$$ K_s = K(:,:,t); @@ -282,9 +329,16 @@ while t>d+1 end etahat(:,t) = QRt*r(:,t); %DK (2012), eq. 4.63 if state_uncertainty_flag - V(:,:,t) = P(:,:,t)-P(:,:,t)*N(:,:,t)*P(:,:,t); %DK (2012), eq. 4.43 + if smoother_redux + ptmp = [P(:,:,t) R*Q; (R*Q)' Q]; + ntmp = [N(:,:,t) zeros(mm,rr); zeros(rr,mm+rr)]; + V(:,:,t) = ptmp - ptmp*ntmp*ptmp; + else + V(:,:,t) = P(:,:,t)-P(:,:,t)*N(:,:,t)*P(:,:,t); %DK (2012), eq. 4.43 + end end end + if d %diffuse periods % initialize r_d^(0) and r_d^(1) as below DK (2012), eq. 5.23 r0 = zeros(mm,d+1); @@ -332,10 +386,23 @@ if d %diffuse periods end etahat(:,t) = QRt*r0(:,t); % DK (2012), p. 135 if state_uncertainty_flag - V(:,:,t)=Pstar(:,:,t)-Pstar(:,:,t)*N(:,:,t)*Pstar(:,:,t)... - -(Pinf(:,:,t)*N_1(:,:,t)*Pstar(:,:,t))'... - - Pinf(:,:,t)*N_1(:,:,t)*Pstar(:,:,t)... - - Pinf(:,:,t)*N_2(:,:,t)*Pinf(:,:,t); % DK (2012), eq. 5.30 + if smoother_redux + pstmp = [Pstar(:,:,t) R*Q; (R*Q)' Q]; + pitmp = [Pinf(:,:,t) zeros(mm,rr); zeros(rr,mm+rr)]; + ntmp = [N(:,:,t) zeros(mm,rr); zeros(rr,mm+rr)]; + ntmp1 = [N_1(:,:,t) zeros(mm,rr); zeros(rr,mm+rr)]; + ntmp2 = [N_2(:,:,t) zeros(mm,rr); zeros(rr,mm+rr)]; + V(:,:,t) = pstmp - pstmp*ntmp*pstmp... + -(pitmp*ntmp1*pstmp)'... + - pitmp*ntmp1*pstmp... + - pitmp*ntmp2*Pinf(:,:,t); % DK (2012), eq. 5.30 + + else + V(:,:,t)=Pstar(:,:,t)-Pstar(:,:,t)*N(:,:,t)*Pstar(:,:,t)... + -(Pinf(:,:,t)*N_1(:,:,t)*Pstar(:,:,t))'... + - Pinf(:,:,t)*N_1(:,:,t)*Pstar(:,:,t)... + - Pinf(:,:,t)*N_2(:,:,t)*Pinf(:,:,t); % DK (2012), eq. 5.30 + end end end end diff --git a/matlab/missing_DiffuseKalmanSmootherH3_Z.m b/matlab/missing_DiffuseKalmanSmootherH3_Z.m index b0b9def45c..0765eacca7 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] = 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) -% function [alphahat,epsilonhat,etahat,a1,P1,aK,PK,d,decomp] = missing_DiffuseKalmanSmootherH3_Z(T,Z,R,Q,H,Pinf1,Pstar1,Y,pp,mm,smpl,data_index,nk,kalman_tol,decomp_flag,state_uncertainty_flag) +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) % Computes the diffuse kalman smoother in the case of a singular var-cov matrix. % Univariate treatment of multivariate time series. % @@ -16,13 +16,17 @@ function [alphahat,epsilonhat,etahat,a,P1,aK,PK,decomp,V] = missing_DiffuseKalma % pp: number of observed variables % mm: number of state variables % smpl: sample size -% data_index [cell] 1*smpl cell of column vectors of indices. -% nk number of forecasting periods -% kalman_tol tolerance for zero divider -% diffuse_kalman_tol tolerance for zero divider -% decomp_flag if true, compute filter decomposition -% state_uncertainty_flag if true, compute uncertainty about smoothed +% data_index: [cell] 1*smpl cell of column vectors of indices. +% nk: number of forecasting periods +% kalman_tol: tolerance for zero divider +% diffuse_kalman_tol: tolerance for zero divider +% decomp_flag: if true, compute filter decomposition +% state_uncertainty_flag: if true, compute uncertainty about smoothed % state estimate +% decomp_flag: if true, compute filter decomposition +% filter_covariance_flag: if true, compute filter covariance +% smoother_redux: if true, compute smoother on restricted +% state space, recover static variables from this % % OUTPUTS % alphahat: smoothed state variables (a_{t|T}) @@ -37,6 +41,9 @@ function [alphahat,epsilonhat,etahat,a,P1,aK,PK,decomp,V] = missing_DiffuseKalma % matrices (meaningless for periods 1:d) % decomp: decomposition of the effect of shocks on filtered values % V: 3D array of state uncertainty matrices +% aalphahat: filtered states in t-1|t +% eetahat: updated shocks in t|t +% d: number of diffuse periods % % Notes: % Outputs are stored in decision-rule order, i.e. to get variables in order of declaration @@ -78,7 +85,6 @@ function [alphahat,epsilonhat,etahat,a,P1,aK,PK,decomp,V] = missing_DiffuseKalma % New output argument aK: 1-step to nk-stpe ahed predictions) % New input argument nk: max order of predictions in aK - if size(H,2)>1 error('missing_DiffuseKalmanSmootherH3_Z:: H is not a vector. This must not happens') end @@ -102,7 +108,11 @@ Kstar = zeros(mm,pp,smpl); Kinf = zeros(spstar(1),pp,smpl); P = zeros(mm,mm,smpl+1); P1 = P; -PK = zeros(nk,mm,mm,smpl+nk); +if filter_covariance_flag + PK = zeros(nk,mm,mm,smpl+nk); +else + PK = []; +end Pstar = zeros(spstar(1),spstar(2),smpl); Pstar(:,:,1) = Pstar1; Pinf = zeros(spinf(1),spinf(2),smpl); @@ -120,10 +130,21 @@ QQ = R*Q*transpose(R); QRt = Q*transpose(R); alphahat = zeros(mm,smpl); etahat = zeros(rr,smpl); +if smoother_redux + aalphahat = alphahat; + eetahat = etahat; +else + aalphahat = []; + eetahat = []; +end epsilonhat = zeros(rr,smpl); r = zeros(mm,smpl); if state_uncertainty_flag - V = zeros(mm,mm,smpl); + if smoother_redux + V = zeros(mm+rr,mm+rr,smpl); + else + V = zeros(mm,mm,smpl); + end N = zeros(mm,mm,smpl); else V=[]; @@ -229,18 +250,40 @@ while notsteady && t<smpl % p. 157, DK (2012) 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; + else + eetahat(:,st) = QRt*ri; + 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) - Pf = P(:,:,t+1); + if filter_covariance_flag + Pf = P(:,:,t+1); + end aK(1,:,t+1) = a1(:,t+1); for jnk=1:nk - if jnk>1 - Pf = T*Pf*T' + QQ; + if filter_covariance_flag + if jnk>1 + Pf = T*Pf*T' + QQ; + end + PK(jnk,:,:,t+jnk) = Pf; end - PK(jnk,:,:,t+jnk) = Pf; if jnk>1 aK(jnk,:,t+jnk) = T*dynare_squeeze(aK(jnk-1,:,t+jnk-1)); end @@ -248,6 +291,9 @@ while notsteady && t<smpl % notsteady = ~(max(max(abs(P(:,:,t+1)-P(:,:,t))))<kalman_tol); end P1(:,:,t+1) = P(:,:,t+1); + +P1(:,:,t+1)=P(:,:,t+1); + % $$$ P_s=tril(P(:,:,t))+tril(P(:,:,t),-1)'; % $$$ P1_s=tril(P1(:,:,t))+tril(P1(:,:,t),-1)'; % $$$ Fi_s = Fi(:,t); @@ -307,7 +353,13 @@ while t > d+1 ri = T'*ri; % KD (2003), eq. (23), equation for r_{t-1,p_{t-1}} if state_uncertainty_flag N(:,:,t) = Ni; % DK (2012), below 6.15, N_{t-1}=N_{t,0} - V(:,:,t) = P1(:,:,t)-P1(:,:,t)*N(:,:,t)*P1(:,:,t); % KD (2000), eq. (7) with N_{t-1} stored in N(:,:,t) + if smoother_redux + ptmp = [P1(:,:,t) R*Q; (R*Q)' Q]; + ntmp = [N(:,:,t) zeros(mm,rr); zeros(rr,mm+rr)]; + V(:,:,t) = ptmp - ptmp*ntmp*ptmp; + else + V(:,:,t) = P1(:,:,t)-P1(:,:,t)*N(:,:,t)*P1(:,:,t); % KD (2000), eq. (7) with N_{t-1} stored in N(:,:,t) + end Ni = T'*Ni*T; % KD (2000), eq. (23), equation for N_{t-1,p_{t-1}} end end @@ -359,10 +411,23 @@ if d end etahat(:,t) = QRt*r(:,t); % KD (2000), eq. (27) if state_uncertainty_flag - V(:,:,t)=Pstar(:,:,t)-Pstar(:,:,t)*N_0(:,:,t)*Pstar(:,:,t)... - -(Pinf(:,:,t)*N_1(:,:,t)*Pstar(:,:,t))'... - - Pinf(:,:,t)*N_1(:,:,t)*Pstar(:,:,t)... - - Pinf(:,:,t)*N_2(:,:,t)*Pinf(:,:,t); % DK (2012), eq. 5.30 + if smoother_redux + pstmp = [Pstar(:,:,t) R*Q; (R*Q)' Q]; + pitmp = [Pinf(:,:,t) zeros(mm,rr); zeros(rr,mm+rr)]; + ntmp0 = [N_0(:,:,t) zeros(mm,rr); zeros(rr,mm+rr)]; + ntmp1 = [N_1(:,:,t) zeros(mm,rr); zeros(rr,mm+rr)]; + ntmp2 = [N_2(:,:,t) zeros(mm,rr); zeros(rr,mm+rr)]; + V(:,:,t) = pstmp - pstmp*ntmp0*pstmp... + -(pitmp*ntmp1*pstmp)'... + - pitmp*ntmp1*pstmp... + - pitmp*ntmp2*Pinf(:,:,t); % DK (2012), eq. 5.30 + + else + V(:,:,t)=Pstar(:,:,t)-Pstar(:,:,t)*N_0(:,:,t)*Pstar(:,:,t)... + -(Pinf(:,:,t)*N_1(:,:,t)*Pstar(:,:,t))'... + - Pinf(:,:,t)*N_1(:,:,t)*Pstar(:,:,t)... + - Pinf(:,:,t)*N_2(:,:,t)*Pinf(:,:,t); % DK (2012), eq. 5.30 + end end if t > 1 r0(:,t-1) = T'*r0(:,t); % KD (2000), below eq. (25) r_{t-1,p_{t-1}}=T'*r_{t,0} diff --git a/preprocessor b/preprocessor index b12b254f2c..1e9ea1acf4 160000 --- a/preprocessor +++ b/preprocessor @@ -1 +1 @@ -Subproject commit b12b254f2c90c92ba8218d07a9860762d41be0fd +Subproject commit 1e9ea1acf4e7447f3d336b5c7d571c9a51699723 diff --git a/tests/Makefile.am b/tests/Makefile.am index b02809da4a..e76ce7fc1c 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -297,6 +297,7 @@ MODFILES = \ kalman_filter_smoother/fs2000a.mod \ kalman_filter_smoother/fs2000_smoother_only.mod \ kalman_filter_smoother/fs2000_smoother_only_ns.mod \ + kalman_filter_smoother/fs2000_smoother_redux.mod \ kalman_filter_smoother/test_compute_Pinf_Pstar_data.mod \ kalman_filter_smoother/test_compute_Pinf_Pstar.mod \ kalman_filter_smoother/SOE.mod \ diff --git a/tests/kalman_filter_smoother/fs2000_smoother_redux.mod b/tests/kalman_filter_smoother/fs2000_smoother_redux.mod new file mode 100644 index 0000000000..72d261a59a --- /dev/null +++ b/tests/kalman_filter_smoother/fs2000_smoother_redux.mod @@ -0,0 +1,225 @@ +// See fs2000.mod in the examples/ directory for details on the model + +var m P c e W R k d n l gy_obs gp_obs y dA; +varexo e_a e_m e_b; + +parameters alp bet gam mst rho psi del; + +alp = 0.33; +bet = 0.99; +gam = 0.003; +mst = 1.011; +rho = 0.7; +psi = 0.787; +del = 0.02; + +model; +dA = exp(gam+e_a); +log(m) = (1-rho)*log(mst) + rho*log(m(-1))+e_m; +-P/(c(+1)*P(+1)*m)+bet*exp(e_b)*P(+1)*(alp*exp(-alp*(gam+log(e(+1))))*k^(alp-1)*n(+1)^(1-alp)+(1-del)*exp(-(gam+log(e(+1)))))/(c(+2)*P(+2)*m(+1))=0; +W = l/n; +-(psi/(1-psi))*(c*P/(1-n))+l/n = 0; +R = P*(1-alp)*exp(-alp*(gam+e_a))*k(-1)^alp*n^(-alp)/W; +1/(c*P)-bet*exp(e_b)*P*(1-alp)*exp(-alp*(gam+e_a))*k(-1)^alp*n^(1-alp)/(m*l*c(+1)*P(+1)) = 0; +c+k = exp(-alp*(gam+e_a))*k(-1)^alp*n^(1-alp)+(1-del)*exp(-(gam+e_a))*k(-1); +P*c = m; +m-1+d = l; +e = exp(e_a); +y = k(-1)^alp*n^(1-alp)*exp(-alp*(gam+e_a)); +gy_obs = dA*y/y(-1); +gp_obs = (P/P(-1))*m(-1)/dA; +end; + +steady_state_model; + dA = exp(gam); + gst = 1/dA; + m = mst; + khst = ( (1-gst*bet*(1-del)) / (alp*gst^alp*bet) )^(1/(alp-1)); + xist = ( ((khst*gst)^alp - (1-gst*(1-del))*khst)/mst )^(-1); + nust = psi*mst^2/( (1-alp)*(1-psi)*bet*gst^alp*khst^alp ); + n = xist/(nust+xist); + P = xist + nust; + k = khst*n; + + l = psi*mst*n/( (1-psi)*(1-n) ); + c = mst/P; + d = l - mst + 1; + y = k^alp*n^(1-alp)*gst^alp; + R = mst/bet; + W = l/n; + ist = y-c; + q = 1 - d; + + e = 1; + + gp_obs = m/dA; + gy_obs = dA; +end; + +write_latex_dynamic_model; + +shocks; +var e_a; stderr 0.014; +var e_b; stderr 0.1; +var e_m; stderr 0.005; +end; + +steady; + +check; + + +varobs gp_obs gy_obs; + +calib_smoother(datafile=fsdat_simul, filtered_vars, filter_step_ahead = [3:4],filter_covariance,smoothed_state_uncertainty) m P c e W R k d n l y dA; +oo0=oo_; + +calib_smoother(datafile=fsdat_simul, filtered_vars, filter_step_ahead = [3:4],filter_covariance,smoothed_state_uncertainty,smoother_redux) m P c e W R k d n l y dA; +oo1=oo_; + +calib_smoother(datafile=fsdat_simul, filtered_vars, filter_step_ahead = [3:4],filter_covariance,smoothed_state_uncertainty,kalman_algo=2,smoother_redux) m P c e W R k d n l y dA; +oo2=oo_; + + +for k=1:M_.exo_nbr + mserr(k)=max(abs(oo0.SmoothedShocks.(M_.exo_names{k})-oo1.SmoothedShocks.(M_.exo_names{k}))); +end +if max(mserr)>1.e-12 + error('smoother_redux with kalman_algo=1 does not replicate original smoother for shocks!') +end + +vlist = M_.endo_names(oo_.dr.order_var(oo_.dr.restrict_var_list)); +for k=1:length(vlist) + merr(k)=max(abs(oo0.SmoothedVariables.(vlist{k})-oo1.SmoothedVariables.(vlist{k}))); + merrU(k)=max(abs(oo0.UpdatedVariables.(vlist{k})-oo1.UpdatedVariables.(vlist{k}))); + merrF(k)=max(abs(oo0.FilteredVariables.(vlist{k})-oo1.FilteredVariables.(vlist{k}))); + +end +if max(merr)>1.e-12 + error('smoother_redux with kalman_algo=1 does not replicate original smoothed restricted var list!') +end +if max(merrU)>1.e-12 + error('smoother_redux with kalman_algo=1 does not replicate original updated restricted var list!') +end +if max(merrF)>1.e-12 + error('smoother_redux with kalman_algo=1 does not replicate original filtered restricted var list!') +end + +vlist1 = M_.endo_names(~ismember(M_.endo_names,vlist)); +for k=1:length(vlist1) + merr1(k)=max(abs(oo0.SmoothedVariables.(vlist1{k})-oo1.SmoothedVariables.(vlist1{k}))); + merr1U(k)=max(abs(oo0.UpdatedVariables.(vlist1{k})-oo1.UpdatedVariables.(vlist1{k}))); + merr1F(k)=max(abs(oo0.FilteredVariables.(vlist1{k})-oo1.FilteredVariables.(vlist1{k}))); +end +if max(merr1)>1.e-12 + for k=1:length(vlist1) + merr2(k)=max(abs(oo0.SmoothedVariables.(vlist1{k})(2:end)-oo1.SmoothedVariables.(vlist1{k})(2:end))); + end + if max(merr2)>1.e-12 + error('smoother_redux with kalman_algo=1 does not replicate original smoothed static variables!') + end +end +if max(merr1U)>1.e-12 + for k=1:length(vlist1) + merr2U(k)=max(abs(oo0.UpdatedVariables.(vlist1{k})(2:end)-oo1.UpdatedVariables.(vlist1{k})(2:end))); + end + if max(merr2U)>1.e-12 + error('smoother_redux with kalman_algo=1 does not replicate original updated static variables!') + end +end +if max(merr1F)>1.e-12 + for k=1:length(vlist1) + merr2F(k)=max(abs(oo0.FilteredVariables.(vlist1{k})(2:end)-oo1.FilteredVariables.(vlist1{k})(2:end))); + end + if max(merr2F)>1.e-12 + error('smoother_redux with kalman_algo=1 does not replicate original filtered static variables!') + end +end +merrK = max(max(max(abs(oo0.FilteredVariablesKStepAhead-oo1.FilteredVariablesKStepAhead)))); +if max(merrK)>1.e-12 + error('smoother_redux with kalman_algo=1 does not replicate original k-step ahead forecasts!') +end +verrK = max(max(max(max(abs(oo0.FilteredVariablesKStepAheadVariances(:,[1:14 16],[1:14 16],:)-oo1.FilteredVariablesKStepAheadVariances(:,[1:14 16],[1:14 16],:)))))); +if verrK>1.e-12 + error('smoother_redux with kalman_algo=1 does not replicate original k-step ahead forecast variances!') +end +verr=max(max(max(abs(oo0.Smoother.Variance([1:14 16],[1:14 16],:)-oo1.Smoother.Variance([1:14 16],[1:14 16],:))))); +if verr>1.e-12 + error('smoother_redux with kalman_algo=1 does not replicate original filter covariance!') +end +verrS=max(max(max(abs(oo0.Smoother.State_uncertainty([1:14 16],[1:14 16],:)-oo1.Smoother.State_uncertainty([1:14 16],[1:14 16],:))))); +if verrS>1.e-12 + error('smoother_redux with kalman_algo=1 does not replicate original state covariance!') +end + +// now I check kalman_algo=2 +for k=1:M_.exo_nbr + mserr(k)=max(abs(oo0.SmoothedShocks.(M_.exo_names{k})-oo2.SmoothedShocks.(M_.exo_names{k}))); +end +if max(mserr)>1.e-12 + error('smoother_redux with kalman_algo=2 does not replicate original smoother for shocks!') +end + +vlist = M_.endo_names(oo_.dr.order_var(oo_.dr.restrict_var_list)); +for k=1:length(vlist) + merr(k)=max(abs(oo0.SmoothedVariables.(vlist{k})-oo2.SmoothedVariables.(vlist{k}))); + merrU(k)=max(abs(oo0.UpdatedVariables.(vlist{k})-oo2.UpdatedVariables.(vlist{k}))); + merrF(k)=max(abs(oo0.FilteredVariables.(vlist{k})-oo2.FilteredVariables.(vlist{k}))); + +end +if max(merr)>1.e-12 + error('smoother_redux with kalman_algo=2 does not replicate original smoothed restricted var list!') +end +if max(merrU)>1.e-12 + error('smoother_redux with kalman_algo=2 does not replicate original updated restricted var list!') +end +if max(merrF)>1.e-12 + error('smoother_redux with kalman_algo=2 does not replicate original filtered restricted var list!') +end + +vlist1 = M_.endo_names(~ismember(M_.endo_names,vlist)); +for k=1:length(vlist1) + merr1(k)=max(abs(oo0.SmoothedVariables.(vlist1{k})-oo2.SmoothedVariables.(vlist1{k}))); + merr1U(k)=max(abs(oo0.UpdatedVariables.(vlist1{k})-oo2.UpdatedVariables.(vlist1{k}))); + merr1F(k)=max(abs(oo0.FilteredVariables.(vlist1{k})-oo2.FilteredVariables.(vlist1{k}))); +end +if max(merr1)>1.e-12 + for k=1:length(vlist1) + merr2(k)=max(abs(oo0.SmoothedVariables.(vlist1{k})(2:end)-oo2.SmoothedVariables.(vlist1{k})(2:end))); + end + if max(merr2)>1.e-12 + error('smoother_redux with kalman_algo=2 does not replicate original smoothed static variables!') + end +end +if max(merr1U)>1.e-12 + for k=1:length(vlist1) + merr2U(k)=max(abs(oo0.UpdatedVariables.(vlist1{k})(2:end)-oo2.UpdatedVariables.(vlist1{k})(2:end))); + end + if max(merr2U)>1.e-12 + error('smoother_redux with kalman_algo=2 does not replicate original updated static variables!') + end +end +if max(merr1F)>1.e-12 + for k=1:length(vlist1) + merr2F(k)=max(abs(oo0.FilteredVariables.(vlist1{k})(2:end)-oo2.FilteredVariables.(vlist1{k})(2:end))); + end + if max(merr2F)>1.e-12 + error('smoother_redux with kalman_algo=2 does not replicate original filtered static variables!') + end +end +merrK = max(max(max(abs(oo0.FilteredVariablesKStepAhead-oo2.FilteredVariablesKStepAhead)))); +if max(merrK)>1.e-12 + error('smoother_redux with kalman_algo=2 does not replicate original k-step ahead forecasts!') +end +verrK = max(max(max(max(abs(oo0.FilteredVariablesKStepAheadVariances(:,[1:14 16],[1:14 16],:)-oo2.FilteredVariablesKStepAheadVariances(:,[1:14 16],[1:14 16],:)))))); +if verrK>1.e-12 + error('smoother_redux with kalman_algo=2 does not replicate original k-step ahead forecast variances!') +end +verr=max(max(max(abs(oo0.Smoother.Variance([1:14 16],[1:14 16],:)-oo2.Smoother.Variance([1:14 16],[1:14 16],:))))); +if verr>1.e-12 + error('smoother_redux with kalman_algo=2 does not replicate original filter covariance!') +end +verrS=max(max(max(abs(oo0.Smoother.State_uncertainty([1:14 16],[1:14 16],:)-oo2.Smoother.State_uncertainty([1:14 16],[1:14 16],:))))); +if verrS>1.e-12 + error('smoother_redux with kalman_algo=2 does not replicate original state covariance!') +end -- GitLab