From f78c428d99884c4b336dba0a7df20c7ad41fb682 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?S=C3=A9bastien=20Villemot?= <sebastien@dynare.org>
Date: Wed, 18 Oct 2023 13:48:45 -0400
Subject: [PATCH] =?UTF-8?q?New=20option=20=E2=80=9Crelative=5Fto=5Finitval?=
 =?UTF-8?q?=E2=80=9D=20to=20=E2=80=9Cmshocks=E2=80=9D=20block?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/DynareBison.yy   | 47 ++++++++++++++++++++++++++++++++++++++------
 src/DynareFlex.ll    |  1 +
 src/ParsingDriver.cc | 14 ++++++++-----
 src/ParsingDriver.hh |  4 ++--
 src/Shocks.cc        | 38 ++++++++++++++++++++++++++---------
 src/Shocks.hh        | 20 +++++++++++++------
 6 files changed, 96 insertions(+), 28 deletions(-)

diff --git a/src/DynareBison.yy b/src/DynareBison.yy
index 7e21d57d..521100fa 100644
--- a/src/DynareBison.yy
+++ b/src/DynareBison.yy
@@ -33,6 +33,13 @@ class ParsingDriver;
 }
 
 %code requires {
+#include <string>
+#include <vector>
+#include <map>
+#include <utility>
+#include <tuple>
+#include <variant>
+
 #include "CommonEnums.hh"
 #include "ExprNode.hh"
 }
@@ -196,7 +203,7 @@ class ParsingDriver;
 %token ENDVAL_STEADY STEADY_SOLVE_ALGO STEADY_MAXIT STEADY_TOLF STEADY_TOLX STEADY_MARKOWITZ
 %token HOMOTOPY_MAX_COMPLETION_SHARE HOMOTOPY_MIN_STEP_SIZE HOMOTOPY_INITIAL_STEP_SIZE HOMOTOPY_STEP_SIZE_INCREASE_SUCCESS_COUNT
 %token HOMOTOPY_LINEARIZATION_FALLBACK HOMOTOPY_MARGINAL_LINEARIZATION_FALLBACK FROM_INITVAL_TO_ENDVAL
-%token STATIC_MFS
+%token STATIC_MFS RELATIVE_TO_INITVAL
 
 %token <vector<string>> SYMBOL_VEC
 
@@ -228,6 +235,8 @@ class ParsingDriver;
 %type <pair<string, expr_t>> occbin_constraints_regime_option
 %type <PacTargetKind> pac_target_kind
 %type <vector<tuple<string, string, vector<pair<string, string>>>>> symbol_list_with_tex_and_partition
+%type <map<string, variant<bool, string>>> mshocks_options_list
+%type <pair<string, variant<bool, string>>> mshocks_option
 %%
 
 %start statement_list;
@@ -1280,13 +1289,39 @@ svar_options : o_coefficients
              | o_chain
              ;
 
-mshocks : MSHOCKS ';' mshock_list END ';' { driver.end_mshocks(false); }
-        | MSHOCKS '(' OVERWRITE ')' ';' mshock_list END ';' { driver.end_mshocks(true); }
-        | MSHOCKS '(' LEARNT_IN EQUAL INT_NUMBER ')' ';' mshock_list END ';' { driver.end_mshocks_learnt_in($5, false); }
-        | MSHOCKS '(' LEARNT_IN EQUAL INT_NUMBER COMMA OVERWRITE ')' ';' mshock_list END ';' { driver.end_mshocks_learnt_in($5, true); }
-        | MSHOCKS '(' OVERWRITE COMMA LEARNT_IN EQUAL INT_NUMBER ')' ';' mshock_list END ';' { driver.end_mshocks_learnt_in($7, true); }
+mshocks : MSHOCKS ';' mshock_list END ';'
+          { driver.end_mshocks(false, false); }
+        | MSHOCKS '(' mshocks_options_list ')' ';' mshock_list END ';'
+          {
+            /* NB: the following relies of the fact that bool is the first
+               alternative in the variant, so that default initialization of the
+               variant by the [] operator will give false */
+            if ($3.contains("learnt_in"))
+              driver.end_mshocks_learnt_in(get<string>($3.at("learnt_in")), get<bool>($3["overwrite"]), get<bool>($3["relative_to_initval"]));
+            else
+              driver.end_mshocks(get<bool>($3["overwrite"]), get<bool>($3["relative_to_initval"]));
+          }
         ;
 
