From 5ccfdd54003331cf680b11d68ccb6cb33d3d35fc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?S=C3=A9bastien=20Villemot?= <sebastien@dynare.org>
Date: Thu, 26 Sep 2024 18:16:53 +0200
Subject: [PATCH] Linear perfect foresight solver: use sparse representation
 for the model

Ref. #1859
---
 .../linear_perfect_foresight_problem.m        | 87 ++++++-------------
 .../private/initialize_stacked_problem.m      | 78 -----------------
 .../solve_stacked_linear_problem.m            | 38 ++++----
 3 files changed, 45 insertions(+), 158 deletions(-)
 delete mode 100644 matlab/perfect-foresight-models/private/initialize_stacked_problem.m

diff --git a/matlab/perfect-foresight-models/linear_perfect_foresight_problem.m b/matlab/perfect-foresight-models/linear_perfect_foresight_problem.m
index adc8045aa6..439ea98c1f 100644
--- a/matlab/perfect-foresight-models/linear_perfect_foresight_problem.m
+++ b/matlab/perfect-foresight-models/linear_perfect_foresight_problem.m
@@ -1,22 +1,10 @@
 function [residuals,JJacobian] = linear_perfect_foresight_problem(y, dynamicjacobian, 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, jendo, jexog)
+                                                                  exo_simul, params, steady_state, ...
+                                                                  maximum_lag, T, ny)
 
 % Computes the residuals and the Jacobian matrix for a linear perfect foresight problem over T periods.
-%
-% INPUTS
-% ...
-%
-% OUTPUTS
-% ...
-%
-% ALGORITHM
-% ...
-%
-% SPECIAL REQUIREMENTS
-%   None.
 
-% Copyright © 2015-2020 Dynare Team
+% Copyright © 2015-2024 Dynare Team
 %
 % This file is part of Dynare.
 %
@@ -33,64 +21,39 @@ function [residuals,JJacobian] = linear_perfect_foresight_problem(y, dynamicjaco
 % 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);
-
-z = zeros(columns(dynamicjacobian), 1);
-
+residuals = NaN(T*ny,1);
 if nargout == 2
     iJacobian = cell(T,1);
 end
 
-i_rows = 1:ny;
-i_cols_J = i_cols;
 offset = 0;
 
-for it = maximum_lag+(1:T)
-    z(jendo) = YY(i_cols);
-    z(jexog) = transpose(exo_simul(it,:));
-    residuals(i_rows) = dynamicjacobian*z;
+for t = 1:T
+    z = [ vec(YY(:, t+(0:2))); transpose(exo_simul(t+maximum_lag, :)) ];
+    residuals(offset + (1:ny)) = dynamicjacobian*z;
+
     if nargout == 2
-        if T==1 && it==maximum_lag+1
-            [rows, cols, vals] = find(dynamicjacobian(:,i_cols_0));
-            if size(dynamicjacobian, 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(dynamicjacobian(:,i_cols_1));
-            if size(dynamicjacobian, 1) == 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(dynamicjacobian(:,i_cols_T));
-            if size(dynamicjacobian, 1) == 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];
+        if T == 1 % static model
+            [rows, cols, vals] = find(dynamicjacobian(:, ny+(1:ny)));
+        elseif t == 1
+            [rows, cols, vals] = find(dynamicjacobian(:, ny+(1:2*ny)));
+        elseif t == T
+            [rows, cols, vals] = find(dynamicjacobian(:, 1:2*ny));
+            cols = cols - ny;
         else
