2009-04-14 16:39:53 +02:00
|
|
|
|
/*
|
2023-01-04 16:03:12 +01:00
|
|
|
|
* Copyright © 2003-2023 Dynare Team
|
2009-04-14 16:39:53 +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
|
2021-06-09 16:52:20 +02:00
|
|
|
|
* along with Dynare. If not, see <https://www.gnu.org/licenses/>.
|
2009-04-14 16:39:53 +02:00
|
|
|
|
*/
|
|
|
|
|
|
2023-11-30 15:28:57 +01:00
|
|
|
|
#include <algorithm>
|
|
|
|
|
#include <cassert>
|
2009-04-14 16:39:53 +02:00
|
|
|
|
#include <cmath>
|
2009-04-14 16:47:57 +02:00
|
|
|
|
#include <cstdlib>
|
2023-11-30 15:28:57 +01:00
|
|
|
|
#include <iostream>
|
2018-06-07 12:53:00 +02:00
|
|
|
|
#include <numeric>
|
2023-12-01 15:06:40 +01:00
|
|
|
|
#include <ranges>
|
2019-11-18 17:13:49 +01:00
|
|
|
|
#include <regex>
|
2021-04-23 17:30:38 +02:00
|
|
|
|
#include <sstream>
|
2022-10-04 16:34:43 +02:00
|
|
|
|
#include <string_view>
|
2009-04-14 16:39:53 +02:00
|
|
|
|
|
2018-06-27 15:01:31 +02:00
|
|
|
|
#include "DynamicModel.hh"
|
2021-07-20 18:18:24 +02:00
|
|
|
|
#include "ParsingDriver.hh"
|
2009-04-14 16:39:53 +02:00
|
|
|
|
|
2018-10-09 18:27:19 +02:00
|
|
|
|
void
|
2023-11-30 15:28:57 +01:00
|
|
|
|
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<BinaryOpNode*>(f(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},
|
|
|
|
|
dyn_jacobian_ncols {m.dyn_jacobian_ncols},
|
|
|
|
|
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_jacob_cols_endo {m.blocks_jacob_cols_endo},
|
|
|
|
|
var_expectation_functions_to_write {m.var_expectation_functions_to_write},
|
|
|
|
|
mfs {m.mfs},
|
|
|
|
|
static_mfs {m.static_mfs}
|
2009-04-16 12:33:30 +02:00
|
|
|
|
{
|
2018-10-09 18:27:19 +02:00
|
|
|
|
copyHelper(m);
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-30 15:28:57 +01:00
|
|
|
|
DynamicModel&
|
|
|
|
|
DynamicModel::operator=(const DynamicModel& m)
|
2018-10-09 18:27:19 +02:00
|
|
|
|
{
|
|
|
|
|
ModelTree::operator=(m);
|
|
|
|
|
|
|
|
|
|
assert(&trend_component_model_table == &m.trend_component_model_table);
|
|
|
|
|
assert(&var_model_table == &m.var_model_table);
|
2019-11-14 17:51:16 +01:00
|
|
|
|
balanced_growth_test_tol = m.balanced_growth_test_tol;
|
2018-10-09 18:27:19 +02:00
|
|
|
|
|
|
|
|
|
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;
|
2023-01-19 10:59:36 +01:00
|
|
|
|
dyn_jacobian_ncols = m.dyn_jacobian_ncols;
|
2018-10-09 18:27:19 +02:00
|
|
|
|
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;
|
2018-12-11 17:26:50 +01:00
|
|
|
|
max_lag_with_diffs_expanded_orig = m.max_lag_with_diffs_expanded_orig;
|
2018-10-09 18:27:19 +02:00
|
|
|
|
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;
|
2019-12-20 16:59:30 +01:00
|
|
|
|
xref_param = m.xref_param;
|
2018-10-09 18:27:19 +02:00
|
|
|
|
xref_endo = m.xref_endo;
|
|
|
|
|
xref_exo = m.xref_exo;
|
|
|
|
|
xref_exo_det = m.xref_exo_det;
|
|
|
|
|
nonzero_hessian_eqs = m.nonzero_hessian_eqs;
|
2020-04-30 14:57:50 +02:00
|
|
|
|
variableMapping = m.variableMapping;
|
2020-05-06 14:02:58 +02:00
|
|
|
|
blocks_jacob_cols_endo = m.blocks_jacob_cols_endo;
|
2018-10-09 18:27:19 +02:00
|
|
|
|
|
|
|
|
|
var_expectation_functions_to_write = m.var_expectation_functions_to_write;
|
2023-10-16 16:24:31 +02:00
|
|
|
|
mfs = m.mfs;
|
2023-10-16 17:47:28 +02:00
|
|
|
|
static_mfs = m.static_mfs;
|
2018-10-09 18:27:19 +02:00
|
|
|
|
|
|
|
|
|
copyHelper(m);
|
|
|
|
|
|
|
|
|
|
return *this;
|
2009-04-16 12:33:30 +02:00
|
|
|
|
}
|
|
|
|
|
|
2009-04-14 16:39:53 +02:00
|
|
|
|
void
|
2023-11-30 15:28:57 +01:00
|
|
|
|
DynamicModel::writeDynamicBytecode(const string& basename) const
|
2010-01-22 11:03:29 +01:00
|
|
|
|
{
|
2023-09-27 11:23:18 +02:00
|
|
|
|
/* Bytecode only works when there are with as many endogenous as equations.
|
|
|
|
|
(e.g. the constructor of FBEGINBLOCK_ makes this assumption) */
|
|
|
|
|
assert(static_cast<int>(equations.size()) == symbol_table.endo_nbr());
|
|
|
|
|
|
2022-07-13 13:04:10 +02:00
|
|
|
|
// Determine the type of model (used for typing the single block)
|
2010-01-22 11:03:29 +01:00
|
|
|
|
BlockSimulationType simulation_type;
|
2022-07-13 13:04:10 +02:00
|
|
|
|
if (max_endo_lag > 0 && max_endo_lead > 0)
|
2020-03-20 17:31:14 +01:00
|
|
|
|
simulation_type = BlockSimulationType::solveTwoBoundariesComplete;
|
2022-07-13 13:04:10 +02:00
|
|
|
|
else if (max_endo_lag >= 0 && max_endo_lead == 0)
|
2020-03-20 17:31:14 +01:00
|
|
|
|
simulation_type = BlockSimulationType::solveForwardComplete;
|
2010-01-22 11:03:29 +01:00
|
|
|
|
else
|
2020-03-20 17:31:14 +01:00
|
|
|
|
simulation_type = BlockSimulationType::solveBackwardComplete;
|
2010-01-22 11:03:29 +01:00
|
|
|
|
|
2022-07-13 13:04:10 +02:00
|
|
|
|
// First write the .bin file
|
2023-11-30 15:28:57 +01:00
|
|
|
|
int u_count_int {
|
|
|
|
|
writeBytecodeBinFile(basename + "/model/bytecode/dynamic.bin",
|
|
|
|
|
simulation_type == BlockSimulationType::solveTwoBoundariesComplete)};
|
2010-01-22 11:03:29 +01:00
|
|
|
|
|
2023-12-14 14:52:50 +01:00
|
|
|
|
Bytecode::Writer code_file {basename + "/model/bytecode/dynamic.cod"};
|
2010-07-23 11:20:24 +02:00
|
|
|
|
|
2022-07-13 13:04:10 +02:00
|
|
|
|
// Declare temporary terms
|
2023-12-14 16:17:22 +01:00
|
|
|
|
code_file << Bytecode::FDIMT {static_cast<int>(temporary_terms_derivatives[0].size()
|
|
|
|
|
+ temporary_terms_derivatives[1].size())};
|
2010-01-22 11:03:29 +01:00
|
|
|
|
|
2022-07-13 13:04:10 +02:00
|
|
|
|
// Declare the (single) block
|
|
|
|
|
vector<int> 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);
|
2010-10-27 15:34:48 +02:00
|
|
|
|
|
2023-11-30 15:28:57 +01:00
|
|
|
|
int jacobian_ncols_endo {static_cast<int>(count_if(
|
|
|
|
|
dyn_jacobian_cols_table.begin(), dyn_jacobian_cols_table.end(),
|
|
|
|
|
[this](const auto& v) { return getTypeByDerivID(v.first) == SymbolType::endogenous; }))};
|
2022-09-13 15:43:13 +02:00
|
|
|
|
vector<int> eq_idx(equations.size());
|
|
|
|
|
iota(eq_idx.begin(), eq_idx.end(), 0);
|
|
|
|
|
vector<int> endo_idx(symbol_table.endo_nbr());
|
|
|
|
|
iota(endo_idx.begin(), endo_idx.end(), 0);
|
2017-06-01 19:58:32 +02:00
|
|
|
|
|
2023-12-14 16:17:22 +01:00
|
|
|
|
code_file << Bytecode::FBEGINBLOCK {symbol_table.endo_nbr(),
|
|
|
|
|
simulation_type,
|
|
|
|
|
0,
|
|
|
|
|
symbol_table.endo_nbr(),
|
|
|
|
|
endo_idx,
|
|
|
|
|
eq_idx,
|
|
|
|
|
false,
|
|
|
|
|
u_count_int,
|
|
|
|
|
jacobian_ncols_endo,
|
|
|
|
|
symbol_table.exo_det_nbr(),
|
|
|
|
|
symbol_table.exo_nbr(),
|
|
|
|
|
exo_det,
|
|
|
|
|
exo};
|
2022-07-13 13:04:10 +02:00
|
|
|
|
|
|
|
|
|
writeBytecodeHelper<true>(code_file);
|
2010-01-22 11:03:29 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2023-11-30 15:28:57 +01:00
|
|
|
|
DynamicModel::writeDynamicBlockBytecode(const string& basename) const
|
2009-12-16 18:13:23 +01:00
|
|
|
|
{
|
2023-12-14 14:52:50 +01:00
|
|
|
|
Bytecode::Writer code_file {basename + "/model/bytecode/block/dynamic.cod"};
|
2009-12-16 18:13:23 +01:00
|
|
|
|
|
2023-01-09 13:35:49 +01:00
|
|
|
|
const filesystem::path bin_filename {basename + "/model/bytecode/block/dynamic.bin"};
|
2022-07-19 18:24:36 +02:00
|
|
|
|
ofstream bin_file {bin_filename, ios::out | ios::binary};
|
|
|
|
|
if (!bin_file.is_open())
|
2009-12-16 18:13:23 +01:00
|
|
|
|
{
|
2023-01-05 16:40:04 +01:00
|
|
|
|
cerr << R"(Error : Can't open file ")" << bin_filename.string() << R"(" for writing)" << endl;
|
2022-07-19 18:24:36 +02:00
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
|
}
|
2022-02-18 12:37:37 +01:00
|
|
|
|
|
2022-07-19 18:24:36 +02:00
|
|
|
|
// Temporary variables declaration
|
2023-12-14 16:17:22 +01:00
|
|
|
|
code_file << Bytecode::FDIMT {static_cast<int>(blocks_temporary_terms_idxs.size())};
|
2010-07-23 11:20:24 +02:00
|
|
|
|
|
2023-05-12 17:22:02 +02:00
|
|
|
|
temporary_terms_t temporary_terms_written;
|
|
|
|
|
|
2022-07-19 18:24:36 +02:00
|
|
|
|
for (int block {0}; block < static_cast<int>(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
|
2023-11-30 15:28:57 +01:00
|
|
|
|
|| simulation_type
|
|
|
|
|
== BlockSimulationType::solveTwoBoundariesComplete
|
|
|
|
|
|| simulation_type == BlockSimulationType::solveBackwardComplete
|
|
|
|
|
|| simulation_type == BlockSimulationType::solveForwardComplete
|
|
|
|
|
? writeBlockBytecodeBinFile(bin_file, block)
|
|
|
|
|
: 0};
|
|
|
|
|
|
2023-12-14 16:17:22 +01:00
|
|
|
|
code_file << Bytecode::FBEGINBLOCK {blocks[block].mfs_size,
|
|
|
|
|
simulation_type,
|
|
|
|
|
blocks[block].first_equation,
|
|
|
|
|
blocks[block].size,
|
|
|
|
|
endo_idx_block2orig,
|
|
|
|
|
eq_idx_block2orig,
|
|
|
|
|
blocks[block].linear,
|
|
|
|
|
u_count,
|
|
|
|
|
static_cast<int>(blocks_jacob_cols_endo[block].size())};
|
2022-07-19 18:24:36 +02:00
|
|
|
|
|
2023-05-12 17:22:02 +02:00
|
|
|
|
writeBlockBytecodeHelper<true>(code_file, block, temporary_terms_written);
|
2009-12-16 18:13:23 +01:00
|
|
|
|
}
|
2023-12-14 16:17:22 +01:00
|
|
|
|
code_file << Bytecode::FEND {};
|
2009-12-16 18:13:23 +01:00
|
|
|
|
}
|
2009-04-14 16:39:53 +02:00
|
|
|
|
|
|
|
|
|
void
|
2023-11-30 15:28:57 +01:00
|
|
|
|
DynamicModel::writeDynamicMFile(const string& basename) const
|
2009-12-16 18:13:23 +01:00
|
|
|
|
{
|
2022-07-11 17:33:09 +02:00
|
|
|
|
auto [d_output, tt_output] = writeModelFileHelper<ExprNodeOutputType::matlabDynamicModel>();
|
|
|
|
|
|
|
|
|
|
ostringstream init_output, end_output;
|
|
|
|
|
init_output << "residual = zeros(" << equations.size() << ", 1);";
|
2022-07-12 17:04:41 +02:00
|
|
|
|
writeDynamicMFileHelper(basename, "dynamic_resid", "residual", "dynamic_resid_tt",
|
2023-11-30 15:28:57 +01:00
|
|
|
|
temporary_terms_derivatives[0].size(), "", init_output, end_output,
|
|
|
|
|
d_output[0], tt_output[0]);
|
2022-07-11 17:33:09 +02:00
|
|
|
|
|
|
|
|
|
init_output.str("");
|
2022-09-14 17:07:08 +02:00
|
|
|
|
init_output << "g1 = zeros(" << equations.size() << ", " << getJacobianColsNbr(false) << ");";
|
2022-07-12 17:04:41 +02:00
|
|
|
|
writeDynamicMFileHelper(basename, "dynamic_g1", "g1", "dynamic_g1_tt",
|
2023-11-30 15:28:57 +01:00
|
|
|
|
temporary_terms_derivatives[0].size()
|
|
|
|
|
+ temporary_terms_derivatives[1].size(),
|
2022-07-12 17:04:41 +02:00
|
|
|
|
"dynamic_resid_tt", init_output, end_output, d_output[1], tt_output[1]);
|
|
|
|
|
writeDynamicMWrapperFunction(basename, "g1");
|
2022-07-11 17:33:09 +02:00
|
|
|
|
|
|
|
|
|
// For order ≥ 2
|
2023-11-30 15:28:57 +01:00
|
|
|
|
int ncols {getJacobianColsNbr(false)};
|
|
|
|
|
int ntt {static_cast<int>(temporary_terms_derivatives[0].size()
|
|
|
|
|
+ temporary_terms_derivatives[1].size())};
|
|
|
|
|
for (size_t i {2}; i < derivatives.size(); i++)
|
2022-07-11 17:33:09 +02:00
|
|
|
|
{
|
2022-09-14 17:07:08 +02:00
|
|
|
|
ncols *= getJacobianColsNbr(false);
|
2022-07-11 17:33:09 +02:00
|
|
|
|
ntt += temporary_terms_derivatives[i].size();
|
2023-11-30 15:28:57 +01:00
|
|
|
|
string gname {"g" + to_string(i)};
|
|
|
|
|
string gprevname {"g" + to_string(i - 1)};
|
2022-07-11 17:33:09 +02:00
|
|
|
|
|
|
|
|
|
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;
|
2023-11-30 15:28:57 +01:00
|
|
|
|
end_output << gname << " = sparse(" << gname << "_i," << gname << "_j," << gname << "_v,"
|
2022-07-11 17:33:09 +02:00
|
|
|
|
<< equations.size() << "," << ncols << ");";
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
init_output << gname << " = sparse([],[],[]," << equations.size() << "," << ncols << ");";
|
2022-07-12 17:04:41 +02:00
|
|
|
|
writeDynamicMFileHelper(basename, "dynamic_" + gname, gname, "dynamic_" + gname + "_tt", ntt,
|
2023-11-30 15:28:57 +01:00
|
|
|
|
"dynamic_" + gprevname + "_tt", init_output, end_output, d_output[i],
|
|
|
|
|
tt_output[i]);
|
2022-07-11 17:33:09 +02:00
|
|
|
|
if (i <= 3)
|
2022-07-12 17:04:41 +02:00
|
|
|
|
writeDynamicMWrapperFunction(basename, gname);
|
2022-07-11 17:33:09 +02:00
|
|
|
|
}
|
|
|
|
|
|
2022-07-12 17:04:41 +02:00
|
|
|
|
writeDynamicMCompatFile(basename);
|
2009-12-16 18:13:23 +01:00
|
|
|
|
}
|
2009-04-14 16:39:53 +02:00
|
|
|
|
|
|
|
|
|
string
|
2023-11-30 15:28:57 +01:00
|
|
|
|
DynamicModel::reform(const string& name1) const
|
2009-12-16 18:13:23 +01:00
|
|
|
|
{
|
|
|
|
|
string name = name1;
|
2023-12-13 10:50:24 +01:00
|
|
|
|
int pos = name.find('\\', 0);
|
2009-12-16 18:13:23 +01:00
|
|
|
|
while (pos >= 0)
|
|
|
|
|
{
|
2019-04-03 16:32:52 +02:00
|
|
|
|
if (name.substr(pos + 1, 1) != R"(\)")
|
2009-12-16 18:13:23 +01:00
|
|
|
|
{
|
2019-04-03 16:32:52 +02:00
|
|
|
|
name = name.insert(pos, R"(\)");
|
2009-12-16 18:13:23 +01:00
|
|
|
|
pos++;
|
|
|
|
|
}
|
|
|
|
|
pos++;
|
2023-12-13 10:50:24 +01:00
|
|
|
|
pos = name.find('\\', pos);
|
2009-12-16 18:13:23 +01:00
|
|
|
|
}
|
2019-12-16 19:42:59 +01:00
|
|
|
|
return name;
|
2009-12-16 18:13:23 +01:00
|
|
|
|
}
|
2009-04-14 16:39:53 +02:00
|
|
|
|
|
2016-07-27 21:01:54 +02:00
|
|
|
|
void
|
2023-11-30 15:28:57 +01:00
|
|
|
|
DynamicModel::printNonZeroHessianEquations(ostream& output) const
|
2018-01-11 12:55:36 +01:00
|
|
|
|
{
|
2019-12-16 19:42:59 +01:00
|
|
|
|
if (nonzero_hessian_eqs.size() != 1)
|
2018-01-11 12:55:36 +01:00
|
|
|
|
output << "[";
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (bool printed_something {false}; int it : nonzero_hessian_eqs)
|
2018-01-11 12:55:36 +01:00
|
|
|
|
{
|
2022-06-03 16:24:26 +02:00
|
|
|
|
if (exchange(printed_something, true))
|
2018-01-11 12:55:36 +01:00
|
|
|
|
output << " ";
|
2022-06-03 16:24:26 +02:00
|
|
|
|
output << it + 1;
|
2018-01-11 12:55:36 +01:00
|
|
|
|
}
|
|
|
|
|
if (nonzero_hessian_eqs.size() != 1)
|
|
|
|
|
output << "]";
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-27 17:14:30 +02:00
|
|
|
|
void
|
2023-11-30 15:28:57 +01:00
|
|
|
|
DynamicModel::writeDynamicMWrapperFunction(const string& basename, const string& ending) const
|
2018-03-27 17:14:30 +02:00
|
|
|
|
{
|
|
|
|
|
string name;
|
|
|
|
|
if (ending == "g1")
|
2018-06-27 15:01:31 +02:00
|
|
|
|
name = "dynamic_resid_g1";
|
2018-03-27 17:14:30 +02:00
|
|
|
|
else if (ending == "g2")
|
2019-12-20 16:59:30 +01:00
|
|
|
|
name = "dynamic_resid_g1_g2";
|
2018-03-27 17:14:30 +02:00
|
|
|
|
else if (ending == "g3")
|
2018-06-27 15:01:31 +02:00
|
|
|
|
name = "dynamic_resid_g1_g2_g3";
|
2018-03-27 17:14:30 +02:00
|
|
|
|
|
2022-10-11 15:59:56 +02:00
|
|
|
|
filesystem::path filename {packageDir(basename) / (name + ".m")};
|
2023-11-30 15:28:57 +01:00
|
|
|
|
ofstream output {filename, ios::out | ios::binary};
|
2018-03-27 17:14:30 +02:00
|
|
|
|
if (!output.is_open())
|
|
|
|
|
{
|
2022-10-11 15:59:56 +02:00
|
|
|
|
cerr << "ERROR: Can't open file " << filename.string() << " for writing" << endl;
|
2018-03-27 17:14:30 +02:00
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ending == "g1")
|
2023-11-30 15:28:57 +01:00
|
|
|
|
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;
|
2018-03-27 17:14:30 +02:00
|
|
|
|
else if (ending == "g2")
|
2023-11-30 15:28:57 +01:00
|
|
|
|
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;
|
2018-03-27 17:14:30 +02:00
|
|
|
|
else if (ending == "g3")
|
2023-11-30 15:28:57 +01:00
|
|
|
|
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;
|
2018-03-27 17:14:30 +02:00
|
|
|
|
|
|
|
|
|
output << "%" << endl
|
|
|
|
|
<< "% Wrapper function automatically created by Dynare" << endl
|
2018-05-23 17:32:50 +02:00
|
|
|
|
<< "%" << endl
|
|
|
|
|
<< endl
|
|
|
|
|
<< " if T_flag" << endl
|
2023-11-30 15:28:57 +01:00
|
|
|
|
<< " T = " << basename << ".dynamic_" << ending
|
|
|
|
|
<< "_tt(T, y, x, params, steady_state, it_);" << endl
|
2018-05-23 17:32:50 +02:00
|
|
|
|
<< " end" << endl;
|
2018-03-27 17:14:30 +02:00
|
|
|
|
|
|
|
|
|
if (ending == "g1")
|
2023-11-30 15:28:57 +01:00
|
|
|
|
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;
|
2018-03-27 17:14:30 +02:00
|
|
|
|
else if (ending == "g2")
|
2023-11-30 15:28:57 +01:00
|
|
|
|
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;
|
2018-03-27 17:14:30 +02:00
|
|
|
|
else if (ending == "g3")
|
2023-11-30 15:28:57 +01:00
|
|
|
|
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;
|
2018-03-27 17:14:30 +02:00
|
|
|
|
|
|
|
|
|
output << endl << "end" << endl;
|
|
|
|
|
output.close();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2023-11-30 15:28:57 +01:00
|
|
|
|
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
|
2018-03-27 17:14:30 +02:00
|
|
|
|
{
|
2022-10-11 15:59:56 +02:00
|
|
|
|
filesystem::path filename {packageDir(basename) / (name_tt + ".m")};
|
2023-11-30 15:28:57 +01:00
|
|
|
|
ofstream output {filename, ios::out | ios::binary};
|
2018-03-27 17:14:30 +02:00
|
|
|
|
if (!output.is_open())
|
|
|
|
|
{
|
2022-10-11 15:59:56 +02:00
|
|
|
|
cerr << "ERROR: Can't open file " << filename.string() << " for writing" << endl;
|
2018-03-27 17:14:30 +02:00
|
|
|
|
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
|
2023-11-30 15:28:57 +01:00
|
|
|
|
<< "% 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
|
2018-03-27 17:14:30 +02:00
|
|
|
|
<< "% to evaluate the model" << endl
|
|
|
|
|
<< "%" << endl
|
|
|
|
|
<< "% Output:" << endl
|
|
|
|
|
<< "% T [#temp variables by 1] double vector of temporary terms" << endl
|
2023-11-30 15:28:57 +01:00
|
|
|
|
<< "%" << endl
|
|
|
|
|
<< endl
|
2018-03-27 17:14:30 +02:00
|
|
|
|
<< "assert(length(T) >= " << ttlen << ");" << endl
|
|
|
|
|
<< endl;
|
|
|
|
|
|
|
|
|
|
if (!previous_tt_name.empty())
|
2023-11-30 15:28:57 +01:00
|
|
|
|
output << "T = " << basename << "." << previous_tt_name
|
|
|
|
|
<< "(T, y, x, params, steady_state, it_);" << endl
|
|
|
|
|
<< endl;
|
2018-03-27 17:14:30 +02:00
|
|
|
|
|
2023-11-30 15:28:57 +01:00
|
|
|
|
output << s_tt.str() << endl << "end" << endl;
|
2018-03-27 17:14:30 +02:00
|
|
|
|
output.close();
|
|
|
|
|
|
2022-10-11 15:59:56 +02:00
|
|
|
|
filename = packageDir(basename) / (name + ".m");
|
2018-06-27 15:12:12 +02:00
|
|
|
|
output.open(filename, ios::out | ios::binary);
|
2018-03-27 17:14:30 +02:00
|
|
|
|
if (!output.is_open())
|
|
|
|
|
{
|
2022-10-11 15:59:56 +02:00
|
|
|
|
cerr << "ERROR: Can't open file " << filename.string() << " for writing" << endl;
|
2018-03-27 17:14:30 +02:00
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-30 15:28:57 +01:00
|
|
|
|
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
|
2018-03-27 17:14:30 +02:00
|
|
|
|
<< "%" << endl
|
|
|
|
|
<< "% File created by Dynare Preprocessor from .mod file" << endl
|
|
|
|
|
<< "%" << endl
|
|
|
|
|
<< "% Inputs:" << endl
|
2023-11-30 15:28:57 +01:00
|
|
|
|
<< "% 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
|
2018-03-27 17:14:30 +02:00
|
|
|
|
<< "% to evaluate the model" << endl
|
2023-11-30 15:28:57 +01:00
|
|
|
|
<< "% T_flag boolean boolean flag saying whether or not to "
|
|
|
|
|
"calculate temporary terms"
|
|
|
|
|
<< endl
|
2018-03-27 17:14:30 +02:00
|
|
|
|
<< "%" << endl
|
|
|
|
|
<< "% Output:" << endl
|
|
|
|
|
<< "% " << retvalname << endl
|
2023-11-30 15:28:57 +01:00
|
|
|
|
<< "%" << endl
|
|
|
|
|
<< endl;
|
2018-03-27 17:14:30 +02:00
|
|
|
|
|
|
|
|
|
if (!name_tt.empty())
|
|
|
|
|
output << "if T_flag" << endl
|
2023-11-30 15:28:57 +01:00
|
|
|
|
<< " T = " << basename << "." << name_tt << "(T, y, x, params, steady_state, it_);"
|
|
|
|
|
<< endl
|
2018-03-27 17:14:30 +02:00
|
|
|
|
<< "end" << endl;
|
|
|
|
|
|
2023-11-30 15:28:57 +01:00
|
|
|
|
output << init_s.str() << endl << s.str() << end_s.str() << endl << "end" << endl;
|
2018-03-27 17:14:30 +02:00
|
|
|
|
output.close();
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-23 16:10:26 +02:00
|
|
|
|
void
|
2023-11-30 15:28:57 +01:00
|
|
|
|
DynamicModel::writeDynamicMCompatFile(const string& basename) const
|
2018-05-23 16:10:26 +02:00
|
|
|
|
{
|
2022-10-11 15:59:56 +02:00
|
|
|
|
filesystem::path filename {packageDir(basename) / "dynamic.m"};
|
2023-11-30 15:28:57 +01:00
|
|
|
|
ofstream output {filename, ios::out | ios::binary};
|
2018-05-23 16:10:26 +02:00
|
|
|
|
if (!output.is_open())
|
|
|
|
|
{
|
2022-10-11 15:59:56 +02:00
|
|
|
|
cerr << "ERROR: Can't open file " << filename.string() << " for writing" << endl;
|
2018-05-23 16:10:26 +02:00
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
|
}
|
2023-11-30 15:28:57 +01:00
|
|
|
|
int ntt {static_cast<int>(
|
|
|
|
|
temporary_terms_derivatives[0].size() + temporary_terms_derivatives[1].size()
|
|
|
|
|
+ temporary_terms_derivatives[2].size() + temporary_terms_derivatives[3].size())};
|
2018-05-23 16:10:26 +02:00
|
|
|
|
|
2018-06-27 15:01:31 +02:00
|
|
|
|
output << "function [residual, g1, g2, g3] = dynamic(y, x, params, steady_state, it_)" << endl
|
2018-05-23 16:10:26 +02:00
|
|
|
|
<< " T = NaN(" << ntt << ", 1);" << endl
|
|
|
|
|
<< " if nargout <= 1" << endl
|
2023-11-30 15:28:57 +01:00
|
|
|
|
<< " residual = " << basename
|
|
|
|
|
<< ".dynamic_resid(T, y, x, params, steady_state, it_, true);" << endl
|
2018-05-23 16:10:26 +02:00
|
|
|
|
<< " elseif nargout == 2" << endl
|
2023-11-30 15:28:57 +01:00
|
|
|
|
<< " [residual, g1] = " << basename
|
|
|
|
|
<< ".dynamic_resid_g1(T, y, x, params, steady_state, it_, true);" << endl
|
2018-05-23 16:10:26 +02:00
|
|
|
|
<< " elseif nargout == 3" << endl
|
2023-11-30 15:28:57 +01:00
|
|
|
|
<< " [residual, g1, g2] = " << basename
|
|
|
|
|
<< ".dynamic_resid_g1_g2(T, y, x, params, steady_state, it_, true);" << endl
|
2018-05-23 16:10:26 +02:00
|
|
|
|
<< " else" << endl
|
2023-11-30 15:28:57 +01:00
|
|
|
|
<< " [residual, g1, g2, g3] = " << basename
|
|
|
|
|
<< ".dynamic_resid_g1_g2_g3(T, y, x, params, steady_state, it_, true);" << endl
|
2018-05-23 16:10:26 +02:00
|
|
|
|
<< " end" << endl
|
|
|
|
|
<< "end" << endl;
|
|
|
|
|
|
|
|
|
|
output.close();
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-14 17:21:31 +01:00
|
|
|
|
vector<map<string, string>>
|
2023-11-30 15:28:57 +01:00
|
|
|
|
DynamicModel::parseIncludeExcludeEquations(const string& inc_exc_option_value, bool exclude_eqs)
|
2019-11-18 17:13:49 +01:00
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
auto removeLeadingTrailingWhitespace = [](string& str) {
|
2021-12-14 17:24:14 +01:00
|
|
|
|
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);
|
|
|
|
|
};
|
|
|
|
|
|
2019-11-18 17:13:49 +01:00
|
|
|
|
string tags;
|
2021-12-14 17:24:14 +01:00
|
|
|
|
if (filesystem::exists(inc_exc_option_value))
|
2019-11-18 17:13:49 +01:00
|
|
|
|
{
|
|
|
|
|
ifstream exclude_file;
|
2021-12-14 17:24:14 +01:00
|
|
|
|
exclude_file.open(inc_exc_option_value, ifstream::in);
|
2019-11-18 17:13:49 +01:00
|
|
|
|
if (!exclude_file.is_open())
|
|
|
|
|
{
|
2021-12-14 17:24:14 +01:00
|
|
|
|
cerr << "ERROR: Could not open " << inc_exc_option_value << endl;
|
2019-11-18 17:13:49 +01:00
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string line;
|
|
|
|
|
bool tagname_on_first_line = false;
|
|
|
|
|
while (getline(exclude_file, line))
|
|
|
|
|
{
|
|
|
|
|
removeLeadingTrailingWhitespace(line);
|
|
|
|
|
if (!line.empty())
|
2023-04-11 14:21:17 +02:00
|
|
|
|
{
|
2023-12-13 10:50:24 +01:00
|
|
|
|
if (tags.empty() && line.find('=') != string::npos)
|
2023-04-11 14:21:17 +02:00
|
|
|
|
{
|
|
|
|
|
tagname_on_first_line = true;
|
|
|
|
|
tags += line + "(";
|
|
|
|
|
}
|
2023-12-13 10:50:24 +01:00
|
|
|
|
else if (line.find('\'') != string::npos)
|
2019-11-18 17:13:49 +01:00
|
|
|
|
tags += line + ",";
|
|
|
|
|
else
|
|
|
|
|
tags += "'" + line + "',";
|
2023-04-11 14:21:17 +02:00
|
|
|
|
}
|
2019-11-18 17:13:49 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!tags.empty())
|
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
tags = tags.substr(0, tags.size() - 1);
|
2019-11-18 17:13:49 +01:00
|
|
|
|
if (tagname_on_first_line)
|
|
|
|
|
tags += ")";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
2021-12-14 17:24:14 +01:00
|
|
|
|
tags = inc_exc_option_value;
|
2019-11-18 17:13:49 +01:00
|
|
|
|
removeLeadingTrailingWhitespace(tags);
|
|
|
|
|
|
|
|
|
|
if (tags.front() == '[' && tags.back() != ']')
|
|
|
|
|
{
|
2021-12-14 17:24:14 +01:00
|
|
|
|
cerr << "ERROR: " << (exclude_eqs ? "exclude_eqs" : "include_eqs")
|
2019-11-18 17:13:49 +01:00
|
|
|
|
<< ": 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);
|
|
|
|
|
|
2019-12-20 16:59:30 +01:00
|
|
|
|
regex q(R"(^\w+\s*=)");
|
2019-11-18 17:13:49 +01:00
|
|
|
|
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);
|
|
|
|
|
}
|
2023-11-30 15:28:57 +01:00
|
|
|
|
tagname = tagname.substr(0, tagname.size() - 1);
|
2019-11-18 17:13:49 +01:00
|
|
|
|
removeLeadingTrailingWhitespace(tagname);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string quote_regex = "'[^']+'";
|
2019-12-16 19:42:59 +01:00
|
|
|
|
string non_quote_regex = R"([^,\s]+)";
|
2023-11-30 15:28:57 +01:00
|
|
|
|
regex r(R"((\s*)" + quote_regex + "|" + non_quote_regex + R"(\s*)(,\s*()" + quote_regex + "|"
|
|
|
|
|
+ non_quote_regex + R"()\s*)*)");
|
2019-12-20 16:59:30 +01:00
|
|
|
|
if (!regex_match(tags, r))
|
2019-11-18 17:13:49 +01:00
|
|
|
|
{
|
2021-12-14 17:24:14 +01:00
|
|
|
|
cerr << "ERROR: " << (exclude_eqs ? "exclude_eqs" : "include_eqs")
|
2019-11-18 17:13:49 +01:00
|
|
|
|
<< ": argument is of incorrect format." << endl;
|
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-14 17:21:31 +01:00
|
|
|
|
vector<map<string, string>> eq_tag_set;
|
2019-12-20 16:59:30 +01:00
|
|
|
|
regex s(quote_regex + "|" + non_quote_regex);
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (auto it = sregex_iterator(tags.begin(), tags.end(), s); it != sregex_iterator(); ++it)
|
2019-11-18 17:13:49 +01:00
|
|
|
|
{
|
2022-10-04 16:34:43 +02:00
|
|
|
|
string_view str {it->str()};
|
|
|
|
|
if (str.front() == '\'' && str.back() == '\'')
|
|
|
|
|
{
|
|
|
|
|
str.remove_prefix(1);
|
|
|
|
|
str.remove_suffix(1);
|
|
|
|
|
}
|
2023-11-30 15:28:57 +01:00
|
|
|
|
eq_tag_set.push_back({{tagname, string {str}}});
|
2019-11-18 17:13:49 +01:00
|
|
|
|
}
|
2021-12-14 17:24:14 +01:00
|
|
|
|
return eq_tag_set;
|
2019-11-18 17:13:49 +01:00
|
|
|
|
}
|
|
|
|
|
|
2021-12-14 17:24:14 +01:00
|
|
|
|
vector<int>
|
2023-11-30 15:28:57 +01:00
|
|
|
|
DynamicModel::removeEquationsHelper(set<map<string, string>>& listed_eqs_by_tag, bool exclude_eqs,
|
2021-12-14 17:24:14 +01:00
|
|
|
|
bool excluded_vars_change_type,
|
2023-11-30 15:28:57 +01:00
|
|
|
|
vector<BinaryOpNode*>& all_equations,
|
|
|
|
|
vector<optional<int>>& all_equations_lineno,
|
|
|
|
|
EquationTags& all_equation_tags, bool static_equations) const
|
2019-11-18 17:13:49 +01:00
|
|
|
|
{
|
2021-12-14 17:24:14 +01:00
|
|
|
|
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<int> listed_eqs_by_number;
|
|
|
|
|
for (auto it = listed_eqs_by_tag.begin(); it != listed_eqs_by_tag.end();)
|
2023-11-30 15:28:57 +01:00
|
|
|
|
if (auto tmp = all_equation_tags.getEqnsByTags(*it); !tmp.empty())
|
2021-12-14 17:24:14 +01:00
|
|
|
|
{
|
|
|
|
|
listed_eqs_by_number.insert(tmp.begin(), tmp.end());
|
|
|
|
|
it = listed_eqs_by_tag.erase(it);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
++it;
|
2019-11-18 17:13:49 +01:00
|
|
|
|
|
2021-12-14 17:24:14 +01:00
|
|
|
|
// Compute the indices of equations to be actually deleted
|
|
|
|
|
set<int> 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++)
|
2022-05-04 16:01:34 +02:00
|
|
|
|
if (!listed_eqs_by_number.contains(i))
|
2021-12-14 17:24:14 +01:00
|
|
|
|
eqs_to_delete_by_number.insert(i);
|
|
|
|
|
|
|
|
|
|
// remove from equations, equations_lineno, equation_tags
|
2023-11-30 15:28:57 +01:00
|
|
|
|
vector<BinaryOpNode*> new_equations;
|
2022-05-05 18:39:27 +02:00
|
|
|
|
vector<optional<int>> new_equations_lineno;
|
2021-12-14 17:24:14 +01:00
|
|
|
|
map<int, int> old_eqn_num_2_new;
|
|
|
|
|
vector<int> excluded_vars;
|
|
|
|
|
for (size_t i = 0; i < all_equations.size(); i++)
|
2022-05-04 16:01:34 +02:00
|
|
|
|
if (eqs_to_delete_by_number.contains(i))
|
2021-12-14 17:24:14 +01:00
|
|
|
|
{
|
|
|
|
|
if (excluded_vars_change_type)
|
2023-04-11 14:21:17 +02:00
|
|
|
|
{
|
|
|
|
|
if (auto tmp = all_equation_tags.getTagValueByEqnAndKey(i, "endogenous"); tmp)
|
|
|
|
|
excluded_vars.push_back(symbol_table.getID(*tmp));
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
set<int> result;
|
|
|
|
|
all_equations[i]->arg1->collectVariables(SymbolType::endogenous, result);
|
|
|
|
|
if (result.size() == 1)
|
|
|
|
|
excluded_vars.push_back(*result.begin());
|
|
|
|
|
else
|
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
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;
|
2023-04-11 14:21:17 +02:00
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-12-14 17:24:14 +01:00
|
|
|
|
}
|
|
|
|
|
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;
|
2019-11-18 17:13:49 +01:00
|
|
|
|
|
2021-12-14 17:24:14 +01:00
|
|
|
|
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++)
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (size_t j = i + 1; j < excluded_vars.size(); j++)
|
2021-12-14 17:24:14 +01:00
|
|
|
|
if (excluded_vars[i] == excluded_vars[j])
|
|
|
|
|
{
|
|
|
|
|
cerr << "ERROR: Variable " << symbol_table.getName(i) << " was excluded twice"
|
2023-11-30 15:28:57 +01:00
|
|
|
|
<< " via a model_remove or model_replace statement, or via the include_eqs or "
|
|
|
|
|
"exclude_eqs option"
|
|
|
|
|
<< endl;
|
2021-12-14 17:24:14 +01:00
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-30 15:28:57 +01:00
|
|
|
|
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;
|
2021-12-14 17:24:14 +01:00
|
|
|
|
|
|
|
|
|
return excluded_vars;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2023-11-30 15:28:57 +01:00
|
|
|
|
DynamicModel::removeEquations(const vector<map<string, string>>& listed_eqs_by_tag,
|
|
|
|
|
bool exclude_eqs, bool excluded_vars_change_type)
|
2021-12-14 17:24:14 +01:00
|
|
|
|
{
|
|
|
|
|
/* Convert the const vector to a (mutable) set */
|
2022-06-03 16:21:30 +02:00
|
|
|
|
set listed_eqs_by_tag2(listed_eqs_by_tag.begin(), listed_eqs_by_tag.end());
|
2021-12-14 17:24:14 +01:00
|
|
|
|
|
2023-11-30 15:28:57 +01:00
|
|
|
|
vector<int> excluded_vars
|
|
|
|
|
= removeEquationsHelper(listed_eqs_by_tag2, exclude_eqs, excluded_vars_change_type, equations,
|
|
|
|
|
equations_lineno, equation_tags, false);
|
2019-11-18 17:13:49 +01:00
|
|
|
|
|
|
|
|
|
// Ignore output because variables are not excluded when equations marked 'static' are excluded
|
2021-12-14 17:24:14 +01:00
|
|
|
|
removeEquationsHelper(listed_eqs_by_tag2, exclude_eqs, excluded_vars_change_type,
|
|
|
|
|
static_only_equations, static_only_equations_lineno,
|
|
|
|
|
static_only_equations_equation_tags, true);
|
2020-02-20 15:29:10 +01:00
|
|
|
|
|
2021-12-14 17:24:14 +01:00
|
|
|
|
if (!listed_eqs_by_tag2.empty())
|
2019-11-18 17:13:49 +01:00
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
cerr
|
|
|
|
|
<< "ERROR: model_remove/model_replace/exclude_eqs/include_eqs: The equations specified by"
|
|
|
|
|
<< endl;
|
|
|
|
|
for (const auto& m : listed_eqs_by_tag)
|
2023-11-14 17:21:31 +01:00
|
|
|
|
{
|
|
|
|
|
cerr << " ";
|
|
|
|
|
if (m.size() > 1)
|
|
|
|
|
cerr << "[ ";
|
|
|
|
|
bool first_printed {false};
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (const auto& [tagname, tagvalue] : m)
|
2023-11-14 17:21:31 +01:00
|
|
|
|
{
|
|
|
|
|
if (exchange(first_printed, true))
|
|
|
|
|
cerr << ", ";
|
|
|
|
|
cerr << tagname << "=" << tagvalue;
|
|
|
|
|
}
|
|
|
|
|
if (m.size() > 1)
|
|
|
|
|
cerr << " ]";
|
|
|
|
|
cerr << endl;
|
|
|
|
|
}
|
2021-12-14 17:24:14 +01:00
|
|
|
|
cerr << "were not found." << endl;
|
2019-11-18 17:13:49 +01:00
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-14 17:24:14 +01:00
|
|
|
|
if (excluded_vars_change_type)
|
2019-11-18 17:13:49 +01:00
|
|
|
|
{
|
2021-12-14 17:24:14 +01:00
|
|
|
|
// Collect list of used variables in updated list of equations
|
|
|
|
|
set<int> 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)
|
2022-05-04 16:01:34 +02:00
|
|
|
|
if (eqn_vars.contains(ev))
|
2021-12-14 17:24:14 +01:00
|
|
|
|
{
|
|
|
|
|
symbol_table.changeType(ev, SymbolType::exogenous);
|
2023-11-30 15:28:57 +01:00
|
|
|
|
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;
|
2021-12-14 17:24:14 +01:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
symbol_table.changeType(ev, SymbolType::excludedVariable);
|
2023-11-30 15:28:57 +01:00
|
|
|
|
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;
|
2021-12-14 17:24:14 +01:00
|
|
|
|
}
|
2019-11-18 17:13:49 +01:00
|
|
|
|
}
|
2021-12-14 17:24:14 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2023-11-30 15:28:57 +01:00
|
|
|
|
DynamicModel::includeExcludeEquations(const string& inc_exc_option_value, bool exclude_eqs)
|
2021-12-14 17:24:14 +01:00
|
|
|
|
{
|
|
|
|
|
if (inc_exc_option_value.empty())
|
|
|
|
|
return;
|
2019-11-18 17:13:49 +01:00
|
|
|
|
|
2021-12-14 17:24:14 +01:00
|
|
|
|
auto listed_eqs_by_tag = parseIncludeExcludeEquations(inc_exc_option_value, exclude_eqs);
|
2019-11-18 17:13:49 +01:00
|
|
|
|
|
2021-12-14 17:24:14 +01:00
|
|
|
|
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())
|
2019-11-18 17:13:49 +01:00
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
cerr << "ERROR: exclude_eqs/include_eqs: You must remove the same number of equations marked "
|
|
|
|
|
"`static` as equations marked `dynamic`."
|
|
|
|
|
<< endl;
|
2021-12-14 17:24:14 +01:00
|
|
|
|
exit(EXIT_FAILURE);
|
2019-11-18 17:13:49 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-20 11:35:14 +02:00
|
|
|
|
void
|
2023-11-30 15:28:57 +01:00
|
|
|
|
DynamicModel::writeBlockDriverOutput(ostream& output) const
|
2020-05-20 11:35:14 +02:00
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
output << "M_.block_structure.time_recursive = " << boolalpha
|
|
|
|
|
<< time_recursive_block_decomposition << ";" << endl;
|
2022-11-30 14:43:44 +01:00
|
|
|
|
|
2020-05-20 11:35:14 +02:00
|
|
|
|
for (int blk = 0; blk < static_cast<int>(blocks.size()); blk++)
|
|
|
|
|
{
|
|
|
|
|
int block_size = blocks[blk].size;
|
2023-11-30 15:28:57 +01:00
|
|
|
|
output << "M_.block_structure.block(" << blk + 1
|
|
|
|
|
<< ").Simulation_Type = " << static_cast<int>(blocks[blk].simulation_type) << ";"
|
|
|
|
|
<< 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 = [";
|
2020-05-20 11:35:14 +02:00
|
|
|
|
for (int eq = 0; eq < block_size; eq++)
|
2023-11-30 15:28:57 +01:00
|
|
|
|
output << " " << getBlockEquationID(blk, eq) + 1;
|
|
|
|
|
output << "];" << endl << "M_.block_structure.block(" << blk + 1 << ").variable = [";
|
2020-05-20 11:35:14 +02:00
|
|
|
|
for (int var = 0; var < block_size; var++)
|
2023-11-30 15:28:57 +01:00
|
|
|
|
output << " " << getBlockVariableID(blk, var) + 1;
|
2020-05-20 11:35:14 +02:00
|
|
|
|
output << "];" << endl
|
2023-11-30 15:28:57 +01:00
|
|
|
|
<< "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
|
|
|
|
|
<< "M_.block_structure.block(" << blk + 1 << ").bytecode_jacob_cols_to_sparse = [";
|
|
|
|
|
const bool one_boundary {
|
|
|
|
|
blocks[blk].simulation_type == BlockSimulationType::solveBackwardSimple
|
|
|
|
|
|| blocks[blk].simulation_type == BlockSimulationType::solveForwardSimple
|
|
|
|
|
|| blocks[blk].simulation_type == BlockSimulationType::solveBackwardComplete
|
|
|
|
|
|| blocks[blk].simulation_type == BlockSimulationType::solveForwardComplete};
|
2023-11-03 12:51:14 +01:00
|
|
|
|
/* bytecode_jacob_cols_to_sparse is an array that maps column indices in
|
|
|
|
|
the Jacobian returned by bytecode MEX in evaluate mode (which only
|
|
|
|
|
contains nonzero columns, but also incorporate recursive variables),
|
|
|
|
|
into column indices in the sparse representiation (which has 3×n
|
|
|
|
|
columns for two-boundaries blocks and n columns for one-boundary
|
|
|
|
|
blocks). Columns unused in the sparse representation are indicated by
|
|
|
|
|
a zero. */
|
|
|
|
|
vector<int> bytecode_jacob_cols_to_sparse(blocks_jacob_cols_endo[blk].size());
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (auto& [key, index] : blocks_jacob_cols_endo[blk])
|
2023-11-03 12:51:14 +01:00
|
|
|
|
{
|
|
|
|
|
auto [var, lag] {key};
|
2023-11-30 15:28:57 +01:00
|
|
|
|
if (var >= blocks[blk].getRecursiveSize() // NB: this check can be removed once Jacobian
|
|
|
|
|
// no longer contains columns for derivatives
|
|
|
|
|
// w.r.t. recursive variables
|
2023-11-03 12:51:14 +01:00
|
|
|
|
&& !(one_boundary && lag != 0))
|
2023-11-30 15:28:57 +01:00
|
|
|
|
bytecode_jacob_cols_to_sparse[index]
|
|
|
|
|
= static_cast<int>(!one_boundary) * (lag + 1) * blocks[blk].mfs_size + var
|
|
|
|
|
- blocks[blk].getRecursiveSize();
|
2023-11-03 12:51:14 +01:00
|
|
|
|
else
|
|
|
|
|
bytecode_jacob_cols_to_sparse[index] = -1;
|
|
|
|
|
}
|
|
|
|
|
for (int i : bytecode_jacob_cols_to_sparse)
|
2023-11-30 15:28:57 +01:00
|
|
|
|
output << i + 1 << " ";
|
2023-11-03 12:51:14 +01:00
|
|
|
|
output << "];" << endl;
|
2020-05-20 11:35:14 +02:00
|
|
|
|
}
|
|
|
|
|
|
2022-09-28 19:18:15 +02:00
|
|
|
|
writeBlockDriverSparseIndicesHelper<true>(output);
|
|
|
|
|
|
2021-04-19 14:51:34 +02:00
|
|
|
|
output << "M_.block_structure.variable_reordered = [";
|
2020-05-20 11:35:14 +02:00
|
|
|
|
for (int i = 0; i < symbol_table.endo_nbr(); i++)
|
2023-11-30 15:28:57 +01:00
|
|
|
|
output << " " << endo_idx_block2orig[i] + 1;
|
|
|
|
|
output << "];" << endl << "M_.block_structure.equation_reordered = [";
|
2020-05-20 11:35:14 +02:00
|
|
|
|
for (int i = 0; i < symbol_table.endo_nbr(); i++)
|
2023-11-30 15:28:57 +01:00
|
|
|
|
output << " " << eq_idx_block2orig[i] + 1;
|
2020-05-20 11:35:14 +02:00
|
|
|
|
output << "];" << endl;
|
|
|
|
|
|
|
|
|
|
map<int, set<pair<int, int>>> lag_row_incidence;
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (const auto& [indices, d1] : derivatives[1])
|
|
|
|
|
if (int deriv_id = indices[1]; getTypeByDerivID(deriv_id) == SymbolType::endogenous)
|
2020-05-20 11:35:14 +02:00
|
|
|
|
{
|
|
|
|
|
int eq = indices[0];
|
2023-11-30 15:28:57 +01:00
|
|
|
|
int var {getTypeSpecificIDByDerivID(deriv_id)};
|
2020-05-20 11:35:14 +02:00
|
|
|
|
int lag = getLagByDerivID(deriv_id);
|
2023-11-30 15:28:57 +01:00
|
|
|
|
lag_row_incidence[lag].insert({eq, var});
|
2020-05-20 11:35:14 +02:00
|
|
|
|
}
|
|
|
|
|
for (auto [lag, eq_var_set] : lag_row_incidence)
|
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
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;
|
2020-05-20 11:35:14 +02:00
|
|
|
|
for (auto [eq, var] : eq_var_set)
|
2023-11-30 15:28:57 +01:00
|
|
|
|
output << " " << eq + 1 << " " << var + 1 << ";" << endl;
|
2020-05-20 11:35:14 +02:00
|
|
|
|
output << "];" << endl;
|
|
|
|
|
}
|
2023-11-30 15:28:57 +01:00
|
|
|
|
output << "M_.block_structure.dyn_tmp_nbr = " << blocks_temporary_terms_idxs.size() << ';'
|
|
|
|
|
<< endl;
|
2020-05-20 11:35:14 +02:00
|
|
|
|
}
|
|
|
|
|
|
2009-04-14 16:39:53 +02:00
|
|
|
|
void
|
2023-11-30 15:28:57 +01:00
|
|
|
|
DynamicModel::writeDriverOutput(ostream& output, bool compute_xrefs) const
|
2009-12-16 18:13:23 +01:00
|
|
|
|
{
|
|
|
|
|
/* 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.
|
|
|
|
|
*/
|
|
|
|
|
|
2021-04-19 14:51:34 +02:00
|
|
|
|
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
|
2023-11-30 15:28:57 +01:00
|
|
|
|
<< "M_.orig_maximum_lag_with_diffs_expanded = " << max_lag_with_diffs_expanded_orig << ";"
|
|
|
|
|
<< endl
|
2021-04-19 14:51:34 +02:00
|
|
|
|
<< "M_.lead_lag_incidence = [";
|
2009-12-16 18:13:23 +01:00
|
|
|
|
// Loop on endogenous variables
|
2023-11-30 15:28:57 +01:00
|
|
|
|
int nstatic = 0, nfwrd = 0, npred = 0, nboth = 0;
|
2009-12-16 18:13:23 +01:00
|
|
|
|
for (int endoID = 0; endoID < symbol_table.endo_nbr(); endoID++)
|
|
|
|
|
{
|
|
|
|
|
output << endl;
|
2023-11-30 15:28:57 +01:00
|
|
|
|
int sstatic = 1, sfwrd = 0, spred = 0, sboth = 0;
|
2009-12-16 18:13:23 +01:00
|
|
|
|
// 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
|
|
|
|
|
{
|
2018-07-17 18:34:07 +02:00
|
|
|
|
int varID = getDerivID(symbol_table.getID(SymbolType::endogenous, endoID), lag);
|
2022-09-14 17:07:08 +02:00
|
|
|
|
output << " " << getJacobianCol(varID, false) + 1;
|
2011-06-18 17:53:50 +02:00
|
|
|
|
if (lag == -1)
|
|
|
|
|
{
|
|
|
|
|
sstatic = 0;
|
|
|
|
|
spred = 1;
|
|
|
|
|
}
|
|
|
|
|
else if (lag == 1)
|
|
|
|
|
{
|
|
|
|
|
if (spred == 1)
|
|
|
|
|
{
|
|
|
|
|
sboth = 1;
|
|
|
|
|
spred = 0;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
sstatic = 0;
|
|
|
|
|
sfwrd = 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
2009-12-16 18:13:23 +01:00
|
|
|
|
}
|
2023-11-30 15:28:57 +01:00
|
|
|
|
catch (UnknownDerivIDException& e)
|
2009-12-16 18:13:23 +01:00
|
|
|
|
{
|
|
|
|
|
output << " 0";
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-06-18 17:53:50 +02:00
|
|
|
|
nstatic += sstatic;
|
2019-12-20 16:59:30 +01:00
|
|
|
|
nfwrd += sfwrd;
|
|
|
|
|
npred += spred;
|
|
|
|
|
nboth += sboth;
|
2009-12-16 18:13:23 +01:00
|
|
|
|
output << ";";
|
|
|
|
|
}
|
2022-06-02 10:50:21 +02:00
|
|
|
|
output << "]';" << endl
|
|
|
|
|
<< "M_.nstatic = " << nstatic << ";" << endl
|
2023-11-30 15:28:57 +01:00
|
|
|
|
<< "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
|
2022-06-02 10:50:21 +02:00
|
|
|
|
<< "M_.dynamic_tmp_nbr = [";
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (const auto& tts : temporary_terms_derivatives)
|
2022-09-26 14:53:36 +02:00
|
|
|
|
output << tts.size() << "; ";
|
2021-04-19 14:51:34 +02:00
|
|
|
|
output << "];" << endl;
|
2020-06-05 17:11:29 +02:00
|
|
|
|
|
2009-12-16 18:13:23 +01:00
|
|
|
|
// Write equation tags
|
2021-04-19 14:51:34 +02:00
|
|
|
|
equation_tags.writeOutput(output);
|
2009-12-16 18:13:23 +01:00
|
|
|
|
|
2019-10-16 17:34:27 +02:00
|
|
|
|
// Write mapping for variables and equations they are present in
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (const auto& variable : variableMapping)
|
2019-10-16 17:34:27 +02:00
|
|
|
|
{
|
2021-04-19 14:51:34 +02:00
|
|
|
|
output << "M_.mapping." << symbol_table.getName(variable.first) << ".eqidx = [";
|
|
|
|
|
for (auto equation : variable.second)
|
|
|
|
|
output << equation + 1 << " ";
|
|
|
|
|
output << "];" << endl;
|
2019-10-16 17:34:27 +02:00
|
|
|
|
}
|
|
|
|
|
|
2013-04-11 17:07:39 +02:00
|
|
|
|
/* Say if static and dynamic models differ (because of [static] and [dynamic]
|
|
|
|
|
equation tags) */
|
2023-11-30 15:28:57 +01:00
|
|
|
|
output << "M_.static_and_dynamic_models_differ = " << boolalpha
|
|
|
|
|
<< (static_only_equations.size() > 0) << ";" << endl;
|
2013-04-11 17:07:39 +02:00
|
|
|
|
|
2019-05-15 15:02:36 +02:00
|
|
|
|
// Say if model contains an external function call
|
|
|
|
|
bool has_external_function = false;
|
2019-12-16 19:42:59 +01:00
|
|
|
|
for (auto equation : equations)
|
|
|
|
|
if (equation->containsExternalFunction())
|
2019-05-15 15:02:36 +02:00
|
|
|
|
{
|
|
|
|
|
has_external_function = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2023-11-30 15:28:57 +01:00
|
|
|
|
output << "M_.has_external_function = " << boolalpha << has_external_function << ';' << endl;
|
2019-05-15 15:02:36 +02:00
|
|
|
|
|
2020-05-20 11:35:14 +02:00
|
|
|
|
// Compute list of state variables, ordered in block-order
|
2016-07-20 23:09:46 +02:00
|
|
|
|
vector<int> state_var;
|
|
|
|
|
for (int endoID = 0; endoID < symbol_table.endo_nbr(); endoID++)
|
2020-05-20 11:35:14 +02:00
|
|
|
|
// Loop on negative lags
|
2016-07-20 23:09:46 +02:00
|
|
|
|
for (int lag = -max_endo_lag; lag < 0; lag++)
|
|
|
|
|
try
|
|
|
|
|
{
|
2020-04-17 14:55:55 +02:00
|
|
|
|
getDerivID(symbol_table.getID(SymbolType::endogenous, endo_idx_block2orig[endoID]), lag);
|
2023-11-30 15:28:57 +01:00
|
|
|
|
if (find(state_var.begin(), state_var.end(), endo_idx_block2orig[endoID])
|
|
|
|
|
== state_var.end())
|
2020-05-20 11:35:14 +02:00
|
|
|
|
state_var.push_back(endo_idx_block2orig[endoID]);
|
2016-07-20 23:09:46 +02:00
|
|
|
|
}
|
2023-11-30 15:28:57 +01:00
|
|
|
|
catch (UnknownDerivIDException& e)
|
2016-07-20 23:09:46 +02:00
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-20 11:35:14 +02:00
|
|
|
|
// Write the block structure of the model
|
2022-09-28 16:54:03 +02:00
|
|
|
|
if (block_decomposed)
|
2023-01-13 12:05:04 +01:00
|
|
|
|
writeBlockDriverOutput(output);
|
2012-09-28 18:41:18 +02:00
|
|
|
|
|
2021-04-19 14:51:34 +02:00
|
|
|
|
output << "M_.state_var = [";
|
2018-11-23 17:19:59 +01:00
|
|
|
|
for (int it : state_var)
|
2023-11-30 15:28:57 +01:00
|
|
|
|
output << it + 1 << " ";
|
2016-07-20 23:09:46 +02:00
|
|
|
|
output << "];" << endl;
|
|
|
|
|
|
2009-12-16 18:13:23 +01:00
|
|
|
|
// Writing initialization for some other variables
|
2021-04-19 14:51:34 +02:00
|
|
|
|
output << "M_.maximum_lag = " << max_lag << ";" << endl
|
|
|
|
|
<< "M_.maximum_lead = " << max_lead << ";" << endl;
|
2013-11-04 16:02:28 +01:00
|
|
|
|
|
2021-04-19 14:51:34 +02:00
|
|
|
|
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;
|
2013-11-04 16:02:28 +01:00
|
|
|
|
|
2021-04-19 14:51:34 +02:00
|
|
|
|
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;
|
2013-11-04 16:02:28 +01:00
|
|
|
|
|
2009-12-16 18:13:23 +01:00
|
|
|
|
if (symbol_table.exo_det_nbr())
|
|
|
|
|
{
|
2021-04-19 14:51:34 +02:00
|
|
|
|
output << "M_.maximum_exo_det_lag = " << max_exo_det_lag << ";" << endl
|
|
|
|
|
<< "M_.maximum_exo_det_lead = " << max_exo_det_lead << ";" << endl
|
2023-11-30 15:28:57 +01:00
|
|
|
|
<< "oo_.exo_det_steady_state = zeros(" << symbol_table.exo_det_nbr() << ", 1);"
|
|
|
|
|
<< endl;
|
2009-12-16 18:13:23 +01:00
|
|
|
|
}
|
2013-11-04 16:02:28 +01:00
|
|
|
|
|
2023-11-30 15:28:57 +01:00
|
|
|
|
output << "M_.params = "
|
|
|
|
|
<< "NaN(" << symbol_table.param_nbr() << ", 1);" << endl;
|
2009-12-16 18:13:23 +01:00
|
|
|
|
|
2021-04-19 14:51:34 +02:00
|
|
|
|
string empty_cell = "cell(" + to_string(symbol_table.endo_nbr()) + ", 1)";
|
2023-11-30 15:28:57 +01:00
|
|
|
|
output << "M_.endo_trends = struct('deflator', " << empty_cell << ", 'log_deflator', "
|
|
|
|
|
<< empty_cell << ", 'growth_factor', " << empty_cell << ", 'log_growth_factor', "
|
|
|
|
|
<< empty_cell << ");" << endl;
|
2021-04-19 14:51:34 +02:00
|
|
|
|
for (int i = 0; i < symbol_table.endo_nbr(); i++)
|
2019-12-13 17:27:50 +01:00
|
|
|
|
{
|
2021-04-19 14:51:34 +02:00
|
|
|
|
int symb_id = symbol_table.getID(SymbolType::endogenous, i);
|
|
|
|
|
if (auto it = nonstationary_symbols_map.find(symb_id); it != nonstationary_symbols_map.end())
|
2019-12-13 17:27:50 +01:00
|
|
|
|
{
|
2021-04-19 14:51:34 +02:00
|
|
|
|
auto [is_log, deflator] = it->second;
|
2023-11-30 15:28:57 +01:00
|
|
|
|
output << "M_.endo_trends(" << i + 1 << ")." << (is_log ? "log_deflator" : "deflator")
|
|
|
|
|
<< " = '";
|
2021-04-19 14:51:34 +02:00
|
|
|
|
deflator->writeJsonOutput(output, {}, {});
|
|
|
|
|
output << "';" << endl;
|
|
|
|
|
|
2023-11-30 15:28:57 +01:00
|
|
|
|
auto growth_factor = const_cast<DynamicModel*>(this)
|
|
|
|
|
->AddDivide(deflator, deflator->decreaseLeadsLags(1))
|
|
|
|
|
->removeTrendLeadLag(trend_symbols_map)
|
|
|
|
|
->replaceTrendVar();
|
|
|
|
|
output << "M_.endo_trends(" << i + 1 << ")."
|
2021-04-19 14:51:34 +02:00
|
|
|
|
<< (is_log ? "log_growth_factor" : "growth_factor") << " = '";
|
|
|
|
|
growth_factor->writeJsonOutput(output, {}, {});
|
|
|
|
|
output << "';" << endl;
|
2019-12-13 17:27:50 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-23 13:55:02 +01:00
|
|
|
|
if (compute_xrefs)
|
|
|
|
|
writeXrefs(output);
|
2015-12-18 15:17:32 +01:00
|
|
|
|
|
2009-12-16 18:13:23 +01:00
|
|
|
|
// Write number of non-zero derivatives
|
2010-07-17 10:14:22 +02:00
|
|
|
|
// Use -1 if the derivatives have not been computed
|
2021-04-19 14:51:34 +02:00
|
|
|
|
output << "M_.NNZDerivatives = [";
|
2019-04-12 15:41:52 +02:00
|
|
|
|
for (int i = 1; i < static_cast<int>(NNZDerivatives.size()); i++)
|
2020-01-20 17:22:32 +01:00
|
|
|
|
output << (i > computed_derivs_order ? -1 : NNZDerivatives[i]) << "; ";
|
2015-07-29 14:52:15 +02:00
|
|
|
|
output << "];" << endl;
|
2022-09-14 17:07:08 +02:00
|
|
|
|
|
|
|
|
|
writeDriverSparseIndicesHelper<true>(output);
|
2022-11-30 14:43:44 +01:00
|
|
|
|
|
|
|
|
|
// 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;
|
2009-12-16 18:13:23 +01:00
|
|
|
|
}
|
2009-05-13 01:03:40 +02:00
|
|
|
|
|
2010-10-15 19:05:16 +02:00
|
|
|
|
void
|
2023-11-30 15:28:57 +01:00
|
|
|
|
DynamicModel::runTrendTest(const eval_context_t& eval_context)
|
2010-10-15 19:05:16 +02:00
|
|
|
|
{
|
|
|
|
|
computeDerivIDs();
|
|
|
|
|
testTrendDerivativesEqualToZero(eval_context);
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-23 13:56:58 +02:00
|
|
|
|
void
|
2018-09-03 15:05:30 +02:00
|
|
|
|
DynamicModel::updateVarAndTrendModel() const
|
2018-08-23 13:56:58 +02:00
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (bool var : {true, false})
|
2018-08-23 13:56:58 +02:00
|
|
|
|
{
|
2022-05-05 18:39:04 +02:00
|
|
|
|
map<string, vector<optional<int>>> trend_varr;
|
2018-08-23 13:56:58 +02:00
|
|
|
|
map<string, vector<set<pair<int, int>>>> rhsr;
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (const auto& [model_name, eqns] :
|
|
|
|
|
(var ? var_model_table.getEqNums() : trend_component_model_table.getEqNums()))
|
2018-08-23 13:56:58 +02:00
|
|
|
|
{
|
2022-05-05 18:39:04 +02:00
|
|
|
|
vector<int> lhs, trend_lhs;
|
|
|
|
|
vector<optional<int>> trend_var;
|
2018-08-23 13:56:58 +02:00
|
|
|
|
vector<set<pair<int, int>>> rhs;
|
2018-09-03 15:05:30 +02:00
|
|
|
|
|
2021-07-16 12:26:57 +02:00
|
|
|
|
if (!var)
|
2018-09-03 15:05:30 +02:00
|
|
|
|
{
|
2021-07-16 12:26:57 +02:00
|
|
|
|
lhs = trend_component_model_table.getLhs(model_name);
|
|
|
|
|
for (auto teqn : trend_component_model_table.getTargetEqNums().at(model_name))
|
2018-09-03 15:05:30 +02:00
|
|
|
|
{
|
|
|
|
|
int eqnidx = 0;
|
2021-07-16 12:26:57 +02:00
|
|
|
|
for (auto eqn : eqns)
|
2018-09-03 15:05:30 +02:00
|
|
|
|
{
|
|
|
|
|
if (eqn == teqn)
|
|
|
|
|
trend_lhs.push_back(lhs[eqnidx]);
|
|
|
|
|
eqnidx++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int lhs_idx = 0;
|
2021-07-16 12:26:57 +02:00
|
|
|
|
for (auto eqn : eqns)
|
2018-08-23 13:56:58 +02:00
|
|
|
|
{
|
|
|
|
|
set<pair<int, int>> rhs_set;
|
2018-11-28 14:32:26 +01:00
|
|
|
|
equations[eqn]->arg2->collectDynamicVariables(SymbolType::endogenous, rhs_set);
|
2018-08-23 13:56:58 +02:00
|
|
|
|
rhs.push_back(rhs_set);
|
2018-09-03 15:05:30 +02:00
|
|
|
|
|
2021-07-16 12:26:57 +02:00
|
|
|
|
if (!var)
|
2018-08-31 12:35:51 +02:00
|
|
|
|
{
|
|
|
|
|
int lhs_symb_id = lhs[lhs_idx++];
|
2022-01-28 16:28:14 +01:00
|
|
|
|
if (symbol_table.isDiffAuxiliaryVariable(lhs_symb_id))
|
2018-08-31 12:35:51 +02:00
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
lhs_symb_id = symbol_table.getOrigSymbIdForAuxVar(lhs_symb_id);
|
|
|
|
|
}
|
|
|
|
|
catch (...)
|
|
|
|
|
{
|
|
|
|
|
}
|
2023-11-30 15:28:57 +01:00
|
|
|
|
optional<int> trend_var_symb_id
|
|
|
|
|
= equations[eqn]->arg2->findTargetVariable(lhs_symb_id);
|
2022-05-05 18:39:04 +02:00
|
|
|
|
if (trend_var_symb_id)
|
2018-09-03 15:05:30 +02:00
|
|
|
|
{
|
2022-05-05 18:39:04 +02:00
|
|
|
|
if (symbol_table.isDiffAuxiliaryVariable(*trend_var_symb_id))
|
2018-09-03 15:05:30 +02:00
|
|
|
|
try
|
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
trend_var_symb_id
|
|
|
|
|
= symbol_table.getOrigSymbIdForAuxVar(*trend_var_symb_id);
|
2018-09-03 15:05:30 +02:00
|
|
|
|
}
|
|
|
|
|
catch (...)
|
|
|
|
|
{
|
|
|
|
|
}
|
2023-11-30 15:28:57 +01:00
|
|
|
|
if (find(trend_lhs.begin(), trend_lhs.end(), *trend_var_symb_id)
|
|
|
|
|
== trend_lhs.end())
|
2018-09-03 15:05:30 +02:00
|
|
|
|
{
|
|
|
|
|
cerr << "ERROR: trend found in trend_component equation #" << eqn << " ("
|
2023-11-30 15:28:57 +01:00
|
|
|
|
<< symbol_table.getName(*trend_var_symb_id)
|
|
|
|
|
<< ") does not correspond to a trend equation" << endl;
|
2018-09-03 15:05:30 +02:00
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-12-13 15:37:07 +01:00
|
|
|
|
trend_var.push_back(trend_var_symb_id);
|
2018-08-31 12:35:51 +02:00
|
|
|
|
}
|
2018-08-23 13:56:58 +02:00
|
|
|
|
}
|
2018-09-03 15:05:30 +02:00
|
|
|
|
|
2021-07-16 12:26:57 +02:00
|
|
|
|
rhsr[model_name] = rhs;
|
|
|
|
|
if (!var)
|
|
|
|
|
trend_varr[model_name] = trend_var;
|
2018-08-23 13:56:58 +02:00
|
|
|
|
}
|
|
|
|
|
|
2021-07-16 12:26:57 +02:00
|
|
|
|
if (var)
|
2022-06-13 14:15:47 +02:00
|
|
|
|
var_model_table.setRhs(move(rhsr));
|
2021-07-16 12:26:57 +02:00
|
|
|
|
else
|
2018-08-31 12:35:51 +02:00
|
|
|
|
{
|
2022-06-13 14:15:47 +02:00
|
|
|
|
trend_component_model_table.setRhs(move(rhsr));
|
|
|
|
|
trend_component_model_table.setTargetVar(move(trend_varr));
|
2018-08-31 12:35:51 +02:00
|
|
|
|
}
|
2018-08-23 13:56:58 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-21 11:46:59 +02:00
|
|
|
|
void
|
|
|
|
|
DynamicModel::fillVarModelTable() const
|
|
|
|
|
{
|
|
|
|
|
map<string, vector<int>> eqnums, lhsr;
|
|
|
|
|
map<string, vector<expr_t>> lhs_expr_tr;
|
|
|
|
|
map<string, vector<set<pair<int, int>>>> rhsr;
|
|
|
|
|
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (const auto& [model_name, eqtags] : var_model_table.getEqTags())
|
2018-08-21 11:46:59 +02:00
|
|
|
|
{
|
|
|
|
|
vector<int> eqnumber, lhs;
|
|
|
|
|
vector<expr_t> lhs_expr_t;
|
|
|
|
|
vector<set<pair<int, int>>> rhs;
|
|
|
|
|
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (const auto& eqtag : eqtags)
|
2018-08-21 11:46:59 +02:00
|
|
|
|
{
|
|
|
|
|
set<pair<int, int>> lhs_set, lhs_tmp_set, rhs_set;
|
2023-11-30 15:28:57 +01:00
|
|
|
|
optional<int> eqn {equation_tags.getEqnByTag("name", eqtag)};
|
2023-01-04 16:44:52 +01:00
|
|
|
|
if (!eqn)
|
2018-08-21 11:46:59 +02:00
|
|
|
|
{
|
2021-07-01 16:16:04 +02:00
|
|
|
|
cerr << "ERROR: no equation is named '" << eqtag << "'" << endl;
|
2018-08-21 11:46:59 +02:00
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-04 16:44:52 +01:00
|
|
|
|
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);
|
2018-08-21 11:46:59 +02:00
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-23 13:03:52 +02:00
|
|
|
|
auto itlhs = lhs_set.begin();
|
|
|
|
|
if (itlhs->second != 0)
|
2018-08-21 11:46:59 +02:00
|
|
|
|
{
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-04 16:44:52 +01:00
|
|
|
|
eqnumber.push_back(*eqn);
|
2018-08-23 13:03:52 +02:00
|
|
|
|
lhs.push_back(itlhs->first);
|
2018-08-21 11:46:59 +02:00
|
|
|
|
lhs_set.clear();
|
|
|
|
|
set<expr_t> lhs_expr_t_set;
|
2023-01-04 16:44:52 +01:00
|
|
|
|
equations[*eqn]->arg1->collectVARLHSVariable(lhs_expr_t_set);
|
2018-08-21 11:46:59 +02:00
|
|
|
|
lhs_expr_t.push_back(*(lhs_expr_t_set.begin()));
|
|
|
|
|
|
2023-01-04 16:44:52 +01:00
|
|
|
|
equations[*eqn]->arg2->collectDynamicVariables(SymbolType::endogenous, rhs_set);
|
2018-08-21 11:46:59 +02:00
|
|
|
|
rhs.push_back(rhs_set);
|
|
|
|
|
}
|
2021-07-06 19:04:59 +02:00
|
|
|
|
eqnums[model_name] = eqnumber;
|
|
|
|
|
lhsr[model_name] = lhs;
|
|
|
|
|
lhs_expr_tr[model_name] = lhs_expr_t;
|
|
|
|
|
rhsr[model_name] = rhs;
|
2018-08-21 11:46:59 +02:00
|
|
|
|
}
|
2022-06-13 14:15:47 +02:00
|
|
|
|
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));
|
2018-08-21 11:46:59 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2019-10-22 16:04:24 +02:00
|
|
|
|
DynamicModel::fillVarModelTableFromOrigModel() const
|
2018-08-21 11:46:59 +02:00
|
|
|
|
{
|
2022-05-16 15:56:54 +02:00
|
|
|
|
map<string, vector<int>> lags;
|
|
|
|
|
map<string, vector<optional<int>>> orig_diff_var;
|
2018-08-21 11:46:59 +02:00
|
|
|
|
map<string, vector<bool>> diff;
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (const auto& [model_name, eqns] : var_model_table.getEqNums())
|
2018-08-21 11:46:59 +02:00
|
|
|
|
{
|
|
|
|
|
set<expr_t> lhs;
|
2022-05-16 15:56:54 +02:00
|
|
|
|
vector<optional<int>> orig_diff_var_vec;
|
2018-08-21 11:46:59 +02:00
|
|
|
|
vector<bool> diff_vec;
|
2021-07-06 19:04:59 +02:00
|
|
|
|
for (auto eqn : eqns)
|
2018-08-21 11:46:59 +02:00
|
|
|
|
{
|
2021-07-06 18:42:19 +02:00
|
|
|
|
// Perform some sanity checks on the RHS
|
2023-11-30 15:28:57 +01:00
|
|
|
|
optional<string> eqtag {equation_tags.getTagValueByEqnAndKey(eqn, "name")};
|
2021-07-06 18:42:19 +02:00
|
|
|
|
set<pair<int, int>> rhs_endo_set, rhs_exo_set;
|
|
|
|
|
equations[eqn]->arg2->collectDynamicVariables(SymbolType::endogenous, rhs_endo_set);
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (const auto& [symb_id, lag] : rhs_endo_set)
|
2021-07-07 10:54:04 +02:00
|
|
|
|
if (lag > 0)
|
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
cerr << "ERROR: in Equation " << eqtag.value_or(to_string(eqn + 1))
|
|
|
|
|
<< ". A VAR model may not have leaded endogenous variables on the RHS. "
|
|
|
|
|
<< endl;
|
2021-07-07 10:54:04 +02:00
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
|
}
|
|
|
|
|
else if (!var_model_table.getStructural().at(model_name) && lag == 0)
|
2021-07-06 18:42:19 +02:00
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
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;
|
2021-07-06 18:42:19 +02:00
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
|
}
|
2021-07-07 10:54:04 +02:00
|
|
|
|
|
2021-07-06 18:42:19 +02:00
|
|
|
|
equations[eqn]->arg2->collectDynamicVariables(SymbolType::exogenous, rhs_exo_set);
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (const auto& [symb_id, lag] : rhs_exo_set)
|
2021-07-06 18:42:19 +02:00
|
|
|
|
if (lag != 0)
|
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
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;
|
2021-07-06 18:42:19 +02:00
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
|
}
|
2018-08-21 11:46:59 +02:00
|
|
|
|
|
|
|
|
|
// save lhs variables
|
2018-11-28 14:32:26 +01:00
|
|
|
|
equations[eqn]->arg1->collectVARLHSVariable(lhs);
|
2018-08-21 11:46:59 +02:00
|
|
|
|
|
2023-11-30 15:28:57 +01:00
|
|
|
|
equations[eqn]->arg1->countDiffs() > 0 ? diff_vec.push_back(true)
|
|
|
|
|
: diff_vec.push_back(false);
|
2018-08-21 11:46:59 +02:00
|
|
|
|
if (diff_vec.back())
|
|
|
|
|
{
|
|
|
|
|
set<pair<int, int>> diff_set;
|
2018-11-28 14:32:26 +01:00
|
|
|
|
equations[eqn]->arg1->collectDynamicVariables(SymbolType::endogenous, diff_set);
|
2018-08-21 11:46:59 +02:00
|
|
|
|
|
|
|
|
|
if (diff_set.size() != 1)
|
|
|
|
|
{
|
|
|
|
|
cerr << "ERROR: problem getting variable for LHS diff operator in equation "
|
|
|
|
|
<< eqn << endl;
|
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
|
}
|
2023-12-01 14:29:23 +01:00
|
|
|
|
orig_diff_var_vec.emplace_back(diff_set.begin()->first);
|
2018-08-21 11:46:59 +02:00
|
|
|
|
}
|
|
|
|
|
else
|
2023-12-01 14:29:23 +01:00
|
|
|
|
orig_diff_var_vec.emplace_back(nullopt);
|
2018-08-21 11:46:59 +02:00
|
|
|
|
}
|
|
|
|
|
|
2021-07-06 19:04:59 +02:00
|
|
|
|
if (eqns.size() != lhs.size())
|
2018-08-21 11:46:59 +02:00
|
|
|
|
{
|
|
|
|
|
cerr << "ERROR: The LHS variables of the VAR model are not unique" << endl;
|
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-22 16:04:24 +02:00
|
|
|
|
set<expr_t> lhs_lag_equiv;
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (const auto& lh : lhs)
|
2019-10-22 16:04:24 +02:00
|
|
|
|
{
|
|
|
|
|
auto [lag_equiv_repr, index] = lh->getLagEquivalenceClass();
|
|
|
|
|
lhs_lag_equiv.insert(lag_equiv_repr);
|
|
|
|
|
}
|
2018-08-21 11:46:59 +02:00
|
|
|
|
|
|
|
|
|
vector<int> max_lag;
|
2021-07-06 19:04:59 +02:00
|
|
|
|
for (auto eqn : eqns)
|
2019-10-22 16:04:24 +02:00
|
|
|
|
max_lag.push_back(equations[eqn]->arg2->VarMaxLag(lhs_lag_equiv));
|
2021-07-06 19:04:59 +02:00
|
|
|
|
lags[model_name] = max_lag;
|
|
|
|
|
diff[model_name] = diff_vec;
|
|
|
|
|
orig_diff_var[model_name] = orig_diff_var_vec;
|
2018-08-21 11:46:59 +02:00
|
|
|
|
}
|
2022-06-13 14:15:47 +02:00
|
|
|
|
var_model_table.setDiff(move(diff));
|
|
|
|
|
var_model_table.setMaxLags(move(lags));
|
|
|
|
|
var_model_table.setOrigDiffVar(move(orig_diff_var));
|
2018-09-06 17:53:07 +02:00
|
|
|
|
}
|
|
|
|
|
|
2021-07-07 10:54:04 +02:00
|
|
|
|
vector<int>
|
|
|
|
|
DynamicModel::getVARDerivIDs(int lhs_symb_id, int lead_lag) const
|
|
|
|
|
{
|
|
|
|
|
vector<int> deriv_ids;
|
|
|
|
|
|
|
|
|
|
// First directly look for the variable itself
|
2023-11-30 15:28:57 +01:00
|
|
|
|
if (auto it = deriv_id_table.find({lhs_symb_id, lead_lag}); it != deriv_id_table.end())
|
2021-07-07 10:54:04 +02:00
|
|
|
|
deriv_ids.push_back(it->second);
|
|
|
|
|
|
|
|
|
|
// Then go through auxiliary variables
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (auto& [key, deriv_id2] : deriv_id_table)
|
2021-07-07 10:54:04 +02:00
|
|
|
|
{
|
|
|
|
|
auto [symb_id2, lead_lag2] = key;
|
2023-11-30 15:28:57 +01:00
|
|
|
|
const AuxVarInfo* avi;
|
2021-07-07 10:54:04 +02:00
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
avi = &symbol_table.getAuxVarInfo(symb_id2);
|
|
|
|
|
}
|
|
|
|
|
catch (SymbolTable::UnknownSymbolIDException)
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-20 14:32:57 +02:00
|
|
|
|
if (avi->type == AuxVarType::endoLag && avi->orig_symb_id.value() == lhs_symb_id
|
|
|
|
|
&& avi->orig_lead_lag.value() + lead_lag2 == lead_lag)
|
2021-07-07 10:54:04 +02:00
|
|
|
|
deriv_ids.push_back(deriv_id2);
|
|
|
|
|
|
|
|
|
|
// Handle diff lag auxvar, possibly nested several times
|
|
|
|
|
int diff_lag_depth = 0;
|
2022-07-20 14:32:57 +02:00
|
|
|
|
while (avi->type == AuxVarType::diffLag)
|
2021-07-07 10:54:04 +02:00
|
|
|
|
{
|
|
|
|
|
diff_lag_depth++;
|
2022-07-20 14:32:57 +02:00
|
|
|
|
if (avi->orig_symb_id == lhs_symb_id && lead_lag2 - diff_lag_depth == lead_lag)
|
2021-07-07 10:54:04 +02:00
|
|
|
|
{
|
|
|
|
|
deriv_ids.push_back(deriv_id2);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
try
|
|
|
|
|
{
|
2022-07-20 14:32:57 +02:00
|
|
|
|
avi = &symbol_table.getAuxVarInfo(avi->orig_symb_id.value());
|
2021-07-07 10:54:04 +02:00
|
|
|
|
}
|
|
|
|
|
catch (SymbolTable::UnknownSymbolIDException)
|
|
|
|
|
{
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return deriv_ids;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
DynamicModel::fillVarModelTableMatrices()
|
|
|
|
|
{
|
|
|
|
|
map<string, map<tuple<int, int, int>, expr_t>> AR;
|
|
|
|
|
map<string, map<tuple<int, int>, expr_t>> A0;
|
2021-07-16 15:12:37 +02:00
|
|
|
|
map<string, map<int, expr_t>> constants;
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (const auto& [model_name, eqns] : var_model_table.getEqNums())
|
2021-07-07 10:54:04 +02:00
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
const vector<int>& lhs = var_model_table.getLhs(model_name);
|
2021-07-07 10:54:04 +02:00
|
|
|
|
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++)
|
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
vector<int> deriv_ids = getVARDerivIDs(lhs_symb_id, -lag);
|
2021-07-07 10:54:04 +02:00
|
|
|
|
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())
|
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
cerr << "ERROR: Equation "
|
|
|
|
|
<< equation_tags.getTagValueByEqnAndKey(eqns[i], "name")
|
|
|
|
|
.value_or(to_string(eqns[i] + 1))
|
|
|
|
|
<< " is not linear" << endl;
|
2021-07-07 10:54:04 +02:00
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-30 15:28:57 +01:00
|
|
|
|
AR[model_name][{i, lag, lhs_symb_id}] = AddUMinus(d);
|
2021-07-07 10:54:04 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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())
|
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
cerr << "ERROR: Equation "
|
|
|
|
|
<< equation_tags.getTagValueByEqnAndKey(eqns[i], "name")
|
|
|
|
|
.value_or(to_string(eqns[i] + 1))
|
|
|
|
|
<< " is not linear" << endl;
|
2021-07-07 10:54:04 +02:00
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-30 15:28:57 +01:00
|
|
|
|
A0[model_name][{i, lhs_symb_id}] = d;
|
2021-07-07 10:54:04 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
2021-07-16 15:12:37 +02:00
|
|
|
|
|
|
|
|
|
// 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;
|
2023-11-30 15:28:57 +01:00
|
|
|
|
map<VariableNode*, NumConstNode*> subst_table;
|
|
|
|
|
auto rhs_vars = var_model_table.getRhs(
|
|
|
|
|
model_name)[i]; // All the (transformed) endogenous on RHS, as computed by
|
|
|
|
|
// updateVarAndTrendModel()
|
2021-07-16 15:12:37 +02:00
|
|
|
|
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;
|
|
|
|
|
}
|
2021-07-07 10:54:04 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
2022-06-13 14:15:47 +02:00
|
|
|
|
var_model_table.setAR(move(AR));
|
|
|
|
|
var_model_table.setA0(move(A0));
|
|
|
|
|
var_model_table.setConstants(move(constants));
|
2021-07-07 10:54:04 +02:00
|
|
|
|
}
|
|
|
|
|
|
2019-03-14 17:20:45 +01:00
|
|
|
|
map<string, map<tuple<int, int, int>, expr_t>>
|
2021-07-07 10:54:04 +02:00
|
|
|
|
DynamicModel::computeAutoregressiveMatrices() const
|
2019-03-14 17:20:45 +01:00
|
|
|
|
{
|
|
|
|
|
map<string, map<tuple<int, int, int>, expr_t>> ARr;
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (const auto& [model_name, eqns] : trend_component_model_table.getNonTargetEqNums())
|
2019-03-14 17:20:45 +01:00
|
|
|
|
{
|
|
|
|
|
map<tuple<int, int, int>, expr_t> AR;
|
2023-11-30 15:28:57 +01:00
|
|
|
|
const vector<int>& lhs = trend_component_model_table.getNonTargetLhs(model_name);
|
|
|
|
|
for (int i {0}; auto eqn : eqns)
|
2019-03-14 17:20:45 +01:00
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
auto bopn = dynamic_cast<BinaryOpNode*>(equations[eqn]->arg2);
|
2019-03-15 12:27:32 +01:00
|
|
|
|
bopn->fillAutoregressiveRow(i++, lhs, AR);
|
2019-03-14 17:20:45 +01:00
|
|
|
|
}
|
2021-07-06 19:04:59 +02:00
|
|
|
|
ARr[model_name] = AR;
|
2019-03-14 17:20:45 +01:00
|
|
|
|
}
|
|
|
|
|
return ARr;
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-18 16:52:13 +01:00
|
|
|
|
void
|
2018-08-14 14:23:21 +02:00
|
|
|
|
DynamicModel::fillTrendComponentModelTable() const
|
2018-01-30 16:33:16 +01:00
|
|
|
|
{
|
2018-08-14 14:23:21 +02:00
|
|
|
|
map<string, vector<int>> eqnums, trend_eqnums, lhsr;
|
|
|
|
|
map<string, vector<expr_t>> lhs_expr_tr;
|
|
|
|
|
map<string, vector<set<pair<int, int>>>> rhsr;
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (const auto& [model_name, eqtags] : trend_component_model_table.getTargetEqTags())
|
2018-01-30 16:33:16 +01:00
|
|
|
|
{
|
2018-08-14 14:23:21 +02:00
|
|
|
|
vector<int> trend_eqnumber;
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (const auto& eqtag : eqtags)
|
2018-01-30 16:33:16 +01:00
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
optional<int> eqn {equation_tags.getEqnByTag("name", eqtag)};
|
2023-01-04 16:44:52 +01:00
|
|
|
|
if (!eqn)
|
2021-07-01 16:16:04 +02:00
|
|
|
|
{
|
|
|
|
|
cerr << "ERROR: no equation is named '" << eqtag << "'" << endl;
|
2018-08-14 14:23:21 +02:00
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
|
}
|
2023-01-04 16:44:52 +01:00
|
|
|
|
trend_eqnumber.push_back(*eqn);
|
2018-02-08 13:07:15 +01:00
|
|
|
|
}
|
2021-07-06 19:04:59 +02:00
|
|
|
|
trend_eqnums[model_name] = trend_eqnumber;
|
2018-08-14 14:23:21 +02:00
|
|
|
|
}
|
2018-01-30 16:33:16 +01:00
|
|
|
|
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (const auto& [model_name, eqtags] : trend_component_model_table.getEqTags())
|
2018-08-14 14:23:21 +02:00
|
|
|
|
{
|
|
|
|
|
vector<int> eqnumber, lhs;
|
|
|
|
|
vector<expr_t> lhs_expr_t;
|
|
|
|
|
vector<set<pair<int, int>>> rhs;
|
|
|
|
|
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (const auto& eqtag : eqtags)
|
2018-08-14 14:23:21 +02:00
|
|
|
|
{
|
|
|
|
|
set<pair<int, int>> lhs_set, lhs_tmp_set, rhs_set;
|
2023-11-30 15:28:57 +01:00
|
|
|
|
optional<int> eqn {equation_tags.getEqnByTag("name", eqtag)};
|
2023-01-04 16:44:52 +01:00
|
|
|
|
if (!eqn)
|
2018-01-30 16:33:16 +01:00
|
|
|
|
{
|
2021-07-01 16:16:04 +02:00
|
|
|
|
cerr << "ERROR: no equation is named '" << eqtag << "'" << endl;
|
2018-08-14 14:23:21 +02:00
|
|
|
|
exit(EXIT_FAILURE);
|
2018-01-30 16:33:16 +01:00
|
|
|
|
}
|
|
|
|
|
|
2023-01-04 16:44:52 +01:00
|
|
|
|
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);
|
2018-01-30 16:33:16 +01:00
|
|
|
|
|
2018-08-14 14:23:21 +02:00
|
|
|
|
if (lhs_set.size() != 1 || !lhs_tmp_set.empty())
|
|
|
|
|
{
|
|
|
|
|
cerr << "ERROR: in Equation " << eqtag
|
2023-11-30 15:28:57 +01:00
|
|
|
|
<< ". A trend component model may only have one endogenous variable on the LHS. "
|
|
|
|
|
<< endl;
|
2018-08-14 14:23:21 +02:00
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-23 13:03:52 +02:00
|
|
|
|
auto itlhs = lhs_set.begin();
|
|
|
|
|
if (itlhs->second != 0)
|
2018-08-14 14:23:21 +02:00
|
|
|
|
{
|
|
|
|
|
cerr << "ERROR: in Equation " << eqtag
|
2023-11-30 15:28:57 +01:00
|
|
|
|
<< ". The variable on the LHS of a trend component model may not appear with a "
|
|
|
|
|
"lead or a lag. "
|
2018-08-14 14:23:21 +02:00
|
|
|
|
<< endl;
|
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
|
}
|
2018-01-30 16:33:16 +01:00
|
|
|
|
|
2023-01-04 16:44:52 +01:00
|
|
|
|
eqnumber.push_back(*eqn);
|
2018-08-23 13:03:52 +02:00
|
|
|
|
lhs.push_back(itlhs->first);
|
2018-08-14 14:23:21 +02:00
|
|
|
|
lhs_set.clear();
|
|
|
|
|
set<expr_t> lhs_expr_t_set;
|
2023-01-04 16:44:52 +01:00
|
|
|
|
equations[*eqn]->arg1->collectVARLHSVariable(lhs_expr_t_set);
|
2018-08-14 14:23:21 +02:00
|
|
|
|
lhs_expr_t.push_back(*(lhs_expr_t_set.begin()));
|
2018-02-08 13:07:15 +01:00
|
|
|
|
|
2023-01-04 16:44:52 +01:00
|
|
|
|
equations[*eqn]->arg2->collectDynamicVariables(SymbolType::endogenous, rhs_set);
|
2018-08-14 14:23:21 +02:00
|
|
|
|
rhs.push_back(rhs_set);
|
|
|
|
|
}
|
2021-07-06 19:04:59 +02:00
|
|
|
|
eqnums[model_name] = eqnumber;
|
|
|
|
|
lhsr[model_name] = lhs;
|
|
|
|
|
lhs_expr_tr[model_name] = lhs_expr_t;
|
|
|
|
|
rhsr[model_name] = rhs;
|
2018-02-08 13:07:15 +01:00
|
|
|
|
}
|
2022-06-13 14:15:47 +02:00
|
|
|
|
trend_component_model_table.setRhs(move(rhsr));
|
2023-11-30 15:28:57 +01:00
|
|
|
|
trend_component_model_table.setVals(move(eqnums), move(trend_eqnums), move(lhsr),
|
|
|
|
|
move(lhs_expr_tr));
|
2018-09-10 17:13:51 +02:00
|
|
|
|
}
|
|
|
|
|
|
2022-01-28 16:38:50 +01:00
|
|
|
|
pair<map<string, map<tuple<int, int>, expr_t>>, map<string, map<tuple<int, int>, expr_t>>>
|
2023-11-30 15:28:57 +01:00
|
|
|
|
DynamicModel::computeErrorComponentMatrices(const ExprNode::subst_table_t& diff_subst_table) const
|
2018-09-10 17:13:51 +02:00
|
|
|
|
{
|
2022-01-28 16:38:50 +01:00
|
|
|
|
map<string, map<tuple<int, int>, expr_t>> A0r, A0starr;
|
2019-08-19 14:51:21 +02:00
|
|
|
|
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (const auto& [model_name, eqns] : trend_component_model_table.getEqNums())
|
2018-09-10 17:13:51 +02:00
|
|
|
|
{
|
2022-01-28 16:38:50 +01:00
|
|
|
|
map<tuple<int, int>, expr_t> A0, A0star;
|
2021-07-06 19:04:59 +02:00
|
|
|
|
vector<int> target_lhs = trend_component_model_table.getTargetLhs(model_name);
|
|
|
|
|
vector<int> nontarget_eqnums = trend_component_model_table.getNonTargetEqNums(model_name);
|
|
|
|
|
vector<int> undiff_nontarget_lhs = getUndiffLHSForPac(model_name, diff_subst_table);
|
2019-02-14 11:44:06 +01:00
|
|
|
|
vector<int> parsed_undiff_nontarget_lhs;
|
2018-09-12 17:56:30 +02:00
|
|
|
|
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (int i {0}; auto eqn : eqns)
|
2018-09-12 17:56:30 +02:00
|
|
|
|
{
|
2019-02-14 11:44:06 +01:00
|
|
|
|
if (find(nontarget_eqnums.begin(), nontarget_eqnums.end(), eqn) != nontarget_eqnums.end())
|
|
|
|
|
parsed_undiff_nontarget_lhs.push_back(undiff_nontarget_lhs.at(i));
|
2018-09-12 17:56:30 +02:00
|
|
|
|
i++;
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (int i {0}; auto eqn : eqns)
|
2019-02-14 11:44:06 +01:00
|
|
|
|
if (find(nontarget_eqnums.begin(), nontarget_eqnums.end(), eqn) != nontarget_eqnums.end())
|
2023-11-30 15:28:57 +01:00
|
|
|
|
equations[eqn]->arg2->fillErrorCorrectionRow(i++, parsed_undiff_nontarget_lhs, target_lhs,
|
|
|
|
|
A0, A0star);
|
2021-07-06 19:04:59 +02:00
|
|
|
|
A0r[model_name] = A0;
|
|
|
|
|
A0starr[model_name] = A0star;
|
2018-09-10 17:13:51 +02:00
|
|
|
|
}
|
2019-08-19 14:51:21 +02:00
|
|
|
|
|
2023-11-30 15:28:57 +01:00
|
|
|
|
return {A0r, A0starr};
|
2018-02-08 13:07:15 +01:00
|
|
|
|
}
|
|
|
|
|
|
2018-05-16 12:17:06 +02:00
|
|
|
|
void
|
2019-10-22 16:04:24 +02:00
|
|
|
|
DynamicModel::fillTrendComponentModelTableFromOrigModel() const
|
2018-05-16 12:17:06 +02:00
|
|
|
|
{
|
2022-05-16 15:56:54 +02:00
|
|
|
|
map<string, vector<int>> lags;
|
|
|
|
|
map<string, vector<optional<int>>> orig_diff_var;
|
2018-08-14 14:23:21 +02:00
|
|
|
|
map<string, vector<bool>> diff;
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (const auto& [model_name, eqns] : trend_component_model_table.getEqNums())
|
2018-05-16 12:17:06 +02:00
|
|
|
|
{
|
2018-08-14 14:23:21 +02:00
|
|
|
|
set<expr_t> lhs;
|
2022-05-16 15:56:54 +02:00
|
|
|
|
vector<optional<int>> orig_diff_var_vec;
|
2018-08-14 14:23:21 +02:00
|
|
|
|
vector<bool> diff_vec;
|
2021-07-06 19:04:59 +02:00
|
|
|
|
for (auto eqn : eqns)
|
2018-05-16 12:17:06 +02:00
|
|
|
|
{
|
2021-07-06 18:42:19 +02:00
|
|
|
|
// Perform some sanity checks on the RHS
|
2023-11-30 15:28:57 +01:00
|
|
|
|
optional<string> eqtag {equation_tags.getTagValueByEqnAndKey(eqn, "name")};
|
2021-07-06 18:42:19 +02:00
|
|
|
|
set<pair<int, int>> rhs_endo_set, rhs_exo_set;
|
|
|
|
|
equations[eqn]->arg2->collectDynamicVariables(SymbolType::endogenous, rhs_endo_set);
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (const auto& [symb_id, lag] : rhs_endo_set)
|
2021-07-06 18:42:19 +02:00
|
|
|
|
if (lag >= 0)
|
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
cerr << "ERROR: in Equation " << eqtag.value_or(to_string(eqn + 1))
|
|
|
|
|
<< ". A trend component model may not have leaded or contemporaneous "
|
|
|
|
|
"endogenous variables on the RHS. "
|
|
|
|
|
<< endl;
|
2021-07-06 18:42:19 +02:00
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
|
}
|
|
|
|
|
equations[eqn]->arg2->collectDynamicVariables(SymbolType::exogenous, rhs_exo_set);
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (const auto& [symb_id, lag] : rhs_exo_set)
|
2021-07-06 18:42:19 +02:00
|
|
|
|
if (lag != 0)
|
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
cerr << "ERROR: in Equation " << eqtag.value_or(to_string(eqn + 1))
|
|
|
|
|
<< ". A trend component model may not have lagged or leaded exogenous "
|
|
|
|
|
"variables on the RHS. "
|
|
|
|
|
<< endl;
|
2021-07-06 18:42:19 +02:00
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
|
}
|
2018-05-31 15:34:25 +02:00
|
|
|
|
|
2018-08-14 14:23:21 +02:00
|
|
|
|
// save lhs variables
|
2018-11-28 14:32:26 +01:00
|
|
|
|
equations[eqn]->arg1->collectVARLHSVariable(lhs);
|
2018-04-16 13:50:31 +02:00
|
|
|
|
|
2019-12-16 19:42:59 +01:00
|
|
|
|
if (equations[eqn]->arg1->countDiffs() > 0)
|
|
|
|
|
diff_vec.push_back(true);
|
|
|
|
|
else
|
|
|
|
|
diff_vec.push_back(false);
|
2018-08-14 14:23:21 +02:00
|
|
|
|
if (diff_vec.back())
|
|
|
|
|
{
|
|
|
|
|
set<pair<int, int>> diff_set;
|
2018-11-28 14:32:26 +01:00
|
|
|
|
equations[eqn]->arg1->collectDynamicVariables(SymbolType::endogenous, diff_set);
|
2018-04-16 13:50:31 +02:00
|
|
|
|
|
2018-08-14 14:23:21 +02:00
|
|
|
|
if (diff_set.size() != 1)
|
|
|
|
|
{
|
|
|
|
|
cerr << "ERROR: problem getting variable for LHS diff operator in equation "
|
|
|
|
|
<< eqn << endl;
|
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
|
}
|
2023-12-01 14:29:23 +01:00
|
|
|
|
orig_diff_var_vec.emplace_back(diff_set.begin()->first);
|
2018-08-14 14:23:21 +02:00
|
|
|
|
}
|
|
|
|
|
else
|
2023-12-01 14:29:23 +01:00
|
|
|
|
orig_diff_var_vec.emplace_back(nullopt);
|
2018-08-14 14:23:21 +02:00
|
|
|
|
}
|
2018-04-16 13:50:31 +02:00
|
|
|
|
|
2021-07-06 19:04:59 +02:00
|
|
|
|
if (eqns.size() != lhs.size())
|
2018-02-08 13:07:15 +01:00
|
|
|
|
{
|
2018-08-21 11:46:59 +02:00
|
|
|
|
cerr << "ERROR: The LHS variables of the trend component model are not unique" << endl;
|
2018-08-14 14:23:21 +02:00
|
|
|
|
exit(EXIT_FAILURE);
|
2018-02-08 13:07:15 +01:00
|
|
|
|
}
|
2018-08-14 14:23:21 +02:00
|
|
|
|
|
2019-10-22 16:04:24 +02:00
|
|
|
|
set<expr_t> lhs_lag_equiv;
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (const auto& lh : lhs)
|
2019-10-22 16:04:24 +02:00
|
|
|
|
{
|
|
|
|
|
auto [lag_equiv_repr, index] = lh->getLagEquivalenceClass();
|
|
|
|
|
lhs_lag_equiv.insert(lag_equiv_repr);
|
|
|
|
|
}
|
2018-08-14 14:23:21 +02:00
|
|
|
|
|
|
|
|
|
vector<int> max_lag;
|
2021-07-06 19:04:59 +02:00
|
|
|
|
for (auto eqn : eqns)
|
2019-10-22 16:04:24 +02:00
|
|
|
|
max_lag.push_back(equations[eqn]->arg2->VarMaxLag(lhs_lag_equiv));
|
2021-07-06 19:04:59 +02:00
|
|
|
|
lags[model_name] = max_lag;
|
|
|
|
|
diff[model_name] = diff_vec;
|
|
|
|
|
orig_diff_var[model_name] = orig_diff_var_vec;
|
2018-01-30 16:33:16 +01:00
|
|
|
|
}
|
2022-06-13 14:15:47 +02:00
|
|
|
|
trend_component_model_table.setDiff(move(diff));
|
|
|
|
|
trend_component_model_table.setMaxLags(move(lags));
|
|
|
|
|
trend_component_model_table.setOrigDiffVar(move(orig_diff_var));
|
2018-01-30 16:33:16 +01:00
|
|
|
|
}
|
2018-08-21 11:46:59 +02:00
|
|
|
|
|
2018-09-12 17:56:30 +02:00
|
|
|
|
void
|
2023-11-30 15:28:57 +01:00
|
|
|
|
DynamicModel::fillTrendComponentModelTableAREC(
|
|
|
|
|
const ExprNode::subst_table_t& diff_subst_table) const
|
2018-09-12 17:56:30 +02:00
|
|
|
|
{
|
2021-07-07 10:54:04 +02:00
|
|
|
|
auto ARr = computeAutoregressiveMatrices();
|
2022-06-13 14:15:47 +02:00
|
|
|
|
trend_component_model_table.setAR(move(ARr));
|
2021-07-01 16:16:04 +02:00
|
|
|
|
auto [A0r, A0starr] = computeErrorComponentMatrices(diff_subst_table);
|
2022-06-13 14:15:47 +02:00
|
|
|
|
trend_component_model_table.setA0(move(A0r), move(A0starr));
|
2018-09-12 17:56:30 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-08-14 14:23:21 +02:00
|
|
|
|
vector<int>
|
2023-11-30 15:28:57 +01:00
|
|
|
|
DynamicModel::getUndiffLHSForPac(const string& aux_model_name,
|
|
|
|
|
const ExprNode::subst_table_t& diff_subst_table) const
|
2018-03-28 18:46:15 +02:00
|
|
|
|
{
|
2018-08-14 14:23:21 +02:00
|
|
|
|
vector<expr_t> lhs_expr_t = trend_component_model_table.getLhsExprT(aux_model_name);
|
|
|
|
|
vector<int> lhs = trend_component_model_table.getLhs(aux_model_name);
|
|
|
|
|
vector<bool> diff = trend_component_model_table.getDiff(aux_model_name);
|
|
|
|
|
vector<int> eqnumber = trend_component_model_table.getEqNums(aux_model_name);
|
2018-09-13 12:21:23 +02:00
|
|
|
|
vector<int> nontrend_eqnums = trend_component_model_table.getNonTargetEqNums(aux_model_name);
|
2018-08-14 14:23:21 +02:00
|
|
|
|
|
|
|
|
|
for (auto eqn : nontrend_eqnums)
|
2018-03-28 18:46:15 +02:00
|
|
|
|
{
|
2022-06-02 10:50:21 +02:00
|
|
|
|
auto i = distance(eqnumber.begin(), find(eqnumber.begin(), eqnumber.end(), eqn));
|
2018-03-28 18:46:15 +02:00
|
|
|
|
|
2018-04-17 15:17:28 +02:00
|
|
|
|
if (eqnumber[i] != eqn)
|
2018-03-28 18:46:15 +02:00
|
|
|
|
{
|
2018-05-28 15:54:20 +02:00
|
|
|
|
cerr << "ERROR: equation " << eqn << " not found in VAR" << endl;
|
2018-03-28 18:46:15 +02:00
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-28 15:55:01 +02:00
|
|
|
|
if (diff.at(i) != true)
|
2018-03-28 18:46:15 +02:00
|
|
|
|
{
|
2018-08-14 14:23:21 +02:00
|
|
|
|
cerr << "ERROR: the variable on the LHS of equation #" << eqn
|
|
|
|
|
<< " does not have the diff operator applied to it yet you are trying to undiff it."
|
|
|
|
|
<< endl;
|
2018-03-28 18:46:15 +02:00
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
|
}
|
|
|
|
|
|
2018-04-17 15:17:28 +02:00
|
|
|
|
bool printerr = false;
|
2018-06-04 12:52:14 +02:00
|
|
|
|
expr_t node = nullptr;
|
2018-04-17 15:17:28 +02:00
|
|
|
|
expr_t aux_var = lhs_expr_t.at(i);
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (const auto& it : diff_subst_table)
|
2019-12-16 19:42:59 +01:00
|
|
|
|
if (it.second == aux_var)
|
2018-04-17 15:17:28 +02:00
|
|
|
|
{
|
2019-12-16 19:42:59 +01:00
|
|
|
|
node = const_cast<expr_t>(it.first);
|
2018-04-17 15:17:28 +02:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-16 19:42:59 +01:00
|
|
|
|
if (!node)
|
2018-04-17 15:17:28 +02:00
|
|
|
|
{
|
|
|
|
|
cerr << "Unexpected error encountered." << endl;
|
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-14 14:23:21 +02:00
|
|
|
|
node = node->undiff();
|
2019-12-16 19:42:59 +01:00
|
|
|
|
auto it1 = diff_subst_table.find(node);
|
2018-08-14 14:23:21 +02:00
|
|
|
|
if (it1 == diff_subst_table.end())
|
|
|
|
|
printerr = true;
|
2018-04-17 15:17:28 +02:00
|
|
|
|
|
|
|
|
|
if (printerr)
|
|
|
|
|
{ // we have undiffed something like diff(x), hence x is not in diff_subst_table
|
|
|
|
|
lhs_expr_t.at(i) = node;
|
2023-11-30 15:28:57 +01:00
|
|
|
|
lhs.at(i) = dynamic_cast<VariableNode*>(node)->symb_id;
|
2018-04-17 15:17:28 +02:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
lhs_expr_t.at(i) = const_cast<expr_t>(it1->first);
|
2023-11-30 15:28:57 +01:00
|
|
|
|
lhs.at(i) = const_cast<VariableNode*>(it1->second)->symb_id;
|
2018-04-17 15:17:28 +02:00
|
|
|
|
}
|
2018-03-28 18:46:15 +02:00
|
|
|
|
}
|
2018-08-14 14:23:21 +02:00
|
|
|
|
return lhs;
|
2018-03-28 18:46:15 +02:00
|
|
|
|
}
|
|
|
|
|
|
2021-10-27 18:17:14 +02:00
|
|
|
|
void
|
2023-11-30 15:28:57 +01:00
|
|
|
|
DynamicModel::analyzePacEquationStructure(const string& name, map<string, string>& pac_eq_name,
|
|
|
|
|
PacModelTable::equation_info_t& pac_equation_info)
|
2018-02-12 17:37:53 +01:00
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (auto& equation : equations)
|
2021-10-28 14:42:56 +02:00
|
|
|
|
if (equation->containsPacExpectation(name))
|
|
|
|
|
{
|
|
|
|
|
if (!pac_eq_name[name].empty())
|
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
cerr << "It is not possible to use 'pac_expectation(" << name
|
|
|
|
|
<< ")' in several equations." << endl;
|
2021-10-28 14:42:56 +02:00
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
|
}
|
2023-11-30 15:28:57 +01:00
|
|
|
|
optional<string> eqn {
|
|
|
|
|
equation_tags.getTagValueByEqnAndKey(&equation - &equations[0], "name")};
|
2023-01-04 16:03:12 +01:00
|
|
|
|
if (!eqn)
|
2021-10-28 14:42:56 +02:00
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
cerr << "Every equation with a 'pac_expectation' operator must have been assigned an "
|
|
|
|
|
"equation tag name"
|
|
|
|
|
<< endl;
|
2021-10-28 14:42:56 +02:00
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
|
}
|
2023-01-04 16:03:12 +01:00
|
|
|
|
pac_eq_name[name] = *eqn;
|
2018-08-09 16:14:40 +02:00
|
|
|
|
|
2021-10-28 14:42:56 +02:00
|
|
|
|
set<pair<int, int>> lhss;
|
|
|
|
|
equation->arg1->collectDynamicVariables(SymbolType::endogenous, lhss);
|
|
|
|
|
auto lhs = *lhss.begin();
|
|
|
|
|
int lhs_symb_id = lhs.first;
|
|
|
|
|
int lhs_orig_symb_id = lhs_symb_id;
|
2022-01-28 16:28:14 +01:00
|
|
|
|
if (symbol_table.isDiffAuxiliaryVariable(lhs_orig_symb_id))
|
2021-10-28 14:42:56 +02:00
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
lhs_orig_symb_id = symbol_table.getOrigSymbIdForAuxVar(lhs_orig_symb_id);
|
|
|
|
|
}
|
|
|
|
|
catch (...)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-30 15:28:57 +01:00
|
|
|
|
auto arg2 = dynamic_cast<BinaryOpNode*>(equation->arg2);
|
2021-10-28 14:42:56 +02:00
|
|
|
|
if (!arg2)
|
|
|
|
|
{
|
|
|
|
|
cerr << "Pac equation in incorrect format" << endl;
|
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
|
}
|
|
|
|
|
auto [optim_share_index, optim_part, non_optim_part, additive_part]
|
2023-11-30 15:28:57 +01:00
|
|
|
|
= arg2->getPacOptimizingShareAndExprNodes(lhs_orig_symb_id);
|
2021-10-28 14:42:56 +02:00
|
|
|
|
|
|
|
|
|
pair<int, vector<tuple<int, bool, int>>> ec_params_and_vars;
|
2022-05-16 17:42:24 +02:00
|
|
|
|
vector<tuple<optional<int>, optional<int>, int>> ar_params_and_vars;
|
2023-11-30 15:28:57 +01:00
|
|
|
|
vector<tuple<int, int, optional<int>, double>> non_optim_vars_params_and_constants,
|
|
|
|
|
optim_additive_vars_params_and_constants, additive_vars_params_and_constants;
|
2021-10-28 14:42:56 +02:00
|
|
|
|
if (!optim_part)
|
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
auto bopn = dynamic_cast<BinaryOpNode*>(equation->arg2);
|
2021-10-28 14:42:56 +02:00
|
|
|
|
if (!bopn)
|
|
|
|
|
{
|
|
|
|
|
cerr << "Error in PAC equation" << endl;
|
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
|
}
|
2023-11-30 15:28:57 +01:00
|
|
|
|
bopn->getPacAREC(lhs_symb_id, lhs_orig_symb_id, ec_params_and_vars, ar_params_and_vars,
|
|
|
|
|
additive_vars_params_and_constants);
|
2021-10-28 14:42:56 +02:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
auto bopn = dynamic_cast<BinaryOpNode*>(optim_part);
|
2021-10-28 14:42:56 +02:00
|
|
|
|
if (!bopn)
|
|
|
|
|
{
|
|
|
|
|
cerr << "Error in PAC equation" << endl;
|
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
|
}
|
2023-11-30 15:28:57 +01:00
|
|
|
|
bopn->getPacAREC(lhs_symb_id, lhs_orig_symb_id, ec_params_and_vars, ar_params_and_vars,
|
|
|
|
|
optim_additive_vars_params_and_constants);
|
2018-09-04 10:39:05 +02:00
|
|
|
|
try
|
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
non_optim_vars_params_and_constants
|
|
|
|
|
= non_optim_part->matchLinearCombinationOfVariables();
|
2021-10-28 14:42:56 +02:00
|
|
|
|
if (additive_part)
|
2023-11-30 15:28:57 +01:00
|
|
|
|
additive_vars_params_and_constants
|
|
|
|
|
= additive_part->matchLinearCombinationOfVariables();
|
2018-09-04 10:39:05 +02:00
|
|
|
|
}
|
2023-11-30 15:28:57 +01:00
|
|
|
|
catch (ExprNode::MatchFailureException& e)
|
2018-09-04 10:39:05 +02:00
|
|
|
|
{
|
2021-10-28 14:42:56 +02:00
|
|
|
|
cerr << "Error in parsing non-optimizing agents or additive part of PAC equation: "
|
|
|
|
|
<< e.message << endl;
|
|
|
|
|
exit(EXIT_FAILURE);
|
2018-09-04 10:39:05 +02:00
|
|
|
|
}
|
2021-10-28 14:42:56 +02:00
|
|
|
|
}
|
2018-09-04 10:39:05 +02:00
|
|
|
|
|
2021-10-28 14:42:56 +02:00
|
|
|
|
if (lhs.first == -1)
|
|
|
|
|
{
|
|
|
|
|
cerr << "analyzePacEquationStructure: error obtaining LHS variable." << endl;
|
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
|
}
|
|
|
|
|
if (ec_params_and_vars.second.empty())
|
|
|
|
|
{
|
|
|
|
|
cerr << "analyzePacEquationStructure: error obtaining RHS parameters." << endl;
|
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
|
}
|
2023-11-30 15:28:57 +01:00
|
|
|
|
pac_equation_info[name] = {lhs,
|
2023-12-13 15:37:07 +01:00
|
|
|
|
optim_share_index,
|
2023-11-30 15:28:57 +01:00
|
|
|
|
move(ar_params_and_vars),
|
|
|
|
|
move(ec_params_and_vars),
|
|
|
|
|
move(non_optim_vars_params_and_constants),
|
|
|
|
|
move(additive_vars_params_and_constants),
|
|
|
|
|
move(optim_additive_vars_params_and_constants)};
|
2021-10-28 14:42:56 +02:00
|
|
|
|
}
|
2018-02-12 17:37:53 +01:00
|
|
|
|
}
|
|
|
|
|
|
2019-02-15 12:52:46 +01:00
|
|
|
|
int
|
2023-11-30 15:28:57 +01:00
|
|
|
|
DynamicModel::getPacTargetSymbId(const string& pac_model_name) const
|
2019-02-15 12:52:46 +01:00
|
|
|
|
{
|
2020-07-23 14:45:32 +02:00
|
|
|
|
for (auto equation : equations)
|
2019-02-15 12:52:46 +01:00
|
|
|
|
if (equation->containsPacExpectation(pac_model_name))
|
|
|
|
|
{
|
2019-02-20 12:59:55 +01:00
|
|
|
|
set<pair<int, int>> lhss;
|
|
|
|
|
equation->arg1->collectDynamicVariables(SymbolType::endogenous, lhss);
|
2020-07-23 14:45:32 +02:00
|
|
|
|
if (lhss.size() != 1)
|
2023-11-30 15:28:57 +01:00
|
|
|
|
throw PacTargetNotIdentifiedException {pac_model_name,
|
|
|
|
|
"LHS must contain a single endogenous"};
|
2020-07-10 14:55:28 +02:00
|
|
|
|
int lhs_symb_id = lhss.begin()->first;
|
2020-07-23 14:45:32 +02:00
|
|
|
|
if (!symbol_table.isDiffAuxiliaryVariable(lhs_symb_id))
|
2023-11-30 15:28:57 +01:00
|
|
|
|
throw PacTargetNotIdentifiedException {pac_model_name, "LHS must be a diff operator"};
|
2020-07-23 14:45:32 +02:00
|
|
|
|
int undiff_lhs_symb_id = symbol_table.getOrigSymbIdForAuxVar(lhs_symb_id);
|
2023-11-30 15:28:57 +01:00
|
|
|
|
auto barg2 = dynamic_cast<BinaryOpNode*>(equation->arg2);
|
2020-07-23 14:45:32 +02:00
|
|
|
|
if (!barg2)
|
2023-11-30 15:28:57 +01:00
|
|
|
|
throw PacTargetNotIdentifiedException {pac_model_name, "RHS must be a binary operator"};
|
2020-07-23 14:45:32 +02:00
|
|
|
|
auto [optim_share_index, optim_part, non_optim_part, additive_part]
|
2023-11-30 15:28:57 +01:00
|
|
|
|
= barg2->getPacOptimizingShareAndExprNodes(undiff_lhs_symb_id);
|
2020-07-23 14:45:32 +02:00
|
|
|
|
/* If there is an optimization part, restrict the search to that part,
|
|
|
|
|
since it contains the MCE . */
|
|
|
|
|
expr_t mce = optim_part ? optim_part : equation->arg2;
|
|
|
|
|
|
|
|
|
|
vector<pair<expr_t, int>> terms;
|
|
|
|
|
mce->decomposeAdditiveTerms(terms);
|
|
|
|
|
for (auto [term, sign] : terms)
|
2019-02-15 12:52:46 +01:00
|
|
|
|
try
|
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
auto [param_id, target_id]
|
|
|
|
|
= term->matchParamTimesTargetMinusVariable(undiff_lhs_symb_id);
|
2020-07-23 14:45:32 +02:00
|
|
|
|
return target_id;
|
2019-02-15 12:52:46 +01:00
|
|
|
|
}
|
2023-11-30 15:28:57 +01:00
|
|
|
|
catch (ExprNode::MatchFailureException&)
|
2019-02-15 12:52:46 +01:00
|
|
|
|
{
|
|
|
|
|
}
|
2023-11-30 15:28:57 +01:00
|
|
|
|
throw PacTargetNotIdentifiedException {pac_model_name,
|
|
|
|
|
"No term of the form parameter*(target-LHS_level)"};
|
2019-02-15 12:52:46 +01:00
|
|
|
|
}
|
2023-11-30 15:28:57 +01:00
|
|
|
|
throw PacTargetNotIdentifiedException {
|
|
|
|
|
pac_model_name, "No equation with the corresponding pac_expectation operator"};
|
2019-02-15 12:52:46 +01:00
|
|
|
|
}
|
|
|
|
|
|
2019-02-19 16:36:10 +01:00
|
|
|
|
void
|
2023-11-30 15:28:57 +01:00
|
|
|
|
DynamicModel::computePacModelConsistentExpectationSubstitution(
|
|
|
|
|
const string& name, int discount_symb_id, int pac_eq_max_lag, expr_t growth_correction_term,
|
|
|
|
|
string auxname, ExprNode::subst_table_t& diff_subst_table,
|
|
|
|
|
map<string, int>& pac_aux_var_symb_ids, map<string, vector<int>>& pac_aux_param_symb_ids,
|
|
|
|
|
map<string, expr_t>& pac_expectation_substitution)
|
2019-02-15 12:52:46 +01:00
|
|
|
|
{
|
2020-07-23 14:45:32 +02:00
|
|
|
|
int pac_target_symb_id;
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
pac_target_symb_id = getPacTargetSymbId(name);
|
|
|
|
|
}
|
2023-11-30 15:28:57 +01:00
|
|
|
|
catch (PacTargetNotIdentifiedException& e)
|
2020-07-23 14:45:32 +02:00
|
|
|
|
{
|
|
|
|
|
cerr << "Can't identify target for PAC model " << name << ": " << e.message;
|
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
|
}
|
2019-02-19 16:36:10 +01:00
|
|
|
|
int neqs = 0;
|
2021-10-27 18:17:14 +02:00
|
|
|
|
|
2021-11-17 12:15:49 +01:00
|
|
|
|
// Create the endogenous representing Z₁ (no orig_expr is given since its definition is recursive)
|
2021-11-18 17:08:08 +01:00
|
|
|
|
if (auxname.empty())
|
|
|
|
|
auxname = "mce_Z1_" + name;
|
|
|
|
|
int mce_z1_symb_id = symbol_table.addPacExpectationAuxiliaryVar(auxname, nullptr);
|
2021-11-16 17:58:20 +01:00
|
|
|
|
pac_aux_var_symb_ids[name] = mce_z1_symb_id;
|
2021-10-27 18:17:14 +02:00
|
|
|
|
|
2021-10-28 14:42:56 +02:00
|
|
|
|
expr_t A = One;
|
|
|
|
|
expr_t fp = Zero;
|
|
|
|
|
expr_t beta = AddVariable(discount_symb_id);
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (int i = 1; i <= pac_eq_max_lag + 1; i++)
|
2021-10-28 14:42:56 +02:00
|
|
|
|
{
|
|
|
|
|
string param_name = "mce_alpha_" + name + "_" + to_string(i);
|
2021-10-27 18:17:14 +02:00
|
|
|
|
try
|
2019-02-19 16:36:10 +01:00
|
|
|
|
{
|
2021-10-28 14:42:56 +02:00
|
|
|
|
int alpha_i_symb_id = symbol_table.addSymbol(param_name, SymbolType::parameter);
|
2021-11-17 18:12:29 +01:00
|
|
|
|
pac_aux_param_symb_ids[name].push_back(alpha_i_symb_id);
|
2021-10-28 14:42:56 +02:00
|
|
|
|
A = AddPlus(A, AddVariable(alpha_i_symb_id));
|
2023-11-30 15:28:57 +01:00
|
|
|
|
fp = AddPlus(fp, AddTimes(AddTimes(AddVariable(alpha_i_symb_id),
|
|
|
|
|
AddPower(beta, AddPossiblyNegativeConstant(i))),
|
|
|
|
|
AddVariable(mce_z1_symb_id, i)));
|
2021-10-27 18:17:14 +02:00
|
|
|
|
}
|
2023-11-30 15:28:57 +01:00
|
|
|
|
catch (SymbolTable::AlreadyDeclaredException& e)
|
2021-10-27 18:17:14 +02:00
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
cerr << "The variable/parameter '" << param_name
|
|
|
|
|
<< "' conflicts with a parameter that will be generated for the '" << name
|
|
|
|
|
<< "' PAC model. Please rename it." << endl;
|
2019-02-19 16:36:10 +01:00
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
|
}
|
2021-10-28 14:42:56 +02:00
|
|
|
|
}
|
2019-02-15 12:52:46 +01:00
|
|
|
|
|
2021-10-28 14:42:56 +02:00
|
|
|
|
// Add diff nodes and eqs for pac_target_symb_id
|
2023-11-30 15:28:57 +01:00
|
|
|
|
const VariableNode* target_base_diff_node;
|
|
|
|
|
auto create_target_lag = [&](int lag) {
|
2021-10-28 14:42:56 +02:00
|
|
|
|
if (symbol_table.isAuxiliaryVariable(pac_target_symb_id))
|
2022-01-28 16:28:14 +01:00
|
|
|
|
// We know it is a log, see ExprNode::matchParamTimesTargetMinusVariable()
|
|
|
|
|
return AddLog(AddVariable(symbol_table.getOrigSymbIdForAuxVar(pac_target_symb_id), lag));
|
2021-10-28 14:42:56 +02:00
|
|
|
|
else
|
2023-11-30 15:28:57 +01:00
|
|
|
|
return dynamic_cast<ExprNode*>(AddVariable(pac_target_symb_id, lag));
|
2021-10-28 14:42:56 +02:00
|
|
|
|
};
|
2020-07-24 12:28:13 +02:00
|
|
|
|
|
2021-10-28 14:42:56 +02:00
|
|
|
|
expr_t diff_node_to_search = AddDiff(create_target_lag(0));
|
2023-11-30 15:28:57 +01:00
|
|
|
|
if (auto sit = diff_subst_table.find(diff_node_to_search); sit != diff_subst_table.end())
|
2021-10-28 14:42:56 +02:00
|
|
|
|
target_base_diff_node = sit->second;
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
int symb_id = symbol_table.addDiffAuxiliaryVar(diff_node_to_search->idx, diff_node_to_search);
|
|
|
|
|
target_base_diff_node = AddVariable(symb_id);
|
2023-11-30 15:28:57 +01:00
|
|
|
|
auto neweq = AddEqual(const_cast<VariableNode*>(target_base_diff_node),
|
|
|
|
|
AddMinus(create_target_lag(0), create_target_lag(-1)));
|
2022-05-05 18:39:27 +02:00
|
|
|
|
addEquation(neweq, nullopt);
|
2021-10-28 14:42:56 +02:00
|
|
|
|
addAuxEquation(neweq);
|
|
|
|
|
neqs++;
|
|
|
|
|
}
|
2019-02-15 12:52:46 +01:00
|
|
|
|
|
2023-11-30 15:28:57 +01:00
|
|
|
|
map<int, VariableNode*> target_aux_var_to_add;
|
|
|
|
|
const VariableNode* last_aux_var = target_base_diff_node;
|
2021-10-28 14:42:56 +02:00
|
|
|
|
for (int i = 1; i <= pac_eq_max_lag; i++, neqs++)
|
|
|
|
|
{
|
|
|
|
|
expr_t this_diff_node = AddDiff(create_target_lag(i));
|
|
|
|
|
int symb_id = symbol_table.addDiffLeadAuxiliaryVar(this_diff_node->idx, this_diff_node,
|
2022-01-27 18:38:25 +01:00
|
|
|
|
last_aux_var->symb_id, 1);
|
2023-11-30 15:28:57 +01:00
|
|
|
|
VariableNode* current_aux_var = AddVariable(symb_id);
|
2021-10-28 14:42:56 +02:00
|
|
|
|
auto neweq = AddEqual(current_aux_var, AddVariable(last_aux_var->symb_id, 1));
|
2022-05-05 18:39:27 +02:00
|
|
|
|
addEquation(neweq, nullopt);
|
2021-10-28 14:42:56 +02:00
|
|
|
|
addAuxEquation(neweq);
|
|
|
|
|
last_aux_var = current_aux_var;
|
|
|
|
|
target_aux_var_to_add[i] = current_aux_var;
|
|
|
|
|
}
|
2019-02-15 12:52:46 +01:00
|
|
|
|
|
2021-10-28 14:42:56 +02:00
|
|
|
|
expr_t fs = Zero;
|
|
|
|
|
for (int k = 1; k <= pac_eq_max_lag; k++)
|
|
|
|
|
{
|
|
|
|
|
expr_t ssum = Zero;
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (int j = k + 1; j <= pac_eq_max_lag + 1; j++)
|
2019-02-15 12:52:46 +01:00
|
|
|
|
{
|
2021-10-28 14:42:56 +02:00
|
|
|
|
int alpha_j_symb_id = -1;
|
|
|
|
|
string param_name = "mce_alpha_" + name + "_" + to_string(j);
|
|
|
|
|
try
|
2019-02-15 12:52:46 +01:00
|
|
|
|
{
|
2021-10-28 14:42:56 +02:00
|
|
|
|
alpha_j_symb_id = symbol_table.getID(param_name);
|
2019-02-15 12:52:46 +01:00
|
|
|
|
}
|
2023-11-30 15:28:57 +01:00
|
|
|
|
catch (SymbolTable::UnknownSymbolNameException& e)
|
2021-10-28 14:42:56 +02:00
|
|
|
|
{
|
|
|
|
|
alpha_j_symb_id = symbol_table.addSymbol(param_name, SymbolType::parameter);
|
|
|
|
|
}
|
2023-11-30 15:28:57 +01:00
|
|
|
|
ssum = AddPlus(ssum, AddTimes(AddVariable(alpha_j_symb_id),
|
|
|
|
|
AddPower(beta, AddPossiblyNegativeConstant(j))));
|
2019-02-15 12:52:46 +01:00
|
|
|
|
}
|
2021-10-28 14:42:56 +02:00
|
|
|
|
fs = AddPlus(fs, AddTimes(ssum, target_aux_var_to_add[k]));
|
2019-02-15 12:52:46 +01:00
|
|
|
|
}
|
2023-11-30 15:28:57 +01:00
|
|
|
|
auto neweq = AddEqual(
|
|
|
|
|
AddVariable(mce_z1_symb_id),
|
|
|
|
|
AddMinus(AddTimes(A, AddMinus(const_cast<VariableNode*>(target_base_diff_node), fs)), fp));
|
2022-05-05 18:39:27 +02:00
|
|
|
|
addEquation(neweq, nullopt);
|
2021-11-17 12:15:49 +01:00
|
|
|
|
/* This equation is not added to the list of auxiliary equations, because it
|
|
|
|
|
is recursive, and this would in particular break dynamic_set_auxiliary_series.m */
|
2021-10-28 14:42:56 +02:00
|
|
|
|
neqs++;
|
|
|
|
|
|
2023-11-30 15:28:57 +01:00
|
|
|
|
cout << "PAC Model Consistent Expectation: added " << neqs
|
|
|
|
|
<< " auxiliary variables and equations for model " << name << "." << endl;
|
2019-02-15 12:52:46 +01:00
|
|
|
|
|
2021-10-28 14:42:56 +02:00
|
|
|
|
/* The growth correction term is not added to the definition of Z₁
|
|
|
|
|
because the latter is recursive. Rather put it at the level of the
|
|
|
|
|
substition of pac_expectation operator. */
|
2021-11-16 17:58:20 +01:00
|
|
|
|
pac_expectation_substitution[name] = AddPlus(AddVariable(mce_z1_symb_id), growth_correction_term);
|
2021-08-31 17:54:20 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-01-30 16:33:16 +01:00
|
|
|
|
void
|
2023-11-30 15:28:57 +01:00
|
|
|
|
DynamicModel::computePacBackwardExpectationSubstitution(
|
|
|
|
|
const string& name, const vector<int>& lhs, int max_lag, const string& aux_model_type,
|
|
|
|
|
expr_t growth_correction_term, string auxname, map<string, int>& pac_aux_var_symb_ids,
|
|
|
|
|
map<string, vector<int>>& pac_aux_param_symb_ids,
|
|
|
|
|
map<string, expr_t>& pac_expectation_substitution)
|
2018-01-30 16:33:16 +01:00
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
auto create_aux_param = [&](const string& param_name) {
|
2021-11-09 14:34:21 +01:00
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
return symbol_table.addSymbol(param_name, SymbolType::parameter);
|
|
|
|
|
}
|
|
|
|
|
catch (SymbolTable::AlreadyDeclaredException)
|
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
cerr << "ERROR: the variable/parameter '" << param_name
|
|
|
|
|
<< "' conflicts with some auxiliary parameter that will be generated for the '" << name
|
|
|
|
|
<< "' PAC model. Please rename that parameter." << endl;
|
2021-11-09 14:34:21 +01:00
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2021-10-28 14:42:56 +02:00
|
|
|
|
expr_t subExpr = Zero;
|
2021-11-17 18:12:29 +01:00
|
|
|
|
if (aux_model_type == "var")
|
2021-10-28 14:42:56 +02:00
|
|
|
|
{
|
2021-11-17 18:12:29 +01:00
|
|
|
|
/* If the auxiliary model is a VAR, add a parameter corresponding
|
|
|
|
|
to the constant. */
|
|
|
|
|
int new_param_symb_id = create_aux_param("h_" + name + "_constant");
|
|
|
|
|
pac_aux_param_symb_ids[name].push_back(new_param_symb_id);
|
|
|
|
|
subExpr = AddPlus(subExpr, AddVariable(new_param_symb_id));
|
2018-01-30 16:33:16 +01:00
|
|
|
|
}
|
2021-11-17 18:12:29 +01:00
|
|
|
|
for (int i = 1; i < max_lag + 1; i++)
|
|
|
|
|
for (auto lhsit : lhs)
|
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
int new_param_symb_id = create_aux_param("h_" + name + "_var_" + symbol_table.getName(lhsit)
|
2021-11-17 18:12:29 +01:00
|
|
|
|
+ "_lag_" + to_string(i));
|
|
|
|
|
pac_aux_param_symb_ids[name].push_back(new_param_symb_id);
|
2023-11-30 15:28:57 +01:00
|
|
|
|
subExpr
|
|
|
|
|
= AddPlus(subExpr, AddTimes(AddVariable(new_param_symb_id), AddVariable(lhsit, -i)));
|
2021-11-17 18:12:29 +01:00
|
|
|
|
}
|
2021-10-28 14:42:56 +02:00
|
|
|
|
|
2021-11-17 12:15:49 +01:00
|
|
|
|
subExpr = AddPlus(subExpr, growth_correction_term);
|
2021-11-16 17:58:20 +01:00
|
|
|
|
|
2021-11-18 17:08:08 +01:00
|
|
|
|
if (auxname.empty())
|
|
|
|
|
auxname = "pac_expectation_" + name;
|
|
|
|
|
int expect_var_id = symbol_table.addPacExpectationAuxiliaryVar(auxname, subExpr);
|
2021-11-17 12:15:49 +01:00
|
|
|
|
expr_t neweq = AddEqual(AddVariable(expect_var_id), subExpr);
|
2022-05-05 18:39:27 +02:00
|
|
|
|
addEquation(neweq, nullopt);
|
2021-11-17 12:15:49 +01:00
|
|
|
|
addAuxEquation(neweq);
|
2021-11-16 17:58:20 +01:00
|
|
|
|
pac_aux_var_symb_ids[name] = expect_var_id;
|
|
|
|
|
pac_expectation_substitution[name] = AddVariable(expect_var_id);
|
2019-02-20 12:59:55 +01:00
|
|
|
|
}
|
|
|
|
|
|
2021-10-26 18:06:26 +02:00
|
|
|
|
void
|
2023-11-30 15:28:57 +01:00
|
|
|
|
DynamicModel::computePacBackwardExpectationSubstitutionWithComponents(
|
|
|
|
|
const string& name, const vector<int>& lhs, int max_lag, const string& aux_model_type,
|
|
|
|
|
vector<PacModelTable::target_component_t>& pac_target_components,
|
|
|
|
|
map<string, expr_t>& pac_expectation_substitution)
|
2021-10-26 18:06:26 +02:00
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
auto create_aux_param = [&](const string& param_name) {
|
2021-10-26 18:06:26 +02:00
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
return symbol_table.addSymbol(param_name, SymbolType::parameter);
|
|
|
|
|
}
|
|
|
|
|
catch (SymbolTable::AlreadyDeclaredException)
|
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
cerr << "ERROR: the variable/parameter '" << param_name
|
|
|
|
|
<< "' conflicts with some auxiliary parameter that will be generated for the '" << name
|
|
|
|
|
<< "' PAC model. Please rename that parameter." << endl;
|
2021-10-26 18:06:26 +02:00
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
expr_t substexpr = Zero;
|
|
|
|
|
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (int component_idx {1};
|
|
|
|
|
auto& [component, growth, auxname, kind, coeff, growth_neutrality_param, h_indices,
|
|
|
|
|
original_growth, growth_info] : pac_target_components)
|
2021-10-26 18:06:26 +02:00
|
|
|
|
{
|
|
|
|
|
string name_component = name + "_component" + to_string(component_idx);
|
|
|
|
|
|
|
|
|
|
// Create the linear combination of the variables from the auxiliary model
|
|
|
|
|
expr_t auxdef = Zero;
|
|
|
|
|
if (aux_model_type == "var")
|
|
|
|
|
{
|
|
|
|
|
/* If the auxiliary model is a VAR, add a parameter corresponding
|
|
|
|
|
to the constant. */
|
|
|
|
|
int new_param_symb_id = create_aux_param("h_" + name_component + "_constant");
|
|
|
|
|
h_indices.push_back(new_param_symb_id);
|
|
|
|
|
auxdef = AddPlus(auxdef, AddVariable(new_param_symb_id));
|
|
|
|
|
}
|
|
|
|
|
for (int i = 1; i < max_lag + 1; i++)
|
|
|
|
|
for (auto lhsit : lhs)
|
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
int new_param_symb_id
|
|
|
|
|
= create_aux_param("h_" + name_component + "_var_" + symbol_table.getName(lhsit)
|
|
|
|
|
+ "_lag_" + to_string(i));
|
2021-10-26 18:06:26 +02:00
|
|
|
|
h_indices.push_back(new_param_symb_id);
|
2023-11-30 15:28:57 +01:00
|
|
|
|
auxdef
|
|
|
|
|
= AddPlus(auxdef, AddTimes(AddVariable(new_param_symb_id), AddVariable(lhsit, -i)));
|
2021-10-26 18:06:26 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If needed, add the growth neutrality correction for this component
|
|
|
|
|
if (growth)
|
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
growth_neutrality_param
|
|
|
|
|
= create_aux_param(name_component + "_pac_growth_neutrality_correction");
|
2021-10-26 18:06:26 +02:00
|
|
|
|
auxdef = AddPlus(auxdef, AddTimes(growth, AddVariable(growth_neutrality_param)));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
growth_neutrality_param = -1;
|
|
|
|
|
|
|
|
|
|
// Create the auxiliary variable for this component
|
|
|
|
|
int aux_id = symbol_table.addPacExpectationAuxiliaryVar(auxname, auxdef);
|
|
|
|
|
expr_t auxvar = AddVariable(aux_id);
|
|
|
|
|
|
|
|
|
|
// Add the equation defining the auxiliary variable for this component
|
|
|
|
|
expr_t neweq = AddEqual(auxvar, auxdef);
|
2022-05-05 18:39:27 +02:00
|
|
|
|
addEquation(neweq, nullopt);
|
2021-10-26 18:06:26 +02:00
|
|
|
|
addAuxEquation(neweq);
|
|
|
|
|
|
|
|
|
|
// Update the expression to be substituted for the pac_expectation operator
|
|
|
|
|
substexpr = AddPlus(substexpr, AddTimes(coeff, auxvar));
|
|
|
|
|
|
|
|
|
|
component_idx++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pac_expectation_substitution[name] = substexpr;
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-20 12:59:55 +01:00
|
|
|
|
void
|
2023-11-30 15:28:57 +01:00
|
|
|
|
DynamicModel::substitutePacExpectation(const map<string, expr_t>& pac_expectation_substitution,
|
|
|
|
|
const map<string, string>& pac_eq_name)
|
2019-02-20 12:59:55 +01:00
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (auto& [model_name, substexpr] : pac_expectation_substitution)
|
2021-10-27 18:17:14 +02:00
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
optional<int> eq {equation_tags.getEqnByTag("name", pac_eq_name.at(model_name))};
|
|
|
|
|
auto substeq = dynamic_cast<BinaryOpNode*>(
|
|
|
|
|
equations[eq.value()]->substitutePacExpectation(model_name, substexpr));
|
2021-10-27 18:17:14 +02:00
|
|
|
|
assert(substeq);
|
2023-01-04 16:44:52 +01:00
|
|
|
|
equations[eq.value()] = substeq;
|
2021-10-27 18:17:14 +02:00
|
|
|
|
}
|
2018-01-30 16:33:16 +01:00
|
|
|
|
}
|
|
|
|
|
|
2021-10-26 18:06:26 +02:00
|
|
|
|
void
|
2023-11-30 15:28:57 +01:00
|
|
|
|
DynamicModel::substitutePacTargetNonstationary(const string& pac_model_name, expr_t substexpr)
|
2021-10-26 18:06:26 +02:00
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (auto& eq : equations)
|
|
|
|
|
eq = dynamic_cast<BinaryOpNode*>(
|
|
|
|
|
eq->substitutePacTargetNonstationary(pac_model_name, substexpr));
|
2021-10-26 18:06:26 +02:00
|
|
|
|
}
|
|
|
|
|
|
2009-04-14 16:39:53 +02:00
|
|
|
|
void
|
2023-11-30 15:28:57 +01:00
|
|
|
|
DynamicModel::computingPass(int derivsOrder, int paramsDerivsOrder,
|
|
|
|
|
const eval_context_t& eval_context, bool no_tmp_terms, bool block,
|
|
|
|
|
bool use_dll)
|
2009-04-14 16:39:53 +02:00
|
|
|
|
{
|
2011-06-22 11:34:25 +02:00
|
|
|
|
initializeVariablesAndEquations();
|
2017-06-01 19:58:32 +02:00
|
|
|
|
|
2009-09-30 17:10:31 +02:00
|
|
|
|
// Prepare for derivation
|
|
|
|
|
computeDerivIDs();
|
|
|
|
|
|
|
|
|
|
// Computes dynamic jacobian columns, must be done after computeDerivIDs()
|
2022-10-07 14:37:25 +02:00
|
|
|
|
computeDynJacobianCols();
|
2009-04-14 16:39:53 +02:00
|
|
|
|
|
2022-01-21 14:31:29 +01:00
|
|
|
|
/* In both MATLAB and Julia, tensors for higher-order derivatives are stored
|
|
|
|
|
in matrices whose columns correspond to variable multi-indices. Since we
|
|
|
|
|
currently are limited to 32-bit signed integers (hence 31 bits) for matrix
|
|
|
|
|
indices, check that we will not overflow (see #89). Note that such a check
|
|
|
|
|
is not needed for parameter derivatives, since tensors for those are not
|
|
|
|
|
stored as matrices. This check cannot be done before since
|
2022-09-14 17:07:08 +02:00
|
|
|
|
getJacobianColsNbr() is not yet set.
|
|
|
|
|
We only do the check for the legacy representation, since the sparse
|
|
|
|
|
representation is not affected by this problem (TODO: thus the check can be
|
|
|
|
|
removed once the legacy representation is dropped). */
|
2023-11-30 15:28:57 +01:00
|
|
|
|
if (log2(getJacobianColsNbr(false)) * derivsOrder >= numeric_limits<int>::digits)
|
2022-01-21 14:31:29 +01:00
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
cerr << "ERROR: The derivatives matrix of the " << modelClassName()
|
|
|
|
|
<< " is too large. Please decrease the approximation order." << endl;
|
2022-01-21 14:31:29 +01:00
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-07 14:37:25 +02:00
|
|
|
|
// Compute derivatives w.r. to all endogenous, exogenous and exogenous deterministic
|
2009-04-20 12:48:54 +02:00
|
|
|
|
set<int> vars;
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (auto& [symb_lag, deriv_id] : deriv_id_table)
|
|
|
|
|
if (SymbolType type = symbol_table.getType(symb_lag.first); type == SymbolType::endogenous
|
|
|
|
|
|| type == SymbolType::exogenous
|
|
|
|
|
|| type == SymbolType::exogenousDet)
|
2023-02-28 15:33:33 +01:00
|
|
|
|
vars.insert(deriv_id);
|
2009-04-14 16:39:53 +02:00
|
|
|
|
|
|
|
|
|
// Launch computations
|
2022-09-14 17:48:33 +02:00
|
|
|
|
cout << "Computing " << modelClassName() << " derivatives (order " << derivsOrder << ")." << endl;
|
2009-04-20 12:48:54 +02:00
|
|
|
|
|
2018-11-22 14:32:40 +01:00
|
|
|
|
computeDerivatives(derivsOrder, vars);
|
2009-04-20 12:48:54 +02:00
|
|
|
|
|
2019-12-13 17:15:03 +01:00
|
|
|
|
if (derivsOrder > 1)
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (const auto& [indices, d2] : derivatives[2])
|
2020-01-20 17:22:32 +01:00
|
|
|
|
nonzero_hessian_eqs.insert(indices[0]);
|
2019-12-13 17:15:03 +01:00
|
|
|
|
|
2016-05-18 12:26:19 +02:00
|
|
|
|
if (paramsDerivsOrder > 0)
|
2009-04-20 15:58:15 +02:00
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
cout << "Computing " << modelClassName() << " derivatives w.r.t. parameters (order "
|
|
|
|
|
<< paramsDerivsOrder << ")." << endl;
|
2016-05-18 12:26:19 +02:00
|
|
|
|
computeParamsDerivatives(paramsDerivsOrder);
|
2009-04-20 15:58:15 +02:00
|
|
|
|
}
|
|
|
|
|
|
2022-09-13 15:43:13 +02:00
|
|
|
|
computeTemporaryTerms(!use_dll, no_tmp_terms);
|
2018-09-21 17:13:19 +02:00
|
|
|
|
|
2022-09-13 15:43:13 +02:00
|
|
|
|
if (paramsDerivsOrder > 0 && !no_tmp_terms)
|
|
|
|
|
computeParamsDerivativesTemporaryTerms();
|
2009-08-25 11:43:01 +02:00
|
|
|
|
|
2023-01-25 18:37:37 +01:00
|
|
|
|
/* Change block decomposition settings when the “block” keyword has not been
|
|
|
|
|
used and the model is purely backward/forward or static. This is meant to
|
|
|
|
|
be used with solve_algo={12,14}. */
|
2022-11-30 14:43:44 +01:00
|
|
|
|
if (!block && (max_endo_lag == 0 || max_endo_lead == 0))
|
|
|
|
|
{
|
|
|
|
|
time_recursive_block_decomposition = true;
|
2023-01-25 18:37:37 +01:00
|
|
|
|
mfs = 3; // Typically useful for equations of the form log(x)=RHS
|
2022-11-30 14:43:44 +01:00
|
|
|
|
}
|
2022-09-28 16:31:51 +02:00
|
|
|
|
computingPassBlock(eval_context, no_tmp_terms);
|
|
|
|
|
if (block_decomposed)
|
|
|
|
|
computeBlockDynJacobianCols();
|
|
|
|
|
if (!block_decomposed && block)
|
2018-09-25 15:56:52 +02:00
|
|
|
|
{
|
2022-09-13 15:43:13 +02:00
|
|
|
|
cerr << "ERROR: Block decomposition requested but failed." << endl;
|
|
|
|
|
exit(EXIT_FAILURE);
|
2018-09-25 15:56:52 +02:00
|
|
|
|
}
|
2017-03-06 16:34:08 +01:00
|
|
|
|
}
|
2015-12-18 15:17:32 +01:00
|
|
|
|
|
2017-03-06 16:34:08 +01:00
|
|
|
|
void
|
|
|
|
|
DynamicModel::computeXrefs()
|
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (int i {0}; auto& equation : equations)
|
2017-03-06 16:34:08 +01:00
|
|
|
|
{
|
|
|
|
|
ExprNode::EquationInfo ei;
|
2018-06-04 12:26:16 +02:00
|
|
|
|
equation->computeXrefs(ei);
|
2017-03-06 16:34:08 +01:00
|
|
|
|
xrefs[i++] = ei;
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (int i {0}; const auto& [eq, eqinfo] : xrefs)
|
2017-03-06 16:34:08 +01:00
|
|
|
|
{
|
2022-06-03 16:24:26 +02:00
|
|
|
|
computeRevXref(xref_param, eqinfo.param, i);
|
|
|
|
|
computeRevXref(xref_endo, eqinfo.endo, i);
|
|
|
|
|
computeRevXref(xref_exo, eqinfo.exo, i);
|
|
|
|
|
computeRevXref(xref_exo_det, eqinfo.exo_det, i);
|
|
|
|
|
i++;
|
2017-03-06 16:34:08 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2023-11-30 15:28:57 +01:00
|
|
|
|
DynamicModel::computeRevXref(map<pair<int, int>, set<int>>& xrefset,
|
|
|
|
|
const set<pair<int, int>>& eiref, int eqn)
|
2017-03-06 16:34:08 +01:00
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (const auto& it : eiref)
|
2017-03-06 16:34:08 +01:00
|
|
|
|
{
|
|
|
|
|
set<int> eq;
|
2022-05-04 16:01:34 +02:00
|
|
|
|
if (xrefset.contains(it))
|
2018-06-04 12:26:16 +02:00
|
|
|
|
eq = xrefset[it];
|
2017-03-06 16:34:08 +01:00
|
|
|
|
eq.insert(eqn);
|
2018-06-04 12:26:16 +02:00
|
|
|
|
xrefset[it] = eq;
|
2017-03-06 16:34:08 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2023-11-30 15:28:57 +01:00
|
|
|
|
DynamicModel::writeXrefs(ostream& output) const
|
2017-03-06 16:34:08 +01:00
|
|
|
|
{
|
|
|
|
|
output << "M_.xref1.param = cell(1, M_.eq_nbr);" << endl
|
|
|
|
|
<< "M_.xref1.endo = cell(1, M_.eq_nbr);" << endl
|
|
|
|
|
<< "M_.xref1.exo = cell(1, M_.eq_nbr);" << endl
|
|
|
|
|
<< "M_.xref1.exo_det = cell(1, M_.eq_nbr);" << endl;
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (int i {1}; const auto& [eq, eqinfo] : xrefs)
|
2017-03-06 16:34:08 +01:00
|
|
|
|
{
|
|
|
|
|
output << "M_.xref1.param{" << i << "} = [ ";
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (const auto& [id, lag] : eqinfo.param)
|
2022-06-03 16:24:26 +02:00
|
|
|
|
output << symbol_table.getTypeSpecificID(id) + 1 << " ";
|
2017-03-06 16:34:08 +01:00
|
|
|
|
output << "];" << endl;
|
|
|
|
|
|
|
|
|
|
output << "M_.xref1.endo{" << i << "} = [ ";
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (const auto& [id, lag] : eqinfo.endo)
|
|
|
|
|
output << "struct('id', " << symbol_table.getTypeSpecificID(id) + 1 << ", 'shift', " << lag
|
|
|
|
|
<< ");";
|
2017-03-06 16:34:08 +01:00
|
|
|
|
output << "];" << endl;
|
|
|
|
|
|
|
|
|
|
output << "M_.xref1.exo{" << i << "} = [ ";
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (const auto& [id, lag] : eqinfo.exo)
|
|
|
|
|
output << "struct('id', " << symbol_table.getTypeSpecificID(id) + 1 << ", 'shift', " << lag
|
|
|
|
|
<< ");";
|
2017-03-06 16:34:08 +01:00
|
|
|
|
output << "];" << endl;
|
|
|
|
|
|
|
|
|
|
output << "M_.xref1.exo_det{" << i << "} = [ ";
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (const auto& [id, lag] : eqinfo.exo_det)
|
|
|
|
|
output << "struct('id', " << symbol_table.getTypeSpecificID(id) + 1 << ", 'shift', " << lag
|
|
|
|
|
<< ");";
|
2017-03-06 16:34:08 +01:00
|
|
|
|
output << "];" << endl;
|
2022-06-03 16:24:26 +02:00
|
|
|
|
|
|
|
|
|
i++;
|
2017-03-06 16:34:08 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
output << "M_.xref2.param = cell(1, M_.param_nbr);" << endl
|
|
|
|
|
<< "M_.xref2.endo = cell(1, M_.endo_nbr);" << endl
|
|
|
|
|
<< "M_.xref2.exo = cell(1, M_.exo_nbr);" << endl
|
|
|
|
|
<< "M_.xref2.exo_det = cell(1, M_.exo_det_nbr);" << endl;
|
|
|
|
|
writeRevXrefs(output, xref_param, "param");
|
|
|
|
|
writeRevXrefs(output, xref_endo, "endo");
|
|
|
|
|
writeRevXrefs(output, xref_exo, "exo");
|
|
|
|
|
writeRevXrefs(output, xref_exo_det, "exo_det");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2023-11-30 15:28:57 +01:00
|
|
|
|
DynamicModel::writeRevXrefs(ostream& output, const map<pair<int, int>, set<int>>& xrefmap,
|
|
|
|
|
const string& type) const
|
2017-03-06 16:34:08 +01:00
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (int last_tsid {-1}; const auto& [key, eqs] : xrefmap)
|
2017-03-06 16:34:08 +01:00
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
auto& [id, lag] = key;
|
2022-06-03 16:24:26 +02:00
|
|
|
|
int tsid = symbol_table.getTypeSpecificID(id) + 1;
|
2017-03-06 16:34:08 +01:00
|
|
|
|
output << "M_.xref2." << type << "{" << tsid << "} = [ ";
|
|
|
|
|
if (last_tsid == tsid)
|
|
|
|
|
output << "M_.xref2." << type << "{" << tsid << "}; ";
|
|
|
|
|
else
|
|
|
|
|
last_tsid = tsid;
|
|
|
|
|
|
2022-06-03 16:24:26 +02:00
|
|
|
|
for (int eq : eqs)
|
2017-03-06 16:34:08 +01:00
|
|
|
|
if (type == "param")
|
2022-06-03 16:24:26 +02:00
|
|
|
|
output << eq + 1 << " ";
|
2017-03-06 16:34:08 +01:00
|
|
|
|
else
|
2023-11-30 15:28:57 +01:00
|
|
|
|
output << "struct('shift', " << lag << ", 'eq', " << eq + 1 << ");";
|
2017-03-06 16:34:08 +01:00
|
|
|
|
output << "];" << endl;
|
|
|
|
|
}
|
2009-04-14 16:39:53 +02:00
|
|
|
|
}
|
|
|
|
|
|
2020-04-24 12:29:02 +02:00
|
|
|
|
map<tuple<int, int, int>, DynamicModel::BlockDerivativeType>
|
|
|
|
|
DynamicModel::determineBlockDerivativesType(int blk)
|
|
|
|
|
{
|
|
|
|
|
map<tuple<int, int, int>, BlockDerivativeType> derivType;
|
|
|
|
|
int size = blocks[blk].size;
|
|
|
|
|
int nb_recursive = blocks[blk].getRecursiveSize();
|
2023-01-17 14:41:02 +01:00
|
|
|
|
for (int lag {time_recursive_block_decomposition ? 0 : -blocks[blk].max_endo_lag};
|
2023-11-30 15:28:57 +01:00
|
|
|
|
lag <= (time_recursive_block_decomposition ? 0 : blocks[blk].max_endo_lead); lag++)
|
2020-04-24 12:29:02 +02:00
|
|
|
|
for (int eq = 0; eq < size; eq++)
|
2020-04-28 18:00:26 +02:00
|
|
|
|
{
|
|
|
|
|
set<pair<int, int>> endos_and_lags;
|
|
|
|
|
int eq_orig = getBlockEquationID(blk, eq);
|
|
|
|
|
equations[eq_orig]->collectEndogenous(endos_and_lags);
|
|
|
|
|
for (int var = 0; var < size; var++)
|
2023-11-30 15:28:57 +01:00
|
|
|
|
if (int var_orig = getBlockVariableID(blk, var); endos_and_lags.contains({var_orig, lag}))
|
2020-04-28 18:00:26 +02:00
|
|
|
|
{
|
2020-05-20 11:49:32 +02:00
|
|
|
|
if (getBlockEquationType(blk, eq) == EquationType::evaluateRenormalized
|
2020-04-28 18:00:26 +02:00
|
|
|
|
&& eq < nb_recursive)
|
|
|
|
|
/* It’s a normalized recursive equation, we have to recompute
|
|
|
|
|
the derivative using the chain rule */
|
2023-11-30 15:28:57 +01:00
|
|
|
|
derivType[{lag, eq, var}] = BlockDerivativeType::normalizedChainRule;
|
|
|
|
|
else if (!derivType.contains({lag, eq, var}))
|
|
|
|
|
derivType[{lag, eq, var}] = BlockDerivativeType::standard;
|
2020-04-28 18:00:26 +02:00
|
|
|
|
|
|
|
|
|
if (var < nb_recursive)
|
|
|
|
|
for (int feedback_var = nb_recursive; feedback_var < size; feedback_var++)
|
2023-11-30 15:28:57 +01:00
|
|
|
|
if (derivType.contains({lag, var, feedback_var}))
|
2020-04-28 18:00:26 +02:00
|
|
|
|
/* A new derivative needs to be computed using the chain rule
|
|
|
|
|
(a feedback variable appears in the recursive equation
|
|
|
|
|
defining the current variable) */
|
2023-11-30 15:28:57 +01:00
|
|
|
|
derivType[{lag, eq, feedback_var}] = BlockDerivativeType::chainRule;
|
2020-04-28 18:00:26 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
2020-04-24 12:29:02 +02:00
|
|
|
|
return derivType;
|
2009-12-16 14:21:31 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2020-03-19 17:46:10 +01:00
|
|
|
|
DynamicModel::computeChainRuleJacobian()
|
2009-12-16 14:21:31 +01:00
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
size_t nb_blocks {blocks.size()};
|
2022-09-28 16:31:51 +02:00
|
|
|
|
|
2020-03-19 17:46:10 +01:00
|
|
|
|
blocks_derivatives.resize(nb_blocks);
|
2022-09-28 19:18:15 +02:00
|
|
|
|
blocks_jacobian_sparse_column_major_order.resize(nb_blocks);
|
|
|
|
|
blocks_jacobian_sparse_colptr.resize(nb_blocks);
|
2022-09-28 16:31:51 +02:00
|
|
|
|
|
|
|
|
|
for (int blk {0}; blk < static_cast<int>(nb_blocks); blk++)
|
2009-12-16 14:21:31 +01:00
|
|
|
|
{
|
2020-04-24 12:29:02 +02:00
|
|
|
|
int nb_recursives = blocks[blk].getRecursiveSize();
|
2023-11-30 15:28:57 +01:00
|
|
|
|
int mfs_size {blocks[blk].mfs_size};
|
|
|
|
|
BlockSimulationType simulation_type {blocks[blk].simulation_type};
|
2020-04-24 12:29:02 +02:00
|
|
|
|
|
|
|
|
|
// Create a map from recursive vars to their defining (normalized) equation
|
2023-11-30 15:28:57 +01:00
|
|
|
|
map<int, BinaryOpNode*> recursive_vars;
|
2020-04-24 12:29:02 +02:00
|
|
|
|
for (int i = 0; i < nb_recursives; i++)
|
2009-12-16 14:21:31 +01:00
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
int deriv_id = getDerivID(
|
|
|
|
|
symbol_table.getID(SymbolType::endogenous, getBlockVariableID(blk, i)), 0);
|
2020-05-20 11:49:32 +02:00
|
|
|
|
if (getBlockEquationType(blk, i) == EquationType::evaluateRenormalized)
|
2020-04-24 12:29:02 +02:00
|
|
|
|
recursive_vars[deriv_id] = getBlockEquationRenormalizedExpr(blk, i);
|
2010-07-23 11:20:24 +02:00
|
|
|
|
else
|
2020-04-24 12:29:02 +02:00
|
|
|
|
recursive_vars[deriv_id] = getBlockEquationExpr(blk, i);
|
2009-12-16 14:21:31 +01:00
|
|
|
|
}
|
2020-04-24 12:29:02 +02:00
|
|
|
|
|
|
|
|
|
// Compute the block derivatives
|
2023-04-05 14:16:22 +02:00
|
|
|
|
unordered_map<expr_t, set<int>> non_null_chain_rule_derivatives;
|
|
|
|
|
unordered_map<expr_t, map<int, expr_t>> chain_rule_deriv_cache;
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (const auto& [indices, derivType] : determineBlockDerivativesType(blk))
|
2009-12-16 14:21:31 +01:00
|
|
|
|
{
|
2020-04-24 12:29:02 +02:00
|
|
|
|
auto [lag, eq, var] = indices;
|
|
|
|
|
int eq_orig = getBlockEquationID(blk, eq), var_orig = getBlockVariableID(blk, var);
|
|
|
|
|
int deriv_id = getDerivID(symbol_table.getID(SymbolType::endogenous, var_orig), lag);
|
2023-11-30 15:28:57 +01:00
|
|
|
|
expr_t d {nullptr};
|
2020-04-24 12:29:02 +02:00
|
|
|
|
switch (derivType)
|
2009-12-16 14:21:31 +01:00
|
|
|
|
{
|
2020-04-24 12:29:02 +02:00
|
|
|
|
case BlockDerivativeType::standard:
|
2023-11-30 15:28:57 +01:00
|
|
|
|
if (auto it = derivatives[1].find({eq_orig, deriv_id}); it != derivatives[1].end())
|
2022-06-13 16:24:33 +02:00
|
|
|
|
d = it->second;
|
|
|
|
|
else
|
|
|
|
|
d = Zero;
|
2020-04-24 12:29:02 +02:00
|
|
|
|
break;
|
|
|
|
|
case BlockDerivativeType::chainRule:
|
2023-11-30 15:28:57 +01:00
|
|
|
|
d = equations[eq_orig]->getChainRuleDerivative(deriv_id, recursive_vars,
|
|
|
|
|
non_null_chain_rule_derivatives,
|
|
|
|
|
chain_rule_deriv_cache);
|
2020-04-24 12:29:02 +02:00
|
|
|
|
break;
|
|
|
|
|
case BlockDerivativeType::normalizedChainRule:
|
2023-11-30 15:28:57 +01:00
|
|
|
|
d = equation_type_and_normalized_equation[eq_orig].second->getChainRuleDerivative(
|
|
|
|
|
deriv_id, recursive_vars, non_null_chain_rule_derivatives,
|
|
|
|
|
chain_rule_deriv_cache);
|
2020-04-24 12:29:02 +02:00
|
|
|
|
break;
|
2009-12-16 14:21:31 +01:00
|
|
|
|
}
|
2020-04-24 12:29:02 +02:00
|
|
|
|
|
2023-11-03 11:52:54 +01:00
|
|
|
|
if (d != Zero && eq >= nb_recursives)
|
2023-11-30 15:28:57 +01:00
|
|
|
|
blocks_derivatives[blk][{eq, var, lag}] = d;
|
2009-12-16 14:21:31 +01:00
|
|
|
|
}
|
2022-09-28 19:18:15 +02:00
|
|
|
|
|
|
|
|
|
// Compute the sparse representation of the Jacobian
|
|
|
|
|
if (simulation_type != BlockSimulationType::evaluateForward
|
|
|
|
|
&& simulation_type != BlockSimulationType::evaluateBackward)
|
|
|
|
|
{
|
|
|
|
|
const bool one_boundary {simulation_type == BlockSimulationType::solveBackwardSimple
|
|
|
|
|
|| simulation_type == BlockSimulationType::solveForwardSimple
|
|
|
|
|
|| simulation_type == BlockSimulationType::solveBackwardComplete
|
|
|
|
|
|| simulation_type == BlockSimulationType::solveForwardComplete};
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (const auto& [indices, d1] : blocks_derivatives[blk])
|
2022-09-28 19:18:15 +02:00
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
auto& [eq, var, lag] {indices};
|
2023-11-03 11:52:54 +01:00
|
|
|
|
assert(lag >= -1 && lag <= 1 && eq >= nb_recursives);
|
|
|
|
|
if (var >= nb_recursives && !(one_boundary && lag != 0))
|
2023-11-30 15:28:57 +01:00
|
|
|
|
blocks_jacobian_sparse_column_major_order[blk].try_emplace(
|
|
|
|
|
{eq - nb_recursives,
|
|
|
|
|
var - nb_recursives + static_cast<int>(!one_boundary) * (lag + 1) * mfs_size},
|
|
|
|
|
d1);
|
2022-09-28 19:18:15 +02:00
|
|
|
|
}
|
2023-11-30 15:28:57 +01:00
|
|
|
|
blocks_jacobian_sparse_colptr[blk] = computeCSCColPtr(
|
|
|
|
|
blocks_jacobian_sparse_column_major_order[blk], (one_boundary ? 1 : 3) * mfs_size);
|
2022-09-28 19:18:15 +02:00
|
|
|
|
}
|
2009-12-16 14:21:31 +01:00
|
|
|
|
}
|
2022-09-28 16:31:51 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
DynamicModel::computeBlockDynJacobianCols()
|
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
size_t nb_blocks {blocks.size()};
|
2023-10-25 14:38:43 +02:00
|
|
|
|
// Structure used for lexicographic ordering over (lag, var ID)
|
|
|
|
|
vector<set<pair<int, int>>> dynamic_endo(nb_blocks);
|
2020-05-06 14:02:58 +02:00
|
|
|
|
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (auto& [indices, d1] : derivatives[1])
|
2009-12-16 14:21:31 +01:00
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
auto [eq_orig, deriv_id] {vectorToTuple<2>(indices)};
|
|
|
|
|
int block_eq {eq2block[eq_orig]};
|
|
|
|
|
int var {getTypeSpecificIDByDerivID(deriv_id)};
|
|
|
|
|
int lag {getLagByDerivID(deriv_id)};
|
|
|
|
|
if (getTypeByDerivID(deriv_id) == SymbolType::endogenous && block_eq == endo2block[var])
|
2023-10-25 14:38:43 +02:00
|
|
|
|
dynamic_endo[block_eq].emplace(lag, getBlockInitialVariableID(block_eq, var));
|
2009-12-16 14:21:31 +01:00
|
|
|
|
}
|
2020-05-06 14:02:58 +02:00
|
|
|
|
|
|
|
|
|
// Compute Jacobian column indices
|
|
|
|
|
blocks_jacob_cols_endo.resize(nb_blocks);
|
2022-09-28 16:31:51 +02:00
|
|
|
|
for (size_t blk {0}; blk < nb_blocks; blk++)
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (int index {0}; auto [lag, var] : dynamic_endo[blk])
|
|
|
|
|
blocks_jacob_cols_endo[blk][{var, lag}] = index++;
|
2009-12-16 14:21:31 +01:00
|
|
|
|
}
|
|
|
|
|
|
2009-04-14 16:39:53 +02:00
|
|
|
|
void
|
2023-11-30 15:28:57 +01:00
|
|
|
|
DynamicModel::writeDynamicFile(const string& basename, bool use_dll, const string& mexext,
|
|
|
|
|
const filesystem::path& matlabroot, bool julia) const
|
2009-12-16 18:13:23 +01:00
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
filesystem::path model_dir {basename};
|
2020-06-23 15:13:04 +02:00
|
|
|
|
model_dir /= "model";
|
|
|
|
|
if (use_dll)
|
2022-10-05 17:45:18 +02:00
|
|
|
|
{
|
|
|
|
|
create_directories(model_dir / "src" / "sparse");
|
|
|
|
|
if (block_decomposed)
|
|
|
|
|
create_directories(model_dir / "src" / "sparse" / "block");
|
|
|
|
|
}
|
2022-09-20 18:28:30 +02:00
|
|
|
|
if (julia)
|
|
|
|
|
create_directories(model_dir / "julia");
|
|
|
|
|
else
|
2022-10-11 15:59:56 +02:00
|
|
|
|
{
|
|
|
|
|
auto plusfolder {packageDir(basename)};
|
|
|
|
|
/* The following is not a duplicate of the same call from
|
|
|
|
|
ModFile::writeMOutput(), because of planner_objective which needs its
|
|
|
|
|
+objective subdirectory */
|
|
|
|
|
create_directories(plusfolder);
|
2022-09-30 17:26:43 +02:00
|
|
|
|
|
|
|
|
|
auto sparsefolder {plusfolder / "+sparse"};
|
|
|
|
|
create_directories(sparsefolder);
|
|
|
|
|
if (block_decomposed)
|
|
|
|
|
create_directories(sparsefolder / "+block");
|
2022-11-09 14:46:02 +01:00
|
|
|
|
|
|
|
|
|
create_directories(plusfolder / "+debug");
|
2022-10-11 15:59:56 +02:00
|
|
|
|
}
|
2023-01-09 13:35:49 +01:00
|
|
|
|
create_directories(model_dir / "bytecode" / "block");
|
2020-06-23 15:13:04 +02:00
|
|
|
|
|
2022-09-20 18:28:30 +02:00
|
|
|
|
// Legacy representation
|
2023-01-13 12:05:04 +01:00
|
|
|
|
if (use_dll)
|
2023-03-20 17:54:32 +01:00
|
|
|
|
writeModelCFile<true>(basename, mexext, matlabroot);
|
2023-01-13 12:05:04 +01:00
|
|
|
|
else if (!julia) // M-files
|
|
|
|
|
writeDynamicMFile(basename);
|
|
|
|
|
// The legacy representation is no longer produced for Julia
|
|
|
|
|
|
2023-09-27 14:49:06 +02:00
|
|
|
|
/* Discretionary optimal policy models don’t have as many variables as
|
|
|
|
|
equations; bytecode does not support that case */
|
|
|
|
|
if (static_cast<int>(equations.size()) == symbol_table.endo_nbr())
|
|
|
|
|
writeDynamicBytecode(basename);
|
2023-01-09 13:35:49 +01:00
|
|
|
|
if (block_decomposed)
|
|
|
|
|
writeDynamicBlockBytecode(basename);
|
2020-06-23 15:13:04 +02:00
|
|
|
|
|
2022-09-20 18:28:30 +02:00
|
|
|
|
// Sparse representation
|
2022-09-30 17:26:43 +02:00
|
|
|
|
if (use_dll)
|
2023-03-20 17:54:32 +01:00
|
|
|
|
writeSparseModelCFiles<true>(basename, mexext, matlabroot);
|
2022-09-30 17:26:43 +02:00
|
|
|
|
else if (julia)
|
2022-09-20 18:28:30 +02:00
|
|
|
|
writeSparseModelJuliaFiles<true>(basename);
|
2022-09-30 17:26:43 +02:00
|
|
|
|
else // MATLAB/Octave
|
|
|
|
|
writeSparseModelMFiles<true>(basename);
|
2022-09-20 18:28:30 +02:00
|
|
|
|
|
2023-03-28 16:37:05 +02:00
|
|
|
|
writeSetAuxiliaryVariablesFile<true>(basename, julia);
|
2022-11-09 14:46:02 +01:00
|
|
|
|
|
|
|
|
|
// Support for model debugging
|
|
|
|
|
if (!julia)
|
|
|
|
|
writeDebugModelMFiles<true>(basename);
|
2018-03-02 17:50:35 +01:00
|
|
|
|
}
|
|
|
|
|
|
2020-01-06 18:26:35 +01:00
|
|
|
|
void
|
|
|
|
|
DynamicModel::clearEquations()
|
|
|
|
|
{
|
|
|
|
|
equations.clear();
|
|
|
|
|
equations_lineno.clear();
|
|
|
|
|
equation_tags.clear();
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-21 18:40:57 +01:00
|
|
|
|
void
|
2023-11-30 15:28:57 +01:00
|
|
|
|
DynamicModel::replaceMyEquations(DynamicModel& dynamic_model) const
|
2011-03-21 18:40:57 +01:00
|
|
|
|
{
|
2020-01-06 18:26:35 +01:00
|
|
|
|
dynamic_model.clearEquations();
|
|
|
|
|
|
2014-01-27 16:41:43 +01:00
|
|
|
|
for (size_t i = 0; i < equations.size(); i++)
|
2020-01-06 18:26:35 +01:00
|
|
|
|
dynamic_model.addEquation(equations[i]->clone(dynamic_model), equations_lineno[i]);
|
|
|
|
|
|
|
|
|
|
dynamic_model.equation_tags = equation_tags;
|
2011-03-21 18:40:57 +01:00
|
|
|
|
}
|
|
|
|
|
|
Ramsey: write derivatives of static model w.r.t. Lagrange multipliers in a new file
The computing of the Ramsey steady state relies on the fact that Lagrange
multipliers appear linearly in the system to be solved. Instead of directly
solving for the Lagrange multipliers along with the other variables,
dyn_ramsey_static.m reduces the size of the problem by always computing the
value of the multipliers that minimizes the residuals, given the other
variables (using a minimum norm solution, easy to compute because of the
linearity property). That function thus needs the derivatives of the optimality
FOCs with respect to the multipliers. The problem is that, when multipliers
appear in an auxiliary variable related to a lead/lag, then those derivatives
need to be retrieved by a chain rule derivation, which cannot be easily done
with the regular static file.
This commit implements the creation of a new file,
ramsey_multipliers_static_g1.{m,mex}, that provides exactly the needed
derivatives w.r.t. Lagrange multipliers through chain rule derivation.
Ref. dynare#633, dynare#1119, dynare#1133
2023-03-24 18:58:12 +01:00
|
|
|
|
int
|
2023-11-30 15:28:57 +01:00
|
|
|
|
DynamicModel::computeRamseyPolicyFOCs(const StaticModel& static_model)
|
2011-03-21 18:40:57 +01:00
|
|
|
|
{
|
Ramsey: write derivatives of static model w.r.t. Lagrange multipliers in a new file
The computing of the Ramsey steady state relies on the fact that Lagrange
multipliers appear linearly in the system to be solved. Instead of directly
solving for the Lagrange multipliers along with the other variables,
dyn_ramsey_static.m reduces the size of the problem by always computing the
value of the multipliers that minimizes the residuals, given the other
variables (using a minimum norm solution, easy to compute because of the
linearity property). That function thus needs the derivatives of the optimality
FOCs with respect to the multipliers. The problem is that, when multipliers
appear in an auxiliary variable related to a lead/lag, then those derivatives
need to be retrieved by a chain rule derivation, which cannot be easily done
with the regular static file.
This commit implements the creation of a new file,
ramsey_multipliers_static_g1.{m,mex}, that provides exactly the needed
derivatives w.r.t. Lagrange multipliers through chain rule derivation.
Ref. dynare#633, dynare#1119, dynare#1133
2023-03-24 18:58:12 +01:00
|
|
|
|
cout << "Ramsey Problem: added " << equations.size() << " multipliers." << endl;
|
|
|
|
|
|
2011-03-21 18:40:57 +01:00
|
|
|
|
// Add aux LM to constraints in equations
|
2011-05-25 06:43:59 +02:00
|
|
|
|
// equation[i]->lhs = rhs becomes equation[i]->MULT_(i+1)*(lhs-rhs) = 0
|
Ramsey: write derivatives of static model w.r.t. Lagrange multipliers in a new file
The computing of the Ramsey steady state relies on the fact that Lagrange
multipliers appear linearly in the system to be solved. Instead of directly
solving for the Lagrange multipliers along with the other variables,
dyn_ramsey_static.m reduces the size of the problem by always computing the
value of the multipliers that minimizes the residuals, given the other
variables (using a minimum norm solution, easy to compute because of the
linearity property). That function thus needs the derivatives of the optimality
FOCs with respect to the multipliers. The problem is that, when multipliers
appear in an auxiliary variable related to a lead/lag, then those derivatives
need to be retrieved by a chain rule derivation, which cannot be easily done
with the regular static file.
This commit implements the creation of a new file,
ramsey_multipliers_static_g1.{m,mex}, that provides exactly the needed
derivatives w.r.t. Lagrange multipliers through chain rule derivation.
Ref. dynare#633, dynare#1119, dynare#1133
2023-03-24 18:58:12 +01:00
|
|
|
|
for (int i {0}; i < static_cast<int>(equations.size()); i++)
|
2011-03-21 18:40:57 +01:00
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
auto substeq = dynamic_cast<BinaryOpNode*>(equations[i]->addMultipliersToConstraints(i));
|
2019-12-16 19:42:59 +01:00
|
|
|
|
assert(substeq);
|
2011-03-21 18:40:57 +01:00
|
|
|
|
equations[i] = substeq;
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-06 18:26:35 +01:00
|
|
|
|
// Add Planner Objective to equations so that it appears in Lagrangian
|
2011-03-21 18:40:57 +01:00
|
|
|
|
assert(static_model.equations.size() == 1);
|
2022-05-05 18:39:27 +02:00
|
|
|
|
addEquation(static_model.equations[0]->clone(*this), nullopt);
|
2011-03-21 18:40:57 +01:00
|
|
|
|
|
|
|
|
|
// Get max endo lead and max endo lag
|
2018-06-04 14:17:36 +02:00
|
|
|
|
set<pair<int, int>> dynvars;
|
2011-03-21 18:40:57 +01:00
|
|
|
|
int max_eq_lead = 0;
|
|
|
|
|
int max_eq_lag = 0;
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (auto& equation : equations)
|
2018-07-17 18:34:07 +02:00
|
|
|
|
equation->collectDynamicVariables(SymbolType::endogenous, dynvars);
|
2011-03-21 18:40:57 +01:00
|
|
|
|
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (const auto& [symb_id, lag] : dynvars)
|
2011-03-21 18:40:57 +01:00
|
|
|
|
{
|
2022-06-02 10:50:21 +02:00
|
|
|
|
max_eq_lead = max(lag, max_eq_lead);
|
|
|
|
|
max_eq_lag = max(-lag, max_eq_lag);
|
2011-03-21 18:40:57 +01:00
|
|
|
|
}
|
|
|
|
|
|
2011-03-29 18:18:32 +02:00
|
|
|
|
// Get Discount Factor
|
2011-05-24 15:34:03 +02:00
|
|
|
|
assert(symbol_table.exists("optimal_policy_discount_factor"));
|
|
|
|
|
int symb_id = symbol_table.getID("optimal_policy_discount_factor");
|
2018-07-17 18:34:07 +02:00
|
|
|
|
assert(symbol_table.getType(symb_id) == SymbolType::parameter);
|
2011-03-29 18:18:32 +02:00
|
|
|
|
expr_t discount_factor_node = AddVariable(symb_id, 0);
|
|
|
|
|
|
2011-03-21 18:40:57 +01:00
|
|
|
|
// Create (modified) Lagrangian (so that we can take the derivative once at time t)
|
|
|
|
|
expr_t lagrangian = Zero;
|
Ramsey: write derivatives of static model w.r.t. Lagrange multipliers in a new file
The computing of the Ramsey steady state relies on the fact that Lagrange
multipliers appear linearly in the system to be solved. Instead of directly
solving for the Lagrange multipliers along with the other variables,
dyn_ramsey_static.m reduces the size of the problem by always computing the
value of the multipliers that minimizes the residuals, given the other
variables (using a minimum norm solution, easy to compute because of the
linearity property). That function thus needs the derivatives of the optimality
FOCs with respect to the multipliers. The problem is that, when multipliers
appear in an auxiliary variable related to a lead/lag, then those derivatives
need to be retrieved by a chain rule derivation, which cannot be easily done
with the regular static file.
This commit implements the creation of a new file,
ramsey_multipliers_static_g1.{m,mex}, that provides exactly the needed
derivatives w.r.t. Lagrange multipliers through chain rule derivation.
Ref. dynare#633, dynare#1119, dynare#1133
2023-03-24 18:58:12 +01:00
|
|
|
|
for (int i {0}; i < static_cast<int>(equations.size()); i++)
|
2011-03-21 18:40:57 +01:00
|
|
|
|
for (int lag = -max_eq_lag; lag <= max_eq_lead; lag++)
|
|
|
|
|
{
|
2018-06-04 12:52:14 +02:00
|
|
|
|
expr_t dfpower = nullptr;
|
2019-12-16 19:42:59 +01:00
|
|
|
|
stringstream lagstream;
|
2011-03-21 18:40:57 +01:00
|
|
|
|
lagstream << abs(lag);
|
|
|
|
|
if (lag < 0)
|
|
|
|
|
dfpower = AddNonNegativeConstant(lagstream.str());
|
|
|
|
|
else if (lag == 0)
|
|
|
|
|
dfpower = Zero;
|
|
|
|
|
else
|
|
|
|
|
dfpower = AddMinus(Zero, AddNonNegativeConstant(lagstream.str()));
|
|
|
|
|
|
2023-11-30 15:28:57 +01:00
|
|
|
|
lagrangian
|
|
|
|
|
= AddPlus(AddTimes(AddPower(discount_factor_node, dfpower),
|
|
|
|
|
equations[i]->getNonZeroPartofEquation()->decreaseLeadsLags(lag)),
|
|
|
|
|
lagrangian);
|
2011-03-21 18:40:57 +01:00
|
|
|
|
}
|
|
|
|
|
|
2020-01-06 18:26:35 +01:00
|
|
|
|
// Save line numbers and tags, see below
|
|
|
|
|
auto old_equations_lineno = equations_lineno;
|
|
|
|
|
auto old_equation_tags = equation_tags;
|
|
|
|
|
|
|
|
|
|
// Prepare derivation of the Lagrangian
|
|
|
|
|
clearEquations();
|
2022-05-05 18:39:27 +02:00
|
|
|
|
addEquation(AddEqual(lagrangian, Zero), nullopt);
|
2011-03-21 18:40:57 +01:00
|
|
|
|
computeDerivIDs();
|
|
|
|
|
|
2020-01-06 18:26:35 +01:00
|
|
|
|
/* Compute Lagrangian derivatives.
|
|
|
|
|
Also restore line numbers and tags for FOCs w.r.t. a Lagrange multiplier
|
Ramsey: write derivatives of static model w.r.t. Lagrange multipliers in a new file
The computing of the Ramsey steady state relies on the fact that Lagrange
multipliers appear linearly in the system to be solved. Instead of directly
solving for the Lagrange multipliers along with the other variables,
dyn_ramsey_static.m reduces the size of the problem by always computing the
value of the multipliers that minimizes the residuals, given the other
variables (using a minimum norm solution, easy to compute because of the
linearity property). That function thus needs the derivatives of the optimality
FOCs with respect to the multipliers. The problem is that, when multipliers
appear in an auxiliary variable related to a lead/lag, then those derivatives
need to be retrieved by a chain rule derivation, which cannot be easily done
with the regular static file.
This commit implements the creation of a new file,
ramsey_multipliers_static_g1.{m,mex}, that provides exactly the needed
derivatives w.r.t. Lagrange multipliers through chain rule derivation.
Ref. dynare#633, dynare#1119, dynare#1133
2023-03-24 18:58:12 +01:00
|
|
|
|
(i.e. a FOC identical to an equation of the original model).
|
|
|
|
|
The guarantee given by the SymbolTable class that symbol IDs are
|
|
|
|
|
increasing, plus the fact that derivation IDs are increasing with symbol
|
|
|
|
|
IDs for a given lag, gives the expected ordering of the equations
|
|
|
|
|
(optimality FOCs first, then original equations a.k.a. constraints). */
|
2011-03-21 18:40:57 +01:00
|
|
|
|
vector<expr_t> neweqs;
|
2022-05-05 18:39:27 +02:00
|
|
|
|
vector<optional<int>> neweqs_lineno;
|
2020-02-20 15:29:10 +01:00
|
|
|
|
map<int, map<string, string>> neweqs_tags;
|
Ramsey: write derivatives of static model w.r.t. Lagrange multipliers in a new file
The computing of the Ramsey steady state relies on the fact that Lagrange
multipliers appear linearly in the system to be solved. Instead of directly
solving for the Lagrange multipliers along with the other variables,
dyn_ramsey_static.m reduces the size of the problem by always computing the
value of the multipliers that minimizes the residuals, given the other
variables (using a minimum norm solution, easy to compute because of the
linearity property). That function thus needs the derivatives of the optimality
FOCs with respect to the multipliers. The problem is that, when multipliers
appear in an auxiliary variable related to a lead/lag, then those derivatives
need to be retrieved by a chain rule derivation, which cannot be easily done
with the regular static file.
This commit implements the creation of a new file,
ramsey_multipliers_static_g1.{m,mex}, that provides exactly the needed
derivatives w.r.t. Lagrange multipliers through chain rule derivation.
Ref. dynare#633, dynare#1119, dynare#1133
2023-03-24 18:58:12 +01:00
|
|
|
|
int orig_endo_nbr {0};
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (auto& [symb_id_and_lag, deriv_id] : deriv_id_table)
|
2020-01-06 18:26:35 +01:00
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
auto& [symb_id, lag] = symb_id_and_lag;
|
2020-01-06 18:26:35 +01:00
|
|
|
|
if (symbol_table.getType(symb_id) == SymbolType::endogenous && lag == 0)
|
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
neweqs.push_back(
|
|
|
|
|
AddEqual(equations[0]->getNonZeroPartofEquation()->getDerivative(deriv_id), Zero));
|
|
|
|
|
if (optional<int> i = symbol_table.getEquationNumberForMultiplier(symb_id); i)
|
2020-01-06 18:26:35 +01:00
|
|
|
|
{
|
|
|
|
|
// This is a derivative w.r.t. a Lagrange multiplier
|
2022-05-16 15:23:35 +02:00
|
|
|
|
neweqs_lineno.push_back(old_equations_lineno[*i]);
|
2023-11-30 15:28:57 +01:00
|
|
|
|
neweqs_tags[neweqs.size() - 1] = old_equation_tags.getTagsByEqn(*i);
|
2020-01-06 18:26:35 +01:00
|
|
|
|
}
|
|
|
|
|
else
|
Ramsey: write derivatives of static model w.r.t. Lagrange multipliers in a new file
The computing of the Ramsey steady state relies on the fact that Lagrange
multipliers appear linearly in the system to be solved. Instead of directly
solving for the Lagrange multipliers along with the other variables,
dyn_ramsey_static.m reduces the size of the problem by always computing the
value of the multipliers that minimizes the residuals, given the other
variables (using a minimum norm solution, easy to compute because of the
linearity property). That function thus needs the derivatives of the optimality
FOCs with respect to the multipliers. The problem is that, when multipliers
appear in an auxiliary variable related to a lead/lag, then those derivatives
need to be retrieved by a chain rule derivation, which cannot be easily done
with the regular static file.
This commit implements the creation of a new file,
ramsey_multipliers_static_g1.{m,mex}, that provides exactly the needed
derivatives w.r.t. Lagrange multipliers through chain rule derivation.
Ref. dynare#633, dynare#1119, dynare#1133
2023-03-24 18:58:12 +01:00
|
|
|
|
{
|
|
|
|
|
orig_endo_nbr++;
|
2023-12-01 14:29:23 +01:00
|
|
|
|
neweqs_lineno.emplace_back(nullopt);
|
Ramsey: write derivatives of static model w.r.t. Lagrange multipliers in a new file
The computing of the Ramsey steady state relies on the fact that Lagrange
multipliers appear linearly in the system to be solved. Instead of directly
solving for the Lagrange multipliers along with the other variables,
dyn_ramsey_static.m reduces the size of the problem by always computing the
value of the multipliers that minimizes the residuals, given the other
variables (using a minimum norm solution, easy to compute because of the
linearity property). That function thus needs the derivatives of the optimality
FOCs with respect to the multipliers. The problem is that, when multipliers
appear in an auxiliary variable related to a lead/lag, then those derivatives
need to be retrieved by a chain rule derivation, which cannot be easily done
with the regular static file.
This commit implements the creation of a new file,
ramsey_multipliers_static_g1.{m,mex}, that provides exactly the needed
derivatives w.r.t. Lagrange multipliers through chain rule derivation.
Ref. dynare#633, dynare#1119, dynare#1133
2023-03-24 18:58:12 +01:00
|
|
|
|
}
|
2020-01-06 18:26:35 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
2011-03-21 18:40:57 +01:00
|
|
|
|
|
2020-01-06 18:26:35 +01:00
|
|
|
|
// Overwrite equations with the Lagrangian derivatives
|
|
|
|
|
clearEquations();
|
|
|
|
|
for (size_t i = 0; i < neweqs.size(); i++)
|
|
|
|
|
addEquation(neweqs[i], neweqs_lineno[i], neweqs_tags[i]);
|
Ramsey: write derivatives of static model w.r.t. Lagrange multipliers in a new file
The computing of the Ramsey steady state relies on the fact that Lagrange
multipliers appear linearly in the system to be solved. Instead of directly
solving for the Lagrange multipliers along with the other variables,
dyn_ramsey_static.m reduces the size of the problem by always computing the
value of the multipliers that minimizes the residuals, given the other
variables (using a minimum norm solution, easy to compute because of the
linearity property). That function thus needs the derivatives of the optimality
FOCs with respect to the multipliers. The problem is that, when multipliers
appear in an auxiliary variable related to a lead/lag, then those derivatives
need to be retrieved by a chain rule derivation, which cannot be easily done
with the regular static file.
This commit implements the creation of a new file,
ramsey_multipliers_static_g1.{m,mex}, that provides exactly the needed
derivatives w.r.t. Lagrange multipliers through chain rule derivation.
Ref. dynare#633, dynare#1119, dynare#1133
2023-03-24 18:58:12 +01:00
|
|
|
|
|
|
|
|
|
return orig_endo_nbr;
|
2011-03-21 18:40:57 +01:00
|
|
|
|
}
|
|
|
|
|
|
2015-07-30 14:40:03 +02:00
|
|
|
|
bool
|
|
|
|
|
DynamicModel::ParamUsedWithLeadLag() const
|
|
|
|
|
{
|
|
|
|
|
return ParamUsedWithLeadLagInternal();
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-16 17:34:27 +02:00
|
|
|
|
void
|
2021-11-23 12:35:43 +01:00
|
|
|
|
DynamicModel::createVariableMapping()
|
2019-10-16 17:34:27 +02:00
|
|
|
|
{
|
2021-11-23 12:35:43 +01:00
|
|
|
|
for (size_t ii = 0; ii < equations.size(); ii++)
|
2019-10-16 17:34:27 +02:00
|
|
|
|
{
|
|
|
|
|
set<int> eqvars;
|
|
|
|
|
equations[ii]->collectVariables(SymbolType::endogenous, eqvars);
|
|
|
|
|
equations[ii]->collectVariables(SymbolType::exogenous, eqvars);
|
|
|
|
|
for (auto eqvar : eqvars)
|
2022-01-27 15:52:59 +01:00
|
|
|
|
variableMapping[symbol_table.getUltimateOrigSymbID(eqvar)].emplace(ii);
|
2019-10-16 17:34:27 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
DynamicModel::expandEqTags()
|
|
|
|
|
{
|
2020-02-20 15:29:10 +01:00
|
|
|
|
set<int> existing_tags = equation_tags.getEqnsByKey("name");
|
2019-10-16 17:34:27 +02:00
|
|
|
|
for (int eq = 0; eq < static_cast<int>(equations.size()); eq++)
|
2022-05-04 16:01:34 +02:00
|
|
|
|
if (!existing_tags.contains(eq))
|
2023-04-11 14:21:17 +02:00
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
if (auto lhs_expr = dynamic_cast<VariableNode*>(equations[eq]->arg1);
|
|
|
|
|
lhs_expr && !equation_tags.exists("name", symbol_table.getName(lhs_expr->symb_id)))
|
2023-04-11 14:21:17 +02:00
|
|
|
|
equation_tags.add(eq, "name", symbol_table.getName(lhs_expr->symb_id));
|
2023-11-30 15:28:57 +01:00
|
|
|
|
else if (!equation_tags.exists("name", to_string(eq + 1)))
|
|
|
|
|
equation_tags.add(eq, "name", to_string(eq + 1));
|
2023-04-11 14:21:17 +02:00
|
|
|
|
else
|
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
cerr << "Error creating default equation tag: cannot assign default tag to equation "
|
|
|
|
|
"number "
|
|
|
|
|
<< eq + 1 << " because it is already in use" << endl;
|
2023-04-11 14:21:17 +02:00
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-10-16 17:34:27 +02:00
|
|
|
|
}
|
|
|
|
|
|
2013-11-29 15:32:49 +01:00
|
|
|
|
set<int>
|
|
|
|
|
DynamicModel::findUnusedEndogenous()
|
2013-09-13 20:37:31 +02:00
|
|
|
|
{
|
2013-11-29 15:32:49 +01:00
|
|
|
|
set<int> usedEndo, unusedEndo;
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (auto& equation : equations)
|
2018-07-17 18:34:07 +02:00
|
|
|
|
equation->collectVariables(SymbolType::endogenous, usedEndo);
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (auto& equation : static_only_equations)
|
2021-12-06 16:30:38 +01:00
|
|
|
|
equation->collectVariables(SymbolType::endogenous, usedEndo);
|
2013-11-29 15:32:49 +01:00
|
|
|
|
set<int> allEndo = symbol_table.getEndogenous();
|
2023-11-30 15:28:57 +01:00
|
|
|
|
set_difference(allEndo.begin(), allEndo.end(), usedEndo.begin(), usedEndo.end(),
|
2013-11-29 15:32:49 +01:00
|
|
|
|
inserter(unusedEndo, unusedEndo.begin()));
|
|
|
|
|
return unusedEndo;
|
2013-09-13 20:37:31 +02:00
|
|
|
|
}
|
|
|
|
|
|
2013-11-29 16:03:15 +01:00
|
|
|
|
set<int>
|
|
|
|
|
DynamicModel::findUnusedExogenous()
|
|
|
|
|
{
|
2016-10-14 16:35:15 +02:00
|
|
|
|
set<int> usedExo, unusedExo, unobservedExo;
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (auto& equation : equations)
|
2018-07-17 18:34:07 +02:00
|
|
|
|
equation->collectVariables(SymbolType::exogenous, usedExo);
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (auto& equation : static_only_equations)
|
2021-12-06 16:30:38 +01:00
|
|
|
|
equation->collectVariables(SymbolType::exogenous, usedExo);
|
2018-03-13 09:20:14 +01:00
|
|
|
|
set<int> observedExo = symbol_table.getObservedExogenous();
|
2013-11-29 16:03:15 +01:00
|
|
|
|
set<int> allExo = symbol_table.getExogenous();
|
2023-11-30 15:28:57 +01:00
|
|
|
|
set_difference(allExo.begin(), allExo.end(), observedExo.begin(), observedExo.end(),
|
2016-10-14 16:35:15 +02:00
|
|
|
|
inserter(unobservedExo, unobservedExo.begin()));
|
2023-11-30 15:28:57 +01:00
|
|
|
|
set_difference(unobservedExo.begin(), unobservedExo.end(), usedExo.begin(), usedExo.end(),
|
2013-11-29 16:03:15 +01:00
|
|
|
|
inserter(unusedExo, unusedExo.begin()));
|
|
|
|
|
return unusedExo;
|
|
|
|
|
}
|
|
|
|
|
|
2017-07-27 18:33:19 +02:00
|
|
|
|
void
|
|
|
|
|
DynamicModel::setLeadsLagsOrig()
|
|
|
|
|
{
|
2018-06-04 14:17:36 +02:00
|
|
|
|
set<pair<int, int>> dynvars;
|
2017-07-27 18:33:19 +02:00
|
|
|
|
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (auto& equation : equations)
|
2017-07-27 18:33:19 +02:00
|
|
|
|
{
|
2018-07-17 18:34:07 +02:00
|
|
|
|
equation->collectDynamicVariables(SymbolType::endogenous, dynvars);
|
|
|
|
|
equation->collectDynamicVariables(SymbolType::exogenous, dynvars);
|
|
|
|
|
equation->collectDynamicVariables(SymbolType::exogenousDet, dynvars);
|
2018-12-11 17:26:50 +01:00
|
|
|
|
|
2023-11-30 15:28:57 +01:00
|
|
|
|
max_lag_with_diffs_expanded_orig
|
|
|
|
|
= max(equation->maxLagWithDiffsExpanded(), max_lag_with_diffs_expanded_orig);
|
2017-07-27 18:33:19 +02:00
|
|
|
|
}
|
|
|
|
|
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (const auto& [symb_id, lag] : dynvars)
|
2017-07-27 18:33:19 +02:00
|
|
|
|
{
|
2022-06-02 10:50:21 +02:00
|
|
|
|
SymbolType type = symbol_table.getType(symb_id);
|
2017-07-27 18:33:19 +02:00
|
|
|
|
|
2019-03-21 17:21:06 +01:00
|
|
|
|
max_lead_orig = max(lag, max_lead_orig);
|
|
|
|
|
max_lag_orig = max(-lag, max_lag_orig);
|
2017-07-27 18:33:19 +02:00
|
|
|
|
|
2019-09-24 14:03:38 +02:00
|
|
|
|
switch (type)
|
2017-07-27 18:33:19 +02:00
|
|
|
|
{
|
2018-07-17 18:34:07 +02:00
|
|
|
|
case SymbolType::endogenous:
|
2019-09-24 14:03:38 +02:00
|
|
|
|
max_endo_lead_orig = max(lag, max_endo_lead_orig);
|
|
|
|
|
max_endo_lag_orig = max(-lag, max_endo_lag_orig);
|
|
|
|
|
break;
|
2018-07-17 18:34:07 +02:00
|
|
|
|
case SymbolType::exogenous:
|
2019-09-24 14:03:38 +02:00
|
|
|
|
max_exo_lead_orig = max(lag, max_exo_lead_orig);
|
|
|
|
|
max_exo_lag_orig = max(-lag, max_exo_lag_orig);
|
|
|
|
|
break;
|
2018-07-17 18:34:07 +02:00
|
|
|
|
case SymbolType::exogenousDet:
|
2019-03-21 17:21:06 +01:00
|
|
|
|
max_exo_det_lead_orig = max(lag, max_exo_det_lead_orig);
|
|
|
|
|
max_exo_det_lag_orig = max(-lag, max_exo_det_lag_orig);
|
2017-07-27 18:33:19 +02:00
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-09-30 17:10:31 +02:00
|
|
|
|
void
|
|
|
|
|
DynamicModel::computeDerivIDs()
|
2009-04-17 18:26:23 +02:00
|
|
|
|
{
|
2018-06-04 14:17:36 +02:00
|
|
|
|
set<pair<int, int>> dynvars;
|
2009-04-17 18:26:23 +02:00
|
|
|
|
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (auto& equation : equations)
|
2009-04-17 18:26:23 +02:00
|
|
|
|
{
|
2022-07-11 17:33:09 +02:00
|
|
|
|
equation->collectDynamicVariables(SymbolType::endogenous, dynvars);
|
2018-07-17 18:34:07 +02:00
|
|
|
|
equation->collectDynamicVariables(SymbolType::exogenous, dynvars);
|
|
|
|
|
equation->collectDynamicVariables(SymbolType::exogenousDet, dynvars);
|
|
|
|
|
equation->collectDynamicVariables(SymbolType::parameter, dynvars);
|
|
|
|
|
equation->collectDynamicVariables(SymbolType::trend, dynvars);
|
|
|
|
|
equation->collectDynamicVariables(SymbolType::logTrend, dynvars);
|
2009-04-17 18:26:23 +02:00
|
|
|
|
}
|
|
|
|
|
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (const auto& [symb_id, lag] : dynvars)
|
2009-09-30 17:10:31 +02:00
|
|
|
|
{
|
2022-06-02 10:50:21 +02:00
|
|
|
|
SymbolType type = symbol_table.getType(symb_id);
|
2009-04-17 18:26:23 +02:00
|
|
|
|
|
2010-11-18 11:21:20 +01:00
|
|
|
|
/* Setting maximum and minimum lags.
|
|
|
|
|
|
|
|
|
|
We don't want these to be affected by lead/lags on parameters: they
|
|
|
|
|
are accepted for facilitating variable flipping, but are simply
|
|
|
|
|
ignored. */
|
2019-03-21 17:21:06 +01:00
|
|
|
|
if (type != SymbolType::parameter)
|
|
|
|
|
{
|
|
|
|
|
max_lead = max(lag, max_lead);
|
|
|
|
|
max_lag = max(-lag, max_lag);
|
|
|
|
|
}
|
2009-04-17 18:26:23 +02:00
|
|
|
|
|
2009-09-30 17:10:31 +02:00
|
|
|
|
switch (type)
|
|
|
|
|
{
|
2018-07-17 18:34:07 +02:00
|
|
|
|
case SymbolType::endogenous:
|
2019-03-21 17:21:06 +01:00
|
|
|
|
max_endo_lead = max(lag, max_endo_lead);
|
|
|
|
|
max_endo_lag = max(-lag, max_endo_lag);
|
2009-09-30 17:10:31 +02:00
|
|
|
|
break;
|
2018-07-17 18:34:07 +02:00
|
|
|
|
case SymbolType::exogenous:
|
2019-03-21 17:21:06 +01:00
|
|
|
|
max_exo_lead = max(lag, max_exo_lead);
|
|
|
|
|
max_exo_lag = max(-lag, max_exo_lag);
|
2009-09-30 17:10:31 +02:00
|
|
|
|
break;
|
2018-07-17 18:34:07 +02:00
|
|
|
|
case SymbolType::exogenousDet:
|
2019-03-21 17:21:06 +01:00
|
|
|
|
max_exo_det_lead = max(lag, max_exo_det_lead);
|
|
|
|
|
max_exo_det_lag = max(-lag, max_exo_det_lag);
|
2009-09-30 17:10:31 +02:00
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
2009-04-17 18:26:23 +02:00
|
|
|
|
|
2009-09-30 17:10:31 +02:00
|
|
|
|
// Create a new deriv_id
|
|
|
|
|
int deriv_id = deriv_id_table.size();
|
2009-04-17 18:26:23 +02:00
|
|
|
|
|
2022-06-02 10:50:21 +02:00
|
|
|
|
deriv_id_table[{symb_id, lag}] = deriv_id;
|
|
|
|
|
inv_deriv_id_table.emplace_back(symb_id, lag);
|
2009-09-30 17:10:31 +02:00
|
|
|
|
}
|
2009-04-17 18:26:23 +02:00
|
|
|
|
}
|
|
|
|
|
|
2009-04-20 12:48:54 +02:00
|
|
|
|
SymbolType
|
2018-06-04 12:50:53 +02:00
|
|
|
|
DynamicModel::getTypeByDerivID(int deriv_id) const noexcept(false)
|
2009-04-17 18:26:23 +02:00
|
|
|
|
{
|
|
|
|
|
return symbol_table.getType(getSymbIDByDerivID(deriv_id));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
2018-06-04 12:50:53 +02:00
|
|
|
|
DynamicModel::getLagByDerivID(int deriv_id) const noexcept(false)
|
2009-04-17 18:26:23 +02:00
|
|
|
|
{
|
2019-04-23 11:07:32 +02:00
|
|
|
|
if (deriv_id < 0 || deriv_id >= static_cast<int>(inv_deriv_id_table.size()))
|
2009-04-17 18:26:23 +02:00
|
|
|
|
throw UnknownDerivIDException();
|
|
|
|
|
|
|
|
|
|
return inv_deriv_id_table[deriv_id].second;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
2018-06-04 12:50:53 +02:00
|
|
|
|
DynamicModel::getSymbIDByDerivID(int deriv_id) const noexcept(false)
|
2009-04-17 18:26:23 +02:00
|
|
|
|
{
|
2019-04-23 11:07:32 +02:00
|
|
|
|
if (deriv_id < 0 || deriv_id >= static_cast<int>(inv_deriv_id_table.size()))
|
2009-04-17 18:26:23 +02:00
|
|
|
|
throw UnknownDerivIDException();
|
|
|
|
|
|
|
|
|
|
return inv_deriv_id_table[deriv_id].first;
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-12 15:28:52 +02:00
|
|
|
|
int
|
|
|
|
|
DynamicModel::getTypeSpecificIDByDerivID(int deriv_id) const
|
|
|
|
|
{
|
|
|
|
|
return symbol_table.getTypeSpecificID(getSymbIDByDerivID(deriv_id));
|
|
|
|
|
}
|
|
|
|
|
|
2009-04-17 18:26:23 +02:00
|
|
|
|
int
|
2018-06-04 12:50:53 +02:00
|
|
|
|
DynamicModel::getDerivID(int symb_id, int lag) const noexcept(false)
|
2009-04-17 18:26:23 +02:00
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
if (auto it = deriv_id_table.find({symb_id, lag}); it == deriv_id_table.end())
|
2009-04-17 18:26:23 +02:00
|
|
|
|
throw UnknownDerivIDException();
|
|
|
|
|
else
|
|
|
|
|
return it->second;
|
|
|
|
|
}
|
|
|
|
|
|
2011-01-13 18:08:26 +01:00
|
|
|
|
void
|
2023-11-30 15:28:57 +01:00
|
|
|
|
DynamicModel::addAllParamDerivId(set<int>& deriv_id_set)
|
2011-01-13 18:08:26 +01:00
|
|
|
|
{
|
|
|
|
|
for (size_t i = 0; i < inv_deriv_id_table.size(); i++)
|
2018-07-17 18:34:07 +02:00
|
|
|
|
if (symbol_table.getType(inv_deriv_id_table[i].first) == SymbolType::parameter)
|
2011-01-13 18:08:26 +01:00
|
|
|
|
deriv_id_set.insert(i);
|
|
|
|
|
}
|
|
|
|
|
|
2009-04-17 18:26:23 +02:00
|
|
|
|
void
|
2022-10-07 14:37:25 +02:00
|
|
|
|
DynamicModel::computeDynJacobianCols()
|
2009-04-17 18:26:23 +02:00
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
// Sort the dynamic endogenous variables by lexicographic order over (lag,
|
|
|
|
|
// type_specific_symbol_id)
|
2009-04-17 18:26:23 +02:00
|
|
|
|
map<pair<int, int>, int> ordered_dyn_endo;
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (const auto& [symb_lag, deriv_id] : deriv_id_table)
|
|
|
|
|
if (const auto& [symb_id, lag] = symb_lag;
|
2022-07-11 17:33:09 +02:00
|
|
|
|
symbol_table.getType(symb_id) == SymbolType::endogenous)
|
2023-11-30 15:28:57 +01:00
|
|
|
|
ordered_dyn_endo[{lag, symbol_table.getTypeSpecificID(symb_id)}] = deriv_id;
|
2009-04-17 18:26:23 +02:00
|
|
|
|
|
2022-09-14 17:07:08 +02:00
|
|
|
|
// Fill the dynamic jacobian columns for endogenous (legacy representation)
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (int sorted_id {0}; const auto& [ignore, deriv_id] : ordered_dyn_endo)
|
2022-07-11 17:33:09 +02:00
|
|
|
|
dyn_jacobian_cols_table[deriv_id] = sorted_id++;
|
2009-04-20 12:48:54 +02:00
|
|
|
|
|
2022-09-14 17:07:08 +02:00
|
|
|
|
/* Fill the dynamic columns for exogenous and exogenous deterministic (legacy
|
|
|
|
|
representation) */
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (const auto& [symb_lag, deriv_id] : deriv_id_table)
|
2022-10-07 14:37:25 +02:00
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
int symb_id {symb_lag.first};
|
|
|
|
|
int tsid {symbol_table.getTypeSpecificID(symb_id)}; // At this point, there is no trend_var
|
|
|
|
|
if (SymbolType type {symbol_table.getType(symb_id)}; type == SymbolType::exogenous)
|
2022-10-07 14:37:25 +02:00
|
|
|
|
dyn_jacobian_cols_table[deriv_id] = ordered_dyn_endo.size() + tsid;
|
|
|
|
|
else if (type == SymbolType::exogenousDet)
|
|
|
|
|
dyn_jacobian_cols_table[deriv_id] = ordered_dyn_endo.size() + symbol_table.exo_nbr() + tsid;
|
|
|
|
|
}
|
2023-01-19 10:59:36 +01:00
|
|
|
|
|
|
|
|
|
/* NB: the following could differ from dyn_jacobian_cols_table.size() if
|
|
|
|
|
there are unused exogenous (and “nostrict” option is given) */
|
2023-11-30 15:28:57 +01:00
|
|
|
|
dyn_jacobian_ncols
|
|
|
|
|
= ordered_dyn_endo.size() + symbol_table.exo_nbr() + symbol_table.exo_det_nbr();
|
2009-04-17 18:26:23 +02:00
|
|
|
|
}
|
|
|
|
|
|
2010-10-15 19:05:16 +02:00
|
|
|
|
void
|
2023-11-30 15:28:57 +01:00
|
|
|
|
DynamicModel::testTrendDerivativesEqualToZero(const eval_context_t& eval_context)
|
2010-10-15 19:05:16 +02:00
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (auto& [symb_lag1, deriv_id1] : deriv_id_table)
|
|
|
|
|
if (auto& [symb_id1, lag1] = symb_lag1;
|
2022-06-02 10:50:21 +02:00
|
|
|
|
symbol_table.getType(symb_id1) == SymbolType::trend
|
|
|
|
|
|| symbol_table.getType(symb_id1) == SymbolType::logTrend)
|
2019-04-23 11:07:32 +02:00
|
|
|
|
for (int eq = 0; eq < static_cast<int>(equations.size()); eq++)
|
2010-10-15 19:05:16 +02:00
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
expr_t homogeneq = AddMinus(equations[eq]->arg1, equations[eq]->arg2);
|
2013-10-29 11:46:54 +01:00
|
|
|
|
|
|
|
|
|
// Do not run the test if the term inside the log is zero
|
2018-07-17 16:32:00 +02:00
|
|
|
|
if (fabs(homogeneq->eval(eval_context)) > zero_band)
|
2013-10-29 11:46:54 +01:00
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
expr_t testeq = AddLog(homogeneq); // F = log(lhs-rhs)
|
2022-06-02 10:50:21 +02:00
|
|
|
|
testeq = testeq->getDerivative(deriv_id1); // d F / d Trend
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (auto& [symb_lag2, deriv_id2] : deriv_id_table)
|
|
|
|
|
if (auto& [symb_id2, lag2] = symb_lag2;
|
2022-06-02 10:50:21 +02:00
|
|
|
|
symbol_table.getType(symb_id2) == SymbolType::endogenous)
|
2010-10-15 19:05:16 +02:00
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
double nearZero = testeq->getDerivative(deriv_id2)->eval(
|
|
|
|
|
eval_context); // eval d F / d Trend d Endog
|
2019-11-14 17:51:16 +01:00
|
|
|
|
if (fabs(nearZero) > balanced_growth_test_tol)
|
2013-10-29 11:46:54 +01:00
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
cerr << "ERROR: trends not compatible with balanced growth path; the "
|
|
|
|
|
"second-order cross partial of equation "
|
|
|
|
|
<< eq + 1;
|
2022-05-05 18:39:27 +02:00
|
|
|
|
if (equations_lineno[eq])
|
2022-08-03 10:52:08 +02:00
|
|
|
|
cerr << " (line " << *equations_lineno[eq] << ") ";
|
2023-11-30 15:28:57 +01:00
|
|
|
|
cerr << "w.r.t. trend variable " << symbol_table.getName(symb_id1)
|
|
|
|
|
<< " and endogenous variable " << symbol_table.getName(symb_id2)
|
|
|
|
|
<< " is not null (abs. value = " << fabs(nearZero)
|
|
|
|
|
<< "). If you are confident that your trends are correctly specified, "
|
|
|
|
|
"you can raise the value of option 'balanced_growth_test_tol' in "
|
|
|
|
|
"the 'model' block."
|
|
|
|
|
<< endl;
|
2019-11-14 17:51:16 +01:00
|
|
|
|
exit(EXIT_FAILURE);
|
2013-10-29 11:46:54 +01:00
|
|
|
|
}
|
2010-10-15 19:05:16 +02:00
|
|
|
|
}
|
2013-10-29 11:46:54 +01:00
|
|
|
|
}
|
2010-10-15 19:05:16 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-04-30 15:14:33 +02:00
|
|
|
|
void
|
2023-11-30 15:28:57 +01:00
|
|
|
|
DynamicModel::writeLatexFile(const string& basename, bool write_equation_tags) const
|
2009-12-16 18:13:23 +01:00
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
writeLatexModelFile(basename, "dynamic", ExprNodeOutputType::latexDynamicModel,
|
|
|
|
|
write_equation_tags);
|
2009-12-16 18:13:23 +01:00
|
|
|
|
}
|
2009-07-07 16:20:48 +02:00
|
|
|
|
|
2015-02-16 08:31:30 +01:00
|
|
|
|
void
|
2023-11-30 15:28:57 +01:00
|
|
|
|
DynamicModel::writeLatexOriginalFile(const string& basename, bool write_equation_tags) const
|
2015-02-16 08:31:30 +01:00
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
writeLatexModelFile(basename, "original", ExprNodeOutputType::latexDynamicModel,
|
|
|
|
|
write_equation_tags);
|
2015-02-16 08:31:30 +01:00
|
|
|
|
}
|
|
|
|
|
|
2009-09-30 17:10:31 +02:00
|
|
|
|
void
|
2018-12-20 17:04:28 +01:00
|
|
|
|
DynamicModel::substituteEndoLeadGreaterThanTwo(bool deterministic_model)
|
2009-09-30 17:10:31 +02:00
|
|
|
|
{
|
2018-12-20 17:04:28 +01:00
|
|
|
|
substituteLeadLagInternal(AuxVarType::endoLead, deterministic_model, {});
|
2009-10-07 16:07:13 +02:00
|
|
|
|
}
|
2009-09-30 17:10:31 +02:00
|
|
|
|
|
2009-10-07 16:07:13 +02:00
|
|
|
|
void
|
2018-12-20 17:04:28 +01:00
|
|
|
|
DynamicModel::substituteEndoLagGreaterThanTwo(bool deterministic_model)
|
2009-10-07 16:07:13 +02:00
|
|
|
|
{
|
2018-12-20 17:04:28 +01:00
|
|
|
|
substituteLeadLagInternal(AuxVarType::endoLag, deterministic_model, {});
|
2009-10-07 16:07:13 +02:00
|
|
|
|
}
|
2009-09-30 17:10:31 +02:00
|
|
|
|
|
2009-10-07 16:07:13 +02:00
|
|
|
|
void
|
2018-12-20 17:04:28 +01:00
|
|
|
|
DynamicModel::substituteExoLead(bool deterministic_model)
|
2009-10-07 16:07:13 +02:00
|
|
|
|
{
|
2018-12-20 17:04:28 +01:00
|
|
|
|
substituteLeadLagInternal(AuxVarType::exoLead, deterministic_model, {});
|
2009-09-30 17:10:31 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2018-12-20 17:04:28 +01:00
|
|
|
|
DynamicModel::substituteExoLag(bool deterministic_model)
|
2009-10-07 18:34:42 +02:00
|
|
|
|
{
|
2018-12-20 17:04:28 +01:00
|
|
|
|
substituteLeadLagInternal(AuxVarType::exoLag, deterministic_model, {});
|
2009-10-07 18:34:42 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2023-11-30 15:28:57 +01:00
|
|
|
|
DynamicModel::substituteLeadLagInternal(AuxVarType type, bool deterministic_model,
|
|
|
|
|
const vector<string>& subset)
|
2009-09-30 17:10:31 +02:00
|
|
|
|
{
|
|
|
|
|
ExprNode::subst_table_t subst_table;
|
2023-11-30 15:28:57 +01:00
|
|
|
|
vector<BinaryOpNode*> neweqs;
|
2009-09-30 17:10:31 +02:00
|
|
|
|
|
2010-10-11 12:52:27 +02:00
|
|
|
|
// Substitute in used model local variables
|
|
|
|
|
set<int> used_local_vars;
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (auto& equation : equations)
|
2018-07-17 18:34:07 +02:00
|
|
|
|
equation->collectVariables(SymbolType::modelLocalVariable, used_local_vars);
|
2010-10-11 12:52:27 +02:00
|
|
|
|
|
2018-06-04 12:26:16 +02:00
|
|
|
|
for (int used_local_var : used_local_vars)
|
2009-10-07 16:07:13 +02:00
|
|
|
|
{
|
2022-09-27 12:51:22 +02:00
|
|
|
|
const expr_t value = local_variables_table.at(used_local_var);
|
2010-09-16 19:18:45 +02:00
|
|
|
|
expr_t subst;
|
2009-12-16 18:13:23 +01:00
|
|
|
|
switch (type)
|
2009-10-07 16:07:13 +02:00
|
|
|
|
{
|
2018-07-17 17:38:26 +02:00
|
|
|
|
case AuxVarType::endoLead:
|
2010-10-11 12:52:27 +02:00
|
|
|
|
subst = value->substituteEndoLeadGreaterThanTwo(subst_table, neweqs, deterministic_model);
|
2009-10-07 16:07:13 +02:00
|
|
|
|
break;
|
2018-07-17 17:38:26 +02:00
|
|
|
|
case AuxVarType::endoLag:
|
2010-10-11 12:52:27 +02:00
|
|
|
|
subst = value->substituteEndoLagGreaterThanTwo(subst_table, neweqs);
|
2009-10-07 16:07:13 +02:00
|
|
|
|
break;
|
2018-07-17 17:38:26 +02:00
|
|
|
|
case AuxVarType::exoLead:
|
2010-10-11 12:52:27 +02:00
|
|
|
|
subst = value->substituteExoLead(subst_table, neweqs, deterministic_model);
|
2009-10-07 18:34:42 +02:00
|
|
|
|
break;
|
2018-07-17 17:38:26 +02:00
|
|
|
|
case AuxVarType::exoLag:
|
2010-10-11 12:52:27 +02:00
|
|
|
|
subst = value->substituteExoLag(subst_table, neweqs);
|
2009-10-07 16:07:13 +02:00
|
|
|
|
break;
|
2018-07-17 17:38:26 +02:00
|
|
|
|
case AuxVarType::diffForward:
|
2013-05-17 16:51:34 +02:00
|
|
|
|
subst = value->differentiateForwardVars(subset, subst_table, neweqs);
|
2013-04-25 18:09:31 +02:00
|
|
|
|
break;
|
2009-11-02 19:31:50 +01:00
|
|
|
|
default:
|
|
|
|
|
cerr << "DynamicModel::substituteLeadLagInternal: impossible case" << endl;
|
|
|
|
|
exit(EXIT_FAILURE);
|
2009-10-07 16:07:13 +02:00
|
|
|
|
}
|
2018-06-04 12:26:16 +02:00
|
|
|
|
local_variables_table[used_local_var] = subst;
|
2009-10-07 16:07:13 +02:00
|
|
|
|
}
|
2009-09-30 17:10:31 +02:00
|
|
|
|
|
|
|
|
|
// Substitute in equations
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (auto& equation : equations)
|
2009-09-30 17:10:31 +02:00
|
|
|
|
{
|
2010-09-16 19:18:45 +02:00
|
|
|
|
expr_t subst;
|
2009-12-16 18:13:23 +01:00
|
|
|
|
switch (type)
|
2009-10-07 16:07:13 +02:00
|
|
|
|
{
|
2018-07-17 17:38:26 +02:00
|
|
|
|
case AuxVarType::endoLead:
|
2023-11-30 15:28:57 +01:00
|
|
|
|
subst = equation->substituteEndoLeadGreaterThanTwo(subst_table, neweqs,
|
|
|
|
|
deterministic_model);
|
2009-10-07 16:07:13 +02:00
|
|
|
|
break;
|
2018-07-17 17:38:26 +02:00
|
|
|
|
case AuxVarType::endoLag:
|
2018-06-04 12:26:16 +02:00
|
|
|
|
subst = equation->substituteEndoLagGreaterThanTwo(subst_table, neweqs);
|
2009-10-07 16:07:13 +02:00
|
|
|
|
break;
|
2018-07-17 17:38:26 +02:00
|
|
|
|
case AuxVarType::exoLead:
|
2018-06-04 12:26:16 +02:00
|
|
|
|
subst = equation->substituteExoLead(subst_table, neweqs, deterministic_model);
|
2009-10-07 18:34:42 +02:00
|
|
|
|
break;
|
2018-07-17 17:38:26 +02:00
|
|
|
|
case AuxVarType::exoLag:
|
2018-06-04 12:26:16 +02:00
|
|
|
|
subst = equation->substituteExoLag(subst_table, neweqs);
|
2009-10-07 16:07:13 +02:00
|
|
|
|
break;
|
2018-07-17 17:38:26 +02:00
|
|
|
|
case AuxVarType::diffForward:
|
2018-06-04 12:26:16 +02:00
|
|
|
|
subst = equation->differentiateForwardVars(subset, subst_table, neweqs);
|
2013-04-25 18:09:31 +02:00
|
|
|
|
break;
|
2009-11-02 19:31:50 +01:00
|
|
|
|
default:
|
|
|
|
|
cerr << "DynamicModel::substituteLeadLagInternal: impossible case" << endl;
|
|
|
|
|
exit(EXIT_FAILURE);
|
2009-10-07 16:07:13 +02:00
|
|
|
|
}
|
2023-11-30 15:28:57 +01:00
|
|
|
|
auto substeq = dynamic_cast<BinaryOpNode*>(subst);
|
2019-12-16 19:42:59 +01:00
|
|
|
|
assert(substeq);
|
2018-06-04 12:26:16 +02:00
|
|
|
|
equation = substeq;
|
2009-09-30 17:10:31 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Add new equations
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (auto& neweq : neweqs)
|
2019-12-03 14:19:32 +01:00
|
|
|
|
{
|
2022-05-05 18:39:27 +02:00
|
|
|
|
addEquation(neweq, nullopt);
|
2023-04-24 15:38:36 +02:00
|
|
|
|
addAuxEquation(neweq);
|
2019-12-03 14:19:32 +01:00
|
|
|
|
}
|
2009-09-30 17:10:31 +02:00
|
|
|
|
|
2018-12-20 17:04:28 +01:00
|
|
|
|
if (neweqs.size() > 0)
|
2009-10-07 16:07:13 +02:00
|
|
|
|
{
|
|
|
|
|
cout << "Substitution of ";
|
2009-12-16 18:13:23 +01:00
|
|
|
|
switch (type)
|
2009-10-07 16:07:13 +02:00
|
|
|
|
{
|
2018-07-17 17:38:26 +02:00
|
|
|
|
case AuxVarType::endoLead:
|
2009-10-07 16:07:13 +02:00
|
|
|
|
cout << "endo leads >= 2";
|
|
|
|
|
break;
|
2018-07-17 17:38:26 +02:00
|
|
|
|
case AuxVarType::endoLag:
|
2009-10-07 16:07:13 +02:00
|
|
|
|
cout << "endo lags >= 2";
|
|
|
|
|
break;
|
2018-07-17 17:38:26 +02:00
|
|
|
|
case AuxVarType::exoLead:
|
2009-10-07 18:34:42 +02:00
|
|
|
|
cout << "exo leads";
|
|
|
|
|
break;
|
2018-07-17 17:38:26 +02:00
|
|
|
|
case AuxVarType::exoLag:
|
2009-10-07 18:34:42 +02:00
|
|
|
|
cout << "exo lags";
|
2009-10-07 16:07:13 +02:00
|
|
|
|
break;
|
2018-07-17 17:38:26 +02:00
|
|
|
|
case AuxVarType::expectation:
|
2009-10-30 06:21:54 +01:00
|
|
|
|
cout << "expectation";
|
|
|
|
|
break;
|
2018-07-17 17:38:26 +02:00
|
|
|
|
case AuxVarType::diffForward:
|
2013-04-25 18:09:31 +02:00
|
|
|
|
cout << "forward vars";
|
|
|
|
|
break;
|
2016-11-25 17:15:13 +01:00
|
|
|
|
default:
|
|
|
|
|
cerr << "DynamicModel::substituteLeadLagInternal: impossible case" << endl;
|
2011-03-21 18:40:57 +01:00
|
|
|
|
exit(EXIT_FAILURE);
|
2009-10-07 16:07:13 +02:00
|
|
|
|
}
|
|
|
|
|
cout << ": added " << neweqs.size() << " auxiliary variables and equations." << endl;
|
|
|
|
|
}
|
2009-09-30 17:10:31 +02:00
|
|
|
|
}
|
|
|
|
|
|
2017-06-12 14:56:44 +02:00
|
|
|
|
void
|
2018-01-30 10:06:56 +01:00
|
|
|
|
DynamicModel::substituteAdl()
|
2017-06-12 14:56:44 +02:00
|
|
|
|
{
|
2021-02-19 17:45:02 +01:00
|
|
|
|
/* Contrary to other substitution methods, we do the substitution in MLV
|
|
|
|
|
definitions here, instead of doing it at the ExprNode method level,
|
|
|
|
|
because otherwise this would substitute MLV in the original model (see
|
|
|
|
|
#65). */
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (auto& [id, definition] : local_variables_table)
|
2021-02-19 17:45:02 +01:00
|
|
|
|
definition = definition->substituteAdl();
|
|
|
|
|
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (auto& equation : equations)
|
|
|
|
|
equation = dynamic_cast<BinaryOpNode*>(equation->substituteAdl());
|
2021-12-06 16:34:57 +01:00
|
|
|
|
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (auto& equation : static_only_equations)
|
|
|
|
|
equation = dynamic_cast<BinaryOpNode*>(equation->substituteAdl());
|
2018-01-30 10:06:56 +01:00
|
|
|
|
}
|
|
|
|
|
|
2020-11-10 18:04:05 +01:00
|
|
|
|
void
|
|
|
|
|
DynamicModel::substituteModelLocalVariables()
|
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (auto& equation : equations)
|
|
|
|
|
equation = dynamic_cast<BinaryOpNode*>(equation->substituteModelLocalVariables());
|
2020-11-10 18:04:05 +01:00
|
|
|
|
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (auto& equation : static_only_equations)
|
|
|
|
|
equation = dynamic_cast<BinaryOpNode*>(equation->substituteModelLocalVariables());
|
2021-12-06 15:35:01 +01:00
|
|
|
|
|
2020-11-10 18:04:05 +01:00
|
|
|
|
/* We can’t clear local_variables_table at this point, because in case of
|
|
|
|
|
ramsey_policy, the original model is saved via DynamicModel::operator=()
|
|
|
|
|
before computing the FOC. But since DataTree::operator=() clones all
|
|
|
|
|
nodes, it will try to clone nodes containing model-local variables, and
|
|
|
|
|
this will fail at the point where DataTree methods try to evaluate those
|
|
|
|
|
nodes to a numerical value. */
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-20 15:29:10 +01:00
|
|
|
|
set<int>
|
2023-11-30 15:28:57 +01:00
|
|
|
|
DynamicModel::getEquationNumbersFromTags(const set<string>& eqtags) const
|
2018-05-30 16:48:08 +02:00
|
|
|
|
{
|
2020-02-20 15:29:10 +01:00
|
|
|
|
set<int> eqnumbers;
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (auto& eqtag : eqtags)
|
2019-11-18 17:13:49 +01:00
|
|
|
|
{
|
2020-02-20 15:29:10 +01:00
|
|
|
|
set<int> tmp = equation_tags.getEqnsByTag("name", eqtag);
|
|
|
|
|
if (tmp.empty())
|
2018-06-05 16:38:37 +02:00
|
|
|
|
{
|
2019-11-18 17:13:49 +01:00
|
|
|
|
cerr << "ERROR: looking for equation tag " << eqtag << " failed." << endl;
|
|
|
|
|
exit(EXIT_FAILURE);
|
2018-06-05 16:38:37 +02:00
|
|
|
|
}
|
2020-02-20 15:29:10 +01:00
|
|
|
|
eqnumbers.insert(tmp.begin(), tmp.end());
|
2019-11-18 17:13:49 +01:00
|
|
|
|
}
|
2019-08-19 14:51:21 +02:00
|
|
|
|
return eqnumbers;
|
2018-06-05 18:38:53 +02:00
|
|
|
|
}
|
|
|
|
|
|
2022-01-19 15:53:45 +01:00
|
|
|
|
set<int>
|
|
|
|
|
DynamicModel::findPacExpectationEquationNumbers() const
|
2018-06-05 18:38:53 +02:00
|
|
|
|
{
|
2022-01-19 15:53:45 +01:00
|
|
|
|
set<int> eqnumbers;
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (int i {0}; auto& equation : equations)
|
2018-06-05 18:38:53 +02:00
|
|
|
|
{
|
2022-01-19 15:53:45 +01:00
|
|
|
|
if (equation->containsPacExpectation())
|
2020-02-20 15:29:10 +01:00
|
|
|
|
eqnumbers.insert(i);
|
2018-06-05 18:38:53 +02:00
|
|
|
|
i++;
|
|
|
|
|
}
|
2022-01-19 15:53:45 +01:00
|
|
|
|
return eqnumbers;
|
2018-06-05 18:38:53 +02:00
|
|
|
|
}
|
|
|
|
|
|
2019-10-22 14:56:28 +02:00
|
|
|
|
pair<lag_equivalence_table_t, ExprNode::subst_table_t>
|
2023-11-30 15:28:57 +01:00
|
|
|
|
DynamicModel::substituteUnaryOps(VarExpectationModelTable& var_expectation_model_table,
|
|
|
|
|
PacModelTable& pac_model_table)
|
2018-06-07 12:53:00 +02:00
|
|
|
|
{
|
|
|
|
|
vector<int> eqnumbers(equations.size());
|
|
|
|
|
iota(eqnumbers.begin(), eqnumbers.end(), 0);
|
2023-11-30 15:28:57 +01:00
|
|
|
|
return substituteUnaryOps({eqnumbers.begin(), eqnumbers.end()}, var_expectation_model_table,
|
|
|
|
|
pac_model_table);
|
2018-06-07 12:53:00 +02:00
|
|
|
|
}
|
|
|
|
|
|
2019-10-22 14:56:28 +02:00
|
|
|
|
pair<lag_equivalence_table_t, ExprNode::subst_table_t>
|
2023-11-30 15:28:57 +01:00
|
|
|
|
DynamicModel::substituteUnaryOps(const set<int>& eqnumbers,
|
|
|
|
|
VarExpectationModelTable& var_expectation_model_table,
|
|
|
|
|
PacModelTable& pac_model_table)
|
2018-06-05 18:38:53 +02:00
|
|
|
|
{
|
2019-10-22 14:56:28 +02:00
|
|
|
|
lag_equivalence_table_t nodes;
|
2019-08-19 14:51:21 +02:00
|
|
|
|
ExprNode::subst_table_t subst_table;
|
|
|
|
|
|
2019-08-19 18:22:55 +02:00
|
|
|
|
// Mark unary ops to be substituted in model local variables that appear in selected equations
|
2018-06-05 16:38:37 +02:00
|
|
|
|
set<int> used_local_vars;
|
2018-06-07 12:53:00 +02:00
|
|
|
|
for (int eqnumber : eqnumbers)
|
2018-07-17 18:34:07 +02:00
|
|
|
|
equations[eqnumber]->collectVariables(SymbolType::modelLocalVariable, used_local_vars);
|
2020-07-08 12:13:35 +02:00
|
|
|
|
for (int mlv : used_local_vars)
|
|
|
|
|
local_variables_table[mlv]->findUnaryOpNodesForAuxVarCreation(nodes);
|
2018-05-30 16:48:08 +02:00
|
|
|
|
|
2019-08-19 18:22:55 +02:00
|
|
|
|
// Mark unary ops to be substituted in selected equations
|
2018-06-07 12:53:00 +02:00
|
|
|
|
for (int eqnumber : eqnumbers)
|
2019-10-22 14:56:28 +02:00
|
|
|
|
equations[eqnumber]->findUnaryOpNodesForAuxVarCreation(nodes);
|
2018-05-30 16:48:08 +02:00
|
|
|
|
|
|
|
|
|
// Substitute in model local variables
|
2023-11-30 15:28:57 +01:00
|
|
|
|
vector<BinaryOpNode*> neweqs;
|
2020-07-08 12:13:35 +02:00
|
|
|
|
for (int mlv : used_local_vars)
|
2023-11-30 15:28:57 +01:00
|
|
|
|
local_variables_table[mlv]
|
|
|
|
|
= local_variables_table[mlv]->substituteUnaryOpNodes(nodes, subst_table, neweqs);
|
2018-05-30 16:48:08 +02:00
|
|
|
|
|
|
|
|
|
// Substitute in equations
|
2020-07-08 12:13:35 +02:00
|
|
|
|
for (int eq : eqnumbers)
|
2018-05-30 16:48:08 +02:00
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
auto substeq = dynamic_cast<BinaryOpNode*>(
|
|
|
|
|
equations[eq]->substituteUnaryOpNodes(nodes, subst_table, neweqs));
|
2019-12-16 19:42:59 +01:00
|
|
|
|
assert(substeq);
|
2020-07-08 12:13:35 +02:00
|
|
|
|
equations[eq] = substeq;
|
2018-05-30 16:48:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
2022-01-20 16:15:43 +01:00
|
|
|
|
// Substitute in expressions of var_expectation_model
|
|
|
|
|
var_expectation_model_table.substituteUnaryOpsInExpression(nodes, subst_table, neweqs);
|
2022-01-18 12:40:15 +01:00
|
|
|
|
// Substitute in growth terms in pac_model and pac_target_info
|
|
|
|
|
pac_model_table.substituteUnaryOpsInGrowth(nodes, subst_table, neweqs);
|
|
|
|
|
|
2018-05-30 16:48:08 +02:00
|
|
|
|
// Add new equations
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (auto& neweq : neweqs)
|
2019-12-03 14:19:32 +01:00
|
|
|
|
{
|
2022-05-05 18:39:27 +02:00
|
|
|
|
addEquation(neweq, nullopt);
|
2023-04-24 15:38:36 +02:00
|
|
|
|
addAuxEquation(neweq);
|
2019-12-03 14:19:32 +01:00
|
|
|
|
}
|
2018-05-30 16:48:08 +02:00
|
|
|
|
|
2018-12-20 17:04:28 +01:00
|
|
|
|
if (subst_table.size() > 0)
|
2023-11-30 15:28:57 +01:00
|
|
|
|
cout << "Substitution of Unary Ops: added " << neweqs.size()
|
|
|
|
|
<< " auxiliary variables and equations." << endl;
|
2019-08-19 14:51:21 +02:00
|
|
|
|
|
2023-11-30 15:28:57 +01:00
|
|
|
|
return {nodes, subst_table};
|
2018-05-30 16:48:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
2019-10-22 14:56:28 +02:00
|
|
|
|
pair<lag_equivalence_table_t, ExprNode::subst_table_t>
|
2023-11-30 15:28:57 +01:00
|
|
|
|
DynamicModel::substituteDiff(VarExpectationModelTable& var_expectation_model_table,
|
|
|
|
|
PacModelTable& pac_model_table)
|
2018-01-30 10:06:56 +01:00
|
|
|
|
{
|
2019-08-20 12:50:41 +02:00
|
|
|
|
/* Note: at this point, we know that there is no diff operator with a lead,
|
|
|
|
|
because they have been expanded by DataTree::AddDiff().
|
|
|
|
|
Hence we can go forward with the substitution without worrying about the
|
|
|
|
|
expectation operator. */
|
|
|
|
|
|
2019-10-22 14:56:28 +02:00
|
|
|
|
lag_equivalence_table_t diff_nodes;
|
2019-08-19 14:51:21 +02:00
|
|
|
|
ExprNode::subst_table_t diff_subst_table;
|
|
|
|
|
|
2019-08-20 12:50:41 +02:00
|
|
|
|
// Mark diff operators to be substituted in model local variables
|
2018-06-05 18:38:53 +02:00
|
|
|
|
set<int> used_local_vars;
|
2022-06-02 10:50:21 +02:00
|
|
|
|
for (auto equation : equations)
|
2018-07-17 18:34:07 +02:00
|
|
|
|
equation->collectVariables(SymbolType::modelLocalVariable, used_local_vars);
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (auto& [symb_id, expr] : local_variables_table)
|
2022-06-02 10:50:21 +02:00
|
|
|
|
if (used_local_vars.contains(symb_id))
|
|
|
|
|
expr->findDiffNodes(diff_nodes);
|
2018-03-02 12:32:01 +01:00
|
|
|
|
|
2019-08-20 12:50:41 +02:00
|
|
|
|
// Mark diff operators to be substituted in equations
|
2022-06-02 10:50:21 +02:00
|
|
|
|
for (auto equation : equations)
|
2019-10-22 14:56:28 +02:00
|
|
|
|
equation->findDiffNodes(diff_nodes);
|
2018-01-30 10:06:56 +01:00
|
|
|
|
|
2021-10-27 16:26:52 +02:00
|
|
|
|
pac_model_table.findDiffNodesInGrowth(diff_nodes);
|
2019-11-07 20:17:49 +01:00
|
|
|
|
|
2018-01-30 10:06:56 +01:00
|
|
|
|
// Substitute in model local variables
|
2023-11-30 15:28:57 +01:00
|
|
|
|
vector<BinaryOpNode*> neweqs;
|
|
|
|
|
for (auto& [symb_id, expr] : local_variables_table)
|
2022-06-02 10:50:21 +02:00
|
|
|
|
expr = expr->substituteDiff(diff_nodes, diff_subst_table, neweqs);
|
2018-01-30 10:06:56 +01:00
|
|
|
|
|
|
|
|
|
// Substitute in equations
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (auto& equation : equations)
|
2018-01-30 10:06:56 +01:00
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
auto substeq = dynamic_cast<BinaryOpNode*>(
|
|
|
|
|
equation->substituteDiff(diff_nodes, diff_subst_table, neweqs));
|
2019-12-16 19:42:59 +01:00
|
|
|
|
assert(substeq);
|
2018-06-04 12:26:16 +02:00
|
|
|
|
equation = substeq;
|
2018-01-30 10:06:56 +01:00
|
|
|
|
}
|
|
|
|
|
|
2022-01-20 16:15:43 +01:00
|
|
|
|
var_expectation_model_table.substituteDiffNodesInExpression(diff_nodes, diff_subst_table, neweqs);
|
2021-10-27 16:26:52 +02:00
|
|
|
|
pac_model_table.substituteDiffNodesInGrowth(diff_nodes, diff_subst_table, neweqs);
|
2019-02-28 19:22:34 +01:00
|
|
|
|
|
2018-01-30 10:06:56 +01:00
|
|
|
|
// Add new equations
|
2022-06-02 10:50:21 +02:00
|
|
|
|
for (auto neweq : neweqs)
|
2019-12-03 14:19:32 +01:00
|
|
|
|
{
|
2022-05-05 18:39:27 +02:00
|
|
|
|
addEquation(neweq, nullopt);
|
2023-04-24 15:38:36 +02:00
|
|
|
|
addAuxEquation(neweq);
|
2019-12-03 14:19:32 +01:00
|
|
|
|
}
|
2018-03-02 16:40:19 +01:00
|
|
|
|
|
2018-12-20 17:04:28 +01:00
|
|
|
|
if (diff_subst_table.size() > 0)
|
2023-11-30 15:28:57 +01:00
|
|
|
|
cout << "Substitution of Diff operator: added " << neweqs.size()
|
|
|
|
|
<< " auxiliary variables and equations." << endl;
|
2019-08-19 14:51:21 +02:00
|
|
|
|
|
2023-11-30 15:28:57 +01:00
|
|
|
|
return {diff_nodes, diff_subst_table};
|
2017-06-12 14:56:44 +02:00
|
|
|
|
}
|
|
|
|
|
|
2009-10-29 18:16:10 +01:00
|
|
|
|
void
|
2018-12-20 17:04:28 +01:00
|
|
|
|
DynamicModel::substituteExpectation(bool partial_information_model)
|
2009-10-29 18:16:10 +01:00
|
|
|
|
{
|
|
|
|
|
ExprNode::subst_table_t subst_table;
|
2023-11-30 15:28:57 +01:00
|
|
|
|
vector<BinaryOpNode*> neweqs;
|
2009-10-29 18:16:10 +01:00
|
|
|
|
|
2009-11-09 16:13:47 +01:00
|
|
|
|
// Substitute in model local variables
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (auto& [symb_id, expr] : local_variables_table)
|
2022-06-02 10:50:21 +02:00
|
|
|
|
expr = expr->substituteExpectation(subst_table, neweqs, partial_information_model);
|
2009-10-29 18:16:10 +01:00
|
|
|
|
|
|
|
|
|
// Substitute in equations
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (auto& equation : equations)
|
2009-10-29 18:16:10 +01:00
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
equation = dynamic_cast<BinaryOpNode*>(
|
|
|
|
|
equation->substituteExpectation(subst_table, neweqs, partial_information_model));
|
2021-12-06 16:20:17 +01:00
|
|
|
|
assert(equation);
|
2009-10-29 18:16:10 +01:00
|
|
|
|
}
|
|
|
|
|
|
2021-12-06 16:20:17 +01:00
|
|
|
|
/* No need to substitute in static_only_equations, since expectation()
|
|
|
|
|
operators in [static] equations are forbidden at the parsing level. */
|
|
|
|
|
|
2009-10-29 18:16:10 +01:00
|
|
|
|
// Add new equations
|
2022-06-02 10:50:21 +02:00
|
|
|
|
for (auto neweq : neweqs)
|
2019-12-03 14:19:32 +01:00
|
|
|
|
{
|
2022-05-05 18:39:27 +02:00
|
|
|
|
addEquation(neweq, nullopt);
|
2023-04-24 15:38:36 +02:00
|
|
|
|
addAuxEquation(neweq);
|
2019-12-03 14:19:32 +01:00
|
|
|
|
}
|
2009-10-29 18:16:10 +01:00
|
|
|
|
|
2018-12-20 17:04:28 +01:00
|
|
|
|
if (subst_table.size() > 0)
|
2009-11-09 16:13:47 +01:00
|
|
|
|
{
|
|
|
|
|
if (partial_information_model)
|
2023-11-30 15:28:57 +01:00
|
|
|
|
cout << "Substitution of Expectation operator: added " << subst_table.size()
|
|
|
|
|
<< " auxiliary variables and " << neweqs.size() << " auxiliary equations." << endl;
|
2009-11-09 16:13:47 +01:00
|
|
|
|
else
|
2023-11-30 15:28:57 +01:00
|
|
|
|
cout << "Substitution of Expectation operator: added " << neweqs.size()
|
|
|
|
|
<< " auxiliary variables and equations." << endl;
|
2009-11-09 16:13:47 +01:00
|
|
|
|
}
|
2009-10-29 18:16:10 +01:00
|
|
|
|
}
|
|
|
|
|
|
2009-11-07 19:37:11 +01:00
|
|
|
|
void
|
|
|
|
|
DynamicModel::transformPredeterminedVariables()
|
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (auto& [id, definition] : local_variables_table)
|
2021-02-19 20:09:48 +01:00
|
|
|
|
definition = definition->decreaseLeadsLagsPredeterminedVariables();
|
2018-04-18 14:29:40 +02:00
|
|
|
|
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (auto& equation : equations)
|
2009-11-07 19:37:11 +01:00
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
equation = dynamic_cast<BinaryOpNode*>(equation->decreaseLeadsLagsPredeterminedVariables());
|
2021-12-06 12:58:48 +01:00
|
|
|
|
assert(equation);
|
2009-11-07 19:37:11 +01:00
|
|
|
|
}
|
2021-12-06 12:58:48 +01:00
|
|
|
|
|
|
|
|
|
// No need to handle static_only_equations, since there are no leads/lags there
|
2009-11-07 19:37:11 +01:00
|
|
|
|
}
|
|
|
|
|
|
2022-03-30 17:40:01 +02:00
|
|
|
|
void
|
|
|
|
|
DynamicModel::substituteLogTransform()
|
|
|
|
|
{
|
|
|
|
|
for (int symb_id : symbol_table.getVariablesWithLogTransform())
|
|
|
|
|
{
|
|
|
|
|
expr_t aux_def = AddLog(AddVariable(symb_id));
|
|
|
|
|
int aux_symb_id = symbol_table.addLogTransformAuxiliaryVar(symb_id, 0, aux_def);
|
|
|
|
|
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (auto& [id, definition] : local_variables_table)
|
2022-03-30 17:40:01 +02:00
|
|
|
|
definition = definition->substituteLogTransform(symb_id, aux_symb_id);
|
|
|
|
|
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (auto& equation : equations)
|
|
|
|
|
equation
|
|
|
|
|
= dynamic_cast<BinaryOpNode*>(equation->substituteLogTransform(symb_id, aux_symb_id));
|
2022-03-30 17:40:01 +02:00
|
|
|
|
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (auto& equation : static_only_equations)
|
|
|
|
|
equation
|
|
|
|
|
= dynamic_cast<BinaryOpNode*>(equation->substituteLogTransform(symb_id, aux_symb_id));
|
2022-03-30 17:40:01 +02:00
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
We add the following new equations:
|
|
|
|
|
+ X=exp(log_X) to the model
|
|
|
|
|
+ log_X=log(X) to the list of auxiliary equations
|
|
|
|
|
|
|
|
|
|
In this way:
|
|
|
|
|
+ statements like X=1 in initval/endval blocks will be correctly
|
|
|
|
|
handled (i.e. log_X will be initialized to 0 in this case), through
|
|
|
|
|
the set_auxiliary_variables.m and dynamic_set_auxiliary_series.m files
|
|
|
|
|
+ computation of X in perfect foresight simulations will be done by
|
|
|
|
|
simple evaluation when using block decomposition (X will belong to an
|
|
|
|
|
block of type “evaluate”, or maybe even the epilogue)
|
|
|
|
|
*/
|
|
|
|
|
addAuxEquation(AddEqual(AddVariable(aux_symb_id), aux_def));
|
2023-11-30 15:28:57 +01:00
|
|
|
|
addEquation(AddEqual(AddVariable(symb_id), AddExp(AddVariable(aux_symb_id))), nullopt, {});
|
2022-03-30 17:40:01 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2023-11-30 15:28:57 +01:00
|
|
|
|
DynamicModel::checkNoWithLogTransform(const set<int>& eqnumbers)
|
2022-03-30 17:40:01 +02:00
|
|
|
|
{
|
|
|
|
|
set<int> endos;
|
|
|
|
|
for (int eq : eqnumbers)
|
|
|
|
|
equations[eq]->collectVariables(SymbolType::endogenous, endos);
|
|
|
|
|
|
2023-11-30 15:28:57 +01:00
|
|
|
|
const set<int>& with_log_transform = symbol_table.getVariablesWithLogTransform();
|
2022-03-30 17:40:01 +02:00
|
|
|
|
|
|
|
|
|
vector<int> intersect;
|
2023-11-30 15:28:57 +01:00
|
|
|
|
set_intersection(endos.begin(), endos.end(), with_log_transform.begin(), with_log_transform.end(),
|
2022-03-30 17:40:01 +02:00
|
|
|
|
back_inserter(intersect));
|
|
|
|
|
if (!intersect.empty())
|
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
cerr << "ERROR: the following variables are declared with var(log) and therefore cannot "
|
|
|
|
|
"appear in a VAR/TCM/PAC equation: ";
|
2022-03-30 17:40:01 +02:00
|
|
|
|
for (int symb_id : intersect)
|
|
|
|
|
cerr << symbol_table.getName(symb_id) << " ";
|
|
|
|
|
cerr << endl;
|
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2010-10-15 19:05:16 +02:00
|
|
|
|
void
|
|
|
|
|
DynamicModel::detrendEquations()
|
|
|
|
|
{
|
2013-10-29 11:46:54 +01:00
|
|
|
|
// We go backwards in the list of trend_vars, to deal correctly with I(2) processes
|
2023-12-01 15:06:40 +01:00
|
|
|
|
for (const auto& it : std::ranges::reverse_view(nonstationary_symbols_map))
|
2021-12-06 12:53:35 +01:00
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (auto& equation : equations)
|
2021-12-06 12:53:35 +01:00
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
equation = dynamic_cast<BinaryOpNode*>(
|
2023-12-01 15:06:40 +01:00
|
|
|
|
equation->detrend(it.first, it.second.first, it.second.second));
|
2021-12-06 12:53:35 +01:00
|
|
|
|
assert(equation);
|
|
|
|
|
}
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (auto& equation : static_only_equations)
|
2021-12-06 12:53:35 +01:00
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
equation = dynamic_cast<BinaryOpNode*>(
|
2023-12-01 15:06:40 +01:00
|
|
|
|
equation->detrend(it.first, it.second.first, it.second.second));
|
2021-12-06 12:53:35 +01:00
|
|
|
|
assert(equation);
|
|
|
|
|
}
|
|
|
|
|
}
|
2010-10-15 19:05:16 +02:00
|
|
|
|
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (auto& equation : equations)
|
2010-10-15 19:05:16 +02:00
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
equation = dynamic_cast<BinaryOpNode*>(equation->removeTrendLeadLag(trend_symbols_map));
|
2021-12-06 12:53:35 +01:00
|
|
|
|
assert(equation);
|
|
|
|
|
}
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (auto& equation : static_only_equations)
|
2021-12-06 12:53:35 +01:00
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
equation = dynamic_cast<BinaryOpNode*>(equation->removeTrendLeadLag(trend_symbols_map));
|
2021-12-06 12:53:35 +01:00
|
|
|
|
assert(equation);
|
2010-10-15 19:05:16 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
DynamicModel::removeTrendVariableFromEquations()
|
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (auto& equation : equations)
|
2010-10-15 19:05:16 +02:00
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
equation = dynamic_cast<BinaryOpNode*>(equation->replaceTrendVar());
|
2021-12-06 12:53:35 +01:00
|
|
|
|
assert(equation);
|
|
|
|
|
}
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (auto& equation : static_only_equations)
|
2021-12-06 12:53:35 +01:00
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
equation = dynamic_cast<BinaryOpNode*>(equation->replaceTrendVar());
|
2021-12-06 12:53:35 +01:00
|
|
|
|
assert(equation);
|
2010-10-15 19:05:16 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-25 18:09:31 +02:00
|
|
|
|
void
|
2023-11-30 15:28:57 +01:00
|
|
|
|
DynamicModel::differentiateForwardVars(const vector<string>& subset)
|
2013-04-25 18:09:31 +02:00
|
|
|
|
{
|
2018-12-20 17:04:28 +01:00
|
|
|
|
substituteLeadLagInternal(AuxVarType::diffForward, true, subset);
|
2013-04-25 18:09:31 +02:00
|
|
|
|
}
|
|
|
|
|
|
2009-09-30 17:10:31 +02:00
|
|
|
|
void
|
2023-11-30 15:28:57 +01:00
|
|
|
|
DynamicModel::fillEvalContext(eval_context_t& eval_context) const
|
2009-09-30 17:10:31 +02:00
|
|
|
|
{
|
|
|
|
|
// First, auxiliary variables
|
2018-06-04 12:26:16 +02:00
|
|
|
|
for (auto aux_equation : aux_equations)
|
2009-09-30 17:10:31 +02:00
|
|
|
|
{
|
2018-11-28 14:32:26 +01:00
|
|
|
|
assert(aux_equation->op_code == BinaryOpcode::equal);
|
2023-11-30 15:28:57 +01:00
|
|
|
|
auto auxvar = dynamic_cast<VariableNode*>(aux_equation->arg1);
|
2019-12-16 19:42:59 +01:00
|
|
|
|
assert(auxvar);
|
2009-09-30 17:10:31 +02:00
|
|
|
|
try
|
|
|
|
|
{
|
2018-11-28 14:32:26 +01:00
|
|
|
|
double val = aux_equation->arg2->eval(eval_context);
|
|
|
|
|
eval_context[auxvar->symb_id] = val;
|
2009-09-30 17:10:31 +02:00
|
|
|
|
}
|
2023-11-30 15:28:57 +01:00
|
|
|
|
catch (ExprNode::EvalException& e)
|
2009-09-30 17:10:31 +02:00
|
|
|
|
{
|
|
|
|
|
// Do nothing
|
|
|
|
|
}
|
|
|
|
|
}
|
2009-07-10 17:37:51 +02:00
|
|
|
|
|
2009-09-30 17:10:31 +02:00
|
|
|
|
// Second, model local variables
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (auto& [symb_id, expression] : local_variables_table)
|
2009-09-30 17:10:31 +02:00
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
double val = expression->eval(eval_context);
|
2022-06-02 10:50:21 +02:00
|
|
|
|
eval_context[symb_id] = val;
|
2009-09-30 17:10:31 +02:00
|
|
|
|
}
|
2023-11-30 15:28:57 +01:00
|
|
|
|
catch (ExprNode::EvalException& e)
|
2009-09-30 17:10:31 +02:00
|
|
|
|
{
|
|
|
|
|
// Do nothing
|
|
|
|
|
}
|
|
|
|
|
}
|
2010-10-15 19:05:16 +02:00
|
|
|
|
|
2023-11-30 15:28:57 +01:00
|
|
|
|
// Third, trend variables
|
2020-01-30 12:47:59 +01:00
|
|
|
|
for (int trendVar : symbol_table.getTrendVarIds())
|
2023-11-30 15:28:57 +01:00
|
|
|
|
eval_context[trendVar] = 2; // not <= 0 bc of log, not 1 bc of powers
|
2009-09-30 17:10:31 +02:00
|
|
|
|
}
|
2011-10-14 14:35:32 +02:00
|
|
|
|
|
2013-04-11 17:07:39 +02:00
|
|
|
|
void
|
2023-12-13 15:37:07 +01:00
|
|
|
|
DynamicModel::addStaticOnlyEquation(expr_t eq, const optional<int>& lineno,
|
|
|
|
|
map<string, string> eq_tags)
|
2013-04-11 17:07:39 +02:00
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
auto beq = dynamic_cast<BinaryOpNode*>(eq);
|
2019-12-16 19:42:59 +01:00
|
|
|
|
assert(beq && beq->op_code == BinaryOpcode::equal);
|
2013-04-11 17:07:39 +02:00
|
|
|
|
|
2023-11-14 15:55:30 +01:00
|
|
|
|
static_only_equations_equation_tags.add(static_only_equations.size(), move(eq_tags));
|
2013-04-11 17:07:39 +02:00
|
|
|
|
static_only_equations.push_back(beq);
|
2023-12-13 15:37:07 +01:00
|
|
|
|
static_only_equations_lineno.push_back(lineno);
|
2013-04-11 17:07:39 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size_t
|
|
|
|
|
DynamicModel::staticOnlyEquationsNbr() const
|
|
|
|
|
{
|
|
|
|
|
return static_only_equations.size();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size_t
|
|
|
|
|
DynamicModel::dynamicOnlyEquationsNbr() const
|
|
|
|
|
{
|
2020-02-20 15:29:10 +01:00
|
|
|
|
return equation_tags.getDynamicEqns().size();
|
2013-04-11 17:07:39 +02:00
|
|
|
|
}
|
|
|
|
|
|
2021-07-20 18:18:24 +02:00
|
|
|
|
void
|
2023-12-13 15:37:07 +01:00
|
|
|
|
DynamicModel::addOccbinEquation(expr_t eq, const optional<int>& lineno, map<string, string> eq_tags,
|
2023-11-30 15:28:57 +01:00
|
|
|
|
const vector<string>& regimes_bind,
|
|
|
|
|
const vector<string>& regimes_relax)
|
2021-07-20 18:18:24 +02:00
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
auto beq = dynamic_cast<BinaryOpNode*>(eq);
|
2021-07-20 18:18:24 +02:00
|
|
|
|
assert(beq && beq->op_code == BinaryOpcode::equal);
|
|
|
|
|
|
|
|
|
|
// Construct the term to be added to the corresponding equation
|
|
|
|
|
expr_t basic_term = AddMinus(beq->arg1, beq->arg2);
|
|
|
|
|
expr_t term = basic_term;
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (auto& regime : regimes_bind)
|
2021-07-20 18:18:24 +02:00
|
|
|
|
{
|
|
|
|
|
int param_id = symbol_table.getID(ParsingDriver::buildOccbinBindParamName(regime));
|
|
|
|
|
term = AddTimes(term, AddVariable(param_id));
|
|
|
|
|
}
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (auto& regime : regimes_relax)
|
2021-07-20 18:18:24 +02:00
|
|
|
|
{
|
|
|
|
|
int param_id = symbol_table.getID(ParsingDriver::buildOccbinBindParamName(regime));
|
|
|
|
|
term = AddTimes(term, AddMinus(One, AddVariable(param_id)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create or update the dynamic equation
|
2023-11-30 15:28:57 +01:00
|
|
|
|
optional<int> eqn {equation_tags.getEqnByTag("name", eq_tags.at("name"))};
|
2023-01-04 16:44:52 +01:00
|
|
|
|
if (eqn)
|
2021-07-20 18:18:24 +02:00
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
BinaryOpNode* orig_eq {equations[*eqn]};
|
2021-07-20 18:18:24 +02:00
|
|
|
|
/* In the following, we could have kept only orig_eq->arg1, but the
|
|
|
|
|
following adds a (somewhat bizarre) support for equation snippets
|
|
|
|
|
without “bind” nor “relax” */
|
2023-01-04 16:44:52 +01:00
|
|
|
|
equations[*eqn] = AddEqual(AddPlus(AddMinus(orig_eq->arg1, orig_eq->arg2), term), Zero);
|
2021-07-20 18:18:24 +02:00
|
|
|
|
// It’s unclear how to update lineno and tags, so don’t do it
|
|
|
|
|
}
|
2023-01-04 16:44:52 +01:00
|
|
|
|
else
|
2021-07-20 18:18:24 +02:00
|
|
|
|
{
|
|
|
|
|
auto eq_tags_dynamic = eq_tags;
|
|
|
|
|
eq_tags_dynamic["dynamic"] = "";
|
|
|
|
|
addEquation(AddEqual(term, Zero), lineno, eq_tags_dynamic);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create or update the static equation (corresponding to the pure relax regime)
|
|
|
|
|
if (regimes_bind.empty())
|
|
|
|
|
{
|
2023-01-04 16:44:52 +01:00
|
|
|
|
/* Similar remark as above. We could have entirely skipped this
|
|
|
|
|
equation updating, since normally there is only one such clause,
|
|
|
|
|
but the following adds a (somewhat bizarre) support for equation
|
|
|
|
|
snippets without “bind” nor “relax” */
|
2023-11-30 15:28:57 +01:00
|
|
|
|
optional<int> eqn_static {
|
|
|
|
|
static_only_equations_equation_tags.getEqnByTag("name", eq_tags.at("name"))};
|
2023-01-04 16:44:52 +01:00
|
|
|
|
if (eqn_static)
|
2021-07-20 18:18:24 +02:00
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
BinaryOpNode* orig_eq {static_only_equations[*eqn_static]};
|
|
|
|
|
static_only_equations[*eqn_static]
|
|
|
|
|
= AddEqual(AddPlus(AddMinus(orig_eq->arg1, orig_eq->arg2), basic_term), Zero);
|
2021-07-20 18:18:24 +02:00
|
|
|
|
// It’s unclear how to update lineno and tags, so don’t do it
|
|
|
|
|
}
|
2023-01-04 16:44:52 +01:00
|
|
|
|
else
|
2021-07-20 18:18:24 +02:00
|
|
|
|
{
|
2023-11-14 15:55:30 +01:00
|
|
|
|
eq_tags["static"] = "";
|
|
|
|
|
addStaticOnlyEquation(AddEqual(basic_term, Zero), lineno, move(eq_tags));
|
2021-07-20 18:18:24 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-10 18:16:11 +02:00
|
|
|
|
bool
|
2023-11-30 15:28:57 +01:00
|
|
|
|
DynamicModel::isChecksumMatching(const string& basename) const
|
2015-05-10 18:16:11 +02:00
|
|
|
|
{
|
2019-10-07 15:20:07 +02:00
|
|
|
|
stringstream buffer;
|
2015-05-10 18:16:11 +02:00
|
|
|
|
|
|
|
|
|
// Write equation tags
|
2020-02-20 15:29:10 +01:00
|
|
|
|
equation_tags.writeCheckSumInfo(buffer);
|
2013-09-17 15:11:57 +02:00
|
|
|
|
|
2023-11-30 15:28:57 +01:00
|
|
|
|
constexpr ExprNodeOutputType buffer_type {ExprNodeOutputType::CDynamicModel};
|
2013-09-17 15:11:57 +02:00
|
|
|
|
|
2019-10-07 15:57:19 +02:00
|
|
|
|
deriv_node_temp_terms_t tef_terms;
|
|
|
|
|
temporary_terms_t temp_term_union;
|
2015-05-10 18:16:11 +02:00
|
|
|
|
|
2023-11-30 15:28:57 +01:00
|
|
|
|
writeTemporaryTerms<buffer_type>(temporary_terms_derivatives[0], temp_term_union,
|
|
|
|
|
temporary_terms_idxs, buffer, tef_terms);
|
2015-05-10 18:16:11 +02:00
|
|
|
|
|
2022-07-12 14:13:27 +02:00
|
|
|
|
writeModelEquations<buffer_type>(buffer, temp_term_union);
|
2013-09-17 15:11:57 +02:00
|
|
|
|
|
2023-11-30 15:28:57 +01:00
|
|
|
|
size_t result = hash<string> {}(buffer.str());
|
2015-05-13 17:11:39 +02:00
|
|
|
|
|
2015-05-11 08:52:50 +02:00
|
|
|
|
// check whether basename directory exist. If not, create it.
|
2019-10-07 15:20:07 +02:00
|
|
|
|
// If it does, read old checksum if it exists, return if equal to result
|
2015-05-10 18:16:11 +02:00
|
|
|
|
fstream checksum_file;
|
2023-11-30 15:28:57 +01:00
|
|
|
|
auto filename = filesystem::path {basename} / "checksum";
|
2019-10-07 15:20:07 +02:00
|
|
|
|
if (!filesystem::create_directory(basename))
|
2015-05-11 08:52:50 +02:00
|
|
|
|
{
|
2018-06-27 15:12:12 +02:00
|
|
|
|
checksum_file.open(filename, ios::in | ios::binary);
|
2015-05-11 08:52:50 +02:00
|
|
|
|
if (checksum_file.is_open())
|
2017-06-01 19:58:32 +02:00
|
|
|
|
{
|
2019-10-07 15:20:07 +02:00
|
|
|
|
size_t old_checksum;
|
2017-06-01 19:58:32 +02:00
|
|
|
|
checksum_file >> old_checksum;
|
|
|
|
|
checksum_file.close();
|
2019-10-07 15:20:07 +02:00
|
|
|
|
if (old_checksum == result)
|
|
|
|
|
return true;
|
2017-06-01 19:58:32 +02:00
|
|
|
|
}
|
2015-05-11 08:52:50 +02:00
|
|
|
|
}
|
2019-10-07 15:20:07 +02:00
|
|
|
|
|
2015-05-11 08:52:50 +02:00
|
|
|
|
// write new checksum file if none or different from old checksum
|
2019-10-07 15:20:07 +02:00
|
|
|
|
checksum_file.open(filename, ios::out | ios::binary);
|
|
|
|
|
if (!checksum_file.is_open())
|
2017-06-01 19:58:32 +02:00
|
|
|
|
{
|
2022-10-11 11:00:50 +02:00
|
|
|
|
cerr << "ERROR: Can't open file " << filename.string() << endl;
|
2019-10-07 15:20:07 +02:00
|
|
|
|
exit(EXIT_FAILURE);
|
2017-06-01 19:58:32 +02:00
|
|
|
|
}
|
2019-10-07 15:20:07 +02:00
|
|
|
|
checksum_file << result;
|
|
|
|
|
checksum_file.close();
|
|
|
|
|
return false;
|
2015-05-10 18:16:11 +02:00
|
|
|
|
}
|
2015-07-21 17:26:08 +02:00
|
|
|
|
|
2017-02-02 15:09:43 +01:00
|
|
|
|
void
|
2023-11-30 15:28:57 +01:00
|
|
|
|
DynamicModel::writeJsonOutput(ostream& output) const
|
2017-02-02 15:09:43 +01:00
|
|
|
|
{
|
2020-06-05 16:07:52 +02:00
|
|
|
|
deriv_node_temp_terms_t tef_terms;
|
|
|
|
|
writeJsonModelLocalVariables(output, false, tef_terms);
|
|
|
|
|
output << ", ";
|
2017-02-20 12:18:11 +01:00
|
|
|
|
writeJsonModelEquations(output, false);
|
2017-03-10 17:23:35 +01:00
|
|
|
|
output << ", ";
|
|
|
|
|
writeJsonXrefs(output);
|
2018-09-18 14:50:31 +02:00
|
|
|
|
output << ", ";
|
|
|
|
|
writeJsonAST(output);
|
2019-10-16 17:34:27 +02:00
|
|
|
|
output << ", ";
|
|
|
|
|
writeJsonVariableMapping(output);
|
2022-09-26 13:07:18 +02:00
|
|
|
|
output << R"(, "dynamic_tmp_nbr": [)";
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (bool printed_something {false}; const auto& tts : temporary_terms_derivatives)
|
2022-09-26 13:07:18 +02:00
|
|
|
|
{
|
|
|
|
|
if (exchange(printed_something, true))
|
|
|
|
|
output << ", ";
|
|
|
|
|
output << tts.size();
|
|
|
|
|
}
|
|
|
|
|
output << "], ";
|
2022-09-14 17:07:08 +02:00
|
|
|
|
writeJsonSparseIndicesHelper<true>(output);
|
2018-09-18 14:50:31 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2023-11-30 15:28:57 +01:00
|
|
|
|
DynamicModel::writeJsonAST(ostream& output) const
|
2018-09-18 14:50:31 +02:00
|
|
|
|
{
|
|
|
|
|
vector<pair<string, string>> eqtags;
|
2019-04-03 16:32:52 +02:00
|
|
|
|
output << R"("abstract_syntax_tree":[)" << endl;
|
2019-04-23 11:07:32 +02:00
|
|
|
|
for (int eq = 0; eq < static_cast<int>(equations.size()); eq++)
|
2018-09-18 14:50:31 +02:00
|
|
|
|
{
|
|
|
|
|
if (eq != 0)
|
|
|
|
|
output << ", ";
|
|
|
|
|
|
2022-05-05 18:39:27 +02:00
|
|
|
|
output << R"({ "number":)" << eq;
|
|
|
|
|
if (equations_lineno[eq])
|
|
|
|
|
output << R"(, "line":)" << *equations_lineno[eq];
|
2018-09-18 14:50:31 +02:00
|
|
|
|
|
2020-02-20 15:29:10 +01:00
|
|
|
|
equation_tags.writeJsonAST(output, eq);
|
2018-09-18 14:50:31 +02:00
|
|
|
|
|
2019-04-03 16:32:52 +02:00
|
|
|
|
output << R"(, "AST": )";
|
2018-09-18 14:50:31 +02:00
|
|
|
|
equations[eq]->writeJsonAST(output);
|
|
|
|
|
output << "}";
|
|
|
|
|
}
|
|
|
|
|
output << "]";
|
2017-03-10 17:23:35 +01:00
|
|
|
|
}
|
|
|
|
|
|
2019-10-16 17:34:27 +02:00
|
|
|
|
void
|
2023-11-30 15:28:57 +01:00
|
|
|
|
DynamicModel::writeJsonVariableMapping(ostream& output) const
|
2019-10-16 17:34:27 +02:00
|
|
|
|
{
|
|
|
|
|
output << R"("variable_mapping":[)" << endl;
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (bool printed_something {false}; const auto& [var, eqs] : variableMapping)
|
2020-12-09 16:45:30 +01:00
|
|
|
|
{
|
2022-06-03 16:24:26 +02:00
|
|
|
|
if (exchange(printed_something, true))
|
2020-12-09 16:45:30 +01:00
|
|
|
|
output << ", ";
|
|
|
|
|
output << R"({"name": ")" << symbol_table.getName(var) << R"(", "equations":[)";
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (bool printed_something2 {false}; int it2 : eqs)
|
|
|
|
|
if (auto tmp = equation_tags.getTagValueByEqnAndKey(it2, "name"); tmp)
|
2020-12-09 16:45:30 +01:00
|
|
|
|
{
|
2022-06-03 16:24:26 +02:00
|
|
|
|
if (exchange(printed_something2, true))
|
2020-12-09 16:45:30 +01:00
|
|
|
|
output << ", ";
|
2023-01-04 16:03:12 +01:00
|
|
|
|
output << '"' << *tmp << '"';
|
2020-12-09 16:45:30 +01:00
|
|
|
|
}
|
|
|
|
|
output << "]}" << endl;
|
2019-10-16 17:34:27 +02:00
|
|
|
|
}
|
|
|
|
|
output << "]";
|
|
|
|
|
}
|
|
|
|
|
|
2017-03-10 17:23:35 +01:00
|
|
|
|
void
|
2023-11-30 15:28:57 +01:00
|
|
|
|
DynamicModel::writeJsonXrefsHelper(ostream& output,
|
|
|
|
|
const map<pair<int, int>, set<int>>& xrefmap) const
|
2017-03-10 17:23:35 +01:00
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (bool printed_something {false}; const auto& [symb_lag, eqs] : xrefmap)
|
2017-03-10 17:23:35 +01:00
|
|
|
|
{
|
2022-06-03 16:24:26 +02:00
|
|
|
|
if (exchange(printed_something, true))
|
2017-03-10 17:23:35 +01:00
|
|
|
|
output << ", ";
|
2022-06-03 16:24:26 +02:00
|
|
|
|
output << R"({"name": ")" << symbol_table.getName(symb_lag.first) << R"(")"
|
2023-11-30 15:28:57 +01:00
|
|
|
|
<< R"(, "shift": )" << symb_lag.second << R"(, "equations": [)";
|
|
|
|
|
for (bool printed_something2 {false}; int eq : eqs)
|
2017-03-10 17:23:35 +01:00
|
|
|
|
{
|
2022-06-03 16:24:26 +02:00
|
|
|
|
if (exchange(printed_something2, true))
|
2017-03-10 17:23:35 +01:00
|
|
|
|
output << ", ";
|
2022-06-03 16:24:26 +02:00
|
|
|
|
output << eq + 1;
|
2017-03-10 17:23:35 +01:00
|
|
|
|
}
|
|
|
|
|
output << "]}";
|
|
|
|
|
}
|
2017-10-13 16:21:13 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2023-11-30 15:28:57 +01:00
|
|
|
|
DynamicModel::writeJsonXrefs(ostream& output) const
|
2017-10-13 16:21:13 +02:00
|
|
|
|
{
|
2019-04-03 16:32:52 +02:00
|
|
|
|
output << R"("xrefs": {)"
|
|
|
|
|
<< R"("parameters": [)";
|
2017-10-13 16:21:13 +02:00
|
|
|
|
writeJsonXrefsHelper(output, xref_param);
|
2017-03-10 17:23:35 +01:00
|
|
|
|
output << "]"
|
2019-04-03 16:32:52 +02:00
|
|
|
|
<< R"(, "endogenous": [)";
|
2017-10-13 16:21:13 +02:00
|
|
|
|
writeJsonXrefsHelper(output, xref_endo);
|
2017-03-10 17:23:35 +01:00
|
|
|
|
output << "]"
|
2019-04-03 16:32:52 +02:00
|
|
|
|
<< R"(, "exogenous": [)";
|
2019-12-16 19:42:59 +01:00
|
|
|
|
writeJsonXrefsHelper(output, xref_exo);
|
2017-03-10 17:23:35 +01:00
|
|
|
|
output << "]"
|
2019-04-03 16:32:52 +02:00
|
|
|
|
<< R"(, "exogenous_deterministic": [)";
|
2017-10-13 16:21:13 +02:00
|
|
|
|
writeJsonXrefsHelper(output, xref_exo_det);
|
2017-03-10 17:23:35 +01:00
|
|
|
|
output << "]}" << endl;
|
2017-02-20 12:18:11 +01:00
|
|
|
|
}
|
|
|
|
|
|
2017-06-23 14:20:48 +02:00
|
|
|
|
void
|
2023-11-30 15:28:57 +01:00
|
|
|
|
DynamicModel::writeJsonOriginalModelOutput(ostream& output) const
|
2017-06-23 14:20:48 +02:00
|
|
|
|
{
|
|
|
|
|
writeJsonModelEquations(output, false);
|
2018-09-18 14:50:31 +02:00
|
|
|
|
output << ", ";
|
|
|
|
|
writeJsonAST(output);
|
2017-06-23 14:20:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
2017-06-28 17:11:24 +02:00
|
|
|
|
void
|
2023-11-30 15:28:57 +01:00
|
|
|
|
DynamicModel::writeJsonDynamicModelInfo(ostream& output) const
|
2017-06-28 17:11:24 +02:00
|
|
|
|
{
|
2019-04-03 16:32:52 +02:00
|
|
|
|
output << R"("model_info": {)"
|
|
|
|
|
<< R"("lead_lag_incidence": [)";
|
2017-06-28 17:11:24 +02:00
|
|
|
|
// Loop on endogenous variables
|
2023-11-30 15:28:57 +01:00
|
|
|
|
int nstatic = 0, nfwrd = 0, npred = 0, nboth = 0;
|
2017-06-28 17:11:24 +02:00
|
|
|
|
for (int endoID = 0; endoID < symbol_table.endo_nbr(); endoID++)
|
|
|
|
|
{
|
|
|
|
|
if (endoID != 0)
|
|
|
|
|
output << ",";
|
|
|
|
|
output << "[";
|
2023-11-30 15:28:57 +01:00
|
|
|
|
int sstatic = 1, sfwrd = 0, spred = 0, sboth = 0;
|
2017-06-28 17:11:24 +02:00
|
|
|
|
// 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
|
|
|
|
|
{
|
|
|
|
|
if (lag != -max_endo_lag)
|
|
|
|
|
output << ",";
|
2018-07-17 18:34:07 +02:00
|
|
|
|
int varID = getDerivID(symbol_table.getID(SymbolType::endogenous, endoID), lag);
|
2022-09-14 17:07:08 +02:00
|
|
|
|
output << " " << getJacobianCol(varID, false) + 1;
|
2017-06-28 17:11:24 +02:00
|
|
|
|
if (lag == -1)
|
|
|
|
|
{
|
|
|
|
|
sstatic = 0;
|
|
|
|
|
spred = 1;
|
|
|
|
|
}
|
|
|
|
|
else if (lag == 1)
|
|
|
|
|
{
|
|
|
|
|
if (spred == 1)
|
|
|
|
|
{
|
|
|
|
|
sboth = 1;
|
|
|
|
|
spred = 0;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
sstatic = 0;
|
|
|
|
|
sfwrd = 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-11-30 15:28:57 +01:00
|
|
|
|
catch (UnknownDerivIDException& e)
|
2017-06-28 17:11:24 +02:00
|
|
|
|
{
|
|
|
|
|
output << " 0";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
nstatic += sstatic;
|
2019-12-20 16:59:30 +01:00
|
|
|
|
nfwrd += sfwrd;
|
|
|
|
|
npred += spred;
|
|
|
|
|
nboth += sboth;
|
2017-06-28 17:11:24 +02:00
|
|
|
|
output << "]";
|
|
|
|
|
}
|
|
|
|
|
output << "], "
|
2020-07-02 09:32:50 +02:00
|
|
|
|
<< R"("nstatic": )" << nstatic << ", " << endl
|
|
|
|
|
<< R"("nfwrd": )" << nfwrd << ", " << endl
|
|
|
|
|
<< R"("npred": )" << npred << ", " << endl
|
|
|
|
|
<< R"("nboth": )" << nboth << ", " << endl
|
2023-11-30 15:28:57 +01:00
|
|
|
|
<< R"("nsfwrd": )" << nfwrd + nboth << ", " << endl
|
|
|
|
|
<< R"("nspred": )" << npred + nboth << ", " << endl
|
|
|
|
|
<< R"("ndynamic": )" << npred + nboth + nfwrd << ", " << endl
|
2020-07-02 09:32:50 +02:00
|
|
|
|
<< R"("maximum_endo_lag": )" << max_endo_lag << ", " << endl
|
|
|
|
|
<< R"("maximum_endo_lead": )" << max_endo_lead << ", " << endl
|
|
|
|
|
<< R"("maximum_exo_lag": )" << max_exo_lag << ", " << endl
|
|
|
|
|
<< R"("maximum_exo_lead": )" << max_exo_lead << ", " << endl
|
|
|
|
|
<< R"("maximum_exo_det_lag": )" << max_exo_det_lag << ", " << endl
|
|
|
|
|
<< R"("maximum_exo_det_lead": )" << max_exo_det_lead << ", " << endl
|
|
|
|
|
<< R"("maximum_lag": )" << max_lag << ", " << endl
|
|
|
|
|
<< R"("maximum_lead": )" << max_lead << ", " << endl
|
|
|
|
|
<< R"("orig_maximum_endo_lag": )" << max_endo_lag_orig << "," << endl
|
|
|
|
|
<< R"("orig_maximum_endo_lead": )" << max_endo_lead_orig << "," << endl
|
|
|
|
|
<< R"("orig_maximum_exo_lag": )" << max_exo_lag_orig << "," << endl
|
|
|
|
|
<< R"("orig_maximum_exo_lead": )" << max_exo_lead_orig << "," << endl
|
|
|
|
|
<< R"("orig_maximum_exo_det_lag": )" << max_exo_det_lag_orig << "," << endl
|
|
|
|
|
<< R"("orig_maximum_exo_det_lead": )" << max_exo_det_lead_orig << "," << endl
|
|
|
|
|
<< R"("orig_maximum_lag": )" << max_lag_orig << "," << endl
|
|
|
|
|
<< R"("orig_maximum_lead": )" << max_lead_orig << "," << endl
|
2023-11-30 15:28:57 +01:00
|
|
|
|
<< R"("orig_maximum_lag_with_diffs_expanded": )" << max_lag_with_diffs_expanded_orig << ","
|
|
|
|
|
<< endl
|
|
|
|
|
<< R"("NNZDerivatives": [)";
|
2021-08-18 16:52:35 +02:00
|
|
|
|
for (int i = 1; i < static_cast<int>(NNZDerivatives.size()); i++)
|
|
|
|
|
{
|
|
|
|
|
output << (i > computed_derivs_order ? -1 : NNZDerivatives[i]);
|
|
|
|
|
if (i < static_cast<int>(NNZDerivatives.size()) - 1)
|
2023-11-30 15:28:57 +01:00
|
|
|
|
output << ", ";
|
2021-08-18 16:52:35 +02:00
|
|
|
|
}
|
2023-11-30 15:28:57 +01:00
|
|
|
|
output << "]}" << endl;
|
2017-06-28 17:11:24 +02:00
|
|
|
|
}
|
|
|
|
|
|
2017-02-20 12:18:11 +01:00
|
|
|
|
void
|
2023-11-30 15:28:57 +01:00
|
|
|
|
DynamicModel::writeJsonComputingPassOutput(ostream& output, bool writeDetails) const
|
2017-02-20 12:18:11 +01:00
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
auto [mlv_output, d_output] {writeJsonComputingPassOutputHelper<true>(writeDetails)};
|
2017-02-20 12:18:11 +01:00
|
|
|
|
|
2017-03-02 18:34:18 +01:00
|
|
|
|
if (writeDetails)
|
2019-04-03 16:32:52 +02:00
|
|
|
|
output << R"("dynamic_model": {)";
|
2017-03-02 18:34:18 +01:00
|
|
|
|
else
|
2019-04-03 16:32:52 +02:00
|
|
|
|
output << R"("dynamic_model_simple": {)";
|
2022-07-12 17:47:02 +02:00
|
|
|
|
output << mlv_output.str();
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (const auto& it : d_output)
|
2019-04-18 17:07:55 +02:00
|
|
|
|
output << ", " << it.str();
|
|
|
|
|
output << "}";
|
2017-02-20 12:18:11 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2023-11-30 15:28:57 +01:00
|
|
|
|
DynamicModel::writeJsonParamsDerivatives(ostream& output, bool writeDetails) const
|
2017-02-20 12:18:11 +01:00
|
|
|
|
{
|
2018-11-15 16:39:53 +01:00
|
|
|
|
if (!params_derivatives.size())
|
2017-02-20 12:18:11 +01:00
|
|
|
|
return;
|
|
|
|
|
|
2023-11-30 15:28:57 +01:00
|
|
|
|
auto [mlv_output, tt_output, rp_output, gp_output, rpp_output, gpp_output, hp_output,
|
|
|
|
|
g3p_output] {writeJsonParamsDerivativesHelper<true>(writeDetails)};
|
2019-08-22 17:00:36 +02:00
|
|
|
|
|
2017-03-02 18:34:18 +01:00
|
|
|
|
if (writeDetails)
|
2019-04-03 16:32:52 +02:00
|
|
|
|
output << R"("dynamic_model_params_derivative": {)";
|
2017-03-02 18:34:18 +01:00
|
|
|
|
else
|
2019-04-03 16:32:52 +02:00
|
|
|
|
output << R"("dynamic_model_params_derivatives_simple": {)";
|
2023-11-30 15:28:57 +01:00
|
|
|
|
output << mlv_output.str() << ", " << tt_output.str() << ", " << rp_output.str() << ", "
|
|
|
|
|
<< gp_output.str() << ", " << rpp_output.str() << ", " << gpp_output.str() << ", "
|
|
|
|
|
<< hp_output.str() << ", " << g3p_output.str() << "}";
|
2017-02-02 15:09:43 +01:00
|
|
|
|
}
|
2018-08-01 19:41:44 +02:00
|
|
|
|
|
|
|
|
|
void
|
2023-11-30 15:28:57 +01:00
|
|
|
|
DynamicModel::substituteVarExpectation(const map<string, expr_t>& subst_table)
|
2018-08-01 19:41:44 +02:00
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (auto& equation : equations)
|
|
|
|
|
equation = dynamic_cast<BinaryOpNode*>(equation->substituteVarExpectation(subst_table));
|
2018-08-01 19:41:44 +02:00
|
|
|
|
}
|
2020-09-10 14:24:20 +02:00
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
DynamicModel::checkNoRemainingPacExpectation() const
|
|
|
|
|
{
|
|
|
|
|
for (size_t eq = 0; eq < equations.size(); eq++)
|
|
|
|
|
if (equations[eq]->containsPacExpectation())
|
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
cerr << "ERROR: in equation "
|
|
|
|
|
<< equation_tags.getTagValueByEqnAndKey(eq, "name").value_or(to_string(eq + 1))
|
2020-09-10 14:24:20 +02:00
|
|
|
|
<< ", the pac_expectation operator references an unknown pac_model" << endl;
|
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-12-06 16:29:24 +01:00
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
DynamicModel::simplifyEquations()
|
|
|
|
|
{
|
|
|
|
|
size_t last_subst_table_size = 0;
|
2023-11-30 15:28:57 +01:00
|
|
|
|
map<VariableNode*, NumConstNode*> subst_table;
|
2021-12-06 16:29:24 +01:00
|
|
|
|
// Equations with “mcp” tag are excluded, see dynare#1697
|
|
|
|
|
findConstantEquationsWithoutMcpTag(subst_table);
|
|
|
|
|
while (subst_table.size() != last_subst_table_size)
|
|
|
|
|
{
|
|
|
|
|
last_subst_table_size = subst_table.size();
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (auto& [id, definition] : local_variables_table)
|
2021-12-06 16:29:24 +01:00
|
|
|
|
definition = definition->replaceVarsInEquation(subst_table);
|
2023-11-30 15:28:57 +01:00
|
|
|
|
for (auto& equation : equations)
|
|
|
|
|
equation = dynamic_cast<BinaryOpNode*>(equation->replaceVarsInEquation(subst_table));
|
|
|
|
|
for (auto& equation : static_only_equations)
|
|
|
|
|
equation = dynamic_cast<BinaryOpNode*>(equation->replaceVarsInEquation(subst_table));
|
2021-12-06 16:29:24 +01:00
|
|
|
|
subst_table.clear();
|
|
|
|
|
findConstantEquationsWithoutMcpTag(subst_table);
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-10-26 18:06:26 +02:00
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
DynamicModel::checkNoRemainingPacTargetNonstationary() const
|
|
|
|
|
{
|
|
|
|
|
for (size_t eq = 0; eq < equations.size(); eq++)
|
|
|
|
|
if (equations[eq]->containsPacTargetNonstationary())
|
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
cerr << "ERROR: in equation "
|
|
|
|
|
<< equation_tags.getTagValueByEqnAndKey(eq, "name").value_or(to_string(eq + 1))
|
|
|
|
|
<< ", the pac_target_nonstationary operator does not match a corresponding "
|
|
|
|
|
"'pac_target_info' block"
|
|
|
|
|
<< endl;
|
2021-10-26 18:06:26 +02:00
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-01-04 16:18:53 +01:00
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
DynamicModel::checkIsLinear() const
|
|
|
|
|
{
|
|
|
|
|
if (!nonzero_hessian_eqs.empty())
|
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
cerr << "ERROR: If the model is declared linear the second derivatives must be equal to zero."
|
|
|
|
|
<< endl
|
2023-01-04 16:18:53 +01:00
|
|
|
|
<< " The following equations have non-zero second derivatives:" << endl;
|
|
|
|
|
for (auto it : nonzero_hessian_eqs)
|
|
|
|
|
{
|
2023-11-30 15:28:57 +01:00
|
|
|
|
cerr << " * Eq # " << it + 1;
|
|
|
|
|
if (optional<string> eqname {equation_tags.getTagValueByEqnAndKey(it, "name")}; eqname)
|
2023-01-04 16:18:53 +01:00
|
|
|
|
cerr << " [" << *eqname << "]";
|
|
|
|
|
cerr << endl;
|
|
|
|
|
}
|
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
|
}
|
|
|
|
|
}
|