diff --git a/matlab/perfect-foresight-models/back_subst_lbj.m b/matlab/perfect-foresight-models/back_subst_lbj.m
new file mode 100644
index 0000000000000000000000000000000000000000..9a9a5cc8346194eaea682d8796896bee0a65b43d
--- /dev/null
+++ b/matlab/perfect-foresight-models/back_subst_lbj.m
@@ -0,0 +1,43 @@
+function dy = back_subst_lbj(c,ny,iyf,periods)
+% Computes backward substitution in LBJ
+%
+% INPUTS
+%    c:              matrix containing the D and d for Sébastien’s presentation
+%    ny:             number of endogenous variables
+%    iyf:            indices of forward variables inside the list of all endogenous variables
+%    periods:        number of simulation periods
+%
+% OUTPUTS
+%    dy:             vector of backsubstitution results
+
+% Copyright © 2023 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/>.
+
+ir = (periods-2)*ny+(1:ny);
+irf = iyf+(periods-1)*ny;
+icf = 1:size(iyf,2);
+nrc = length(iyf)+1;
+
+for i = 2:periods
+    c(ir,nrc) = c(ir,nrc)-c(ir,icf)*c(irf,nrc);
+    ir = ir-ny;
+    irf = irf-ny;
+end
+
+dy = reshape(c(:,nrc), ny, periods);
+
+end
diff --git a/matlab/perfect-foresight-models/sim1_lbj.m b/matlab/perfect-foresight-models/sim1_lbj.m
index 63138e22b242b5ad85763ead161d59c9ef245c26..f48bd315e46f932f92127b419f8e31ae0032ad07 100644
--- a/matlab/perfect-foresight-models/sim1_lbj.m
+++ b/matlab/perfect-foresight-models/sim1_lbj.m
@@ -94,8 +94,7 @@ for iter = 1:options_.simul.maxit
         icp = icp + ny;
         c (ic,:) = jacobian(:,is)\jacobian(:,isf1);
     end
-    c = bksup1(c, ny, nrc, iyf, options_.periods);
-    c = reshape(c, ny, options_.periods);
+    c = back_subst_lbj(c, ny, iyf, options_.periods);
     endogenousvariables(:,M_.maximum_lag+(1:options_.periods)) = endogenousvariables(:,M_.maximum_lag+(1:options_.periods))+c;
     err = max(max(abs(c)));
     if verbose
@@ -119,27 +118,3 @@ if verbose
 end
 
 end
-
-function d = bksup1(c,ny,jcf,iyf,periods)
-% Solves deterministic models recursively by backsubstitution for one lead/lag
-%
-% INPUTS
-%    ny:             number of endogenous variables
-%    jcf:            variables index forward
-%
-% OUTPUTS
-%    d:              vector of backsubstitution results
-
-ir = [(periods-2)*ny+1:ny+(periods-2)*ny];
-irf = iyf+(periods-1)*ny;
-icf = [1:size(iyf,2)];
-
-for i = 2:periods
-    c(ir,jcf) = c(ir,jcf)-c(ir,icf)*c(irf,jcf);
-    ir = ir-ny;
-    irf = irf-ny;
-end
-
-d = c(:,jcf);
-
-end
diff --git a/matlab/perfect-foresight-models/solve_block_decomposed_problem.m b/matlab/perfect-foresight-models/solve_block_decomposed_problem.m
index 1af0b6c0a57e82096cf948d13a99ae64f76e6dc7..4f03473c7dd138a8b9b971b89f15d385f6d4e1d8 100644
--- a/matlab/perfect-foresight-models/solve_block_decomposed_problem.m
+++ b/matlab/perfect-foresight-models/solve_block_decomposed_problem.m
@@ -92,7 +92,11 @@ for blk = 1:nblocks
             y_index = M_.block_structure.block(blk).variable(end-M_.block_structure.block(blk).mfs+1:end);
             [y, T, success, maxblkerror, iter] = solve_one_boundary(fh_dynamic, y, exo_simul, M_.params, steady_state, T, y_index, M_.block_structure.block(blk).NNZDerivatives, options_.periods, M_.block_structure.block(blk).is_linear, blk, M_.maximum_lag, options_.simul.maxit, options_.dynatol.f, cutoff, options_.stack_solve_algo, is_forward, true, false, M_, options_);
         case {5, 8} % solveTwoBoundaries{Simple,Complete}
