From 1e40e0a8a1e1b0dbe97d4b91bc662e5ad545d3d4 Mon Sep 17 00:00:00 2001
From: Houtan Bastani <houtan@dynare.org>
Date: Fri, 6 Oct 2017 14:49:14 +0200
Subject: [PATCH] preprocessor: interface for generate_irfs block. Closes #1531

---
 ComputingTasks.cc | 70 +++++++++++++++++++++++++++++++++++++++++++++++
 ComputingTasks.hh | 16 +++++++++++
 DynareBison.yy    | 34 +++++++++++++++++++++--
 DynareFlex.ll     |  3 ++
 ParsingDriver.cc  | 45 ++++++++++++++++++++++++++++++
 ParsingDriver.hh  | 10 ++++++-
 6 files changed, 174 insertions(+), 4 deletions(-)

diff --git a/ComputingTasks.cc b/ComputingTasks.cc
index 51d623f6..4a0fac83 100644
--- a/ComputingTasks.cc
+++ b/ComputingTasks.cc
@@ -4489,3 +4489,73 @@ SMMEstimationStatement::writeJsonOutput(ostream &output) const
     }
   output << "}";
 }
+
+
+
+GenerateIRFsStatement::GenerateIRFsStatement(const OptionsList &options_list_arg,
+                                             const vector<string> &generate_irf_names_arg,
+                                             const generate_irf_elements_t &generate_irf_elements_arg) :
+  options_list(options_list_arg),
+  generate_irf_names(generate_irf_names_arg),
+  generate_irf_elements(generate_irf_elements_arg)
+{
+}
+
+void
+GenerateIRFsStatement::writeOutput(ostream &output, const string &basename, bool minimal_workspace) const
+{
+  options_list.writeOutput(output);
+
+  if (generate_irf_names.empty())
+    {
+      output << "options_.irf_opt.irf_shock_graphtitles = {};" << endl
+             << "options_.irf_opt.irf_shocks = [];" << endl;
+      return;
+    }
+
+  output << "options_.irf_opt.irf_shock_graphtitles = { ";
+  for (vector<string>::const_iterator it = generate_irf_names.begin();
+       it != generate_irf_names.end(); it++)
+    output << "'" << *it << "'; ";
+  output << "};" << endl;
+
+  int idx = 1;
+  output << "options_.irf_opt.irf_shocks = zeros(M_.exo_nbr, " << generate_irf_elements.size() << ");" << endl;
+  for (generate_irf_elements_t::const_iterator it = generate_irf_elements.begin();
+       it != generate_irf_elements.end(); it++, idx++)
+    output << "options_.irf_opt.irf_shocks(M_.exo_names == '" << it->first.first << "', " << idx << ") = "
+           << it->first.second << ";" << endl
+           << "options_.irf_opt.irf_shocks(M_.exo_names == '" << it->second.first << "', " << idx << ") = "
+           << it->second.second << ";" << endl;
+}
+
+void
+GenerateIRFsStatement::writeJsonOutput(ostream &output) const
+{
+  output << "{\"statementName\": \"generate_irfs\"";
+  if (options_list.getNumberOfOptions())
+    {
+      output << ", ";
+      options_list.writeJsonOutput(output);
+    }
+  if (!generate_irf_elements.empty())
+    {
+      size_t n = generate_irf_elements.size();
+      size_t idx = 1;
+      output << ", \"irf_elements\": [";
+      for (generate_irf_elements_t::const_iterator it = generate_irf_elements.begin();
+           it != generate_irf_elements.end(); it++)
+        {
+          output << "{\"name\": \"" << generate_irf_names[idx-1] << "\", "
+                 << "\"exogenous_variable_1\": \"" << it->first.first << "\", "
+                 << "\"exogenous_variable_1_value\": \"" << it->first.second << "\", "
+                 << "\"exogenous_variable_2\": \"" << it->second.first << "\", "
+                 << "\"exogenous_variable_2_value\": \"" << it->second.second << "\""
+                 << "}";
+          if (++idx <= n)
+            output << ", ";
+        }
+      output << "]";
+    }
+  output << "}";
+}
diff --git a/ComputingTasks.hh b/ComputingTasks.hh
index 37132114..9d16f306 100644
--- a/ComputingTasks.hh
+++ b/ComputingTasks.hh
@@ -1107,4 +1107,20 @@ public:
   virtual void writeJsonOutput(ostream &output) const;
 };
 
+class GenerateIRFsStatement : public Statement
+{
+public:
+  typedef vector<pair<pair<string, double>, pair<string, double> > > generate_irf_elements_t;
+private:
+  const OptionsList options_list;
+  const vector<string> generate_irf_names;
+  const generate_irf_elements_t generate_irf_elements;
+public:
+  GenerateIRFsStatement(const OptionsList &options_list_arg,
+                        const vector<string> & generate_irf_names_arg,
+                        const generate_irf_elements_t &generate_irf_elements_arg);
+  virtual void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const;
+  virtual void writeJsonOutput(ostream &output) const;
+};
+
 #endif
diff --git a/DynareBison.yy b/DynareBison.yy
index 2fe510d3..28bff082 100644
--- a/DynareBison.yy
+++ b/DynareBison.yy
@@ -113,9 +113,9 @@ class ParsingDriver;
 %token CPF_WEIGHTS AMISANOTRISTANI MURRAYJONESPARSLOW WRITE_EQUATION_TAGS
 %token NONLINEAR_FILTER_INITIALIZATION FILTER_ALGORITHM PROPOSAL_APPROXIMATION CUBATURE UNSCENTED MONTECARLO DISTRIBUTION_APPROXIMATION
 %token <string_val> NAME
-%token USE_PENALIZED_OBJECTIVE_FOR_HESSIAN INIT_STATE RESCALE_PREDICTION_ERROR_COVARIANCE
+%token USE_PENALIZED_OBJECTIVE_FOR_HESSIAN INIT_STATE 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
+%token NOGRAPH POSTERIOR_NOGRAPH POSTERIOR_GRAPH NOMOMENTS NOPRINT NORMAL_PDF SAVE_DRAWS STDERR_MULTIPLES DIAGONAL_ONLY
 %token OBSERVATION_TRENDS OPTIM OPTIM_WEIGHTS ORDER OSR OSR_PARAMS MAX_DIM_COVA_GROUP ADVANCED OUTFILE OUTVARS OVERWRITE
 %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
@@ -276,6 +276,7 @@ statement : parameters
           | external_function
           | steady_state_model
           | trend_var
+          | generate_irfs
           | log_trend_var
           | ms_estimation
           | ms_simulation
@@ -2722,6 +2723,32 @@ calib_smoother_option : o_filtered_vars
                       | o_parameter_set
                       ;
 
+generate_irfs : GENERATE_IRFS ';' END ';'
+                { driver.end_generate_irfs(); }
+              | GENERATE_IRFS ';' generate_irfs_element_list END ';'
+                { driver.end_generate_irfs(); }
+              | GENERATE_IRFS '(' generate_irfs_options_list ')' ';' END ';'
+                { driver.end_generate_irfs(); }
+              | GENERATE_IRFS '(' generate_irfs_options_list ')' ';' generate_irfs_element_list END ';'
+                { driver.end_generate_irfs(); }
+              ;
+
+generate_irfs_options_list : generate_irfs_option COMMA generate_irfs_options_list
+                           | generate_irfs_option
+                           ;
+
+generate_irfs_option : o_stderr_multiples
+                     | o_diagonal_only
+                     ;
+
+generate_irfs_element_list : generate_irfs_element_list generate_irfs_element
+                           | generate_irfs_element
+                           ;
+
+generate_irfs_element : NAME COMMA symbol EQUAL signed_number COMMA symbol EQUAL signed_number ';'
+                        { driver.add_generate_irfs_element($1, $3, $5, $7, $9); }
+                      ;
+
 extended_path : EXTENDED_PATH ';'
                 { driver.extended_path(); }
               | EXTENDED_PATH '(' extended_path_options_list ')' ';'            
