preprocessor/src/SubModel.cc

1835 lines
68 KiB
C++
Raw Permalink Normal View History

2018-08-14 14:23:21 +02:00
/*
* Copyright © 2018-2023 Dynare Team
2018-08-14 14:23:21 +02:00
*
* 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/>.
2018-08-14 14:23:21 +02:00
*/
#include <algorithm>
#include <cassert>
#include <numeric>
2018-08-14 14:23:21 +02:00
#include "DynamicModel.hh"
#include "SubModel.hh"
2018-08-14 14:23:21 +02:00
TrendComponentModelTable::TrendComponentModelTable(SymbolTable& symbol_table_arg) :
symbol_table {symbol_table_arg}
2018-08-14 14:23:21 +02:00
{
}
void
TrendComponentModelTable::addTrendComponentModel(string name_arg, vector<string> eqtags_arg,
vector<string> target_eqtags_arg)
2018-08-14 14:23:21 +02:00
{
if (isExistingTrendComponentModelName(name_arg))
{
cerr << "Error: a trend component model already exists with the name " << name_arg << endl;
exit(EXIT_FAILURE);
}
eqtags[name_arg] = move(eqtags_arg);
target_eqtags[name_arg] = move(target_eqtags_arg);
2018-08-14 14:23:21 +02:00
names.insert(move(name_arg));
}
void
TrendComponentModelTable::setVals(map<string, vector<int>> eqnums_arg,
map<string, vector<int>> target_eqnums_arg,
map<string, vector<int>> lhs_arg,
map<string, vector<expr_t>> lhs_expr_t_arg)
2018-08-14 14:23:21 +02:00
{
eqnums = move(eqnums_arg);
target_eqnums = move(target_eqnums_arg);
2018-09-12 11:56:04 +02:00
lhs = move(lhs_arg);
lhs_expr_t = move(lhs_expr_t_arg);
2018-08-14 14:23:21 +02:00
for (const auto& it : eqnums)
2018-08-14 14:23:21 +02:00
{
vector<int> nontrend_vec;
for (auto eq : it.second)
if (find(target_eqnums[it.first].begin(), target_eqnums[it.first].end(), eq)
== target_eqnums[it.first].end())
2018-08-14 14:23:21 +02:00
nontrend_vec.push_back(eq);
nontarget_eqnums[it.first] = nontrend_vec;
2018-08-14 14:23:21 +02:00
}
for (const auto& name : names)
2018-09-12 11:56:04 +02:00
{
vector<int> nontarget_lhs_vec, target_lhs_vec;
2018-09-12 11:56:04 +02:00
vector<int> lhsv = getLhs(name);
vector<int> eqnumsv = getEqNums(name);
for (int nontrend_it : getNonTargetEqNums(name))
nontarget_lhs_vec.push_back(
lhsv.at(distance(eqnumsv.begin(), find(eqnumsv.begin(), eqnumsv.end(), nontrend_it))));
nontarget_lhs[name] = nontarget_lhs_vec;
for (int trend_it : getTargetEqNums(name))
target_lhs_vec.push_back(
lhsv.at(distance(eqnumsv.begin(), find(eqnumsv.begin(), eqnumsv.end(), trend_it))));
target_lhs[name] = target_lhs_vec;
2018-09-12 11:56:04 +02:00
}
2018-08-14 14:23:21 +02:00
}
void
TrendComponentModelTable::setRhs(map<string, vector<set<pair<int, int>>>> rhs_arg)
{
rhs = move(rhs_arg);
}
void
TrendComponentModelTable::setTargetVar(map<string, vector<optional<int>>> target_vars_arg)
2018-08-14 14:23:21 +02:00
{
target_vars = move(target_vars_arg);
2018-08-14 14:23:21 +02:00
}
void
TrendComponentModelTable::setMaxLags(map<string, vector<int>> max_lags_arg)
{
max_lags = move(max_lags_arg);
}
void
TrendComponentModelTable::setDiff(map<string, vector<bool>> diff_arg)
{
diff = move(diff_arg);
}
void
TrendComponentModelTable::setOrigDiffVar(map<string, vector<optional<int>>> orig_diff_var_arg)
2018-08-14 14:23:21 +02:00
{
orig_diff_var = move(orig_diff_var_arg);
}
void
TrendComponentModelTable::setAR(map<string, map<tuple<int, int, int>, expr_t>> AR_arg)
{
AR = move(AR_arg);
}
void
TrendComponentModelTable::setA0(map<string, map<tuple<int, int>, expr_t>> A0_arg,
map<string, map<tuple<int, int>, expr_t>> A0star_arg)
{
A0 = move(A0_arg);
A0star = move(A0star_arg);
}
const map<string, vector<string>>&
2018-08-14 14:23:21 +02:00
TrendComponentModelTable::getEqTags() const
{
return eqtags;
}
const vector<string>&
TrendComponentModelTable::getEqTags(const string& name_arg) const
2018-08-14 14:23:21 +02:00
{
checkModelName(name_arg);
return eqtags.at(name_arg);
2018-08-14 14:23:21 +02:00
}
void
TrendComponentModelTable::checkModelName(const string& name_arg) const
2018-08-14 14:23:21 +02:00
{
if (!isExistingTrendComponentModelName(name_arg))
{
cerr << name_arg << " is not a recognized equation tag of a trend component model equation"
<< endl;
2018-08-14 14:23:21 +02:00
exit(EXIT_FAILURE);
}
}
const vector<int>&
TrendComponentModelTable::getNonTargetLhs(const string& name_arg) const
2018-09-12 11:56:04 +02:00
{
checkModelName(name_arg);
return nontarget_lhs.at(name_arg);
2018-09-12 11:56:04 +02:00
}
const vector<int>&
TrendComponentModelTable::getTargetLhs(const string& name_arg) const
2018-09-12 11:56:04 +02:00
{
checkModelName(name_arg);
return target_lhs.at(name_arg);
2018-09-12 11:56:04 +02:00
}
const vector<int>&
TrendComponentModelTable::getLhs(const string& name_arg) const
2018-08-14 14:23:21 +02:00
{
checkModelName(name_arg);
return lhs.at(name_arg);
2018-08-14 14:23:21 +02:00
}
const vector<expr_t>&
TrendComponentModelTable::getLhsExprT(const string& name_arg) const
2018-08-14 14:23:21 +02:00
{
checkModelName(name_arg);
return lhs_expr_t.at(name_arg);
2018-08-14 14:23:21 +02:00
}
const map<string, vector<string>>&
TrendComponentModelTable::getTargetEqTags() const
2018-08-14 14:23:21 +02:00
{
return target_eqtags;
2018-08-14 14:23:21 +02:00
}
const map<string, vector<int>>&
2018-08-14 14:23:21 +02:00
TrendComponentModelTable::getEqNums() const
{
return eqnums;
}
const map<string, vector<int>>&
TrendComponentModelTable::getTargetEqNums() const
{
return target_eqnums;
}
const vector<int>&
TrendComponentModelTable::getTargetEqNums(const string& name_arg) const
{
checkModelName(name_arg);
return target_eqnums.at(name_arg);
}
const map<string, vector<int>>&
TrendComponentModelTable::getNonTargetEqNums() const
{
return nontarget_eqnums;
}
const vector<int>&
TrendComponentModelTable::getNonTargetEqNums(const string& name_arg) const
2018-08-14 14:23:21 +02:00
{
checkModelName(name_arg);
return nontarget_eqnums.at(name_arg);
2018-08-14 14:23:21 +02:00
}
const vector<int>&
TrendComponentModelTable::getEqNums(const string& name_arg) const
2018-08-14 14:23:21 +02:00
{
checkModelName(name_arg);
return eqnums.at(name_arg);
2018-08-14 14:23:21 +02:00
}
const vector<int>&
TrendComponentModelTable::getMaxLags(const string& name_arg) const
2018-08-14 14:23:21 +02:00
{
checkModelName(name_arg);
return max_lags.at(name_arg);
2018-08-14 14:23:21 +02:00
}
int
TrendComponentModelTable::getMaxLag(const string& name_arg) const
2018-08-14 14:23:21 +02:00
{
int max_lag_int = 0;
for (auto it : getMaxLags(name_arg))
max_lag_int = max(max_lag_int, it);
return max_lag_int;
}
const vector<bool>&
TrendComponentModelTable::getDiff(const string& name_arg) const
2018-08-14 14:23:21 +02:00
{
checkModelName(name_arg);
return diff.at(name_arg);
2018-08-14 14:23:21 +02:00
}
void
TrendComponentModelTable::writeOutput(const string& basename, ostream& output) const
2018-08-14 14:23:21 +02:00
{
if (names.empty())
return;
const filesystem::path filename {DataTree::packageDir(basename) / "trend_component_ar_a0.m"};
ofstream ar_ec_output {filename, ios::out | ios::binary};
if (!ar_ec_output.is_open())
{
cerr << "ERROR: Can't open file " << filename.string() << " for writing" << endl;
exit(EXIT_FAILURE);
}
ar_ec_output << "function [AR, A0, A0star] = trend_component_ar_a0(model_name, params)" << endl
2019-12-20 16:59:30 +01:00
<< "%function [AR, A0, A0star] = trend_component_ar_a0(model_name, params)" << endl
<< "% File automatically generated by the Dynare preprocessor" << endl
<< endl;
for (const auto& name : names)
2018-08-14 14:23:21 +02:00
{
2018-08-20 10:50:28 +02:00
output << "M_.trend_component." << name << ".model_name = '" << name << "';" << endl
2018-08-14 14:23:21 +02:00
<< "M_.trend_component." << name << ".eqtags = {";
for (const auto& it : eqtags.at(name))
2018-08-14 14:23:21 +02:00
output << "'" << it << "'; ";
output << "};" << endl << "M_.trend_component." << name << ".eqn = [";
2018-08-14 14:23:21 +02:00
for (auto it : eqnums.at(name))
output << it + 1 << " ";
output << "];" << endl << "M_.trend_component." << name << ".targets = [";
2018-08-14 14:23:21 +02:00
for (auto it : eqnums.at(name))
if (find(target_eqnums.at(name).begin(), target_eqnums.at(name).end(), it)
== target_eqnums.at(name).end())
2018-08-14 14:23:21 +02:00
output << "false ";
else
output << "true ";
output << "];" << endl << "M_.trend_component." << name << ".lhs = [";
2018-08-14 14:23:21 +02:00
for (auto it : lhs.at(name))
output << symbol_table.getTypeSpecificID(it) + 1 << " ";
output << "];" << endl << "M_.trend_component." << name << ".max_lag = [";
2018-08-14 14:23:21 +02:00
for (auto it : max_lags.at(name))
output << it << " ";
output << "];" << endl << "M_.trend_component." << name << ".diff = [";
for (bool it : diff.at(name))
output << boolalpha << it << " ";
output << "];" << endl << "M_.trend_component." << name << ".orig_diff_var = [";
for (const auto& it : orig_diff_var.at(name))
output << (it ? symbol_table.getTypeSpecificID(*it) + 1 : -1) << " ";
output << "];" << endl << "M_.trend_component." << name << ".nonstationary = [";
for (size_t i = 0; i < diff.at(name).size(); i++)
output << "true ";
2018-08-14 14:23:21 +02:00
output << "];" << endl;
for (int i {1}; const auto& it : rhs.at(name))
2018-08-14 14:23:21 +02:00
{
output << "M_.trend_component." << name << ".rhs.vars_at_eq{" << i << "}.var = [";
for (auto [var, lag] : it)
output << symbol_table.getTypeSpecificID(var) + 1 << " ";
2018-08-14 14:23:21 +02:00
output << "];" << endl
<< "M_.trend_component." << name << ".rhs.vars_at_eq{" << i << "}.lag = [";
for (auto [var, lag] : it)
output << lag << " ";
2018-08-14 14:23:21 +02:00
output << "];" << endl;
i++;
}
output << "M_.trend_component." << name << ".target_vars = [";
for (const optional<int>& it : target_vars.at(name))
output << (it ? symbol_table.getTypeSpecificID(*it) + 1 : -1) << " ";
output << "];" << endl;
vector<string> target_eqtags_vec = target_eqtags.at(name);
output << "M_.trend_component." << name << ".target_eqtags = {";
for (const auto& it : target_eqtags_vec)
output << "'" << it << "';";
output << "};" << endl;
vector<string> eqtags_vec = eqtags.at(name);
output << "M_.trend_component." << name << ".target_eqn = [";
for (const auto& it : target_eqtags_vec)
output << distance(eqtags_vec.begin(), find(eqtags_vec.begin(), eqtags_vec.end(), it)) + 1
<< " ";
output << "];" << endl;
vector<int> target_lhs_vec = getTargetLhs(name);
vector<int> nontarget_lhs_vec = getNonTargetLhs(name);
ar_ec_output << "if strcmp(model_name, '" << name << "')" << endl
2019-12-20 16:59:30 +01:00
<< " % AR" << endl
<< " AR = zeros(" << nontarget_lhs_vec.size() << ", "
<< nontarget_lhs_vec.size() << ", " << getMaxLag(name) << ");" << endl;
for (const auto& [key, expr] : AR.at(name))
{
auto [eqn, lag, lhs_symb_id] = key;
int colidx = static_cast<int>(
distance(nontarget_lhs_vec.begin(),
find(nontarget_lhs_vec.begin(), nontarget_lhs_vec.end(), lhs_symb_id)));
ar_ec_output << " AR(" << eqn + 1 << ", " << colidx + 1 << ", " << lag << ") = ";
expr->writeOutput(ar_ec_output, ExprNodeOutputType::matlabDynamicModel);
ar_ec_output << ";" << endl;
}
ar_ec_output << endl
<< " % A0" << endl
<< " A0 = zeros(" << nontarget_lhs_vec.size() << ", "
<< nontarget_lhs_vec.size() << ");" << endl;
for (const auto& [key, expr] : A0.at(name))
{
auto [eqn, colidx] = key;
ar_ec_output << " A0(" << eqn + 1 << ", " << colidx + 1 << ") = ";
expr->writeOutput(ar_ec_output, ExprNodeOutputType::matlabDynamicModel);
ar_ec_output << ";" << endl;
}
ar_ec_output << endl
<< " % A0star" << endl
<< " A0star = zeros(" << nontarget_lhs_vec.size() << ", "
<< target_lhs_vec.size() << ");" << endl;
for (const auto& [key, expr] : A0star.at(name))
{
auto [eqn, colidx] = key;
ar_ec_output << " A0star(" << eqn + 1 << ", " << colidx + 1 << ") = ";
expr->writeOutput(ar_ec_output, ExprNodeOutputType::matlabDynamicModel);
ar_ec_output << ";" << endl;
}
ar_ec_output << " return" << endl << "end" << endl << endl;
2018-08-14 14:23:21 +02:00
}
ar_ec_output << "error([model_name ' is not a valid trend_component_model name'])" << endl
2019-12-20 16:59:30 +01:00
<< "end" << endl;
ar_ec_output.close();
2018-08-14 14:23:21 +02:00
}
void
TrendComponentModelTable::writeJsonOutput(ostream& output) const
2018-08-14 14:23:21 +02:00
{
for (bool printed_something {false}; const auto& name : names)
2018-08-14 14:23:21 +02:00
{
if (exchange(printed_something, true))
2018-08-14 14:23:21 +02:00
output << ", ";
output << R"({"statementName": "trend_component_model",)"
<< R"("model_name": ")" << name << R"(",)"
<< R"("eqtags": [)";
for (bool printed_something2 {false}; const auto& it : eqtags.at(name))
2018-08-22 09:15:00 +02:00
{
if (exchange(printed_something2, true))
2018-08-22 09:15:00 +02:00
output << ", ";
output << R"(")" << it << R"(")";
2018-08-22 09:15:00 +02:00
}
output << R"(], "target_eqtags": [)";
for (bool printed_something2 {false}; const auto& it : target_eqtags.at(name))
2018-08-22 09:15:00 +02:00
{
if (exchange(printed_something2, true))
2018-08-22 09:15:00 +02:00
output << ", ";
output << R"(")" << it << R"(")";
2018-08-22 09:15:00 +02:00
}
output << "]}";
2018-08-14 14:23:21 +02:00
}
}
2018-08-21 11:46:59 +02:00
VarModelTable::VarModelTable(SymbolTable& symbol_table_arg) : symbol_table {symbol_table_arg}
2018-08-21 11:46:59 +02:00
{
}
void
VarModelTable::addVarModel(string name_arg, bool structural_arg, vector<string> eqtags_arg)
2018-08-21 11:46:59 +02:00
{
if (isExistingVarModelName(name_arg))
{
cerr << "Error: a VAR model already exists with the name " << name_arg << endl;
exit(EXIT_FAILURE);
}
structural[name_arg] = structural_arg;
2018-08-21 11:46:59 +02:00
eqtags[name_arg] = move(eqtags_arg);
names.insert(move(name_arg));
}
void
VarModelTable::writeOutput(const string& basename, ostream& output) const
2018-08-21 11:46:59 +02:00
{
if (names.empty())
return;
const filesystem::path filename {DataTree::packageDir(basename) / "varmatrices.m"};
ofstream ar_output {filename, ios::out | ios::binary};
2018-09-06 17:53:07 +02:00
if (!ar_output.is_open())
{
cerr << "ERROR: Can't open file " << filename.string() << " for writing" << endl;
2018-09-06 17:53:07 +02:00
exit(EXIT_FAILURE);
}
ar_output << "function [ar, a0, constants] = varmatrices(model_name, params, reducedform)" << endl
<< "% File automatically generated by the Dynare preprocessor" << endl
<< endl
<< "if nargin<3" << endl
<< " reducedform = false;" << endl
<< "end" << endl
<< endl;
2018-09-06 17:53:07 +02:00
for (const auto& name : names)
2018-08-21 11:46:59 +02:00
{
output << "M_.var." << name << ".model_name = '" << name << "';" << endl
<< "M_.var." << name << ".structural = " << boolalpha << structural.at(name) << ";"
<< endl
<< "M_.var." << name << ".eqtags = {";
for (const auto& it : eqtags.at(name))
2018-08-21 11:46:59 +02:00
output << "'" << it << "'; ";
output << "};" << endl << "M_.var." << name << ".eqn = [";
2018-08-21 11:46:59 +02:00
for (auto it : eqnums.at(name))
output << it + 1 << " ";
output << "];" << endl << "M_.var." << name << ".lhs = [";
2018-08-21 11:46:59 +02:00
for (auto it : lhs.at(name))
output << symbol_table.getTypeSpecificID(it) + 1 << " ";
output << "];" << endl << "M_.var." << name << ".max_lag = [";
2018-08-21 11:46:59 +02:00
for (auto it : max_lags.at(name))
output << it << " ";
output << "];" << endl << "M_.var." << name << ".diff = [";
for (bool it : diff.at(name))
output << boolalpha << it << " ";
2018-08-21 11:46:59 +02:00
output << "];" << endl
<< "M_.var." << name << ".nonstationary = M_.var." << name << ".diff;" << endl
2018-08-21 11:46:59 +02:00
<< "M_.var." << name << ".orig_diff_var = [";
for (const auto& it : orig_diff_var.at(name))
output << (it ? symbol_table.getTypeSpecificID(*it) + 1 : -1) << " ";
2018-08-21 11:46:59 +02:00
output << "];" << endl;
for (int i {1}; const auto& it : rhs.at(name))
2018-08-21 11:46:59 +02:00
{
output << "M_.var." << name << ".rhs.vars_at_eq{" << i << "}.var = [";
for (auto [var, lag] : it)
output << symbol_table.getTypeSpecificID(var) + 1 << " ";
output << "];" << endl << "M_.var." << name << ".rhs.vars_at_eq{" << i << "}.lag = [";
for (auto [var, lag] : it)
output << lag << " ";
2018-08-21 11:46:59 +02:00
output << "];" << endl;
i++;
}
2018-09-06 17:53:07 +02:00
vector<int> lhs = getLhsOrigIds(name);
2018-09-06 17:53:07 +02:00
ar_output << "if strcmp(model_name, '" << name << "')" << endl
<< " ar = zeros(" << lhs.size() << ", " << lhs.size() << ", " << getMaxLag(name)
<< ");" << endl;
for (const auto& [key, expr] : AR.at(name))
2018-09-06 17:53:07 +02:00
{
auto [eqn, lag, lhs_symb_id] = key;
int colidx
= static_cast<int>(distance(lhs.begin(), find(lhs.begin(), lhs.end(), lhs_symb_id)));
ar_output << " ar(" << eqn + 1 << "," << colidx + 1 << "," << lag << ") = ";
expr->writeOutput(ar_output, ExprNodeOutputType::matlabDynamicModel);
2018-09-12 18:21:34 +02:00
ar_output << ";" << endl;
2018-09-06 17:53:07 +02:00
}
ar_output << " if nargout>1" << endl << " a0 = eye(" << lhs.size() << ");" << endl;
for (const auto& [key, expr] : A0.at(name))
{
auto [eqn, lhs_symb_id] = key;
int colidx
= static_cast<int>(distance(lhs.begin(), find(lhs.begin(), lhs.end(), lhs_symb_id)));
if (eqn != colidx)
{
ar_output << " a0(" << eqn + 1 << "," << colidx + 1 << ") = ";
expr->writeOutput(ar_output, ExprNodeOutputType::matlabDynamicModel);
ar_output << ";" << endl;
}
}
ar_output << " if reducedform" << endl
<< " for i=1:" << getMaxLag(name) << endl
<< " ar(:,:,i) = a0\\ar(:,:,i);" << endl
<< " end" << endl
<< " if nargout<3" << endl
<< " a0 = eye(" << lhs.size() << ");" << endl
<< " end" << endl
<< " end" << endl
<< " if nargout>2" << endl
<< " constants = zeros(" << lhs.size() << ",1);" << endl;
for (auto [eqn, expr] : constants.at(name))
{
ar_output << " constants(" << eqn + 1 << ") = ";
expr->writeOutput(ar_output, ExprNodeOutputType::matlabDynamicModel);
ar_output << ";" << endl;
}
ar_output << " end" << endl
<< " if reducedform" << endl
<< " constants = a0\\constants;" << endl
<< " a0 = eye(" << lhs.size() << ");" << endl
<< " end" << endl
<< " end" << endl
<< " return" << endl
<< "end" << endl
<< endl;
2018-08-21 11:46:59 +02:00
}
ar_output << "error('%s is not a valid var_model name', model_name)" << endl;
2018-09-06 17:53:07 +02:00
ar_output.close();
2018-08-21 11:46:59 +02:00
}
void
VarModelTable::writeJsonOutput(ostream& output) const
2018-08-21 11:46:59 +02:00
{
for (bool printed_something {false}; const auto& name : names)
2018-08-21 11:46:59 +02:00
{
if (exchange(printed_something, true))
2018-08-21 11:46:59 +02:00
output << ", ";
output << R"({"statementName": "var_model",)"
<< R"("model_name": ")" << name << R"(",)"
<< R"("eqtags": [)";
for (bool printed_something2 {false}; const auto& it : eqtags.at(name))
2018-08-21 11:46:59 +02:00
{
if (exchange(printed_something2, true))
output << ", ";
output << R"(")" << it << R"(")";
2018-08-21 11:46:59 +02:00
}
output << "]}";
2018-08-21 11:46:59 +02:00
}
}
const map<string, bool>&
VarModelTable::getStructural() const
{
return structural;
}
const map<string, vector<string>>&
2018-08-21 11:46:59 +02:00
VarModelTable::getEqTags() const
{
return eqtags;
}
const vector<string>&
VarModelTable::getEqTags(const string& name_arg) const
2018-08-21 11:46:59 +02:00
{
checkModelName(name_arg);
return eqtags.at(name_arg);
2018-08-21 11:46:59 +02:00
}
void
VarModelTable::checkModelName(const string& name_arg) const
2018-08-21 11:46:59 +02:00
{
if (!isExistingVarModelName(name_arg))
{
cerr << name_arg << " is not a recognized equation tag of a VAR model equation" << endl;
2018-08-21 11:46:59 +02:00
exit(EXIT_FAILURE);
}
}
void
VarModelTable::setEqNums(map<string, vector<int>> eqnums_arg)
{
eqnums = move(eqnums_arg);
}
void
VarModelTable::setLhs(map<string, vector<int>> lhs_arg)
{
lhs = move(lhs_arg);
for (const auto& it : lhs)
{
vector<int> lhsvec;
for (auto ids : it.second)
{
int lhs_last_orig_symb_id = ids;
int lhs_orig_symb_id = ids;
if (symbol_table.isDiffAuxiliaryVariable(lhs_orig_symb_id))
try
2019-12-20 16:59:30 +01:00
{
lhs_last_orig_symb_id = lhs_orig_symb_id;
lhs_orig_symb_id = symbol_table.getOrigSymbIdForAuxVar(lhs_orig_symb_id);
}
catch (...)
{
}
lhsvec.emplace_back(lhs_last_orig_symb_id);
}
lhs_orig_symb_ids[it.first] = lhsvec;
}
2018-08-21 11:46:59 +02:00
}
void
VarModelTable::setRhs(map<string, vector<set<pair<int, int>>>> rhs_arg)
{
rhs = move(rhs_arg);
}
void
VarModelTable::setLhsExprT(map<string, vector<expr_t>> lhs_expr_t_arg)
{
lhs_expr_t = move(lhs_expr_t_arg);
}
const map<string, vector<int>>&
2018-08-21 11:46:59 +02:00
VarModelTable::getEqNums() const
{
return eqnums;
}
const vector<bool>&
VarModelTable::getDiff(const string& name_arg) const
{
checkModelName(name_arg);
return diff.at(name_arg);
}
const vector<int>&
VarModelTable::getEqNums(const string& name_arg) const
2018-08-21 11:46:59 +02:00
{
checkModelName(name_arg);
return eqnums.at(name_arg);
2018-08-21 11:46:59 +02:00
}
void
VarModelTable::setMaxLags(map<string, vector<int>> max_lags_arg)
{
max_lags = move(max_lags_arg);
}
void
VarModelTable::setDiff(map<string, vector<bool>> diff_arg)
{
diff = move(diff_arg);
}
void
VarModelTable::setOrigDiffVar(map<string, vector<optional<int>>> orig_diff_var_arg)
2018-08-21 11:46:59 +02:00
{
orig_diff_var = move(orig_diff_var_arg);
}
2018-09-06 17:53:07 +02:00
void
VarModelTable::setAR(map<string, map<tuple<int, int, int>, expr_t>> AR_arg)
2018-09-06 17:53:07 +02:00
{
AR = move(AR_arg);
}
void
VarModelTable::setA0(map<string, map<tuple<int, int>, expr_t>> A0_arg)
{
A0 = move(A0_arg);
}
void
VarModelTable::setConstants(map<string, map<int, expr_t>> constants_arg)
{
constants = move(constants_arg);
}
const vector<int>&
VarModelTable::getMaxLags(const string& name_arg) const
2018-08-21 11:46:59 +02:00
{
checkModelName(name_arg);
return max_lags.at(name_arg);
2018-08-21 11:46:59 +02:00
}
int
VarModelTable::getMaxLag(const string& name_arg) const
2018-08-21 11:46:59 +02:00
{
vector<int> maxlags {getMaxLags(name_arg)};
return reduce(maxlags.begin(), maxlags.end(), 0, [](int a, int b) { return max(a, b); });
2018-08-21 11:46:59 +02:00
}
const vector<int>&
VarModelTable::getLhs(const string& name_arg) const
2018-08-21 11:46:59 +02:00
{
checkModelName(name_arg);
return lhs.at(name_arg);
2018-08-21 11:46:59 +02:00
}
const vector<int>&
VarModelTable::getLhsOrigIds(const string& name_arg) const
{
checkModelName(name_arg);
return lhs_orig_symb_ids.at(name_arg);
}
const vector<set<pair<int, int>>>&
VarModelTable::getRhs(const string& name_arg) const
2018-08-21 11:46:59 +02:00
{
checkModelName(name_arg);
return rhs.at(name_arg);
2018-08-21 11:46:59 +02:00
}
const vector<expr_t>&
VarModelTable::getLhsExprT(const string& name_arg) const
2018-08-21 11:46:59 +02:00
{
checkModelName(name_arg);
return lhs_expr_t.at(name_arg);
2018-08-21 11:46:59 +02:00
}
VarExpectationModelTable::VarExpectationModelTable(SymbolTable& symbol_table_arg) :
symbol_table {symbol_table_arg}
{
}
void
VarExpectationModelTable::addVarExpectationModel(string name_arg, expr_t expression_arg,
string aux_model_name_arg, string horizon_arg,
expr_t discount_arg, int time_shift_arg)
{
if (isExistingVarExpectationModelName(name_arg))
{
cerr << "Error: a var_expectation_model already exists with the name " << name_arg << endl;
exit(EXIT_FAILURE);
}
expression[name_arg] = expression_arg;
aux_model_name[name_arg] = move(aux_model_name_arg);
horizon[name_arg] = move(horizon_arg);
discount[name_arg] = discount_arg;
time_shift[name_arg] = time_shift_arg;
names.insert(move(name_arg));
}
bool
VarExpectationModelTable::isExistingVarExpectationModelName(const string& name_arg) const
{
return names.contains(name_arg);
}
bool
VarExpectationModelTable::empty() const
{
return names.empty();
}
void
VarExpectationModelTable::writeOutput(ostream& output) const
{
for (const auto& name : names)
{
string mstruct = "M_.var_expectation." + name;
output << mstruct << ".auxiliary_model_name = '" << aux_model_name.at(name) << "';" << endl
<< mstruct << ".horizon = " << horizon.at(name) << ';' << endl
<< mstruct << ".time_shift = " << time_shift.at(name) << ';' << endl;
auto& vpc = vars_params_constants.at(name);
if (!vpc.size())
{
cerr << "ERROR: VarExpectationModelStatement::writeOutput: matchExpression() has not "
"been called"
<< endl;
exit(EXIT_FAILURE);
}
ostringstream vars_list, params_list, constants_list;
for (bool printed_something {false}; const auto& [variable_id, param_id, constant] : vpc)
{
if (exchange(printed_something, true))
{
vars_list << ", ";
params_list << ", ";
constants_list << ", ";
}
vars_list << symbol_table.getTypeSpecificID(variable_id) + 1;
if (param_id)
params_list << symbol_table.getTypeSpecificID(*param_id) + 1;
else
params_list << "NaN";
constants_list << constant;
}
output << mstruct << ".expr.vars = [ " << vars_list.str() << " ];" << endl
<< mstruct << ".expr.params = [ " << params_list.str() << " ];" << endl
<< mstruct << ".expr.constants = [ " << constants_list.str() << " ];" << endl;
if (auto disc_var = dynamic_cast<const VariableNode*>(discount.at(name)); disc_var)
output << mstruct
<< ".discount_index = " << symbol_table.getTypeSpecificID(disc_var->symb_id) + 1
<< ';' << endl;
else
{
output << mstruct << ".discount_value = ";
discount.at(name)->writeOutput(output);
output << ';' << endl;
}
output << mstruct << ".param_indices = [ ";
for (int param_id : aux_param_symb_ids.at(name))
output << symbol_table.getTypeSpecificID(param_id) + 1 << ' ';
output << "];" << endl;
}
}
void
VarExpectationModelTable::substituteUnaryOpsInExpression(const lag_equivalence_table_t& nodes,
ExprNode::subst_table_t& subst_table,
vector<BinaryOpNode*>& neweqs)
{
for (const auto& name : names)
expression[name] = expression[name]->substituteUnaryOpNodes(nodes, subst_table, neweqs);
}
void
VarExpectationModelTable::substituteDiffNodesInExpression(const lag_equivalence_table_t& nodes,
ExprNode::subst_table_t& subst_table,
vector<BinaryOpNode*>& neweqs)
{
for (const auto& name : names)
expression[name] = expression[name]->substituteDiff(nodes, subst_table, neweqs);
}
void
VarExpectationModelTable::transformPass(ExprNode::subst_table_t& diff_subst_table,
DynamicModel& dynamic_model,
const VarModelTable& var_model_table,
const TrendComponentModelTable& trend_component_model_table)
{
map<string, expr_t> var_expectation_subst_table;
for (const auto& name : names)
{
// Collect information about the auxiliary model
int max_lag;
vector<int> lhs;
if (var_model_table.isExistingVarModelName(aux_model_name[name]))
{
max_lag = var_model_table.getMaxLag(aux_model_name[name]);
lhs = var_model_table.getLhs(aux_model_name[name]);
}
else if (trend_component_model_table.isExistingTrendComponentModelName(aux_model_name[name]))
{
max_lag = trend_component_model_table.getMaxLag(aux_model_name[name]) + 1;
lhs = dynamic_model.getUndiffLHSForPac(aux_model_name[name], diff_subst_table);
}
else
{
cerr << "ERROR: var_expectation_model " << name
<< " refers to nonexistent auxiliary model " << aux_model_name[name] << endl;
exit(EXIT_FAILURE);
}
// Match the linear combination in the expression option
try
{
auto vpc = expression[name]->matchLinearCombinationOfVariables();
for (const auto& [variable_id, lag, param_id, constant] : vpc)
{
if (lag != 0)
throw ExprNode::MatchFailureException {"lead/lags are not allowed"};
if (symbol_table.getType(variable_id) != SymbolType::endogenous)
throw ExprNode::MatchFailureException {"Variable is not an endogenous"};
vars_params_constants[name].emplace_back(variable_id, param_id, constant);
}
}
catch (ExprNode::MatchFailureException& e)
{
cerr << "ERROR: expression in var_expectation_model " << name
<< " is not of the expected form: " << e.message << endl;
exit(EXIT_FAILURE);
}
/* Create auxiliary parameters and the expression to be substituted into
the var_expectations statement */
expr_t subst_expr = dynamic_model.Zero;
if (var_model_table.isExistingVarModelName(aux_model_name[name]))
{
/* If the auxiliary model is a VAR, add a parameter corresponding to
the constant. */
string constant_param_name = "var_expectation_model_" + name + "_constant";
int constant_param_id
= symbol_table.addSymbol(constant_param_name, SymbolType::parameter);
aux_param_symb_ids[name].push_back(constant_param_id);
subst_expr
= dynamic_model.AddPlus(subst_expr, dynamic_model.AddVariable(constant_param_id));
}
for (int lag = 0; lag < max_lag; lag++)
for (auto variable : lhs)
{
string param_name = "var_expectation_model_" + name + '_'
+ symbol_table.getName(variable) + '_' + to_string(lag);
int new_param_id = symbol_table.addSymbol(param_name, SymbolType::parameter);
aux_param_symb_ids[name].push_back(new_param_id);
subst_expr = dynamic_model.AddPlus(
subst_expr, dynamic_model.AddTimes(
dynamic_model.AddVariable(new_param_id),
dynamic_model.AddVariable(variable, -lag + time_shift[name])));
}
if (var_expectation_subst_table.contains(name))
{
cerr << "ERROR: model name '" << name
<< "' is used by several var_expectation_model statements" << endl;
exit(EXIT_FAILURE);
}
var_expectation_subst_table[name] = subst_expr;
}
// Actually substitute var_expectation statements
dynamic_model.substituteVarExpectation(var_expectation_subst_table);
/* At this point, we know that all var_expectation operators have been
substituted, because of the error check performed in
VarExpectationNode::substituteVarExpectation(). */
}
void
VarExpectationModelTable::writeJsonOutput(ostream& output) const
{
for (bool printed_something {false}; const auto& name : names)
{
if (exchange(printed_something, true))
output << ", ";
output << R"({"statementName": "var_expectation_model",)"
<< R"("model_name": ")" << name << R"(", )"
<< R"("expression": ")";
expression.at(name)->writeOutput(output);
output << R"(", )"
<< R"("auxiliary_model_name": ")" << aux_model_name.at(name) << R"(", )"
<< R"("horizon": ")" << horizon.at(name) << R"(", )"
<< R"("discount": ")";
discount.at(name)->writeOutput(output);
output << R"(", )"
<< R"("time_shift": )" << time_shift.at(name) << R"(})";
}
}
PacModelTable::PacModelTable(SymbolTable& symbol_table_arg) : symbol_table {symbol_table_arg}
{
}
void
PacModelTable::addPacModel(string name_arg, string aux_model_name_arg, string discount_arg,
expr_t growth_arg, string auxname_arg, PacTargetKind kind_arg)
{
if (isExistingPacModelName(name_arg))
{
cerr << "Error: a PAC model already exists with the name " << name_arg << endl;
exit(EXIT_FAILURE);
}
aux_model_name[name_arg] = move(aux_model_name_arg);
discount[name_arg] = move(discount_arg);
growth[name_arg] = growth_arg;
original_growth[name_arg] = growth_arg;
auxname[name_arg] = move(auxname_arg);
kind[name_arg] = kind_arg;
names.insert(move(name_arg));
}
bool
PacModelTable::isExistingPacModelName(const string& name_arg) const
{
return names.contains(name_arg);
}
bool
PacModelTable::empty() const
{
return names.empty();
}
void
PacModelTable::checkPass(ModFileStructure& mod_file_struct)
{
for (auto& [name, gv] : growth)
if (gv)
{
if (target_info.contains(name))
{
cerr << "ERROR: for PAC model '" << name
<< "', it is not possible to declare a 'growth' option in the 'pac_model' command "
"when there is also a 'pac_target_info' block"
<< endl;
exit(EXIT_FAILURE);
}
gv->collectVariables(SymbolType::exogenous, mod_file_struct.pac_params);
}
for (auto& [name, auxn] : auxname)
if (!auxn.empty() && target_info.contains(name))
{
cerr << "ERROR: for PAC model '" << name
<< "', it is not possible to declare an 'auxname' option in the 'pac_model' command "
"when there is also a 'pac_target_info' block"
<< endl;
exit(EXIT_FAILURE);
}
for (auto& [name, k] : kind)
if (k != PacTargetKind::unspecified)
{
if (target_info.contains(name))
{
cerr << "ERROR: for PAC model '" << name
<< "', it is not possible to declare a 'kind' option in the 'pac_model' command "
"when there is also a 'pac_target_info' block"
<< endl;
exit(EXIT_FAILURE);
}
if (aux_model_name[name].empty())
{
cerr << "ERROR: for PAC model '" << name
<< "', it is not possible to declare a 'kind' option in the 'pac_model' command "
"since this is a MCE model"
<< endl;
exit(EXIT_FAILURE);
}
}
for (const auto& [name, ti] : target_info)
for (auto& [expr, gv, auxname, kind, coeff, growth_neutrality_param, h_indices, original_gv,
gv_info] : get<2>(ti))
if (gv)
gv->collectVariables(SymbolType::exogenous, mod_file_struct.pac_params);
for (const auto& [name, ti] : target_info)
{
auto& [target, auxname_target_nonstationary, components] = ti;
if (!target)
{
cerr << "ERROR: the block 'pac_target_info(" << name
<< ")' is missing the 'target' statement" << endl;
exit(EXIT_FAILURE);
}
if (auxname_target_nonstationary.empty())
{
cerr << "ERROR: the block 'pac_target_info(" << name
<< ")' is missing the 'auxname_target_nonstationary' statement" << endl;
exit(EXIT_FAILURE);
}
int nonstationary_nb = 0;
for (auto& [component, growth_component, auxname, kind, coeff, growth_neutrality_param,
h_indices, original_growth_component, growth_component_info] : components)
{
if (auxname.empty())
{
cerr << "ERROR: the block 'pac_target_info(" << name
<< ")' is missing the 'auxname' statement in some 'component'" << endl;
exit(EXIT_FAILURE);
}
if (kind == PacTargetKind::unspecified)
{
cerr << "ERROR: the block 'pac_target_info(" << name
<< ")' is missing the 'kind' statement in some 'component'" << endl;
exit(EXIT_FAILURE);
}
if (kind == PacTargetKind::ll && growth_component)
{
cerr << "ERROR: in the block 'pac_target_info(" << name
<< ")', a component of 'kind ll' (i.e. stationary) has a 'growth' option. This "
"is not permitted."
<< endl;
exit(EXIT_FAILURE);
}
if (kind == PacTargetKind::dd || kind == PacTargetKind::dl)
nonstationary_nb++;
}
if (!nonstationary_nb)
{
cerr << "ERROR: the block 'pac_target_info(" << name
<< ")' must contain at least one nonstationary component (i.e. of 'kind' equal to "
"either 'dd' or 'dl')."
<< endl;
exit(EXIT_FAILURE);
}
}
}
void
PacModelTable::substituteUnaryOpsInGrowth(const lag_equivalence_table_t& nodes,
ExprNode::subst_table_t& subst_table,
vector<BinaryOpNode*>& neweqs)
{
for (auto& [name, gv] : growth)
if (gv)
gv = gv->substituteUnaryOpNodes(nodes, subst_table, neweqs);
for (auto& [name, ti] : target_info)
for (auto& [expr, gv, auxname, kind, coeff, growth_neutrality_param, h_indices, original_gv,
gv_info] : get<2>(ti))
if (gv)
gv = gv->substituteUnaryOpNodes(nodes, subst_table, neweqs);
}
void
PacModelTable::findDiffNodesInGrowth(lag_equivalence_table_t& diff_nodes) const
{
for (auto& [name, gv] : growth)
if (gv)
gv->findDiffNodes(diff_nodes);
for (const auto& [name, ti] : target_info)
for (auto& [expr, gv, auxname, kind, coeff, growth_neutrality_param, h_indices, original_gv,
gv_info] : get<2>(ti))
if (gv)
gv->findDiffNodes(diff_nodes);
}
void
PacModelTable::substituteDiffNodesInGrowth(const lag_equivalence_table_t& diff_nodes,
ExprNode::subst_table_t& diff_subst_table,
vector<BinaryOpNode*>& neweqs)
{
for (auto& [name, gv] : growth)
if (gv)
gv = gv->substituteDiff(diff_nodes, diff_subst_table, neweqs);
for (auto& [name, ti] : target_info)
for (auto& [expr, gv, auxname, kind, coeff, growth_neutrality_param, h_indices, original_gv,
gv_info] : get<2>(ti))
if (gv)
gv = gv->substituteDiff(diff_nodes, diff_subst_table, neweqs);
}
void
PacModelTable::transformPass(const lag_equivalence_table_t& unary_ops_nodes,
ExprNode::subst_table_t& unary_ops_subst_table,
const lag_equivalence_table_t& diff_nodes,
ExprNode::subst_table_t& diff_subst_table, DynamicModel& dynamic_model,
const VarModelTable& var_model_table,
const TrendComponentModelTable& trend_component_model_table)
{
// model name → expression for pac_expectation
map<string, expr_t> pac_expectation_substitution;
for (const auto& name : names)
{
/* Fill the growth_info structure.
Cannot be done in an earlier pass since growth terms can be
transformed by DynamicModel::substituteDiff(). */
if (growth[name])
try
{
growth_info[name] = growth[name]->matchLinearCombinationOfVariablesPlusConstant();
}
catch (ExprNode::MatchFailureException& e)
{
cerr << "ERROR: PAC growth must be a linear combination of variables" << endl;
exit(EXIT_FAILURE);
}
// Perform transformations for the pac_target_info block (if any)
if (target_info.contains(name))
{
// Substitute unary ops and diffs in the target…
expr_t& target = get<0>(target_info[name]);
vector<BinaryOpNode*> neweqs;
target = target->substituteUnaryOpNodes(unary_ops_nodes, unary_ops_subst_table, neweqs);
if (neweqs.size() > 0)
{
cerr
<< "ERROR: the 'target' expression of 'pac_target_info(" << name
<< ")' contains a variable with a unary operator that is not present in the model"
<< endl;
exit(EXIT_FAILURE);
}
target = target->substituteDiff(diff_nodes, diff_subst_table, neweqs);
if (neweqs.size() > 0)
{
cerr << "ERROR: the 'target' expression of 'pac_target_info(" << name
<< ")' contains a diff'd variable that is not present in the model" << endl;
exit(EXIT_FAILURE);
}
// …and in component expressions
auto& components = get<2>(target_info[name]);
for (auto& [component, growth_component, auxname, kind, coeff, growth_neutrality_param,
h_indices, original_growth_component, growth_component_info] : components)
{
component = component->substituteUnaryOpNodes(unary_ops_nodes, unary_ops_subst_table,
neweqs);
if (neweqs.size() > 0)
{
cerr << "ERROR: a 'component' expression of 'pac_target_info(" << name
<< ")' contains a variable with a unary operator that is not present in the "
"model"
<< endl;
exit(EXIT_FAILURE);
}
component = component->substituteDiff(diff_nodes, diff_subst_table, neweqs);
if (neweqs.size() > 0)
{
cerr << "ERROR: a 'component' expression of 'pac_target_info(" << name
<< ")' contains a diff'd variable that is not present in the model" << endl;
exit(EXIT_FAILURE);
}
}
/* Fill the growth_info structure.
Cannot be done in an earlier pass since growth terms can be
transformed by DynamicModel::substituteDiff(). */
for (auto& [component, growth_component, auxname, kind, coeff, growth_neutrality_param,
h_indices, original_growth_component, growth_component_info] : components)
{
if (growth_component)
try
{
growth_component_info
= growth_component->matchLinearCombinationOfVariablesPlusConstant();
}
catch (ExprNode::MatchFailureException& e)
{
cerr << "ERROR: PAC growth must be a linear combination of variables" << endl;
exit(EXIT_FAILURE);
}
}
// Identify the model equation defining the target
expr_t target_expr;
try
{
target_expr = dynamic_model.getRHSFromLHS(target);
}
catch (ExprNode::MatchFailureException)
{
cerr << "ERROR: there is no equation whose LHS is equal to the 'target' of "
"'pac_target_info("
<< name << ")'" << endl;
exit(EXIT_FAILURE);
}
// Substitute unary ops and diffs in that equation, before parsing (see dynare#1837)
target_expr
= target_expr->substituteUnaryOpNodes(unary_ops_nodes, unary_ops_subst_table, neweqs);
if (neweqs.size() > 0)
{
cerr
<< "ERROR: the equation defining the target of 'pac_target_info(" << name
<< ")' contains a variable with a unary operator that is not present in the model"
<< endl;
exit(EXIT_FAILURE);
}
target_expr = target_expr->substituteDiff(diff_nodes, diff_subst_table, neweqs);
if (neweqs.size() > 0)
{
cerr << "ERROR: the equation defining the target of 'pac_target_info(" << name
<< ")' contains a diff'd variable that is not present in the model" << endl;
exit(EXIT_FAILURE);
}
// Parse that model equation
vector<pair<int, expr_t>> terms;
expr_t constant;
try
{
tie(terms, constant) = target_expr->matchLinearCombinationOfEndogenousWithConstant();
}
catch (ExprNode::MatchFailureException)
{
cerr << "ERROR: the model equation defining the 'target' of 'pac_target_info(" << name
<< ")' is not of the right form (should be a linear combination of endogenous "
"variables)"
<< endl;
exit(EXIT_FAILURE);
}
// Associate the coefficients of the linear combination with the right components
for (auto [var, coeff] : terms)
if (auto it = find_if(
components.begin(), components.end(),
[&](const auto& v) { return get<0>(v) == dynamic_model.AddVariable(var); });
it != components.end())
get<4>(*it) = coeff;
else
{
cerr << "ERROR: the model equation defining the 'target' of 'pac_target_info("
<< name << ")' contains a variable (" << symbol_table.getName(var)
<< ") that is not declared as a 'component'" << endl;
exit(EXIT_FAILURE);
}
// Verify that all declared components appear in that equation
for (const auto& [component, growth_component, auxname, kind, coeff,
growth_neutrality_param, h_indices, original_growth_component,
growth_component_info] : components)
if (!coeff)
{
cerr << "ERROR: a 'component' of 'pac_target_info(" << name
<< ")' does not appear in the model equation defining the 'target'" << endl;
exit(EXIT_FAILURE);
}
/* Add the variable and equation defining the stationary part of the
target. Note that it includes the constant. */
expr_t yns = constant;
for (const auto& [component, growth_component, auxname, kind, coeff,
growth_neutrality_param, h_indices, original_growth_component,
growth_component_info] : components)
if (kind != PacTargetKind::ll)
yns = dynamic_model.AddPlus(yns, dynamic_model.AddTimes(coeff, component));
int target_nonstationary_id
= symbol_table.addPacTargetNonstationaryAuxiliaryVar(get<1>(target_info[name]), yns);
expr_t neweq
= dynamic_model.AddEqual(dynamic_model.AddVariable(target_nonstationary_id), yns);
dynamic_model.addEquation(neweq, nullopt);
dynamic_model.addAuxEquation(neweq);
/* Perform the substitution of the pac_target_nonstationary operator.
This needs to be done here, otherwise
DynamicModel::analyzePacEquationStructure() will not be able to
identify the error-correction part */
dynamic_model.substitutePacTargetNonstationary(
name, dynamic_model.AddVariable(target_nonstationary_id, -1));
}
// Collect some information about PAC models
int max_lag;
if (trend_component_model_table.isExistingTrendComponentModelName(aux_model_name[name]))
{
aux_model_type[name] = "trend_component";
max_lag = trend_component_model_table.getMaxLag(aux_model_name[name]) + 1;
lhs[name] = dynamic_model.getUndiffLHSForPac(aux_model_name[name], diff_subst_table);
}
else if (var_model_table.isExistingVarModelName(aux_model_name[name]))
{
aux_model_type[name] = "var";
max_lag = var_model_table.getMaxLag(aux_model_name[name]);
lhs[name] = var_model_table.getLhs(aux_model_name[name]);
}
else if (aux_model_name[name].empty())
max_lag = 0;
else
{
cerr << "Error: aux_model_name not recognized as VAR model or Trend Component model"
<< endl;
exit(EXIT_FAILURE);
}
dynamic_model.analyzePacEquationStructure(name, eq_name, equation_info);
// Declare parameter for growth neutrality correction
if (growth[name])
{
string param_name = name + "_pac_growth_neutrality_correction";
try
{
int param_idx = symbol_table.addSymbol(param_name, SymbolType::parameter);
growth_neutrality_params[name] = param_idx;
}
catch (SymbolTable::AlreadyDeclaredException)
{
cerr << "The variable/parameter '" << param_name
<< "' conflicts with the auxiliary parameter that will be generated for the "
"growth neutrality correction of the '"
<< name << "' PAC model. Please rename that parameter." << endl;
exit(EXIT_FAILURE);
}
}
// Compute the expressions that will be substituted for the pac_expectation operators
expr_t growth_correction_term = dynamic_model.Zero;
if (growth[name])
growth_correction_term = dynamic_model.AddTimes(
growth[name], dynamic_model.AddVariable(growth_neutrality_params[name]));
if (aux_model_name[name].empty())
{
if (target_info.contains(name))
{
cerr << "ERROR: the block 'pac_target_info(" << name
<< ")' is not supported in the context of a PAC model with model-consistent "
"expectations (MCE)."
<< endl;
exit(EXIT_FAILURE);
}
else
dynamic_model.computePacModelConsistentExpectationSubstitution(
name, symbol_table.getID(discount[name]), pacEquationMaxLag(name),
growth_correction_term, auxname[name], diff_subst_table, aux_var_symb_ids,
aux_param_symb_ids, pac_expectation_substitution);
}
else
{
if (target_info.contains(name))
{
assert(growth_correction_term == dynamic_model.Zero);
dynamic_model.computePacBackwardExpectationSubstitutionWithComponents(
name, lhs[name], max_lag, aux_model_type[name], get<2>(target_info[name]),
pac_expectation_substitution);
}
else
dynamic_model.computePacBackwardExpectationSubstitution(
name, lhs[name], max_lag, aux_model_type[name], growth_correction_term,
auxname[name], aux_var_symb_ids, aux_param_symb_ids, pac_expectation_substitution);
}
}
// Actually perform the substitution of pac_expectation
dynamic_model.substitutePacExpectation(pac_expectation_substitution, eq_name);
dynamic_model.checkNoRemainingPacExpectation();
// Check that there is no remaining pac_target_nonstationary operator
dynamic_model.checkNoRemainingPacTargetNonstationary();
}
void
PacModelTable::writeOutput(ostream& output) const
{
// Helper to print the “growth_info” structure (linear decomposition of growth)
auto growth_info_helper = [&](const string& fieldname, const growth_info_t& gi) {
for (int i {1}; auto [growth_symb_id, growth_lag, param_id, constant] : gi)
{
string structname = fieldname + "(" + to_string(i++) + ").";
if (growth_symb_id)
{
string var_field = "endo_id";
if (symbol_table.getType(*growth_symb_id) == SymbolType::exogenous)
{
var_field = "exo_id";
output << structname << "endo_id = 0;" << endl;
}
else
output << structname << "exo_id = 0;" << endl;
try
{
// case when this is not the highest lag of the growth variable
int aux_symb_id = symbol_table.searchAuxiliaryVars(*growth_symb_id, growth_lag);
output << structname << var_field << " = "
<< symbol_table.getTypeSpecificID(aux_symb_id) + 1 << ";" << endl
<< structname << "lag = 0;" << endl;
}
catch (...)
{
try
{
// case when this is the highest lag of the growth variable
int tmp_growth_lag = growth_lag + 1;
int aux_symb_id
= symbol_table.searchAuxiliaryVars(*growth_symb_id, tmp_growth_lag);
output << structname << var_field << " = "
<< symbol_table.getTypeSpecificID(aux_symb_id) + 1 << ";" << endl
<< structname << "lag = -1;" << endl;
}
catch (...)
{
// case when there is no aux var for the variable
output << structname << var_field << " = "
<< symbol_table.getTypeSpecificID(*growth_symb_id) + 1 << ";" << endl
<< structname << "lag = " << growth_lag << ";" << endl;
}
}
}
else
output << structname << "endo_id = 0;" << endl
<< structname << "exo_id = 0;" << endl
<< structname << "lag = 0;" << endl;
output << structname
<< "param_id = " << (param_id ? symbol_table.getTypeSpecificID(*param_id) + 1 : 0)
<< ";" << endl
<< structname << "constant = " << constant << ";" << endl;
}
};
for (const auto& name : names)
{
output << "M_.pac." << name << ".auxiliary_model_name = '" << aux_model_name.at(name) << "';"
<< endl
<< "M_.pac." << name
<< ".discount_index = " << symbol_table.getTypeSpecificID(discount.at(name)) + 1 << ";"
<< endl;
if (growth.at(name))
{
output << "M_.pac." << name << ".growth_str = '";
original_growth.at(name)->writeJsonOutput(output, {}, {}, true);
output << "';" << endl;
growth_info_helper("M_.pac." + name + ".growth_linear_comb", growth_info.at(name));
}
}
// Write the auxiliary parameter IDs created for the pac_expectation operator
for (auto& [name, ids] : aux_param_symb_ids)
{
output << "M_.pac." << name << "."
<< (aux_model_name.at(name).empty() ? "mce.alpha" : "h_param_indices") << " = [";
for (auto id : ids)
output << symbol_table.getTypeSpecificID(id) + 1 << " ";
output << "];" << endl;
}
// Write the auxiliary variable IDs created for the pac_expectation operator
for (auto& [name, id] : aux_var_symb_ids)
output << "M_.pac." << name << "." << (aux_model_name.at(name).empty() ? "mce.z1" : "aux_id")
<< " = " << symbol_table.getTypeSpecificID(id) + 1 << ";" << endl;
// Write PAC equation name info
for (auto& [name, eq] : eq_name)
output << "M_.pac." << name << ".eq_name = '" << eq << "';" << endl;
for (auto& [model, growth_neutrality_param_index] : growth_neutrality_params)
output << "M_.pac." << model << ".growth_neutrality_param_index = "
<< symbol_table.getTypeSpecificID(growth_neutrality_param_index) + 1 << ";" << endl;
for (auto& [model, type] : aux_model_type)
output << "M_.pac." << model << ".auxiliary_model_type = '" << type << "';" << endl;
for (auto& [name, k] : kind)
if (!aux_model_name.empty())
output << "M_.pac." << name << ".kind = '"
<< (k == PacTargetKind::unspecified ? "" : kindToString(k)) << "';" << endl;
for (auto& [name, val] : equation_info)
{
auto& [lhs_pac_var, optim_share_index, ar_params_and_vars, ec_params_and_vars,
non_optim_vars_params_and_constants, additive_vars_params_and_constants,
optim_additive_vars_params_and_constants]
= val;
output << "M_.pac." << name
<< ".lhs_var = " << symbol_table.getTypeSpecificID(lhs_pac_var.first) + 1 << ";"
<< endl;
if (optim_share_index)
output << "M_.pac." << name << ".share_of_optimizing_agents_index = "
<< symbol_table.getTypeSpecificID(*optim_share_index) + 1 << ";" << endl;
output << "M_.pac." << name
<< ".ec.params = " << symbol_table.getTypeSpecificID(ec_params_and_vars.first) + 1
<< ";" << endl
<< "M_.pac." << name << ".ec.vars = [";
for (auto& it : ec_params_and_vars.second)
output << symbol_table.getTypeSpecificID(get<0>(it)) + 1 << " ";
output << "];" << endl << "M_.pac." << name << ".ec.istarget = [";
for (auto& it : ec_params_and_vars.second)
output << boolalpha << get<1>(it) << " ";
output << "];" << endl << "M_.pac." << name << ".ec.scale = [";
for (auto& it : ec_params_and_vars.second)
output << get<2>(it) << " ";
output << "];" << endl << "M_.pac." << name << ".ec.isendo = [";
for (auto& it : ec_params_and_vars.second)
switch (symbol_table.getType(get<0>(it)))
{
case SymbolType::endogenous:
output << "true ";
break;
case SymbolType::exogenous:
output << "false ";
break;
default:
cerr << "expecting endogenous or exogenous" << endl;
exit(EXIT_FAILURE);
}
output << "];" << endl << "M_.pac." << name << ".ar.params = [";
for (auto& [pid, vid, vlag] : ar_params_and_vars)
output << (pid ? symbol_table.getTypeSpecificID(*pid) + 1 : -1) << " ";
output << "];" << endl << "M_.pac." << name << ".ar.vars = [";
for (auto& [pid, vid, vlag] : ar_params_and_vars)
output << (vid ? symbol_table.getTypeSpecificID(*vid) + 1 : -1) << " ";
output << "];" << endl << "M_.pac." << name << ".ar.lags = [";
for (auto& [pid, vid, vlag] : ar_params_and_vars)
output << vlag << " ";
output << "];" << endl
<< "M_.pac." << name << ".max_lag = " << pacEquationMaxLag(name) << ";" << endl;
if (!non_optim_vars_params_and_constants.empty())
{
output << "M_.pac." << name << ".non_optimizing_behaviour.params = [";
for (auto& it : non_optim_vars_params_and_constants)
if (get<2>(it))
output << symbol_table.getTypeSpecificID(*get<2>(it)) + 1 << " ";
else
output << "NaN ";
output << "];" << endl << "M_.pac." << name << ".non_optimizing_behaviour.vars = [";
for (auto& it : non_optim_vars_params_and_constants)
output << symbol_table.getTypeSpecificID(get<0>(it)) + 1 << " ";
output << "];" << endl << "M_.pac." << name << ".non_optimizing_behaviour.isendo = [";
for (auto& it : non_optim_vars_params_and_constants)
switch (symbol_table.getType(get<0>(it)))
{
case SymbolType::endogenous:
output << "true ";
break;
case SymbolType::exogenous:
output << "false ";
break;
default:
cerr << "expecting endogenous or exogenous" << endl;
exit(EXIT_FAILURE);
}
output << "];" << endl << "M_.pac." << name << ".non_optimizing_behaviour.lags = [";
for (auto& it : non_optim_vars_params_and_constants)
output << get<1>(it) << " ";
output << "];" << endl
<< "M_.pac." << name << ".non_optimizing_behaviour.scaling_factor = [";
for (auto& it : non_optim_vars_params_and_constants)
output << get<3>(it) << " ";
output << "];" << endl;
}
if (!additive_vars_params_and_constants.empty())
{
output << "M_.pac." << name << ".additive.params = [";
for (auto& it : additive_vars_params_and_constants)
if (get<2>(it))
output << symbol_table.getTypeSpecificID(*get<2>(it)) + 1 << " ";
else
output << "NaN ";
output << "];" << endl << "M_.pac." << name << ".additive.vars = [";
for (auto& it : additive_vars_params_and_constants)
output << symbol_table.getTypeSpecificID(get<0>(it)) + 1 << " ";
output << "];" << endl << "M_.pac." << name << ".additive.isendo = [";
for (auto& it : additive_vars_params_and_constants)
switch (symbol_table.getType(get<0>(it)))
{
case SymbolType::endogenous:
output << "true ";
break;
case SymbolType::exogenous:
output << "false ";
break;
default:
cerr << "expecting endogenous or exogenous" << endl;
exit(EXIT_FAILURE);
}
output << "];" << endl << "M_.pac." << name << ".additive.lags = [";
for (auto& it : additive_vars_params_and_constants)
output << get<1>(it) << " ";
output << "];" << endl << "M_.pac." << name << ".additive.scaling_factor = [";
for (auto& it : additive_vars_params_and_constants)
output << get<3>(it) << " ";
output << "];" << endl;
}
if (!optim_additive_vars_params_and_constants.empty())
{
output << "M_.pac." << name << ".optim_additive.params = [";
for (auto& it : optim_additive_vars_params_and_constants)
if (get<2>(it))
output << symbol_table.getTypeSpecificID(*get<2>(it)) + 1 << " ";
else
output << "NaN ";
output << "];" << endl << "M_.pac." << name << ".optim_additive.vars = [";
for (auto& it : optim_additive_vars_params_and_constants)
output << symbol_table.getTypeSpecificID(get<0>(it)) + 1 << " ";
output << "];" << endl << "M_.pac." << name << ".optim_additive.isendo = [";
for (auto& it : optim_additive_vars_params_and_constants)
switch (symbol_table.getType(get<0>(it)))
{
case SymbolType::endogenous:
output << "true ";
break;
case SymbolType::exogenous:
output << "false ";
break;
default:
cerr << "expecting endogenous or exogenous" << endl;
exit(EXIT_FAILURE);
}
output << "];" << endl << "M_.pac." << name << ".optim_additive.lags = [";
for (auto& it : optim_additive_vars_params_and_constants)
output << get<1>(it) << " ";
output << "];" << endl << "M_.pac." << name << ".optim_additive.scaling_factor = [";
for (auto& it : optim_additive_vars_params_and_constants)
output << get<3>(it) << " ";
output << "];" << endl;
}
}
for (auto& [name, val] : target_info)
for (int component_idx {1};
auto& [component, growth_component, auxname, kind, coeff, growth_neutrality_param,
h_indices, original_growth_component, growth_component_info] : get<2>(val))
{
string fieldname = "M_.pac." + name + ".components(" + to_string(component_idx) + ")";
output << fieldname << ".aux_id = " << symbol_table.getTypeSpecificID(auxname) + 1 << ";"
<< endl
<< fieldname << ".endo_var = "
<< symbol_table.getTypeSpecificID(dynamic_cast<VariableNode*>(component)->symb_id)
+ 1
<< ";" << endl
<< fieldname << ".kind = '" << kindToString(kind) << "';" << endl
<< fieldname << ".h_param_indices = [";
for (int id : h_indices)
output << symbol_table.getTypeSpecificID(id) + 1 << " ";
output << "];" << endl << fieldname << ".coeff_str = '";
coeff->writeJsonOutput(output, {}, {}, true);
output << "';" << endl;
if (growth_component)
{
output << fieldname << ".growth_neutrality_param_index = "
<< symbol_table.getTypeSpecificID(growth_neutrality_param) + 1 << ";" << endl
<< fieldname << ".growth_str = '";
original_growth_component->writeJsonOutput(output, {}, {}, true);
output << "';" << endl;
growth_info_helper(fieldname + ".growth_linear_comb", growth_component_info);
}
component_idx++;
}
}
void
PacModelTable::writeJsonOutput(ostream& output) const
{
for (bool printed_something {false}; const auto& name : names)
{
/* The calling method has already added a comma, so dont output one for
the first statement */
if (exchange(printed_something, true))
output << ", ";
output << R"({"statementName": "pac_model",)"
<< R"("model_name": ")" << name << R"(",)"
<< R"("auxiliary_model_name": ")" << aux_model_name.at(name) << R"(",)"
<< R"("discount_index": )" << symbol_table.getTypeSpecificID(discount.at(name)) + 1;
if (growth.at(name))
{
output << R"(,"growth_str": ")";
original_growth.at(name)->writeJsonOutput(output, {}, {}, true);
output << R"(")";
}
output << "}" << endl;
}
for (auto& [name, val] : target_info)
{
output << R"(, {"statementName": "pac_target_info", "model_name": ")" << name
<< R"(", "target": ")";
get<0>(val)->writeJsonOutput(output, {}, {}, true);
output << R"(", "auxname_target_nonstationary": ")" << get<1>(val) << R"(", "components": [)";
for (auto& [component, growth_component, auxname, kind, coeff, growth_neutrality_param,
h_indices, original_growth_component, growth_component_info] : get<2>(val))
{
if (component != get<0>(get<2>(val).front()))
output << ", ";
output << R"({"component": ")";
component->writeJsonOutput(output, {}, {}, true);
output << R"(", "auxname": ")" << auxname << R"(", "kind": ")" << kindToString(kind);
if (growth_component)
{
output << R"(", "growth_str": ")";
original_growth_component->writeJsonOutput(output, {}, {}, true);
}
output << R"("})";
}
output << "]}" << endl;
}
}
int
PacModelTable::pacEquationMaxLag(const string& name_arg) const
{
return get<2>(equation_info.at(name_arg)).size();
}
string
PacModelTable::kindToString(PacTargetKind kind)
{
switch (kind)
{
case PacTargetKind::unspecified:
cerr << "Internal error: kind should not be unspecified" << endl;
exit(EXIT_FAILURE);
case PacTargetKind::ll:
return "ll";
case PacTargetKind::dl:
return "dl";
case PacTargetKind::dd:
return "dd";
}
__builtin_unreachable(); // Silence GCC warning
}
void
PacModelTable::setTargetExpr(const string& name_arg, expr_t target)
{
get<0>(target_info[name_arg]) = target;
}
void
PacModelTable::setTargetAuxnameNonstationary(const string& name_arg, string auxname)
{
get<1>(target_info[name_arg]) = move(auxname);
}
void
PacModelTable::addTargetComponent(const string& name_arg, target_component_t component)
{
get<7>(component) = get<1>(component); // original_growth = growth
get<2>(target_info[name_arg]).emplace_back(move(component));
}
void
PacModelTable::writeTargetCoefficientsFile(const string& basename) const
{
if (target_info.empty())
return;
filesystem::path filename {DataTree::packageDir(basename) / "pac_target_coefficients.m"};
ofstream output {filename, ios::out | ios::binary};
if (!output.is_open())
{
cerr << "ERROR: Can't open file " << filename.string() << " for writing" << endl;
exit(EXIT_FAILURE);
}
output << "function coeffs = pac_target_coefficients(model_name, params)" << endl;
for (auto& [model_name, val] : target_info)
{
output << " if strcmp(model_name, '" << model_name << "')" << endl
<< " coeffs = NaN(" << get<2>(val).size() << ",1);" << endl;
for (int i {1};
auto& [component, growth_component, auxname, kind, coeff, growth_neutrality_param,
h_indices, original_growth_component, growth_component_info] : get<2>(val))
{
output << " coeffs(" << i++ << ") = ";
coeff->writeOutput(output, ExprNodeOutputType::matlabDynamicModel);
output << ";" << endl;
}
output << " return" << endl << " end" << endl;
}
output << " error([ 'Unknown PAC model: ' model_name ])" << endl << "end" << endl;
output.close();
}