From c48248fc0d65ed70fb13dd508ed0254f113cbcb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Villemot?= <sebastien@dynare.org> Date: Wed, 30 Nov 2022 14:43:44 +0100 Subject: [PATCH] Implement time-recursive block decomposition tuned for purely backward/forward/static models MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Such a decomposition has to be simulated with periods as the outer loop and blocks as the inner loop. It is enabled by default for purely backward/forward/static models, as long as the “block” option is not given. In that case, “mfs” is also set to 3 by default (until that value becomes the global default). M_.time_recursive_block_decomposition is set to “true” when that decomposition has been performed, “false” otherwise for the traditional decomposition (the latter has to be simulated with blocks as the outer loop and periods as the inner loop). --- src/DynamicModel.cc | 11 ++++++++++- src/ModelTree.cc | 16 ++++++++++------ src/ModelTree.hh | 19 +++++++++++++++++++ 3 files changed, 39 insertions(+), 7 deletions(-) diff --git a/src/DynamicModel.cc b/src/DynamicModel.cc index 1a2def69..0c50e7db 100644 --- a/src/DynamicModel.cc +++ b/src/DynamicModel.cc @@ -1350,6 +1350,8 @@ void DynamicModel::writeBlockDriverOutput(ostream &output, const string &basename, const vector<int> &state_var, bool estimation_present) const { + output << "M_.block_structure.time_recursive = " << boolalpha << time_recursive_block_decomposition << ";" << endl; + for (int blk = 0; blk < static_cast<int>(blocks.size()); blk++) { int block_size = blocks[blk].size; @@ -2959,6 +2961,11 @@ DynamicModel::computingPass(int derivsOrder, int paramsDerivsOrder, const eval_c if (paramsDerivsOrder > 0 && !no_tmp_terms) computeParamsDerivativesTemporaryTerms(); + if (!block && (max_endo_lag == 0 || max_endo_lead == 0)) + { + time_recursive_block_decomposition = true; + mfs = 3; // FIXME: remove this line when mfs=3 becomes the global default + } computingPassBlock(eval_context, no_tmp_terms); if (block_decomposed) computeBlockDynJacobianCols(); @@ -3077,7 +3084,9 @@ DynamicModel::determineBlockDerivativesType(int blk) map<tuple<int, int, int>, BlockDerivativeType> derivType; int size = blocks[blk].size; int nb_recursive = blocks[blk].getRecursiveSize(); - for (int lag = -blocks[blk].max_lag; lag <= blocks[blk].max_lead; lag++) + for (int lag {time_recursive_block_decomposition ? 0 : -blocks[blk].max_lag}; + lag <= (time_recursive_block_decomposition ? 0 : blocks[blk].max_lead); + lag++) for (int eq = 0; eq < size; eq++) { set<pair<int, int>> endos_and_lags; diff --git a/src/ModelTree.cc b/src/ModelTree.cc index 372ca706..51060f45 100644 --- a/src/ModelTree.cc +++ b/src/ModelTree.cc @@ -166,6 +166,7 @@ ModelTree::ModelTree(const ModelTree &m) : eq_idx_orig2block{m.eq_idx_orig2block}, endo_idx_orig2block{m.endo_idx_orig2block}, block_decomposed{m.block_decomposed}, + time_recursive_block_decomposition{m.time_recursive_block_decomposition}, blocks{m.blocks}, endo2block{m.endo2block}, eq2block{m.eq2block}, @@ -210,6 +211,7 @@ ModelTree::operator=(const ModelTree &m) equation_type_and_normalized_equation.clear(); blocks_derivatives.clear(); block_decomposed = m.block_decomposed; + time_recursive_block_decomposition = m.time_recursive_block_decomposition; blocks = m.blocks; endo2block = m.endo2block; eq2block = m.eq2block; @@ -408,7 +410,8 @@ ModelTree::computePrologueAndEpilogue() set<pair<int, int>> endos_and_lags; equations[i]->collectEndogenous(endos_and_lags); for (auto [endo, lag] : endos_and_lags) - IM[i * n + endo2eq[endo]] = true; + if (!time_recursive_block_decomposition || lag == 0) + IM[i * n + endo2eq[endo]] = true; } bool something_has_been_done; @@ -662,7 +665,7 @@ ModelTree::computeBlockDecomposition(int prologue, int epilogue) For detecting dependencies between variables, use the symbolic adjacency matrix */ VariableDependencyGraph G(nb_simvars); - for (const auto &[key, value] : computeSymbolicJacobian(false)) + for (const auto &[key, value] : computeSymbolicJacobian(time_recursive_block_decomposition)) { auto [eq, endo] = key; if (eq_idx_orig2block[eq] >= prologue @@ -722,10 +725,11 @@ ModelTree::computeBlockDecomposition(int prologue, int epilogue) feedback set */ for (int i = 0; i < nb_simvars; i++) if (equation_type_and_normalized_equation[eq_idx_block2orig[i+prologue]].first == EquationType::solve - || variable_lag_lead[endo_idx_block2orig[i+prologue]].first > 0 - || variable_lag_lead[endo_idx_block2orig[i+prologue]].second > 0 - || equation_lag_lead[eq_idx_block2orig[i+prologue]].first > 0 - || equation_lag_lead[eq_idx_block2orig[i+prologue]].second > 0 + || (!time_recursive_block_decomposition && + (variable_lag_lead[endo_idx_block2orig[i+prologue]].first > 0 + || variable_lag_lead[endo_idx_block2orig[i+prologue]].second > 0 + || equation_lag_lead[eq_idx_block2orig[i+prologue]].first > 0 + || equation_lag_lead[eq_idx_block2orig[i+prologue]].second > 0)) || mfs == 0) add_edge(vertex(i, G), vertex(i, G), G); diff --git a/src/ModelTree.hh b/src/ModelTree.hh index c6312782..de892f53 100644 --- a/src/ModelTree.hh +++ b/src/ModelTree.hh @@ -212,6 +212,25 @@ protected: // Whether block decomposition has been successfully computed bool block_decomposed {false}; + /* Whether the block decomposition to compute is time-recursive (i.e. the + model can be simulated as a whole period-by-period). + + If true, only contemporaneous occurrences of variables are considered when + computing the block structure; leads and lags are essentially treated as + exogenous, i.e. they are ignored. Such a decomposition only makes sense + for models that are purely backward/forward/static. When using the + resulting block decomposition to simulate the model, periods must be the + outer loop, and blocks the inner loop. + + If false, then the full lead/lag structure is taken into account when + computing the block structure. This is the only option if there are both + leads and lags. When using the + resulting block decomposition to simulate the model, blocks must be the + outer loop, and periods the inner loop. + + Of course, this setting does not make any difference on StaticModel. */ + bool time_recursive_block_decomposition {false}; + // Stores various informations on the blocks vector<BlockInfo> blocks; -- GitLab