diff --git a/src/DynareBison.yy b/src/DynareBison.yy index d7b058465461a4e7e35386160b90b6e7055023ba..b6fc9a3bab1de4b8c4893ca20f89dd964cc14eb7 100644 --- a/src/DynareBison.yy +++ b/src/DynareBison.yy @@ -219,7 +219,7 @@ str_tolower(string s) %token HOMOTOPY_MAX_COMPLETION_SHARE HOMOTOPY_MIN_STEP_SIZE HOMOTOPY_INITIAL_STEP_SIZE HOMOTOPY_STEP_SIZE_INCREASE_SUCCESS_COUNT %token HOMOTOPY_LINEARIZATION_FALLBACK HOMOTOPY_MARGINAL_LINEARIZATION_FALLBACK HOMOTOPY_EXCLUDE_VAREXO FROM_INITVAL_TO_ENDVAL %token STATIC_MFS RELATIVE_TO_INITVAL MATCHED_IRFS MATCHED_IRFS_WEIGHTS WEIGHTS PERPENDICULAR -%token HETEROGENEITY HETEROGENEITY_DIMENSION SUM +%token HETEROGENEITY HETEROGENEITY_DIMENSION SUM PERFECT_FORESIGHT_CONTROLLED_PATHS EXOGENIZE ENDOGENIZE %token <vector<string>> SYMBOL_VEC @@ -263,6 +263,8 @@ str_tolower(string s) %type <tuple<string, string, string>> matched_irfs_weights_elem_var_varexo %type <pair<tuple<string, string, string, string, string, string>, expr_t>> matched_irfs_weights_elem %type <map<tuple<string, string, string, string, string, string>, expr_t>> matched_irfs_weights_list +%type <tuple<string, vector<AbstractShocksStatement::period_range_t>, vector<expr_t>, string>> perfect_foresight_controlled_paths_elem +%type <vector<tuple<string, vector<AbstractShocksStatement::period_range_t>, vector<expr_t>, string>>> perfect_foresight_controlled_paths_list %% %start statement_list; @@ -384,6 +386,7 @@ statement : parameters | perfect_foresight_solver | perfect_foresight_with_expectation_errors_setup | perfect_foresight_with_expectation_errors_solver + | perfect_foresight_controlled_paths | prior_function | posterior_function | method_of_moments @@ -1614,6 +1617,31 @@ perfect_foresight_with_expectation_errors_solver_options : o_pfwee_constant_simu | perfect_foresight_solver_options ; +perfect_foresight_controlled_paths : PERFECT_FORESIGHT_CONTROLLED_PATHS ';' perfect_foresight_controlled_paths_list END ';' + { driver.perfect_foresight_controlled_paths($3, 1); } +| PERFECT_FORESIGHT_CONTROLLED_PATHS '(' LEARNT_IN EQUAL integer_or_date ')' ';' perfect_foresight_controlled_paths_list END ';' + { driver.perfect_foresight_controlled_paths($8, $5); } + ; + +perfect_foresight_controlled_paths_list : perfect_foresight_controlled_paths_list perfect_foresight_controlled_paths_elem + { + $$ = $1; + $$.push_back($2); + } + | perfect_foresight_controlled_paths_elem + { $$ = { $1 }; } + ; + +perfect_foresight_controlled_paths_elem : EXOGENIZE symbol ';' PERIODS period_list ';' VALUES value_list ';' ENDOGENIZE symbol ';' + { + driver.check_symbol_is_endogenous($2); + driver.check_symbol_is_exogenous($11, false); + if ($5.size() != $8.size()) + driver.error("The number of periods is different from the number of values"); + $$ = { $2, $5, $8, $11}; + } + ; + method_of_moments : METHOD_OF_MOMENTS ';' { driver.method_of_moments(); } | METHOD_OF_MOMENTS '(' method_of_moments_options_list ')' ';' diff --git a/src/DynareFlex.ll b/src/DynareFlex.ll index 9f8cfc07fff45a6c62297e477fa71e2049a2057d..7a4aa1eb3525a2cf04b798087b181c29ae74e94f 100644 --- a/src/DynareFlex.ll +++ b/src/DynareFlex.ll @@ -236,6 +236,7 @@ DATE -?[0-9]+([ya]|m([1-9]|1[0-2])|q[1-4]|[sh][12]) <INITIAL>pac_target_info {BEGIN DYNARE_BLOCK; return token::PAC_TARGET_INFO;} <INITIAL>matched_irfs {BEGIN DYNARE_BLOCK; return token::MATCHED_IRFS;} <INITIAL>matched_irfs_weights {BEGIN DYNARE_BLOCK; return token::MATCHED_IRFS_WEIGHTS;} +<INITIAL>perfect_foresight_controlled_paths {BEGIN DYNARE_BLOCK; return token::PERFECT_FORESIGHT_CONTROLLED_PATHS;} /* For the semicolon after an "end" keyword */ <INITIAL>; {return Dynare::parser::token_type (yytext[0]);} @@ -847,6 +848,8 @@ DATE -?[0-9]+([ya]|m([1-9]|1[0-2])|q[1-4]|[sh][12]) return token::DD; } <DYNARE_BLOCK>weights {return token::WEIGHTS;} +<DYNARE_BLOCK>exogenize {return token::EXOGENIZE;} +<DYNARE_BLOCK>endogenize {return token::ENDOGENIZE;} /* Inside Dynare statement */ <DYNARE_STATEMENT>solve_algo {return token::SOLVE_ALGO;} diff --git a/src/ModFile.cc b/src/ModFile.cc index 5a0f38c064303bd443954d5588db13bb595eb68a..928ec611b51b1c724c52a1c7c22c13fc15d49dc1 100644 --- a/src/ModFile.cc +++ b/src/ModFile.cc @@ -1,5 +1,5 @@ /* - * Copyright © 2006-2024 Dynare Team + * Copyright © 2006-2025 Dynare Team * * This file is part of Dynare. * @@ -1137,7 +1137,8 @@ ModFile::writeMOutput(const string& basename, bool clear_all, bool clear_global, << "M_.heteroskedastic_shocks.Qvalue_orig = [];" << endl << "M_.heteroskedastic_shocks.Qscale_orig = [];" << endl << "M_.matched_irfs = {};" << endl - << "M_.matched_irfs_weights = {};" << endl; + << "M_.matched_irfs_weights = {};" << endl + << "M_.perfect_foresight_controlled_paths = [];" << endl; // NB: options_.{ramsey,discretionary}_policy should rather be fields of M_ mOutputFile << boolalpha << "options_.linear = " << linear << ";" << endl diff --git a/src/ParsingDriver.cc b/src/ParsingDriver.cc index abbbf4680a9ba0aea3af6a0781cffa18f04e6629..545b6ab5d833b34c08cb899b93a638eb2702e34d 100644 --- a/src/ParsingDriver.cc +++ b/src/ParsingDriver.cc @@ -1,5 +1,5 @@ /* - * Copyright © 2003-2024 Dynare Team + * Copyright © 2003-2025 Dynare Team * * This file is part of Dynare. * @@ -3747,6 +3747,29 @@ ParsingDriver::perfect_foresight_with_expectation_errors_solver() options_list.clear(); } +void +ParsingDriver::perfect_foresight_controlled_paths( + const vector<tuple<string, vector<AbstractShocksStatement::period_range_t>, vector<expr_t>, + string>>& paths, + variant<int, string> learnt_in_period) +{ + PerfectForesightControlledPathsStatement::paths_t paths_transformed; + for (const auto& [exogenize, periods, values, endogenize] : paths) + { + int exogenize_id = mod_file->symbol_table.getID(exogenize); + int endogenize_id = mod_file->symbol_table.getID(endogenize); + + vector<pair<AbstractShocksStatement::period_range_t, expr_t>> v; + + for (size_t i = 0; i < periods.size(); i++) + v.emplace_back(periods[i], values[i]); + + paths_transformed.emplace_back(exogenize_id, move(v), endogenize_id); + } + mod_file->addStatement(make_unique<PerfectForesightControlledPathsStatement>( + move(paths_transformed), move(learnt_in_period), mod_file->symbol_table)); +} + void ParsingDriver::method_of_moments() { diff --git a/src/ParsingDriver.hh b/src/ParsingDriver.hh index df251e792b9d40b4e63040a5fb0bafb7544c6262..ea387be7e3ac53a5f174567385d64d3a4896769f 100644 --- a/src/ParsingDriver.hh +++ b/src/ParsingDriver.hh @@ -1,5 +1,5 @@ /* - * Copyright © 2003-2024 Dynare Team + * Copyright © 2003-2025 Dynare Team * * This file is part of Dynare. * @@ -921,6 +921,10 @@ public: void perfect_foresight_solver(); void perfect_foresight_with_expectation_errors_setup(); void perfect_foresight_with_expectation_errors_solver(); + void perfect_foresight_controlled_paths( + const vector<tuple<string, vector<AbstractShocksStatement::period_range_t>, vector<expr_t>, + string>>& paths, + variant<int, string> learnt_in_period); void prior_posterior_function(bool prior_func); //! Method of Moments estimation statement void method_of_moments(); diff --git a/src/Shocks.cc b/src/Shocks.cc index 3c33baf2ec06e9af90d475e0cbc7881502142f9e..49cfdfa812152dfb1cb0b269e084ce4dd1e2585c 100644 --- a/src/Shocks.cc +++ b/src/Shocks.cc @@ -45,6 +45,17 @@ static auto print_json_period_range = []<class T>(ostream& output, const T& arg) static_assert(always_false_v<T>, "Non-exhaustive visitor!"); }; +static auto print_matlab_learnt_in = [](ostream& output, const auto& p) { output << p; }; + +static auto print_json_learnt_in = []<class T>(ostream& output, const T& p) { + if constexpr (is_same_v<T, int>) + output << p; + else if constexpr (is_same_v<T, string>) + output << '"' << p << '"'; + else + static_assert(always_false_v<T>, "Non-exhaustive visitor!"); +}; + AbstractShocksStatement::AbstractShocksStatement(bool overwrite_arg, ShockType type_arg, det_shocks_t det_shocks_arg, const SymbolTable& symbol_table_arg) : @@ -564,7 +575,6 @@ void ShocksLearntInStatement::writeOutput(ostream& output, [[maybe_unused]] const string& basename, [[maybe_unused]] bool minimal_workspace) const { - auto print_matlab_learnt_in = [&](const auto& p) { output << p; }; if (overwrite) { output << "if ~isempty(M_.learnt_shocks)" << endl @@ -576,7 +586,7 @@ ShocksLearntInStatement::writeOutput(ostream& output, [[maybe_unused]] const str output << "') || x ~= "; /* NB: date expression not parenthesized since it can only contain a + operator, which has higher precedence than ~= and || */ - visit(print_matlab_learnt_in, learnt_in_period); + visit(bind(print_matlab_learnt_in, ref(output), placeholders::_1), learnt_in_period); output << ", {M_.learnt_shocks.learnt_in}));" << endl << "end" << endl; } @@ -585,7 +595,7 @@ ShocksLearntInStatement::writeOutput(ostream& output, [[maybe_unused]] const str for (const auto& [type, period_range, value] : shock_vec) { output << "struct('learnt_in',"; - visit(print_matlab_learnt_in, learnt_in_period); + visit(bind(print_matlab_learnt_in, ref(output), placeholders::_1), learnt_in_period); output << ",'exo_id'," << symbol_table.getTypeSpecificID(id) + 1 << ",'periods',"; visit(bind(print_matlab_period_range, ref(output), placeholders::_1), period_range); output << ",'type','" << typeToString(type) << "'" @@ -601,16 +611,7 @@ ShocksLearntInStatement::writeJsonOutput(ostream& output) const { output << R"({"statementName": "shocks")" << R"(, "learnt_in": )"; - visit( - [&]<class T>(const T& p) { - if constexpr (is_same_v<T, int>) - output << p; - else if constexpr (is_same_v<T, string>) - output << '"' << p << '"'; - else - static_assert(always_false_v<T>, "Non-exhaustive visitor!"); - }, - learnt_in_period); + visit(bind(print_json_learnt_in, ref(output), placeholders::_1), learnt_in_period); output << R"(, "overwrite": )" << boolalpha << overwrite << R"(, "learnt_shocks": [)"; for (bool printed_something {false}; const auto& [id, shock_vec] : learnt_shocks) { @@ -915,6 +916,65 @@ ConditionalForecastPathsStatement::writeJsonOutput(ostream& output) const output << "]}"; } +PerfectForesightControlledPathsStatement::PerfectForesightControlledPathsStatement( + paths_t paths_arg, variant<int, string> learnt_in_period_arg, + const SymbolTable& symbol_table_arg) : + paths {move(paths_arg)}, + learnt_in_period {move(learnt_in_period_arg)}, + symbol_table {symbol_table_arg} +{ +} + +void +PerfectForesightControlledPathsStatement::writeOutput(ostream& output, + [[maybe_unused]] const string& basename, + [[maybe_unused]] bool minimal_workspace) const +{ + for (const auto& [exogenize_id, constraints, endogenize_id] : paths) + for (const auto& [period_range, value] : constraints) + { + output << "M_.perfect_foresight_controlled_paths = [ " + "M_.perfect_foresight_controlled_paths;" + << endl + << "struct('exogenize_id'," << symbol_table.getTypeSpecificID(exogenize_id) + 1 + << ",'periods',"; + visit(bind(print_matlab_period_range, ref(output), placeholders::_1), period_range); + output << ",'value',"; + value->writeOutput(output); + output << ",'endogenize_id'," << symbol_table.getTypeSpecificID(endogenize_id) + 1 + << ",'learnt_in',"; + visit(bind(print_matlab_learnt_in, ref(output), placeholders::_1), learnt_in_period); + output << ") ];" << endl; + } +} + +void +PerfectForesightControlledPathsStatement::writeJsonOutput(ostream& output) const +{ + output << R"({"statementName": "perfect_foresight_controlled_paths")" + << R"(, "paths": [)"; + for (bool printed_something {false}; + const auto& [exogenize_id, constraints, endogenize_id] : paths) + { + if (exchange(printed_something, true)) + output << ", "; + output << R"({"exogenize": ")" << symbol_table.getName(exogenize_id) << R"(", )" + << R"("values": [)"; + for (bool printed_something2 {false}; const auto& [period_range, value] : constraints) + { + if (exchange(printed_something2, true)) + output << ", "; + output << "{"; + visit(bind(print_json_period_range, ref(output), placeholders::_1), period_range); + output << R"(, "value": ")"; + value->writeJsonOutput(output, {}, {}); + output << R"("})"; + } + output << R"(], "endogenize": ")" << symbol_table.getName(endogenize_id) << R"("})"; + } + output << "]}"; +} + MomentCalibration::MomentCalibration(constraints_t constraints_arg, const SymbolTable& symbol_table_arg) : constraints {move(constraints_arg)}, symbol_table {symbol_table_arg} diff --git a/src/Shocks.hh b/src/Shocks.hh index dc021e4ab7604d422ad624c2393a9d87b75f4d40..fabffe54d230adac1c4bad43fddfe137d35485c5 100644 --- a/src/Shocks.hh +++ b/src/Shocks.hh @@ -1,5 +1,5 @@ /* - * Copyright © 2003-2024 Dynare Team + * Copyright © 2003-2025 Dynare Team * * This file is part of Dynare. * @@ -216,6 +216,26 @@ public: static int computePathLength(const AbstractShocksStatement::det_shocks_t& paths); }; +class PerfectForesightControlledPathsStatement : public Statement +{ +public: + // (exogenize_id, vector of (period range, value), endogenize_id) + using paths_t + = vector<tuple<int, vector<pair<AbstractShocksStatement::period_range_t, expr_t>>, int>>; + +private: + const paths_t paths; + const variant<int, string> learnt_in_period; + const SymbolTable& symbol_table; + +public: + PerfectForesightControlledPathsStatement(paths_t paths_arg, + variant<int, string> learnt_in_period_arg, + const SymbolTable& symbol_table_arg); + void writeOutput(ostream& output, const string& basename, bool minimal_workspace) const override; + void writeJsonOutput(ostream& output) const override; +}; + class MomentCalibration : public Statement { public: