Implement “occbin_constraints” block

Consequently drop “occbin” option to “model”.

Incidentally, allow more values in equation tag names (previously some keywords
such as “alpha” were disallowed).

Ref. #68
var-models
Sébastien Villemot 2021-07-15 16:24:21 +02:00
parent 6b9d94405c
commit 08ac455fcd
No known key found for this signature in database
GPG Key ID: 2CECE9350ECEBE4A
14 changed files with 182 additions and 88 deletions

View File

@ -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;
}

View File

@ -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

View File

@ -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)
{

View File

@ -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
;
%%

View File

@ -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;}

View File

@ -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;
}

View File

@ -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

View File

@ -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)
{

View File

@ -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};

View File

@ -2120,9 +2120,3 @@ ModelTree::updateReverseVariableEquationOrderings()
eq_idx_orig2block[eq_idx_block2orig[i]] = i;
}
}
bool
ModelTree::hasOccbinTags() const
{
return equation_tags.hasOccbinTags();
}

View File

@ -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

View File

@ -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();
}

View File

@ -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

View File

@ -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