/*
* Copyright © 2003-2023 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 .
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "DynamicModel.hh"
#include "ParsingDriver.hh"
void
DynamicModel::copyHelper(const DynamicModel &m)
{
auto f = [this](const ExprNode *e) { return e->clone(*this); };
for (const auto &it : m.static_only_equations)
static_only_equations.push_back(dynamic_cast(f(it)));
auto convert_block_derivative = [f](const map, expr_t> &dt)
{
map, expr_t> dt2;
for (const auto &it : dt)
dt2.emplace(it.first, f(it.second));
return dt2;
};
for (const auto &it : m.blocks_derivatives_other_endo)
blocks_derivatives_other_endo.emplace_back(convert_block_derivative(it));
for (const auto &it : m.blocks_derivatives_exo)
blocks_derivatives_exo.emplace_back(convert_block_derivative(it));
for (const auto &it : m.blocks_derivatives_exo_det)
blocks_derivatives_exo_det.emplace_back(convert_block_derivative(it));
}
DynamicModel::DynamicModel(SymbolTable &symbol_table_arg,
NumericalConstants &num_constants_arg,
ExternalFunctionsTable &external_functions_table_arg,
TrendComponentModelTable &trend_component_model_table_arg,
VarModelTable &var_model_table_arg) :
ModelTree{symbol_table_arg, num_constants_arg, external_functions_table_arg, true},
trend_component_model_table{trend_component_model_table_arg},
var_model_table{var_model_table_arg}
{
}
DynamicModel::DynamicModel(const DynamicModel &m) :
ModelTree{m},
trend_component_model_table{m.trend_component_model_table},
var_model_table{m.var_model_table},
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},
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},
max_lag{m.max_lag},
max_lead{m.max_lead},
max_endo_lag{m.max_endo_lag},
max_endo_lead{m.max_endo_lead},
max_exo_lag{m.max_exo_lag},
max_exo_lead{m.max_exo_lead},
max_exo_det_lag{m.max_exo_det_lag},
max_exo_det_lead{m.max_exo_det_lead},
max_lag_orig{m.max_lag_orig},
max_lead_orig{m.max_lead_orig},
max_lag_with_diffs_expanded_orig{m.max_lag_with_diffs_expanded_orig},
max_endo_lag_orig{m.max_endo_lag_orig},
max_endo_lead_orig{m.max_endo_lead_orig},
max_exo_lag_orig{m.max_exo_lag_orig},
max_exo_lead_orig{m.max_exo_lead_orig},
max_exo_det_lag_orig{m.max_exo_det_lag_orig},
max_exo_det_lead_orig{m.max_exo_det_lead_orig},
xrefs{m.xrefs},
xref_param{m.xref_param},
xref_endo{m.xref_endo},
xref_exo{m.xref_exo},
xref_exo_det{m.xref_exo_det},
nonzero_hessian_eqs{m.nonzero_hessian_eqs},
variableMapping{m.variableMapping},
blocks_other_endo{m.blocks_other_endo},
blocks_exo{m.blocks_exo},
blocks_exo_det{m.blocks_exo_det},
blocks_jacob_cols_endo{m.blocks_jacob_cols_endo},
blocks_jacob_cols_other_endo{m.blocks_jacob_cols_other_endo},
blocks_jacob_cols_exo{m.blocks_jacob_cols_exo},
blocks_jacob_cols_exo_det{m.blocks_jacob_cols_exo_det},
var_expectation_functions_to_write{m.var_expectation_functions_to_write}
{
copyHelper(m);
}
DynamicModel &
DynamicModel::operator=(const DynamicModel &m)
{
ModelTree::operator=(m);
assert(&trend_component_model_table == &m.trend_component_model_table);
assert(&var_model_table == &m.var_model_table);
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;
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;
max_lag = m.max_lag;
max_lead = m.max_lead;
max_endo_lag = m.max_endo_lag;
max_endo_lead = m.max_endo_lead;
max_exo_lag = m.max_exo_lag;
max_exo_lead = m.max_exo_lead;
max_exo_det_lag = m.max_exo_det_lag;
max_exo_det_lead = m.max_exo_det_lead;
max_lag_orig = m.max_lag_orig;
max_lead_orig = m.max_lead_orig;
max_lag_with_diffs_expanded_orig = m.max_lag_with_diffs_expanded_orig;
max_endo_lag_orig = m.max_endo_lag_orig;
max_endo_lead_orig = m.max_endo_lead_orig;
max_exo_lag_orig = m.max_exo_lag_orig;
max_exo_lead_orig = m.max_exo_lead_orig;
max_exo_det_lag_orig = m.max_exo_det_lag_orig;
max_exo_det_lead_orig = m.max_exo_det_lead_orig;
xrefs = m.xrefs;
xref_param = m.xref_param;
xref_endo = m.xref_endo;
xref_exo = m.xref_exo;
xref_exo_det = m.xref_exo_det;
nonzero_hessian_eqs = m.nonzero_hessian_eqs;
variableMapping = m.variableMapping;
blocks_derivatives_other_endo.clear();
blocks_derivatives_exo.clear();
blocks_derivatives_exo_det.clear();
blocks_other_endo = m.blocks_other_endo;
blocks_exo = m.blocks_exo;
blocks_exo_det = m.blocks_exo_det;
blocks_jacob_cols_endo = m.blocks_jacob_cols_endo;
blocks_jacob_cols_other_endo = m.blocks_jacob_cols_other_endo;
blocks_jacob_cols_exo = m.blocks_jacob_cols_exo;
blocks_jacob_cols_exo_det = m.blocks_jacob_cols_exo_det;
var_expectation_functions_to_write = m.var_expectation_functions_to_write;
copyHelper(m);
return *this;
}
void
DynamicModel::additionalBlockTemporaryTerms(int blk,
vector> &blocks_temporary_terms,
map> &reference_count) const
{
for (const auto &[ignore, d] : blocks_derivatives_exo[blk])
d->computeBlockTemporaryTerms(blk, blocks[blk].size, blocks_temporary_terms, reference_count);
for (const auto &[ignore, d] : blocks_derivatives_exo_det[blk])
d->computeBlockTemporaryTerms(blk, blocks[blk].size, blocks_temporary_terms, reference_count);
for (const auto &[ignore, d] : blocks_derivatives_other_endo[blk])
d->computeBlockTemporaryTerms(blk, blocks[blk].size, blocks_temporary_terms, reference_count);
}
int
DynamicModel::nzeDeterministicJacobianForBlock(int blk) const
{
BlockSimulationType simulation_type = blocks[blk].simulation_type;
int block_recursive_size = blocks[blk].getRecursiveSize();
int nze_deterministic = 0;
if (simulation_type == BlockSimulationType::solveTwoBoundariesComplete
|| simulation_type == BlockSimulationType::solveTwoBoundariesSimple)
nze_deterministic = count_if(blocks_derivatives[blk].begin(), blocks_derivatives[blk].end(),
[=](const auto &kv) {
auto [eq, var, lag] = kv.first;
return eq >= block_recursive_size && var >= block_recursive_size;
});
else if (simulation_type == BlockSimulationType::solveBackwardSimple
|| simulation_type == BlockSimulationType::solveForwardSimple
|| simulation_type == BlockSimulationType::solveBackwardComplete
|| simulation_type == BlockSimulationType::solveForwardComplete)
nze_deterministic = count_if(blocks_derivatives[blk].begin(), blocks_derivatives[blk].end(),
[=](const auto &kv) {
auto [eq, var, lag] = kv.first;
return lag == 0 && eq >= block_recursive_size && var >= block_recursive_size;
});
return nze_deterministic;
}
void
DynamicModel::writeDynamicPerBlockMFiles(const string &basename) const
{
temporary_terms_t temporary_terms; // Temp terms written so far
for (int blk = 0; blk < static_cast(blocks.size()); blk++)
{
BlockSimulationType simulation_type = blocks[blk].simulation_type;
int block_size = blocks[blk].size;
int block_mfs_size = blocks[blk].mfs_size;
// Number of nonzero derivatives for the various Jacobians
int nze_stochastic = blocks_derivatives[blk].size();
int nze_deterministic = nzeDeterministicJacobianForBlock(blk);
int nze_other_endo = blocks_derivatives_other_endo[blk].size();
int nze_exo = blocks_derivatives_exo[blk].size();
int nze_exo_det = blocks_derivatives_exo_det[blk].size();
filesystem::path filename {packageDir(basename) / "+block" / ("dynamic_" + to_string(blk+1) + ".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 << "%" << endl
<< "% " << filename.string() << " : Computes dynamic version of one block" << endl
<< "%" << endl
<< "% Warning : this file is generated automatically by Dynare" << endl
<< "% from model file (.mod)" << endl << endl
<< "%" << endl;
if (simulation_type == BlockSimulationType::evaluateBackward
|| simulation_type == BlockSimulationType::evaluateForward)
output << "function [y, T, g1, varargout] = dynamic_" << blk+1 << "(y, x, params, steady_state, T, it_, stochastic_mode)" << endl;
else
output << "function [residual, y, T, g1, varargout] = dynamic_" << blk+1 << "(y, x, params, steady_state, T, it_, stochastic_mode)" << endl;
output << " % ////////////////////////////////////////////////////////////////////////" << endl
<< " % //" << " Block "sv.substr(static_cast(log10(blk + 1))) << blk+1
<< " //" << endl
<< " % // Simulation type "
<< BlockSim(simulation_type) << " //" << endl
<< " % ////////////////////////////////////////////////////////////////////////" << endl;
if (simulation_type != BlockSimulationType::evaluateForward
&& simulation_type != BlockSimulationType::evaluateBackward)
output << " residual=zeros(" << block_mfs_size << ",1);" << endl;
output << " if stochastic_mode" << endl
<< " g1_i=zeros(" << nze_stochastic << ",1);" << endl
<< " g1_j=zeros(" << nze_stochastic << ",1);" << endl
<< " g1_v=zeros(" << nze_stochastic << ",1);" << endl
<< " g1_x_i=zeros(" << nze_exo << ",1);" << endl
<< " g1_x_j=zeros(" << nze_exo << ",1);" << endl
<< " g1_x_v=zeros(" << nze_exo << ",1);" << endl
<< " g1_xd_i=zeros(" << nze_exo_det << ",1);" << endl
<< " g1_xd_j=zeros(" << nze_exo_det << ",1);" << endl
<< " g1_xd_v=zeros(" << nze_exo_det << ",1);" << endl
<< " g1_o_i=zeros(" << nze_other_endo << ",1);" << endl
<< " g1_o_j=zeros(" << nze_other_endo << ",1);" << endl
<< " g1_o_v=zeros(" << nze_other_endo << ",1);" << endl;
if (simulation_type != BlockSimulationType::evaluateForward
&& simulation_type != BlockSimulationType::evaluateBackward)
output << " else" << endl
<< " g1_i=zeros(" << nze_deterministic << ",1);" << endl
<< " g1_j=zeros(" << nze_deterministic << ",1);" << endl
<< " g1_v=zeros(" << nze_deterministic << ",1);" << endl;
output << " end" << endl
<< endl;
writeDynamicPerBlockHelper(blk, output, temporary_terms,
nze_stochastic, nze_deterministic, nze_exo, nze_exo_det, nze_other_endo);
output << endl
<< " if stochastic_mode" << endl
<< " g1=sparse(g1_i, g1_j, g1_v, " << block_size << ", " << blocks_jacob_cols_endo[blk].size() << ");" << endl
<< " varargout{1}=sparse(g1_x_i, g1_x_j, g1_x_v, " << block_size << ", " << blocks_jacob_cols_exo[blk].size() << ");" << endl
<< " varargout{2}=sparse(g1_xd_i, g1_xd_j, g1_xd_v, " << block_size << ", " << blocks_jacob_cols_exo_det[blk].size() << ");" << endl
<< " varargout{3}=sparse(g1_o_i, g1_o_j, g1_o_v, " << block_size << ", " << blocks_jacob_cols_other_endo[blk].size() << ");" << endl
<< " else" << endl;
switch (simulation_type)
{
case BlockSimulationType::evaluateForward:
case BlockSimulationType::evaluateBackward:
output << " g1=[];" << endl;
break;
case BlockSimulationType::solveBackwardSimple:
case BlockSimulationType::solveForwardSimple:
case BlockSimulationType::solveBackwardComplete:
case BlockSimulationType::solveForwardComplete:
output << " g1=sparse(g1_i, g1_j, g1_v, " << block_mfs_size
<< ", " << block_mfs_size << ");" << endl;
break;
case BlockSimulationType::solveTwoBoundariesSimple:
case BlockSimulationType::solveTwoBoundariesComplete:
output << " g1=sparse(g1_i, g1_j, g1_v, " << block_mfs_size
<< ", " << 3*block_mfs_size << ");" << endl;
break;
default:
break;
}
output << " end" << endl
<< "end" << endl;
output.close();
}
}
void
DynamicModel::writeBlockBytecodeAdditionalDerivatives(BytecodeWriter &code_file, int block,
const temporary_terms_t &temporary_terms_union,
const deriv_node_temp_terms_t &tef_terms) const
{
constexpr ExprNodeBytecodeOutputType output_type {ExprNodeBytecodeOutputType::dynamicModel};
/* FIXME: there is an inconsistency between endos and the following 3 other
variable types. For the latter, the index of equation within the block is
taken from FNUMEXPR, while it is taken from FSTPG3 for the former. */
for (const auto &[indices, d] : blocks_derivatives_exo[block])
{
const auto &[eq, var, lag] {indices};
code_file << FNUMEXPR_{ExpressionType::FirstExoDerivative, eq, 0, lag};
d->writeBytecodeOutput(code_file, output_type, temporary_terms_union, blocks_temporary_terms_idxs, tef_terms);
code_file << FSTPG3_{eq, var, lag, blocks_jacob_cols_exo[block].at({ var, lag })};
}
for (const auto &[indices, d] : blocks_derivatives_exo_det[block])
{
const auto &[eq, var, lag] {indices};
code_file << FNUMEXPR_{ExpressionType::FirstExodetDerivative, eq, 0, lag};
d->writeBytecodeOutput(code_file, output_type, temporary_terms_union, blocks_temporary_terms_idxs, tef_terms);
code_file << FSTPG3_{eq, var, lag, blocks_jacob_cols_exo_det[block].at({ var, lag })};
}
for (const auto &[indices, d] : blocks_derivatives_other_endo[block])
{
const auto &[eq, var, lag] {indices};
code_file << FNUMEXPR_{ExpressionType::FirstOtherEndoDerivative, eq, 0, lag};
d->writeBytecodeOutput(code_file, output_type, temporary_terms_union, blocks_temporary_terms_idxs, tef_terms);
code_file << FSTPG3_{eq, var, lag, blocks_jacob_cols_other_endo[block].at({ var, lag })};
}
}
vector
DynamicModel::writeDynamicPerBlockCFiles(const string &basename, const string &mexext,
const filesystem::path &matlabroot,
const filesystem::path &dynareroot) const
{
temporary_terms_t temporary_terms; // Temp terms written so far
const filesystem::path model_src_dir { filesystem::path{basename} / "model" / "src" };
vector compiled_object_files;
for (int blk = 0; blk < static_cast(blocks.size()); blk++)
{
BlockSimulationType simulation_type = blocks[blk].simulation_type;
int block_size = blocks[blk].size;
int block_mfs_size = blocks[blk].mfs_size;
// Number of nonzero derivatives for the various Jacobians
int nze_stochastic = blocks_derivatives[blk].size();
int nze_deterministic = nzeDeterministicJacobianForBlock(blk);
int nze_other_endo = blocks_derivatives_other_endo[blk].size();
int nze_exo = blocks_derivatives_exo[blk].size();
int nze_exo_det = blocks_derivatives_exo_det[blk].size();
filesystem::path filename { model_src_dir / ("dynamic_" + to_string(blk+1) + ".c") };
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 << "/* Block " << blk+1 << endl
<< " " << BlockSim(simulation_type) << " */" << endl
<< endl
<< "#include " << endl
<< "#include " << endl
<< "#include " << endl
<< R"(#include "mex.h")" << endl
<< endl;
// Write function definition if BinaryOpcode::powerDeriv is used
writePowerDerivHeader(output);
output << endl;
if (simulation_type == BlockSimulationType::evaluateBackward
|| simulation_type == BlockSimulationType::evaluateForward)
output << "void dynamic_" << blk+1 << "(double *restrict y, const double *restrict x, int nb_row_x, const double *restrict params, const double *restrict steady_state, double *restrict T, int it_, bool stochastic_mode, double *restrict g1_i, double *restrict g1_j, double *restrict g1_v, double *restrict g1_x_i, double *restrict g1_x_j, double *restrict g1_x_v, double *restrict g1_xd_i, double *restrict g1_xd_j, double *restrict g1_xd_v, double *restrict g1_o_i, double *restrict g1_o_j, double *restrict g1_o_v)" << endl;
else
output << "void dynamic_" << blk+1 << "(double *restrict y, const double *restrict x, int nb_row_x, const double *restrict params, const double *restrict steady_state, double *restrict T, int it_, bool stochastic_mode, double *restrict residual, double *restrict g1_i, double *restrict g1_j, double *restrict g1_v, double *restrict g1_x_i, double *restrict g1_x_j, double *restrict g1_x_v, double *restrict g1_xd_i, double *restrict g1_xd_j, double *restrict g1_xd_v, double *restrict g1_o_i, double *restrict g1_o_j, double *restrict g1_o_v)" << endl;
output << '{' << endl;
writeDynamicPerBlockHelper(blk, output, temporary_terms,
nze_stochastic, nze_deterministic, nze_exo, nze_exo_det, nze_other_endo);
output << '}' << endl
<< endl;
ostringstream header;
if (simulation_type == BlockSimulationType::evaluateBackward
|| simulation_type == BlockSimulationType::evaluateForward)
header << "void dynamic_" << blk+1 << "_mx(mxArray *y, const mxArray *x, const mxArray *params, const mxArray *steady_state, mxArray *T, const mxArray *it_, const mxArray *stochastic_mode, mxArray **g1, mxArray **g1_x, mxArray **g1_xd, mxArray **g1_o)";
else
header << "void dynamic_" << blk+1 << "_mx(mxArray *y, const mxArray *x, const mxArray *params, const mxArray *steady_state, mxArray *T, const mxArray *it_, const mxArray *stochastic_mode, mxArray **residual, mxArray **g1, mxArray **g1_x, mxArray **g1_xd, mxArray **g1_o)";
output << header.str() << endl
<< '{' << endl
<< " int nb_row_x = mxGetM(x);" << endl;
if (simulation_type != BlockSimulationType::evaluateForward
&& simulation_type != BlockSimulationType::evaluateBackward)
output << " *residual = mxCreateDoubleMatrix(" << block_mfs_size << ",1,mxREAL);" << endl;
output << " mxArray *g1_i = NULL, *g1_j = NULL, *g1_v = NULL;" << endl
<< " mxArray *g1_x_i = NULL, *g1_x_j = NULL, *g1_x_v = NULL;" << endl
<< " mxArray *g1_xd_i = NULL, *g1_xd_j = NULL, *g1_xd_v = NULL;" << endl
<< " mxArray *g1_o_i = NULL, *g1_o_j = NULL, *g1_o_v = NULL;" << endl
<< " if (mxGetScalar(stochastic_mode)) {" << endl
<< " g1_i=mxCreateDoubleMatrix(" << nze_stochastic << ",1,mxREAL);" << endl
<< " g1_j=mxCreateDoubleMatrix(" << nze_stochastic << ",1,mxREAL);" << endl
<< " g1_v=mxCreateDoubleMatrix(" << nze_stochastic << ",1,mxREAL);" << endl
<< " g1_x_i=mxCreateDoubleMatrix(" << nze_exo << ",1,mxREAL);" << endl
<< " g1_x_j=mxCreateDoubleMatrix(" << nze_exo << ",1,mxREAL);" << endl
<< " g1_x_v=mxCreateDoubleMatrix(" << nze_exo << ",1,mxREAL);" << endl
<< " g1_xd_i=mxCreateDoubleMatrix(" << nze_exo_det << ",1,mxREAL);" << endl
<< " g1_xd_j=mxCreateDoubleMatrix(" << nze_exo_det << ",1,mxREAL);" << endl
<< " g1_xd_v=mxCreateDoubleMatrix(" << nze_exo_det << ",1,mxREAL);" << endl
<< " g1_o_i=mxCreateDoubleMatrix(" << nze_other_endo << ",1,mxREAL);" << endl
<< " g1_o_j=mxCreateDoubleMatrix(" << nze_other_endo << ",1,mxREAL);" << endl
<< " g1_o_v=mxCreateDoubleMatrix(" << nze_other_endo << ",1,mxREAL);" << endl;
if (simulation_type != BlockSimulationType::evaluateForward
&& simulation_type != BlockSimulationType::evaluateBackward)
output << " } else {" << endl
<< " g1_i=mxCreateDoubleMatrix(" << nze_deterministic << ",1,mxREAL);" << endl
<< " g1_j=mxCreateDoubleMatrix(" << nze_deterministic << ",1,mxREAL);" << endl
<< " g1_v=mxCreateDoubleMatrix(" << nze_deterministic << ",1,mxREAL);" << endl;
output << " }" << endl
<< endl;
// N.B.: In the following, it_ is decreased by 1, to follow C convention
if (simulation_type == BlockSimulationType::evaluateBackward
|| simulation_type == BlockSimulationType::evaluateForward)
output << " dynamic_" << blk+1 << "(mxGetPr(y), mxGetPr(x), nb_row_x, mxGetPr(params), mxGetPr(steady_state), mxGetPr(T), mxGetScalar(it_)-1, mxGetScalar(stochastic_mode), g1_i ? mxGetPr(g1_i) : NULL, g1_j ? mxGetPr(g1_j) : NULL, g1_v ? mxGetPr(g1_v) : NULL, g1_x_i ? mxGetPr(g1_x_i) : NULL, g1_x_j ? mxGetPr(g1_x_j) : NULL, g1_x_v ? mxGetPr(g1_x_v) : NULL, g1_xd_i ? mxGetPr(g1_xd_i) : NULL, g1_xd_j ? mxGetPr(g1_xd_j) : NULL, g1_xd_v ? mxGetPr(g1_xd_v) : NULL, g1_o_i ? mxGetPr(g1_o_i) : NULL, g1_o_j ? mxGetPr(g1_o_j) : NULL, g1_o_v ? mxGetPr(g1_o_v) : NULL);" << endl;
else
output << " dynamic_" << blk+1 << "(mxGetPr(y), mxGetPr(x), nb_row_x, mxGetPr(params), mxGetPr(steady_state), mxGetPr(T), mxGetScalar(it_)-1, mxGetScalar(stochastic_mode), mxGetPr(*residual), g1_i ? mxGetPr(g1_i) : NULL, g1_j ? mxGetPr(g1_j) : NULL, g1_v ? mxGetPr(g1_v) : NULL, g1_x_i ? mxGetPr(g1_x_i) : NULL, g1_x_j ? mxGetPr(g1_x_j) : NULL, g1_x_v ? mxGetPr(g1_x_v) : NULL, g1_xd_i ? mxGetPr(g1_xd_i) : NULL, g1_xd_j ? mxGetPr(g1_xd_j) : NULL, g1_xd_v ? mxGetPr(g1_xd_v) : NULL, g1_o_i ? mxGetPr(g1_o_i) : NULL, g1_o_j ? mxGetPr(g1_o_j) : NULL, g1_o_v ? mxGetPr(g1_o_v) : NULL);" << endl;
output << endl
<< " if (mxGetScalar(stochastic_mode)) {" << endl
<< " mxArray *m = mxCreateDoubleScalar(" << block_size << ");" << endl
<< " mxArray *n = mxCreateDoubleScalar(" << blocks_jacob_cols_endo[blk].size() << ");" << endl
<< " mxArray *plhs[1];" << endl
<< " mxArray *prhs[5] = { g1_i, g1_j, g1_v, m, n };" << endl
<< R"( mexCallMATLAB(1, plhs, 5, prhs, "sparse");)" << endl
<< " *g1=plhs[0];" << endl
<< " mxDestroyArray(g1_i);" << endl
<< " mxDestroyArray(g1_j);" << endl
<< " mxDestroyArray(g1_v);" << endl
<< " mxDestroyArray(n);" << endl
<< " n = mxCreateDoubleScalar(" << blocks_jacob_cols_exo[blk].size() << ");" << endl
<< " mxArray *prhs_x[5] = { g1_x_i, g1_x_j, g1_x_v, m, n };" << endl
<< R"( mexCallMATLAB(1, plhs, 5, prhs_x, "sparse");)" << endl
<< " *g1_x=plhs[0];" << endl
<< " mxDestroyArray(g1_x_i);" << endl
<< " mxDestroyArray(g1_x_j);" << endl
<< " mxDestroyArray(g1_x_v);" << endl
<< " mxDestroyArray(n);" << endl
<< " n = mxCreateDoubleScalar(" << blocks_jacob_cols_exo_det[blk].size() << ");" << endl
<< " mxArray *prhs_xd[5] = { g1_xd_i, g1_xd_j, g1_xd_v, m, n };" << endl
<< R"( mexCallMATLAB(1, plhs, 5, prhs_xd, "sparse");)" << endl
<< " *g1_xd=plhs[0];" << endl
<< " mxDestroyArray(g1_xd_i);" << endl
<< " mxDestroyArray(g1_xd_j);" << endl
<< " mxDestroyArray(g1_xd_v);" << endl
<< " mxDestroyArray(n);" << endl
<< " n = mxCreateDoubleScalar(" << blocks_jacob_cols_other_endo[blk].size() << ");" << endl
<< " mxArray *prhs_o[5] = { g1_o_i, g1_o_j, g1_o_v, m, n };" << endl
<< R"( mexCallMATLAB(1, plhs, 5, prhs_o, "sparse");)" << endl
<< " *g1_o=plhs[0];" << endl
<< " mxDestroyArray(g1_o_i);" << endl
<< " mxDestroyArray(g1_o_j);" << endl
<< " mxDestroyArray(g1_o_v);" << endl
<< " mxDestroyArray(n);" << endl
<< " mxDestroyArray(m);" << endl
<< " } else {" << endl;
switch (simulation_type)
{
case BlockSimulationType::evaluateForward:
case BlockSimulationType::evaluateBackward:
output << " *g1=mxCreateDoubleMatrix(0,0,mxREAL);" << endl;
break;
case BlockSimulationType::solveBackwardSimple:
case BlockSimulationType::solveForwardSimple:
case BlockSimulationType::solveBackwardComplete:
case BlockSimulationType::solveForwardComplete:
output << " mxArray *m = mxCreateDoubleScalar(" << block_mfs_size << ");" << endl
<< " mxArray *n = mxCreateDoubleScalar(" << block_mfs_size << ");" << endl
<< " mxArray *plhs[1];" << endl
<< " mxArray *prhs[5] = { g1_i, g1_j, g1_v, m, n };" << endl
<< R"( mexCallMATLAB(1, plhs, 5, prhs, "sparse");)" << endl
<< " *g1=plhs[0];" << endl
<< " mxDestroyArray(g1_i);" << endl
<< " mxDestroyArray(g1_j);" << endl
<< " mxDestroyArray(g1_v);" << endl
<< " mxDestroyArray(n);" << endl
<< " mxDestroyArray(m);" << endl;
break;
case BlockSimulationType::solveTwoBoundariesSimple:
case BlockSimulationType::solveTwoBoundariesComplete:
output << " mxArray *m = mxCreateDoubleScalar(" << block_mfs_size << ");" << endl
<< " mxArray *n = mxCreateDoubleScalar(" << 3*block_mfs_size << ");" << endl
<< " mxArray *plhs[1];" << endl
<< " mxArray *prhs[5] = { g1_i, g1_j, g1_v, m, n };" << endl
<< R"( mexCallMATLAB(1, plhs, 5, prhs, "sparse");)" << endl
<< " *g1=plhs[0];" << endl
<< " mxDestroyArray(g1_i);" << endl
<< " mxDestroyArray(g1_j);" << endl
<< " mxDestroyArray(g1_v);" << endl
<< " mxDestroyArray(n);" << endl
<< " mxDestroyArray(m);" << endl;
break;
default:
break;
}
output << " *g1_x=mxCreateDoubleMatrix(0,0,mxREAL);" << endl
<< " *g1_xd=mxCreateDoubleMatrix(0,0,mxREAL);" << endl
<< " *g1_o=mxCreateDoubleMatrix(0,0,mxREAL);" << endl
<< " }" << endl
<< "}" << endl;
output.close();
// Compile intermediary object under /model/src/
compiled_object_files.emplace_back(compileMEX(model_src_dir, "dynamic_" + to_string(blk+1),
mexext, { filename }, matlabroot, dynareroot,
false));
filename = model_src_dir / ("dynamic_" + to_string(blk+1) + ".h");
ofstream header_output{filename, ios::out | ios::binary};
if (!header_output.is_open())
{
cerr << "ERROR: Can't open file " << filename.string() << " for writing" << endl;
exit(EXIT_FAILURE);
}
header_output << header.str() << ';' << endl;
header_output.close();
}
return compiled_object_files;
}
void
DynamicModel::writeDynamicBytecode(const string &basename) const
{
// Determine the type of model (used for typing the single block)
BlockSimulationType simulation_type;
if (max_endo_lag > 0 && max_endo_lead > 0)
simulation_type = BlockSimulationType::solveTwoBoundariesComplete;
else if (max_endo_lag >= 0 && max_endo_lead == 0)
simulation_type = BlockSimulationType::solveForwardComplete;
else
simulation_type = BlockSimulationType::solveBackwardComplete;
// First write the .bin file
int u_count_int { writeBytecodeBinFile(basename + "/model/bytecode/dynamic.bin",
simulation_type == BlockSimulationType::solveTwoBoundariesComplete) };
BytecodeWriter code_file {basename + "/model/bytecode/dynamic.cod"};
// Declare temporary terms
code_file << FDIMT_{static_cast(temporary_terms_derivatives[0].size()
+ temporary_terms_derivatives[1].size())};
// Declare the (single) block
vector exo(symbol_table.exo_nbr()), exo_det(symbol_table.exo_det_nbr());
iota(exo.begin(), exo.end(), 0);
iota(exo_det.begin(), exo_det.end(), 0);
int jacobian_ncols_endo
{ static_cast(count_if(dyn_jacobian_cols_table.begin(), dyn_jacobian_cols_table.end(),
[this](const auto &v)
{ return getTypeByDerivID(v.first) == SymbolType::endogenous; }))
};
int jacobian_ncols_exo {symbol_table.exo_nbr()};
int jacobian_ncols_exo_det {symbol_table.exo_det_nbr()};
vector eq_idx(equations.size());
iota(eq_idx.begin(), eq_idx.end(), 0);
vector endo_idx(symbol_table.endo_nbr());
iota(endo_idx.begin(), endo_idx.end(), 0);
code_file << FBEGINBLOCK_{symbol_table.endo_nbr(),
simulation_type,
0,
symbol_table.endo_nbr(),
endo_idx,
eq_idx,
false,
symbol_table.endo_nbr(),
max_endo_lag,
max_endo_lead,
u_count_int,
jacobian_ncols_endo,
symbol_table.exo_det_nbr(),
jacobian_ncols_exo_det,
symbol_table.exo_nbr(),
jacobian_ncols_exo,
0,
0,
exo_det,
exo,
{}};
writeBytecodeHelper(code_file);
}
void
DynamicModel::writeDynamicBlockBytecode(const string &basename) const
{
BytecodeWriter code_file {basename + "/model/bytecode/dynamic.cod"};
const filesystem::path bin_filename {basename + "/model/bytecode/dynamic.bin"};
ofstream bin_file {bin_filename, ios::out | ios::binary};
if (!bin_file.is_open())
{
cerr << R"(Error : Can't open file ")" << bin_filename.string() << R"(" for writing)" << endl;
exit(EXIT_FAILURE);
}
// Temporary variables declaration
code_file << FDIMT_{static_cast(blocks_temporary_terms_idxs.size())};
for (int block {0}; block < static_cast(blocks.size()); block++)
{
const BlockSimulationType simulation_type {blocks[block].simulation_type};
// Write section of .bin file except for evaluate blocks and solve simple blocks
const int u_count {simulation_type == BlockSimulationType::solveTwoBoundariesSimple
|| simulation_type == BlockSimulationType::solveTwoBoundariesComplete
|| simulation_type == BlockSimulationType::solveBackwardComplete
|| simulation_type == BlockSimulationType::solveForwardComplete
? writeBlockBytecodeBinFile(bin_file, block)
: 0};
code_file << FBEGINBLOCK_{blocks[block].mfs_size,
simulation_type,
blocks[block].first_equation,
blocks[block].size,
endo_idx_block2orig,
eq_idx_block2orig,
blocks[block].linear,
symbol_table.endo_nbr(),
blocks[block].max_lag,
blocks[block].max_lead,
u_count,
static_cast(blocks_jacob_cols_endo[block].size()),
static_cast(blocks_exo_det[block].size()),
static_cast(blocks_jacob_cols_exo_det[block].size()),
static_cast(blocks_exo[block].size()),
static_cast(blocks_jacob_cols_exo[block].size()),
static_cast(blocks_other_endo[block].size()),
static_cast(blocks_jacob_cols_other_endo[block].size()),
{ blocks_exo_det[block].begin(), blocks_exo_det[block].end() },
{ blocks_exo[block].begin(), blocks_exo[block].end() },
{ blocks_other_endo[block].begin(), blocks_other_endo[block].end() }};
writeBlockBytecodeHelper(code_file, block);
}
code_file << FEND_{};
}
void
DynamicModel::writeDynamicMFile(const string &basename) const
{
auto [d_output, tt_output] = writeModelFileHelper();
ostringstream init_output, end_output;
init_output << "residual = zeros(" << equations.size() << ", 1);";
writeDynamicMFileHelper(basename, "dynamic_resid", "residual", "dynamic_resid_tt",
temporary_terms_derivatives[0].size(),
"", init_output, end_output, d_output[0], tt_output[0]);
init_output.str("");
init_output << "g1 = zeros(" << equations.size() << ", " << getJacobianColsNbr(false) << ");";
writeDynamicMFileHelper(basename, "dynamic_g1", "g1", "dynamic_g1_tt",
temporary_terms_derivatives[0].size() + temporary_terms_derivatives[1].size(),
"dynamic_resid_tt", init_output, end_output, d_output[1], tt_output[1]);
writeDynamicMWrapperFunction(basename, "g1");
// For order ≥ 2
int ncols{getJacobianColsNbr(false)};
int ntt { static_cast(temporary_terms_derivatives[0].size() + temporary_terms_derivatives[1].size())};
for (size_t i{2}; i < derivatives.size(); i++)
{
ncols *= getJacobianColsNbr(false);
ntt += temporary_terms_derivatives[i].size();
string gname{"g" + to_string(i)};
string gprevname{"g" + to_string(i-1)};
init_output.str("");
end_output.str("");
if (derivatives[i].size())
{
init_output << gname << "_i = zeros(" << NNZDerivatives[i] << ",1);" << endl
<< gname << "_j = zeros(" << NNZDerivatives[i] << ",1);" << endl
<< gname << "_v = zeros(" << NNZDerivatives[i] << ",1);" << endl;
end_output << gname << " = sparse("
<< gname << "_i," << gname << "_j," << gname << "_v,"
<< equations.size() << "," << ncols << ");";
}
else
init_output << gname << " = sparse([],[],[]," << equations.size() << "," << ncols << ");";
writeDynamicMFileHelper(basename, "dynamic_" + gname, gname, "dynamic_" + gname + "_tt", ntt,
"dynamic_" + gprevname + "_tt", init_output, end_output,
d_output[i], tt_output[i]);
if (i <= 3)
writeDynamicMWrapperFunction(basename, gname);
}
writeDynamicMCompatFile(basename);
}
string
DynamicModel::reform(const string &name1) const
{
string name = name1;
int pos = name.find(R"(\)", 0);
while (pos >= 0)
{
if (name.substr(pos + 1, 1) != R"(\)")
{
name = name.insert(pos, R"(\)");
pos++;
}
pos++;
pos = name.find(R"(\)", pos);
}
return name;
}
void
DynamicModel::printNonZeroHessianEquations(ostream &output) const
{
if (nonzero_hessian_eqs.size() != 1)
output << "[";
for (bool printed_something{false};
int it : nonzero_hessian_eqs)
{
if (exchange(printed_something, true))
output << " ";
output << it + 1;
}
if (nonzero_hessian_eqs.size() != 1)
output << "]";
}
void
DynamicModel::writeDynamicBlockMFile(const string &basename) const
{
filesystem::path filename {packageDir(basename) / "dynamic.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 [residual, y, T, g1, varargout] = dynamic(nblock, y, x, params, steady_state, T, it_, stochastic_mode)" << endl
<< " switch nblock" << endl;
for (int blk = 0; blk < static_cast(blocks.size()); blk++)
{
output << " case " << blk+1 << endl;
BlockSimulationType simulation_type = blocks[blk].simulation_type;
if (simulation_type == BlockSimulationType::evaluateBackward
|| simulation_type == BlockSimulationType::evaluateForward)
output << " [y, T, g1, varargout{1:nargout-4}] = " << basename << ".block.dynamic_" << blk+1 << "(y, x, params, steady_state, T, it_, stochastic_mode);" << endl
<< " residual = [];" << endl;
else
output << " [residual, y, T, g1, varargout{1:nargout-4}] = " << basename << ".block.dynamic_" << blk+1 << "(y, x, params, steady_state, T, it_, stochastic_mode);" << endl;
}
output << " end" << endl
<< "end" << endl;
output.close();
}
void
DynamicModel::writeDynamicBlockCFile(const string &basename, vector per_block_object_files, const string &mexext, const filesystem::path &matlabroot, const filesystem::path &dynareroot) const
{
const filesystem::path filename {basename + "/model/src/dynamic.c"};
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 << "#include " << endl
<< R"(#include "mex.h")" << endl;
for (int blk = 0; blk < static_cast(blocks.size()); blk++)
output << R"(#include "dynamic_)" << blk+1 << R"(.h")" << endl;
output << endl;
writePowerDeriv(output);
output << endl
<< "void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])" << endl
<< "{" << endl
<< " if (nrhs != 8)" << endl
<< R"( mexErrMsgTxt("Requires exactly 8 input arguments");)" << endl
<< " if (nlhs > 7)" << endl
<< R"( mexErrMsgTxt("Accepts at most 7 output arguments");)" << endl
<< " int nblock = (int) mxGetScalar(prhs[0]);" << endl
<< " const mxArray *y = prhs[1], *x = prhs[2], *params = prhs[3], *steady_state = prhs[4], *T = prhs[5], *it_ = prhs[6], *stochastic_mode = prhs[7];" << endl
<< " mxArray *T_new = mxDuplicateArray(T);" << endl
<< " mxArray *y_new = mxDuplicateArray(y);" << endl
<< " mxArray *residual, *g1, *g1_x, *g1_xd, *g1_o;" << endl
<< " switch (nblock)" << endl
<< " {" << endl;
for (int blk = 0; blk < static_cast(blocks.size()); blk++)
{
output << " case " << blk+1 << ':' << endl;
BlockSimulationType simulation_type = blocks[blk].simulation_type;
if (simulation_type == BlockSimulationType::evaluateBackward
|| simulation_type == BlockSimulationType::evaluateForward)
output << " dynamic_" << blk+1 << "_mx(y_new, x, params, steady_state, T_new, it_, stochastic_mode, &g1, &g1_x, &g1_xd, &g1_o);" << endl
<< " residual = mxCreateDoubleMatrix(0,0,mxREAL);" << endl;
else
output << " dynamic_" << blk+1 << "_mx(y_new, x, params, steady_state, T_new, it_, stochastic_mode, &residual, &g1, &g1_x, &g1_xd, &g1_o);" << endl;
output << " break;" << endl;
}
output << " }" << endl
<< endl
<< " if (nlhs >= 1)" << endl
<< " plhs[0] = residual;" << endl
<< " else" << endl
<< " mxDestroyArray(residual);" << endl
<< " if (nlhs >= 2)" << endl
<< " plhs[1] = y_new;" << endl
<< " else" << endl
<< " mxDestroyArray(y_new);" << endl
<< " if (nlhs >= 3)" << endl
<< " plhs[2] = T_new;" << endl
<< " else" << endl
<< " mxDestroyArray(T_new);" << endl
<< " if (nlhs >= 4)" << endl
<< " plhs[3] = g1;" << endl
<< " else" << endl
<< " mxDestroyArray(g1);" << endl
<< " if (nlhs >= 5)" << endl
<< " plhs[4] = g1_x;" << endl
<< " else" << endl
<< " mxDestroyArray(g1_x);" << endl
<< " if (nlhs >= 6)" << endl
<< " plhs[5] = g1_xd;" << endl
<< " else" << endl
<< " mxDestroyArray(g1_xd);" << endl
<< " if (nlhs >= 7)" << endl
<< " plhs[6] = g1_o;" << endl
<< " else" << endl
<< " mxDestroyArray(g1_o);" << endl
<< "}" << endl;
output.close();
per_block_object_files.push_back(filename);
compileMEX(packageDir(basename), "dynamic", mexext, per_block_object_files, matlabroot, dynareroot);
}
void
DynamicModel::writeDynamicMWrapperFunction(const string &basename, const string &ending) const
{
string name;
if (ending == "g1")
name = "dynamic_resid_g1";
else if (ending == "g2")
name = "dynamic_resid_g1_g2";
else if (ending == "g3")
name = "dynamic_resid_g1_g2_g3";
filesystem::path filename {packageDir(basename) / (name + ".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);
}
if (ending == "g1")
output << "function [residual, g1] = " << name << "(T, y, x, params, steady_state, it_, T_flag)" << endl
<< "% function [residual, g1] = " << name << "(T, y, x, params, steady_state, it_, T_flag)" << endl;
else if (ending == "g2")
output << "function [residual, g1, g2] = " << name << "(T, y, x, params, steady_state, it_, T_flag)" << endl
<< "% function [residual, g1, g2] = " << name << "(T, y, x, params, steady_state, it_, T_flag)" << endl;
else if (ending == "g3")
output << "function [residual, g1, g2, g3] = " << name << "(T, y, x, params, steady_state, it_, T_flag)" << endl
<< "% function [residual, g1, g2, g3] = " << name << "(T, y, x, params, steady_state, it_, T_flag)" << endl;
output << "%" << endl
<< "% Wrapper function automatically created by Dynare" << endl
<< "%" << endl
<< endl
<< " if T_flag" << endl
<< " T = " << basename << ".dynamic_" << ending << "_tt(T, y, x, params, steady_state, it_);" << endl
<< " end" << endl;
if (ending == "g1")
output << " residual = " << basename << ".dynamic_resid(T, y, x, params, steady_state, it_, false);" << endl
<< " g1 = " << basename << ".dynamic_g1(T, y, x, params, steady_state, it_, false);" << endl;
else if (ending == "g2")
output << " [residual, g1] = " << basename << ".dynamic_resid_g1(T, y, x, params, steady_state, it_, false);" << endl
<< " g2 = " << basename << ".dynamic_g2(T, y, x, params, steady_state, it_, false);" << endl;
else if (ending == "g3")
output << " [residual, g1, g2] = " << basename << ".dynamic_resid_g1_g2(T, y, x, params, steady_state, it_, false);" << endl
<< " g3 = " << basename << ".dynamic_g3(T, y, x, params, steady_state, it_, false);" << endl;
output << endl << "end" << endl;
output.close();
}
void
DynamicModel::writeDynamicMFileHelper(const string &basename,
const string &name, const string &retvalname,
const string &name_tt, size_t ttlen,
const string &previous_tt_name,
const ostringstream &init_s, const ostringstream &end_s,
const ostringstream &s, const ostringstream &s_tt) const
{
filesystem::path filename {packageDir(basename) / (name_tt + ".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 T = " << name_tt << "(T, y, x, params, steady_state, it_)" << endl
<< "% function T = " << name_tt << "(T, y, x, params, steady_state, it_)" << endl
<< "%" << endl
<< "% File created by Dynare Preprocessor from .mod file" << endl
<< "%" << endl
<< "% Inputs:" << endl
<< "% T [#temp variables by 1] double vector of temporary terms to be filled by function" << endl
<< "% y [#dynamic variables by 1] double vector of endogenous variables in the order stored" << endl
<< "% in M_.lead_lag_incidence; see the Manual" << endl
<< "% x [nperiods by M_.exo_nbr] double matrix of exogenous variables (in declaration order)" << endl
<< "% for all simulation periods" << endl
<< "% steady_state [M_.endo_nbr by 1] double vector of steady state values" << endl
<< "% params [M_.param_nbr by 1] double vector of parameter values in declaration order" << endl
<< "% it_ scalar double time period for exogenous variables for which" << endl
<< "% to evaluate the model" << endl
<< "%" << endl
<< "% Output:" << endl
<< "% T [#temp variables by 1] double vector of temporary terms" << endl
<< "%" << endl << endl
<< "assert(length(T) >= " << ttlen << ");" << endl
<< endl;
if (!previous_tt_name.empty())
output << "T = " << basename << "." << previous_tt_name << "(T, y, x, params, steady_state, it_);" << endl << endl;
output << s_tt.str() << endl
<< "end" << endl;
output.close();
filename = packageDir(basename) / (name + ".m");
output.open(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 " << retvalname << " = " << name << "(T, y, x, params, steady_state, it_, T_flag)" << endl
<< "% function " << retvalname << " = " << name << "(T, y, x, params, steady_state, it_, T_flag)" << endl
<< "%" << endl
<< "% File created by Dynare Preprocessor from .mod file" << endl
<< "%" << endl
<< "% Inputs:" << endl
<< "% T [#temp variables by 1] double vector of temporary terms to be filled by function" << endl
<< "% y [#dynamic variables by 1] double vector of endogenous variables in the order stored" << endl
<< "% in M_.lead_lag_incidence; see the Manual" << endl
<< "% x [nperiods by M_.exo_nbr] double matrix of exogenous variables (in declaration order)" << endl
<< "% for all simulation periods" << endl
<< "% steady_state [M_.endo_nbr by 1] double vector of steady state values" << endl
<< "% params [M_.param_nbr by 1] double vector of parameter values in declaration order" << endl
<< "% it_ scalar double time period for exogenous variables for which" << endl
<< "% to evaluate the model" << endl
<< "% T_flag boolean boolean flag saying whether or not to calculate temporary terms" << endl
<< "%" << endl
<< "% Output:" << endl
<< "% " << retvalname << endl
<< "%" << endl << endl;
if (!name_tt.empty())
output << "if T_flag" << endl
<< " T = " << basename << "." << name_tt << "(T, y, x, params, steady_state, it_);" << endl
<< "end" << endl;
output << init_s.str() << endl
<< s.str()
<< end_s.str() << endl
<< "end" << endl;
output.close();
}
void
DynamicModel::writeDynamicMCompatFile(const string &basename) const
{
filesystem::path filename {packageDir(basename) / "dynamic.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);
}
int ntt { static_cast(temporary_terms_derivatives[0].size() + temporary_terms_derivatives[1].size() + temporary_terms_derivatives[2].size() + temporary_terms_derivatives[3].size()) };
output << "function [residual, g1, g2, g3] = dynamic(y, x, params, steady_state, it_)" << endl
<< " T = NaN(" << ntt << ", 1);" << endl
<< " if nargout <= 1" << endl
<< " residual = " << basename << ".dynamic_resid(T, y, x, params, steady_state, it_, true);" << endl
<< " elseif nargout == 2" << endl
<< " [residual, g1] = " << basename << ".dynamic_resid_g1(T, y, x, params, steady_state, it_, true);" << endl
<< " elseif nargout == 3" << endl
<< " [residual, g1, g2] = " << basename << ".dynamic_resid_g1_g2(T, y, x, params, steady_state, it_, true);" << endl
<< " else" << endl
<< " [residual, g1, g2, g3] = " << basename << ".dynamic_resid_g1_g2_g3(T, y, x, params, steady_state, it_, true);" << endl
<< " end" << endl
<< "end" << endl;
output.close();
}
void
DynamicModel::writeDynamicJacobianNonZeroEltsFile(const string &basename) const
{
vector> nzij_pred, nzij_current, nzij_fwrd; // pairs (tsid, equation)
for (const auto &[indices, d1] : derivatives[1])
{
if (getTypeByDerivID(indices[1]) != SymbolType::endogenous)
continue;
int tsid { getTypeSpecificIDByDerivID(indices[1]) };
int lag = getLagByDerivID(indices[1]);
if (lag == -1)
nzij_pred.emplace_back(tsid, indices[0]);
else if (lag == 0)
nzij_current.emplace_back(tsid, indices[0]);
else
nzij_fwrd.emplace_back(tsid, indices[0]);
}
sort(nzij_pred.begin(), nzij_pred.end());
sort(nzij_current.begin(), nzij_current.end());
sort(nzij_fwrd.begin(), nzij_fwrd.end());
const filesystem::path filename {packageDir(basename) / "dynamic_g1_nz.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 [nzij_pred, nzij_current, nzij_fwrd] = dynamic_g1_nz()" << endl
<< "% Returns the coordinates of non-zero elements in the Jacobian, in column-major order, for each lead/lag (only for endogenous)" << endl;
auto print_nzij = [&output](const vector> &nzij, const string &name) {
output << " " << name << " = zeros(" << nzij.size() << ", 2, 'int32');" << endl;
for (int idx{1};
const auto &it : nzij)
{
output << " " << name << "(" << idx << ",1)=" << it.second+1 << ';'
<< " " << name << "(" << idx << ",2)=" << it.first+1 << ';' << endl;
idx++;
}
};
print_nzij(nzij_pred, "nzij_pred");
print_nzij(nzij_current, "nzij_current");
print_nzij(nzij_fwrd, "nzij_fwrd");
output << "end" << endl;
output.close();
}
vector>
DynamicModel::parseIncludeExcludeEquations(const string &inc_exc_option_value, bool exclude_eqs)
{
auto removeLeadingTrailingWhitespace = [](string &str)
{
str.erase(0, str.find_first_not_of("\t\n\v\f\r "));
str.erase(str.find_last_not_of("\t\n\v\f\r ") + 1);
};
string tags;
if (filesystem::exists(inc_exc_option_value))
{
ifstream exclude_file;
exclude_file.open(inc_exc_option_value, ifstream::in);
if (!exclude_file.is_open())
{
cerr << "ERROR: Could not open " << inc_exc_option_value << endl;
exit(EXIT_FAILURE);
}
string line;
bool tagname_on_first_line = false;
while (getline(exclude_file, line))
{
removeLeadingTrailingWhitespace(line);
if (!line.empty())
if (tags.empty() && line.find("=") != string::npos)
{
tagname_on_first_line = true;
tags += line + "(";
}
else
if (line.find("'") != string::npos)
tags += line + ",";
else
tags += "'" + line + "',";
}
if (!tags.empty())
{
tags = tags.substr(0, tags.size()-1);
if (tagname_on_first_line)
tags += ")";
}
}
else
tags = inc_exc_option_value;
removeLeadingTrailingWhitespace(tags);
if (tags.front() == '[' && tags.back() != ']')
{
cerr << "ERROR: " << (exclude_eqs ? "exclude_eqs" : "include_eqs")
<< ": if the first character is '[' the last must be ']'" << endl;
exit(EXIT_FAILURE);
}
if (tags.front() == '[' && tags.back() == ']')
tags = tags.substr(1, tags.length() - 2);
removeLeadingTrailingWhitespace(tags);
regex q(R"(^\w+\s*=)");
smatch matches;
string tagname = "name";
if (regex_search(tags, matches, q))
{
tagname = matches[0].str();
tags = tags.substr(tagname.size(), tags.length() - tagname.size() + 1);
removeLeadingTrailingWhitespace(tags);
if (tags.front() == '(' && tags.back() == ')')
{
tags = tags.substr(1, tags.length() - 2);
removeLeadingTrailingWhitespace(tags);
}
tagname = tagname.substr(0, tagname.size()-1);
removeLeadingTrailingWhitespace(tagname);
}
string quote_regex = "'[^']+'";
string non_quote_regex = R"([^,\s]+)";
regex r(R"((\s*)" + quote_regex + "|" + non_quote_regex + R"(\s*)(,\s*()" + quote_regex + "|" + non_quote_regex + R"()\s*)*)");
if (!regex_match(tags, r))
{
cerr << "ERROR: " << (exclude_eqs ? "exclude_eqs" : "include_eqs")
<< ": argument is of incorrect format." << endl;
exit(EXIT_FAILURE);
}
vector> eq_tag_set;
regex s(quote_regex + "|" + non_quote_regex);
for (auto it = sregex_iterator(tags.begin(), tags.end(), s);
it != sregex_iterator(); ++it)
{
string_view str {it->str()};
if (str.front() == '\'' && str.back() == '\'')
{
str.remove_prefix(1);
str.remove_suffix(1);
}
eq_tag_set.emplace_back(tagname, str);
}
return eq_tag_set;
}
vector
DynamicModel::removeEquationsHelper(set> &listed_eqs_by_tag, bool exclude_eqs,
bool excluded_vars_change_type,
vector &all_equations,
vector> &all_equations_lineno,
EquationTags &all_equation_tags, bool static_equations) const
{
if (all_equations.empty())
return {};
/* Try to convert the list of equations by tags into a list of equation
numbers.
The tag pairs that match an equation are removed from the list, so that
the caller knows which tag pairs have not been handled. */
set listed_eqs_by_number;
for (auto it = listed_eqs_by_tag.begin(); it != listed_eqs_by_tag.end();)
if (auto tmp = all_equation_tags.getEqnsByTag(it->first, it->second);
!tmp.empty())
{
listed_eqs_by_number.insert(tmp.begin(), tmp.end());
it = listed_eqs_by_tag.erase(it);
}
else
++it;
// Compute the indices of equations to be actually deleted
set eqs_to_delete_by_number;
if (exclude_eqs)
eqs_to_delete_by_number = listed_eqs_by_number;
else
for (size_t i = 0; i < all_equations.size(); i++)
if (!listed_eqs_by_number.contains(i))
eqs_to_delete_by_number.insert(i);
// remove from equations, equations_lineno, equation_tags
vector new_equations;
vector> new_equations_lineno;
map old_eqn_num_2_new;
vector excluded_vars;
for (size_t i = 0; i < all_equations.size(); i++)
if (eqs_to_delete_by_number.contains(i))
{
if (excluded_vars_change_type)
if (auto tmp = all_equation_tags.getTagValueByEqnAndKey(i, "endogenous"); tmp)
excluded_vars.push_back(symbol_table.getID(*tmp));
else
{
set result;
all_equations[i]->arg1->collectVariables(SymbolType::endogenous, result);
if (result.size() == 1)
excluded_vars.push_back(*result.begin());
else
{
cerr << "ERROR: Equation " << i+1
<< " has been excluded but it does not have a single variable on its left-hand side or an `endogenous` tag" << endl;
exit(EXIT_FAILURE);
}
}
}
else
{
new_equations.emplace_back(all_equations[i]);
old_eqn_num_2_new[i] = new_equations.size() - 1;
new_equations_lineno.emplace_back(all_equations_lineno[i]);
}
int n_excl = all_equations.size() - new_equations.size();
all_equations = new_equations;
all_equations_lineno = new_equations_lineno;
all_equation_tags.erase(eqs_to_delete_by_number, old_eqn_num_2_new);
if (!static_equations)
for (size_t i = 0; i < excluded_vars.size(); i++)
for (size_t j = i+1; j < excluded_vars.size(); j++)
if (excluded_vars[i] == excluded_vars[j])
{
cerr << "ERROR: Variable " << symbol_table.getName(i) << " was excluded twice"
<< " via a model_remove or model_replace statement, or via the include_eqs or exclude_eqs option" << endl;
exit(EXIT_FAILURE);
}
cout << "Excluded " << n_excl << (static_equations ? " static " : " dynamic ")
<< "equation" << (n_excl > 1 ? "s" : "") << " via model_remove or model_replace statement, or via include_eqs or exclude_eqs option" << endl;
return excluded_vars;
}
void
DynamicModel::removeEquations(const vector> &listed_eqs_by_tag, bool exclude_eqs,
bool excluded_vars_change_type)
{
/* Convert the const vector to a (mutable) set */
set listed_eqs_by_tag2(listed_eqs_by_tag.begin(), listed_eqs_by_tag.end());
vector excluded_vars = removeEquationsHelper(listed_eqs_by_tag2, exclude_eqs,
excluded_vars_change_type,
equations, equations_lineno,
equation_tags, false);
// Ignore output because variables are not excluded when equations marked 'static' are excluded
removeEquationsHelper(listed_eqs_by_tag2, exclude_eqs, excluded_vars_change_type,
static_only_equations, static_only_equations_lineno,
static_only_equations_equation_tags, true);
if (!listed_eqs_by_tag2.empty())
{
cerr << "ERROR: model_remove/model_replace/exclude_eqs/include_eqs: The equations specified by" << endl;
for (const auto &[tagname, tagvalue] : listed_eqs_by_tag)
cerr << " " << tagname << "=" << tagvalue << endl;
cerr << "were not found." << endl;
exit(EXIT_FAILURE);
}
if (excluded_vars_change_type)
{
// Collect list of used variables in updated list of equations
set eqn_vars;
for (auto eqn : equations)
eqn->collectVariables(SymbolType::endogenous, eqn_vars);
for (auto eqn : static_only_equations)
eqn->collectVariables(SymbolType::endogenous, eqn_vars);
/* Change type of endogenous variables determined by excluded equations.
They become exogenous if they are still used somewhere, otherwise they are
completely excluded from the model. */
for (auto ev : excluded_vars)
if (eqn_vars.contains(ev))
{
symbol_table.changeType(ev, SymbolType::exogenous);
cerr << "Variable '" << symbol_table.getName(ev) << "' turned into an exogenous, as its defining equation has been removed (but it still appears in an equation)" << endl;
}
else
{
symbol_table.changeType(ev, SymbolType::excludedVariable);
cerr << "Variable '" << symbol_table.getName(ev) << "' has been excluded from the model, as its defining equation has been removed and it appears nowhere else" << endl;
}
}
}
void
DynamicModel::includeExcludeEquations(const string &inc_exc_option_value, bool exclude_eqs)
{
if (inc_exc_option_value.empty())
return;
auto listed_eqs_by_tag = parseIncludeExcludeEquations(inc_exc_option_value, exclude_eqs);
removeEquations(listed_eqs_by_tag, exclude_eqs, true);
/* There is already a check about #static and #dynamic in
ModFile::checkPass(), but the present method is called from
ModFile::transformPass(), so we must do the check again */
if (staticOnlyEquationsNbr() != dynamicOnlyEquationsNbr())
{
cerr << "ERROR: exclude_eqs/include_eqs: You must remove the same number of equations marked `static` as equations marked `dynamic`." << endl;
exit(EXIT_FAILURE);
}
}
void
DynamicModel::writeBlockDriverOutput(ostream &output, const string &basename,
const vector &state_var, bool estimation_present) const
{
output << "M_.block_structure.time_recursive = " << boolalpha << time_recursive_block_decomposition << ";" << endl;
for (int blk = 0; blk < static_cast(blocks.size()); blk++)
{
int block_size = blocks[blk].size;
output << "M_.block_structure.block(" << blk+1 << ").Simulation_Type = " << static_cast(blocks[blk].simulation_type) << ";" << endl
<< "M_.block_structure.block(" << blk+1 << ").maximum_lag = " << blocks[blk].max_lag << ";" << endl
<< "M_.block_structure.block(" << blk+1 << ").maximum_lead = " << blocks[blk].max_lead << ";" << endl
<< "M_.block_structure.block(" << blk+1 << ").maximum_endo_lag = " << blocks[blk].max_endo_lag << ";" << endl
<< "M_.block_structure.block(" << blk+1 << ").maximum_endo_lead = " << blocks[blk].max_endo_lead << ";" << endl
<< "M_.block_structure.block(" << blk+1 << ").maximum_exo_lag = " << blocks[blk].max_exo_lag << ";" << endl
<< "M_.block_structure.block(" << blk+1 << ").maximum_exo_lead = " << blocks[blk].max_exo_lead << ";" << endl
<< "M_.block_structure.block(" << blk+1 << ").maximum_exo_det_lag = " << blocks[blk].max_exo_det_lag << ";" << endl
<< "M_.block_structure.block(" << blk+1 << ").maximum_exo_det_lead = " << blocks[blk].max_exo_det_lead << ";" << endl
<< "M_.block_structure.block(" << blk+1 << ").endo_nbr = " << block_size << ";" << endl
<< "M_.block_structure.block(" << blk+1 << ").mfs = " << blocks[blk].mfs_size << ";" << endl
<< "M_.block_structure.block(" << blk+1 << ").equation = [";
for (int eq = 0; eq < block_size; eq++)
output << " " << getBlockEquationID(blk, eq)+1;
output << "];" << endl
<< "M_.block_structure.block(" << blk+1 << ").variable = [";
for (int var = 0; var < block_size; var++)
output << " " << getBlockVariableID(blk, var)+1;
output << "];" << endl
<< "M_.block_structure.block(" << blk+1 << ").exogenous = [";
for (int exo : blocks_exo[blk])
output << " " << exo+1;
output << "];" << endl
<< "M_.block_structure.block(" << blk+1 << ").exo_nbr = " << blocks_exo[blk].size() << ";" << endl
<< "M_.block_structure.block(" << blk+1 << ").exogenous_det = [";
for (int exo_det : blocks_exo_det[blk])
output << " " << exo_det+1;
output << "];" << endl
<< "M_.block_structure.block(" << blk+1 << ").exo_det_nbr = " << blocks_exo_det[blk].size() << ";" << endl
<< "M_.block_structure.block(" << blk+1 << ").other_endogenous = [";
for (int other_endo : blocks_other_endo[blk])
output << " " << other_endo+1;
output << "];" << endl
<< "M_.block_structure.block(" << blk+1 << ").other_endogenous_block = [";
for (int other_endo : blocks_other_endo[blk])
output << " " << endo2block[other_endo]+1;
output << "];" << endl;
output << "M_.block_structure.block(" << blk+1 << ").tm1 = zeros(" << blocks_other_endo[blk].size() << ", " << state_var.size() << ");" << endl;
for (int line{1};
auto other_endo : blocks_other_endo[blk])
{
if (auto it = find(state_var.begin(), state_var.end(), other_endo);
it != state_var.end())
output << "M_.block_structure.block(" << blk+1 << ").tm1("
<< line << ", "
<< distance(state_var.begin(), it)+1 << ") = 1;" << endl;
line++;
}
output << "M_.block_structure.block(" << blk+1 << ").other_endo_nbr = " << blocks_other_endo[blk].size() << ";" << endl;
int count_lead_lag_incidence = 0;
vector local_state_var;
output << "M_.block_structure.block(" << blk+1 << ").lead_lag_incidence = [" << endl;
for (int lag = -1; lag <= 1; lag++)
{
for (int var = 0; var < block_size; var++)
{
for (int eq = 0; eq < block_size; eq++)
if (blocks_derivatives[blk].contains({ eq, var, lag }))
{
if (lag == -1)
local_state_var.push_back(getBlockVariableID(blk, var));
output << " " << ++count_lead_lag_incidence;
goto var_found;
}
output << " 0";
var_found:
;
}
output << ";" << endl;
}
output << "];" << endl;
output << "M_.block_structure.block(" << blk+1 << ").sorted_col_dr_ghx = [";
for (int lsv : local_state_var)
output << distance(state_var.begin(), find(state_var.begin(), state_var.end(), lsv))+1 << " ";
output << "];" << endl;
count_lead_lag_incidence = 0;
output << "M_.block_structure.block(" << blk+1 << ").lead_lag_incidence_other = [" << endl;
for (int lag = -1; lag <= 1; lag++)
{
for (int other_endo : blocks_other_endo[blk])
{
for (int eq = 0; eq < block_size; eq++)
if (blocks_derivatives_other_endo[blk].contains({ eq, other_endo, lag }))
{
output << " " << ++count_lead_lag_incidence;
goto other_endo_found;
}
output << " 0";
other_endo_found:
;
}
output << ";" << endl;
}
output << "];" << endl;
output << "M_.block_structure.block(" << blk+1 << ").n_static = " << blocks[blk].n_static << ";" << endl
<< "M_.block_structure.block(" << blk+1 << ").n_forward = " << blocks[blk].n_forward << ";" << endl
<< "M_.block_structure.block(" << blk+1 << ").n_backward = " << blocks[blk].n_backward << ";" << endl
<< "M_.block_structure.block(" << blk+1 << ").n_mixed = " << blocks[blk].n_mixed << ";" << endl
<< "M_.block_structure.block(" << blk+1 << ").is_linear = " << boolalpha << blocks[blk].linear << ';' << endl
<< "M_.block_structure.block(" << blk+1 << ").NNZDerivatives = " << blocks_derivatives[blk].size() << ';' << endl;
}
writeBlockDriverSparseIndicesHelper(output);
output << "M_.block_structure.variable_reordered = [";
for (int i = 0; i < symbol_table.endo_nbr(); i++)
output << " " << endo_idx_block2orig[i]+1;
output << "];" << endl
<< "M_.block_structure.equation_reordered = [";
for (int i = 0; i < symbol_table.endo_nbr(); i++)
output << " " << eq_idx_block2orig[i]+1;
output << "];" << endl;
map>> lag_row_incidence;
for (const auto &[indices, d1] : derivatives[1])
if (int deriv_id = indices[1];
getTypeByDerivID(deriv_id) == SymbolType::endogenous)
{
int eq = indices[0];
int var { getTypeSpecificIDByDerivID(deriv_id) };
int lag = getLagByDerivID(deriv_id);
lag_row_incidence[lag].insert({ eq, var });
}
for (auto [lag, eq_var_set] : lag_row_incidence)
{
output << "M_.block_structure.incidence(" << max_endo_lag+lag+1 << ").lead_lag = " << lag << ";" << endl
<< "M_.block_structure.incidence(" << max_endo_lag+lag+1 << ").sparse_IM = [" << endl;
for (auto [eq, var] : eq_var_set)
output << " " << eq+1 << " " << var+1 << ";" << endl;
output << "];" << endl;
}
output << "M_.block_structure.dyn_tmp_nbr = " << blocks_temporary_terms_idxs.size() << ';' << endl;
if (estimation_present)
{
filesystem::create_directories(basename + "/model/bytecode");
const filesystem::path main_name {basename + "/model/bytecode/kfi"};
ofstream KF_index_file{main_name, ios::out | ios::binary | ios::ate};
if (!KF_index_file.is_open())
{
cerr << "ERROR: Can't open file " << main_name.string() << " for writing" << endl;
exit(EXIT_FAILURE);
}
int n_obs = symbol_table.observedVariablesNbr();
int n_state = state_var.size();
for (int it : state_var)
if (symbol_table.isObservedVariable(symbol_table.getID(SymbolType::endogenous, it)))
n_obs--;
int n = n_obs + n_state;
output << "M_.nobs_non_statevar = " << n_obs << ";" << endl;
int nb_diag = 0;
vector i_nz_state_var(n);
for (int i = 0; i < n_obs; i++)
i_nz_state_var[i] = n;
int lp = n_obs;
vector state_equ;
for (int it : state_var)
state_equ.push_back(eq_idx_block2orig[endo_idx_orig2block[it]]);
for (int blk = 0; blk < static_cast(blocks.size()); blk++)
{
int nze = 0;
for (int i = 0; i < blocks[blk].size; i++)
if (int var = getBlockVariableID(blk, i);
find(state_var.begin(), state_var.end(), var) != state_var.end())
nze++;
if (blk == 0)
{
set> row_state_var_incidence;
for (const auto &[idx, ignore] : blocks_derivatives[blk])
if (auto it_state_var = find(state_var.begin(), state_var.end(), getBlockVariableID(blk, get<1>(idx)));
it_state_var != state_var.end())
if (auto it_state_equ = find(state_equ.begin(), state_equ.end(), getBlockEquationID(blk, get<0>(idx)));
it_state_equ != state_equ.end())
row_state_var_incidence.emplace(it_state_equ - state_equ.begin(), it_state_var - state_var.begin());
auto row_state_var_incidence_it = row_state_var_incidence.begin();
bool diag = true;
int nb_diag_r = 0;
while (row_state_var_incidence_it != row_state_var_incidence.end() && diag)
{
diag = (row_state_var_incidence_it->first == row_state_var_incidence_it->second);
if (diag)
{
int equ = row_state_var_incidence_it->first;
row_state_var_incidence_it++;
if (equ != row_state_var_incidence_it->first)
nb_diag_r++;
}
}
set> col_state_var_incidence;
for (auto [equ, var] : row_state_var_incidence)
col_state_var_incidence.emplace(var, equ);
auto col_state_var_incidence_it = col_state_var_incidence.begin();
diag = true;
int nb_diag_c = 0;
while (col_state_var_incidence_it != col_state_var_incidence.end() && diag)
{
diag = (col_state_var_incidence_it->first == col_state_var_incidence_it->second);
if (diag)
{
int var = col_state_var_incidence_it->first;
col_state_var_incidence_it++;
if (var != col_state_var_incidence_it->first)
nb_diag_c++;
}
}
nb_diag = min(nb_diag_r, nb_diag_c);
row_state_var_incidence.clear();
col_state_var_incidence.clear();
}
for (int i = 0; i < nze; i++)
i_nz_state_var[lp + i] = lp + nze;
lp += nze;
}
output << "M_.nz_state_var = [";
for (int i = 0; i < lp; i++)
output << i_nz_state_var[i] << " ";
output << "];" << endl
<< "M_.n_diag = " << nb_diag << ";" << endl;
KF_index_file.write(reinterpret_cast(&nb_diag), sizeof(nb_diag));
using index_KF = pair>;
vector v_index_KF;
for (int i = 0; i < n; i++)
for (int j = n_obs; j < n; j++)
{
int j1 = j - n_obs;
int j1_n_state = j1 * n_state - n_obs;
if ((i < n_obs) || (i >= nb_diag + n_obs) || (j1 >= nb_diag))
for (int k = n_obs; k < i_nz_state_var[i]; k++)
v_index_KF.emplace_back(i + j1 * n, pair(i + k * n, k + j1_n_state));
}
int size_v_index_KF = v_index_KF.size();
KF_index_file.write(reinterpret_cast(&size_v_index_KF), sizeof(size_v_index_KF));
for (auto &it : v_index_KF)
KF_index_file.write(reinterpret_cast(&it), sizeof(index_KF));
vector v_index_KF_2;
int n_n_obs = n * n_obs;
for (int i = 0; i < n; i++)
for (int j = i; j < n; j++)
if ((i < n_obs) || (i >= nb_diag + n_obs) || (j < n_obs) || (j >= nb_diag + n_obs))
for (int k = n_obs; k < i_nz_state_var[j]; k++)
{
int k_n = k * n;
v_index_KF_2.emplace_back(i * n + j, pair(i + k_n - n_n_obs, j + k_n));
}
int size_v_index_KF_2 = v_index_KF_2.size();
KF_index_file.write(reinterpret_cast(&size_v_index_KF_2), sizeof(size_v_index_KF_2));
for (auto &it : v_index_KF_2)
KF_index_file.write(reinterpret_cast(&it), sizeof(index_KF));
KF_index_file.close();
}
}
void
DynamicModel::writeDriverOutput(ostream &output, const string &basename, bool estimation_present, bool compute_xrefs) const
{
/* Writing initialisation for M_.lead_lag_incidence matrix
M_.lead_lag_incidence is a matrix with as many columns as there are
endogenous variables and as many rows as there are periods in the
models (nbr of rows = M_.max_lag+M_.max_lead+1)
The matrix elements are equal to zero if a variable isn't present in the
model at a given period.
*/
output << "M_.orig_maximum_endo_lag = " << max_endo_lag_orig << ";" << endl
<< "M_.orig_maximum_endo_lead = " << max_endo_lead_orig << ";" << endl
<< "M_.orig_maximum_exo_lag = " << max_exo_lag_orig << ";" << endl
<< "M_.orig_maximum_exo_lead = " << max_exo_lead_orig << ";" << endl
<< "M_.orig_maximum_exo_det_lag = " << max_exo_det_lag_orig << ";" << endl
<< "M_.orig_maximum_exo_det_lead = " << max_exo_det_lead_orig << ";" << endl
<< "M_.orig_maximum_lag = " << max_lag_orig << ";" << endl
<< "M_.orig_maximum_lead = " << max_lead_orig << ";" << endl
<< "M_.orig_maximum_lag_with_diffs_expanded = " << max_lag_with_diffs_expanded_orig << ";" << endl
<< "M_.lead_lag_incidence = [";
// Loop on endogenous variables
int nstatic = 0,
nfwrd = 0,
npred = 0,
nboth = 0;
for (int endoID = 0; endoID < symbol_table.endo_nbr(); endoID++)
{
output << endl;
int sstatic = 1,
sfwrd = 0,
spred = 0,
sboth = 0;
// Loop on periods
for (int lag = -max_endo_lag; lag <= max_endo_lead; lag++)
{
// Print variableID if exists with current period, otherwise print 0
try
{
int varID = getDerivID(symbol_table.getID(SymbolType::endogenous, endoID), lag);
output << " " << getJacobianCol(varID, false) + 1;
if (lag == -1)
{
sstatic = 0;
spred = 1;
}
else if (lag == 1)
{
if (spred == 1)
{
sboth = 1;
spred = 0;
}
else
{
sstatic = 0;
sfwrd = 1;
}
}
}
catch (UnknownDerivIDException &e)
{
output << " 0";
}
}
nstatic += sstatic;
nfwrd += sfwrd;
npred += spred;
nboth += sboth;
output << ";";
}
output << "]';" << endl
<< "M_.nstatic = " << nstatic << ";" << endl
<< "M_.nfwrd = " << nfwrd << ";" << endl
<< "M_.npred = " << npred << ";" << endl
<< "M_.nboth = " << nboth << ";" << endl
<< "M_.nsfwrd = " << nfwrd+nboth << ";" << endl
<< "M_.nspred = " << npred+nboth << ";" << endl
<< "M_.ndynamic = " << npred+nboth+nfwrd << ";" << endl
<< "M_.dynamic_tmp_nbr = [";
for (const auto &tts : temporary_terms_derivatives)
output << tts.size() << "; ";
output << "];" << endl;
// Write equation tags
equation_tags.writeOutput(output);
// Write mapping for variables and equations they are present in
for (const auto &variable : variableMapping)
{
output << "M_.mapping." << symbol_table.getName(variable.first) << ".eqidx = [";
for (auto equation : variable.second)
output << equation + 1 << " ";
output << "];" << endl;
}
/* Say if static and dynamic models differ (because of [static] and [dynamic]
equation tags) */
output << "M_.static_and_dynamic_models_differ = "
<< boolalpha << (static_only_equations.size() > 0)
<< ";" << endl;
// Say if model contains an external function call
bool has_external_function = false;
for (auto equation : equations)
if (equation->containsExternalFunction())
{
has_external_function = true;
break;
}
output << "M_.has_external_function = " << boolalpha << has_external_function
<< ';' << endl;
// Compute list of state variables, ordered in block-order
vector state_var;
for (int endoID = 0; endoID < symbol_table.endo_nbr(); endoID++)
// Loop on negative lags
for (int lag = -max_endo_lag; lag < 0; lag++)
try
{
getDerivID(symbol_table.getID(SymbolType::endogenous, endo_idx_block2orig[endoID]), lag);
if (find(state_var.begin(), state_var.end(), endo_idx_block2orig[endoID]) == state_var.end())
state_var.push_back(endo_idx_block2orig[endoID]);
}
catch (UnknownDerivIDException &e)
{
}
// Write the block structure of the model
if (block_decomposed)
writeBlockDriverOutput(output, basename, state_var, estimation_present);
output << "M_.state_var = [";
for (int it : state_var)
output << it+1 << " ";
output << "];" << endl;
// Writing initialization for some other variables
output << "M_.exo_names_orig_ord = [1:" << symbol_table.exo_nbr() << "];" << endl;
output << "M_.maximum_lag = " << max_lag << ";" << endl
<< "M_.maximum_lead = " << max_lead << ";" << endl;
output << "M_.maximum_endo_lag = " << max_endo_lag << ";" << endl
<< "M_.maximum_endo_lead = " << max_endo_lead << ";" << endl
<< "oo_.steady_state = zeros(" << symbol_table.endo_nbr() << ", 1);" << endl;
output << "M_.maximum_exo_lag = " << max_exo_lag << ";" << endl
<< "M_.maximum_exo_lead = " << max_exo_lead << ";" << endl
<< "oo_.exo_steady_state = zeros(" << symbol_table.exo_nbr() << ", 1);" << endl;
if (symbol_table.exo_det_nbr())
{
output << "M_.maximum_exo_det_lag = " << max_exo_det_lag << ";" << endl
<< "M_.maximum_exo_det_lead = " << max_exo_det_lead << ";" << endl
<< "oo_.exo_det_steady_state = zeros(" << symbol_table.exo_det_nbr() << ", 1);" << endl;
}
output << "M_.params = " << "NaN(" << symbol_table.param_nbr() << ", 1);" << endl;
string empty_cell = "cell(" + to_string(symbol_table.endo_nbr()) + ", 1)";
output << "M_.endo_trends = struct('deflator', " << empty_cell
<< ", 'log_deflator', " << empty_cell << ", 'growth_factor', " << empty_cell
<< ", 'log_growth_factor', " << empty_cell << ");" << endl;
for (int i = 0; i < symbol_table.endo_nbr(); i++)
{
int symb_id = symbol_table.getID(SymbolType::endogenous, i);
if (auto it = nonstationary_symbols_map.find(symb_id); it != nonstationary_symbols_map.end())
{
auto [is_log, deflator] = it->second;
output << "M_.endo_trends(" << i+1 << ")."
<< (is_log ? "log_deflator" : "deflator") << " = '";
deflator->writeJsonOutput(output, {}, {});
output << "';" << endl;
auto growth_factor = const_cast(this)->AddDivide(deflator, deflator->decreaseLeadsLags(1))->removeTrendLeadLag(trend_symbols_map)->replaceTrendVar();
output << "M_.endo_trends(" << i+1 << ")."
<< (is_log ? "log_growth_factor" : "growth_factor") << " = '";
growth_factor->writeJsonOutput(output, {}, {});
output << "';" << endl;
}
}
if (compute_xrefs)
writeXrefs(output);
// Write number of non-zero derivatives
// Use -1 if the derivatives have not been computed
output << "M_.NNZDerivatives = [";
for (int i = 1; i < static_cast(NNZDerivatives.size()); i++)
output << (i > computed_derivs_order ? -1 : NNZDerivatives[i]) << "; ";
output << "];" << endl;
writeDriverSparseIndicesHelper(output);
// Write LHS of each equation in text form
output << "M_.lhs = {" << endl;
for (auto eq : equations)
{
output << "'";
eq->arg1->writeJsonOutput(output, {}, {});
output << "'; " << endl;
}
output << "};" << endl;
}
void
DynamicModel::runTrendTest(const eval_context_t &eval_context)
{
computeDerivIDs();
testTrendDerivativesEqualToZero(eval_context);
}
void
DynamicModel::updateVarAndTrendModel() const
{
for (bool var : { true, false })
{
map>> trend_varr;
map>>> rhsr;
for (const auto &[model_name, eqns] : (var ? var_model_table.getEqNums()
: trend_component_model_table.getEqNums()))
{
vector lhs, trend_lhs;
vector> trend_var;
vector>> rhs;
if (!var)
{
lhs = trend_component_model_table.getLhs(model_name);
for (auto teqn : trend_component_model_table.getTargetEqNums().at(model_name))
{
int eqnidx = 0;
for (auto eqn : eqns)
{
if (eqn == teqn)
trend_lhs.push_back(lhs[eqnidx]);
eqnidx++;
}
}
}
int lhs_idx = 0;
for (auto eqn : eqns)
{
set> rhs_set;
equations[eqn]->arg2->collectDynamicVariables(SymbolType::endogenous, rhs_set);
rhs.push_back(rhs_set);
if (!var)
{
int lhs_symb_id = lhs[lhs_idx++];
if (symbol_table.isDiffAuxiliaryVariable(lhs_symb_id))
try
{
lhs_symb_id = symbol_table.getOrigSymbIdForAuxVar(lhs_symb_id);
}
catch (...)
{
}
optional trend_var_symb_id = equations[eqn]->arg2->findTargetVariable(lhs_symb_id);
if (trend_var_symb_id)
{
if (symbol_table.isDiffAuxiliaryVariable(*trend_var_symb_id))
try
{
trend_var_symb_id = symbol_table.getOrigSymbIdForAuxVar(*trend_var_symb_id);
}
catch (...)
{
}
if (find(trend_lhs.begin(), trend_lhs.end(), *trend_var_symb_id) == trend_lhs.end())
{
cerr << "ERROR: trend found in trend_component equation #" << eqn << " ("
<< symbol_table.getName(*trend_var_symb_id) << ") does not correspond to a trend equation" << endl;
exit(EXIT_FAILURE);
}
}
trend_var.push_back(move(trend_var_symb_id));
}
}
rhsr[model_name] = rhs;
if (!var)
trend_varr[model_name] = trend_var;
}
if (var)
var_model_table.setRhs(move(rhsr));
else
{
trend_component_model_table.setRhs(move(rhsr));
trend_component_model_table.setTargetVar(move(trend_varr));
}
}
}
void
DynamicModel::fillVarModelTable() const
{
map> eqnums, lhsr;
map> lhs_expr_tr;
map>>> rhsr;
for (const auto &[model_name, eqtags] : var_model_table.getEqTags())
{
vector eqnumber, lhs;
vector lhs_expr_t;
vector>> rhs;
for (const auto &eqtag : eqtags)
{
set> lhs_set, lhs_tmp_set, rhs_set;
optional eqn { equation_tags.getEqnByTag("name", eqtag) };
if (!eqn)
{
cerr << "ERROR: no equation is named '" << eqtag << "'" << endl;
exit(EXIT_FAILURE);
}
equations[*eqn]->arg1->collectDynamicVariables(SymbolType::endogenous, lhs_set);
equations[*eqn]->arg1->collectDynamicVariables(SymbolType::exogenous, lhs_tmp_set);
equations[*eqn]->arg1->collectDynamicVariables(SymbolType::parameter, lhs_tmp_set);
if (lhs_set.size() != 1 || !lhs_tmp_set.empty())
{
cerr << "ERROR: in Equation " << eqtag
<< ". A VAR may only have one endogenous variable on the LHS. " << endl;
exit(EXIT_FAILURE);
}
auto itlhs = lhs_set.begin();
if (itlhs->second != 0)
{
cerr << "ERROR: in Equation " << eqtag
<< ". The variable on the LHS of a VAR may not appear with a lead or a lag. "
<< endl;
exit(EXIT_FAILURE);
}
eqnumber.push_back(*eqn);
lhs.push_back(itlhs->first);
lhs_set.clear();
set lhs_expr_t_set;
equations[*eqn]->arg1->collectVARLHSVariable(lhs_expr_t_set);
lhs_expr_t.push_back(*(lhs_expr_t_set.begin()));
equations[*eqn]->arg2->collectDynamicVariables(SymbolType::endogenous, rhs_set);
rhs.push_back(rhs_set);
}
eqnums[model_name] = eqnumber;
lhsr[model_name] = lhs;
lhs_expr_tr[model_name] = lhs_expr_t;
rhsr[model_name] = rhs;
}
var_model_table.setEqNums(move(eqnums));
var_model_table.setLhs(move(lhsr));
var_model_table.setRhs(move(rhsr));
var_model_table.setLhsExprT(move(lhs_expr_tr));
}
void
DynamicModel::fillVarModelTableFromOrigModel() const
{
map> lags;
map>> orig_diff_var;
map> diff;
for (const auto &[model_name, eqns] : var_model_table.getEqNums())
{
set lhs;
vector> orig_diff_var_vec;
vector diff_vec;
for (auto eqn : eqns)
{
// Perform some sanity checks on the RHS
optional eqtag { equation_tags.getTagValueByEqnAndKey(eqn, "name") };
set> rhs_endo_set, rhs_exo_set;
equations[eqn]->arg2->collectDynamicVariables(SymbolType::endogenous, rhs_endo_set);
for (const auto &[symb_id, lag] : rhs_endo_set)
if (lag > 0)
{
cerr << "ERROR: in Equation " << eqtag.value_or(to_string(eqn+1))
<< ". A VAR model may not have leaded endogenous variables on the RHS. " << endl;
exit(EXIT_FAILURE);
}
else if (!var_model_table.getStructural().at(model_name) && lag == 0)
{
cerr << "ERROR: in Equation " << eqtag.value_or(to_string(eqn+1))
<< ". A non-structural VAR model may not have contemporaneous endogenous variables on the RHS. " << endl;
exit(EXIT_FAILURE);
}
equations[eqn]->arg2->collectDynamicVariables(SymbolType::exogenous, rhs_exo_set);
for (const auto &[symb_id, lag] : rhs_exo_set)
if (lag != 0)
{
cerr << "ERROR: in Equation " << eqtag.value_or(to_string(eqn+1))
<< ". A VAR model may not have lagged or leaded exogenous variables on the RHS. " << endl;
exit(EXIT_FAILURE);
}
// save lhs variables
equations[eqn]->arg1->collectVARLHSVariable(lhs);
equations[eqn]->arg1->countDiffs() > 0 ?
diff_vec.push_back(true) : diff_vec.push_back(false);
if (diff_vec.back())
{
set> diff_set;
equations[eqn]->arg1->collectDynamicVariables(SymbolType::endogenous, diff_set);
if (diff_set.size() != 1)
{
cerr << "ERROR: problem getting variable for LHS diff operator in equation "
<< eqn << endl;
exit(EXIT_FAILURE);
}
orig_diff_var_vec.push_back(diff_set.begin()->first);
}
else
orig_diff_var_vec.push_back(nullopt);
}
if (eqns.size() != lhs.size())
{
cerr << "ERROR: The LHS variables of the VAR model are not unique" << endl;
exit(EXIT_FAILURE);
}
set lhs_lag_equiv;
for (const auto &lh : lhs)
{
auto [lag_equiv_repr, index] = lh->getLagEquivalenceClass();
lhs_lag_equiv.insert(lag_equiv_repr);
}
vector max_lag;
for (auto eqn : eqns)
max_lag.push_back(equations[eqn]->arg2->VarMaxLag(lhs_lag_equiv));
lags[model_name] = max_lag;
diff[model_name] = diff_vec;
orig_diff_var[model_name] = orig_diff_var_vec;
}
var_model_table.setDiff(move(diff));
var_model_table.setMaxLags(move(lags));
var_model_table.setOrigDiffVar(move(orig_diff_var));
}
vector
DynamicModel::getVARDerivIDs(int lhs_symb_id, int lead_lag) const
{
vector deriv_ids;
// First directly look for the variable itself
if (auto it = deriv_id_table.find({ lhs_symb_id, lead_lag });
it != deriv_id_table.end())
deriv_ids.push_back(it->second);
// Then go through auxiliary variables
for (auto &[key, deriv_id2] : deriv_id_table)
{
auto [symb_id2, lead_lag2] = key;
const AuxVarInfo *avi;
try
{
avi = &symbol_table.getAuxVarInfo(symb_id2);
}
catch (SymbolTable::UnknownSymbolIDException)
{
continue;
}
if (avi->type == AuxVarType::endoLag && avi->orig_symb_id.value() == lhs_symb_id
&& avi->orig_lead_lag.value() + lead_lag2 == lead_lag)
deriv_ids.push_back(deriv_id2);
// Handle diff lag auxvar, possibly nested several times
int diff_lag_depth = 0;
while (avi->type == AuxVarType::diffLag)
{
diff_lag_depth++;
if (avi->orig_symb_id == lhs_symb_id && lead_lag2 - diff_lag_depth == lead_lag)
{
deriv_ids.push_back(deriv_id2);
break;
}
try
{
avi = &symbol_table.getAuxVarInfo(avi->orig_symb_id.value());
}
catch (SymbolTable::UnknownSymbolIDException)
{
break;
}
}
}
return deriv_ids;
}
void
DynamicModel::fillVarModelTableMatrices()
{
map, expr_t>> AR;
map, expr_t>> A0;
map> constants;
for (const auto &[model_name, eqns] : var_model_table.getEqNums())
{
const vector &lhs = var_model_table.getLhs(model_name);
int max_lag = var_model_table.getMaxLag(model_name);
for (auto lhs_symb_id : lhs)
{
// Fill autoregressive matrix (AR)
for (int lag = 1; lag <= max_lag; lag++)
{
vector deriv_ids = getVARDerivIDs(lhs_symb_id, -lag);;
for (size_t i = 0; i < eqns.size(); i++)
{
expr_t d = Zero;
for (int deriv_id : deriv_ids)
d = AddPlus(d, equations[eqns[i]]->getDerivative(deriv_id));
if (d != Zero)
{
if (!d->isConstant())
{
cerr << "ERROR: Equation " << equation_tags.getTagValueByEqnAndKey(eqns[i], "name").value_or(to_string(eqns[i]+1)) << " is not linear" << endl;
exit(EXIT_FAILURE);
}
AR[model_name][{ i, lag, lhs_symb_id }] = AddUMinus(d);
}
}
}
// Fill A0 matrix (for contemporaneous variables)
int lhs_deriv_id = getDerivID(lhs_symb_id, 0);
for (size_t i = 0; i < eqns.size(); i++)
{
expr_t d = equations[eqns[i]]->getDerivative(lhs_deriv_id);
if (d != Zero)
{
if (!d->isConstant())
{
cerr << "ERROR: Equation " << equation_tags.getTagValueByEqnAndKey(eqns[i], "name").value_or(to_string(eqns[i]+1)) << " is not linear" << endl;
exit(EXIT_FAILURE);
}
A0[model_name][{ i, lhs_symb_id }] = d;
}
}
// Fill constants vector
// Constants are computed by replacing all (transformed) endos and exos by zero
constants[model_name] = {}; // Ensure that the map exists, even if constants are all zero
for (size_t i = 0; i < eqns.size(); i++)
{
auto rhs = equations[eqns[i]]->arg2;
map subst_table;
auto rhs_vars = var_model_table.getRhs(model_name)[i]; // All the (transformed) endogenous on RHS, as computed by updateVarAndTrendModel()
rhs->collectDynamicVariables(SymbolType::exogenous, rhs_vars); // Add exos
for (auto [symb_id, lag] : rhs_vars)
subst_table[AddVariable(symb_id, lag)] = Zero;
expr_t c = rhs->replaceVarsInEquation(subst_table);
if (c != Zero)
constants[model_name][i] = c;
}
}
}
var_model_table.setAR(move(AR));
var_model_table.setA0(move(A0));
var_model_table.setConstants(move(constants));
}
map, expr_t>>
DynamicModel::computeAutoregressiveMatrices() const
{
map, expr_t>> ARr;
for (const auto &[model_name, eqns] : trend_component_model_table.getNonTargetEqNums())
{
map, expr_t> AR;
const vector &lhs = trend_component_model_table.getNonTargetLhs(model_name);
for (int i{0};
auto eqn : eqns)
{
auto bopn = dynamic_cast(equations[eqn]->arg2);
bopn->fillAutoregressiveRow(i++, lhs, AR);
}
ARr[model_name] = AR;
}
return ARr;
}
void
DynamicModel::fillTrendComponentModelTable() const
{
map> eqnums, trend_eqnums, lhsr;
map> lhs_expr_tr;
map>>> rhsr;
for (const auto &[model_name, eqtags] : trend_component_model_table.getTargetEqTags())
{
vector trend_eqnumber;
for (const auto &eqtag : eqtags)
{
optional eqn { equation_tags.getEqnByTag("name", eqtag) };
if (!eqn)
{
cerr << "ERROR: no equation is named '" << eqtag << "'" << endl;
exit(EXIT_FAILURE);
}
trend_eqnumber.push_back(*eqn);
}
trend_eqnums[model_name] = trend_eqnumber;
}
for (const auto &[model_name, eqtags] : trend_component_model_table.getEqTags())
{
vector eqnumber, lhs;
vector lhs_expr_t;
vector>> rhs;
for (const auto &eqtag : eqtags)
{
set> lhs_set, lhs_tmp_set, rhs_set;
optional eqn { equation_tags.getEqnByTag("name", eqtag) };
if (!eqn)
{
cerr << "ERROR: no equation is named '" << eqtag << "'" << endl;
exit(EXIT_FAILURE);
}
equations[*eqn]->arg1->collectDynamicVariables(SymbolType::endogenous, lhs_set);
equations[*eqn]->arg1->collectDynamicVariables(SymbolType::exogenous, lhs_tmp_set);
equations[*eqn]->arg1->collectDynamicVariables(SymbolType::parameter, lhs_tmp_set);
if (lhs_set.size() != 1 || !lhs_tmp_set.empty())
{
cerr << "ERROR: in Equation " << eqtag
<< ". A trend component model may only have one endogenous variable on the LHS. " << endl;
exit(EXIT_FAILURE);
}
auto itlhs = lhs_set.begin();
if (itlhs->second != 0)
{
cerr << "ERROR: in Equation " << eqtag
<< ". The variable on the LHS of a trend component model may not appear with a lead or a lag. "
<< endl;
exit(EXIT_FAILURE);
}
eqnumber.push_back(*eqn);
lhs.push_back(itlhs->first);
lhs_set.clear();
set lhs_expr_t_set;
equations[*eqn]->arg1->collectVARLHSVariable(lhs_expr_t_set);
lhs_expr_t.push_back(*(lhs_expr_t_set.begin()));
equations[*eqn]->arg2->collectDynamicVariables(SymbolType::endogenous, rhs_set);
rhs.push_back(rhs_set);
}
eqnums[model_name] = eqnumber;
lhsr[model_name] = lhs;
lhs_expr_tr[model_name] = lhs_expr_t;
rhsr[model_name] = rhs;
}
trend_component_model_table.setRhs(move(rhsr));
trend_component_model_table.setVals(move(eqnums), move(trend_eqnums), move(lhsr), move(lhs_expr_tr));
}
pair