+mshocks_options_list : mshocks_option
+                       { $$ = { $1 }; }
+                     | mshocks_options_list mshocks_option
+                       {
+                         $$ = $1;
+                         auto [it, success] = $$.insert($2);
+                         if (!success)
+                           driver.error("The '" + $2.first + "' option is declared multiple times");
+                       }
+                     ;
+
+mshocks_option : OVERWRITE
+                 { $$ = { "overwrite", true }; }
+               | LEARNT_IN EQUAL INT_NUMBER
+                 { $$ = { "learnt_in", $3 }; }
+               | RELATIVE_TO_INITVAL
+                 { $$ = { "relative_to_initval", true }; }
+               ;
+
 mshock_list : mshock_list det_shock_elem
             | det_shock_elem
             ;
diff --git a/src/DynareFlex.ll b/src/DynareFlex.ll
index 60544e89..092712bf 100644
--- a/src/DynareFlex.ll
+++ b/src/DynareFlex.ll
@@ -829,6 +829,7 @@ DATE -?[0-9]+([ya]|m([1-9]|1[0-2])|q[1-4])
   yylval->build<string>(yytext);
   return token::ERROR_RELAX;
 }
+<DYNARE_BLOCK>relative_to_initval {return token::RELATIVE_TO_INITVAL;}
 
 <DYNARE_BLOCK>; {return Dynare::parser::token_type (yytext[0]);}
 <DYNARE_BLOCK># {return Dynare::parser::token_type (yytext[0]);}
diff --git a/src/ParsingDriver.cc b/src/ParsingDriver.cc
index f8ce2cac..18abd332 100644
--- a/src/ParsingDriver.cc
+++ b/src/ParsingDriver.cc
@@ -887,9 +887,10 @@ ParsingDriver::end_shocks(bool overwrite)
 }
 
 void
