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