rework equation tags

Create new EquationTags class to simplify use of equation tags throughout the code and avoid repeated code

issue #38
issue#70
Houtan Bastani 2020-02-20 15:29:10 +01:00
parent 28b98c7c0e
commit 7371558321
No known key found for this signature in database
GPG Key ID: 000094FB955BE169
10 changed files with 402 additions and 335 deletions

View File

@ -103,7 +103,6 @@ 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},
@ -166,7 +165,6 @@ 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;
@ -2907,7 +2905,8 @@ DynamicModel::writeDynamicJacobianNonZeroElts(const string &basename) const
}
void
DynamicModel::parseIncludeExcludeEquations(const string &inc_exc_eq_tags, set<pair<string, string>> &eq_tag_set, bool exclude_eqs)
DynamicModel::parseIncludeExcludeEquations(const string &inc_exc_eq_tags,
set<pair<string, string>> &eq_tag_set, bool exclude_eqs)
{
string tags;
if (filesystem::exists(inc_exc_eq_tags))
@ -3010,24 +3009,13 @@ DynamicModel::includeExcludeEquations(const string &eqs, bool exclude_eqs)
vector<int> excluded_vars
= ModelTree::includeExcludeEquations(eq_tag_set, exclude_eqs,
equations, equations_lineno,
equation_tags, equation_tags_xref, false);
equation_tags, false);
// `static_only_equation_tags` is `vector<vector<pair<string, string>>>`
// while `equation_tags` is `vector<pair<int, pair<string, string>>>`
// so convert former structure to latter to conform with function call
int n = 0;
vector<pair<int, pair<string, string>>> 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);
static_only_equations_equation_tags, true);
if (!eq_tag_set.empty())
{
cerr << "ERROR: " << (exclude_eqs ? "exclude_eqs" : "include_eqs") << ": The equations specified by `";
@ -3045,17 +3033,6 @@ DynamicModel::includeExcludeEquations(const string &eqs, bool exclude_eqs)
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<pair<string, string>>());
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<pair<int, int>> eqn_vars;
for (const auto &eqn : equations)
@ -3180,42 +3157,10 @@ DynamicModel::writeOutput(ostream &output, const string &basename, bool block_de
}
// Write equation tags
if (julia)
{
output << modstruct << "equation_tags = [" << endl;
for (const auto &equation_tag : equation_tags)
output << " EquationTag("
<< equation_tag.first + 1 << R"( , ")"
<< equation_tag.second.first << R"(" , ")"
<< equation_tag.second.second << R"("))" << endl;
output << " ]" << endl;
}
else
{
output << modstruct << "equations_tags = {" << endl;
for (const auto &equation_tag : equation_tags)
output << " " << equation_tag.first + 1 << " , '"
<< equation_tag.second.first << "' , '"
<< equation_tag.second.second << "' ;" << endl;
output << "};" << endl;
}
equation_tags.writeOutput(output, modstruct, julia);
// Write Occbin tags
map<int, vector<pair<string, string>>> occbin_options;
for (const auto &[eqn, tag] : equation_tags)
if (tag.first == "pswitch"
|| tag.first == "bind"
|| tag.first == "relax"
|| tag.first == "pcrit")
occbin_options[eqn].push_back(tag);
int idx = 0;
for (const auto &[eqn, tags] : occbin_options)
{
idx++;
for (const auto &[tag_name, tag_value] : tags)
output << "M_.occbin.constraint(" << idx << ")." << tag_name << " = '" << tag_value << "';" << endl;
}
equation_tags.writeOccbinOutput(output, modstruct, julia);
// Write mapping for variables and equations they are present in
for (const auto &variable : variableMapping)
@ -4071,16 +4016,8 @@ DynamicModel::fillVarModelTable() const
for (const auto &eqtag : it.second)
{
int eqn = -1;
set<pair<int, int>> lhs_set, lhs_tmp_set, rhs_set;
for (const auto &equation_tag : equation_tags)
if (equation_tag.second.first == "name"
&& equation_tag.second.second == eqtag)
{
eqn = equation_tag.first;
break;
}
int eqn = equation_tags.getEqnByTag("name", eqtag);
if (eqn == -1)
{
cerr << "ERROR: equation tag '" << eqtag << "' not found" << endl;
@ -4243,15 +4180,7 @@ DynamicModel::fillTrendComponentModelTable() const
vector<int> trend_eqnumber;
for (const auto &eqtag : it.second)
{
int eqn = -1;
for (const auto &equation_tag : equation_tags)
if (equation_tag.second.first == "name"
&& equation_tag.second.second == eqtag)
{
eqn = equation_tag.first;
break;
}
int eqn = equation_tags.getEqnByTag("name", eqtag);
if (eqn == -1)
{
cerr << "ERROR: trend equation tag '" << eqtag << "' not found" << endl;
@ -4270,16 +4199,8 @@ DynamicModel::fillTrendComponentModelTable() const
for (const auto &eqtag : it.second)
{
int eqn = -1;
set<pair<int, int>> lhs_set, lhs_tmp_set, rhs_set;
for (const auto &equation_tag : equation_tags)
if (equation_tag.second.first == "name"
&& equation_tag.second.second == eqtag)
{
eqn = equation_tag.first;
break;
}
int eqn = equation_tags.getEqnByTag("name", eqtag);
if (eqn == -1)
{
cerr << "ERROR: equation tag '" << eqtag << "' not found" << endl;
@ -4636,14 +4557,7 @@ DynamicModel::walkPacParameters(const string &name)
}
}
string eqtag;
for (auto &tag : equation_tags)
if (tag.first == (&equation - &equations[0]))
if (tag.second.first == "name")
{
eqtag = tag.second.second;
break;
}
string eqtag = equation_tags.getTagValueByEqnAndKey(&equation - &equations[0], "name");
if (eqtag.empty())
{
cerr << "Every equation with a pac expectation must have been assigned an equation tag name" << endl;
@ -4686,14 +4600,7 @@ DynamicModel::getPacMaxLag(const string &pac_model_name, map<pair<string, string
exit(EXIT_FAILURE);
}
string eqtag;
for (auto &tag : equation_tags)
if (tag.first == (&equation - &equations[0]))
if (tag.second.first == "name")
{
eqtag = tag.second.second;
break;
}
string eqtag = equation_tags.getTagValueByEqnAndKey(&equation - &equations[0], "name");
string eq = eqtag_and_lag[{pac_model_name, eqtag}].first;
eqtag_and_lag[{pac_model_name, eqtag}] = {eq, equation->PacMaxLag(endogs.begin()->first)};
}
@ -4731,15 +4638,7 @@ DynamicModel::declarePacModelConsistentExpectationEndogs(const string &name)
for (auto &equation : equations)
if (equation->containsPacExpectation())
{
string eqtag;
for (auto &tag : equation_tags)
if (tag.first == (&equation - &equations[0]))
if (tag.second.first == "name")
{
eqtag = tag.second.second;
break;
}
if (eqtag.empty())
if (!equation_tags.exists(&equation - &equations[0], "name"))
{
cerr << "Every equation with a pac expectation must have been assigned an equation tag name" << endl;
exit(EXIT_FAILURE);
@ -4934,15 +4833,13 @@ DynamicModel::substitutePacExpectation(const string &pac_model_name)
for (auto &it : pac_expectation_substitution)
if (it.first.first == pac_model_name)
for (auto &equation : equations)
for (auto & [tagged_eq, tag_pair] : equation_tags)
if (tagged_eq == (&equation - &equations[0])
&& tag_pair.first == "name" && tag_pair.second == it.first.second)
{
auto substeq = dynamic_cast<BinaryOpNode *>(equation->substitutePacExpectation(pac_model_name, it.second));
assert(substeq);
equation = substeq;
break;
}
if (equation_tags.exists(&equation - &equations[0], "name", it.first.second))
{
auto substeq = dynamic_cast<BinaryOpNode *>(equation->substitutePacExpectation(pac_model_name, it.second));
assert(substeq);
equation = substeq;
break;
}
}
void
@ -5536,7 +5433,6 @@ DynamicModel::clearEquations()
equations.clear();
equations_lineno.clear();
equation_tags.clear();
equation_tags_xref.clear();
}
void
@ -5548,7 +5444,6 @@ DynamicModel::replaceMyEquations(DynamicModel &dynamic_model) const
dynamic_model.addEquation(equations[i]->clone(dynamic_model), equations_lineno[i]);
dynamic_model.equation_tags = equation_tags;
dynamic_model.equation_tags_xref = equation_tags_xref;
}
void
@ -5623,7 +5518,7 @@ DynamicModel::computeRamseyPolicyFOCs(const StaticModel &static_model)
(i.e. a FOC identical to an equation of the original model) */
vector<expr_t> neweqs;
vector<int> neweqs_lineno;
map<int, vector<pair<string, string>>> neweqs_tags;
map<int, map<string, string>> neweqs_tags;
for (auto &[symb_id_and_lag, deriv_id] : deriv_id_table)
{
auto &[symb_id, lag] = symb_id_and_lag;
@ -5635,10 +5530,10 @@ DynamicModel::computeRamseyPolicyFOCs(const StaticModel &static_model)
{
// This is a derivative w.r.t. a Lagrange multiplier
neweqs_lineno.push_back(old_equations_lineno[i]);
vector<pair<string, string>> tags;
for (auto &[j, tagpair] : old_equation_tags)
if (j == i)
tags.emplace_back(tagpair);
map<string, string> tags;
auto tmp = old_equation_tags.getTagsByEqn(i);
for (const auto &[key, value] : tmp)
tags[key] = value;
neweqs_tags[neweqs.size()-1] = tags;
}
else
@ -5686,30 +5581,20 @@ DynamicModel::createVariableMapping(int orig_eq_nbr)
void
DynamicModel::expandEqTags()
{
set<int> existing_tags;
for (const auto &eqn : equation_tags)
if (eqn.second.first == "name")
existing_tags.insert(eqn.first);
set<int> existing_tags = equation_tags.getEqnsByKey("name");
for (int eq = 0; eq < static_cast<int>(equations.size()); eq++)
if (existing_tags.find(eq) == existing_tags.end())
if (auto lhs_expr = dynamic_cast<VariableNode *>(equations[eq]->arg1); lhs_expr && equation_tags_xref.find({ "name", symbol_table.getName(lhs_expr->symb_id)}) == equation_tags_xref.end())
{
equation_tags.emplace_back(eq, pair("name", symbol_table.getName(lhs_expr->symb_id)));
equation_tags_xref.emplace(pair("name", symbol_table.getName(lhs_expr->symb_id)), eq);
}
else if (equation_tags_xref.find({ "name", to_string(eq+1) }) == equation_tags_xref.end())
{
equation_tags.emplace_back(eq, pair("name", to_string(eq+1)));
equation_tags_xref.emplace(pair("name", to_string(eq+1)), eq);
}
if (auto lhs_expr = dynamic_cast<VariableNode *>(equations[eq]->arg1);
lhs_expr
&& !equation_tags.exists("name", symbol_table.getName(lhs_expr->symb_id)))
equation_tags.add(eq, "name", symbol_table.getName(lhs_expr->symb_id));
else if (!equation_tags.exists("name", to_string(eq+1)))
equation_tags.add(eq, "name", to_string(eq+1));
else
{
cerr << "Error creating default equation tag: cannot assign default tag to equation number " << eq+1 << " because it is already in use" << endl;
exit(EXIT_FAILURE);
}
sort(equation_tags.begin(), equation_tags.end());
}
set<int>
@ -6459,39 +6344,32 @@ DynamicModel::substituteAdl()
equation = dynamic_cast<BinaryOpNode *>(equation->substituteAdl());
}
vector<int>
set<int>
DynamicModel::getEquationNumbersFromTags(const set<string> &eqtags) const
{
vector<int> eqnumbers;
set<int> eqnumbers;
for (auto &eqtag : eqtags)
{
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)
set<int> tmp = equation_tags.getEqnsByTag("name", eqtag);
if (tmp.empty())
{
cerr << "ERROR: looking for equation tag " << eqtag << " failed." << endl;
exit(EXIT_FAILURE);
}
eqnumbers.insert(tmp.begin(), tmp.end());
}
return eqnumbers;
}
void
DynamicModel::findPacExpectationEquationNumbers(vector<int> &eqnumbers) const
DynamicModel::findPacExpectationEquationNumbers(set<int> &eqnumbers) const
{
int i = 0;
for (auto &equation : equations)
{
if (equation->containsPacExpectation()
&& find(eqnumbers.begin(), eqnumbers.end(), i) == eqnumbers.end())
eqnumbers.push_back(i);
eqnumbers.insert(i);
i++;
}
}
@ -6507,9 +6385,10 @@ DynamicModel::substituteUnaryOps()
pair<lag_equivalence_table_t, ExprNode::subst_table_t>
DynamicModel::substituteUnaryOps(const set<string> &var_model_eqtags)
{
vector<int> eqnumbers = getEquationNumbersFromTags(var_model_eqtags);
set<int> eqnumbers = getEquationNumbersFromTags(var_model_eqtags);
findPacExpectationEquationNumbers(eqnumbers);
return substituteUnaryOps(eqnumbers);
vector<int> eqnumbers_vec(eqnumbers.begin(), eqnumbers.end());
return substituteUnaryOps(eqnumbers_vec);
}
pair<lag_equivalence_table_t, ExprNode::subst_table_t>
@ -6751,21 +6630,14 @@ DynamicModel::isModelLocalVariableUsed() const
}
void
DynamicModel::addStaticOnlyEquation(expr_t eq, int lineno, const vector<pair<string, string>> &eq_tags)
DynamicModel::addStaticOnlyEquation(expr_t eq, int lineno, const map<string, string> &eq_tags)
{
auto beq = dynamic_cast<BinaryOpNode *>(eq);
assert(beq && beq->op_code == BinaryOpcode::equal);
vector<pair<string, string>> soe_eq_tags;
for (const auto &eq_tag : eq_tags)
soe_eq_tags.push_back(eq_tag);
int n = static_only_equations.size();
static_only_equations_equation_tags.add(static_only_equations.size(), eq_tags);
static_only_equations.push_back(beq);
static_only_equations_lineno.push_back(lineno);
static_only_equations_equation_tags.push_back(soe_eq_tags);
for (auto &it : soe_eq_tags)
static_only_equation_tags_xref.emplace(it, n);
}
size_t
@ -6777,13 +6649,7 @@ DynamicModel::staticOnlyEquationsNbr() const
size_t
DynamicModel::dynamicOnlyEquationsNbr() const
{
set<int> eqs;
for (const auto &equation_tag : equation_tags)
if (equation_tag.second.first == "dynamic")
eqs.insert(equation_tag.first);
return eqs.size();
return equation_tags.getDynamicEqns().size();
}
bool
@ -6792,10 +6658,7 @@ DynamicModel::isChecksumMatching(const string &basename, bool block) const
stringstream buffer;
// Write equation tags
for (const auto &equation_tag : equation_tags)
buffer << " " << equation_tag.first + 1
<< equation_tag.second.first
<< equation_tag.second.second << endl;
equation_tags.writeCheckSumInfo(buffer);
ExprNodeOutputType buffer_type = block ? ExprNodeOutputType::matlabDynamicModelSparse : ExprNodeOutputType::CDynamicModel;
@ -6866,24 +6729,7 @@ DynamicModel::writeJsonAST(ostream &output) const
output << R"({ "number":)" << eq
<< R"(, "line":)" << equations_lineno[eq];
for (const auto &equation_tag : equation_tags)
if (equation_tag.first == eq)
eqtags.push_back(equation_tag.second);
if (!eqtags.empty())
{
output << R"(, "tags": {)";
int i = 0;
for (const auto &[name, value] : eqtags)
{
if (i != 0)
output << ", ";
output << R"(")" << name << R"(": ")" << value << R"(")";
i++;
}
output << "}";
eqtags.clear();
}
equation_tags.writeJsonAST(output, eq);
output << R"(, "AST": )";
equations[eq]->writeJsonAST(output);
@ -6904,9 +6750,8 @@ DynamicModel::writeJsonVariableMapping(ostream &output) const
int it = 0;
int end_idx_eq = static_cast<int>(variable.second.size())-1;
for (const auto &equation : variable.second)
for (const auto &equation_tag : equation_tags)
if (equation_tag.first == equation && equation_tag.second.first == "name")
output << R"(")" << equation_tag.second.second << (it++ == end_idx_eq ? R"("])" : R"(", )");
if (auto tmp = equation_tags.getTagValueByEqnAndKey(equation, "name"); !tmp.empty())
output << R"(")" << tmp << (it++ == end_idx_eq ? R"("])" : R"(", )");
output << (ii++ == end_idx_map ? R"(})" : R"(},)") << endl;
}
output << "]";

View File

@ -1,5 +1,5 @@
/*
* Copyright © 2003-2019 Dynare Team
* Copyright © 2003-2020 Dynare Team
*
* This file is part of Dynare.
*
@ -54,10 +54,7 @@ private:
vector<int> static_only_equations_lineno;
//! Stores the equation tags of equations declared as [static]
vector<vector<pair<string, string>>> static_only_equations_equation_tags;
//! Stores mapping from equation tags to equation number
multimap<pair<string, string>, int> static_only_equation_tags_xref;
EquationTags static_only_equations_equation_tags;
using deriv_id_table_t = map<pair<int, int>, int>;
//! Maps a pair (symbol_id, lag) to a deriv ID
@ -260,9 +257,9 @@ private:
//! Create a legacy *_dynamic.m file for Matlab/Octave not yet using the temporary terms array interface
void writeDynamicMatlabCompatLayer(const string &basename) const;
vector<int> getEquationNumbersFromTags(const set<string> &eqtags) const;
set<int> getEquationNumbersFromTags(const set<string> &eqtags) const;
void findPacExpectationEquationNumbers(vector<int> &eqnumber) const;
void findPacExpectationEquationNumbers(set<int> &eqnumber) const;
//! Internal helper for the copy constructor and assignment operator
/*! Copies all the structures that contain ExprNode*, by the converting the
@ -452,7 +449,7 @@ public:
void replaceMyEquations(DynamicModel &dynamic_model) const;
//! Adds an equation marked as [static]
void addStaticOnlyEquation(expr_t eq, int lineno, const vector<pair<string, string>> &eq_tags);
void addStaticOnlyEquation(expr_t eq, int lineno, const map<string, string> &eq_tags);
//! Returns number of static only equations
size_t staticOnlyEquationsNbr() const;

183
src/EquationTags.cc Normal file
View File

@ -0,0 +1,183 @@
/*
* Copyright © 2020 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 <http://www.gnu.org/licenses/>.
*/
#include "EquationTags.hh"
#include <regex>
set<int>
EquationTags::getEqnsByKey(const string &key) const
{
set<int> retval;
for (const auto & [eqn, tags] : eqn_tags)
if (tags.find(key) != tags.end())
retval.insert(eqn);
return retval;
}
set<int>
EquationTags::getEqnsByTag(const string &key, const string &value) const
{
set<int> retval;
for (const auto & [eqn, tags] : eqn_tags)
if (auto tmp = tags.find(key); tmp != tags.end() && tmp->second == value)
retval.insert(eqn);
return retval;
}
int
EquationTags::getEqnByTag(const string &key, const string &value) const
{
for (const auto & [eqn, tags] : eqn_tags)
if (auto tmp = tags.find(key); tmp != tags.end() && tmp->second == value)
return eqn;
return -1;
}
void
EquationTags::erase(const set<int> &eqns, const map<int, int> &old_eqn_num_2_new)
{
for (const auto &eqn : eqns)
eqn_tags.erase(eqn);
for (const auto & [oldeqn, neweqn] : old_eqn_num_2_new)
for (auto & [eqn, tags] : eqn_tags)
if (eqn == oldeqn)
{
auto tmp = eqn_tags.extract(eqn);
tmp.key() = neweqn;
eqn_tags.insert(move(tmp));
}
}
void
EquationTags::writeCheckSumInfo(ostream &output) const
{
for (const auto & [eqn, tags] : eqn_tags)
for (const auto & [key, value] : tags)
output << " " << eqn + 1
<< key << " " << value << endl;
}
void
EquationTags::writeOutput(ostream &output, const string &modstruct, bool julia) const
{
if (julia)
{
output << modstruct << "equation_tags = [" << endl;
for (const auto & [eqn, tags] : eqn_tags)
for (const auto & [key, value] : tags)
output << " EquationTag("
<< eqn + 1 << R"( , ")"
<< key << R"(" , ")" << value << R"("))" << endl;
output << " ]" << endl;
}
else
{
output << modstruct << "equations_tags = {" << endl;
for (const auto & [eqn, tags] : eqn_tags)
{
for (const auto & [key, value] : tags)
output << " " << eqn + 1 << " , '"
<< key << "' , '" << value << "' ;" << endl;
}
output << "};" << endl;
}
}
void
EquationTags::writeOccbinOutput(ostream &output, const string &modstruct, bool julia) const
{
if (julia)
return;
map<int, map<string, string>> occbin_options;
for (const auto & [eqn, tags] : eqn_tags)
for (const auto & [key, value] : tags)
if (key == "pswitch"
|| key == "bind"
|| key == "relax"
|| key == "pcrit")
occbin_options[eqn][key] = value;
int idx = 0;
for (const auto & [eqn, tags] : occbin_options)
{
idx++;
for (const auto & [key, value] : tags)
output << modstruct << "occbin.constraint(" << idx << ")."
<< key << " = '" << value << "';" << endl;
}
}
void
EquationTags::writeLatexOutput(ostream &output, int eqn) const
{
if (!exists(eqn))
return;
auto escape_special_latex_symbols
= [](string str)
{
const regex special_latex_chars (R"([&%$#_{}])");
const regex backslash (R"(\\)");
const regex tilde (R"(~)");
const regex carrot (R"(\^)");
const regex textbackslash (R"(\\textbackslash)");
str = regex_replace(str, backslash, R"(\textbackslash)");
str = regex_replace(str, special_latex_chars, R"(\$&)");
str = regex_replace(str, carrot, R"(\^{})");
str = regex_replace(str, tilde, R"(\textasciitilde{})");
return regex_replace(str, textbackslash, R"(\textbackslash{})");
};
bool wrote_eq_tag = false;
output << R"(\noindent[)";
for (const auto & [key, value] : eqn_tags.at(eqn))
{
if (wrote_eq_tag)
output << ", ";
output << escape_special_latex_symbols(key);
if (!value.empty())
output << "= `" << escape_special_latex_symbols(value) << "'";
wrote_eq_tag = true;
}
output << "]" << endl;
}
void
EquationTags::writeJsonAST(ostream &output, const int eqn) const
{
if (!exists(eqn))
return;
output << R"(, "tags": {)";
bool wroteFirst = false;
for (const auto &[key, value] : eqn_tags.at(eqn))
{
if (wroteFirst)
output << ", ";
else
wroteFirst = true;
output << R"(")" << key << R"(": ")" << value << R"(")";
}
output << "}";
}

