From aa263e6a2adc9414142551a01bbcc46abb6a3894 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?S=C3=A9bastien=20Villemot?= <sebastien@dynare.org>
Date: Fri, 15 Mar 2024 18:43:54 +0100
Subject: [PATCH] Perfect foresight: allow stack_solve_algo={2,3} without block
 nor bytecode

---
 doc/manual/source/the-model-file.rst          |  6 ++---
 .../perfect_foresight_solver_core.m           |  7 ++++--
 .../private/check_input_arguments.m           |  8 +++----
 matlab/perfect-foresight-models/sim1.m        | 23 +++++++++++++++----
 tests/run_block_bytecode_tests.m              |  4 ++--
 5 files changed, 30 insertions(+), 18 deletions(-)

diff --git a/doc/manual/source/the-model-file.rst b/doc/manual/source/the-model-file.rst
index a47791f929..aa9d5fd3aa 100644
--- a/doc/manual/source/the-model-file.rst
+++ b/doc/manual/source/the-model-file.rst
@@ -3706,15 +3706,13 @@ speed-up on large models.
 
                Use a Newton algorithm with a Generalized Minimal Residual
                (GMRES) solver at each iteration, applied on the stacked system
-               of all equations in all periods (requires ``bytecode`` and/or
-               ``block`` option, see :ref:`model-decl`)
+               of all equations in all periods.
 
            ``3``
 
                Use a Newton algorithm with a Stabilized Bi-Conjugate Gradient
                (BiCGStab) solver at each iteration, applied on the stacked
-               system of all equations in all periods (requires ``bytecode``
-               and/or ``block`` option, see :ref:`model-decl`).
+               system of all equations in all periods.
 
            ``4``
 
diff --git a/matlab/perfect-foresight-models/perfect_foresight_solver_core.m b/matlab/perfect-foresight-models/perfect_foresight_solver_core.m
index 8d6ec41b2f..2c7202f6a1 100644
--- a/matlab/perfect-foresight-models/perfect_foresight_solver_core.m
+++ b/matlab/perfect-foresight-models/perfect_foresight_solver_core.m
@@ -17,7 +17,7 @@ function [y, success, maxerror, iter, per_block_status] = perfect_foresight_solv
 % - iter                [integer] Number of iterations of the underlying nonlinear solver (empty for non-iterative methods)
 % - per_block_status    [struct] In the case of block decomposition, provides per-block solver status information (empty if no block decomposition)
 
-% Copyright © 2015-2023 Dynare Team
+% Copyright © 2015-2024 Dynare Team
 %
 % This file is part of Dynare.
 %
@@ -96,8 +96,11 @@ else
             [y, success] = sim1_purely_static(y, exo_simul, steady_state, M_, options_);
         else % General case
             switch options_.stack_solve_algo
-              case 0
+              case {0 2 3}
                 if options_.linear_approximation
+                    if ismember(options_.stack_solve_algo, [2 3])
+                        error('Invalid value of stack_solve_algo option!')
+                    end
                     [y, success, maxerror] = sim1_linear(y, exo_simul, steady_state, exo_steady_state, M_, options_);
                 else
                     [y, success, maxerror, iter] = sim1(y, exo_simul, steady_state, M_, options_);
diff --git a/matlab/perfect-foresight-models/private/check_input_arguments.m b/matlab/perfect-foresight-models/private/check_input_arguments.m
index ccf4a34a56..b77de81f46 100644
--- a/matlab/perfect-foresight-models/private/check_input_arguments.m
+++ b/matlab/perfect-foresight-models/private/check_input_arguments.m
@@ -7,7 +7,7 @@ function check_input_arguments(options_, M_, oo_)
 %   M_                  [structure] describing the model
 %   oo_                 [structure] storing the results
 
-% Copyright © 2015-2023 Dynare Team
+% Copyright © 2015-2024 Dynare Team
 %
 % This file is part of Dynare.
 %
@@ -28,10 +28,8 @@ if options_.stack_solve_algo < 0 || options_.stack_solve_algo > 7
     error('perfect_foresight_solver:ArgCheck','PERFECT_FORESIGHT_SOLVER: stack_solve_algo must be between 0 and 7')
 end
 
