From 08ac455fcddd032058738669ed6a84fda9c76e05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Villemot?= <sebastien@dynare.org> Date: Thu, 15 Jul 2021 16:24:21 +0200 Subject: [PATCH] =?UTF-8?q?Implement=20=E2=80=9Coccbin=5Fconstraints?= =?UTF-8?q?=E2=80=9D=20block?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Consequently drop “occbin” option to “model”. Incidentally, allow more values in equation tag names (previously some keywords such as “alpha” were disallowed). Ref. #68 --- src/ComputingTasks.cc | 67 +++++++++++++++++++++++++++++++++++++++++++ src/ComputingTasks.hh | 14 +++++++++ src/DynamicModel.cc | 4 --- src/DynareBison.yy | 57 ++++++++++++++++++++++++++++++++---- src/DynareFlex.ll | 18 +++++++++++- src/EquationTags.cc | 37 ------------------------ src/EquationTags.hh | 5 ---- src/ModFile.cc | 19 ++++-------- src/ModFile.hh | 3 -- src/ModelTree.cc | 6 ---- src/ModelTree.hh | 4 --- src/ParsingDriver.cc | 28 ++++++++++++++---- src/ParsingDriver.hh | 6 ++-- src/Statement.hh | 2 ++ 14 files changed, 182 insertions(+), 88 deletions(-) diff --git a/src/ComputingTasks.cc b/src/ComputingTasks.cc index d57356c9..14618870 100644 --- a/src/ComputingTasks.cc +++ b/src/ComputingTasks.cc @@ -5233,3 +5233,70 @@ MatchedMomentsStatement::writeJsonOutput(ostream &output) const } output << "]}" << endl; } + +OccbinConstraintsStatement::OccbinConstraintsStatement(const SymbolTable &symbol_table_arg, + const vector<tuple<string, expr_t, expr_t, expr_t, expr_t>> constraints_arg) + : symbol_table{symbol_table_arg}, constraints{constraints_arg} +{ +} + +void +OccbinConstraintsStatement::checkPass(ModFileStructure &mod_file_struct, WarningConsolidation &warnings) +{ + if (mod_file_struct.occbin_constraints_present) + { + cerr << "ERROR: Multiple 'occbin_constraints' blocks are not allowed" << endl; + exit(EXIT_FAILURE); + } + mod_file_struct.occbin_constraints_present = true; +} + +void +OccbinConstraintsStatement::writeOutput(ostream &output, const string &basename, bool minimal_workspace) const +{ + output << "M_.occbin.constraint = [" << endl; + for (const auto &[name, bind, relax, error_bind, error_relax] : constraints) + { + output << "struct('pswitch','" << name << "','bind','"; + bind->writeJsonOutput(output, {}, {}); + output << "','relax','"; + if (relax) + relax->writeJsonOutput(output, {}, {}); + output << "','error_bind','"; + if (error_bind) + error_bind->writeJsonOutput(output, {}, {}); + output << "','error_relax','"; + if (error_relax) + error_relax->writeJsonOutput(output, {}, {}); + output << "');" << endl; + } + output << "];" << endl + << "[M_, oo_, options_] = occbin.initialize(M_, oo_, options_);" << endl; +} + +void +OccbinConstraintsStatement::writeJsonOutput(ostream &output) const +{ + output << R"({"statementName": "occbin_constraints", "constraints": [)" << endl; + for (auto it = constraints.begin(); it != constraints.end(); ++it) + { + auto [name, bind, relax, error_bind, error_relax] = *it; + + output << R"({ "name": ")" << name << R"(", "bind": ")"; + bind->writeJsonOutput(output, {}, {}); + output << R"(", "relax": ")"; + if (relax) + relax->writeJsonOutput(output, {}, {}); + output << R"(", "error_bind": ")"; + if (error_bind) + error_bind->writeJsonOutput(output, {}, {}); + output << R"(", "error_relax": ")"; + if (error_relax) + error_relax->writeJsonOutput(output, {}, {}); + output << R"(" })"; + if (next(it) != constraints.end()) + output << ','; + output << endl; + } + output << "]}" << endl; +} diff --git a/src/ComputingTasks.hh b/src/ComputingTasks.hh index dbf99d4e..46e6e390 100644 --- a/src/ComputingTasks.hh +++ b/src/ComputingTasks.hh @@ -1254,4 +1254,18 @@ public: void writeJsonOutput(ostream &output) const override; }; +class OccbinConstraintsStatement : public Statement +{ +private: + const SymbolTable &symbol_table; +public: + // The tuple is (name, bind, relax, error_bind, error_relax) (where relax and error_{bind,relax} can be nullptr) + const vector<tuple<string, expr_t, expr_t, expr_t, expr_t>> constraints; + OccbinConstraintsStatement(const SymbolTable &symbol_table_arg, + const vector<tuple<string, expr_t, expr_t, expr_t, expr_t>> constraints_arg); + void checkPass(ModFileStructure &mod_file_struct, WarningConsolidation &warnings) override; + void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override; + void writeJsonOutput(ostream &output) const override; +}; + #endif diff --git a/src/DynamicModel.cc b/src/DynamicModel.cc index 7beab035..da888776 100644 --- a/src/DynamicModel.cc +++ b/src/DynamicModel.cc @@ -2894,10 +2894,6 @@ DynamicModel::writeDriverOutput(ostream &output, const string &basename, bool bl // Write equation tags equation_tags.writeOutput(output); - // Write Occbin tags - if (occbin) - equation_tags.writeOccbinOutput(output); - // Write mapping for variables and equations they are present in for (const auto &variable : variableMapping) { diff --git a/src/DynareBison.yy b/src/DynareBison.yy index ddcb24d8..6396a717 100644 --- a/src/DynareBison.yy +++ b/src/DynareBison.yy @@ -107,7 +107,7 @@ class ParsingDriver; %token USE_PENALIZED_OBJECTIVE_FOR_HESSIAN INIT_STATE FAST_REALTIME RESCALE_PREDICTION_ERROR_COVARIANCE GENERATE_IRFS %token NAN_CONSTANT NO_STATIC NOBS NOCONSTANT NODISPLAY NOCORR NODIAGNOSTIC NOFUNCTIONS NO_HOMOTOPY %token NOGRAPH POSTERIOR_NOGRAPH POSTERIOR_GRAPH NOMOMENTS NOPRINT NORMAL_PDF SAVE_DRAWS MODEL_NAME STDERR_MULTIPLES DIAGONAL_ONLY -%token DETERMINISTIC_TRENDS OBSERVATION_TRENDS OPTIM OPTIM_WEIGHTS ORDER OSR OSR_PARAMS MAX_DIM_COVA_GROUP ADVANCED OUTFILE OUTVARS OVERWRITE DISCOUNT OCCBIN +%token DETERMINISTIC_TRENDS OBSERVATION_TRENDS OPTIM OPTIM_WEIGHTS ORDER OSR OSR_PARAMS MAX_DIM_COVA_GROUP ADVANCED OUTFILE OUTVARS OVERWRITE DISCOUNT %token PARALLEL_LOCAL_FILES PARAMETERS PARAMETER_SET PARTIAL_INFORMATION PERIODS PERIOD PLANNER_OBJECTIVE PLOT_CONDITIONAL_FORECAST PLOT_PRIORS PREFILTER PRESAMPLE %token PERFECT_FORESIGHT_SETUP PERFECT_FORESIGHT_SOLVER NO_POSTERIOR_KERNEL_DENSITY FUNCTION %token PERFECT_FORESIGHT_WITH_EXPECTATION_ERRORS_SETUP PERFECT_FORESIGHT_WITH_EXPECTATION_ERRORS_SOLVER @@ -124,7 +124,7 @@ class ParsingDriver; %token SIMUL_DEBUG SMOOTHER_DEBUG %token LIKELIHOOD_INVERSION_FILTER SMOOTHER_INVERSION_FILTER FILTER_USE_RELEXATION %token LIKELIHOOD_PIECEWISE_KALMAN_FILTER SMOOTHER_PIECEWISE_KALMAN_FILTER -%token <string> TEX_NAME TRUE +%token <string> TEX_NAME TRUE BIND RELAX ERROR_BIND ERROR_RELAX %token UNIFORM_PDF UNIT_ROOT_VARS USE_DLL USEAUTOCORR GSA_SAMPLE_FILE USE_UNIVARIATE_FILTERS_IF_SINGULARITY_IS_DETECTED %token VALUES SCALES VAR VAREXO VAREXO_DET VARIABLE VAROBS VAREXOBS PREDETERMINED_VARIABLES VAR_EXPECTATION VAR_EXPECTATION_MODEL PLOT_SHOCK_DECOMPOSITION MODEL_LOCAL_VARIABLE %token WRITE_LATEX_DYNAMIC_MODEL WRITE_LATEX_STATIC_MODEL WRITE_LATEX_ORIGINAL_MODEL WRITE_LATEX_STEADY_STATE_MODEL @@ -172,7 +172,7 @@ class ParsingDriver; %token NUMBER_OF_POSTERIOR_DRAWS_AFTER_PERTURBATION MAX_NUMBER_OF_STAGES %token RANDOM_FUNCTION_CONVERGENCE_CRITERION RANDOM_PARAMETER_CONVERGENCE_CRITERION NO_INIT_ESTIMATION_CHECK_FIRST_OBS %token HETEROSKEDASTIC_FILTER TIME_SHIFT STRUCTURAL TERMINAL_STEADY_STATE_AS_GUESS_VALUE -%token SURPRISE +%token SURPRISE OCCBIN_CONSTRAINTS /* 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 @@ -208,6 +208,10 @@ class ParsingDriver; %type <tuple<string,string,string,string>> prior_eq_opt options_eq_opt %type <vector<pair<int, int>>> period_list %type <vector<expr_t>> matched_moments_list value_list +%type <tuple<string, expr_t, expr_t, expr_t, expr_t>> occbin_constraints_regime +%type <vector<tuple<string, expr_t, expr_t, expr_t, expr_t>>> occbin_constraints_regimes_list +%type <map<string, expr_t>> occbin_constraints_regime_options_list +%type <pair<string, expr_t>> occbin_constraints_regime_option %% %start statement_list; @@ -336,6 +340,7 @@ statement : parameters | var_expectation_model | compilation_setup | matched_moments + | occbin_constraints ; dsample : DSAMPLE INT_NUMBER ';' @@ -867,6 +872,43 @@ matched_moments_list : hand_side ';' } ; +occbin_constraints : OCCBIN_CONSTRAINTS ';' { driver.begin_occbin_constraints(); } + occbin_constraints_regimes_list END ';' { driver.end_occbin_constraints($4); } + ; + + +occbin_constraints_regimes_list : occbin_constraints_regime + { $$ = { $1 }; } + | occbin_constraints_regimes_list occbin_constraints_regime + { + $$ = $1; + $$.push_back($2); + } + ; + +occbin_constraints_regime : NAME QUOTED_STRING ';' occbin_constraints_regime_options_list + { $$ = { $2, $4["bind"], $4["relax"], $4["error_bind"], $4["error_relax"] }; } + ; + +occbin_constraints_regime_options_list : occbin_constraints_regime_option + { $$ = { $1 }; } + | occbin_constraints_regime_options_list occbin_constraints_regime_option + { + $$ = $1; + $$.insert($2); + } + ; + +occbin_constraints_regime_option : BIND hand_side ';' + { $$ = { "bind", $2 }; } + | RELAX hand_side ';' + { $$ = { "relax", $2 }; } + | ERROR_BIND hand_side ';' + { $$ = { "error_bind", $2 }; } + | ERROR_RELAX hand_side ';' + { $$ = { "error_relax", $2 }; } + ; + model_options : BLOCK { driver.block(); } | o_cutoff | o_mfs @@ -878,7 +920,6 @@ model_options : BLOCK { driver.block(); } | o_linear | PARALLEL_LOCAL_FILES EQUAL '(' parallel_local_filename_list ')' | BALANCED_GROWTH_TEST_TOL EQUAL non_negative_number { driver.balanced_growth_test_tol($3); } - | OCCBIN { driver.occbin(); } ; model_options_list : model_options_list COMMA model_options @@ -911,9 +952,9 @@ tags_list : tags_list COMMA tag_pair | tag_pair ; -tag_pair : NAME EQUAL QUOTED_STRING +tag_pair : symbol EQUAL QUOTED_STRING { driver.add_equation_tags($1, $3); } - | NAME + | symbol { driver.add_equation_tags($1, ""); } ; @@ -4126,6 +4167,10 @@ symbol : NAME | PRIOR | TRUE | FALSE + | BIND + | RELAX + | ERROR_BIND + | ERROR_RELAX ; %% diff --git a/src/DynareFlex.ll b/src/DynareFlex.ll index fec10ec5..ef319001 100644 --- a/src/DynareFlex.ll +++ b/src/DynareFlex.ll @@ -230,6 +230,7 @@ DATE -?[0-9]+([ya]|m([1-9]|1[0-2])|q[1-4]) <INITIAL>ramsey_constraints {BEGIN DYNARE_BLOCK; return token::RAMSEY_CONSTRAINTS;} <INITIAL>generate_irfs {BEGIN DYNARE_BLOCK; return token::GENERATE_IRFS;} <INITIAL>matched_moments {BEGIN DYNARE_BLOCK; return token::MATCHED_MOMENTS;} +<INITIAL>occbin_constraints {BEGIN DYNARE_BLOCK; return token::OCCBIN_CONSTRAINTS;} /* For the semicolon after an "end" keyword */ <INITIAL>; {return Dynare::parser::token_type (yytext[0]);} @@ -762,6 +763,22 @@ DATE -?[0-9]+([ya]|m([1-9]|1[0-2])|q[1-4]) <DYNARE_BLOCK>weibull_pdf {return token::WEIBULL_PDF;} <DYNARE_BLOCK>dsge_prior_weight {return token::DSGE_PRIOR_WEIGHT;} <DYNARE_BLOCK>surprise {return token::SURPRISE;} +<DYNARE_BLOCK>bind { + yylval->build<string>(yytext); + return token::BIND; +} +<DYNARE_BLOCK>relax { + yylval->build<string>(yytext); + return token::RELAX; +} +<DYNARE_BLOCK>error_bind { + yylval->build<string>(yytext); + return token::ERROR_BIND; +} +<DYNARE_BLOCK>error_relax { + yylval->build<string>(yytext); + return token::ERROR_RELAX; +} <DYNARE_BLOCK>; {return Dynare::parser::token_type (yytext[0]);} <DYNARE_BLOCK># {return Dynare::parser::token_type (yytext[0]);} @@ -858,7 +875,6 @@ DATE -?[0-9]+([ya]|m([1-9]|1[0-2])|q[1-4]) <DYNARE_BLOCK>no_static {return token::NO_STATIC;} <DYNARE_BLOCK>differentiate_forward_vars {return token::DIFFERENTIATE_FORWARD_VARS;} <DYNARE_BLOCK>parallel_local_files {return token::PARALLEL_LOCAL_FILES;} -<DYNARE_BLOCK>occbin {return token::OCCBIN;} <DYNARE_STATEMENT,DYNARE_BLOCK>linear {return token::LINEAR;} diff --git a/src/EquationTags.cc b/src/EquationTags.cc index 2d271375..42621546 100644 --- a/src/EquationTags.cc +++ b/src/EquationTags.cc @@ -86,30 +86,6 @@ EquationTags::writeOutput(ostream &output) const output << "};" << endl; } -void -EquationTags::writeOccbinOutput(ostream &output) const -{ - map<int, map<string, string>> occbin_options; - for (const auto & [eqn, tags] : eqn_tags) - for (const auto & [key, value] : tags) - if (key == "pswitch" - || key == "bind" - || key == "relax" - || key == "pcrit") - occbin_options[eqn][key] = value; - - int idx = 0; - for (const auto & [eqn, tags] : occbin_options) - { - idx++; - for (const auto & [key, value] : tags) - output << "M_.occbin.constraint(" << idx << ")." - << key << " = '" << value << "';" << endl; - output << "M_.occbin.constraint(" << idx << ").equation = " - << eqn+1 << ";" << endl; - } -} - void EquationTags::writeLatexOutput(ostream &output, int eqn) const { @@ -165,16 +141,3 @@ EquationTags::writeJsonAST(ostream &output, const int eqn) const } output << "}"; } - -bool -EquationTags::hasOccbinTags() const -{ - for (const auto & [eqn, tags] : eqn_tags) - for (const auto & [key, value] : tags) - if (key == "pswitch" - || key == "bind" - || key == "relax" - || key == "pcrit") - return true; - return false; -} diff --git a/src/EquationTags.hh b/src/EquationTags.hh index a8024eea..34277bae 100644 --- a/src/EquationTags.hh +++ b/src/EquationTags.hh @@ -130,13 +130,8 @@ public: //! Various functions to write equation tags void writeCheckSumInfo(ostream &output) const; void writeOutput(ostream &output) const; - void writeOccbinOutput(ostream &output) const; void writeLatexOutput(ostream &output, int eqn) const; void writeJsonAST(ostream &output, const int eq) const; - - /* Returns true if at least one equation has a tag associated to occbin - (bind/relax/pswitch/pcrit) */ - bool hasOccbinTags() const; }; #endif diff --git a/src/ModFile.cc b/src/ModFile.cc index 6c8b6b6e..2892fbfc 100644 --- a/src/ModFile.cc +++ b/src/ModFile.cc @@ -684,25 +684,19 @@ ModFile::transformPass(bool nostrict, bool stochastic, bool compute_xrefs, bool exit(EXIT_FAILURE); } - if (occbin && !dynamic_model.hasOccbinTags()) - { - cerr << "ERROR: the 'occbin' option is present, but there is no equation with the associated tags (bind/relax/pswitch/pcrit)" << endl; - exit(EXIT_FAILURE); - } - - if (occbin + if (mod_file_struct.occbin_constraints_present && (mod_file_struct.osr_present || mod_file_struct.mom_estimation_present || mod_file_struct.ramsey_model_present || mod_file_struct.ramsey_policy_present || mod_file_struct.discretionary_policy_present || mod_file_struct.extended_path_present || mod_file_struct.identification_present || mod_file_struct.sensitivity_present)) { - cerr << "ERROR: the 'occbin' option is not compatible with commands other than 'estimation', 'stoch_simul', and 'calib_smoother'." << endl; + cerr << "ERROR: the 'occbin_constraints' block is not compatible with commands other than 'estimation', 'stoch_simul', and 'calib_smoother'." << endl; exit(EXIT_FAILURE); } - if (mod_file_struct.shocks_surprise_present && !occbin) + if (mod_file_struct.shocks_surprise_present && !mod_file_struct.occbin_constraints_present) { - cerr << "ERROR: the 'shocks(surprise)' block can only be used in conjunction with occbin commands." << endl; + cerr << "ERROR: the 'shocks(surprise)' block can only be used in conjunction with the 'occbin_constraints' block." << endl; exit(EXIT_FAILURE); } @@ -1044,14 +1038,11 @@ ModFile::writeMOutput(const string &basename, bool clear_all, bool clear_global, if (dynamic_model.equation_number() > 0) { - dynamic_model.writeDriverOutput(mOutputFile, basename, block, use_dll, occbin, mod_file_struct.estimation_present, compute_xrefs); + dynamic_model.writeDriverOutput(mOutputFile, basename, block, use_dll, mod_file_struct.occbin_constraints_present, mod_file_struct.estimation_present, compute_xrefs); if (!no_static) static_model.writeDriverOutput(mOutputFile, block); } - if (occbin) - mOutputFile << "[M_, oo_, options_] = occbin.initialize(M_, oo_, options_);" << endl; - if (onlymodel || gui) for (const auto &statement : statements) { diff --git a/src/ModFile.hh b/src/ModFile.hh index 7839adfd..e2f31264 100644 --- a/src/ModFile.hh +++ b/src/ModFile.hh @@ -99,9 +99,6 @@ public: with a lead */ vector<string> differentiate_forward_vars_subset; - //! Whether “occbin” option is used - bool occbin{false}; - //! Are nonstationary variables present ? bool nonstationary_variables{false}; diff --git a/src/ModelTree.cc b/src/ModelTree.cc index aeafc23a..6d000438 100644 --- a/src/ModelTree.cc +++ b/src/ModelTree.cc @@ -2120,9 +2120,3 @@ ModelTree::updateReverseVariableEquationOrderings() eq_idx_orig2block[eq_idx_block2orig[i]] = i; } } - -bool -ModelTree::hasOccbinTags() const -{ - return equation_tags.hasOccbinTags(); -} diff --git a/src/ModelTree.hh b/src/ModelTree.hh index 467c0c94..546fc6e9 100644 --- a/src/ModelTree.hh +++ b/src/ModelTree.hh @@ -501,10 +501,6 @@ public: return "UNKNOWN "; } } - - /* Returns true if at least one equation has a tag associated to occbin - (bind/relax/pswitch/pcrit) */ - bool hasOccbinTags() const; }; #endif diff --git a/src/ParsingDriver.cc b/src/ParsingDriver.cc index 2674f8f2..60d05907 100644 --- a/src/ParsingDriver.cc +++ b/src/ParsingDriver.cc @@ -617,12 +617,6 @@ ParsingDriver::bytecode() mod_file->bytecode = true; } -void -ParsingDriver::occbin() -{ - mod_file->occbin = true; -} - void ParsingDriver::differentiate_forward_vars_all() { @@ -3423,3 +3417,25 @@ ParsingDriver::end_matched_moments(const vector<expr_t> &moments) reset_data_tree(); } + +void +ParsingDriver::begin_occbin_constraints() +{ + set_current_data_tree(&mod_file->dynamic_model); +} + +void +ParsingDriver::end_occbin_constraints(const vector<tuple<string, expr_t, expr_t, expr_t, expr_t>> &constraints) +{ + // Perform a few checks + for (const auto &[name, bind, relax, error_bind, error_relax] : constraints) + { + check_symbol_is_parameter(name); + if (!bind) + error("The 'bind' expression is missing in constraint '" + name + "'"); + } + + mod_file->addStatement(make_unique<OccbinConstraintsStatement>(mod_file->symbol_table, constraints)); + + reset_data_tree(); +} diff --git a/src/ParsingDriver.hh b/src/ParsingDriver.hh index 324883b4..88582079 100644 --- a/src/ParsingDriver.hh +++ b/src/ParsingDriver.hh @@ -326,8 +326,6 @@ public: //! the model is stored in a binary file void bytecode(); - //! the model has occasional binding constraints - void occbin(); //! the static model is not computed void no_static(); //! the differentiate_forward_vars option is enabled (for all vars) @@ -886,6 +884,10 @@ public: void begin_matched_moments(); //! Add a matched_moments block void end_matched_moments(const vector<expr_t> &moments); + //! Start parsing an occbin_constraints block + void begin_occbin_constraints(); + //! Add an occbin_constraints block + void end_occbin_constraints(const vector<tuple<string, expr_t, expr_t, expr_t, expr_t>> &constraints); }; #endif // ! PARSING_DRIVER_HH diff --git a/src/Statement.hh b/src/Statement.hh index 0b23aad6..24d0d463 100644 --- a/src/Statement.hh +++ b/src/Statement.hh @@ -147,6 +147,8 @@ public: set<int> parameters_in_planner_discount; // Whether a shocks(surprise) block appears bool shocks_surprise_present{false}; + // Whether an occbin_constraints block appears + bool occbin_constraints_present{false}; }; class Statement -- GitLab