🐛 Fix wrong third-order computation in pruned state space system

Related to #1706

 Add unit test for pruned state space up to order 3


//


Changed tolerance in new test
time-shift
Willi Mutschler 2020-02-14 13:56:36 +01:00
parent f69cd14983
commit f4dc2ee743
No known key found for this signature in database
GPG Key ID: 91E724BF17A73F6D
15 changed files with 1656 additions and 996 deletions

View File

@ -1,33 +1,54 @@
% By Willi Mutschler, September 26, 2016. Email: willi@mutschler.eu
% Quadruplication Matrix as defined by
% Meijer (2005) - Matrix algebra for higher order moments. Linear Algebra and its Applications, 410,pp. 112134
function [DP6,DP6inv] = Q6_plication(p)
% Computes the 6-way duplication Matrix DP6 (and its Moore-Penrose inverse)
% such that for any p-dimensional vector x:
% y=kron(kron(kron(kron(kron(x,x),x,x),x),x)=DP6*z
% where z is of dimension np=p*(p+1)*(p+2)*(p+3)*(p+4)*(p+5)/(1*2*3*4*5*6)
% and is obtained from y by removing each second and later occurence of the
% same element. This is a generalization of the Duplication matrix.
% Reference: Meijer (2005) - Matrix algebra for higher order moments.
% Linear Algebra and its Applications, 410,pp. 112-134
% =========================================================================
% INPUTS
% * p [integer] size of vector
% -------------------------------------------------------------------------
% OUTPUTS
% * DP6 [p^6 by np] 6-way duplication matrix
% * DP6inv [np by np] Moore-Penrose inverse of DP6
% -------------------------------------------------------------------------
% This function is called by
% * pruned_state_space_system.m
% -------------------------------------------------------------------------
% This function calls
% * binom_coef (embedded)
% * mue (embedded)
% * uperm
% =========================================================================
% Copyright (C) 2020 Dynare Team
%
% Inputs:
% p: size of vector
% Outputs:
% QP: quadruplication matrix
% QPinv: Moore-Penrose inverse of QP
% This file is part of Dynare.
%
function [DP6,DP6inv] = Q6_plication(p,progress)
if nargin <2
progress =0;
end
reverseStr = ''; counti=1;
% 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 <http://www.gnu.org/licenses/>.
% =========================================================================
np = p*(p+1)*(p+2)*(p+3)*(p+4)*(p+5)/(1*2*3*4*5*6);
DP6 = spalloc(p^6,p*(p+1)*(p+2)*(p+3)*(p+4)*(p+5)/(1*2*3*4*5*6),p^6);
DP6 = spalloc(p^6,np,p^6);
counti=1;
for i1=1:p
for i2=i1:p
for i3=i2:p
for i4=i3:p
for i5=i4:p
for i6=i5:p
if progress && (rem(counti,100)== 0)
msg = sprintf(' Q6-plication Matrix Processed %d/%d', counti, np); fprintf([reverseStr, msg]); reverseStr = repmat(sprintf('\b'), 1, length(msg));
elseif progress && (counti==np)
msg = sprintf(' Q6-plication Matrix Processed %d/%d\n', counti, np); fprintf([reverseStr, msg]); reverseStr = repmat(sprintf('\b'), 1, length(msg));
end
idx = uperm([i6 i5 i4 i3 i2 i1]);
for r = 1:size(idx,1)
ii1 = idx(r,1); ii2= idx(r,2); ii3=idx(r,3); ii4=idx(r,4); ii5=idx(r,5); ii6=idx(r,6);
@ -45,11 +66,13 @@ end
DP6inv = (transpose(DP6)*DP6)\transpose(DP6);
function m = mue(p,i1,i2,i3,i4,i5,i6)
% Auxiliary expression, see page 122 of Meijer (2005)
m = binom_coef(p,6,1) - binom_coef(p,1,i1+1) - binom_coef(p,2,i2+1) - binom_coef(p,3,i3+1) - binom_coef(p,4,i4+1) - binom_coef(p,5,i5+1) - binom_coef(p,6,i6+1);
m = round(m);
end
function N = binom_coef(p,q,i)
% Auxiliary expression for binomial coefficients, see page 119 of Meijer (2005)
t = q; r =p+q-i;
if t==0
N=1;

View File

@ -1,19 +1,27 @@
%
% bivmom.m Date: 1/11/2004
% This Matlab program computes the product moment of X_1^{p_1}X_2^{p_2},
% where X_1 and X_2 are standard bivariate normally distributed.
function [y,dy] = bivmom(p,rho)
% Computes the product moment (and its derivative with respect to standard
% errors and correlation parameters) of X_1^{p_1}X_2^{p_2}, where X_1 and X_2
% are standard bivariate normally distributed.
% n : dimension of X
% rho: correlation coefficient between X_1 and X_2
% Reference: Kotz, Balakrishnan, and Johnson (2000), Continuous Multivariate
% Distributions, Vol. 1, p.261
% Note that there is a typo in Eq.(46.25), there should be an extra rho in front
% of the equation.
% Usage: bivmom(p,rho)
%
% Retrieved from http://www-2.rotman.utoronto.ca/~kan/papers/prodmom.zip
% This function is part of replication codes of the following paper:
% =========================================================================
% INPUTS
% p [2 by 1] powers of X_{1} and X_{2}
% rho [1 by 1] correlation coefficient between X_1 and X_2
% -------------------------------------------------------------------------
% OUTPUTS
% y [1 by 1] product moment E[X_1^{p_1}X_2^{p_2}]
% dy [1 by 1] derivative of y wrt to rho
% -------------------------------------------------------------------------
% This function is based upon bivmom.m which is part of replication codes
% of the following paper:
% Kan, R.: "From moments of sum to moments of product." Journal of
% Multivariate Analysis, 2008, vol. 99, issue 3, pages 542-554.
% bivmom.m can be retrieved from http://www-2.rotman.utoronto.ca/~kan/papers/prodmom.zip
% Further references:
% Kotz, Balakrishnan, and Johnson (2000), Continuous Multivariate Distributions, Vol. 1, p.261
% Note that there is a typo in Eq.(46.25), there should be an extra rho in front
% of the equation.
% =========================================================================
% Copyright (C) 2008-2015 Raymond Kan <kan@chass.utoronto.ca>
% Copyright (C) 2019-2020 Dynare Team
@ -33,7 +41,6 @@
% You should have received a copy of the GNU General Public License
% along with Dynare. If not, see <http://www.gnu.org/licenses/>.
% =========================================================================
function [y,dy] = bivmom(p,rho)
s1 = p(1);
s2 = p(2);
rho2 = rho^2;

View File

@ -15,12 +15,13 @@ function k = commutation(n, m, sparseflag)
% This function is called by
% * get_perturbation_params_derivs.m (previously getH.m)
% * get_identification_jacobians.m (previously getJJ.m)
% * pruned_state_space_system.m
% -------------------------------------------------------------------------
% This function calls
% * vec (embedded)
% =========================================================================
% Copyright (C) 1997 Tom Minka <minka@microsoft.com>
% Copyright (C) 2019 Dynare Team
% Copyright (C) 2019-2020 Dynare Team
%
% This file is part of Dynare.
%
@ -47,24 +48,12 @@ if nargin < 3
sparseflag = 0;
end
if 0
% first method
i = 1:(n*m);
a = reshape(i, n, m);
j = vec(transpose(a));
k = zeros(n*m,n*m);
for r = i
k(r, j(r)) = 1;
end
if sparseflag
k = reshape(kron(vec(speye(n)), speye(m)), n*m, n*m);
else
% second method
k = reshape(kron(vec(eye(n)), eye(m)), n*m, n*m);
end
if sparseflag ~= 0
k = sparse(k);
end
function V = vec(A)
V = A(:);
end

View File

@ -1,4 +1,4 @@
function [MEAN, dMEAN, REDUCEDFORM, dREDUCEDFORM, DYNAMIC, dDYNAMIC, MOMENTS, dMOMENTS, dSPECTRUM, dMINIMAL, derivatives_info] = get_identification_jacobians(estim_params, M, oo, options, options_ident, indpmodel, indpstderr, indpcorr, indvobs)
function [MEAN, dMEAN, REDUCEDFORM, dREDUCEDFORM, DYNAMIC, dDYNAMIC, MOMENTS, dMOMENTS, dSPECTRUM, dSPECTRUM_NO_MEAN, dMINIMAL, derivatives_info] = get_identification_jacobians(estim_params, M, oo, options, options_ident, indpmodel, indpstderr, indpcorr, indvobs)
% function [MEAN, dMEAN, REDUCEDFORM, dREDUCEDFORM, DYNAMIC, dDYNAMIC, MOMENTS, dMOMENTS, dSPECTRUM, dMINIMAL, derivatives_info] = get_identification_jacobians(estim_params, M, oo, options, options_ident, indpmodel, indpstderr, indpcorr, indvobs)
% previously getJJ.m in Dynare 4.5
% Sets up the Jacobians needed for identification analysis
@ -54,7 +54,14 @@ function [MEAN, dMEAN, REDUCEDFORM, dREDUCEDFORM, DYNAMIC, dDYNAMIC, MOMENTS, dM
% * order==1: corresponds to Iskrev (2010)'s J matrix
% * order==2: corresponds to Mutschler (2015)'s \bar{M}_2 matrix, i.e. theoretical moments from the pruned state space system
%
% dSPECTRUM: [totparam_nbr by totparam_nbr] in DR order. Gram matrix of Jacobian (wrt all params) of spectral density for VAROBS variables, where
% dSPECTRUM: [totparam_nbr by totparam_nbr] in DR order. Gram matrix of Jacobian (wrt all params) of mean and of spectral density for VAROBS variables, where
% spectral density at frequency w: f(w) = (2*pi)^(-1)*H(exp(-i*w))*E[Inov*Inov']*ctranspose(H(exp(-i*w)) with H being the Transfer function
% dSPECTRUM = dMEAN*dMEAN + int_{-\pi}^\pi transpose(df(w)/dp')*(df(w)/dp') dw
% * order==1: corresponds to Qu and Tkachenko (2012)'s G matrix, where Inov and H are computed from linear state space system
% * order==2: corresponds to Mutschler (2015)'s G_2 matrix, where Inov and H are computed from second-order pruned state space system
% * order==3: Inov and H are computed from third-order pruned state space system
%
% dSPECTRUM_NO_MEAN:[totparam_nbr by totparam_nbr] in DR order. Gram matrix of Jacobian (wrt all params) of spectral density for VAROBS variables, where
% spectral density at frequency w: f(w) = (2*pi)^(-1)*H(exp(-i*w))*E[Inov*Inov']*ctranspose(H(exp(-i*w)) with H being the Transfer function
% dSPECTRUM = int_{-\pi}^\pi transpose(df(w)/dp')*(df(w)/dp') dw
% * order==1: corresponds to Qu and Tkachenko (2012)'s G matrix, where Inov and H are computed from linear state space system
@ -81,11 +88,11 @@ function [MEAN, dMEAN, REDUCEDFORM, dREDUCEDFORM, DYNAMIC, dDYNAMIC, MOMENTS, dM
% * get_minimal_state_representation
% * duplication
% * dyn_vech
% * fjaco
% * get_perturbation_params_derivs (previously getH)
% * get_all_parameters
% * fjaco
% * lyapunov_symm
% * identification_numerical_objective (previously thet2tau)
% * pruned_state_space_system
% * vec
% =========================================================================
% Copyright (C) 2010-2020 Dynare Team
@ -220,22 +227,9 @@ elseif order == 3
end
% Get (pruned) state space representation:
options.options_ident.indvobs = indvobs;
options.options_ident.indpmodel = indpmodel;
options.options_ident.indpstderr = indpstderr;
options.options_ident.indpcorr = indpcorr;
oo.dr = pruned_state_space_system(M, options, oo.dr);
MEAN = oo.dr.pruned.E_y; dMEAN = oo.dr.pruned.dE_y;
A = oo.dr.pruned.A; dA = oo.dr.pruned.dA;
B = oo.dr.pruned.B; dB = oo.dr.pruned.dB;
C = oo.dr.pruned.C; dC = oo.dr.pruned.dC;
D = oo.dr.pruned.D; dD = oo.dr.pruned.dD;
c = oo.dr.pruned.c; dc = oo.dr.pruned.dc;
d = oo.dr.pruned.d; dd = oo.dr.pruned.dd;
Varinov = oo.dr.pruned.Varinov; dVarinov = oo.dr.pruned.dVarinov;
Om_z = oo.dr.pruned.Om_z; dOm_z = oo.dr.pruned.dOm_z;
Om_y = oo.dr.pruned.Om_y; dOm_y = oo.dr.pruned.dOm_y;
pruned = pruned_state_space_system(M, options, oo.dr, indvobs, nlags, useautocorr, 1);
MEAN = pruned.E_y;
dMEAN = pruned.dE_y;
%storage for Jacobians used in dsge_likelihood.m for analytical Gradient and Hession of likelihood (only at order=1)
derivatives_info = struct();
if order == 1
@ -247,12 +241,19 @@ if order == 1
end
%% Compute dMOMENTS
%Note that our state space system for computing second moments is the following:
% zhat = A*zhat(-1) + B*xi, where zhat = z - E(z)
% yhat = C*zhat(-1) + D*xi, where yhat = y - E(y)
if ~no_identification_moments
MOMENTS = identification_numerical_objective(xparam1, 1, estim_params, M, oo, options, indpmodel, indpstderr, indpcorr, indvobs, useautocorr, nlags, grid_nbr); %[outputflag=1]
MOMENTS = [MEAN; MOMENTS];
if ~no_identification_moments
if useautocorr
E_yy = pruned.Corr_y; dE_yy = pruned.dCorr_y;
E_yyi = pruned.Corr_yi; dE_yyi = pruned.dCorr_yi;
else
E_yy = pruned.Var_y; dE_yy = pruned.dVar_y;
E_yyi = pruned.Var_yi; dE_yyi = pruned.dVar_yi;
end
MOMENTS = [MEAN; dyn_vech(E_yy)];
for i=1:nlags
MOMENTS = [MOMENTS; vec(E_yyi(:,:,i))];
end
if kronflag == -1
%numerical derivative of autocovariogram
dMOMENTS = fjaco(str2func('identification_numerical_objective'), xparam1, 1, estim_params, M, oo, options, indpmodel, indpstderr, indpcorr, indvobs, useautocorr, nlags, grid_nbr); %[outputflag=1]
@ -260,58 +261,10 @@ if ~no_identification_moments
else
dMOMENTS = zeros(obs_nbr + obs_nbr*(obs_nbr+1)/2 + nlags*obs_nbr^2 , totparam_nbr);
dMOMENTS(1:obs_nbr,:) = dMEAN; %add Jacobian of first moments of VAROBS variables
% Denote Ezz0 = E[zhat*zhat'], then the following Lyapunov equation defines the autocovariagram: Ezz0 -A*Ezz0*A' = B*Sig_xi*B' = Om_z
[Ezz0,u] = lyapunov_symm(A, Om_z, options.lyapunov_fixed_point_tol, options.qz_criterium, options.lyapunov_complex_threshold, 1, options.debug);
stationary_vars = (1:length(indvobs))';
if ~isempty(u)
x = abs(C*u);
stationary_vars = find(all(x < options.Schur_vec_tol,2));
end
Eyy0 = NaN*ones(obs_nbr,obs_nbr);
Eyy0(stationary_vars,stationary_vars) = C(stationary_vars,:)*Ezz0*C(stationary_vars,:)' + Om_y(stationary_vars,stationary_vars);
%here method=1 is used, whereas all other calls of lyapunov_symm use method=2. The reason is that T and U are persistent, and input matrix A is the same, so using option 2 for all the rest of iterations spares a lot of computing time while not repeating Schur every time
indzeros = find(abs(Eyy0) < 1e-12); %find values that are numerical zero
Eyy0(indzeros) = 0;
if useautocorr
sdy = sqrt(diag(Eyy0)); %theoretical standard deviation
sdy = sdy(stationary_vars);
sy = sdy*sdy'; %cross products of standard deviations
end
for jp = 1:totparam_nbr
if jp <= (stderrparam_nbr+corrparam_nbr)
%Note that for stderr and corr parameters, the derivatives of the system matrices are zero, i.e. dA=dB=dC=dD=0
dEzz0 = lyapunov_symm(A,dOm_z(:,:,jp),options.lyapunov_fixed_point_tol,options.qz_criterium,options.lyapunov_complex_threshold,2,options.debug); %here method=2 is used to spare a lot of computing time while not repeating Schur every time
dEyy0 = C*dEzz0*C' + dOm_y(:,:,jp);
else %model parameters
dEzz0 = lyapunov_symm(A,dOm_z(:,:,jp)+dA(:,:,jp)*Ezz0*A'+A*Ezz0*dA(:,:,jp)',options.lyapunov_fixed_point_tol,options.qz_criterium,options.lyapunov_complex_threshold,2,options.debug); %here method=2 is used to spare a lot of computing time while not repeating Schur every time
dEyy0 = dC(:,:,jp)*Ezz0*C' + C*dEzz0*C' + C*Ezz0*dC(:,:,jp)' + dOm_y(:,:,jp);
end
%indzeros = find(abs(dEyy0) < 1e-12);
%dEyy0(indzeros) = 0;
if useautocorr
dsy = 1/2./sdy.*diag(dEyy0);
dsy = dsy*sdy'+sdy*dsy';
dEyy0corr = (dEyy0.*sy-dsy.*Eyy0)./(sy.*sy);
dEyy0corr = dEyy0corr-diag(diag(dEyy0corr))+diag(diag(dEyy0));
dMOMENTS(obs_nbr+1 : obs_nbr+obs_nbr*(obs_nbr+1)/2 , jp) = dyn_vech(dEyy0corr); %focus only on VAROBS variables
else
dMOMENTS(obs_nbr+1 : obs_nbr+obs_nbr*(obs_nbr+1)/2 , jp) = dyn_vech(dEyy0); %focus only on VAROBS variables
end
tmpEyyi = A*Ezz0*C(stationary_vars,:)' + B*Varinov*D(stationary_vars,:)';
%we could distinguish between stderr and corr params, but this has no real speed effect as we multipliy with zeros
dtmpEyyi = dA(:,:,jp)*Ezz0*C' + A*dEzz0*C' + A*Ezz0*dC(:,:,jp)' + dB(:,:,jp)*Varinov*D' + B*dVarinov(:,:,jp)*D' + B*Varinov*dD(:,:,jp)';
Ai = eye(size(A,1)); %this is A^0
dAi = zeros(size(A,1),size(A,1)); %this is d(A^0)
dMOMENTS(obs_nbr+1 : obs_nbr+obs_nbr*(obs_nbr+1)/2 , jp) = dyn_vech(dE_yy(:,:,jp));
for i = 1:nlags
Eyyi = C(stationary_vars,:)*Ai*tmpEyyi;
dEyyi = dC(:,:,jp)*Ai*tmpEyyi + C*dAi*tmpEyyi + C*Ai*dtmpEyyi;
if useautocorr
dEyyi = (dEyyi.*sy-dsy.*Eyyi)./(sy.*sy);
end
dMOMENTS(obs_nbr + obs_nbr*(obs_nbr+1)/2 + (i-1)*obs_nbr^2 + 1 : obs_nbr + obs_nbr*(obs_nbr+1)/2 + i*obs_nbr^2, jp) = vec(dEyyi); %focus only on VAROBS variables
dAi = dAi*A + Ai*dA(:,:,jp); %note that this is d(A^(i-1))
Ai = Ai*A; %note that this is A^(i-1)
dMOMENTS(obs_nbr + obs_nbr*(obs_nbr+1)/2 + (i-1)*obs_nbr^2 + 1 : obs_nbr + obs_nbr*(obs_nbr+1)/2 + i*obs_nbr^2, jp) = vec(dE_yyi(:,:,i,jp));
end
end
end
@ -357,7 +310,7 @@ if ~no_identification_spectrum
freqs = (0 : pi/(grid_nbr/2):pi); % we focus only on positive frequencies
tpos = exp( sqrt(-1)*freqs); %positive Fourier frequencies
tneg = exp(-sqrt(-1)*freqs); %negative Fourier frequencies
IA = eye(size(A,1));
IA = eye(size(pruned.A,1));
if kronflag == -1
%numerical derivative of spectral density
dOmega_tmp = fjaco(str2func('identification_numerical_objective'), xparam1, 2, estim_params, M, oo, options, indpmodel, indpstderr, indpcorr, indvobs, useautocorr, nlags, grid_nbr); %[outputflag=2]
@ -366,68 +319,63 @@ if ~no_identification_spectrum
kk = kk+1;
dOmega = dOmega_tmp(1 + (kk-1)*obs_nbr^2 : kk*obs_nbr^2,:);
if ig == 1 % add zero frequency once
dSPECTRUM = dOmega'*dOmega;
dSPECTRUM_NO_MEAN = dOmega'*dOmega;
else % due to symmetry to negative frequencies we can add positive frequencies twice
dSPECTRUM = dSPECTRUM + 2*(dOmega'*dOmega);
dSPECTRUM_NO_MEAN = dSPECTRUM_NO_MEAN + 2*(dOmega'*dOmega);
end
end
elseif kronflag == 1
%use Kronecker products
dA = reshape(dA,size(dA,1)*size(dA,2),size(dA,3));
dB = reshape(dB,size(dB,1)*size(dB,2),size(dB,3));
dC = reshape(dC,size(dC,1)*size(dC,2),size(dC,3));
dD = reshape(dD,size(dD,1)*size(dD,2),size(dD,3));
dVarinov = reshape(dVarinov,size(dVarinov,1)*size(dVarinov,2),size(dVarinov,3));
K_obs_exo = commutation(obs_nbr,size(Varinov,1));
dA = reshape(pruned.dA,size(pruned.dA,1)*size(pruned.dA,2),size(pruned.dA,3));
dB = reshape(pruned.dB,size(pruned.dB,1)*size(pruned.dB,2),size(pruned.dB,3));
dC = reshape(pruned.dC,size(pruned.dC,1)*size(pruned.dC,2),size(pruned.dC,3));
dD = reshape(pruned.dD,size(pruned.dD,1)*size(pruned.dD,2),size(pruned.dD,3));
dVarinov = reshape(pruned.dVarinov,size(pruned.dVarinov,1)*size(pruned.dVarinov,2),size(pruned.dVarinov,3));
K_obs_exo = commutation(obs_nbr,size(pruned.Varinov,1));
for ig=1:length(freqs)
z = tneg(ig);
zIminusA = (z*IA - A);
zIminusA = (z*IA - pruned.A);
zIminusAinv = zIminusA\IA;
Transferfct = D + C*zIminusAinv*B; % Transfer function
Transferfct = pruned.D + pruned.C*zIminusAinv*pruned.B; % Transfer function
dzIminusA = -dA;
dzIminusAinv = kron(-(transpose(zIminusA)\IA),zIminusAinv)*dzIminusA; %this takes long
dTransferfct = dD + DerivABCD(C,dC,zIminusAinv,dzIminusAinv,B,dB); %also long
dTransferfct = dD + DerivABCD(pruned.C,dC,zIminusAinv,dzIminusAinv,pruned.B,dB); %this takes long
dTransferfct_conjt = K_obs_exo*conj(dTransferfct);
dOmega = (1/(2*pi))*DerivABCD(Transferfct,dTransferfct,Varinov,dVarinov,Transferfct',dTransferfct_conjt); %also long
dOmega = (1/(2*pi))*DerivABCD(Transferfct,dTransferfct,pruned.Varinov,dVarinov,Transferfct',dTransferfct_conjt); %also long
if ig == 1 % add zero frequency once
dSPECTRUM = dOmega'*dOmega;
dSPECTRUM_NO_MEAN = dOmega'*dOmega;
else % due to symmetry to negative frequencies we can add positive frequencies twice
dSPECTRUM = dSPECTRUM + 2*(dOmega'*dOmega);
dSPECTRUM_NO_MEAN = dSPECTRUM_NO_MEAN + 2*(dOmega'*dOmega);
end
end
%put back into tensor notation
dA = reshape(dA,size(A,1),size(A,2),totparam_nbr);
dB = reshape(dB,size(B,1),size(B,2),totparam_nbr);
dC = reshape(dC,size(C,1),size(C,2),totparam_nbr);
dD = reshape(dD,size(D,1),size(D,2),totparam_nbr);
dVarinov = reshape(dVarinov,size(Varinov,1),size(Varinov,2),totparam_nbr);
elseif (kronflag==0) || (kronflag==-2)
for ig = 1:length(freqs)
IzminusA = tpos(ig)*IA - A;
invIzminusA = IzminusA\eye(size(A,1));
Transferfct = D + C*invIzminusA*B;
IzminusA = tpos(ig)*IA - pruned.A;
invIzminusA = IzminusA\eye(size(pruned.A,1));
Transferfct = pruned.D + pruned.C*invIzminusA*pruned.B;
dOmega = zeros(obs_nbr^2,totparam_nbr);
for j = 1:totparam_nbr
if j <= stderrparam_nbr+corrparam_nbr %stderr and corr parameters: only dSig is nonzero
dOmega_tmp = Transferfct*dVarinov(:,:,j)*Transferfct';
dOmega_tmp = Transferfct*pruned.dVarinov(:,:,j)*Transferfct';
else %model parameters
dinvIzminusA = -invIzminusA*(-dA(:,:,j))*invIzminusA;
dTransferfct = dD(:,:,j) + dC(:,:,j)*invIzminusA*B + C*dinvIzminusA*B + C*invIzminusA*dB(:,:,j);
dOmega_tmp = dTransferfct*Varinov*Transferfct' + Transferfct*dVarinov(:,:,j)*Transferfct' + Transferfct*Varinov*dTransferfct';
dinvIzminusA = -invIzminusA*(-pruned.dA(:,:,j))*invIzminusA;
dTransferfct = pruned.dD(:,:,j) + pruned.dC(:,:,j)*invIzminusA*pruned.B + pruned.C*dinvIzminusA*pruned.B + pruned.C*invIzminusA*pruned.dB(:,:,j);
dOmega_tmp = dTransferfct*pruned.Varinov*Transferfct' + Transferfct*pruned.dVarinov(:,:,j)*Transferfct' + Transferfct*pruned.Varinov*dTransferfct';
end
dOmega(:,j) = (1/(2*pi))*dOmega_tmp(:);
end
if ig == 1 % add zero frequency once
dSPECTRUM = dOmega'*dOmega;
dSPECTRUM_NO_MEAN = dOmega'*dOmega;
else % due to symmetry to negative frequencies we can add positive frequencies twice
dSPECTRUM = dSPECTRUM + 2*(dOmega'*dOmega);
dSPECTRUM_NO_MEAN = dSPECTRUM_NO_MEAN + 2*(dOmega'*dOmega);
end
end
end
% Normalize Matrix and add steady state Jacobian, note that G is real and symmetric by construction
dSPECTRUM = 2*pi*dSPECTRUM./(2*length(freqs)-1) + dMEAN'*dMEAN;
dSPECTRUM = real(dSPECTRUM);
dSPECTRUM_NO_MEAN = real(2*pi*dSPECTRUM_NO_MEAN./(2*length(freqs)-1));
dSPECTRUM = dSPECTRUM_NO_MEAN + dMEAN'*dMEAN;
else
dSPECTRUM_NO_MEAN = [];
dSPECTRUM = [];
end
@ -442,14 +390,14 @@ if ~no_identification_minimal
dMINIMAL = [];
else
% Derive and check minimal state vector of first-order
SYS.A = oo.dr.ghx(oo.dr.pruned.indx,:);
SYS.dA = oo.dr.derivs.dghx(oo.dr.pruned.indx,:,:);
SYS.B = oo.dr.ghu(oo.dr.pruned.indx,:);
SYS.dB = oo.dr.derivs.dghu(oo.dr.pruned.indx,:,:);
SYS.C = oo.dr.ghx(oo.dr.pruned.indy,:);
SYS.dC = oo.dr.derivs.dghx(oo.dr.pruned.indy,:,:);
SYS.D = oo.dr.ghu(oo.dr.pruned.indy,:);
SYS.dD = oo.dr.derivs.dghu(oo.dr.pruned.indy,:,:);
SYS.A = oo.dr.ghx(pruned.indx,:);
SYS.dA = oo.dr.derivs.dghx(pruned.indx,:,:);
SYS.B = oo.dr.ghu(pruned.indx,:);
SYS.dB = oo.dr.derivs.dghu(pruned.indx,:,:);
SYS.C = oo.dr.ghx(pruned.indy,:);
SYS.dC = oo.dr.derivs.dghx(pruned.indy,:,:);
SYS.D = oo.dr.ghu(pruned.indy,:);
SYS.dD = oo.dr.derivs.dghu(pruned.indy,:,:);
[CheckCO,minnx,SYS] = get_minimal_state_representation(SYS,1);
if CheckCO == 0

View File

@ -145,6 +145,12 @@ analytic_derivation_mode = options.analytic_derivation_mode;
% * -2: numerical two-sided finite difference method to compute numerically dYss, dg1, dg2, dg3, d2Yss and d2g1, the other output arguments are computed analytically as in kronflag=0
gstep = options.gstep;
order = options.order;
if isempty(options.qz_criterium)
% set default value for qz_criterium: if there are no unit roots one can use 1.0
% If they are possible, you may have have multiple unit roots and the accuracy
% decreases when computing the eigenvalues in lyapunov_symm. Hence, we normally use 1+1e-6
options = select_qz_criterium_value(options);
end
qz_criterium = options.qz_criterium;
threads_BC = options.threads.kronecker.sparse_hessian_times_B_kronecker_C;
@ -269,6 +275,11 @@ if d2flag
KalmanB = ghu;
end
% Store some objects
DERIVS.indpmodel = indpmodel;
DERIVS.indpstderr = indpstderr;
DERIVS.indpcorr = indpcorr;
if analytic_derivation_mode == -1
%% numerical two-sided finite difference method using function get_perturbation_params_derivs_numerical_objective.m (previously thet2tau.m in Dynare 4.5) for
% Jacobian (wrt selected stderr, corr and model parameters) of

View File

@ -137,7 +137,7 @@ no_identification_spectrum = options_ident.no_identification_spectrum;
if info(1) == 0 %no errors in solution
% Compute parameter Jacobians for identification analysis
[MEAN, dMEAN, REDUCEDFORM, dREDUCEDFORM, DYNAMIC, dDYNAMIC, MOMENTS, dMOMENTS, dSPECTRUM, dMINIMAL, derivatives_info] = get_identification_jacobians(estim_params_, M_, oo_, options_, options_ident, indpmodel, indpstderr, indpcorr, indvobs);
[MEAN, dMEAN, REDUCEDFORM, dREDUCEDFORM, DYNAMIC, dDYNAMIC, MOMENTS, dMOMENTS, dSPECTRUM, dSPECTRUM_NO_MEAN, dMINIMAL, derivatives_info] = get_identification_jacobians(estim_params_, M_, oo_, options_, options_ident, indpmodel, indpstderr, indpcorr, indvobs);
if isempty(dMINIMAL)
% Komunjer and Ng is not computed if (1) minimality conditions are not fullfilled or (2) there are more shocks and measurement errors than observables, so we need to reset options
no_identification_minimal = 1;
@ -194,7 +194,7 @@ if info(1) == 0 %no errors in solution
options_ident.no_identification_spectrum = 1; %do not recompute dSPECTRUM
options_ident.ar = nlags; %store new lag number
options_.ar = nlags; %store new lag number
[~, ~, ~, ~, ~, ~, MOMENTS, dMOMENTS, ~, ~, ~] = get_identification_jacobians(estim_params_, M_, oo_, options_, options_ident, indpmodel, indpstderr, indpcorr, indvobs);
[~, ~, ~, ~, ~, ~, MOMENTS, dMOMENTS, ~, ~, ~, ~] = get_identification_jacobians(estim_params_, M_, oo_, options_, options_ident, indpmodel, indpstderr, indpcorr, indvobs);
ind_dMOMENTS = (find(max(abs(dMOMENTS'),[],1) > tol_deriv)); %new index with non-zero rows
end
@ -508,6 +508,7 @@ if info(1) == 0 %no errors in solution
ide_spectrum.norm_dSPECTRUM = norm_dSPECTRUM;
ide_spectrum.tilda_dSPECTRUM = tilda_dSPECTRUM;
ide_spectrum.dSPECTRUM = dSPECTRUM;
ide_spectrum.dSPECTRUM_NO_MEAN = dSPECTRUM_NO_MEAN;
end
%% Perform identification checks, i.e. find out which parameters are involved

View File

@ -30,17 +30,15 @@ function out = identification_numerical_objective(params, outputflag, estim_para
% Jacobian of the dynamic model equations, and Y_t selected variables
% -------------------------------------------------------------------------
% This function is called by
% * get_first_order_solution_params_deriv.m (previously getH.m)
% * get_identification_jacobians.m (previously getJJ.m)
% -------------------------------------------------------------------------
% This function calls
% * [M.fname,'.dynamic']
% * dynare_resolve
% * dyn_vech
% * lyapunov_symm
% * resol
% * vec
% =========================================================================
% Copyright (C) 2011-2019 Dynare Team
% Copyright (C) 2011-2020 Dynare Team
%
% This file is part of Dynare.
%
@ -58,16 +56,6 @@ function out = identification_numerical_objective(params, outputflag, estim_para
% along with Dynare. If not, see <http://www.gnu.org/licenses/>.
% =========================================================================
if nargin < 11 || isempty(useautocorr)
useautocorr = 0;
end
if nargin < 12 || isempty(nlags)
nlags = 3;
end
if nargin < 13 || isempty(grid_nbr)
grid_nbr = 0;
end
%% Update stderr, corr and model parameters
%note that if no estimated_params_block is given, then all stderr and model parameters are selected but no corr parameters
if length(params) > length(indpmodel)
@ -90,50 +78,21 @@ end
%% compute Kalman transition matrices and steady state with updated parameters
[~,info,M,options,oo] = resol(0,M,options,oo);
options = rmfield(options,'options_ident');
oo.dr = pruned_state_space_system(M, options, oo.dr);
A = oo.dr.pruned.A;
B = oo.dr.pruned.B;
C = oo.dr.pruned.C(indvar,:);
D = oo.dr.pruned.D(indvar,:);
Om_z = oo.dr.pruned.Om_z;
Om_y = oo.dr.pruned.Om_y(indvar,indvar);
Varinov = oo.dr.pruned.Varinov;
obs_nbr = size(C,1);
pruned = pruned_state_space_system(M, options, oo.dr, indvar, nlags, useautocorr, 0);
%% out = [vech(cov(Y_t,Y_t)); vec(cov(Y_t,Y_{t-1}); ...; vec(cov(Y_t,Y_{t-nlags})] of indvar variables, in DR order. This is Iskrev (2010)'s J matrix.
if outputflag == 1
% Denote Ezz0 = E_t(z_t * z_t'), then the following Lyapunov equation defines the autocovariagrom: Ezz0 -A*Ezz*A' = B*Sig_e*B'
[Ezz0,u] = lyapunov_symm(A, Om_z, options.lyapunov_fixed_point_tol, options.qz_criterium, options.lyapunov_complex_threshold, 1, options.debug);
stationary_vars = (1:size(C,1))';
if ~isempty(u)
x = abs(C*u);
stationary_vars = find(all(x < options.Schur_vec_tol,2));
end
Eyy0 = NaN*ones(obs_nbr,obs_nbr);
Eyy0(stationary_vars,stationary_vars) = C(stationary_vars,:)*Ezz0*C(stationary_vars,:)' + Om_y(stationary_vars,stationary_vars);
indzeros = find(abs(Eyy0) < 1e-12); %find values that are numerical zero
Eyy0(indzeros) = 0;
if outputflag == 1
if useautocorr
sy = sqrt(diag(Ezz0)); %theoretical standard deviation
sy = sy(stationary_vars);
sy = sy*sy'; %cross products of standard deviations
sy0 = sy-diag(diag(sy))+eye(length(sy));
Eyy0corr = NaN*ones(size(C,1),size(C,1));
Eyy0corr(stationary_vars,stationary_vars) = Eyy0./sy0;
out = dyn_vech(Eyy0corr); %focus only on unique terms
out = dyn_vech(pruned.Corr_y);
else
out = dyn_vech(Eyy0); %focus only on unique terms
end
% compute autocovariances/autocorrelations of lagged observed variables
tmpEyyi = A*Ezz0*C(stationary_vars,:)' + B*Varinov*D(stationary_vars,:)';
Ai = eye(size(A,1)); %this is A^0
out = dyn_vech(pruned.Var_y);
end
for i = 1:nlags
Eyyi = NaN*ones(obs_nbr,obs_nbr);
Eyyi(stationary_vars,stationary_vars) = C(stationary_vars,:)*Ai*tmpEyyi;
if useautocorr
Eyyi = Eyyi./sy;
out = [out;vec(pruned.Corr_yi(:,:,i))];
else
out = [out;vec(pruned.Var_yi(:,:,i))];
end
out = [out;vec(Eyyi)];
Ai = Ai*A; %note that this is A^(i-1)
end
end
@ -142,13 +101,13 @@ if outputflag == 2
% This computes the spectral density g_omega where the interval [-pi;\pi] is discretized by grid_nbr points
freqs = (0 : pi/(grid_nbr/2):pi);% we focus only on positive values including the 0 frequency
tpos = exp( sqrt(-1)*freqs); %Fourier frequencies
IA = eye(size(A,1));
var_nbr = size(C,1);
IA = eye(size(pruned.A,1));
var_nbr = size(pruned.C,1);
out = zeros(var_nbr^2*length(freqs),1);
kk = 0;
for ig = 1:length(freqs)
Transferfct = D + C*((tpos(ig)*IA-A)\B);
g_omega = (1/(2*pi))*(Transferfct*Varinov*Transferfct'); % note that ' is the conjugate transpose
Transferfct = pruned.D + pruned.C*((tpos(ig)*IA-pruned.A)\pruned.B);
g_omega = (1/(2*pi))*(Transferfct*pruned.Varinov*Transferfct'); % note that ' is the conjugate transpose
kk = kk+1;
out(1 + (kk-1)*var_nbr^2 : kk*var_nbr^2) = g_omega(:);
end

View File

@ -1,10 +1,37 @@
function dy = prodmom_deriv(V,ii,nu,dV,dC)
% This function builds upon and extends prodmom.m to compute the
% derivatives of product moments of normally distributed variables with
% respect to standard errors and correlation parameters.
% prodmom.m is part of replication codes of the following paper:
function [y,dy] = prodmom_deriv(V,ii,nu,dV,dC)
% Computes the product moments (and its derivatives with respect to standard
% errors and correlation parameters) of normally distributed variables, i.e.
% this function computes the product moment of
% X_{i_1}^{nu_1}X_{i_2}^{nu_2}...X_{i_m}^{nu_m}, where X_{i_j} are elements
% from X ~ N(0_n,V) and V is positive semidefinite.
% Example: To get E[X_2X_4^3X_7^2], use prodmom_deriv(V,[2 4 7],[1 3 2])
% =========================================================================
% INPUTS
% V [n by n] covariance matrix of X (needs to be positive semidefinite)
% ii [m by 1] vector of i_j
% nu [nu_m by 1] power of X_{i_j}
% dV [n by n by stderrparam_nbr+corrparam_nbr] derivative of V with respect
% to selected standard error (stderr)
% and correlation (corr) parameters
% dC [n by n by stderrparam_nbr+corrparam_nbr] derivative of Correlation matrix C with respect
% to selected standard error (stderr)
% and correlation (corr) parameters
% -------------------------------------------------------------------------
% OUTPUTS
% y [1 by 1] product moment E[X_{i_1}^{nu_1}X_{i_2}^{nu_2}...X_{i_m}^{nu_m}]
% dy [1 by stderrparam_nbr+corrparam_nbr] derivatives of y wrt to selected
% standard error and corr parameters
% -------------------------------------------------------------------------
% This function is based upon prodmom.m which is part of replication codes
% of the following paper:
% Kan, R.: "From moments of sum to moments of product." Journal of
% Multivariate Analysis, 2008, vol. 99, issue 3, pages 542-554.
% prodmom.m can be retrieved from http://www-2.rotman.utoronto.ca/~kan/papers/prodmom.zip
% Further references:
% Triantafyllopoulos (2003) On the Central Moments of the Multidimensional
% Gaussian Distribution, Mathematical Scientist
% Kotz, Balakrishnan, and Johnson (2000), Continuous Multivariate
% Distributions, Vol. 1, p.261
% =========================================================================
% Copyright (C) 2008-2015 Raymond Kan <kan@chass.utoronto.ca>
% Copyright (C) 2019-2020 Dynare Team
@ -25,89 +52,118 @@ function dy = prodmom_deriv(V,ii,nu,dV,dC)
% along with Dynare. If not, see <http://www.gnu.org/licenses/>.
% =========================================================================
if nargin<3
nu = ones(size(ii));
nu = ones(size(ii));
end
s = sum(nu);
if s==0
dy = zeros(1,1,size(dV,3));
return
y = 1;
if nargout > 1
dy = zeros(1,1,size(dV,3));
end
return
end
if rem(s,2)==1
dy = zeros(1,1,size(dV,3));
return
y = 0;
if nargout > 1
dy = zeros(1,1,size(dV,3));
end
return
end
nuz = nu==0;
nu(nuz) = [];
ii(nuz) = [];
m = length(ii);
V = V(ii,ii);
dV = dV(ii,ii,:);
if nargout > 1
dV = dV(ii,ii,:);
end
s2 = s/2;
%
% Use univariate normal results
%
if m==1
dy = s2*V^(s2-1)*dV*prod([1:2:s-1]);
dy = reshape(dy,1,size(dV,3));
y = V^s2*prod([1:2:s-1]);
if nargout > 1
dy = s2*V^(s2-1)*dV*prod([1:2:s-1]);
dy = reshape(dy,1,size(dV,3));
end
return
end
%
% Use bivariate normal results when there are only two distinct indices
%
if m==2
rho = V(1,2)/sqrt(V(1,1)*V(2,2));
drho = dC(ii(1),ii(2),:);
[tmp,dtmp] = bivmom(nu,rho);
dy = (nu(1)/2)*V(1,1)^(nu(1)/2-1)*dV(1,1,:) * V(2,2)^(nu(2)/2) * tmp...
+ V(1,1)^(nu(1)/2) * (nu(2)/2)*V(2,2)^(nu(2)/2-1)*dV(2,2,:) * tmp...
+ V(1,1)^(nu(1)/2) * V(2,2)^(nu(2)/2) * dtmp * drho;
dy = reshape(dy,1,size(dV,3));
return
rho = V(1,2)/sqrt(V(1,1)*V(2,2));
if nargout > 1
drho = dC(ii(1),ii(2),:);
[tmp,dtmp] = bivmom(nu,rho);
dy = (nu(1)/2)*V(1,1)^(nu(1)/2-1)*dV(1,1,:) * V(2,2)^(nu(2)/2) * tmp...
+ V(1,1)^(nu(1)/2) * (nu(2)/2)*V(2,2)^(nu(2)/2-1)*dV(2,2,:) * tmp...
+ V(1,1)^(nu(1)/2) * V(2,2)^(nu(2)/2) * dtmp * drho;
dy = reshape(dy,1,size(dV,3));
else
tmp = bivmom(nu,rho);
end
y = V(1,1)^(nu(1)/2)*V(2,2)^(nu(2)/2)*tmp;
return
end
%
% Regular case
%
[nu,inu] = sort(nu,2,'descend');
V = V(inu,inu); % Extract only the relevant part of V
dV = dV(inu,inu,:); % Extract only the relevant part of dV
x = zeros(1,m);
V = V./2;
dV = dV./2;
nu2 = nu./2;
p = 2;
q = nu2*V*nu2';
%dq = nu2*dV*nu2';
%dq = multiprod(multiprod(nu2,dV),nu2');
dq = NaN(size(q,1), size(q,2), size(dV,3));
for jp = 1:size(dV,3)
dq(:,:,jp) = nu2*dV(:,:,jp)*nu2';
y = 0;
if nargout > 1
dV = dV(inu,inu,:); % Extract only the relevant part of dV
dV = dV./2;
%dq = nu2*dV*nu2';
%dq = multiprod(multiprod(nu2,dV),nu2');
dq = NaN(size(q,1), size(q,2), size(dV,3));
for jp = 1:size(dV,3)
dq(:,:,jp) = nu2*dV(:,:,jp)*nu2';
end
dy = 0;
end
dy = 0;
for i=1:fix(prod(nu+1)/2)
dy = dy+p*s2*q^(s2-1)*dq;
y = y+p*q^s2;
if nargout > 1
dy = dy+p*s2*q^(s2-1)*dq;
end
for j=1:m
if x(j)<nu(j)
x(j) = x(j)+1;
p = -round(p*(nu(j)+1-x(j))/x(j));
%dq = dq-2*(nu2-x)*dV(:,j,:)-dV(j,j,:);
%dq = dq-2*multiprod((nu2-x),dV(:,j,:))-dV(j,j,:);
for jp=1:size(dV,3)
dq(:,:,jp) = dq(:,:,jp)-2*(nu2-x)*dV(:,j,jp)-dV(j,j,jp);
end
q = q-2*(nu2-x)*V(:,j)-V(j,j);
break
x(j) = x(j)+1;
p = -round(p*(nu(j)+1-x(j))/x(j));
q = q-2*(nu2-x)*V(:,j)-V(j,j);
if nargout > 1
%dq = dq-2*(nu2-x)*dV(:,j,:)-dV(j,j,:);
%dq = dq-2*multiprod((nu2-x),dV(:,j,:))-dV(j,j,:);
for jp=1:size(dV,3)
dq(:,:,jp) = dq(:,:,jp)-2*(nu2-x)*dV(:,j,jp)-dV(j,j,jp);
end
end
break
else
x(j) = 0;
if rem(nu(j),2)==1
p = -p;
end
%dq = dq+2*nu(j)*multiprod((nu2-x),dV(:,j,:))-nu(j)^2*dV(j,j,:);
for jp=1:size(dV,3)
dq(:,:,jp) = dq(:,:,jp)+2*nu(j)*(nu2-x)*dV(:,j,jp)-nu(j)^2*dV(j,j,jp);
end
q = q+2*nu(j)*(nu2-x)*V(:,j)-nu(j)^2*V(j,j);
x(j) = 0;
if rem(nu(j),2)==1
p = -p;
end
if nargout > 1
%dq = dq+2*nu(j)*multiprod((nu2-x),dV(:,j,:))-nu(j)^2*dV(j,j,:);
for jp=1:size(dV,3)
dq(:,:,jp) = dq(:,:,jp)+2*nu(j)*(nu2-x)*dV(:,j,jp)-nu(j)^2*dV(j,j,jp);
end
end
q = q+2*nu(j)*(nu2-x)*V(:,j)-nu(j)^2*V(j,j);
end
end
end
dy = dy/prod([1:s2]);
dy = reshape(dy,1,size(dV,3));
y = y/prod([1:s2]);
if nargout > 1
dy = dy/prod([1:s2]);
dy = reshape(dy,1,size(dV,3));
end

File diff suppressed because it is too large Load Diff

View File

@ -1,46 +1,54 @@
% By Willi Mutschler, September 26, 2016. Email: willi@mutschler.eu
% Quadruplication Matrix as defined by
% Meijer (2005) - Matrix algebra for higher order moments. Linear Algebra and its Applications, 410,pp. 112134
function [QP,QPinv] = quadruplication(p)
% Computes the Quadruplication Matrix QP (and its Moore-Penrose inverse)
% such that for any p-dimensional vector x:
% y=kron(kron(kron(x,x),x),x)=QP*z
% where z is of dimension np=p*(p+1)*(p+2)*(p+3)/2 and is obtained from y
% by removing each second and later occurence of the same element.
% This is a generalization of the Duplication matrix.
% Reference: Meijer (2005) - Matrix algebra for higher order moments.
% Linear Algebra and its Applications, 410,pp. 112-134
% =========================================================================
% INPUTS
% * p [integer] size of vector
% -------------------------------------------------------------------------
% OUTPUTS
% * QP [p^4 by np] Quadruplication matrix
% * QPinv [np by np] Moore-Penrose inverse of QP
% -------------------------------------------------------------------------
% This function is called by
% * pruned_state_space_system.m
% -------------------------------------------------------------------------
% This function calls
% * mue (embedded)
% * uperm
% =========================================================================
% Copyright (C) 2020 Dynare Team
%
% Inputs:
% p: size of vector
% Outputs:
% QP: quadruplication matrix
% QPinv: Moore-Penrose inverse of QP
% This file is part of Dynare.
%
function [QP,QPinv] = quadruplication(p,progress,sparseflag)
if nargin <2
progress =0;
end
if nargin < 3
sparseflag = 1;
end
reverseStr = ''; counti=1;
% 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 <http://www.gnu.org/licenses/>.
% =========================================================================
np = p*(p+1)*(p+2)*(p+3)/24;
if sparseflag
QP = spalloc(p^4,p*(p+1)*(p+2)*(p+3)/24,p^4);
else
QP = zeros(p^4,p*(p+1)*(p+2)*(p+3)/24);
end
QP = spalloc(p^4,np,p^4);
if nargout > 1
if sparseflag
QPinv = spalloc(p*(p+1)*(p+2)*(p+3)/24,p*(p+1)*(p+2)*(p+3)/24,p^4);
else
QPinv = zeros(p*(p+1)*(p+2)*(p+3)/24,p*(p+1)*(p+2)*(p+3)/24);
end
QPinv = spalloc(np,np,p^4);
end
counti = 1;
for l=1:p
for k=l:p
for j=k:p
for i=j:p
if progress && (rem(counti,100)== 0)
msg = sprintf(' Quadruplication Matrix Processed %d/%d', counti, np); fprintf([reverseStr, msg]); reverseStr = repmat(sprintf('\b'), 1, length(msg));
elseif progress && (counti==np)
msg = sprintf(' Quadruplication Matrix Processed %d/%d\n', counti, np); fprintf([reverseStr, msg]); reverseStr = repmat(sprintf('\b'), 1, length(msg));
end
idx = uperm([i j k l]);
for r = 1:size(idx,1)
ii = idx(r,1); jj= idx(r,2); kk=idx(r,3); ll=idx(r,4);
@ -75,6 +83,7 @@ end
%QPinv = (transpose(QP)*QP)\transpose(QP);
function m = mue(p,i,j,k,l)
% Auxiliary expression, see page 118 of Meijer (2005)
m = i + (j-1)*p + 1/2*(k-1)*p^2 + 1/6*(l-1)*p^3 - 1/2*j*(j-1) + 1/6*k*(k-1)*(k-2) - 1/24*l*(l-1)*(l-2)*(l-3) - 1/2*(k-1)^2*p + 1/6*(l-1)^3*p - 1/4*(l-1)*(l-2)*p^2 - 1/4*l*(l-1)*p + 1/6*(l-1)*p;
m = round(m);
end

View File

@ -1,4 +1,5 @@
function p = uperm(a)
% Return all unique permutations of possibly-repeating array elements
% =========================================================================
% Copyright (C) 2014 Bruno Luong <brunoluong@yahoo.com>
% Copyright (C) 2020 Dynare Team

1
tests/.gitignore vendored
View File

@ -126,6 +126,7 @@ wsOct
!/practicing/dataHST.mat
!/practicing/data_consRicardoypg.mat
!/practicing/datasaver.m
!/pruning/Andreasen_et_al_2018_Dynare44Pruning_v2.mat
!/utils/printMakeCheckMatlabErrMsg.m
!/utils/printMakeCheckOctaveErrMsg.m
!/prior_posterior_function/posterior_function_demo.m

View File

@ -21,6 +21,7 @@ MODFILES = \
analytic_derivatives/fs2000_analytic_derivation.mod \
analytic_derivatives/BrockMirman_PertParamsDerivs.mod \
analytic_derivatives/burnside_3_order_PertParamsDerivs.mod \
pruning/AnSchorfheide_pruned_state_space.mod \
measurement_errors/fs2000_corr_me_ml_mcmc/fs2000_corr_ME.mod \
TeX/fs2000_corr_ME.mod \
estimation/MH_recover/fs2000_recover_tarb.mod \
@ -716,6 +717,10 @@ analytic_derivatives: m/analytic_derivatives o/analytic_derivatives
m/analytic_derivatives: $(patsubst %.mod, %.m.trs, $(filter analytic_derivatives/%.mod, $(MODFILES)))
o/analytic_derivatives: $(patsubst %.mod, %.o.trs, $(filter analytic_derivatives/%.mod, $(MODFILES)))
pruning: m/pruning o/pruning
m/pruning: $(patsubst %.mod, %.m.trs, $(filter pruning/%.mod, $(MODFILES)))
o/pruning: $(patsubst %.mod, %.o.trs, $(filter pruning/%.mod, $(MODFILES)))
fs2000: m/fs2000 o/fs2000
m/fs2000: $(patsubst %.mod, %.m.trs, $(filter fs2000/%.mod, $(MODFILES)))
o/fs2000: $(patsubst %.mod, %.o.trs, $(filter fs2000/%.mod, $(MODFILES)))
@ -837,6 +842,7 @@ EXTRA_DIST = \
gsa/data_ca1.mat \
analytic_derivatives/fsdat_simul.m \
analytic_derivatives/nBrockMirmanSYM.mat \
pruning/Andreasen_et_al_2018_Dynare44Pruning_v2.mat \
fs2000/fsdat_simul.m \
fs2000/fsdat_simul_dseries.m \
fs2000/fsdat_simul_missing_obs.m \

View File

@ -0,0 +1,193 @@
% This mod file compares the functionality of Dynare's pruned_state_space.m with the
% external Dynare pruning toolbox of Andreasen, Fernández-Villaverde and Rubio-Ramírez (2018):
% "The Pruned State-Space System for Non-Linear DSGE Models: Theory and Empirical Applications",
% Review of Economic Studies, Volume 85, Issue 1, Pages 149.
% The model under study is taken from An and Schorfheide (2007): "Bayesian Analysis of DSGE Models",
% Econometric Reviews, Volume 26, Issue 2-4, Pages 113-172.
% Note that we use version 2 of the toolbox, i.e. the one which is called
% "Third-order GMM estimate package for DSGE models (version 2)" and can be
% downloaded from https://sites.google.com/site/mandreasendk/home-1
%
% Created by @wmutschl (Willi Mutschler, willi@mutschler.eu)
%
% =========================================================================
% Copyright (C) 2020 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 <http://www.gnu.org/licenses/>.
% =========================================================================
% set this to 1 if you want to recompute using the Andreasen et al toolbox
% otherwise the results are loaded from Andreasen_et_al_2018_Dynare44Pruning_v2.mat
@#define Andreasen_et_al_toolbox = 0
var YGR INFL INT
c p R g y z; %if ordering of var is changed comparison code below needs to be adapted
varexo e_r e_g e_z;
parameters tau nu kap cyst psi1 psi2 rhor rhog rhoz rrst pist gamst;
tau = 2;
nu = 0.1;
kap = 0.33;
cyst = 0.85;
psi1 = 1.5;
psi2 = 0.125;
rhor = 0.75;
rhog = 0.95;
rhoz = 0.9;
rrst = 1;
pist = 3.2;
gamst = 0.55;
sig_r = .2;
sig_g = .6;
sig_z = .3;
model;
#pist2 = exp(pist/400);
#rrst2 = exp(rrst/400);
#bet = 1/rrst2;
#phi = tau*(1-nu)/nu/kap/pist2^2;
#gst = 1/cyst;
#cst = (1-nu)^(1/tau);
#yst = cst*gst;
#dy = y-y(-1);
1 = exp(-tau*c(+1)+tau*c+R-z(+1)-p(+1));
(1-nu)/nu/phi/(pist2^2)*(exp(tau*c)-1) = (exp(p)-1)*((1-1/2/nu)*exp(p)+1/2/nu) - bet*(exp(p(+1))-1)*exp(-tau*c(+1)+tau*c+y(+1)-y+p(+1));
exp(c-y) = exp(-g) - phi*pist2^2*gst/2*(exp(p)-1)^2;
R = rhor*R(-1) + (1-rhor)*psi1*p + (1-rhor)*psi2*(y-g) + e_r;
g = rhog*g(-1) + e_g;
z = rhoz*z(-1) + e_z;
YGR = gamst+100*(dy+z);
INFL = pist+400*p;
INT = pist+rrst+4*gamst+400*R;
end;
shocks;
var e_r = sig_r^2;
var e_g = sig_g^2;
var e_z = sig_z^2;
end;
steady_state_model;
y = 0;
R = 0;
g = 0;
z = 0;
c = 0;
p = 0;
YGR = gamst;
INFL = pist;
INT = pist + rrst + 4*gamst;
end;
steady; check; model_diagnostics;
@#for orderApp in [1, 2, 3]
stoch_simul(order=@{orderApp},pruning,irf=0,periods=0);
pruned_state_space.order_@{orderApp} = pruned_state_space_system(M_, options_, oo_.dr, [], options_.ar, 1, 0);
@#if Andreasen_et_al_toolbox
addpath('Dynare44Pruning_v2/simAndMoments3order'); %provide path to toolbox
optPruning.orderApp = @{orderApp};
outAndreasenetal.order_@{orderApp} = RunDynarePruning(optPruning,oo_,M_,[oo_.dr.ghx oo_.dr.ghu]);
rmpath('Dynare44Pruning_v2/simAndMoments3order');
close all;
@#endif
@#endfor
@#if Andreasen_et_al_toolbox
delete Andreasen_et_al_2018_Dynare44Pruning_v2.mat;
pause(3);
save('Andreasen_et_al_2018_Dynare44Pruning_v2.mat', 'outAndreasenetal')
pause(3);
@#endif
load('Andreasen_et_al_2018_Dynare44Pruning_v2.mat')
% Make comparisons only at orders 1 and 2
for iorder = 1:3
fprintf('ORDER %d:\n',iorder);
pruned = pruned_state_space.(sprintf('order_%d',iorder));
outAndreasen = outAndreasenetal.(sprintf('order_%d',iorder));
%make sure variable ordering is correct
if ~isequal(M_.endo_names,[outAndreasen.label_y; outAndreasen.label_v(1:M_.nspred)])
error('variable ordering is not the same, change declaration order');
end
norm_E_yx = norm(pruned.E_y(oo_.dr.inv_order_var) - [outAndreasen.Mean_y; outAndreasen.Mean_v(1:M_.nspred)] , Inf);
fprintf('max(sum(abs(E[y;x]''))): %d\n',norm_E_yx);
norm_Var_y = norm(pruned.Var_y(oo_.dr.inv_order_var(1:(M_.endo_nbr-M_.nspred)),oo_.dr.inv_order_var(1:(M_.endo_nbr-M_.nspred))) - outAndreasen.Var_y , Inf);
fprintf('max(sum(abs(Var[y]''))):: %d\n',norm_Var_y);
norm_Var_x = norm(pruned.Var_y(M_.nstatic+(1:M_.nspred),M_.nstatic+(1:M_.nspred)) - outAndreasen.Var_v(1:M_.nspred,1:M_.nspred) , Inf);
fprintf('max(sum(abs(Var[x]''))): %d\n',norm_Var_x);
norm_Corr_yi1 = norm(pruned.Corr_yi(oo_.dr.inv_order_var(1:(M_.endo_nbr-M_.nspred)),oo_.dr.inv_order_var(1:(M_.endo_nbr-M_.nspred)),1) - outAndreasen.Corr_y(:,:,1) , Inf);
fprintf('max(sum(abs(Corr[y,y(-1)]''))): %d\n',norm_Corr_yi1);
norm_Corr_yi2 = norm(pruned.Corr_yi(oo_.dr.inv_order_var(1:(M_.endo_nbr-M_.nspred)),oo_.dr.inv_order_var(1:(M_.endo_nbr-M_.nspred)),2) - outAndreasen.Corr_y(:,:,2) , Inf);
fprintf('max(sum(abs(Corr[y,y(-2)]''))): %d\n',norm_Corr_yi2);
norm_Corr_xi1 = norm(pruned.Corr_yi(M_.nstatic+(1:M_.nspred),M_.nstatic+(1:M_.nspred),1) - outAndreasen.Corr_v(1:M_.nspred,1:M_.nspred,1) , Inf);
fprintf('max(sum(abs(Corr[x,x(-1)]''))): %d\n',norm_Corr_xi1);
norm_Corr_xi2 = norm(pruned.Corr_yi(M_.nstatic+(1:M_.nspred),M_.nstatic+(1:M_.nspred),2) - outAndreasen.Corr_v(1:M_.nspred,1:M_.nspred,2) , Inf);
fprintf('max(sum(abs(Corr[x,x(-2)]''))): %d\n',norm_Corr_xi2);
if iorder < 3 && any([norm_E_yx norm_Var_y norm_Var_x norm_Corr_yi1 norm_Corr_yi2 norm_Corr_xi1 norm_Corr_xi2] > 1e-5)
error('Something wrong with pruned_state_space.m compared to Andreasen et al 2018 Toolbox v2 at order %d.',iorder);
end
end
skipline();
fprintf('Note that at third order, there is an error in the computation of Var_z in Andreasen et al (2018)''s toolbox, @wmutschl is in contact to clarify this.\n');
fprintf('EXAMPLE:\n')
fprintf(' Consider Var[kron(kron(xf,xf),xf)] = E[kron(kron(kron(kron(kron(xf,xf),xf),xf),xf),xf)] - E[kron(kron(xf,xf),xf)]*E[kron(kron(xf,xf),xf)].''\n');
fprintf(' Now note that xf=hx*xf(-1)+hu*u is Gaussian, that is E[kron(kron(xf,xf),xf)]=0, and Var[kron(kron(xf,xf),xf)] are the sixth-order product moments\n');
fprintf(' which can be computed using the prodmom.m function by providing E[xf*xf''] as covariance matrix.\n');
fprintf(' In order to replicate this you have to change UnconditionalMoments_3rd_Lyap.m to also output Var_z.\n')
dynare_nx = M_.nspred;
dynare_E_xf2 = pruned_state_space.order_3.Var_z(1:dynare_nx,1:dynare_nx);
dynare_E_xf6 = pruned_state_space.order_3.Var_z((end-dynare_nx^3+1):end,(end-dynare_nx^3+1):end);
dynare_E_xf6 = dynare_E_xf6(:);
Andreasen_nx = M_.nspred+M_.exo_nbr;
Andreasen_E_xf2 = outAndreasenetal.order_3.Var_z(1:Andreasen_nx,1:Andreasen_nx);
Andreasen_E_xf6 = outAndreasenetal.order_3.Var_z((end-Andreasen_nx^3+1):end,(end-Andreasen_nx^3+1):end);
Andreasen_E_xf6 = Andreasen_E_xf6(:);
fprintf('Second-order product moments of xf and u are the same:\n')
norm_E_xf2 = norm(dynare_E_xf2-Andreasen_E_xf2(1:M_.nspred,1:M_.nspred),Inf)
norm_E_uu = norm(M_.Sigma_e-Andreasen_E_xf2(M_.nspred+(1:M_.exo_nbr),M_.nspred+(1:M_.exo_nbr)),Inf)
% Compute unique sixth-order product moments of xf, i.e. unique(E[kron(kron(kron(kron(kron(xf,xf),xf),xf),xf),xf)],'stable')
dynare_nx6 = dynare_nx*(dynare_nx+1)/2*(dynare_nx+2)/3*(dynare_nx+3)/4*(dynare_nx+4)/5*(dynare_nx+5)/6;
dynare_Q6Px = Q6_plication(dynare_nx);
dynare_COMBOS6 = flipud(allVL1(dynare_nx, 6)); %all possible (unique) combinations of powers that sum up to six
dynare_true_E_xf6 = zeros(dynare_nx6,1); %only unique entries
for j6 = 1:size(dynare_COMBOS6,1)
dynare_true_E_xf6(j6) = prodmom(dynare_E_xf2, 1:dynare_nx, dynare_COMBOS6(j6,:));
end
dynare_true_E_xf6 = dynare_Q6Px*dynare_true_E_xf6; %add duplicate entries
norm_dynare_E_xf6 = norm(dynare_true_E_xf6 - dynare_E_xf6, Inf);
Andreasen_nx6 = Andreasen_nx*(Andreasen_nx+1)/2*(Andreasen_nx+2)/3*(Andreasen_nx+3)/4*(Andreasen_nx+4)/5*(Andreasen_nx+5)/6;
Andreasen_Q6Px = Q6_plication(Andreasen_nx);
Andreasen_COMBOS6 = flipud(allVL1(Andreasen_nx, 6)); %all possible (unique) combinations of powers that sum up to six
Andreasen_true_E_xf6 = zeros(Andreasen_nx6,1); %only unique entries
for j6 = 1:size(Andreasen_COMBOS6,1)
Andreasen_true_E_xf6(j6) = prodmom(Andreasen_E_xf2, 1:Andreasen_nx, Andreasen_COMBOS6(j6,:));
end
Andreasen_true_E_xf6 = Andreasen_Q6Px*Andreasen_true_E_xf6; %add duplicate entries
norm_Andreasen_E_xf6 = norm(Andreasen_true_E_xf6 - Andreasen_E_xf6, Inf);
fprintf('Sixth-order product moments of xf and u are not the same!\n');
fprintf(' Dynare maximum absolute deviations of sixth-order product moments of xf: %d\n',norm_dynare_E_xf6)
fprintf(' Andreasen et al maximum absolute deviations of sixth-order product moments of xf: %d\n',norm_Andreasen_E_xf6)
skipline();
fprintf('Note that the standard deviations are set quite high to make the numerical differences more apparent.\n');