119
src/EquationTags.hh Normal file
View File

@ -0,0 +1,119 @@
/*
* Copyright © 2020 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 <http://www.gnu.org/licenses/>.
*/
#ifndef _EQUATION_TAGS_HH
#define _EQUATION_TAGS_HH
using namespace std;
#include <map>
#include <set>
class EquationTags
{
private:
map<int, map<string, string>> eqn_tags;
public:
// Add multiple equatoin tags for the given equation
inline void add(int eqn, map<string, string> tags)
{
if (eqn_tags.find(eqn) == eqn_tags.end())
eqn_tags[eqn] = move(tags);
else
eqn_tags[eqn].insert(tags.begin(), tags.end());
}
//! Add a single equation tag for the given equation
inline void add(int eqn, string key, string value)
{
eqn_tags[eqn][move(key)] = move(value);
}
//! Clear all equation tag information
inline void clear()
{
eqn_tags.clear();
}
//! Erase tags for given equations, using old_eqn_num_2_new as the mapping
//! to use for the remaining equation numbers
void erase(const set<int> &eqns, const map<int, int> &old_eqn_num_2_new);
//! Various functions to get info from equation tags
//! Get equation tags for a given equation
inline map<string, string> getTagsByEqn(const int eqn) const
{
if (eqn_tags.find(eqn) != eqn_tags.end())
return eqn_tags.find(eqn)->second;
return map<string, string>{};
}
//! Get equations that have the given key
set<int> getEqnsByKey(const string &key) const;
//! Get equations that have the given key and value
set<int> getEqnsByTag(const string &key, const string &value) const;
//! Get the first equation that has the given key and value
int getEqnByTag(const string &key, const string &value) const;
//! Get the tag value given the equation number and key
inline string getTagValueByEqnAndKey(const int eqn, const string &key) const
{
return exists(eqn, key) ? eqn_tags.at(eqn).at(key) : "";
}
//! Get the equations marked dynamic
inline set<int> getDynamicEqns() const
{
return getEqnsByTag("dynamic", "");
}
//! Returns true if equation tag with key and value exists
inline bool exists(const string &key, const string &value) const
{
return getEqnByTag(key, value) >= 0;
}
inline bool exists(const int eqn) const
{
return eqn_tags.find(eqn) != eqn_tags.end();
}
//! Returns true if equation tag with key exists for a given equation
inline bool exists(const int eqn, const string &key) const
{
return exists(eqn) ? eqn_tags.at(eqn).find(key) != eqn_tags.at(eqn).end() : false;
}
//! Returns true if equation tag with key and value exists for a given equation
inline bool exists(const int eqn, const string &key, const string &value) const
{
return exists(eqn, key) ? eqn_tags.at(eqn).at(key) == value : false;
}
//! Various functions to write equation tags
void writeCheckSumInfo(ostream &output) const;
void writeOutput(ostream &output, const string &modstruct, bool julia) const;
void writeOccbinOutput(ostream &output, const string &modstruct, bool julia) const;
void writeLatexOutput(ostream &output, int eqn) const;
void writeJsonAST(ostream &output, const int eq) const;
};
#endif