-ParsingDriver::end_mshocks(bool overwrite)
+ParsingDriver::end_mshocks(bool overwrite, bool relative_to_initval)
 {
-  mod_file->addStatement(make_unique<MShocksStatement>(overwrite, move(det_shocks),
+  mod_file->addStatement(make_unique<MShocksStatement>(overwrite, relative_to_initval,
+                                                       move(det_shocks),
                                                        mod_file->symbol_table));
   det_shocks.clear();
   if (!learnt_shocks_add.empty())
@@ -960,14 +961,14 @@ ParsingDriver::end_shocks_learnt_in(const string &learnt_in_period, bool overwri
 }
 
 void
-ParsingDriver::end_mshocks_learnt_in(const string &learnt_in_period, bool overwrite)
+ParsingDriver::end_mshocks_learnt_in(const string &learnt_in_period, bool overwrite, bool relative_to_initval)
 {
   int learnt_in_period_int = stoi(learnt_in_period);
   if (learnt_in_period_int < 1)
     error("mshocks: value '" + learnt_in_period + "' is not allowed for 'learnt_in' option");
   if (learnt_in_period_int == 1)
     {
-      end_mshocks(overwrite);
+      end_mshocks(overwrite, relative_to_initval);
       return;
     }
 
@@ -977,11 +978,14 @@ ParsingDriver::end_mshocks_learnt_in(const string &learnt_in_period, bool overwr
         error("mshocks: for variable " + mod_file->symbol_table.getName(symb_id) + ", shock period (" + to_string(period1) + ") is earlier than the period in which the shock is learnt (" + learnt_in_period + ")");
 
   ShocksLearntInStatement::learnt_shocks_t learnt_shocks;
+  const auto type { relative_to_initval ?
+                    ShocksLearntInStatement::LearntShockType::multiplyInitialSteadyState :
+                    ShocksLearntInStatement::LearntShockType::multiplySteadyState };
   for (const auto &[id, v] : det_shocks)
     {
       vector<tuple<ShocksLearntInStatement::LearntShockType, int, int, expr_t>> v2;
       for (auto [period1, period2, value] : v)
-        v2.emplace_back(ShocksLearntInStatement::LearntShockType::multiplySteadyState, period1, period2, value);
+        v2.emplace_back(type, period1, period2, value);
       learnt_shocks[id] = v2;
     }
 
diff --git a/src/ParsingDriver.hh b/src/ParsingDriver.hh
index 7c6f7144..f9a47ac1 100644
--- a/src/ParsingDriver.hh
+++ b/src/ParsingDriver.hh
@@ -447,13 +447,13 @@ public:
   //! Writes a shocks statement
   void end_shocks(bool overwrite);
   //! Writes a mshocks statement
-  void end_mshocks(bool overwrite);
+  void end_mshocks(bool overwrite, bool relative_to_initval);
   //! Writes a shocks(surprise) statement
   void end_shocks_surprise(bool overwrite);
   //! Writes a shocks(learnt_in=…) block
   void end_shocks_learnt_in(const string &learnt_in_period, bool overwrite);
   //! Writes a mshocks(learnt_in=…) block
-  void end_mshocks_learnt_in(const string &learnt_in_period, bool overwrite);
+  void end_mshocks_learnt_in(const string &learnt_in_period, bool overwrite, bool relative_to_initval);
   //! Writes a heteroskedastic_shocks statement
   void end_heteroskedastic_shocks(bool overwrite);
   /* Adds a deterministic shock, a path element inside a
diff --git a/src/Shocks.cc b/src/Shocks.cc
index 1218c6c9..c0c2b95e 100644
--- a/src/Shocks.cc
+++ b/src/Shocks.cc
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2003-2022 Dynare Team
+ * Copyright © 2003-2023 Dynare Team
  *
  * This file is part of Dynare.
  *
@@ -24,12 +24,11 @@
 
 #include "Shocks.hh"
 
-AbstractShocksStatement::AbstractShocksStatement(bool mshocks_arg,
-                                                 bool overwrite_arg,
+AbstractShocksStatement::AbstractShocksStatement(bool overwrite_arg, ShockType type_arg,
                                                  det_shocks_t det_shocks_arg,
                                                  const SymbolTable &symbol_table_arg) :
-  mshocks{mshocks_arg},
   overwrite{overwrite_arg},
+  type{type_arg},
   det_shocks{move(det_shocks_arg)},
   symbol_table{symbol_table_arg}
 {
@@ -48,7 +47,7 @@ AbstractShocksStatement::writeDetShocks(ostream &output) const
                << boolalpha
                << "struct('exo_det'," << exo_det
                << ",'exo_id'," << symbol_table.getTypeSpecificID(id)+1
-               << ",'multiplicative'," << mshocks
+               << ",'type','" << typeToString(type) << "'"
                << ",'periods'," << period1 << ":" << period2
                << ",'value',";
         value->writeOutput(output);
@@ -87,6 +86,21 @@ AbstractShocksStatement::writeJsonDetShocks(ostream &output) const
   output << "]";
 }
 
+string
+AbstractShocksStatement::typeToString(ShockType type)
+{
+  switch (type)
+    {
+    case ShockType::level:
+      return "level";
+    case ShockType::multiplySteadyState:
+      return "multiply_steady_state";
+    case ShockType::multiplyInitialSteadyState:
+      return "multiply_initial_steady_state";
+    }
+  __builtin_unreachable(); // Silence GCC warning
+}
+
 ShocksStatement::ShocksStatement(bool overwrite_arg,
                                  det_shocks_t det_shocks_arg,
                                  var_and_std_shocks_t var_shocks_arg,
@@ -94,7 +108,7 @@ ShocksStatement::ShocksStatement(bool overwrite_arg,
                                  covar_and_corr_shocks_t covar_shocks_arg,
                                  covar_and_corr_shocks_t corr_shocks_arg,
                                  const SymbolTable &symbol_table_arg) :
-  AbstractShocksStatement{false, overwrite_arg, move(det_shocks_arg), symbol_table_arg},
+  AbstractShocksStatement{overwrite_arg, ShockType::level, move(det_shocks_arg), symbol_table_arg},
   var_shocks{move(var_shocks_arg)},
   std_shocks{move(std_shocks_arg)},
   covar_shocks{move(covar_shocks_arg)},
@@ -400,10 +414,13 @@ ShocksStatement::has_calibrated_measurement_errors() const
   return false;
 }
 
-MShocksStatement::MShocksStatement(bool overwrite_arg,
+MShocksStatement::MShocksStatement(bool overwrite_arg, bool relative_to_initval_arg,
                                    det_shocks_t det_shocks_arg,
                                    const SymbolTable &symbol_table_arg) :
-  AbstractShocksStatement{true, overwrite_arg, move(det_shocks_arg), symbol_table_arg}
+  AbstractShocksStatement{overwrite_arg,
+                          relative_to_initval_arg ? ShockType::multiplyInitialSteadyState : ShockType::multiplySteadyState,
+                          move(det_shocks_arg), symbol_table_arg},
+  relative_to_initval{relative_to_initval_arg}
 {
 }
 
@@ -425,7 +442,8 @@ void
 MShocksStatement::writeJsonOutput(ostream &output) const
 {
   output << R"({"statementName": "mshocks")"
-         << R"(, "overwrite": )" << boolalpha << overwrite;
+         << R"(, "overwrite": )" << boolalpha << overwrite
+         << R"(, "relative_to_initval": )" << boolalpha << relative_to_initval;
   if (!det_shocks.empty())
     {
       output << ", ";
@@ -526,6 +544,8 @@ ShocksLearntInStatement::typeToString(LearntShockType type)
       return "multiply";
     case LearntShockType::multiplySteadyState:
       return "multiply_steady_state";
+    case LearntShockType::multiplyInitialSteadyState:
+      return "multiply_initial_steady_state";
     }
   __builtin_unreachable(); // Silence GCC warning
 }
diff --git a/src/Shocks.hh b/src/Shocks.hh
index e5743d02..43573760 100644
--- a/src/Shocks.hh
+++ b/src/Shocks.hh
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2003-2022 Dynare Team
+ * Copyright © 2003-2023 Dynare Team
  *
  * This file is part of Dynare.
  *
@@ -35,17 +35,23 @@ class AbstractShocksStatement : public Statement
 public:
   // The tuple is (period1, period2, value)
   using det_shocks_t = map<int, vector<tuple<int, int, expr_t>>>;
+  enum class ShockType
+    {
+      level,                     // The value is the level of the exogenous (“values” statement in “shocks”)
+      multiplySteadyState,       // The value is the ratio of the exogenous over its (terminal) steady state (“values” statement in “mshocks”)
+      multiplyInitialSteadyState // The value is the ratio of the exogenous over its initial steady state (“values” statement in “mshocks(relative_to_initval)”)
+    };
 protected:
-  //! Is this statement a "mshocks" statement ? (instead of a "shocks" statement)
-  const bool mshocks;
   //! Does this "shocks" statement replace the previous ones?
   const bool overwrite;
+  const ShockType type; // Type of shocks represented by this block
   const det_shocks_t det_shocks;
   const SymbolTable &symbol_table;
   void writeDetShocks(ostream &output) const;
   void writeJsonDetShocks(ostream &output) const;
+  static string typeToString(ShockType type);
 
-  AbstractShocksStatement(bool mshocks_arg, bool overwrite_arg,
+  AbstractShocksStatement(bool overwrite_arg, ShockType type_arg,
                           det_shocks_t det_shocks_arg,
                           const SymbolTable &symbol_table_arg);
 };
@@ -79,7 +85,8 @@ public:
 class MShocksStatement : public AbstractShocksStatement
 {
 public:
-  MShocksStatement(bool overwrite_arg,
+  const bool relative_to_initval;
+  MShocksStatement(bool overwrite_arg, bool relative_to_initval_arg,
                    det_shocks_t det_shocks_arg,
                    const SymbolTable &symbol_table_arg);
   void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
@@ -120,7 +127,8 @@ public:
       level,              // The value is the level of the exogenous (“values” statement in “shocks(learnt_in=…)”)
       add,                // The value is the additive change of the exogenous compared to previous information period (“add” statement in “shocks(learnt_in=…)”)
       multiply,           // The value is the multiplicative change of the exogenous compared to previous information period (“multiply” statement in “shocks(learnt_in=…)”)
-      multiplySteadyState // The value is the ratio of the exogenous over its (terminal) steady state as anticipated in the same informational period (“values” statement in “mshocks(learnt_in=…)”)
+      multiplySteadyState, // The value is the ratio of the exogenous over its (terminal) steady state as anticipated in the same informational period (“values” statement in “mshocks(learnt_in=…)”)
+      multiplyInitialSteadyState // The value is the ratio of the exogenous over its initial steady state as anticipated in the same informational period (“values” statement in “mshocks(learnt_in=…, relative_to_initval)”)
     };
   // The tuple is (type, period1, period2, value)
   using learnt_shocks_t = map<int, vector<tuple<LearntShockType, int, int, expr_t>>>;
-- 
GitLab