From 0c1373bc5e796858533b4b8ded6cb72936111a4f Mon Sep 17 00:00:00 2001
From: Houtan Bastani <houtan@dynare.org>
Date: Tue, 10 Sep 2019 12:37:11 +0200
Subject: [PATCH] stop processing when certain functions are used in a linear
 context on endogenous/exogenous variables. closes dynare#1537

---
 src/DataTree.cc | 28 ++++++++++++++++++++++++++++
 src/DataTree.hh |  6 +++++-
 src/ModFile.cc  | 48 ++++++++++++++++++++++++++++++++++++------------
 3 files changed, 69 insertions(+), 13 deletions(-)

diff --git a/src/DataTree.cc b/src/DataTree.cc
index 4c6a4151..28dbbc6d 100644
--- a/src/DataTree.cc
+++ b/src/DataTree.cc
@@ -765,6 +765,20 @@ DataTree::isUnaryOpUsed(UnaryOpcode opcode) const
   return false;
 }
 
+bool
+DataTree::isUnaryOpUsedOnType(SymbolType type, UnaryOpcode opcode) const
+{
+  set<int> var;
+  for (const auto & it : unary_op_node_map)
+    if (get<1>(it.first) == opcode)
+      {
+        it.second->collectVariables(type, var);
+        if (!var.empty())
+          return true;
+      }
+  return false;
+}
+
 bool
 DataTree::isBinaryOpUsed(BinaryOpcode opcode) const
 {
@@ -775,6 +789,20 @@ DataTree::isBinaryOpUsed(BinaryOpcode opcode) const
   return false;
 }
 
+bool
+DataTree::isBinaryOpUsedOnType(SymbolType type, BinaryOpcode opcode) const
+{
+  set<int> var;
+  for (const auto & it : binary_op_node_map)
+    if (get<2>(it.first) == opcode)
+      {
+        it.second->collectVariables(type, var);
+        if (!var.empty())
+          return true;
+      }
+  return false;
+}
+
 bool
 DataTree::isTrinaryOpUsed(TrinaryOpcode opcode) const
 {
diff --git a/src/DataTree.hh b/src/DataTree.hh
index 9b62ad6a..6fb6bb71 100644
--- a/src/DataTree.hh
+++ b/src/DataTree.hh
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2003-2018 Dynare Team
+ * Copyright © 2003-2019 Dynare Team
  *
  * This file is part of Dynare.
  *
@@ -261,8 +261,12 @@ public:
   bool isSymbolUsed(int symb_id) const;
   //! Checks if a given unary op is used somewhere in the data tree
   bool isUnaryOpUsed(UnaryOpcode opcode) const;
+  //! Checks if a given unary op is used somewhere in the data tree on an endogenous variable
+  bool isUnaryOpUsedOnType(SymbolType type, UnaryOpcode opcode) const;
   //! Checks if a given binary op is used somewhere in the data tree
   bool isBinaryOpUsed(BinaryOpcode opcode) const;
+  //! Checks if a given binary op is used somewhere in the data tree on an endogenous variable
+  bool isBinaryOpUsedOnType(SymbolType type, BinaryOpcode opcode) const;
   //! Checks if a given trinary op is used somewhere in the data tree
   bool isTrinaryOpUsed(TrinaryOpcode opcode) const;
   //! Checks if a given external function is used somewhere in the data tree
diff --git a/src/ModFile.cc b/src/ModFile.cc
index dfa4d68e..00ea67b1 100644
--- a/src/ModFile.cc
+++ b/src/ModFile.cc
@@ -307,17 +307,41 @@ ModFile::checkPass(bool nostrict, bool stochastic)
     warnings << R"(WARNING: you are using a function (max, min, abs, sign) or an operator (<, >, <=, >=, ==, !=) which is unsuitable for a stochastic context; see the reference manual, section about "Expressions", for more details.)" << endl;
 
   if (linear
-      && (dynamic_model.isUnaryOpUsed(UnaryOpcode::sign)
-          || dynamic_model.isUnaryOpUsed(UnaryOpcode::abs)
-          || dynamic_model.isBinaryOpUsed(BinaryOpcode::max)
-          || dynamic_model.isBinaryOpUsed(BinaryOpcode::min)
-          || dynamic_model.isBinaryOpUsed(BinaryOpcode::greater)
-          || dynamic_model.isBinaryOpUsed(BinaryOpcode::less)
-          || dynamic_model.isBinaryOpUsed(BinaryOpcode::greaterEqual)
-          || dynamic_model.isBinaryOpUsed(BinaryOpcode::lessEqual)
-          || dynamic_model.isBinaryOpUsed(BinaryOpcode::equalEqual)
-          || dynamic_model.isBinaryOpUsed(BinaryOpcode::different)))
-    warnings << "WARNING: you have declared your model 'linear' but you are using a function (max, min, abs, sign) or an operator (<, >, <=, >=, ==, !=) which potentially makes it non-linear." << endl;
+      && (dynamic_model.isUnaryOpUsedOnType(SymbolType::endogenous, UnaryOpcode::sign)
+          || dynamic_model.isUnaryOpUsedOnType(SymbolType::endogenous, UnaryOpcode::abs)
+          || dynamic_model.isBinaryOpUsedOnType(SymbolType::endogenous, BinaryOpcode::max)
+          || dynamic_model.isBinaryOpUsedOnType(SymbolType::endogenous, BinaryOpcode::min)
+          || dynamic_model.isBinaryOpUsedOnType(SymbolType::endogenous, BinaryOpcode::greater)
+          || dynamic_model.isBinaryOpUsedOnType(SymbolType::endogenous, BinaryOpcode::less)
+          || dynamic_model.isBinaryOpUsedOnType(SymbolType::endogenous, BinaryOpcode::greaterEqual)
+          || dynamic_model.isBinaryOpUsedOnType(SymbolType::endogenous, BinaryOpcode::lessEqual)
+          || dynamic_model.isBinaryOpUsedOnType(SymbolType::endogenous, BinaryOpcode::equalEqual)
+          || dynamic_model.isBinaryOpUsedOnType(SymbolType::endogenous, BinaryOpcode::different)))
+    {
+      cerr << "ERROR: you have declared your model 'linear' but you are using a function "
+           << "(max, min, abs, sign) or an operator (<, >, <=, >=, ==, !=) on an "
+           << "endogenous variable." << endl;
+      exit(EXIT_FAILURE);
+    }
+
+  if (linear
+      && !mod_file_struct.perfect_foresight_solver_present
+      && (dynamic_model.isUnaryOpUsedOnType(SymbolType::exogenous, UnaryOpcode::sign)
+          || dynamic_model.isUnaryOpUsedOnType(SymbolType::exogenous, UnaryOpcode::abs)
+          || dynamic_model.isBinaryOpUsedOnType(SymbolType::exogenous, BinaryOpcode::max)
+          || dynamic_model.isBinaryOpUsedOnType(SymbolType::exogenous, BinaryOpcode::min)
+          || dynamic_model.isBinaryOpUsedOnType(SymbolType::exogenous, BinaryOpcode::greater)
+          || dynamic_model.isBinaryOpUsedOnType(SymbolType::exogenous, BinaryOpcode::less)
+          || dynamic_model.isBinaryOpUsedOnType(SymbolType::exogenous, BinaryOpcode::greaterEqual)
+          || dynamic_model.isBinaryOpUsedOnType(SymbolType::exogenous, BinaryOpcode::lessEqual)
+          || dynamic_model.isBinaryOpUsedOnType(SymbolType::exogenous, BinaryOpcode::equalEqual)
+          || dynamic_model.isBinaryOpUsedOnType(SymbolType::exogenous, BinaryOpcode::different)))
+    {
+      cerr << "ERROR: you have declared your model 'linear' but you are using a function "
+           << "(max, min, abs, sign) or an operator (<, >, <=, >=, ==, !=) on an "
+           << "exogenous variable in a non-perfect-foresight context." << endl;
+      exit(EXIT_FAILURE);
+    }
 
   // Test if some estimated parameters are used within the values of shocks
   // statements (see issue #469)
@@ -1193,7 +1217,7 @@ ModFile::writeExternalFilesJulia(const string &basename, FileOutputType output)
   jlOutputFile << endl
                << "options_ = dynare_options()" << endl
                << R"(options_.dynare_version = ")" << PACKAGE_VERSION << R"(")" << endl;
-  if (linear == 1)
+  if (linear)
     jlOutputFile << "options_.linear = true" << endl;
 
   // Write Model
-- 
GitLab