diff --git a/src/ModelTree.cc b/src/ModelTree.cc index ae87ac98d000359e494a61ca0ed86c4b231fa418..54cb93eec6e2cea3de3f39a0d60dfa0bc42e7661 100644 --- a/src/ModelTree.cc +++ b/src/ModelTree.cc @@ -34,6 +34,7 @@ #endif #include <regex> +#include <utility> void ModelTree::copyHelper(const ModelTree &m) @@ -300,18 +301,17 @@ ModelTree::computeNormalization(const jacob_map_t &contemporaneous_jacobian, boo void ModelTree::computeNonSingularNormalization(jacob_map_t &contemporaneous_jacobian, double cutoff, jacob_map_t &static_jacobian) { - bool check = false; - cout << "Normalizing the model..." << endl; int n = equations.size(); // Compute the maximum value of each row of the contemporaneous Jacobian matrix - jacob_map_t normalized_contemporaneous_jacobian(contemporaneous_jacobian); vector<double> max_val(n, 0.0); for (const auto &[eq_and_endo, val] : contemporaneous_jacobian) max_val[eq_and_endo.first] = max(max_val[eq_and_endo.first], fabs(val)); + // Compute normalized contemporaneous Jacobian + jacob_map_t normalized_contemporaneous_jacobian(contemporaneous_jacobian); for (auto &[eq_and_endo, val] : normalized_contemporaneous_jacobian) val /= max_val[eq_and_endo.first]; @@ -319,46 +319,53 @@ ModelTree::computeNonSingularNormalization(jacob_map_t &contemporaneous_jacobian double current_cutoff = 0.99999999; const double cutoff_lower_limit = 1e-19; - int suppressed = 0; - while (!check && current_cutoff > cutoff_lower_limit) + bool found_normalization = false; + int last_suppressed = 0; + while (!found_normalization && current_cutoff > cutoff_lower_limit) { - jacob_map_t tmp_normalized_contemporaneous_jacobian; - int suppress = 0; - for (auto &[eq_and_endo, val] : normalized_contemporaneous_jacobian) + // Drop elements below cutoff from normalized contemporaneous Jacobian + jacob_map_t normalized_contemporaneous_jacobian_above_cutoff; + int suppressed = 0; + for (const auto &[eq_and_endo, val] : normalized_contemporaneous_jacobian) if (fabs(val) > max(current_cutoff, cutoff)) - tmp_normalized_contemporaneous_jacobian[eq_and_endo] = val; + normalized_contemporaneous_jacobian_above_cutoff[eq_and_endo] = val; else - suppress++; + suppressed++; - if (suppress != suppressed) - check = computeNormalization(tmp_normalized_contemporaneous_jacobian, false); - suppressed = suppress; - if (!check) + if (suppressed != last_suppressed) + found_normalization = computeNormalization(normalized_contemporaneous_jacobian_above_cutoff, false); + last_suppressed = suppressed; + if (!found_normalization) { current_cutoff /= 2; // In this last case try to normalize with the complete jacobian if (current_cutoff <= cutoff_lower_limit) - check = computeNormalization(normalized_contemporaneous_jacobian, false); + found_normalization = computeNormalization(normalized_contemporaneous_jacobian, false); } } - if (!check) + if (!found_normalization) { cout << "Normalization failed with cutoff, trying symbolic normalization..." << endl; - //if no non-singular normalization can be found, try to find a normalization even with a potential singularity - jacob_map_t tmp_normalized_contemporaneous_jacobian; + /* If no non-singular normalization can be found, try to find a + normalization even with a potential singularity. + TODO: Explain why symbolic_jacobian is not contemporaneous. */ + jacob_map_t symbolic_jacobian; for (int i = 0; i < n; i++) { set<pair<int, int>> endos_and_lags; equations[i]->collectEndogenous(endos_and_lags); for (const auto &[endo, lag] : endos_and_lags) - tmp_normalized_contemporaneous_jacobian[{ i, endo }] = 1; + symbolic_jacobian[{ i, endo }] = 1; } - check = computeNormalization(tmp_normalized_contemporaneous_jacobian, true); - if (check) + found_normalization = computeNormalization(symbolic_jacobian, true); + if (found_normalization) { - // Update the jacobian matrix - for (const auto &[eq_and_endo, ignore] : tmp_normalized_contemporaneous_jacobian) + /* Update the Jacobian matrices by ensuring that they have a + numerical value associated to each symbolic occurrence. + TODO: Explain why this is needed. Maybe in order to have the + right incidence matrix in computePrologueAndEpilogue? */ + for (const auto &[eq_and_endo, ignore] : symbolic_jacobian) { if (static_jacobian.find(eq_and_endo) == static_jacobian.end()) static_jacobian[eq_and_endo] = 0; @@ -381,7 +388,7 @@ ModelTree::computeNonSingularNormalization(jacob_map_t &contemporaneous_jacobian } } - if (!check) + if (!found_normalization) { cerr << "No normalization could be computed. Aborting." << endl; exit(EXIT_FAILURE); @@ -444,6 +451,7 @@ ModelTree::evaluateAndReduceJacobian(const eval_context_t &eval_context, double } // Get rid of the elements of the Jacobian matrix below the cutoff + // TODO: Why? for (const auto &it : jacobian_elements_to_delete) derivatives[1].erase(it); @@ -535,47 +543,53 @@ ModelTree::computeNaturalNormalization() } void -ModelTree::computePrologueAndEpilogue(const jacob_map_t &static_jacobian_arg) +ModelTree::computePrologueAndEpilogue(const jacob_map_t &static_jacobian) { - vector<int> eq2endo(equations.size(), 0); - equation_reordered.resize(equations.size()); - variable_reordered.resize(equations.size()); - int n = equations.size(); - vector<bool> IM(n*n); - int i = 0; - for (auto it : endo2eq) + const int n = equations.size(); + + /* Compute reverse map (eq→var) of normalization. Also initialize + “equation_reordered” and “variable_reordered” to the identity + permutation. */ + vector<int> eq2endo(n); + equation_reordered.resize(n); + variable_reordered.resize(n); + for (int i = 0; i < n; i++) { + int it = endo2eq[i]; eq2endo[it] = i; equation_reordered[i] = i; variable_reordered[it] = i; - i++; } + + /* Compute incidence matrix, equations in rows, variables in columns. Row + (resp. column) indices are to be interpreted according to + “equation_ordered” (resp. “variable_reordered”). Stored in row-major + order. */ + vector<bool> IM(n*n, false); if (cutoff == 0) - { - set<pair<int, int>> endo; - for (int i = 0; i < n; i++) - { - endo.clear(); - equations[i]->collectEndogenous(endo); - for (const auto &it : endo) - IM[i * n + endo2eq[it.first]] = true; - } - } + for (int i = 0; i < n; i++) + { + set<pair<int, int>> endos_and_lags; + equations[i]->collectEndogenous(endos_and_lags); + for (const auto &[endo, lag] : endos_and_lags) + IM[i * n + endo2eq[endo]] = true; + } else - for (const auto &it : static_jacobian_arg) - IM[it.first.first * n + endo2eq[it.first.second]] = true; - bool something_has_been_done = true; - prologue = 0; - int k = 0; + for (const auto &[eq_and_endo, val] : static_jacobian) + IM[eq_and_endo.first * n + endo2eq[eq_and_endo.second]] = true; + + bool something_has_been_done; // Find the prologue equations and place first the AR(1) shock equations first - while (something_has_been_done) + prologue = 0; + do { - int tmp_prologue = prologue; something_has_been_done = false; + int new_prologue = prologue; for (int i = prologue; i < n; i++) { int nze = 0; - for (int j = tmp_prologue; j < n; j++) + int k = 0; + for (int j = new_prologue; j < n; j++) if (IM[i * n + j]) { nze++; @@ -583,42 +597,35 @@ ModelTree::computePrologueAndEpilogue(const jacob_map_t &static_jacobian_arg) } if (nze == 1) { + // Swap equations indexed by “new_prologue” and i for (int j = 0; j < n; j++) - { - bool tmp_bool = IM[tmp_prologue * n + j]; - IM[tmp_prologue * n + j] = IM[i * n + j]; - IM[i * n + j] = tmp_bool; - } - int tmp = equation_reordered[tmp_prologue]; - equation_reordered[tmp_prologue] = equation_reordered[i]; - equation_reordered[i] = tmp; + swap(IM[new_prologue * n + j], IM[i * n + j]); + swap(equation_reordered[new_prologue], equation_reordered[i]); + + // Swap variables indexed by “new_prologue” and k (in the matching) for (int j = 0; j < n; j++) - { - bool tmp_bool = IM[j * n + tmp_prologue]; - IM[j * n + tmp_prologue] = IM[j * n + k]; - IM[j * n + k] = tmp_bool; - } - tmp = variable_reordered[tmp_prologue]; - variable_reordered[tmp_prologue] = variable_reordered[k]; - variable_reordered[k] = tmp; - tmp_prologue++; + swap(IM[j * n + new_prologue], IM[j * n + k]); + swap(variable_reordered[new_prologue], variable_reordered[k]); + + new_prologue++; something_has_been_done = true; } } - prologue = tmp_prologue; + prologue = new_prologue; } - - something_has_been_done = true; - epilogue = 0; + while (something_has_been_done); + // Find the epilogue equations - while (something_has_been_done) + epilogue = 0; + do { - int tmp_epilogue = epilogue; something_has_been_done = false; + int new_epilogue = epilogue; for (int i = prologue; i < n - static_cast<int>(epilogue); i++) { int nze = 0; - for (int j = prologue; j < n - tmp_epilogue; j++) + int k = 0; + for (int j = prologue; j < n - new_epilogue; j++) if (IM[j * n + i]) { nze++; @@ -627,29 +634,20 @@ ModelTree::computePrologueAndEpilogue(const jacob_map_t &static_jacobian_arg) if (nze == 1) { for (int j = 0; j < n; j++) - { - bool tmp_bool = IM[(n - 1 - tmp_epilogue) * n + j]; - IM[(n - 1 - tmp_epilogue) * n + j] = IM[k * n + j]; - IM[k * n + j] = tmp_bool; - } - int tmp = equation_reordered[n - 1 - tmp_epilogue]; - equation_reordered[n - 1 - tmp_epilogue] = equation_reordered[k]; - equation_reordered[k] = tmp; + swap(IM[(n - 1 - new_epilogue) * n + j], IM[k * n + j]); + swap(equation_reordered[n - 1 - new_epilogue], equation_reordered[k]); + for (int j = 0; j < n; j++) - { - bool tmp_bool = IM[j * n + n - 1 - tmp_epilogue]; - IM[j * n + n - 1 - tmp_epilogue] = IM[j * n + i]; - IM[j * n + i] = tmp_bool; - } - tmp = variable_reordered[n - 1 - tmp_epilogue]; - variable_reordered[n - 1 - tmp_epilogue] = variable_reordered[i]; - variable_reordered[i] = tmp; - tmp_epilogue++; + swap(IM[j * n + n - 1 - new_epilogue], IM[j * n + i]); + swap(variable_reordered[n - 1 - new_epilogue], variable_reordered[i]); + + new_epilogue++; something_has_been_done = true; } } - epilogue = tmp_epilogue; + epilogue = new_epilogue; } + while (something_has_been_done); } void @@ -1109,11 +1107,11 @@ ModelTree::reduceBlocksAndTypeDetermination(const vector<pair<int, int>> &blocks { for (int j = first_equation; j < first_equation+c_Size; j++) { - auto it = dynamic_jacobian.find({ -1, equation_reordered[eq], variable_reordered[j] }); - if (it != dynamic_jacobian.end()) + if (dynamic_jacobian.find({ -1, equation_reordered[eq], variable_reordered[j] }) + != dynamic_jacobian.end()) is_lag = true; - it = dynamic_jacobian.find({ +1, equation_reordered[eq], variable_reordered[j] }); - if (it != dynamic_jacobian.end()) + if (dynamic_jacobian.find({ +1, equation_reordered[eq], variable_reordered[j] }) + != dynamic_jacobian.end()) is_lead = true; } } diff --git a/src/ModelTree.hh b/src/ModelTree.hh index 25cb35416590fc396b2cad08c607285761fd2a3d..c083341a89161901fb19ea3092e9a6415edabf19 100644 --- a/src/ModelTree.hh +++ b/src/ModelTree.hh @@ -256,7 +256,7 @@ protected: /*! First index is equation number, second index is endogenous type specific ID */ using jacob_map_t = map<pair<int, int>, double>; - //! Normalization of equations + //! Normalization of equations, as computed by computeNonSingularNormalization() /*! Maps endogenous type specific IDs to equation numbers */ vector<int> endo2eq; @@ -277,14 +277,15 @@ protected: /*! Applied to the jacobian contemporaneous_jacobian and stop when a matching is found. If no matching is found using a strictly positive cutoff, then a zero cutoff is applied (i.e. use a symbolic normalization); in that case, the method adds zeros in the jacobian matrices to reflect all the edges in the symbolic incidence matrix. - If no matching is found with a zero cutoff close to zero an error message is printout. + If no matching is found with a zero cutoff, an error message is printed. + The resulting normalization is stored in endo2eq. */ void computeNonSingularNormalization(jacob_map_t &contemporaneous_jacobian, double cutoff, jacob_map_t &static_jacobian); //! Try to find a natural normalization if all equations are matched to an endogenous variable on the LHS bool computeNaturalNormalization(); //! Evaluate the jacobian (w.r.t. endogenous) and suppress all the elements below the cutoff /*! Returns a pair (contemporaneous_jacobian, static_jacobian). Also fills - dynamic_jacobian. External functions are evaluated to 1. */ + dynamic_jacobian. Elements below the cutoff are discarded. External functions are evaluated to 1. */ pair<jacob_map_t, jacob_map_t> evaluateAndReduceJacobian(const eval_context_t &eval_context, double cutoff, bool verbose); //! Select and reorder the non linear equations of the model /*! Returns a tuple (blocks, equation_lag_lead, variable_lag_lead, n_static,