-            [y, T, success, maxblkerror, iter] = solve_two_boundaries(fh_dynamic, y, exo_simul, steady_state, T, blk, cutoff, options_, M_);
+            if ismember(options_.stack_solve_algo, [1 6])
+                [y, T, success, maxblkerror, iter] = solve_two_boundaries_lbj(fh_dynamic, y, exo_simul, steady_state, T, blk, options_, M_);
+            else
+                [y, T, success, maxblkerror, iter] = solve_two_boundaries_stacked(fh_dynamic, y, exo_simul, steady_state, T, blk, cutoff, options_, M_);
+            end
     end
 
     tmp = y(M_.block_structure.block(blk).variable, :);
diff --git a/matlab/perfect-foresight-models/solve_two_boundaries_lbj.m b/matlab/perfect-foresight-models/solve_two_boundaries_lbj.m
new file mode 100644
index 0000000000000000000000000000000000000000..752cc8665925505047616f99d2e5ed7907e38685
--- /dev/null
+++ b/matlab/perfect-foresight-models/solve_two_boundaries_lbj.m
@@ -0,0 +1,102 @@
+function [y, T, success, err, iter] = solve_two_boundaries_lbj(fh, y, x, steady_state, T, blk, options_, M_)
+% Computes the deterministic simulation of a block of equations containing
+% both lead and lag variables, using the LBJ algorithm.
+%
+% INPUTS
+%   fh                  [handle]        function handle to the dynamic file for the block
+%   y                   [matrix]        All the endogenous variables of the model
+%   x                   [matrix]        All the exogenous variables of the model
+%   steady_state        [vector]        steady state of the model
+%   T                   [matrix]        Temporary terms
+%   blk                 [integer]       block number
+%   options_            [structure]     storing the options
+%   M_                  [structure]     Model description
+%
+% OUTPUTS
+%   y                   [matrix]        All endogenous variables of the model
+%   T                   [matrix]        Temporary terms
+%   success             [logical]       Whether a solution was found
+%   err                 [double]        ∞-norm of Δy
+%   iter                [integer]       Number of iterations
+%
+% ALGORITHM
+%   Laffargue, Boucekkine, Juillard (LBJ)
+%   see Juillard (1996) Dynare: A program for the resolution and
+%   simulation of dynamic models with forward variables through the use
+%   of a relaxation algorithm. CEPREMAP. Couverture Orange. 9602.
+
+% Copyright © 2023 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/>.
+
+sparse_rowval = M_.block_structure.block(blk).g1_sparse_rowval;
+sparse_colval = M_.block_structure.block(blk).g1_sparse_colval;
+sparse_colptr = M_.block_structure.block(blk).g1_sparse_colptr;
+
+periods = options_.periods;
+
+% NB: notations are deliberately similar to those of sim1_lbj.m
+
+ny = M_.block_structure.block(blk).mfs;
+
+% Compute which columns, in the 3×n-wide Jacobian, have non-zero elements
+% corresponding to the forward (iyf) or backward (iyp) variables
+iyp = find(sparse_colptr(2:ny+1)-sparse_colptr(1:ny));
+iyf = find(sparse_colptr(2*ny+2:end)-sparse_colptr(2*ny+1:end-1));
+
+y_index = M_.block_structure.block(blk).variable(end-ny+1:end);
+
+success = false;
+
+for iter = 1:options_.simul.maxit
+    h = clock;
+
+    c = zeros(ny*periods, length(iyf)+1); % Stores the D and d of Sébastien’s presentation
+    it_ = M_.maximum_lag+1;
+    [yy, T(:, it_), r, g1] = fh(dynendo(y, it_, M_), x(it_, :), M_.params, steady_state, ...
+                                sparse_rowval, sparse_colval, sparse_colptr, T(:, it_));
+    y(:, it_) = yy(M_.endo_nbr+(1:M_.endo_nbr));
+    ic = 1:ny;
+    icp = iyp;
+    c(ic, :) = full(g1(:, ny+(1:ny))) \ [ full(g1(:, 2*ny+iyf)) -r ];
+    for it_ = M_.maximum_lag+(2:periods)
+        [yy, T(:, it_), r, g1] = fh(dynendo(y, it_, M_), x(it_, :), M_.params, steady_state, ...
+                                    sparse_rowval, sparse_colval, sparse_colptr, T(:, it_));
+        y(:, it_) = yy(M_.endo_nbr+(1:M_.endo_nbr));
+        j = [ full(g1(:, ny+(1:ny))) -r ];
+        j(:, [ iyf ny+1 ]) = j(:, [ iyf ny+1 ]) - full(g1(:, iyp)) * c(icp, :);
+        ic = ic + ny;
+        icp = icp + ny;
+        c(ic, :) = j(:, 1:ny) \ [ full(g1(:, 2*ny+iyf)) j(:, ny+1) ];
+    end
+
+    dy = back_subst_lbj(c, ny, iyf, periods);
+
+    y(y_index, M_.maximum_lag+(1:periods)) = y(y_index, M_.maximum_lag+(1:periods)) + dy;
+    err = max(max(abs(dy)));
+
+    if options_.verbosity
+        fprintf('Iter: %s,\t err. = %s, \t time = %s\n', num2str(iter), num2str(err), num2str(etime(clock, h)));
+    end
+    if err < options_.dynatol.x
+        success = true;
+        break
+    end
+end
+
+
+function y3n = dynendo(y, it_, M_)
+    y3n = reshape(y(:, it_+(-1:1)), 3*M_.endo_nbr, 1);
diff --git a/matlab/solve_two_boundaries.m b/matlab/perfect-foresight-models/solve_two_boundaries_stacked.m
similarity index 87%
rename from matlab/solve_two_boundaries.m
rename to matlab/perfect-foresight-models/solve_two_boundaries_stacked.m
index 339337218aa2d74249dff420180e9c4b4748c68c..829cd19a85d324efbfab1aaff277a61a264c5b90 100644
--- a/matlab/solve_two_boundaries.m
+++ b/matlab/perfect-foresight-models/solve_two_boundaries_stacked.m
@@ -1,6 +1,7 @@
-function [y, T, success, max_res, iter] = solve_two_boundaries(fh, y, x, steady_state, T, Block_Num, cutoff, options_, M_)
-% Computes the deterministic simulation of a block of equation containing
-% both lead and lag variables using relaxation methods
+function [y, T, success, max_res, iter] = solve_two_boundaries_stacked(fh, y, x, steady_state, T, Block_Num, cutoff, options_, M_)
+% Computes the deterministic simulation of a block of equations containing
+% both lead and lag variables, using a Newton method over the stacked Jacobian
+% (in particular, this excludes LBJ).
 %
 % INPUTS
 %   fh                  [handle]        function handle to the dynamic file for the block
@@ -47,6 +48,10 @@ periods = options_.periods;
 y_kmin = M_.maximum_lag;
 stack_solve_algo = options_.stack_solve_algo;
 
+if ~ismember(stack_solve_algo, [0 2 3 4])
+    error('Unsupported stack_solve_algo value')
+end
+
 verbose = options_.verbosity;
 
 cvg=false;
@@ -158,46 +163,6 @@ while ~(cvg || iter > options_.simul.maxit)
             dx = g1a\b- ya;
             ya = ya + lambda*dx;
             y(y_index, y_kmin+(1:periods))=reshape(ya',length(y_index),periods);
-        elseif stack_solve_algo==1 || stack_solve_algo==6
-            for t=1:periods
-                first_elem = (t-1)*Blck_size+1;
-                last_elem = t*Blck_size;
-                next_elem = (t+1)*Blck_size;
-                Elem = first_elem:last_elem;
-                Elem_1 = last_elem+1:next_elem;
-                B1_inv = inv(g1a(Elem, Elem));
-                if (t < periods)
-                    S1 = B1_inv * g1a(Elem, Elem_1);
-                    g1a(Elem, Elem_1) = S1;
-                end
-                b(Elem) = B1_inv * b(Elem);
-                g1a(Elem, Elem) = ones(Blck_size, Blck_size);
-                if t<periods
-                    g1a(Elem_1, Elem_1) = g1a(Elem_1, Elem_1) - g1a(Elem_1, Elem) * S1;
-                    b(Elem_1) = b(Elem_1) - g1a(Elem_1, Elem) * b(Elem);
-                    g1a(Elem_1, Elem) = zeros(Blck_size, Blck_size);
-                end
-            end
-            za = b(Elem);
-            zaa = za;
-            y_Elem = (periods - 1) * Blck_size + 1:(periods) * Blck_size;
-            dx = ya;
-            dx(y_Elem) = za - ya(y_Elem);
-            ya(y_Elem) = ya(y_Elem) + lambda*dx(y_Elem);
-            y(y_index, y_kmin + periods) = ya(y_Elem);
-            for t=periods-1:-1:1
-                first_elem = (t-1)*Blck_size+1;
-                last_elem = t*Blck_size;
-                next_elem = (t+1)*Blck_size;
-                Elem_1 = last_elem+1:next_elem;
-                Elem = first_elem:last_elem;
-                za = b(Elem) - g1a(Elem, Elem_1) * zaa;
-                zaa = za;
-                y_Elem = Blck_size * (t-1)+1:Blck_size * (t);
-                dx(y_Elem) = za - ya(y_Elem);
-                ya(y_Elem) = ya(y_Elem) + lambda*dx(y_Elem);
-                y(y_index, y_kmin + t) = ya(y_Elem);
-            end
         elseif stack_solve_algo==2
             flag1=1;
             while flag1>0