From 341b9c666762b7a581483c82cb8c4f30483e157c Mon Sep 17 00:00:00 2001
From: Houtan Bastani <houtan@dynare.org>
Date: Thu, 28 Feb 2019 19:22:34 +0100
Subject: [PATCH] pac_model: allow diff as argument to growth option

---
 src/ComputingTasks.cc | 68 +++++++++++++++++++++++++++++++------------
 src/ComputingTasks.hh |  9 +++---
 src/DynamicModel.cc   |  6 +++-
 src/DynamicModel.hh   |  2 +-
 src/DynareBison.yy    |  6 ++--
 src/ModFile.cc        | 15 ++++++++--
 src/ModFile.hh        |  3 +-
 src/ParsingDriver.cc  | 41 ++++++++++++++------------
 src/ParsingDriver.hh  | 10 +++++--
 9 files changed, 106 insertions(+), 54 deletions(-)

diff --git a/src/ComputingTasks.cc b/src/ComputingTasks.cc
index 63aae410..2f3976e0 100644
--- a/src/ComputingTasks.cc
+++ b/src/ComputingTasks.cc
@@ -260,44 +260,76 @@ PriorPosteriorFunctionStatement::writeJsonOutput(ostream &output) const
 PacModelStatement::PacModelStatement(string name_arg,
                                      string aux_model_name_arg,
                                      string discount_arg,
-                                     int growth_symb_id_arg,
-                                     int growth_lag_arg,
+                                     expr_t growth_arg,
                                      double steady_state_growth_rate_number_arg,
                                      int steady_state_growth_rate_symb_id_arg,
                                      const SymbolTable &symbol_table_arg) :
   name{move(name_arg)},
   aux_model_name{move(aux_model_name_arg)},
   discount{move(discount_arg)},
-  growth_symb_id{growth_symb_id_arg},
-  growth_lag{growth_lag_arg},
+  growth{growth_arg},
   steady_state_growth_rate_number{steady_state_growth_rate_number_arg},
   steady_state_growth_rate_symb_id{steady_state_growth_rate_symb_id_arg},
   symbol_table{symbol_table_arg}
 {
+  growth_symb_id = -1;
+  growth_lag = 0;
 }
 
 void
 PacModelStatement::checkPass(ModFileStructure &mod_file_struct, WarningConsolidation &warnings)
 {
-  mod_file_struct.pac_params.insert(symbol_table.getID(discount));
-  if (growth_symb_id >= 0)
+  if (growth == nullptr)
+    return;
+
+  VariableNode *vn = dynamic_cast<VariableNode *>(growth);
+  if (vn != nullptr)
     {
-      switch (symbol_table.getType(growth_symb_id))
-        {
-        case SymbolType::endogenous:
-        case SymbolType::exogenous:
-        case SymbolType::parameter:
-          break;
-        default:
+      mod_file_struct.pac_params.insert(vn->symb_id);
+      mod_file_struct.pac_params.insert(vn->lag);
+    }
+
+  UnaryOpNode *uon = dynamic_cast<UnaryOpNode *>(growth);
+  if (uon != nullptr)
+    if (uon->op_code == UnaryOpcode::diff)
+      {
+        VariableNode *uonvn = dynamic_cast<VariableNode *>(uon->arg);
+        UnaryOpNode *uon1 = dynamic_cast<UnaryOpNode *>(uon->arg);
+        while (uonvn == nullptr && uon1 != nullptr)
+          {
+            uonvn = dynamic_cast<VariableNode *>(uon1->arg);
+            uon1 = dynamic_cast<UnaryOpNode *>(uon1->arg);
+          }
+        if (uonvn == nullptr)
           {
-            cerr << "ERROR: The Expression passed to the growth option of pac_model must be an "
-                 << "endogenous (lagged or not), exogenous (lagged or not), or parameter" << endl;
+            cerr << "Pac growth parameter must be either a variable or a diff unary op of a variable" << endl;
             exit(EXIT_FAILURE);
           }
-        }
-      mod_file_struct.pac_params.insert(growth_symb_id);
-      mod_file_struct.pac_params.insert(growth_lag);
+         mod_file_struct.pac_params.insert(uonvn->symb_id);
+         mod_file_struct.pac_params.insert(uonvn->lag);
+      }
+
+  if (vn == nullptr && uon == nullptr)
+    {
+      cerr << "Pac growth parameter must be either a variable or a diff unary op of a variable" << endl;
+      exit(EXIT_FAILURE);
+    }
+}
+
+void
+PacModelStatement::overwriteGrowth(expr_t new_growth)
+{
+  if (new_growth == nullptr || growth == nullptr)
+    return;
+
+  growth = new_growth;
+  VariableNode *vn = dynamic_cast<VariableNode *>(growth);
+  if (vn == nullptr)
+    {
+      cerr << "PacModelStatement::overwriteGrowth: Internal Dynare error: should not arrive here" << endl;
     }
+  growth_symb_id = vn->symb_id;
+  growth_lag = vn->lag;
 }
 
 void
diff --git a/src/ComputingTasks.hh b/src/ComputingTasks.hh
index 47c4446e..36764150 100644
--- a/src/ComputingTasks.hh
+++ b/src/ComputingTasks.hh
@@ -138,22 +138,23 @@ class PacModelStatement : public Statement
 {
 public:
   const string name, aux_model_name, discount;
-  const int growth_symb_id, growth_lag;
+  expr_t growth;
 private:
   const double steady_state_growth_rate_number;
   const int steady_state_growth_rate_symb_id;
   const SymbolTable &symbol_table;
-  vector<int> lhs;
 public:
+  int growth_symb_id;
+  int growth_lag;
   PacModelStatement(string name_arg,
                     string aux_model_name_arg,
                     string discount_arg,
-                    int growth_symb_id_arg,
-                    int growth_lag_arg,
+                    expr_t growth_arg,
                     double steady_state_growth_rate_number_arg,
                     int steady_state_growth_rate_symb_id_arg,
                     const SymbolTable &symbol_table_arg);
   void checkPass(ModFileStructure &mod_file_struct, WarningConsolidation &warnings) override;
+  void overwriteGrowth(expr_t new_growth);
   void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;
   void writeJsonOutput(ostream &output) const override;
 };
diff --git a/src/DynamicModel.cc b/src/DynamicModel.cc
index 28ab0c6c..eeba1b8c 100644
--- a/src/DynamicModel.cc
+++ b/src/DynamicModel.cc
@@ -6303,7 +6303,7 @@ DynamicModel::substituteUnaryOps(StaticModel &static_model, diff_table_t &nodes,
 }
 
 void
-DynamicModel::substituteDiff(StaticModel &static_model, diff_table_t &diff_table, ExprNode::subst_table_t &diff_subst_table)
+DynamicModel::substituteDiff(StaticModel &static_model, diff_table_t &diff_table, ExprNode::subst_table_t &diff_subst_table, vector<expr_t> &pac_growth)
 {
   set<int> used_local_vars;
   for (const auto & equation : equations)
@@ -6354,6 +6354,10 @@ DynamicModel::substituteDiff(StaticModel &static_model, diff_table_t &diff_table
       equation = substeq;
     }
 
+  for (auto & it : pac_growth)
+    if (it != nullptr)
+      it = it->substituteDiff(static_model, diff_table, diff_subst_table, neweqs);
+
   // Add new equations
   for (auto & neweq : neweqs)
     addEquation(neweq, -1);
diff --git a/src/DynamicModel.hh b/src/DynamicModel.hh
index 99759db4..30394526 100644
--- a/src/DynamicModel.hh
+++ b/src/DynamicModel.hh
@@ -444,7 +444,7 @@ public:
   void substituteUnaryOps(StaticModel &static_model, diff_table_t &nodes, ExprNode::subst_table_t &subst_table, vector<int> &eqnumbers);
 
   //! Substitutes diff operator
-  void substituteDiff(StaticModel &static_model, diff_table_t &diff_table, ExprNode::subst_table_t &diff_subst_table);
+  void substituteDiff(StaticModel &static_model, diff_table_t &diff_table, ExprNode::subst_table_t &diff_subst_table, vector<expr_t> &pac_growth);
 
   //! Substitute VarExpectation operators
   void substituteVarExpectation(const map<string, expr_t> &subst_table);
diff --git a/src/DynareBison.yy b/src/DynareBison.yy
index 75f7d105..dc952902 100644
--- a/src/DynareBison.yy
+++ b/src/DynareBison.yy
@@ -388,7 +388,7 @@ trend_component_model_options : o_trend_component_model_name
                               | o_trend_component_model_eq_tags
                               ;
 
-pac_model : PAC_MODEL '(' pac_model_options_list ')' ';' { driver.pac_model(); } ;
+pac_model : PAC_MODEL '(' { driver.begin_pac_model(); } pac_model_options_list ')' ';' { driver.pac_model(); };
 
 pac_model_options_list : pac_model_options_list COMMA pac_model_options
                        | pac_model_options
@@ -3131,9 +3131,7 @@ o_file : FILE EQUAL filename { driver.option_str("file", $3); };
 o_pac_name : MODEL_NAME EQUAL symbol { driver.option_str("pac.model_name", $3); };
 o_pac_aux_model_name : AUXILIARY_MODEL_NAME EQUAL symbol { driver.option_str("pac.aux_model_name", $3); };
 o_pac_discount : DISCOUNT EQUAL symbol { driver.option_str("pac.discount", $3); };
-o_pac_growth : GROWTH EQUAL symbol { driver.set_pac_growth($3, 0); }
-             | GROWTH EQUAL symbol '(' MINUS INT_NUMBER ')' { driver.set_pac_growth($3, stoi($6)); }
-             ;
+o_pac_growth : GROWTH { driver.begin_pac_growth(); } EQUAL hand_side { driver.set_pac_growth($4); };
 o_pac_steady_state_growth : STEADY_STATE_GROWTH EQUAL signed_number { driver.set_pac_steady_state_growth($3); }
                           | STEADY_STATE_GROWTH EQUAL symbol { driver.set_pac_steady_state_growth($3); }
                           ;
diff --git a/src/ModFile.cc b/src/ModFile.cc
index 4b133dab..2ddecdc1 100644
--- a/src/ModFile.cc
+++ b/src/ModFile.cc
@@ -370,8 +370,13 @@ ModFile::transformPass(bool nostrict, bool stochastic, bool compute_xrefs, const
   for (auto & statement : statements)
     {
       auto pms = dynamic_cast<PacModelStatement *>(statement.get());
-      if (pms != nullptr && pms->aux_model_name == "")
-        dynamic_model.declarePacModelConsistentExpectationEndogs(pms->name);
+      if (pms != nullptr)
+        {
+          if (pms->growth != nullptr)
+            pac_growth.push_back(pms->growth);
+          if (pms->aux_model_name == "")
+            dynamic_model.declarePacModelConsistentExpectationEndogs(pms->name);
+        }
     }
   dynamic_model.substituteAdl();
   dynamic_model.setLeadsLagsOrig();
@@ -409,7 +414,7 @@ ModFile::transformPass(bool nostrict, bool stochastic, bool compute_xrefs, const
   // Create auxiliary variable and equations for Diff operators
   diff_table_t diff_table;
   ExprNode::subst_table_t diff_subst_table;
-  dynamic_model.substituteDiff(diff_static_model, diff_table, diff_subst_table);
+  dynamic_model.substituteDiff(diff_static_model, diff_table, diff_subst_table, pac_growth);
 
   // Fill Trend Component Model Table
   dynamic_model.fillTrendComponentModelTable();
@@ -419,11 +424,15 @@ ModFile::transformPass(bool nostrict, bool stochastic, bool compute_xrefs, const
   original_model.fillVarModelTableFromOrigModel(diff_static_model);
 
   // Pac Model
+  int i = 0;
   for (auto & statement : statements)
     {
       auto pms = dynamic_cast<PacModelStatement *>(statement.get());
       if (pms != nullptr)
          {
+           if (pms->growth != nullptr)
+             pms->overwriteGrowth(pac_growth.at(i++));
+
            int max_lag;
            vector<int> lhs;
            vector<bool> nonstationary;
diff --git a/src/ModFile.hh b/src/ModFile.hh
index a7b73f50..e6c4f2bf 100644
--- a/src/ModFile.hh
+++ b/src/ModFile.hh
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2006-2018 Dynare Team
+ * Copyright (C) 2006-2019 Dynare Team
  *
  * This file is part of Dynare.
  *
@@ -130,6 +130,7 @@ private:
   void writeJsonOutputParsingCheck(const string &basename, JsonFileOutputType json_output_mode, bool transformpass, bool computingpass) const;
   void writeJsonComputingPassOutput(const string &basename, JsonFileOutputType json_output_mode, bool jsonderivsimple) const;
   void writeJsonFileHelper(const string &fname, ostringstream &output) const;
+  vector<expr_t> pac_growth;
 public:
   //! Add a statement
   void addStatement(unique_ptr<Statement> st);
diff --git a/src/ParsingDriver.cc b/src/ParsingDriver.cc
index 56465009..39f89247 100644
--- a/src/ParsingDriver.cc
+++ b/src/ParsingDriver.cc
@@ -2607,6 +2607,22 @@ ParsingDriver::add_pac_expectation(const string &var_model_name)
   return data_tree->AddPacExpectation(var_model_name);
 }
 
+void
+ParsingDriver::begin_pac_growth()
+{
+  set_current_data_tree(&mod_file->dynamic_model);
+}
+
+void
+ParsingDriver::begin_pac_model()
+{
+  parsing_pac_model = true;
+  pac_growth = nullptr;
+  pac_steady_state_growth_rate_number = -1;
+  pac_steady_state_growth_rate_symb_id = -1;
+  options_list.clear();
+}
+
 void
 ParsingDriver::pac_model()
 {
@@ -2626,13 +2642,6 @@ ParsingDriver::pac_model()
       }
     else
       aux_model_name = it->second;
-  else
-    if (pac_growth_symb_id >= 0 && mod_file->symbol_table.getType(pac_growth_symb_id) == SymbolType::parameter
-        && (pac_steady_state_growth_rate_number >= 0 || pac_steady_state_growth_rate_symb_id >=0))
-      warning("If growth option is constant, steady_state_growth is ignored");
-    else if (pac_growth_symb_id >= 0 && mod_file->symbol_table.getType(pac_growth_symb_id) != SymbolType::parameter
-             && (pac_steady_state_growth_rate_number < 0 || pac_steady_state_growth_rate_symb_id < 0))
-      error("The steady state growth rate of the target must be provided (steady_state_growth option) if option growth is not constant");
 
   if (pac_steady_state_growth_rate_symb_id >= 0
       && mod_file->symbol_table.getType(pac_steady_state_growth_rate_symb_id) != SymbolType::parameter)
@@ -2644,24 +2653,18 @@ ParsingDriver::pac_model()
   auto discount = it->second;
 
   mod_file->addStatement(make_unique<PacModelStatement>(name, aux_model_name, discount,
-                                                        pac_growth_symb_id, pac_growth_lag,
+                                                        pac_growth,
                                                         pac_steady_state_growth_rate_number,
                                                         pac_steady_state_growth_rate_symb_id,
                                                         mod_file->symbol_table));
-  pac_growth_symb_id = -1;
-  pac_growth_lag = 0;
-  pac_steady_state_growth_rate_number = -1;
-  pac_steady_state_growth_rate_symb_id = -1;
-  options_list.clear();
+  parsing_pac_model = false;
 }
 
 void
-ParsingDriver::set_pac_growth(const string &name, int lag)
+ParsingDriver::set_pac_growth(expr_t pac_growth_arg)
 {
-  if (!mod_file->symbol_table.exists(name))
-    error("Unknown symbol used in pac_growth option: " + name + "\n");
-  pac_growth_symb_id = mod_file->symbol_table.getID(name);
-  pac_growth_lag = -lag;
+  pac_growth = pac_growth_arg;
+  reset_data_tree();
 }
 
 void
@@ -2981,7 +2984,7 @@ ParsingDriver::add_model_var_or_external_function(const string &function_name, b
   expr_t nid;
   if (mod_file->symbol_table.exists(function_name))
     if (mod_file->symbol_table.getType(function_name) != SymbolType::externalFunction)
-      if (!in_model_block && !parsing_epilogue)
+      if (!in_model_block && !parsing_epilogue && !parsing_pac_model)
         {
           if (stack_external_function_args.top().size() > 0)
             error(string("Symbol ") + function_name + string(" cannot take arguments."));
diff --git a/src/ParsingDriver.hh b/src/ParsingDriver.hh
index e0fa48ae..78187ac2 100644
--- a/src/ParsingDriver.hh
+++ b/src/ParsingDriver.hh
@@ -255,8 +255,7 @@ private:
   WarningConsolidation &warnings;
 
   //! Temporary storage for growth declared in pac_model
-  int pac_growth_symb_id = -1;
-  int pac_growth_lag = 0;
+  expr_t pac_growth;
   double pac_steady_state_growth_rate_number = -1;
   int pac_steady_state_growth_rate_symb_id = -1;
 
@@ -271,6 +270,9 @@ private:
   //! True when parsing the epilogue block
   bool parsing_epilogue{false};
 
+  //! True when parsing pac_model statement
+  bool parsing_pac_model{false};
+
 public:
   ParsingDriver(WarningConsolidation &warnings_arg, bool nostrict_arg) :
     warnings{warnings_arg}, nostrict{nostrict_arg} { };
@@ -732,9 +734,11 @@ public:
   //! Writes token "PAC_EXPECTATION(model_name, discount, growth)" to model tree
   expr_t add_pac_expectation(const string &var_model_name);
   //! Creates pac_model statement
+  void begin_pac_growth();
+  void begin_pac_model();
   void pac_model();
   //! Adds growth for pac
-  void set_pac_growth(const string &name, int lag = 0);
+  void set_pac_growth(expr_t pac_growth_arg);
   //! Adds steady state growth for pac
   void set_pac_steady_state_growth(const string &name_or_number);
   //! Writes token "diff(arg1)" to model tree
-- 
GitLab