diff --git a/matlab/dyn_risky_steadystate_solver.m b/matlab/dyn_risky_steadystate_solver.m index 04c50b84b234287a994130c8e792906f8119f810..fc559af49bff666afacef8668b7afd3e04e7922b 100644 --- a/matlab/dyn_risky_steadystate_solver.m +++ b/matlab/dyn_risky_steadystate_solver.m @@ -346,8 +346,12 @@ if nargout > 1 nu2 = exo_nbr*(exo_nbr+1)/2; nu3 = exo_nbr*(exo_nbr+1)*(exo_nbr+2)/3; M_np.NZZDerivatives = [nnz(d1_np); nnz(d2_np); nnz(d3_np)]; - [err,g_0, g_1, g_2, g_3] = k_order_perturbation(dr_np,M_np,options,d1_np,d2_np,d3_np); + [err, dynpp_derivs] = k_order_perturbation(dr_np,M_np,options,d1_np,d2_np,d3_np); mexErrCheck('k_order_perturbation', err); + g_0 = dynpp_derivs.g_0; + g_1 = dynpp_derivs.g_1; + g_2 = dynpp_derivs.g_2; + g_3 = dynpp_derivs.g_3; gu1 = g_1(i_fwrd_g,end-exo_nbr+1:end); ghuu = unfold2(g_2(:,end-nu2+1:end),exo_nbr); @@ -547,4 +551,4 @@ for i=1:n m2 = m2 - (n-i+1); kk = kk + nx*nx; end -end \ No newline at end of file +end diff --git a/matlab/k_order_pert.m b/matlab/k_order_pert.m index 892cec68b1ae17f12102e6779a0301133b0b6705..3a903b5345f740ab5ac01414839f85548e828647 100644 --- a/matlab/k_order_pert.m +++ b/matlab/k_order_pert.m @@ -28,9 +28,6 @@ M.var_order_endo_names = char(M.var_order_endo_names); M.exo_names = char(M.exo_names); order = options.order; -endo_nbr = M.endo_nbr; -exo_nbr = M.exo_nbr; -nspred = M.nspred; if order>1 && options.loglinear error('The loglinear-option currently only works at order 1') @@ -40,106 +37,49 @@ if M.maximum_endo_lead == 0 && order>1 'backward models']) end -switch(order) - case 1 - [err, g_1] = k_order_perturbation(dr,M,options); - if err - info(1)=9; - return - end - dr.g_1 = g_1; - case 2 - [err, g_0, g_1, g_2] = k_order_perturbation(dr,M,options); - if err - info(1)=9; - return - end - dr.g_0 = g_0; - dr.g_1 = g_1; - dr.g_2 = g_2; - case 3 - if options.pruning - [err, g_0, g_1, g_2, g_3, derivs] = k_order_perturbation(dr,M,options); - if err - info(1)=9; - return - end - else - [err, g_0, g_1, g_2, g_3] = k_order_perturbation(dr,M,options); - if err - info(1)=9; - return - end - end - dr.g_0 = g_0; - dr.g_1 = g_1; - dr.g_2 = g_2; - dr.g_3 = g_3; - otherwise +if order > 3 error('order > 3 isn''t implemented') end -% Now fill in dr.ghx, dr.ghu... +[err, dynpp_derivs, dyn_derivs] = k_order_perturbation(dr,M,options); +if err + info(1)=9; + return +end + +% Copy Dynare++ tensors + +for i = 0:order + gname = [ 'g_' num2str(i) ]; + dr.(gname) = dynpp_derivs.(gname); +end -if options.pruning && order == 3 - dr.ghx = derivs.gy; - dr.ghu = derivs.gu; - dr.ghxx = derivs.gyy; - dr.ghxu = derivs.gyu; - dr.ghuu = derivs.guu; - dr.ghs2 = derivs.gss; - dr.ghxxx = derivs.gyyy; - dr.ghxxu = derivs.gyyu; - dr.ghxuu = derivs.gyuu; - dr.ghuuu = derivs.guuu; - dr.ghxss = derivs.gyss; - dr.ghuss = derivs.guss; -else - nspred = M.nspred; +% Fill equivalent Dynare matrices (up to 3rd order) - dr.ghx = dr.g_1(:,1:nspred); - dr.ghu = dr.g_1(:,nspred+1:end); +dr.ghx = dyn_derivs.gy; +dr.ghu = dyn_derivs.gu; - if options.loglinear - k = find(dr.kstate(:,2) <= M.maximum_endo_lag+1); - klag = dr.kstate(k,[1 2]); - k1 = dr.order_var; - dr.ghx = repmat(1./dr.ys(k1),1,size(dr.ghx,2)).*dr.ghx.* ... - repmat(dr.ys(k1(klag(:,1)))',size(dr.ghx,1),1); - dr.ghu = repmat(1./dr.ys(k1),1,size(dr.ghu,2)).*dr.ghu; - end +if options.loglinear + k = find(dr.kstate(:,2) <= M.maximum_endo_lag+1); + klag = dr.kstate(k,[1 2]); + k1 = dr.order_var; + dr.ghx = repmat(1./dr.ys(k1),1,size(dr.ghx,2)).*dr.ghx.* ... + repmat(dr.ys(k1(klag(:,1)))',size(dr.ghx,1),1); + dr.ghu = repmat(1./dr.ys(k1),1,size(dr.ghu,2)).*dr.ghu; +end + +if order >= 2 + dr.ghxx = dyn_derivs.gyy; + dr.ghxu = dyn_derivs.gyu; + dr.ghuu = dyn_derivs.guu; + dr.ghs2 = dyn_derivs.gss; +end - if order > 1 - dr.ghs2 = 2*g_0; - s0 = 0; - s1 = 0; - ghxx=zeros(endo_nbr, nspred^2); - ghxu=zeros(endo_nbr, nspred*exo_nbr); - ghuu=zeros(endo_nbr, exo_nbr^2); - for i=1:size(g_2,2) - if s0 < nspred && s1 < nspred - ghxx(:,s0*nspred+s1+1) = 2*g_2(:,i); - if s1 > s0 - ghxx(:,s1*nspred+s0+1) = 2*g_2(:,i); - end - elseif s0 < nspred && s1 < nspred+exo_nbr - ghxu(:,(s0*exo_nbr+s1-nspred+1)) = 2*g_2(:,i); - elseif s0 < nspred+exo_nbr && s1 < nspred+exo_nbr - ghuu(:,(s0-nspred)*exo_nbr+s1-nspred +1) = 2*g_2(:,i); - if s1 > s0 - ghuu(:,(s1-nspred)*exo_nbr+s0-nspred+1) = 2*g_2(:,i); - end - else - error('dr1:k_order_perturbation:g_2','Unaccounted columns in g_2'); - end - s1 = s1+1; - if s1 == nspred+exo_nbr - s0 = s0+1; - s1 = s0; - end - end % for loop - dr.ghxx = ghxx; - dr.ghxu = ghxu; - dr.ghuu = ghuu; - end +if order >= 3 + dr.ghxxx = dyn_derivs.gyyy; + dr.ghxxu = dyn_derivs.gyyu; + dr.ghxuu = dyn_derivs.gyuu; + dr.ghuuu = dyn_derivs.guuu; + dr.ghxss = dyn_derivs.gyss; + dr.ghuss = dyn_derivs.guss; end diff --git a/matlab/mex/k_order_perturbation.m b/matlab/mex/k_order_perturbation.m index 7c10d0efdf86ac44d765b331cf6fc9a86d81f4b7..2f4b1eec96723d045e5b688d6571415b91a2bc81 100644 --- a/matlab/mex/k_order_perturbation.m +++ b/matlab/mex/k_order_perturbation.m @@ -1,54 +1,31 @@ -% [err, g_0, g_1, g_2, g_3, derivs] = k_order_perturbation(dr,DynareModel,DynareOptions) +% [err, dynpp_derivs, dyn_derivs] = k_order_perturbation(dr,DynareModel,DynareOptions,g1,g2,g3) % computes a k_order_petrubation solution for k=1,2,3 % % INPUTS % dr: struct describing the reduced form solution of the model. % DynareModel: struct jobs's parameters % DynareOptions: struct job's options +% g1: matrix First order derivatives of the model (optional) +% g2: matrix Second order derivatives of the model (optional) +% g3: matrix Third order derivatives of the model (optional) % % OUTPUTS -% err: double err code (currently unused) -% g_0: vector dynare++ output. Constant effect of future volatility (in -% dr.order_var order) on the decision rule -% (=ghs2/2). Contains zero when order of -% approximation is 1. -% g_1: matrix dynare++ output. First order Taylor coefficients -% of decision rule. When order of approximation -% is 3, the. Contains both the effect of -% state endogenous variable and shocks. The rows -% are in dr.order_var order. The columns are in -% dr.order_var order of state endogenous -% variables and shocks -% g_2: matrix dynare++ output. Second order Taylor coefficients of decision -% rule. Contains both the effect of state endogenous -% variable and shocks. The rows are in dr.order_var -% order. Each row corresponds to the vectorized -% version of the lower triangle of the Hessian -% matrix. The Taylor coefficient (1/2) is -% included. The columns of the Hessian matrix are in -% dr.order_var order of state endogenous variables -% and shocks -% g_3: matrix dynare++ output. Third order Taylor coefficients of decision -% rule. Contains both the effect of state endogenous -% variable and shocks. The rows are in dr.order_var -% order. Each row corresponds to the vectorized -% version of the 3rd order derivatives tensor where each -% combination of variables appears only once. -% The Taylor coefficient (1/6) is -% included. Inside the tensor, the variables are in -% dr.order_var order of state endogenous variables -% and shocks -% derivs struct contains the original derivatives of the -% decision function (ghx, ghu, ghxx, ghxu, ghuu, -% ghs2, ghxxx, ghxxu, ghxuu,ghuuu, ghxss, ghuss), -% keeping the effect of future volatility -% separate (in ghs2, ghxss and ghuss). The -% derivatives matrices contain full versions of -% the Hessian matrices and 3rd order -% tensor. Symmetric derivatives are repeated. The -% Taylor coefficients (1/2 and 1/6) aren't +% err: double error code +% dynpp_derivs struct Derivatives of the decision rule in Dynare++ format. +% The tensors are folded and the Taylor coefficients (1/n!) are % included. -% k_order_peturbation is a compiled MEX function. It's source code is in +% Fieldnames are g_0, g_1, g_2, … +% dyn_derivs struct Derivatives of the decision rule in Dynare format, up to third order. +% Matrices are dense and unfolded. The Taylor coefficients (1/2 +% and 1/6) aren't included. +% The derivatives w.r.t. different categories of variables +% are separated. +% Fieldnames are: +% + gy, gu +% + if order ≥ 2: gyy, gyu, guu, gss +% + if order ≥ 3: gyyy, gyyu, gyuu, guuu, gyss, guss +% +% k_order_perturbation is a compiled MEX function. It's source code is in % dynare/mex/sources/k_order_perturbation.cc and it uses code provided by % dynare++ diff --git a/mex/sources/k_order_perturbation/k_order_perturbation.cc b/mex/sources/k_order_perturbation/k_order_perturbation.cc index 27da80e9ab6e9f1b59a419a6e5fc6c29e5a8ff70..f518895eba0c0fdfeafdefd90dbb94a77ede38cc 100644 --- a/mex/sources/k_order_perturbation/k_order_perturbation.cc +++ b/mex/sources/k_order_perturbation/k_order_perturbation.cc @@ -39,6 +39,13 @@ #include "dynmex.h" +/* Vector for storing field names like “g_0”, “g_1”, … + A static structure is needed since MATLAB apparently does not create its own + copy of the strings (contrary to what is said at: + https://fr.mathworks.com/matlabcentral/answers/315937-mxcreatestructarray-and-mxcreatestructmatrix-field-name-memory-management + ) */ +std::vector<std::string> g_fieldnames; + /* Convert MATLAB Dynare endo and exo names array to a vector<string> array of string pointers. MATLAB “mx” function returns a long string concatenated by columns rather than rows hence a rather low level approach is needed. */ @@ -57,7 +64,7 @@ DynareMxArrayToString(const mxArray *mxFldp, int len, int width, std::vector<std } void -copy_derivatives(mxArray *destin, const Symmetry &sym, const FGSContainer &derivs, const std::string &fieldname) +copy_derivatives(mxArray *destin, const Symmetry &sym, const FGSContainer &derivs, const char *fieldname) { const FGSTensor &x = derivs.get(sym); auto x_unfolded = x.unfold(); @@ -65,7 +72,7 @@ copy_derivatives(mxArray *destin, const Symmetry &sym, const FGSContainer &deriv int m = x_unfolded->numCols(); mxArray *tmp = mxCreateDoubleMatrix(n, m, mxREAL); std::copy_n(x_unfolded->getData().base(), n*m, mxGetPr(tmp)); - mxSetField(destin, 0, fieldname.c_str(), tmp); + mxSetField(destin, 0, fieldname, tmp); } extern "C" { @@ -224,64 +231,57 @@ extern "C" { // run stochastic steady app.walkStochSteady(); - /* Write derivative outputs into memory map */ - std::map<std::string, ConstTwoDMatrix> mm; - app.getFoldDecisionRule().writeMMap(mm, ""); + const FoldDecisionRule &fdr = app.getFoldDecisionRule(); - // get latest ysteady - ySteady = dynare.getSteady(); + // Add possibly missing field names + for (int i = static_cast<int>(g_fieldnames.size()); i <= kOrder; i++) + g_fieldnames.emplace_back("g_" + std::to_string(i)); + // Create structure for storing derivatives in Dynare++ format + const char *g_fieldnames_c[kOrder+1]; + for (int i = 0; i <= kOrder; i++) + g_fieldnames_c[i] = g_fieldnames[i].c_str(); + plhs[1] = mxCreateStructMatrix(1, 1, kOrder+1, g_fieldnames_c); - if (kOrder == 1) + // Fill that structure + for (int i = 0; i <= kOrder; i++) { - /* Set the output pointer to the output matrix ysteady. */ - auto cit = mm.begin(); - ++cit; - plhs[1] = mxCreateDoubleMatrix(cit->second.numRows(), cit->second.numCols(), mxREAL); - - // Copy Dynare++ matrix into MATLAB matrix - const ConstVector &vec = cit->second.getData(); + const FFSTensor &t = fdr.get(Symmetry{i}); + mxArray *tmp = mxCreateDoubleMatrix(t.numRows(), t.numCols(), mxREAL); + const ConstVector &vec = t.getData(); assert(vec.skip() == 1); - std::copy_n(vec.base(), vec.length(), mxGetPr(plhs[1])); + std::copy_n(vec.base(), vec.length(), mxGetPr(tmp)); + mxSetField(plhs[1], 0, ("g_" + std::to_string(i)).c_str(), tmp); } - if (kOrder >= 2) + + if (nlhs > 2) { - int ii = 1; - for (auto cit = mm.begin(); cit != mm.end() && ii < nlhs; ++cit) + /* Return as 3rd argument a struct containing derivatives in Dynare + format (unfolded matrices, without Taylor coefficient) up to 3rd + order */ + const FGSContainer &derivs = app.get_rule_ders(); + + size_t nfields = (kOrder == 1 ? 2 : (kOrder == 2 ? 6 : 12)); + const char *c_fieldnames[] = { "gy", "gu", "gyy", "gyu", "guu", "gss", + "gyyy", "gyyu", "gyuu", "guuu", "gyss", "guss" }; + plhs[2] = mxCreateStructMatrix(1, 1, nfields, c_fieldnames); + + copy_derivatives(plhs[2], Symmetry{1, 0, 0, 0}, derivs, "gy"); + copy_derivatives(plhs[2], Symmetry{0, 1, 0, 0}, derivs, "gu"); + if (kOrder >= 2) { - plhs[ii] = mxCreateDoubleMatrix(cit->second.numRows(), cit->second.numCols(), mxREAL); - - // Copy Dynare++ matrix into MATLAB matrix - const ConstVector &vec = cit->second.getData(); - assert(vec.skip() == 1); - std::copy_n(vec.base(), vec.length(), mxGetPr(plhs[ii])); - - ++ii; - + copy_derivatives(plhs[2], Symmetry{2, 0, 0, 0}, derivs, "gyy"); + copy_derivatives(plhs[2], Symmetry{0, 2, 0, 0}, derivs, "guu"); + copy_derivatives(plhs[2], Symmetry{1, 1, 0, 0}, derivs, "gyu"); + copy_derivatives(plhs[2], Symmetry{0, 0, 0, 2}, derivs, "gss"); } - if (kOrder == 3 && nlhs > 5) + if (kOrder >= 3) { - /* Return as 5th argument a struct containing *unfolded* matrices - for 3rd order decision rule */ - const FGSContainer &derivs = app.get_rule_ders(); - const std::string fieldnames[] = {"gy", "gu", "gyy", "gyu", "guu", "gss", - "gyyy", "gyyu", "gyuu", "guuu", "gyss", "guss"}; - // creates the char** expected by mxCreateStructMatrix() - const char *c_fieldnames[12]; - for (int i = 0; i < 12; ++i) - c_fieldnames[i] = fieldnames[i].c_str(); - plhs[ii] = mxCreateStructMatrix(1, 1, 12, c_fieldnames); - copy_derivatives(plhs[ii], Symmetry{1, 0, 0, 0}, derivs, "gy"); - copy_derivatives(plhs[ii], Symmetry{0, 1, 0, 0}, derivs, "gu"); - copy_derivatives(plhs[ii], Symmetry{2, 0, 0, 0}, derivs, "gyy"); - copy_derivatives(plhs[ii], Symmetry{0, 2, 0, 0}, derivs, "guu"); - copy_derivatives(plhs[ii], Symmetry{1, 1, 0, 0}, derivs, "gyu"); - copy_derivatives(plhs[ii], Symmetry{0, 0, 0, 2}, derivs, "gss"); - copy_derivatives(plhs[ii], Symmetry{3, 0, 0, 0}, derivs, "gyyy"); - copy_derivatives(plhs[ii], Symmetry{0, 3, 0, 0}, derivs, "guuu"); - copy_derivatives(plhs[ii], Symmetry{2, 1, 0, 0}, derivs, "gyyu"); - copy_derivatives(plhs[ii], Symmetry{1, 2, 0, 0}, derivs, "gyuu"); - copy_derivatives(plhs[ii], Symmetry{1, 0, 0, 2}, derivs, "gyss"); - copy_derivatives(plhs[ii], Symmetry{0, 1, 0, 2}, derivs, "guss"); + copy_derivatives(plhs[2], Symmetry{3, 0, 0, 0}, derivs, "gyyy"); + copy_derivatives(plhs[2], Symmetry{0, 3, 0, 0}, derivs, "guuu"); + copy_derivatives(plhs[2], Symmetry{2, 1, 0, 0}, derivs, "gyyu"); + copy_derivatives(plhs[2], Symmetry{1, 2, 0, 0}, derivs, "gyuu"); + copy_derivatives(plhs[2], Symmetry{1, 0, 0, 2}, derivs, "gyss"); + copy_derivatives(plhs[2], Symmetry{0, 1, 0, 2}, derivs, "guss"); } } }