-if ~options_.block && ~options_.bytecode && options_.stack_solve_algo ~= 0 ...
-        && options_.stack_solve_algo ~= 1 && options_.stack_solve_algo ~= 6 ...
-        && options_.stack_solve_algo ~= 7
-    error('perfect_foresight_solver:ArgCheck','PERFECT_FORESIGHT_SOLVER: you must use stack_solve_algo={0,1,6,7} when not using block nor bytecode option')
+if ~options_.block && ~options_.bytecode && ~ismember(options_.stack_solve_algo, [0:3 6 7])
+    error('perfect_foresight_solver:ArgCheck','PERFECT_FORESIGHT_SOLVER: you must use stack_solve_algo={0,1,2,3,6,7} when not using block nor bytecode option')
 end
 
 if options_.block && ~options_.bytecode && options_.stack_solve_algo == 5
diff --git a/matlab/perfect-foresight-models/sim1.m b/matlab/perfect-foresight-models/sim1.m
index ad7eb61dc3..6fae2f151e 100644
--- a/matlab/perfect-foresight-models/sim1.m
+++ b/matlab/perfect-foresight-models/sim1.m
@@ -16,7 +16,7 @@ function [endogenousvariables, success, err, iter] = sim1(endogenousvariables, e
 %   - err                 [double] ∞-norm of the residual
 %   - iter                [integer] Number of iterations
 
-% Copyright © 1996-2023 Dynare Team
+% Copyright © 1996-2024 Dynare Team
 %
 % This file is part of Dynare.
 %
@@ -128,7 +128,7 @@ for iter = 1:options_.simul.maxit
     if options_.simul.robust_lin_solve
         dy = -lin_solve_robust(A, res, verbose, options_);
     else
-        dy = -lin_solve(A, res, verbose);
+        dy = -lin_solve(A, res, verbose, options_);
     end
     if any(isnan(dy)) || any(isinf(dy))
         if verbose
@@ -180,13 +180,26 @@ if verbose
     skipline();
 end
 
-function x = lin_solve(A, b, verbose)
+function x = lin_solve(A, b, verbose, options_)
 if norm(b) < sqrt(eps) % then x = 0 is a solution
     x = 0;
     return
 end
 
-x = A\b;
+if options_.stack_solve_algo == 0
+    x = A\b;
+else
+    ilu_setup.type = 'ilutp';
+    ilu_setup.droptol = 1e-10;
+    [L1, U1] = ilu(A, ilu_setup);
+    if options_.stack_solve_algo == 2
+        x = gmres(A, b, [], [], [], L1, U1);
+    elseif options_.stack_solve_algo == 3
+        x = bicgstab(A, b, [], [], L1, U1);
+    else
+        error('sim1: invalid value for options_.stack_solve_algo')
+    end
+end
 x(~isfinite(x)) = 0;
 relres = norm(b - A*x) / norm(b);
 if relres > 1e-6 && verbose
@@ -343,4 +356,4 @@ if rank_jacob < size(jacob,1)
             fprintf('Equation %5u, period %5u\n',equation(ii),period(ii))
         end
     end
-end
\ No newline at end of file
+end
diff --git a/tests/run_block_bytecode_tests.m b/tests/run_block_bytecode_tests.m
index 820db3f843..e9aac91947 100644
--- a/tests/run_block_bytecode_tests.m
+++ b/tests/run_block_bytecode_tests.m
@@ -1,4 +1,4 @@
-% Copyright © 2011-2023 Dynare Team
+% Copyright © 2011-2024 Dynare Team
 %
 % This file is part of Dynare.
 %
@@ -42,7 +42,7 @@ for blockFlag = 0:1
         default_stack_solve_algo = 0;
         if ~blockFlag && storageFlag ~= 2
             solve_algos = [1:4 9];
-            stack_solve_algos = [0 1 6];
+            stack_solve_algos = [0:3 6];
         elseif blockFlag && storageFlag ~= 2
             solve_algos = [1:4 6:9];
             stack_solve_algos = [0:4 6];
-- 
GitLab