-            [rows,cols,vals] = find(dynamicjacobian(:,i_cols_j));
-            if size(dynamicjacobian, 1) == 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(dynamicjacobian(:, 1:3*ny));
+            cols = cols - ny;
+        end
+        if size(dynamicjacobian, 1) == 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/private/initialize_stacked_problem.m b/matlab/perfect-foresight-models/private/initialize_stacked_problem.m
deleted file mode 100644
index 8aea2e0f57..0000000000
--- a/matlab/perfect-foresight-models/private/initialize_stacked_problem.m
+++ /dev/null
@@ -1,78 +0,0 @@
-function [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_y)
-
-% Sets up the stacked perfect foresight problem for use with dynare_solve.m
-%
-% INPUTS
-% - endogenousvariables [double] N*T array, paths for the endogenous variables (initial guess).
-% - options_            [struct] contains various options.
-% - M_                  [struct] contains a description of the model.
-% - steadystate_y       [double] N*1 array, steady state for the endogenous variables.
-%
-% OUTPUTS
-% - options_            [struct] contains various options.
-% - y0                  [double] N*1 array, initial conditions for the endogenous variables
-% - yT                  [double] N*1 array, terminal conditions for the endogenous variables
-% - z                   [double] T*M array, paths for the exogenous 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_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)
-% - 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_0            [double] indices of contemporaneous variables in M_.lead_lag_incidence in dynamic
-%                                Jacobian (relevant in problems with periods=1)
-% - i_cols_J0           [double] indices of contemporaneous variables appearing in M_.lead_lag_incidence (relevant in problems with periods=1)
-% - dynamicmodel        [handle] function handle to _dynamic-file
-
-% Copyright © 2015-2024 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 <https://www.gnu.org/licenses/>.
-
-periods = options_.periods;
-
-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+periods+1);
-else
-    yT = NaN(M_.endo_nbr, 1);
-end
-z = endogenousvariables(:,M_.maximum_lag+(1:periods));
-illi = M_.lead_lag_incidence';
-[i_cols,~,i_cols_j] = find(illi(:));
-if M_.maximum_endo_lag == 0
-    i_cols = i_cols + M_.endo_nbr;
-end
-illi = illi(:,(1+M_.maximum_endo_lag):(1+M_.maximum_endo_lag+M_.maximum_endo_lead));
-[i_cols_J1,~,i_cols_1] = find(illi(:));
-i_cols_T = nonzeros(M_.lead_lag_incidence(1:(1+M_.maximum_endo_lag),:)');
-if periods==1
-    i_cols_0 = nonzeros(M_.lead_lag_incidence(1+M_.maximum_endo_lag,:)');
-    i_cols_J0 = find(M_.lead_lag_incidence(1+M_.maximum_endo_lag,:)');
-else
-    i_cols_0 = [];
-    i_cols_J0 = [];
-end
-dynamicmodel = str2func([M_.fname,'.dynamic']);
diff --git a/matlab/perfect-foresight-models/solve_stacked_linear_problem.m b/matlab/perfect-foresight-models/solve_stacked_linear_problem.m
index 3e104717a2..2e919a53e9 100644
--- a/matlab/perfect-foresight-models/solve_stacked_linear_problem.m
+++ b/matlab/perfect-foresight-models/solve_stacked_linear_problem.m
@@ -1,6 +1,6 @@
 function [endogenousvariables, success] = solve_stacked_linear_problem(endogenousvariables, exogenousvariables, steadystate_y, steadystate_x, M_, options_)
 
-% Copyright © 2015-2023 Dynare Team
+% Copyright © 2015-2024 Dynare Team
 %
 % This file is part of Dynare.
 %
@@ -17,27 +17,31 @@ function [endogenousvariables, success] = solve_stacked_linear_problem(endogenou
 % 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_y);
-
-ip = find(M_.lead_lag_incidence(1,:)');
-ic = find(M_.lead_lag_incidence(2,:)');
-in = find(M_.lead_lag_incidence(3,:)');
+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));
 
-% Evaluate the Jacobian of the dynamic model at the deterministic steady state.
-[d1,jacobian] = dynamicmodel(steadystate_y([ip; ic; in]), transpose(steadystate_x), M_.params, steadystate_y, 1);
+% Evaluate the residuals and Jacobian of the dynamic model at the deterministic steady state.
+y3n = repmat(steadystate_y, 3, 1);
+[d1, TT_order, TT] = feval([M_.fname,'.sparse.dynamic_resid'], y3n, steadystate_x', M_.params, ...
+                           steadystate_y);
+jacobian = feval([M_.fname,'.sparse.dynamic_g1'], y3n, steadystate_x', M_.params, steadystate_y, ...
+                 M_.dynamic_g1_sparse_rowval, M_.dynamic_g1_sparse_colval, ...
+                 M_.dynamic_g1_sparse_colptr, TT_order, TT);
 
 % Check that the dynamic model was evaluated at the steady state.
 if ~options_.steadystate.nocheck && max(abs(d1))>1e-12
     error('Jacobian is not evaluated at the steady state!')
 end
 
-nyp = nnz(M_.lead_lag_incidence(1,:));
-ny0 = nnz(M_.lead_lag_incidence(2,:));
-nyf = nnz(M_.lead_lag_incidence(3,:));
-nd = nyp+ny0+nyf; % size of y (first argument passed to the dynamic file).
-jexog = transpose(nd+(1:M_.exo_nbr));
-jendo = transpose(1:nd);
 z = bsxfun(@minus, z, steadystate_y);
 x = bsxfun(@minus, exogenousvariables, steadystate_x');
 
@@ -46,9 +50,7 @@ x = bsxfun(@minus, exogenousvariables, steadystate_x');
                                            options_, ...
                                            jacobian, y0-steadystate_y, yT-steadystate_y, ...
                                            x, M_.params, steadystate_y, ...
-                                           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, ...
-                                           jendo, jexog);
+                                           M_.maximum_lag, options_.periods, M_.endo_nbr);
 
 if all(imag(y)<.1*options_.dynatol.x)
     if ~isreal(y)
-- 
GitLab