View File

@ -10,6 +10,8 @@ dynare_m_SOURCES = \
DynareBison.yy \
ComputingTasks.cc \
ComputingTasks.hh \
EquationTags.cc \
EquationTags.hh \
ModelTree.cc \
ModelTree.hh \
StaticModel.cc \

View File

@ -111,7 +111,6 @@ ModelTree::ModelTree(const ModelTree &m) :
user_set_compiler{m.user_set_compiler},
equations_lineno{m.equations_lineno},
equation_tags{m.equation_tags},
equation_tags_xref{m.equation_tags_xref},
computed_derivs_order{m.computed_derivs_order},
NNZDerivatives{m.NNZDerivatives},
equation_reordered{m.equation_reordered},
@ -138,7 +137,6 @@ ModelTree::operator=(const ModelTree &m)
equations_lineno = m.equations_lineno;
aux_equations.clear();
equation_tags = m.equation_tags;
equation_tags_xref = m.equation_tags_xref;
computed_derivs_order = m.computed_derivs_order;
NNZDerivatives = m.NNZDerivatives;
@ -1857,41 +1855,7 @@ ModelTree::writeLatexModelFile(const string &mod_basename, const string &latex_b
{
content_output << "% Equation " << eq + 1 << endl;
if (write_equation_tags)
{
auto escape_special_latex_symbols
= [](string str)
{
const regex special_latex_chars (R"([&%$#_{}])");
const regex backslash (R"(\\)");
const regex tilde (R"(~)");
const regex carrot (R"(\^)");
const regex textbackslash (R"(\\textbackslash)");
str = regex_replace(str, backslash, R"(\textbackslash)");
str = regex_replace(str, special_latex_chars, R"(\$&)");
str = regex_replace(str, carrot, R"(\^{})");
str = regex_replace(str, tilde, R"(\textasciitilde{})");
return regex_replace(str, textbackslash, R"(\textbackslash{})");
};
bool wrote_eq_tag = false;
for (const auto & [tagged_eq, tag_pair] : equation_tags)
if (tagged_eq == eq)
{
if (!wrote_eq_tag)
content_output << R"(\noindent[)";
else
content_output << ", ";
content_output << escape_special_latex_symbols(tag_pair.first);
if (!(tag_pair.second.empty()))
content_output << "= `" << escape_special_latex_symbols(tag_pair.second) << "'";
wrote_eq_tag = true;
}
if (wrote_eq_tag)
content_output << "]" << endl;
}
equation_tags.writeLatexOutput(content_output, eq);
content_output << R"(\begin{dmath})" << endl;
// Here it is necessary to cast to superclass ExprNode, otherwise the overloaded writeOutput() method is not found
@ -1919,8 +1883,7 @@ ModelTree::addEquation(expr_t eq, int lineno)
vector<int>
ModelTree::includeExcludeEquations(set<pair<string, string>> &eqs, bool exclude_eqs,
vector<BinaryOpNode *> &equations, vector<int> &equations_lineno,
vector<pair<int, pair<string, string>>> &equation_tags,
multimap<pair<string, string>, int> &equation_tags_xref, bool static_equations) const
EquationTags &equation_tags, bool static_equations) const
{
vector<int> excluded_vars;
if (equations.empty())
@ -1929,12 +1892,12 @@ ModelTree::includeExcludeEquations(set<pair<string, string>> &eqs, bool exclude_
// Get equation numbers of tags
set<int> tag_eqns;
for (auto &it : eqs)
if (equation_tags_xref.find(it) != equation_tags_xref.end())
if (auto tmp = equation_tags.getEqnsByTag(it.first, it.second); !tmp.empty())
{
auto range = equation_tags_xref.equal_range(it);
for_each(range.first, range.second, [&tag_eqns](auto &x) { tag_eqns.insert(x.second); });
tag_eqns.insert(tmp.begin(), tmp.end());
eqs.erase(it);
}
if (tag_eqns.empty())
return excluded_vars;
@ -1946,23 +1909,16 @@ ModelTree::includeExcludeEquations(set<pair<string, string>> &eqs, bool exclude_
if (tag_eqns.find(i) == tag_eqns.end())
eqns.insert(i);
// remove from equations, equations_lineno, equation_tags, equation_tags_xref
// remove from equations, equations_lineno, equation_tags
vector<BinaryOpNode *> new_eqns;
vector<int> new_equations_lineno;
map<int, int> 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 & [tagged_eq, tag_pair] : equation_tags)
if (tagged_eq == static_cast<int>(i) && tag_pair.first == "endogenous")
{
found = true;
excluded_vars.push_back(symbol_table.getID(tag_pair.second));
break;
}
if (!found)
if (auto tmp = equation_tags.getTagValueByEqnAndKey(i, "endogenous"); !tmp.empty())
{
excluded_vars.push_back(symbol_table.getID(tmp));
set<pair<int, int>> result;
equations[i]->arg1->collectDynamicVariables(SymbolType::endogenous, result);
if (result.size() == 1)
@ -1986,17 +1942,7 @@ ModelTree::includeExcludeEquations(set<pair<string, string>> &eqs, bool exclude_
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);
equation_tags.erase(eqns, old_eqn_num_2_new);
if (!static_equations)
for (size_t i = 0; i < excluded_vars.size(); i++)
@ -2040,14 +1986,9 @@ ModelTree::findConstantEquationsWithoutTags(map<VariableNode *, NumConstNode *>
}
void
ModelTree::addEquation(expr_t eq, int lineno, const vector<pair<string, string>> &eq_tags)
ModelTree::addEquation(expr_t eq, int lineno, const map<string, string> &eq_tags)
{
int n = equations.size();
for (const auto &eq_tag : eq_tags)
{
equation_tags.emplace_back(n, eq_tag);
equation_tags_xref.emplace(eq_tag, n);
}
equation_tags.add(equations.size(), eq_tags);
addEquation(eq, lineno);
}

View File

@ -31,6 +31,7 @@ using namespace std;
#include <filesystem>
#include "DataTree.hh"
#include "EquationTags.hh"
#include "ExtendedPreprocessorTypes.hh"
// Helper to convert a vector into a tuple
@ -87,9 +88,7 @@ protected:
//! Stores line numbers of declared equations; -1 means undefined
vector<int> equations_lineno;
//! Stores equation tags
vector<pair<int, pair<string, string>>> equation_tags;
//! Stores mapping from equation tags to equation number
multimap<pair<string, string>, int> equation_tags_xref;
EquationTags equation_tags;
/*
* ************** END **************
*/
@ -274,8 +273,7 @@ protected:
//! Remove equations specified by exclude_eqs
vector<int> includeExcludeEquations(set<pair<string, string>> &eqs, bool exclude_eqs,
vector<BinaryOpNode *> &equations, vector<int> &equations_lineno,
vector<pair<int, pair<string, string>>> &equation_tags,
multimap<pair<string, string>, int> &equation_tags_xref, bool static_equations) const;
EquationTags &equation_tags, bool static_equations) const;
//! Determine the simulation type of each block
virtual BlockSimulationType getBlockSimulationType(int block_number) const = 0;
@ -361,7 +359,7 @@ public:
//! Declare a node as an equation of the model; also give its line number
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<pair<string, string>> &eq_tags);
void addEquation(expr_t eq, int lineno, const map<string, string> &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
@ -394,11 +392,7 @@ public:
inline map<string, string>
getEquationTags(int eq) const
{
map<string, string> r;
for (auto &[eq2, tagpair] : equation_tags)
if (eq2 == eq)
r[tagpair.first] = tagpair.second;
return r;
return equation_tags.getTagsByEqn(eq);
}
inline static string

View File

@ -296,7 +296,7 @@ ParsingDriver::add_predetermined_variable(const string &name)
void
ParsingDriver::add_equation_tags(string key, string value)
{
eq_tags.emplace_back(key, value);
eq_tags[key] = value;
transform(key.begin(), key.end(), key.begin(), ::tolower);
if (key.compare("endogenous") == 0)
@ -2494,15 +2494,7 @@ ParsingDriver::add_model_equal(expr_t arg1, expr_t arg2)
expr_t id = model_tree->AddEqual(arg1, arg2);
// Detect if the equation is tagged [static]
bool is_static_only = false;
for (auto &eq_tag : eq_tags)
if (eq_tag.first == "static")
{
is_static_only = true;
break;
}
if (is_static_only)
if (eq_tags.find("static") != eq_tags.end())
{
if (!id->isInStaticForm())
error("An equation tagged [static] cannot contain leads, lags, expectations or STEADY_STATE operators");

View File

@ -1,5 +1,5 @@
/*
* Copyright © 2003-2019 Dynare Team
* Copyright © 2003-2020 Dynare Team
*
* This file is part of Dynare.
*
@ -248,7 +248,7 @@ private:
//! For parsing the graph_format option
SymbolList graph_formats;
//! Temporary storage for equation tags
vector<pair<string, string>> eq_tags;
map<string, string> eq_tags;
//! Map Var name to variables
map<string, vector<string>> var_map;

View File

@ -1,5 +1,5 @@
/*
* Copyright © 2003-2019 Dynare Team
* Copyright © 2003-2020 Dynare Team
*
* This file is part of Dynare.
*
@ -159,38 +159,32 @@ StaticModel::StaticModel(const DynamicModel &m) :
// Convert equations
int static_only_index = 0;
set<int> dynamic_equations = m.equation_tags.getDynamicEqns();
for (int i = 0; i < static_cast<int>(m.equations.size()); i++)
{
// Detect if equation is marked [dynamic]
bool is_dynamic_only = false;
vector<pair<string, string>> eq_tags;
for (const auto & [tagged_eq, tag_pair] : m.equation_tags)
if (tagged_eq == i)
try
{
// If equation is dynamic, replace it by an equation marked [static]
if (dynamic_equations.find(i) != dynamic_equations.end())
{
eq_tags.push_back(tag_pair);
if (tag_pair.first == "dynamic")
is_dynamic_only = true;
auto [static_only_equations,
static_only_equations_lineno,
static_only_equations_equation_tags] = m.getStaticOnlyEquationsInfo();
addEquation(static_only_equations[static_only_index]->toStatic(*this),
static_only_equations_lineno[static_only_index],
static_only_equations_equation_tags.getTagsByEqn(static_only_index));
static_only_index++;
}
try
{
// If yes, replace it by an equation marked [static]
if (is_dynamic_only)
{
auto [static_only_equations, static_only_equations_lineno, static_only_equations_equation_tags] = m.getStaticOnlyEquationsInfo();
addEquation(static_only_equations[static_only_index]->toStatic(*this), static_only_equations_lineno[static_only_index], static_only_equations_equation_tags[static_only_index]);
static_only_index++;
}
else
addEquation(m.equations[i]->toStatic(*this), m.equations_lineno[i], eq_tags);
}
catch (DataTree::DivisionByZeroException)
{
cerr << "...division by zero error encountred when converting equation " << i << " to static" << endl;
exit(EXIT_FAILURE);
}
}
else
addEquation(m.equations[i]->toStatic(*this),
m.equations_lineno[i],
m.equation_tags.getTagsByEqn(i));
}
catch (DataTree::DivisionByZeroException)
{
cerr << "...division by zero error encountred when converting equation " << i << " to static" << endl;
exit(EXIT_FAILURE);
}
// Convert auxiliary equations
for (auto aux_eq : m.aux_equations)