From a17c3f10575f0ab8904ab3a53bb6c13d0052cfd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Villemot?= <sebastien@dynare.org> Date: Thu, 26 Sep 2024 17:20:13 +0200 Subject: [PATCH] MCP solver: use sparse representation for the model Ref. #1859 --- .../perfect_foresight_mcp_problem.m | 151 +++++++----------- .../solve_stacked_problem.m | 29 ++-- 2 files changed, 81 insertions(+), 99 deletions(-) diff --git a/matlab/perfect-foresight-models/perfect_foresight_mcp_problem.m b/matlab/perfect-foresight-models/perfect_foresight_mcp_problem.m index 13bb412050..d6bbda7c7e 100644 --- a/matlab/perfect-foresight-models/perfect_foresight_mcp_problem.m +++ b/matlab/perfect-foresight-models/perfect_foresight_mcp_problem.m @@ -1,50 +1,43 @@ -function [residuals,JJacobian] = perfect_foresight_mcp_problem(y, dynamic_function, Y0, YT, ... - exo_simul, params, steady_state, ... - maximum_lag, T, ny, i_cols, ... - i_cols_J1, i_cols_1, i_cols_T, ... - i_cols_j, i_cols_0,i_cols_J0, eq_index) -% function [residuals,JJacobian] = perfect_foresight_mcp_problem(y, dynamic_function, Y0, YT, ... -% exo_simul, params, steady_state, ... -% maximum_lag, T, ny, i_cols, ... -% i_cols_J1, i_cols_1, i_cols_T, ... -% i_cols_j,eq_index) +function [residuals,JJacobian] = perfect_foresight_mcp_problem(y, dynamic_resid_function, ... + dynamic_g1_function, Y0, YT, ... + exo_simul, params, steady_state, ... + dynamic_g1_sparse_rowval, ... + dynamic_g1_sparse_colval, ... + dynamic_g1_sparse_colptr, ... + maximum_lag, T, ny, eq_index) +% function [residuals,JJacobian] = perfect_foresight_mcp_problem(y, dynamic_resid_function, ... +% dynamic_g1_function, Y0, YT, ... +% exo_simul, params, steady_state, ... +% dynamic_g1_sparse_rowval, ... +% dynamic_g1_sparse_colval, ... +% dynamic_g1_sparse_colptr, ... +% maximum_lag, T, ny, eq_index) % Computes the residuals and the Jacobian matrix for a perfect foresight problem over T periods % in a mixed complementarity problem context % % INPUTS -% y [double] N*1 array, terminal conditions for the endogenous variables -% dynamic_function [handle] function handle to _dynamic-file -% Y0 [double] N*1 array, initial conditions for the endogenous variables -% YT [double] N*1 array, terminal conditions for the endogenous variables -% exo_simul [double] nperiods*M_.exo_nbr matrix of exogenous variables (in declaration order) -% for all simulation periods -% params [double] nparams*1 array, parameter values -% steady_state [double] endo_nbr*1 vector of steady state values -% maximum_lag [scalar] maximum lag present in the model -% T [scalar] number of simulation periods -% ny [scalar] number of endogenous variables -% i_cols [double] indices of variables appearing in M_.lead_lag_incidence -% and that need to be passed to _dynamic-file -% i_cols_J1 [double] indices of contemporaneous and forward looking variables -% appearing in M_.lead_lag_incidence -% i_cols_1 [double] indices of contemporaneous and forward looking variables in -% M_.lead_lag_incidence in dynamic Jacobian (relevant in first period) -% i_cols_T [double] columns of dynamic Jacobian related to contemporaneous and backward-looking -% variables (relevant in last period) -% i_cols_j [double] indices of variables in M_.lead_lag_incidence -% in dynamic Jacobian (relevant in intermediate periods) -% eq_index [double] N*1 array, index vector describing residual mapping resulting -% from complementarity setup +% y [double] (ny*T)*1 array, path for the endogenous variables +% dynamic_resid_function [handle] function handle to dynamic residuals +% dynamic_g1_function [handle] function handle to dynamic Jacobian +% Y0 [double] ny*1 array, initial conditions for the endogenous variables +% YT [double] ny*1 array, terminal conditions for the endogenous variables +% exo_simul [double] (max_lag+nperiods+max_lead)*M_.exo_nbr matrix of exogenous +% variables (in declaration order) for all simulation periods +% params [double] nparams*1 array, parameter values +% steady_state [double] ny*1 vector of steady state values +% dynamic_g1_sparse_rowval [integer vector] eponymous field in M_ +% dynamic_g1_sparse_colval [integer vector] eponymous field in M_ +% dynamic_g1_sparse_colptr [integer vector] eponymous field in M_ +% maximum_lag [scalar] maximum lag present in the model +% T [scalar] number of simulation periods +% ny [scalar] number of endogenous variables +% eq_index [double] ny*1 array, index vector describing residual mapping resulting +% from complementarity setup % OUTPUTS -% residuals [double] (N*T)*1 array, residuals of the stacked problem -% JJacobian [double] (N*T)*(N*T) array, Jacobian of the stacked problem -% ALGORITHM -% None -% -% SPECIAL REQUIREMENTS -% None. +% residuals [double] (ny*T)*1 array, residuals of the stacked problem +% JJacobian [double] (ny*T)*(ny*T) array, Jacobian of the stacked problem -% Copyright © 1996-2020 Dynare Team +% Copyright © 1996-2024 Dynare Team % % This file is part of Dynare. % @@ -61,65 +54,43 @@ function [residuals,JJacobian] = perfect_foresight_mcp_problem(y, dynamic_functi % You should have received a copy of the GNU General Public License % along with Dynare. If not, see <https://www.gnu.org/licenses/>. +YY = reshape([Y0; y; YT], ny, T+2); -YY = [Y0; y; YT]; - -residuals = zeros(T*ny,1); +residuals = NaN(T*ny,1); if nargout == 2 iJacobian = cell(T,1); end -i_rows = 1:ny; offset = 0; -i_cols_J = i_cols; -for it = maximum_lag+(1:T) - if nargout == 1 - res = dynamic_function(YY(i_cols),exo_simul, params, ... - steady_state,it); - residuals(i_rows) = res(eq_index); - elseif nargout == 2 - [res,jacobian] = dynamic_function(YY(i_cols),exo_simul, params, steady_state,it); - residuals(i_rows) = res(eq_index); - if T==1 && it==maximum_lag+1 - [rows, cols, vals] = find(jacobian(eq_index,i_cols_0)); - if size(jacobian, 1) == 1 % find() will return row vectors in this case - rows = rows'; - cols = cols'; - vals = vals'; - end - iJacobian{1} = [rows, i_cols_J0(cols), vals]; - elseif it == maximum_lag+1 - [rows,cols,vals] = find(jacobian(eq_index,i_cols_1)); - if numel(eq_index) == 1 % find() will return row vectors in this case - rows = rows'; - cols = cols'; - vals = vals'; - end - iJacobian{1} = [offset+rows, i_cols_J1(cols), vals]; - elseif it == maximum_lag+T - [rows,cols,vals] = find(jacobian(eq_index,i_cols_T)); - if numel(eq_index) == 1 % find() will return row vectors in this case - rows = rows'; - cols = cols'; - vals = vals'; - end - iJacobian{T} = [offset+rows, i_cols_J(i_cols_T(cols)), vals]; +for t = 1:T + y3n = YY(:, t+(0:2)); + x = exo_simul(t+maximum_lag, :); + + [res, TT_order, TT] = dynamic_resid_function(y3n, x, params, steady_state); + residuals(offset + (1:ny)) = res(eq_index); + + if nargout == 2 + jacobian = dynamic_g1_function(y3n, x, params, steady_state, dynamic_g1_sparse_rowval, dynamic_g1_sparse_colval, dynamic_g1_sparse_colptr, TT_order, TT); + if T == 1 % static model + [rows, cols, vals] = find(jacobian(eq_index, ny+(1:ny))); + elseif t == 1 + [rows, cols, vals] = find(jacobian(eq_index, ny+(1:2*ny))); + elseif t == T + [rows, cols, vals] = find(jacobian(eq_index, 1:2*ny)); + cols = cols - ny; else - [rows,cols,vals] = find(jacobian(eq_index,i_cols_j)); - if numel(eq_index) == 1 % find() will return row vectors in this case - rows = rows'; - cols = cols'; - vals = vals'; - end - iJacobian{it-maximum_lag} = [offset+rows, i_cols_J(cols), vals]; - i_cols_J = i_cols_J + ny; + [rows, cols, vals] = find(jacobian(eq_index, 1:3*ny)); + cols = cols - ny; + end + if numel(eq_index) == 1 % find() will return row vectors in this case + rows = rows'; + cols = cols'; + vals = vals'; end - offset = offset + ny; + iJacobian{t} = [offset+rows, offset+cols, vals]; end - - i_rows = i_rows + ny; - i_cols = i_cols + ny; + offset = offset + ny; end if nargout == 2 diff --git a/matlab/perfect-foresight-models/solve_stacked_problem.m b/matlab/perfect-foresight-models/solve_stacked_problem.m index cd5996343e..4ec67beacf 100644 --- a/matlab/perfect-foresight-models/solve_stacked_problem.m +++ b/matlab/perfect-foresight-models/solve_stacked_problem.m @@ -31,8 +31,17 @@ function [endogenousvariables, success, maxerror] = solve_stacked_problem(endoge % You should have received a copy of the GNU General Public License % along with Dynare. If not, see <https://www.gnu.org/licenses/>. -[options_, y0, yT, z, i_cols, i_cols_J1, i_cols_T, i_cols_j, i_cols_1, i_cols_0, i_cols_J0, dynamicmodel] = ... - initialize_stacked_problem(endogenousvariables, options_, M_, steadystate); +if M_.maximum_lag > 0 + y0 = endogenousvariables(:, M_.maximum_lag); +else + y0 = NaN(M_.endo_nbr, 1); +end +if M_.maximum_lead > 0 + yT = endogenousvariables(:, M_.maximum_lag+options_.periods+1); +else + yT = NaN(M_.endo_nbr, 1); +end +z = endogenousvariables(:,M_.maximum_lag+(1:options_.periods)); if (options_.solve_algo == 10 || options_.solve_algo == 11)% mixed complementarity problem [lb, ub] = feval(sprintf('%s.dynamic_complementarity_conditions', M_.fname), M_.params); @@ -47,14 +56,16 @@ if (options_.solve_algo == 10 || options_.solve_algo == 11)% mixed complementari options_.mcppath.lb = repmat(lb,options_.periods,1); options_.mcppath.ub = repmat(ub,options_.periods,1); end + dynamic_resid_function = str2func([M_.fname,'.sparse.dynamic_resid']); + dynamic_g1_function = str2func([M_.fname,'.sparse.dynamic_g1']); [y, check, res, ~, errorcode] = dynare_solve(@perfect_foresight_mcp_problem, z(:), ... - options_.simul.maxit, options_.dynatol.f, options_.dynatol.x, ... - options_, ... - dynamicmodel, y0, yT, ... - exogenousvariables, M_.params, steadystate, ... - M_.maximum_lag, options_.periods, M_.endo_nbr, i_cols, ... - i_cols_J1, i_cols_1, i_cols_T, i_cols_j, i_cols_0, i_cols_J0, ... - M_.dynamic_mcp_equations_reordering); + options_.simul.maxit, options_.dynatol.f, options_.dynatol.x, ... + options_, ... + dynamic_resid_function, dynamic_g1_function, y0, yT, ... + exogenousvariables, M_.params, steadystate, ... + M_.dynamic_g1_sparse_rowval, M_.dynamic_g1_sparse_colval, M_.dynamic_g1_sparse_colptr, ... + M_.maximum_lag, options_.periods, M_.endo_nbr, ... + M_.dynamic_mcp_equations_reordering); eq_to_ignore=find(isfinite(lb) | isfinite(ub)); else -- GitLab