From c5f9ae89e720aea42411ab8d0bb3e414ebf0ec1c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?S=C3=A9bastien=20Villemot?= <sebastien@dynare.org>
Date: Mon, 22 Feb 2021 18:26:21 +0100
Subject: [PATCH] =?UTF-8?q?Implement=20new=20=E2=80=9Coccbin=E2=80=9D=20op?=
 =?UTF-8?q?tion=20to=20=E2=80=9Cmodel=E2=80=9D=20block?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

For the time being, the preprocessor will refuse that this option be used with
any command other than estimation.

By the way, remove occbin_likelihood and occbin_smoother options to estimation.

Ref. dynare#569
---
 src/ComputingTasks.cc | 14 --------------
 src/DynamicModel.cc   |  5 +++--
 src/DynamicModel.hh   |  2 +-
 src/DynareBison.yy    |  9 +++------
 src/DynareFlex.ll     |  3 +--
 src/EquationTags.cc   | 13 +++++++++++++
 src/EquationTags.hh   |  4 ++++
 src/ModFile.cc        | 24 +++++++++++++++++++++++-
 src/ModFile.hh        |  3 +++
 src/ModelTree.cc      |  6 ++++++
 src/ModelTree.hh      |  4 ++++
 src/ParsingDriver.cc  |  6 ++++++
 src/ParsingDriver.hh  |  2 ++
 13 files changed, 69 insertions(+), 26 deletions(-)