@@ -3113,7 +3140,8 @@ o_bvar_prior_omega : BVAR_PRIOR_OMEGA EQUAL INT_NUMBER { driver.option_num("bvar
 o_bvar_prior_flat : BVAR_PRIOR_FLAT { driver.option_num("bvar_prior_flat", "1"); };
 o_bvar_prior_train : BVAR_PRIOR_TRAIN EQUAL INT_NUMBER { driver.option_num("bvar_prior_train", $3); };
 o_bvar_replic : BVAR_REPLIC EQUAL INT_NUMBER { driver.option_num("bvar_replic", $3); };
-
+o_stderr_multiples : STDERR_MULTIPLES { driver.option_num("irf_opt.stderr_multiples", "1"); };
+o_diagonal_only : DIAGONAL_ONLY { driver.option_num("irf_opt.diagonal_only", "1"); };
 o_number_of_particles : NUMBER_OF_PARTICLES EQUAL INT_NUMBER { driver.option_num("particle.number_of_particles", $3); };
 o_resampling : RESAMPLING EQUAL SYSTEMATIC
               | RESAMPLING EQUAL NONE {driver.option_num("particle.resampling.status.systematic", "0"); driver.option_num("particle.resampling.status.none", "1"); }
diff --git a/DynareFlex.ll b/DynareFlex.ll
index c8cbdf06..67f2c169 100644
--- a/DynareFlex.ll
+++ b/DynareFlex.ll
@@ -210,6 +210,7 @@ DATE -?[0-9]+([YyAa]|[Mm]([1-9]|1[0-2])|[Qq][1-4]|[Ww]([1-9]{1}|[1-4][0-9]|5[0-2
 <INITIAL>moment_calibration {BEGIN DYNARE_BLOCK; return token::MOMENT_CALIBRATION;}
 <INITIAL>irf_calibration {BEGIN DYNARE_BLOCK; return token::IRF_CALIBRATION;}
 <INITIAL>ramsey_constraints {BEGIN DYNARE_BLOCK; return token::RAMSEY_CONSTRAINTS;}
+<INITIAL>generate_irfs {BEGIN DYNARE_BLOCK; return token::GENERATE_IRFS;}
 
  /* For the semicolon after an "end" keyword */
 <INITIAL>; {return Dynare::parser::token_type (yytext[0]);}
@@ -707,6 +708,8 @@ DATE -?[0-9]+([YyAa]|[Mm]([1-9]|1[0-2])|[Qq][1-4]|[Ww]([1-9]{1}|[1-4][0-9]|5[0-2
 <DYNARE_STATEMENT>irf_plot_threshold {return token::IRF_PLOT_THRESHOLD;}
 <DYNARE_STATEMENT>no_homotopy {return token::NO_HOMOTOPY;}
 
+<DYNARE_BLOCK>stderr_multiples {return token::STDERR_MULTIPLES;}
+<DYNARE_BLOCK>diagonal_only {return token::DIAGONAL_ONLY;}
 <DYNARE_BLOCK>equation {return token::EQUATION;}
 <DYNARE_BLOCK>exclusion {return token::EXCLUSION;}
 <DYNARE_BLOCK>lag {return token::LAG;}
diff --git a/ParsingDriver.cc b/ParsingDriver.cc
index 41f8be66..79d07572 100644
--- a/ParsingDriver.cc
+++ b/ParsingDriver.cc
@@ -580,6 +580,36 @@ ParsingDriver::homotopy_val(string *name, expr_t val1, expr_t val2)
   delete name;
 }
 
+void
+ParsingDriver::end_generate_irfs()
+{
+  mod_file->addStatement(new GenerateIRFsStatement(options_list, generate_irf_names, generate_irf_elements));
+  generate_irf_elements.clear();
+  generate_irf_names.clear();
+  options_list.clear();
+}
+
+void
+ParsingDriver::add_generate_irfs_element(const string *name, string *exo1, string *value1, string *exo2, string *value2)
+{
+  check_symbol_is_exogenous(exo1);
+  check_symbol_is_exogenous(exo2);
+  for (vector<string>::const_iterator it = generate_irf_names.begin();
+       it != generate_irf_names.end(); it++)
+    if (*it == *name)
+      error("Names in the generate_irfs block must be unique but you entered '" + *name + "' more than once.");
+
+  generate_irf_names.push_back(*name);
+  generate_irf_elements.push_back(make_pair(make_pair(*exo1, atof(value1->c_str())),
+                                            make_pair(*exo2, atof(value2->c_str()))));
+
+  delete name;
+  delete exo1;
+  delete exo2;
+  delete value1;
+  delete value2;
+}
+
 void
 ParsingDriver::forecast()
 {
@@ -1630,6 +1660,21 @@ ParsingDriver::check_symbol_is_endogenous_or_exogenous(string *name)
     }
 }
 
+void
+ParsingDriver::check_symbol_is_exogenous(string *name)
+{
+  check_symbol_existence(*name);
+  int symb_id = mod_file->symbol_table.getID(*name);
+  switch (mod_file->symbol_table.getType(symb_id))
+    {
+    case eExogenous:
+    case eExogenousDet:
+      break;
+    default:
+      error(*name + " is not exogenous.");
+    }
+}
+
 void
 ParsingDriver::set_std_prior(string *name, string *subsample_name)
 {
diff --git a/ParsingDriver.hh b/ParsingDriver.hh
index b0f2fb2b..b136e21c 100644
--- a/ParsingDriver.hh
+++ b/ParsingDriver.hh
@@ -93,6 +93,9 @@ private:
   //! Checks that a given symbol exists and is a endogenous or exogenous, and stops with an error message if it isn't
   void check_symbol_is_endogenous_or_exogenous(string *name);
 
+  //! Checks that a given symbol exists and is a exogenous, and stops with an error message if it isn't
+  void check_symbol_is_exogenous(string *name);
+
   //! Checks for symbol existence in model block. If it doesn't exist, an error message is stored to be printed at
   //! the end of the model block
   void check_symbol_existence_in_model_block(const string &name);
@@ -196,7 +199,9 @@ private:
       Ri_TYPE
     };
   SvarRestrictionType svar_restriction_type;
-
+  //! Temporary storage for generate_irf_elements
+  GenerateIRFsStatement::generate_irf_elements_t generate_irf_elements;
+  vector<string> generate_irf_names;
   //! Temporary storage for argument list of external function
   stack<vector<expr_t> >  stack_external_function_args;
   //! Temporary storage for parameters in joint prior statement
@@ -525,6 +530,9 @@ public:
   void add_lower_cholesky();
   //! Svar_Global_Identification_Check Statement
   void add_svar_global_identification_check();
+  //! generate_irfs Block
+  void end_generate_irfs();
+  void add_generate_irfs_element(const string *name, string *exo1, string *value1, string *exo2, string *value2);
   //! Forecast Statement
   void forecast();
   void set_trends();
-- 
GitLab