diff --git a/src/DynareBison.yy b/src/DynareBison.yy index caa7d611d258e3bc13f852c160b11aa770505816..9b9aae02757fdfad9377d9333de5f464dbb78683 100644 --- a/src/DynareBison.yy +++ b/src/DynareBison.yy @@ -175,7 +175,7 @@ class ParsingDriver; %token HETEROSKEDASTIC_FILTER TIME_SHIFT STRUCTURAL TERMINAL_STEADY_STATE_AS_GUESS_VALUE CONSTANT_SIMULATION_LENGTH %token SURPRISE OCCBIN_CONSTRAINTS %token PAC_TARGET_INFO COMPONENT TARGET AUXNAME AUXNAME_TARGET_NONSTATIONARY PAC_TARGET_NONSTATIONARY -%token <string> KIND LL DL DD +%token <string> KIND LL DL DD ADD MULTIPLY /* Method of Moments */ %token METHOD_OF_MOMENTS MOM_METHOD %token BARTLETT_KERNEL_LAG WEIGHTING_MATRIX WEIGHTING_MATRIX_SCALING_FACTOR ANALYTIC_STANDARD_ERRORS ANALYTIC_JACOBIAN PENALIZED_ESTIMATOR VERBOSE @@ -1152,7 +1152,11 @@ shock_elem : det_shock_elem ; det_shock_elem : VAR symbol ';' PERIODS period_list ';' VALUES value_list ';' - { driver.add_det_shock($2, $5, $8, false); } + { driver.add_det_shock($2, $5, $8, ParsingDriver::DetShockType::standard); } + | VAR symbol ';' PERIODS period_list ';' ADD value_list ';' + { driver.add_det_shock($2, $5, $8, ParsingDriver::DetShockType::add); } + | VAR symbol ';' PERIODS period_list ';' MULTIPLY value_list ';' + { driver.add_det_shock($2, $5, $8, ParsingDriver::DetShockType::multiply); } ; det_shock_list : det_shock_list det_shock_elem @@ -3207,7 +3211,7 @@ conditional_forecast_paths_shock_list : conditional_forecast_paths_shock_elem ; conditional_forecast_paths_shock_elem : VAR symbol ';' PERIODS period_list ';' VALUES value_list ';' - { driver.add_det_shock($2, $5, $8, true); } + { driver.add_det_shock($2, $5, $8, ParsingDriver::DetShockType::conditional_forecast); } ; steady_state_model : STEADY_STATE_MODEL ';' { driver.begin_steady_state_model(); } @@ -4284,6 +4288,8 @@ symbol : NAME | LL | DL | DD + | ADD + | MULTIPLY ; %% diff --git a/src/DynareFlex.ll b/src/DynareFlex.ll index 40dc09bfd77c4730718f1b62d5351760bf77c3fb..8f0e1b1a9f7dfa10b027150e3f7930751ee87eb8 100644 --- a/src/DynareFlex.ll +++ b/src/DynareFlex.ll @@ -770,6 +770,14 @@ DATE -?[0-9]+([ya]|m([1-9]|1[0-2])|q[1-4]) <DYNARE_BLOCK>corr {return token::CORR;} <DYNARE_BLOCK>periods {return token::PERIODS;} <DYNARE_BLOCK>scales {return token::SCALES;} +<DYNARE_BLOCK>add { + yylval->build<string>(yytext); + return token::ADD; +} +<DYNARE_BLOCK>multiply { + yylval->build<string>(yytext); + return token::MULTIPLY; +} <DYNARE_STATEMENT,DYNARE_BLOCK>cutoff {return token::CUTOFF;} <DYNARE_STATEMENT,DYNARE_BLOCK>mfs {return token::MFS;} <DYNARE_STATEMENT,DYNARE_BLOCK>balanced_growth_test_tol {return token::BALANCED_GROWTH_TEST_TOL;} diff --git a/src/ParsingDriver.cc b/src/ParsingDriver.cc index 806a4642816ea73dd298027ddad7783788b8eff3..2b1772487e734823449b08c612494baa6eb30dad 100644 --- a/src/ParsingDriver.cc +++ b/src/ParsingDriver.cc @@ -841,6 +841,10 @@ ParsingDriver::end_shocks(bool overwrite) mod_file->addStatement(make_unique<ShocksStatement>(overwrite, det_shocks, var_shocks, std_shocks, covar_shocks, corr_shocks, mod_file->symbol_table)); det_shocks.clear(); + if (!learnt_shocks_add.empty()) + error("shocks: 'add' keyword not allowed unless 'learnt_in' option is passed"); + if (!learnt_shocks_multiply.empty()) + error("shocks: 'multiply' keyword not allowed unless 'learnt_in' option is passed"); var_shocks.clear(); std_shocks.clear(); covar_shocks.clear(); @@ -852,6 +856,10 @@ ParsingDriver::end_mshocks(bool overwrite) { mod_file->addStatement(make_unique<MShocksStatement>(overwrite, det_shocks, mod_file->symbol_table)); det_shocks.clear(); + if (!learnt_shocks_add.empty()) + error("mshocks: 'add' keyword not allowed"); + if (!learnt_shocks_multiply.empty()) + error("mshocks: 'multiply' keyword not allowed"); } void @@ -859,6 +867,10 @@ ParsingDriver::end_shocks_surprise(bool overwrite) { mod_file->addStatement(make_unique<ShocksSurpriseStatement>(overwrite, det_shocks, mod_file->symbol_table)); det_shocks.clear(); + if (!learnt_shocks_add.empty()) + error("shocks(surprise): 'add' keyword not allowed"); + if (!learnt_shocks_multiply.empty()) + error("shocks(surprise): 'multiply' keyword not allowed"); } void @@ -871,8 +883,35 @@ ParsingDriver::end_shocks_learnt_in(const string &learnt_in_period, bool overwri for (auto [period1, period2, expr] : vals) if (period1 < learnt_in_period_int) error("shocks: for variable " + mod_file->symbol_table.getName(symb_id) + ", shock period (" + to_string(period1) + ") is earlier than the period in which the shock is learnt (" + learnt_in_period + ")"); - mod_file->addStatement(make_unique<ShocksLearntInStatement>(learnt_in_period_int, overwrite, det_shocks, mod_file->symbol_table)); + + // Aggregate the three types of shocks + ShocksLearntInStatement::learnt_shocks_t learnt_shocks; + for (const auto &[id, v] : det_shocks) + { + vector<tuple<ShocksLearntInStatement::LearntShockType, int, int, expr_t>> v2; + for (auto [period1, period2, value] : v) + v2.emplace_back(ShocksLearntInStatement::LearntShockType::level, period1, period2, value); + learnt_shocks[id] = v2; + } + for (const auto &[id, v] : learnt_shocks_add) + { + vector<tuple<ShocksLearntInStatement::LearntShockType, int, int, expr_t>> v2; + for (auto [period1, period2, value] : v) + v2.emplace_back(ShocksLearntInStatement::LearntShockType::add, period1, period2, value); + learnt_shocks[id] = v2; + } + for (const auto &[id, v] : learnt_shocks_multiply) + { + vector<tuple<ShocksLearntInStatement::LearntShockType, int, int, expr_t>> v2; + for (auto [period1, period2, value] : v) + v2.emplace_back(ShocksLearntInStatement::LearntShockType::multiply, period1, period2, value); + learnt_shocks[id] = v2; + } + + mod_file->addStatement(make_unique<ShocksLearntInStatement>(learnt_in_period_int, overwrite, learnt_shocks, mod_file->symbol_table)); det_shocks.clear(); + learnt_shocks_add.clear(); + learnt_shocks_multiply.clear(); } void @@ -885,16 +924,28 @@ ParsingDriver::end_heteroskedastic_shocks(bool overwrite) } void -ParsingDriver::add_det_shock(const string &var, const vector<pair<int, int>> &periods, const vector<expr_t> &values, bool conditional_forecast) +ParsingDriver::add_det_shock(const string &var, const vector<pair<int, int>> &periods, const vector<expr_t> &values, DetShockType type) { - if (conditional_forecast) - check_symbol_is_endogenous(var); - else - check_symbol_is_exogenous(var, true); + switch (type) + { + case DetShockType::conditional_forecast: + check_symbol_is_endogenous(var); + break; + case DetShockType::standard: + // Allow exo_det, for stochastic context + check_symbol_is_exogenous(var, true); + break; + case DetShockType::add: + case DetShockType::multiply: + check_symbol_is_exogenous(var, false); + break; + } int symb_id = mod_file->symbol_table.getID(var); - if (det_shocks.find(symb_id) != det_shocks.end()) + if (det_shocks.find(symb_id) != det_shocks.end() + || learnt_shocks_add.find(symb_id) != learnt_shocks_add.end() + || learnt_shocks_multiply.find(symb_id) != learnt_shocks_multiply.end()) error("shocks/conditional_forecast_paths: variable " + var + " declared twice"); if (periods.size() != values.size()) @@ -905,7 +956,19 @@ ParsingDriver::add_det_shock(const string &var, const vector<pair<int, int>> &pe for (size_t i = 0; i < periods.size(); i++) v.emplace_back(periods[i].first, periods[i].second, values[i]); - det_shocks[symb_id] = v; + switch (type) + { + case DetShockType::standard: + case DetShockType::conditional_forecast: + det_shocks[symb_id] = v; + break; + case DetShockType::add: + learnt_shocks_add[symb_id] = v; + break; + case DetShockType::multiply: + learnt_shocks_multiply[symb_id] = v; + break; + } } void @@ -2435,6 +2498,10 @@ ParsingDriver::conditional_forecast_paths() { mod_file->addStatement(make_unique<ConditionalForecastPathsStatement>(det_shocks, mod_file->symbol_table)); det_shocks.clear(); + if (!learnt_shocks_add.empty()) + error("conditional_forecast_paths: 'add' keyword not allowed"); + if (!learnt_shocks_multiply.empty()) + error("conditional_forecast_paths: 'multiply' keyword not allowed"); } void diff --git a/src/ParsingDriver.hh b/src/ParsingDriver.hh index 8b3af55fa14ef03810869214ebc1acea6a590a5a..e53b5307a15ea73d4dbb23c6469f04db84f0c8da 100644 --- a/src/ParsingDriver.hh +++ b/src/ParsingDriver.hh @@ -150,8 +150,11 @@ private: //! Temporary storage of covariances from optim_weights OptimWeightsStatement::covar_weights_t covar_weights; /* Temporary storage for deterministic shocks. Also used for - conditional_forecast paths and for surprise shocks. */ + conditional_forecast paths, for shocks(surprise), and shocks(learnt_in=…) + (for the latter, only used for shocks declared in level through “values”). */ ShocksStatement::det_shocks_t det_shocks; + // Temporary storage for shocks declared with “add” and “multiply” in shocks(learnt_in=…) + ShocksStatement::det_shocks_t learnt_shocks_add, learnt_shocks_multiply; //! Temporary storage for variances of shocks ShocksStatement::var_and_std_shocks_t var_shocks; //! Temporary storage for standard errors of shocks @@ -455,7 +458,14 @@ public: void end_heteroskedastic_shocks(bool overwrite); /* Adds a deterministic shock, a path element inside a conditional_forecast_paths block, or a surprise shock */ - void add_det_shock(const string &var, const vector<pair<int, int>> &periods, const vector<expr_t> &values, bool conditional_forecast); + enum class DetShockType + { + standard, + add, // for “add” in “shocks(learnt_in)” + multiply, // for “multiply” in “shocks(learnt_in)” + conditional_forecast + }; + void add_det_shock(const string &var, const vector<pair<int, int>> &periods, const vector<expr_t> &values, DetShockType type); //! Adds a heteroskedastic shock (either values or scales) void add_heteroskedastic_shock(const string &var, const vector<pair<int, int>> &periods, const vector<expr_t> &values, bool scales); //! Adds a std error shock diff --git a/src/Shocks.cc b/src/Shocks.cc index 240c464cae69b7463008e33a1762d65ea0834ec4..62e50d988d1b2bf9d0046bae9783430de6b3823c 100644 --- a/src/Shocks.cc +++ b/src/Shocks.cc @@ -492,7 +492,7 @@ ShocksSurpriseStatement::writeJsonOutput(ostream &output) const } ShocksLearntInStatement::ShocksLearntInStatement(int learnt_in_period_arg, bool overwrite_arg, - AbstractShocksStatement::det_shocks_t learnt_shocks_arg, + learnt_shocks_t learnt_shocks_arg, const SymbolTable &symbol_table_arg) : learnt_in_period{learnt_in_period_arg}, overwrite{overwrite_arg}, learnt_shocks{move(learnt_shocks_arg)}, symbol_table{symbol_table_arg} @@ -505,6 +505,21 @@ ShocksLearntInStatement::checkPass(ModFileStructure &mod_file_struct, WarningCon mod_file_struct.shocks_learnt_in_present = true; } +string +ShocksLearntInStatement::typeToString(LearntShockType type) +{ + switch (type) + { + case LearntShockType::level: + return "level"; + case LearntShockType::add: + return "add"; + case LearntShockType::multiply: + return "multiply"; + } + exit(EXIT_FAILURE); // Silence GCC warning +} + void ShocksLearntInStatement::writeOutput(ostream &output, const string &basename, bool minimal_workspace) const { @@ -516,11 +531,12 @@ ShocksLearntInStatement::writeOutput(ostream &output, const string &basename, bo output << "M_.learnt_shocks = [ M_.learnt_shocks;" << endl; for (const auto &[id, shock_vec] : learnt_shocks) { - for (const auto &[period1, period2, value] : shock_vec) + for (const auto &[type, period1, period2, value] : shock_vec) { output << "struct('learnt_in'," << learnt_in_period << ",'exo_id'," << symbol_table.getTypeSpecificID(id)+1 << ",'periods'," << period1 << ":" << period2 + << ",'type','" << typeToString(type) << "'" << ",'value',"; value->writeOutput(output); output << ");" << endl; @@ -544,11 +560,12 @@ ShocksLearntInStatement::writeJsonOutput(ostream &output) const << R"("values": [)"; for (auto it1 = it->second.begin(); it1 != it->second.end(); ++it1) { - auto [period1, period2, value] = *it1; + auto [type, period1, period2, value] = *it1; if (it1 != it->second.begin()) output << ", "; output << R"({"period1": )" << period1 << ", " << R"("period2": )" << period2 << ", " + << R"("type": ")" << typeToString(type) << R"(", )" << R"("value": ")"; value->writeJsonOutput(output, {}, {}); output << R"("})"; diff --git a/src/Shocks.hh b/src/Shocks.hh index dac2ab0c224c8160f2a61470309980ad81321dd0..6280b1d330dba8435965a33b063c02947adcc60a 100644 --- a/src/Shocks.hh +++ b/src/Shocks.hh @@ -115,12 +115,21 @@ public: const int learnt_in_period; //! Does this "shocks(learnt_in=…)" statement replace the previous ones? const bool overwrite; - const AbstractShocksStatement::det_shocks_t learnt_shocks; + enum class LearntShockType + { + level, + add, + multiply + }; + // The tuple is (type, period1, period2, value) + using learnt_shocks_t = map<int, vector<tuple<LearntShockType, int, int, expr_t>>>; + const learnt_shocks_t learnt_shocks; private: const SymbolTable &symbol_table; + static string typeToString(LearntShockType type); public: ShocksLearntInStatement(int learnt_in_period_arg, bool overwrite_arg, - AbstractShocksStatement::det_shocks_t learnt_shocks_arg, + learnt_shocks_t learnt_shocks_arg, const SymbolTable &symbol_table_arg); void checkPass(ModFileStructure &mod_file_struct, WarningConsolidation &warnings) override; void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;