diff --git a/src/ComputingTasks.cc b/src/ComputingTasks.cc
index fcf94a42..8f65bc0a 100644
--- a/src/ComputingTasks.cc
+++ b/src/ComputingTasks.cc
@@ -1019,20 +1019,6 @@ EstimationStatement::writeOutput(ostream &output, const string &basename, bool m
 {
   options_list.writeOutput(output);
 
-  bool occbin_option_present = false;
-  if (auto it = options_list.num_options.find("occbin_likelihood");
-      it != options_list.num_options.end() && it->second == "true")
-    occbin_option_present = true;
-
-  if (auto it = options_list.num_options.find("occbin_smoother");
-      it != options_list.num_options.end() && it->second == "true")
-    occbin_option_present = true;
-
-  if (occbin_option_present)
-    output << "options_ = set_default_occbin_options(options_, M_);" << endl
-           << "clear mr_runsim_occbin_fn" << endl
-           << "M_ = get_wish_list(M_);" << endl;
-
   // Special treatment for order option and particle filter
   if (auto it = options_list.num_options.find("order");
       it == options_list.num_options.end())
diff --git a/src/DynamicModel.cc b/src/DynamicModel.cc
index f6c19af0..ab209904 100644
--- a/src/DynamicModel.cc
+++ b/src/DynamicModel.cc
@@ -2798,7 +2798,7 @@ DynamicModel::writeBlockDriverOutput(ostream &output, const string &basename,
 }
 
 void
-DynamicModel::writeDriverOutput(ostream &output, const string &basename, bool block_decomposition, bool use_dll, bool estimation_present, bool compute_xrefs) const
+DynamicModel::writeDriverOutput(ostream &output, const string &basename, bool block_decomposition, bool use_dll, bool occbin, bool estimation_present, bool compute_xrefs) const
 {
   /* Writing initialisation for M_.lead_lag_incidence matrix
      M_.lead_lag_incidence is a matrix with as many columns as there are
@@ -2895,7 +2895,8 @@ DynamicModel::writeDriverOutput(ostream &output, const string &basename, bool bl
   equation_tags.writeOutput(output);
 
   // Write Occbin tags
-  equation_tags.writeOccbinOutput(output);
+  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/DynamicModel.hh b/src/DynamicModel.hh
index d98b13c5..c64ef062 100644
--- a/src/DynamicModel.hh
+++ b/src/DynamicModel.hh
@@ -300,7 +300,7 @@ public:
   void computingPass(bool jacobianExo, int derivsOrder, int paramsDerivsOrder,
                      const eval_context_t &eval_context, bool no_tmp_terms, bool block, bool use_dll, bool bytecode);
   //! Writes information about the dynamic model to the driver file
-  void writeDriverOutput(ostream &output, const string &basename, bool block, bool use_dll, bool estimation_present, bool compute_xrefs) const;
+  void writeDriverOutput(ostream &output, const string &basename, bool block, bool use_dll, bool occbin, bool estimation_present, bool compute_xrefs) const;
 
   //! Write JSON AST
   void writeJsonAST(ostream &output) const;
diff --git a/src/DynareBison.yy b/src/DynareBison.yy
index 68765865..6c47638a 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
+%token DETERMINISTIC_TRENDS OBSERVATION_TRENDS OPTIM OPTIM_WEIGHTS ORDER OSR OSR_PARAMS MAX_DIM_COVA_GROUP ADVANCED OUTFILE OUTVARS OVERWRITE DISCOUNT OCCBIN
 %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 PRINT PRIOR_MC PRIOR_TRUNC PRIOR_MODE PRIOR_MEAN POSTERIOR_MODE POSTERIOR_MEAN POSTERIOR_MEDIAN MLE_MODE PRUNING PARTICLE_FILTER_OPTIONS
@@ -131,7 +131,7 @@ class ParsingDriver;
 %precedence UNARY
 %nonassoc POWER
 %token EXP LOG LN LOG10 SIN COS TAN ASIN ACOS ATAN ERF DIFF ADL AUXILIARY_MODEL_NAME
-%token SQRT CBRT NORMCDF NORMPDF STEADY_STATE EXPECTATION OCCBIN_LIKELIHOOD OCCBIN_SMOOTHER
+%token SQRT CBRT NORMCDF NORMPDF STEADY_STATE EXPECTATION
 /* GSA analysis */
 %token DYNARE_SENSITIVITY MORRIS STAB REDFORM PPRIOR PRIOR_RANGE PPOST ILPTAU MORRIS_NLIV
 %token MORRIS_NTRA NSAM LOAD_REDFORM LOAD_RMSE LOAD_STAB ALPHA2_STAB LOGTRANS_REDFORM THRESHOLD_REDFORM
@@ -873,6 +873,7 @@ 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
@@ -2089,8 +2090,6 @@ estimation_options : o_datafile
                    | o_emas_max_iter
                    | o_stderr_multiples
                    | o_diagonal_only
-                   | o_occbin_likelihood
-                   | o_occbin_smoother
                    | o_no_init_estimation_check_first_obs
                    | o_heteroskedastic_filter
                    ;
@@ -3518,8 +3517,6 @@ o_proposal_approximation : PROPOSAL_APPROXIMATION EQUAL CUBATURE {driver.option_
 o_distribution_approximation : DISTRIBUTION_APPROXIMATION EQUAL CUBATURE {driver.option_num("particle.distribution_approximation.cubature", "true"); driver.option_num("particle.distribution_approximation.unscented", "false"); driver.option_num("particle.distribution_approximation.montecarlo", "false");}
 		| DISTRIBUTION_APPROXIMATION EQUAL UNSCENTED {driver.option_num("particle.distribution_approximation.cubature", "false"); driver.option_num("particle.distribution_approximation.unscented", "true"); driver.option_num("particle.distribution_approximation.montecarlo", "false");}
 		| DISTRIBUTION_APPROXIMATION EQUAL MONTECARLO {driver.option_num("particle.distribution_approximation.cubature", "false"); driver.option_num("particle.distribution_approximation.unscented", "false"); driver.option_num("particle.distribution_approximation.montecarlo", "true");} ;
-o_occbin_likelihood : OCCBIN_LIKELIHOOD { driver.option_num("occbin_likelihood", "true"); };
-o_occbin_smoother : OCCBIN_SMOOTHER { driver.option_num("occbin_smoother", "true"); };
 o_gsa_identification : IDENTIFICATION EQUAL INT_NUMBER { driver.option_num("identification", $3); }; /*not in doc */
 o_gsa_morris : MORRIS EQUAL INT_NUMBER { driver.option_num("morris", $3); };
 o_gsa_stab : STAB  EQUAL INT_NUMBER { driver.option_num("stab", $3); };
diff --git a/src/DynareFlex.ll b/src/DynareFlex.ll
index b37ea326..feedb790 100644
--- a/src/DynareFlex.ll
+++ b/src/DynareFlex.ll
@@ -434,8 +434,6 @@ DATE -?[0-9]+([ya]|m([1-9]|1[0-2])|q[1-4])
 <DYNARE_STATEMENT>rescale_prediction_error_covariance {return token::RESCALE_PREDICTION_ERROR_COVARIANCE;}
 <DYNARE_STATEMENT>use_penalized_objective_for_hessian {return token::USE_PENALIZED_OBJECTIVE_FOR_HESSIAN;}
 <DYNARE_STATEMENT>expression    {return token::EXPRESSION;}
-<DYNARE_STATEMENT>occbin_likelihood {return token::OCCBIN_LIKELIHOOD;}
-<DYNARE_STATEMENT>occbin_smoother {return token::OCCBIN_SMOOTHER;}
 <DYNARE_STATEMENT>first_simulation_period {return token::FIRST_SIMULATION_PERIOD;}
 <DYNARE_STATEMENT>no_init_estimation_check_first_obs {return token::NO_INIT_ESTIMATION_CHECK_FIRST_OBS;}
 
@@ -836,6 +834,7 @@ 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 c86fd51a..18cacd5f 100644
--- a/src/EquationTags.cc
+++ b/src/EquationTags.cc
@@ -165,3 +165,16 @@ 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 d573f8c0..b983fbd4 100644
--- a/src/EquationTags.hh
+++ b/src/EquationTags.hh
@@ -115,6 +115,10 @@ public:
   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 d340903d..e72e5825 100644
--- a/src/ModFile.cc
+++ b/src/ModFile.cc
@@ -686,6 +686,23 @@ 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
+      && (mod_file_struct.stoch_simul_present || mod_file_struct.osr_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
+          || mod_file_struct.mom_estimation_present || mod_file_struct.calib_smoother_present))
+    {
+      cerr << "ERROR: the 'occbin' option is not compatible with commands other than 'estimation'" << endl;
+      exit(EXIT_FAILURE);
+    }
+
   if (!mod_file_struct.ramsey_model_present)
     cout << "Found " << dynamic_model.equation_number() << " equation(s)." << endl;
   else
@@ -1018,11 +1035,16 @@ 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, mod_file_struct.estimation_present, compute_xrefs);
+      dynamic_model.writeDriverOutput(mOutputFile, basename, block, use_dll, occbin, mod_file_struct.estimation_present, compute_xrefs);
       if (!no_static)
         static_model.writeDriverOutput(mOutputFile, block);
     }
 
+  if (occbin)
+    mOutputFile << "options_ = set_default_occbin_options(options_, M_);" << endl
+                << "clear mr_runsim_occbin_fn" << endl
+                << "M_ = get_wish_list(M_);" << endl;
+
   if (onlymodel || gui)
     for (const auto &statement : statements)
       {
diff --git a/src/ModFile.hh b/src/ModFile.hh
index 01e1a471..6ab993f7 100644
--- a/src/ModFile.hh
+++ b/src/ModFile.hh
@@ -99,6 +99,9 @@ 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 e6fc89d7..906c285c 100644
--- a/src/ModelTree.cc
+++ b/src/ModelTree.cc
@@ -2120,3 +2120,9 @@ 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 6cfdfcf6..f295a04d 100644
--- a/src/ModelTree.hh
+++ b/src/ModelTree.hh
@@ -494,6 +494,10 @@ 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 4b0d9520..f25e0ef5 100644
--- a/src/ParsingDriver.cc
+++ b/src/ParsingDriver.cc
@@ -634,6 +634,12 @@ ParsingDriver::bytecode()
   mod_file->bytecode = true;
 }
 
+void
+ParsingDriver::occbin()
+{
+  mod_file->occbin = true;
+}
+
 void
 ParsingDriver::differentiate_forward_vars_all()
 {
diff --git a/src/ParsingDriver.hh b/src/ParsingDriver.hh
index 4402222e..46c3fde5 100644
--- a/src/ParsingDriver.hh
+++ b/src/ParsingDriver.hh
@@ -325,6 +325,8 @@ 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)
-- 
GitLab