From 0ff7a9d2ab37017349b570e194d9b5a6a66b76a0 Mon Sep 17 00:00:00 2001 From: Houtan Bastani Date: Mon, 18 Nov 2019 17:13:49 +0100 Subject: [PATCH] introduce command line options: `exclude_eqs` and `include_eqs` Allows for the inclusion/exclusion of a set of equations, specified either on the command line or in a text file. If the equation has a single endogenous variable on the LHS, then the equation is moved. If not, if the equation has an `endogenous` tag then that variable is removed along with this equation. If not, then an error is thrown. As a command line argument, `exclude_eqs` can take the form (same syntax for `include_eqs`): * `exclude_eqs=eq1 to remove all equations declared as `[name=eq1]` * `exclude_eqs=[eq 1, eq 2]` to remove all equations declared as `[name=eq 1]` or `[name=eq 2]` * `exclude_eqs=[tagname=X]` to remove all equations declared as `[tagname=X]` * `exclude_eqs=[tagname=(X, Y)]` to remove all equations declared as `[tagname=X]` or `[tagname=Y]` When declared in a file, the file should be of the form: ``` eq 1 eq 2 ``` to remove all equations declared as `[name=eq 1]` or `[name=eq 2]`. It should be of the form: ``` tagname= X Y ``` to remove all equations declared as `[tagname=X]` or `[tagname=Y]`. --- src/CodeInterpreter.hh | 3 +- src/DynamicModel.cc | 198 ++++++++++++++++++++++++++++++++- src/DynamicModel.hh | 32 ++++++ src/DynareMain.cc | 30 ++++- src/DynareMain2.cc | 4 +- src/ExprNode.cc | 12 ++ src/ModFile.cc | 5 +- src/ModFile.hh | 3 +- src/ModelTree.cc | 98 ++++++++++++++++ src/ModelTree.hh | 10 +- src/NumericalInitialization.cc | 25 ++++- 11 files changed, 400 insertions(+), 20 deletions(-) diff --git a/src/CodeInterpreter.hh b/src/CodeInterpreter.hh index 3d5b74b0..8d963149 100644 --- a/src/CodeInterpreter.hh +++ b/src/CodeInterpreter.hh @@ -150,7 +150,8 @@ enum class SymbolType logTrend = 15, //!< Log-trend variable unusedEndogenous = 16, //!< Type to mark unused endogenous variables when `nostrict` option is passed endogenousVAR = 17, //!< Variables declared in a var_model statement - epilogue = 18 //!< Variables created in epilogue block + epilogue = 18, //!< Variables created in epilogue block + excludedVariable = 19 //!< Type to use when an equation is excluded via include/exclude_eqs and the LHS variable is not used elsewhere in the model }; enum ExpressionType diff --git a/src/DynamicModel.cc b/src/DynamicModel.cc index e6cd7e91..2ff943d6 100644 --- a/src/DynamicModel.cc +++ b/src/DynamicModel.cc @@ -23,6 +23,7 @@ #include #include #include +#include #include "DynamicModel.hh" @@ -103,6 +104,7 @@ DynamicModel::DynamicModel(const DynamicModel &m) : balanced_growth_test_tol {m.balanced_growth_test_tol}, static_only_equations_lineno {m.static_only_equations_lineno}, static_only_equations_equation_tags {m.static_only_equations_equation_tags}, + static_only_equation_tags_xref {m.static_only_equation_tags_xref}, deriv_id_table {m.deriv_id_table}, inv_deriv_id_table {m.inv_deriv_id_table}, dyn_jacobian_cols_table {m.dyn_jacobian_cols_table}, @@ -165,6 +167,7 @@ DynamicModel::operator=(const DynamicModel &m) static_only_equations_lineno = m.static_only_equations_lineno; static_only_equations_equation_tags = m.static_only_equations_equation_tags; + static_only_equation_tags_xref = m.static_only_equation_tags_xref; deriv_id_table = m.deriv_id_table; inv_deriv_id_table = m.inv_deriv_id_table; dyn_jacobian_cols_table = m.dyn_jacobian_cols_table; @@ -2930,6 +2933,179 @@ DynamicModel::writeDynamicJacobianNonZeroElts(const string &basename) const output.close(); } +void +DynamicModel::parseIncludeExcludeEquations(const string &inc_exc_eq_tags, set> & eq_tag_set, bool exclude_eqs) +{ + string tags; + if (filesystem::exists(inc_exc_eq_tags)) + { + ifstream exclude_file; + exclude_file.open(inc_exc_eq_tags, ifstream::in); + if (!exclude_file.is_open()) + { + cerr << "ERROR: Could not open " << inc_exc_eq_tags << endl; + exit(EXIT_FAILURE); + } + + string line; + bool tagname_on_first_line = false; + while (getline(exclude_file, line)) + { + removeLeadingTrailingWhitespace(line); + if (!line.empty()) + if (tags.empty() && line.find("=") != string::npos) + { + tagname_on_first_line = true; + tags += line + "("; + } + else + if (line.find("'") != string::npos) + tags += line + ","; + else + tags += "'" + line + "',"; + } + + if (!tags.empty()) + { + tags = tags.substr(0, tags.size()-1); + if (tagname_on_first_line) + tags += ")"; + } + } + else + tags = inc_exc_eq_tags; + removeLeadingTrailingWhitespace(tags); + + if (tags.front() == '[' && tags.back() != ']') + { + cerr << "Error: " << (exclude_eqs ? "exclude_eqs" : "include_eqs") + << ": if the first character is '[' the last must be ']'" << endl; + exit(EXIT_FAILURE); + } + + if (tags.front() == '[' && tags.back() == ']') + tags = tags.substr(1, tags.length() - 2); + removeLeadingTrailingWhitespace(tags); + + regex q ("^\\w+\\s*="); + smatch matches; + string tagname = "name"; + if (regex_search(tags, matches, q)) + { + tagname = matches[0].str(); + tags = tags.substr(tagname.size(), tags.length() - tagname.size() + 1); + removeLeadingTrailingWhitespace(tags); + if (tags.front() == '(' && tags.back() == ')') + { + tags = tags.substr(1, tags.length() - 2); + removeLeadingTrailingWhitespace(tags); + } + tagname = tagname.substr(0, tagname.size()-1); + removeLeadingTrailingWhitespace(tagname); + } + + string quote_regex = "'[^']+'"; + string non_quote_regex = "[^,\\s]+"; + regex r ("(\\s*" + quote_regex + "|" + non_quote_regex + "\\s*)(,\\s*(" + quote_regex + "|" + non_quote_regex + ")\\s*)*"); + if (!regex_match (tags, r)) + { + cerr << "Error: " << (exclude_eqs ? "exclude_eqs" : "include_eqs") + << ": argument is of incorrect format." << endl; + exit(EXIT_FAILURE); + } + + regex s (quote_regex + "|" + non_quote_regex); + for (auto it = sregex_iterator(tags.begin(), tags.end(), s); + it != sregex_iterator(); it++) + { + auto str = it->str(); + if (str[0] == '\'' && str[str.size()-1] == '\'') + str = str.substr(1, str.size()-2); + eq_tag_set.insert({tagname, str}); + } +} + +void +DynamicModel::includeExcludeEquations(const string & eqs, bool exclude_eqs) +{ + if (eqs.empty()) + return; + + set> eq_tag_set; + parseIncludeExcludeEquations(eqs, eq_tag_set, exclude_eqs); + + vector excluded_vars = + ModelTree::includeExcludeEquations(eq_tag_set, exclude_eqs, + equations, equations_lineno, + equation_tags, equation_tags_xref, false); + + // `static_only_equation_tags` is `vector>>` + // while `equation_tags` is `vector>>` + // so convert former structure to latter to conform with function call + int n = 0; + vector>> tmp_static_only_equation_tags; + for (auto & eqn_tags : static_only_equations_equation_tags) + { + for (auto & eqn_tag : eqn_tags) + tmp_static_only_equation_tags.emplace_back(make_pair(n, eqn_tag)); + n++; + } + // Ignore output because variables are not excluded when equations marked 'static' are excluded + ModelTree::includeExcludeEquations(eq_tag_set, exclude_eqs, + static_only_equations, static_only_equations_lineno, + tmp_static_only_equation_tags, + static_only_equation_tags_xref, true); + if (!eq_tag_set.empty()) + { + cerr << "ERROR: " << (exclude_eqs ? "exclude_eqs" : "include_eqs") << ": The equations specified by `"; + cerr << eq_tag_set.begin()->first << "= "; + for (auto & it : eq_tag_set) + cerr << it.second << ", "; + cerr << "` were not found." << endl; + exit(EXIT_FAILURE); + } + + if (staticOnlyEquationsNbr() != dynamicOnlyEquationsNbr()) + { + cerr << "ERROR: " << (exclude_eqs ? "exclude_eqs" : "include_eqs") + << ": You must remove the same number of equations marked `static` as equations marked `dynamic`." << endl; + exit(EXIT_FAILURE); + } + + // convert back static equation info + if (static_only_equations.empty()) + static_only_equations_equation_tags.clear(); + else + { + static_only_equations_equation_tags.resize(static_only_equations.size()); + fill(static_only_equations_equation_tags.begin(), static_only_equations_equation_tags.end(), vector>()); + for (auto & it : tmp_static_only_equation_tags) + static_only_equations_equation_tags.at(it.first).emplace_back(it.second); + } + + // Collect list of used variables in updated list of equations + set> eqn_vars; + for (const auto & eqn : equations) + eqn->collectDynamicVariables(SymbolType::endogenous, eqn_vars); + for (const auto & eqn : static_only_equations) + eqn->collectDynamicVariables(SymbolType::endogenous, eqn_vars); + + // Change LHS variable type of excluded equation if it is used in an eqution that has been kept + for (auto ev : excluded_vars) + { + bool found = false; + for (const auto & it : eqn_vars) + if (it.first == ev) + { + symbol_table.changeType(ev, SymbolType::exogenous); + found = true; + break; + } + if (!found) + symbol_table.changeType(ev, SymbolType::excludedVariable); + } +} + void DynamicModel::writeOutput(ostream &output, const string &basename, bool block_decomposition, bool linear_decomposition, bool byte_code, bool use_dll, int order, bool estimation_present, bool compute_xrefs, bool julia) const { @@ -6305,13 +6481,22 @@ DynamicModel::getEquationNumbersFromTags(const set &eqtags) const { vector eqnumbers; for (auto & eqtag : eqtags) - for (const auto & equation_tag : equation_tags) - if (equation_tag.second.first == "name" - && equation_tag.second.second == eqtag) + { + bool found = false; + for (const auto & equation_tag : equation_tags) + if (equation_tag.second.first == "name" + && equation_tag.second.second == eqtag) + { + found = true; + eqnumbers.push_back(equation_tag.first); + break; + } + if (!found) { - eqnumbers.push_back(equation_tag.first); - break; + cerr << "ERROR: looking for equation tag " << eqtag << " failed." << endl; + exit(EXIT_FAILURE); } + } return eqnumbers; } @@ -6618,9 +6803,12 @@ DynamicModel::addStaticOnlyEquation(expr_t eq, int lineno, const vector>> static_only_equations_equation_tags; + //! Stores mapping from equation tags to equation number + multimap, int> static_only_equation_tags_xref; + using deriv_id_table_t = map, int>; //! Maps a pair (symbol_id, lag) to a deriv ID deriv_id_table_t deriv_id_table; @@ -266,6 +269,32 @@ private: pointers into their equivalent in the new tree */ void copyHelper(const DynamicModel &m); + // Internal helper functions for includeExcludeEquations() + /*! Handles parsing of argument passed to exclude_eqs/include_eqs*/ + /* + Expects command line arguments of the form: + * filename.txt + * eq1 + * ['eq 1', 'eq 2'] + * [tagname='eq 1'] + * [tagname=('eq 1', 'eq 2')] + If argument is a file, the file should be formatted as: + eq 1 + eq 2 + OR + tagname= + X + Y + */ + void parseIncludeExcludeEquations(const string &inc_exc_eq_tags, set> & eq_tag_set, bool exclude_eqs); + + // General function that removes leading/trailing whitespace from a string + inline void removeLeadingTrailingWhitespace(string & str) + { + str.erase(0, str.find_first_not_of("\t\n\v\f\r ")); + str.erase(str.find_last_not_of("\t\n\v\f\r ") + 1); + } + public: DynamicModel(SymbolTable &symbol_table_arg, NumericalConstants &num_constants_arg, @@ -397,6 +426,9 @@ public: //! Set the max leads/lags of the original model void setLeadsLagsOrig(); + //! Removes equations from the model according to name tags + void includeExcludeEquations(const string & eqs, bool exclude_eqs); + //! Replaces model equations with derivatives of Lagrangian w.r.t. endogenous void computeRamseyPolicyFOCs(const StaticModel &static_model); //! Replaces the model equations in dynamic_model with those in this model diff --git a/src/DynareMain.cc b/src/DynareMain.cc index 2dd6f803..d4bc6408 100644 --- a/src/DynareMain.cc +++ b/src/DynareMain.cc @@ -46,6 +46,7 @@ void main2(stringstream &in, const string &basename, bool debug, bool clear_all, WarningConsolidation &warnings_arg, bool nostrict, bool stochastic, bool check_model_changes, bool minimal_workspace, bool compute_xrefs, FileOutputType output_mode, LanguageOutputType lang, int params_derivs_order, bool transform_unary_ops, + const string &exclude_eqs, const string &include_eqs, JsonOutputPointType json, JsonFileOutputType json_output_mode, bool onlyjson, bool jsonderivsimple, const string &mexext, const filesystem::path &matlabroot, const filesystem::path &dynareroot, bool onlymodel); @@ -60,7 +61,7 @@ usage() cerr << "Dynare usage: dynare mod_file [debug] [noclearall] [onlyclearglobals] [savemacro[=macro_file]] [onlymacro] [nolinemacro] [noemptylinemacro] [notmpterms] [nolog] [warn_uninit]" << " [console] [nograph] [nointeractive] [parallel[=cluster_name]] [conffile=parallel_config_path_and_filename] [parallel_slave_open_mode] [parallel_test]" << " [-D[=]] [-I/path] [nostrict] [stochastic] [fast] [minimal_workspace] [compute_xrefs] [output=dynamic|first|second|third] [language=matlab|julia]" - << " [params_derivs_order=0|1|2] [transform_unary_ops]" + << " [params_derivs_order=0|1|2] [transform_unary_ops] [exclude_eqs=] [include_eqs=]" << " [json=parse|check|transform|compute] [jsonstdout] [onlyjson] [jsonderivsimple] [nopathchange] [nopreprocessoroutput]" << " [mexext=] [matlabroot=] [onlymodel]" << endl; @@ -154,6 +155,7 @@ main(int argc, char **argv) bool minimal_workspace = false; bool compute_xrefs = false; bool transform_unary_ops = false; + string exclude_eqs, include_eqs; vector> defines; vector paths; FileOutputType output_mode{FileOutputType::none}; @@ -371,6 +373,24 @@ main(int argc, char **argv) } mexext = s.substr(7); } + else if (s.substr(0, 11) == "exclude_eqs") + { + if (s.length() <= 12 || s.at(11) != '=') + { + cerr << "Incorrect syntax for exclude_eqs option" << endl; + usage(); + } + exclude_eqs = s.substr(12); + } + else if (s.substr(0, 11) == "include_eqs") + { + if (s.length() <= 12 || s.at(11) != '=') + { + cerr << "Incorrect syntax for include_eqs option" << endl; + usage(); + } + include_eqs = s.substr(12); + } else if (s.substr(0, 10) == "matlabroot") { if (s.length() <= 11 || s.at(10) != '=') @@ -418,11 +438,17 @@ main(int argc, char **argv) if (only_macro) return EXIT_SUCCESS; + if (!exclude_eqs.empty() && !include_eqs.empty()) + { + cerr << "You may only pass one of `include_eqs` and `exclude_eqs`" << endl; + exit(EXIT_FAILURE); + } + // Do the rest main2(macro_output, basename, debug, clear_all, clear_global, no_tmp_terms, no_log, no_warn, warn_uninit, console, nograph, nointeractive, parallel, config_file, warnings, nostrict, stochastic, check_model_changes, minimal_workspace, - compute_xrefs, output_mode, language, params_derivs_order, transform_unary_ops, + compute_xrefs, output_mode, language, params_derivs_order, transform_unary_ops, exclude_eqs, include_eqs, json, json_output_mode, onlyjson, jsonderivsimple, mexext, matlabroot, dynareroot, onlymodel); diff --git a/src/DynareMain2.cc b/src/DynareMain2.cc index 38428887..abcb9dda 100644 --- a/src/DynareMain2.cc +++ b/src/DynareMain2.cc @@ -32,6 +32,7 @@ main2(stringstream &in, const string &basename, bool debug, bool clear_all, bool WarningConsolidation &warnings, bool nostrict, bool stochastic, bool check_model_changes, bool minimal_workspace, bool compute_xrefs, FileOutputType output_mode, LanguageOutputType language, int params_derivs_order, bool transform_unary_ops, + const string &exclude_eqs, const string &include_eqs, JsonOutputPointType json, JsonFileOutputType json_output_mode, bool onlyjson, bool jsonderivsimple, const string &mexext, const filesystem::path &matlabroot, const filesystem::path &dynareroot, bool onlymodel) @@ -51,7 +52,8 @@ main2(stringstream &in, const string &basename, bool debug, bool clear_all, bool mod_file->writeJsonOutput(basename, json, json_output_mode, onlyjson); // Perform transformations on the model (creation of auxiliary vars and equations) - mod_file->transformPass(nostrict, stochastic, compute_xrefs || json == JsonOutputPointType::transformpass, transform_unary_ops); + mod_file->transformPass(nostrict, stochastic, compute_xrefs || json == JsonOutputPointType::transformpass, + transform_unary_ops, exclude_eqs, include_eqs); if (json == JsonOutputPointType::transformpass) mod_file->writeJsonOutput(basename, json, json_output_mode, onlyjson); diff --git a/src/ExprNode.cc b/src/ExprNode.cc index 96829a72..9d116abd 100644 --- a/src/ExprNode.cc +++ b/src/ExprNode.cc @@ -839,6 +839,11 @@ VariableNode::prepareForDerivation() case SymbolType::epilogue: cerr << "VariableNode::prepareForDerivation: impossible case" << endl; exit(EXIT_FAILURE); + case SymbolType::excludedVariable: + cerr << "VariableNode::prepareForDerivation: impossible case: " + << "You are trying to derive a variable that has been excluded via include_eqs/exclude_eqs: " + << datatree.symbol_table.getName(symb_id) << endl; + exit(EXIT_FAILURE); } } @@ -871,6 +876,7 @@ VariableNode::computeDerivative(int deriv_id) case SymbolType::externalFunction: case SymbolType::endogenousVAR: case SymbolType::epilogue: + case SymbolType::excludedVariable: cerr << "VariableNode::computeDerivative: Impossible case!" << endl; exit(EXIT_FAILURE); } @@ -939,6 +945,9 @@ VariableNode::writeJsonAST(ostream &output) const case SymbolType::epilogue: output << "epilogue"; break; + case SymbolType::excludedVariable: + cerr << "VariableNode::computeDerivative: Impossible case!" << endl; + exit(EXIT_FAILURE); } output << R"(", "lag" : )" << lag << "}"; } @@ -1220,6 +1229,7 @@ VariableNode::writeOutput(ostream &output, ExprNodeOutputType output_type, case SymbolType::logTrend: case SymbolType::statementDeclaredVariable: case SymbolType::endogenousVAR: + case SymbolType::excludedVariable: cerr << "VariableNode::writeOutput: Impossible case" << endl; exit(EXIT_FAILURE); } @@ -1439,6 +1449,7 @@ VariableNode::getChainRuleDerivative(int deriv_id, const map &recur case SymbolType::externalFunction: case SymbolType::endogenousVAR: case SymbolType::epilogue: + case SymbolType::excludedVariable: cerr << "VariableNode::getChainRuleDerivative: Impossible case" << endl; exit(EXIT_FAILURE); } @@ -1478,6 +1489,7 @@ VariableNode::computeXrefs(EquationInfo &ei) const case SymbolType::externalFunction: case SymbolType::endogenousVAR: case SymbolType::epilogue: + case SymbolType::excludedVariable: break; } } diff --git a/src/ModFile.cc b/src/ModFile.cc index bb55a0c0..e2581efc 100644 --- a/src/ModFile.cc +++ b/src/ModFile.cc @@ -381,7 +381,8 @@ ModFile::checkPass(bool nostrict, bool stochastic) } void -ModFile::transformPass(bool nostrict, bool stochastic, bool compute_xrefs, const bool transform_unary_ops) +ModFile::transformPass(bool nostrict, bool stochastic, bool compute_xrefs, const bool transform_unary_ops, + const string &exclude_eqs, const string &include_eqs) { /* Save the original model (must be done before any model transformations by preprocessor) — except substituting out variables which we know are constant (they @@ -390,6 +391,8 @@ ModFile::transformPass(bool nostrict, bool stochastic, bool compute_xrefs, const — except diff operators with a lead which have been expanded by DataTree:AddDiff() */ + dynamic_model.includeExcludeEquations(exclude_eqs, true); + dynamic_model.includeExcludeEquations(include_eqs, false); dynamic_model.simplifyEquations(); dynamic_model.substituteAdl(); dynamic_model.setLeadsLagsOrig(); diff --git a/src/ModFile.hh b/src/ModFile.hh index fd79a2eb..e177934e 100644 --- a/src/ModFile.hh +++ b/src/ModFile.hh @@ -146,7 +146,8 @@ public: void checkPass(bool nostrict, bool stochastic); //! Perform some transformations on the model (creation of auxiliary vars and equations) /*! \param compute_xrefs if true, equation cross references will be computed */ - void transformPass(bool nostrict, bool stochastic, bool compute_xrefs, const bool transform_unary_ops); + void transformPass(bool nostrict, bool stochastic, bool compute_xrefs, const bool transform_unary_ops, + const string &exclude_eqs, const string &include_eqs); //! Execute computations /*! \param no_tmp_terms if true, no temporary terms will be computed in the static and dynamic files */ /*! \param params_derivs_order compute this order of derivs wrt parameters */ diff --git a/src/ModelTree.cc b/src/ModelTree.cc index 8ce37ef4..5109166c 100644 --- a/src/ModelTree.cc +++ b/src/ModelTree.cc @@ -1934,6 +1934,104 @@ ModelTree::addEquation(expr_t eq, int lineno) equations_lineno.push_back(lineno); } +vector +ModelTree::includeExcludeEquations(set> &eqs, bool exclude_eqs, + vector &equations, vector &equations_lineno, + vector>> &equation_tags, + multimap, int> &equation_tags_xref, bool static_equations) const +{ + vector excluded_vars; + if (equations.empty()) + return excluded_vars; + + // Get equation numbers of tags + set tag_eqns; + for (auto & it : eqs) + if (equation_tags_xref.find(it) != equation_tags_xref.end()) + { + auto range = equation_tags_xref.equal_range(it); + for_each ( range.first, range.second, [&tag_eqns](auto & x){ tag_eqns.insert(x.second); } ); + eqs.erase(it); + } + if (tag_eqns.empty()) + return excluded_vars; + + set eqns; + if (exclude_eqs) + eqns = tag_eqns; + else + for (size_t i = 0; i < equations.size(); i++) + if (tag_eqns.find(i) == tag_eqns.end()) + eqns.insert(i); + + // remove from equations, equations_lineno, equation_tags, equation_tags_xref + vector new_eqns; + vector new_equations_lineno; + map old_eqn_num_2_new; + for (size_t i = 0; i < equations.size(); i++) + if (eqns.find(i) != eqns.end()) + { + bool found = false; + for (const auto & it : equation_tags) + if (it.first == static_cast(i) && it.second.first == "endogenous") + { + found = true; + excluded_vars.push_back(symbol_table.getID(it.second.second)); + break; + } + if (!found) + { + set> result; + equations[i]->arg1->collectDynamicVariables(SymbolType::endogenous, result); + if (result.size() == 1) + excluded_vars.push_back(result.begin()->first); + else + { + cerr << "ERROR: Equation " << i + << " has been excluded but does not have a single variable on LHS or `endogenous` tag" << endl; + exit(EXIT_FAILURE); + } + } + } + else + { + new_eqns.emplace_back(equations[i]); + old_eqn_num_2_new[i] = new_eqns.size() - 1; + new_equations_lineno.emplace_back(equations_lineno[i]); + } + int n_excl = equations.size() - new_eqns.size(); + + equations = new_eqns; + equations_lineno = new_equations_lineno; + + equation_tags.erase(remove_if(equation_tags.begin(), equation_tags.end(), + [&](const auto& it) { return eqns.find(it.first) != eqns.end(); }), + equation_tags.end()); + for (auto & it : old_eqn_num_2_new) + for (auto & it1 : equation_tags) + if (it1.first == it.first) + it1.first = it.second; + + equation_tags_xref.clear(); + for (const auto & it : equation_tags) + equation_tags_xref.emplace(it.second, it.first); + + if (!static_equations) + for (size_t i = 0; i < excluded_vars.size(); i++) + for (size_t j = i+1; j < excluded_vars.size(); j++) + if (excluded_vars[i] == excluded_vars[j]) + { + cerr << "Error: Variable " << symbol_table.getName(i) << " was excluded twice" + << " via in/exclude_eqs option" << endl; + exit(EXIT_FAILURE); + } + + cout << "Excluded " << n_excl << (static_equations ? " static " : " dynamic ") + << "equation" << (n_excl > 1 ? "s" : "" ) << " via in/exclude_eqs option" << endl; + + return excluded_vars; +} + void ModelTree::simplifyEquations() { diff --git a/src/ModelTree.hh b/src/ModelTree.hh index 2a15f955..fe197688 100644 --- a/src/ModelTree.hh +++ b/src/ModelTree.hh @@ -75,13 +75,10 @@ protected: */ //! Stores declared and generated auxiliary equations vector equations; - //! Stores line numbers of declared equations; -1 means undefined vector equations_lineno; - //! Stores equation tags vector>> equation_tags; - //! Stores mapping from equation tags to equation number multimap, int> equation_tags_xref; /* @@ -262,6 +259,11 @@ protected: void printBlockDecomposition(const vector> &blocks) const; //! Determine for each block if it is linear or not vector BlockLinear(const blocks_derivatives_t &blocks_derivatives, const vector &variable_reordered) const; + //! Remove equations specified by exclude_eqs + vector includeExcludeEquations(set> &eqs, bool exclude_eqs, + vector &equations, vector &equations_lineno, + vector>> &equation_tags, + multimap, int> &equation_tags_xref, bool static_equations) const; //! Determine the simulation type of each block virtual BlockSimulationType getBlockSimulationType(int block_number) const = 0; @@ -348,6 +350,8 @@ public: void addEquation(expr_t eq, int lineno); //! Declare a node as an equation of the model, also giving its tags void addEquation(expr_t eq, int lineno, const vector> &eq_tags); + //! Add equation tags to equation tag table + void addEquationTagsToTable(const vector> &eq_tags); //! Declare a node as an auxiliary equation of the model, adding it at the end of the list of auxiliary equations void addAuxEquation(expr_t eq); //! Returns the number of equations in the model diff --git a/src/NumericalInitialization.cc b/src/NumericalInitialization.cc index fd3d0cac..11f7de1e 100644 --- a/src/NumericalInitialization.cc +++ b/src/NumericalInitialization.cc @@ -148,12 +148,25 @@ InitOrEndValStatement::writeInitValues(ostream &output) const SymbolType type = symbol_table.getType(symb_id); int tsid = symbol_table.getTypeSpecificID(symb_id) + 1; - if (type == SymbolType::endogenous) - output << "oo_.steady_state"; - else if (type == SymbolType::exogenous) - output << "oo_.exo_steady_state"; - else if (type == SymbolType::exogenousDet) - output << "oo_.exo_det_steady_state"; + switch (type) + { + case SymbolType::endogenous: + output << "oo_.steady_state"; + break; + case SymbolType::exogenous: + output << "oo_.exo_steady_state"; + break; + case SymbolType::exogenousDet: + output << "oo_.exo_det_steady_state"; + break; + case SymbolType::excludedVariable: + cerr << "ERROR: Variable `" << symbol_table.getName(symb_id) + << "` was excluded but found in an initval or endval statement" << endl; + exit(EXIT_FAILURE); + default: + cerr << "Should not arrive here" << endl; + exit(EXIT_FAILURE); + } output << "( " << tsid << " ) = "; expression->writeOutput(output);