From 5150ef81d4344e2bc7ceba9e552bef73cc869510 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   | 15 ++++++++++++++-
 src/EquationTags.hh   |  6 +++++-
 src/ModFile.cc        | 26 ++++++++++++++++++++++++--
 src/ModFile.hh        |  3 +++
 src/ModelTree.cc      |  6 ++++++
 src/ModelTree.hh      |  4 ++++
 src/ParsingDriver.cc  |  6 ++++++
 src/ParsingDriver.hh  |  2 ++
 13 files changed, 72 insertions(+), 29 deletions(-)

diff --git a/src/ComputingTasks.cc b/src/ComputingTasks.cc
index 00f31006..73c4ab9d 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 e4b9fe61..08966826 100644
--- a/src/DynamicModel.cc
+++ b/src/DynamicModel.cc
@@ -2798,7 +2798,7 @@ DynamicModel::writeBlockDriverOutput(ostream &output, const string &basename, co
 }
 
 void
-DynamicModel::writeDriverOutput(ostream &output, const string &basename, bool block_decomposition, bool use_dll, bool estimation_present, bool compute_xrefs, bool julia) const
+DynamicModel::writeDriverOutput(ostream &output, const string &basename, bool block_decomposition, bool use_dll, bool occbin, bool estimation_present, bool compute_xrefs, bool julia) const
 {
   /* Writing initialisation for M_.lead_lag_incidence matrix
      M_.lead_lag_incidence is a matrix with as many columns as there are
@@ -2909,7 +2909,8 @@ DynamicModel::writeDriverOutput(ostream &output, const string &basename, bool bl
   equation_tags.writeOutput(output, modstruct, julia);
 
   // Write Occbin tags
-  equation_tags.writeOccbinOutput(output, modstruct, julia);
+  if (occbin)
+    equation_tags.writeOccbinOutput(output, modstruct, julia);
 
   // Write mapping for variables and equations they are present in
   if (!julia)
diff --git a/src/DynamicModel.hh b/src/DynamicModel.hh
index 1c68bb87..6d046ddb 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, bool julia) const;
+  void writeDriverOutput(ostream &output, const string &basename, bool block, bool use_dll, bool occbin, bool estimation_present, bool compute_xrefs, bool julia) const;
 
   //! Write JSON AST
   void writeJsonAST(ostream &output) const;
diff --git a/src/DynareBison.yy b/src/DynareBison.yy
index 1a9c213e..212034a1 100644
--- a/src/DynareBison.yy
+++ b/src/DynareBison.yy
@@ -106,7 +106,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 OBSERVATION_TRENDS OPTIM OPTIM_WEIGHTS ORDER OSR OSR_PARAMS MAX_DIM_COVA_GROUP ADVANCED OUTFILE OUTVARS OVERWRITE DISCOUNT
+%token 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
@@ -130,7 +130,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
@@ -866,6 +866,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
@@ -2010,8 +2011,6 @@ estimation_options : o_datafile
                    | o_emas_max_iter
                    | o_stderr_multiples
                    | o_diagonal_only
-                   | o_occbin_likelihood
-                   | o_occbin_smoother
                    ;
 
 list_optim_option : QUOTED_STRING COMMA QUOTED_STRING
@@ -3419,8 +3418,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 dda43195..cdc7f6f0 100644
--- a/src/DynareFlex.ll
+++ b/src/DynareFlex.ll
@@ -432,8 +432,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>alpha {
@@ -825,6 +823,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 d6077212..b1b61d41 100644
--- a/src/EquationTags.cc
+++ b/src/EquationTags.cc
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2020 Dynare Team
+ * Copyright © 2020-2021 Dynare Team
  *
  * This file is part of Dynare.
  *
@@ -183,3 +183,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 ddd1eec5..5738e127 100644
--- a/src/EquationTags.hh
+++ b/src/EquationTags.hh
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2020 Dynare Team
+ * Copyright © 2020-2021 Dynare Team
  *
  * This file is part of Dynare.
  *
@@ -115,6 +115,10 @@ public:
   void writeOccbinOutput(ostream &output, const string &modstruct, bool julia) 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 40a7b25f..fcad0e87 100644
--- a/src/ModFile.cc
+++ b/src/ModFile.cc
@@ -679,6 +679,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
@@ -1011,11 +1028,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, false);
+      dynamic_model.writeDriverOutput(mOutputFile, basename, block, use_dll, occbin, mod_file_struct.estimation_present, compute_xrefs, false);
       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)
       {
@@ -1219,7 +1241,7 @@ ModFile::writeJuliaOutput(const string &basename) const
 
   if (dynamic_model.equation_number() > 0)
     {
-      dynamic_model.writeDriverOutput(jlOutputFile, basename, false, false,
+      dynamic_model.writeDriverOutput(jlOutputFile, basename, false, false, false,
                                       mod_file_struct.estimation_present, false, true);
       if (!no_static)
         {
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 b540bcb5..1197e2ec 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 e075e612..2b1c1b31 100644
--- a/src/ModelTree.hh
+++ b/src/ModelTree.hh
@@ -480,6 +480,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 9b0695c7..a7a93a7e 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 ed4ef240..cc33b0de 100644
--- a/src/ParsingDriver.hh
+++ b/src/ParsingDriver.hh
@@ -331,6 +331,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