function [H,G,retcode]=discretionary_policy_engine(AAlag,AA0,AAlead,BB,bigw,instr_id,beta,solve_maxit,discretion_tol,qz_criterium,H00,verbose) % Solves the discretionary problem for a model of the form: % % Loss=E_0 sum_{t=0}^{\infty} beta^t [y_t'*W*y+x_t'*Q*x_t] % subject to % AAlag*yy_{t-1}+AA0*yy_t+AAlead*yy_{t+1}+BB*e=0 % % with W the weight on the variables in vector y_t. % % The solution takes the form % y_t=H*y_{t-1}+G*e_t % where H=[H1;F1] and G=[H2;F2]. % % We use the Dennis (2007, Macroeconomic Dynamics) algorithm and so we need % to re-write the model in the form % A0*y_t=A1*y_{t-1}+A2*y_{t+1}+A3*x_t+A4*x_{t+1}+A5*e_t, with W the % weight on the y_t vector and Q the weight on the x_t vector of % instruments. % % Inputs: % AAlag [double] matrix of coefficients on lagged % variables % AA0 [double] matrix of coefficients on % contemporaneous variables % AAlead [double] matrix of coefficients on % leaded variables % BB [double] matrix of coefficients on % shocks % bigw [double] matrix of coefficients on variables in % loss/objective function; stacks [W and Q] % instr_id [double] location vector of the instruments in the yy_t vector. % beta [scalar] planner discount factor % solve_maxit [scalar] maximum number of iterations % discretion_tol [scalar] convergence criterion for solution % qz_criterium [scalar] tolerance for QZ decomposition % H00 % verbose [scalar] dummy to control verbosity % % Outputs: % H [double] (endo_nbr*endo_nbr) solution matrix for endogenous % variables, stacks [H1 and H1] % G [double] (endo_nbr*exo_nbr) solution matrix for shocks, stacks [H2 and F2] % % retcode [scalar] return code % % Algorithm: % Dennis, Richard (2007): Optimal policy in rational expectations models: new solution algorithms, % Macroeconomic Dynamics, 11, 31–55. % Copyright (C) 2007-2018 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<12 verbose=0; if nargin<11 H00=[]; if nargin<10 qz_criterium=1.000001; if nargin<9 discretion_tol=sqrt(eps); if nargin<8 solve_maxit=3000; if nargin<7 beta=.99; if nargin<6 error([mfilename,':: Insufficient number of input arguments']) elseif nargin>12 error([mfilename,':: Number of input arguments cannot exceed 12']) end end end end end end end [A0,A1,A2,A3,A4,A5,W,Q,endo_nbr,exo_nbr,aux,endo_augm_id]=GetDennisMatrices(AAlag,AA0,AAlead,BB,bigw,instr_id); % aux is a logical index of the instruments which appear with lags in the % model. Their location in the state vector is instr_id(aux); % endo_augm_id is index (not logical) of locations of the augmented vector % of non-instrumental variables AuxiliaryVariables_nbr=sum(aux); H0=zeros(endo_nbr+AuxiliaryVariables_nbr); if ~isempty(H00) H0(1:endo_nbr,1:endo_nbr)=H00; clear H00 end H10=H0(endo_augm_id,endo_augm_id); F10=H0(instr_id,endo_augm_id); iter=0; H1=H10; F1=F10; %solve equations (20) and (22) via fixed point iteration while 1 iter=iter+1; P=SylvesterDoubling(W+beta*F1'*Q*F1,beta*H1',H1,discretion_tol,solve_maxit); if any(any(isnan(P))) || any(any(isinf(P))) P=SylvesterHessenbergSchur(W+beta*F1'*Q*F1,beta*H1',H1); if any(any(isnan(P))) || any(any(isinf(P))) retcode=2; return end end D=A0-A2*H1-A4*F1; %equation (20) Dinv=inv(D); A3DPD=A3'*Dinv'*P*Dinv; F1=-(Q+A3DPD*A3)\(A3DPD*A1); %component of (26) H1=Dinv*(A1+A3*F1); %component of (27) [rcode,NQ]=CheckConvergence([H1;F1]-[H10;F10],iter,solve_maxit,discretion_tol); if rcode break else if verbose disp(NQ) end end H10=H1; F10=F1; end %check if successful retcode = 0; switch rcode case 3 % nan retcode=63; retcode(2)=10000; if verbose disp([mfilename,':: NaN elements in the solution']) end case 2% maxiter retcode = 61; if verbose disp([mfilename,':: Maximum Number of Iterations reached']) end case 1 BadEig=max(abs(eig(H1)))>qz_criterium; if BadEig retcode=62; retcode(2)=100*max(abs(eig(H1))); if verbose disp([mfilename,':: Some eigenvalues greater than qz_criterium, Model potentially unstable']) end end end if retcode(1) H=[]; G=[]; else F2=-(Q+A3DPD*A3)\(A3DPD*A5); %equation (29) H2=Dinv*(A5+A3*F2); %equation (31) H=zeros(endo_nbr+AuxiliaryVariables_nbr); G=zeros(endo_nbr+AuxiliaryVariables_nbr,exo_nbr); H(endo_augm_id,endo_augm_id)=H1; H(instr_id,endo_augm_id)=F1; G(endo_augm_id,:)=H2; G(instr_id,:)=F2; % Account for auxilliary variables H(:,instr_id(aux))=H(:,end-(AuxiliaryVariables_nbr-1:-1:0)); H=H(1:endo_nbr,1:endo_nbr); G=G(1:endo_nbr,:); end end function [rcode,NQ]=CheckConvergence(Q,iter,MaxIter,crit) NQ=max(max(abs(Q)));% norm(Q); seems too costly if isnan(NQ) rcode=3; elseif iter>MaxIter; rcode=2; elseif NQ= size(h,1) [u,gbarp] = hess(g'); [t,hbar] = schur(h); [vbar] = sylvest_private(gbarp,u'*d*t,hbar,1e-15); v = u*vbar*t'; else [u,gbar] = schur(g); [t,hbarp] = hess(h'); [vbar] = sylvest_private(hbarp,t'*d'*u,gbar,1e-15); v = u*vbar'*t'; end end function v = sylvest_private(g,d,h,tol) % % SYLVEST Solves a Sylvester equation % % solves the Sylvester equation % v = d + g v h % for v where both g and h must be upper block triangular. % The output info is zero on a successful return. % The input tol indicates when an element of g or h should be considered % zero. [m,n] = size(d); v = zeros(m,n); w = eye(m); i = 1; temp = []; %First handle the i = 1 case outside the loop if i< n if abs(h(i+1,i)) < tol v(:,i)= (w - g*h(i,i))\d(:,i); i = i+1; else A = [w-g*h(i,i) (-g*h(i+1,i));... -g*h(i,i+1) w-g*h(i+1,i+1)]; C = [d(:,i); d(:,i+1)]; X = A\C; v(:,i) = X(1:m,:); v(:,i+1) = X(m+1:2*m, :); i = i+2; end end %Handle the rest of the matrix with the possible exception of i=n while i if abs(h(i+1,i)) < tol v(:,i) = (w - g*h(i,i))\(d(:,i) + temp*h(1:b,i)); i = i+1; else A = [w - g*h(i,i) (-g*h(i+1,i)); ... -g*h(i,i+1) w - g*h(i+1,i+1)]; C = [d(:,i) + temp*h(1:b,i); ... d(:,i+1) + temp*h(1:b,i+1)]; X = A\C; v(:,i) = X(1:m,:); v(:,i+1) = X(m+1:2*m, :); i = i+2; end end %Handle the i = n case if i=n was not in a 2-2 block if i==n b = i-1; temp = [temp g*v(:,size(temp,2)+1:b)]; v(:,i) = (w-g*h(i,i))\(d(:,i) + temp*h(1:b,i)); end end