From 5a4297088dbbc1cd8b3e0332d89ecb75b6cfe6fe Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?S=C3=A9bastien=20Villemot?= <sebastien@dynare.org>
Date: Wed, 5 Apr 2023 14:53:47 +0200
Subject: [PATCH] Optimization: use std::unordered_map instead of std::map when
 computing temporary terms
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Improve performance on very large models (⩾ 5000 equations).

Note that std::unordered_set cannot be used for the temporary_terms_t type,
because ordering is needed there (for writing the output files).
---
 src/ExprNode.cc    | 64 ++++++++++++++++++++--------------------
 src/ExprNode.hh    | 73 +++++++++++++++++++++++++---------------------
 src/ModelTree.cc   | 40 ++++++++++++++++---------
 src/StaticModel.cc |  7 +++--
 4 files changed, 102 insertions(+), 82 deletions(-)

diff --git a/src/ExprNode.cc b/src/ExprNode.cc
index 8a203031..12527dab 100644
--- a/src/ExprNode.cc
+++ b/src/ExprNode.cc
@@ -104,7 +104,7 @@ ExprNode::cost([[maybe_unused]] int cost, [[maybe_unused]] bool is_matlab) const
 }
 
 int
-ExprNode::cost([[maybe_unused]] const vector<vector<temporary_terms_t>> &blocks_temporary_terms,
+ExprNode::cost([[maybe_unused]] const vector<vector<unordered_set<expr_t>>> &blocks_temporary_terms,
                [[maybe_unused]] bool is_matlab) const
 {
   // For a terminal node, the cost is null
@@ -112,7 +112,7 @@ ExprNode::cost([[maybe_unused]] const vector<vector<temporary_terms_t>> &blocks_
 }
 
 int
-ExprNode::cost([[maybe_unused]] const map<pair<int, int>, temporary_terms_t> &temp_terms_map,
+ExprNode::cost([[maybe_unused]] const map<pair<int, int>, unordered_set<expr_t>> &temp_terms_map,
                [[maybe_unused]] bool is_matlab) const
 {
   // For a terminal node, the cost is null
@@ -211,8 +211,8 @@ ExprNode::collectEndogenous(set<pair<int, int>> &result) const
 
 void
 ExprNode::computeTemporaryTerms([[maybe_unused]] const pair<int, int> &derivOrder,
-                                [[maybe_unused]] map<pair<int, int>, temporary_terms_t> &temp_terms_map,
-                                [[maybe_unused]] map<expr_t, pair<int, pair<int, int>>> &reference_count,
+                                [[maybe_unused]] map<pair<int, int>, unordered_set<expr_t>> &temp_terms_map,
+                                [[maybe_unused]] unordered_map<expr_t, pair<int, pair<int, int>>> &reference_count,
                                 [[maybe_unused]] bool is_matlab) const
 {
   // Nothing to do for a terminal node
@@ -220,8 +220,8 @@ ExprNode::computeTemporaryTerms([[maybe_unused]] const pair<int, int> &derivOrde
 
 void
 ExprNode::computeBlockTemporaryTerms([[maybe_unused]] int blk, [[maybe_unused]] int eq,
-                                     [[maybe_unused]] vector<vector<temporary_terms_t>> &blocks_temporary_terms,
-                                     [[maybe_unused]] map<expr_t, tuple<int, int, int>> &reference_count) const
+                                     [[maybe_unused]] vector<vector<unordered_set<expr_t>>> &blocks_temporary_terms,
+                                     [[maybe_unused]] unordered_map<expr_t, tuple<int, int, int>> &reference_count) const
 {
   // Nothing to do for a terminal node
 }
@@ -2377,7 +2377,7 @@ UnaryOpNode::computeDerivative(int deriv_id)
 }
 
 int
-UnaryOpNode::cost(const map<pair<int, int>, temporary_terms_t> &temp_terms_map, bool is_matlab) const
+UnaryOpNode::cost(const map<pair<int, int>, unordered_set<expr_t>> &temp_terms_map, bool is_matlab) const
 {
   // For a temporary term, the cost is null
   for (const auto &it : temp_terms_map)
@@ -2388,7 +2388,7 @@ UnaryOpNode::cost(const map<pair<int, int>, temporary_terms_t> &temp_terms_map,
 }
 
 int
-UnaryOpNode::cost(const vector<vector<temporary_terms_t>> &blocks_temporary_terms, bool is_matlab) const
+UnaryOpNode::cost(const vector<vector<unordered_set<expr_t>>> &blocks_temporary_terms, bool is_matlab) const
 {
   // For a temporary term, the cost is null
   for (const auto &blk_tt : blocks_temporary_terms)
@@ -2513,8 +2513,8 @@ UnaryOpNode::cost(int cost, bool is_matlab) const
 
 void
 UnaryOpNode::computeTemporaryTerms(const pair<int, int> &derivOrder,
-                                   map<pair<int, int>, temporary_terms_t> &temp_terms_map,
-                                   map<expr_t, pair<int, pair<int, int>>> &reference_count,
+                                   map<pair<int, int>, unordered_set<expr_t>> &temp_terms_map,
+                                   unordered_map<expr_t, pair<int, pair<int, int>>> &reference_count,
                                    bool is_matlab) const
 {
   expr_t this2 = const_cast<UnaryOpNode *>(this);
@@ -2535,8 +2535,8 @@ UnaryOpNode::computeTemporaryTerms(const pair<int, int> &derivOrder,
 }
 
 void
-UnaryOpNode::computeBlockTemporaryTerms(int blk, int eq, vector<vector<temporary_terms_t>> &blocks_temporary_terms,
-                                        map<expr_t, tuple<int, int, int>> &reference_count) const
+UnaryOpNode::computeBlockTemporaryTerms(int blk, int eq, vector<vector<unordered_set<expr_t>>> &blocks_temporary_terms,
+                                        unordered_map<expr_t, tuple<int, int, int>> &reference_count) const
 {
   expr_t this2 = const_cast<UnaryOpNode *>(this);
   if (auto it = reference_count.find(this2);
@@ -4256,7 +4256,7 @@ BinaryOpNode::precedenceJson(const temporary_terms_t &temporary_terms) const
 }
 
 int
-BinaryOpNode::cost(const map<pair<int, int>, temporary_terms_t> &temp_terms_map, bool is_matlab) const
+BinaryOpNode::cost(const map<pair<int, int>, unordered_set<expr_t>> &temp_terms_map, bool is_matlab) const
 {
   // For a temporary term, the cost is null
   for (const auto &it : temp_terms_map)
@@ -4269,7 +4269,7 @@ BinaryOpNode::cost(const map<pair<int, int>, temporary_terms_t> &temp_terms_map,
 }
 
 int
-BinaryOpNode::cost(const vector<vector<temporary_terms_t>> &blocks_temporary_terms, bool is_matlab) const
+BinaryOpNode::cost(const vector<vector<unordered_set<expr_t>>> &blocks_temporary_terms, bool is_matlab) const
 {
   // For a temporary term, the cost is null
   for (const auto &blk_tt : blocks_temporary_terms)
@@ -4344,8 +4344,8 @@ BinaryOpNode::cost(int cost, bool is_matlab) const
 
 void
 BinaryOpNode::computeTemporaryTerms(const pair<int, int> &derivOrder,
-                                    map<pair<int, int>, temporary_terms_t> &temp_terms_map,
-                                    map<expr_t, pair<int, pair<int, int>>> &reference_count,
+                                    map<pair<int, int>, unordered_set<expr_t>> &temp_terms_map,
+                                    unordered_map<expr_t, pair<int, pair<int, int>>> &reference_count,
                                     bool is_matlab) const
 {
   expr_t this2 = const_cast<BinaryOpNode *>(this);
@@ -4372,8 +4372,8 @@ BinaryOpNode::computeTemporaryTerms(const pair<int, int> &derivOrder,
 }
 
 void
-BinaryOpNode::computeBlockTemporaryTerms(int blk, int eq, vector<vector<temporary_terms_t>> &blocks_temporary_terms,
-                                         map<expr_t, tuple<int, int, int>> &reference_count) const
+BinaryOpNode::computeBlockTemporaryTerms(int blk, int eq, vector<vector<unordered_set<expr_t>>> &blocks_temporary_terms,
+                                         unordered_map<expr_t, tuple<int, int, int>> &reference_count) const
 {
   expr_t this2 = const_cast<BinaryOpNode *>(this);
   if (auto it = reference_count.find(this2);
@@ -6009,7 +6009,7 @@ TrinaryOpNode::precedence([[maybe_unused]] ExprNodeOutputType output_type,
 }
 
 int
-TrinaryOpNode::cost(const map<pair<int, int>, temporary_terms_t> &temp_terms_map, bool is_matlab) const
+TrinaryOpNode::cost(const map<pair<int, int>, unordered_set<expr_t>> &temp_terms_map, bool is_matlab) const
 {
   // For a temporary term, the cost is null
   for (const auto &it : temp_terms_map)
@@ -6024,7 +6024,7 @@ TrinaryOpNode::cost(const map<pair<int, int>, temporary_terms_t> &temp_terms_map
 }
 
 int
-TrinaryOpNode::cost(const vector<vector<temporary_terms_t>> &blocks_temporary_terms, bool is_matlab) const
+TrinaryOpNode::cost(const vector<vector<unordered_set<expr_t>>> &blocks_temporary_terms, bool is_matlab) const
 {
   // For a temporary term, the cost is null
   for (const auto &blk_tt : blocks_temporary_terms)
@@ -6064,8 +6064,8 @@ TrinaryOpNode::cost(int cost, bool is_matlab) const
 
 void
 TrinaryOpNode::computeTemporaryTerms(const pair<int, int> &derivOrder,
-                                     map<pair<int, int>, temporary_terms_t> &temp_terms_map,
-                                     map<expr_t, pair<int, pair<int, int>>> &reference_count,
+                                     map<pair<int, int>, unordered_set<expr_t>> &temp_terms_map,
+                                     unordered_map<expr_t, pair<int, pair<int, int>>> &reference_count,
                                      bool is_matlab) const
 {
   expr_t this2 = const_cast<TrinaryOpNode *>(this);
@@ -6091,8 +6091,8 @@ TrinaryOpNode::computeTemporaryTerms(const pair<int, int> &derivOrder,
 }
 
 void
-TrinaryOpNode::computeBlockTemporaryTerms(int blk, int eq, vector<vector<temporary_terms_t>> &blocks_temporary_terms,
-                                          map<expr_t, tuple<int, int, int>> &reference_count) const
+TrinaryOpNode::computeBlockTemporaryTerms(int blk, int eq, vector<vector<unordered_set<expr_t>>> &blocks_temporary_terms,
+                                          unordered_map<expr_t, tuple<int, int, int>> &reference_count) const
 {
   expr_t this2 = const_cast<TrinaryOpNode *>(this);
   if (auto it = reference_count.find(this2);
@@ -7010,8 +7010,8 @@ AbstractExternalFunctionNode::getIndxInTefTerms(int the_symb_id, const deriv_nod
 
 void
 AbstractExternalFunctionNode::computeTemporaryTerms(const pair<int, int> &derivOrder,
-                                                    map<pair<int, int>, temporary_terms_t> &temp_terms_map,
-                                                    [[maybe_unused]] map<expr_t, pair<int, pair<int, int>>> &reference_count,
+                                                    map<pair<int, int>, unordered_set<expr_t>> &temp_terms_map,
+                                                    [[maybe_unused]] unordered_map<expr_t, pair<int, pair<int, int>>> &reference_count,
                                                     [[maybe_unused]] bool is_matlab) const
 {
   /* All external function nodes are declared as temporary terms.
@@ -7037,8 +7037,8 @@ AbstractExternalFunctionNode::computeTemporaryTerms(const pair<int, int> &derivO
 }
 
 void
-AbstractExternalFunctionNode::computeBlockTemporaryTerms(int blk, int eq, vector<vector<temporary_terms_t>> &blocks_temporary_terms,
-                                                         [[maybe_unused]] map<expr_t, tuple<int, int, int>> &reference_count) const
+AbstractExternalFunctionNode::computeBlockTemporaryTerms(int blk, int eq, vector<vector<unordered_set<expr_t>>> &blocks_temporary_terms,
+                                                         [[maybe_unused]] unordered_map<expr_t, tuple<int, int, int>> &reference_count) const
 {
   // See comments in computeTemporaryTerms() for the logic
   expr_t this2 = const_cast<AbstractExternalFunctionNode *>(this);
@@ -8206,8 +8206,8 @@ SubModelNode::SubModelNode(DataTree &datatree_arg,
 
 void
 SubModelNode::computeTemporaryTerms([[maybe_unused]] const pair<int, int> &derivOrder,
-                                    [[maybe_unused]] map<pair<int, int>, temporary_terms_t> &temp_terms_map,
-                                    [[maybe_unused]] map<expr_t, pair<int, pair<int, int>>> &reference_count,
+                                    [[maybe_unused]] map<pair<int, int>, unordered_set<expr_t>> &temp_terms_map,
+                                    [[maybe_unused]] unordered_map<expr_t, pair<int, pair<int, int>>> &reference_count,
                                     [[maybe_unused]] bool is_matlab) const
 {
   cerr << "SubModelNode::computeTemporaryTerms not implemented." << endl;
@@ -8216,8 +8216,8 @@ SubModelNode::computeTemporaryTerms([[maybe_unused]] const pair<int, int> &deriv
 
 void
 SubModelNode::computeBlockTemporaryTerms([[maybe_unused]] int blk, [[maybe_unused]] int eq,
-                                         [[maybe_unused]] vector<vector<temporary_terms_t>> &blocks_temporary_terms,
-                                         [[maybe_unused]] map<expr_t, tuple<int, int, int>> &reference_count) const
+                                         [[maybe_unused]] vector<vector<unordered_set<expr_t>>> &blocks_temporary_terms,
+                                         [[maybe_unused]] unordered_map<expr_t, tuple<int, int, int>> &reference_count) const
 {
   cerr << "SubModelNode::computeBlocksTemporaryTerms not implemented." << endl;
   exit(EXIT_FAILURE);
diff --git a/src/ExprNode.hh b/src/ExprNode.hh
index 31025900..83339ea9 100644
--- a/src/ExprNode.hh
+++ b/src/ExprNode.hh
@@ -28,6 +28,7 @@
 #include <optional>
 #include <utility>
 #include <unordered_map>
+#include <unordered_set>
 
 using namespace std;
 
@@ -51,7 +52,7 @@ struct ExprNodeLess;
   see the definition of ExprNodeLess */
 using temporary_terms_t = set<expr_t, ExprNodeLess>;
 /*! Keeps track of array indices of temporary_terms for writing */
-using temporary_terms_idxs_t = map<expr_t, int>;
+using temporary_terms_idxs_t = unordered_map<expr_t, int>;
 
 //! Type for evaluation contexts
 /*! The key is a symbol id. Lags are assumed to be null */
@@ -291,8 +292,8 @@ protected:
   //! Cost of computing current node
   /*! Nodes included in temporary_terms are considered having a null cost */
   virtual int cost(int cost, bool is_matlab) const;
-  virtual int cost(const vector<vector<temporary_terms_t>> &blocks_temporary_terms, bool is_matlab) const;
-  virtual int cost(const map<pair<int, int>, temporary_terms_t> &temp_terms_map, bool is_matlab) const;
+  virtual int cost(const vector<vector<unordered_set<expr_t>>> &blocks_temporary_terms, bool is_matlab) const;
+  virtual int cost(const map<pair<int, int>, unordered_set<expr_t>> &temp_terms_map, bool is_matlab) const;
 
   //! For creating equation cross references
   struct EquationInfo
@@ -379,10 +380,13 @@ public:
     A node will be marked as a temporary term if it is referenced at least
     two times (i.e. has at least two parents), and has a computing cost
     (multiplied by reference count) greater to datatree.min_cost
+
+    NB: the use of std::unordered_map instead of std::map for caching
+    purposes improves performance on very large models (⩾5000 equations)
   */
   virtual void computeTemporaryTerms(const pair<int, int> &derivOrder,
-                                     map<pair<int, int>, temporary_terms_t> &temp_terms_map,
-                                     map<expr_t, pair<int, pair<int, int>>> &reference_count,
+                                     map<pair<int, int>, unordered_set<expr_t>> &temp_terms_map,
+                                     unordered_map<expr_t, pair<int, pair<int, int>>> &reference_count,
                                      bool is_matlab) const;
 
   //! Compute temporary terms in this expression for block decomposed model
@@ -397,10 +401,13 @@ public:
     expression first appears, third integer is the equation number within the block)
 
     Same rules as computeTemporaryTerms() for computing cost.
+
+    NB: the use of std::unordered_{set,map} instead of std::{set,map} for caching
+    and output improves performance on very large models (⩾5000 equations)
   */
   virtual void computeBlockTemporaryTerms(int blk, int eq,
-                                          vector<vector<temporary_terms_t>> &blocks_temporary_terms,
-                                          map<expr_t, tuple<int, int, int>> &reference_count) const;
+                                          vector<vector<unordered_set<expr_t>>> &blocks_temporary_terms,
+                                          unordered_map<expr_t, tuple<int, int, int>> &reference_count) const;
 
   //! Writes output of node, using a Txxx notation for nodes in temporary_terms, and specifiying the set of already written external functions
   /*!
@@ -1024,18 +1031,18 @@ private:
   expr_t computeDerivative(int deriv_id) override;
   expr_t computeChainRuleDerivative(int deriv_id, const map<int, BinaryOpNode *> &recursive_variables, unordered_map<expr_t, set<int>> &non_null_chain_rule_derivatives, unordered_map<expr_t, map<int, expr_t>> &cache) override;
   int cost(int cost, bool is_matlab) const override;
-  int cost(const vector<vector<temporary_terms_t>> &blocks_temporary_terms, bool is_matlab) const override;
-  int cost(const map<pair<int, int>, temporary_terms_t> &temp_terms_map, bool is_matlab) const override;
+  int cost(const vector<vector<unordered_set<expr_t>>> &blocks_temporary_terms, bool is_matlab) const override;
+  int cost(const map<pair<int, int>, unordered_set<expr_t>> &temp_terms_map, bool is_matlab) const override;
   //! Returns the derivative of this node if darg is the derivative of the argument
   expr_t composeDerivatives(expr_t darg, int deriv_id);
 public:
   UnaryOpNode(DataTree &datatree_arg, int idx_arg, UnaryOpcode op_code_arg, const expr_t arg_arg, int expectation_information_set_arg, int param1_symb_id_arg, int param2_symb_id_arg, string adl_param_name_arg, vector<int> adl_lags_arg);
   void computeTemporaryTerms(const pair<int, int> &derivOrder,
-                             map<pair<int, int>, temporary_terms_t> &temp_terms_map,
-                             map<expr_t, pair<int, pair<int, int>>> &reference_count,
+                             map<pair<int, int>, unordered_set<expr_t>> &temp_terms_map,
+                             unordered_map<expr_t, pair<int, pair<int, int>>> &reference_count,
                              bool is_matlab) const override;
-  void computeBlockTemporaryTerms(int blk, int eq, vector<vector<temporary_terms_t>> &blocks_temporary_terms,
-                                  map<expr_t, tuple<int, int, int>> &reference_count) const override;
+  void computeBlockTemporaryTerms(int blk, int eq, vector<vector<unordered_set<expr_t>>> &blocks_temporary_terms,
+                                  unordered_map<expr_t, tuple<int, int, int>> &reference_count) const override;
   void writeOutput(ostream &output, ExprNodeOutputType output_type, const temporary_terms_t &temporary_terms, const temporary_terms_idxs_t &temporary_terms_idxs, const deriv_node_temp_terms_t &tef_terms) const override;
   void writeJsonAST(ostream &output) const override;
   void writeJsonOutput(ostream &output, const temporary_terms_t &temporary_terms, const deriv_node_temp_terms_t &tef_terms, bool isdynamic) const override;
@@ -1124,8 +1131,8 @@ private:
   expr_t computeDerivative(int deriv_id) override;
   expr_t computeChainRuleDerivative(int deriv_id, const map<int, BinaryOpNode *> &recursive_variables, unordered_map<expr_t, set<int>> &non_null_chain_rule_derivatives, unordered_map<expr_t, map<int, expr_t>> &cache) override;
   int cost(int cost, bool is_matlab) const override;
-  int cost(const vector<vector<temporary_terms_t>> &blocks_temporary_terms, bool is_matlab) const override;
-  int cost(const map<pair<int, int>, temporary_terms_t> &temp_terms_map, bool is_matlab) const override;
+  int cost(const vector<vector<unordered_set<expr_t>>> &blocks_temporary_terms, bool is_matlab) const override;
+  int cost(const map<pair<int, int>, unordered_set<expr_t>> &temp_terms_map, bool is_matlab) const override;
   //! Returns the derivative of this node if darg1 and darg2 are the derivatives of the arguments
   expr_t composeDerivatives(expr_t darg1, expr_t darg2);
   // Returns the node obtained by applying a transformation recursively on the arguments (in same datatree)
@@ -1143,11 +1150,11 @@ public:
   int precedenceJson(const temporary_terms_t &temporary_terms) const override;
   int precedence(ExprNodeOutputType output_type, const temporary_terms_t &temporary_terms) const override;
   void computeTemporaryTerms(const pair<int, int> &derivOrder,
-                             map<pair<int, int>, temporary_terms_t> &temp_terms_map,
-                             map<expr_t, pair<int, pair<int, int>>> &reference_count,
+                             map<pair<int, int>, unordered_set<expr_t>> &temp_terms_map,
+                             unordered_map<expr_t, pair<int, pair<int, int>>> &reference_count,
                              bool is_matlab) const override;
-  void computeBlockTemporaryTerms(int blk, int eq, vector<vector<temporary_terms_t>> &blocks_temporary_terms,
-                                  map<expr_t, tuple<int, int, int>> &reference_count) const override;
+  void computeBlockTemporaryTerms(int blk, int eq, vector<vector<unordered_set<expr_t>>> &blocks_temporary_terms,
+                                  unordered_map<expr_t, tuple<int, int, int>> &reference_count) const override;
   void writeOutput(ostream &output, ExprNodeOutputType output_type, const temporary_terms_t &temporary_terms, const temporary_terms_idxs_t &temporary_terms_idxs, const deriv_node_temp_terms_t &tef_terms) const override;
   void writeJsonAST(ostream &output) const override;
   void writeJsonOutput(ostream &output, const temporary_terms_t &temporary_terms, const deriv_node_temp_terms_t &tef_terms, bool isdynamic) const override;
@@ -1274,8 +1281,8 @@ private:
   expr_t computeDerivative(int deriv_id) override;
   expr_t computeChainRuleDerivative(int deriv_id, const map<int, BinaryOpNode *> &recursive_variables, unordered_map<expr_t, set<int>> &non_null_chain_rule_derivatives, unordered_map<expr_t, map<int, expr_t>> &cache) override;
   int cost(int cost, bool is_matlab) const override;
-  int cost(const vector<vector<temporary_terms_t>> &blocks_temporary_terms, bool is_matlab) const override;
-  int cost(const map<pair<int, int>, temporary_terms_t> &temp_terms_map, bool is_matlab) const override;
+  int cost(const vector<vector<unordered_set<expr_t>>> &blocks_temporary_terms, bool is_matlab) const override;
+  int cost(const map<pair<int, int>, unordered_set<expr_t>> &temp_terms_map, bool is_matlab) const override;
   //! Returns the derivative of this node if darg1, darg2 and darg3 are the derivatives of the arguments
   expr_t composeDerivatives(expr_t darg1, expr_t darg2, expr_t darg3);
   // Returns the node obtained by applying a transformation recursively on the arguments (in same datatree)
@@ -1293,11 +1300,11 @@ public:
                 TrinaryOpcode op_code_arg, const expr_t arg2_arg, const expr_t arg3_arg);
   int precedence(ExprNodeOutputType output_type, const temporary_terms_t &temporary_terms) const override;
   void computeTemporaryTerms(const pair<int, int> &derivOrder,
-                             map<pair<int, int>, temporary_terms_t> &temp_terms_map,
-                             map<expr_t, pair<int, pair<int, int>>> &reference_count,
+                             map<pair<int, int>, unordered_set<expr_t>> &temp_terms_map,
+                             unordered_map<expr_t, pair<int, pair<int, int>>> &reference_count,
                              bool is_matlab) const override;
-  void computeBlockTemporaryTerms(int blk, int eq, vector<vector<temporary_terms_t>> &blocks_temporary_terms,
-                                  map<expr_t, tuple<int, int, int>> &reference_count) const override;
+  void computeBlockTemporaryTerms(int blk, int eq, vector<vector<unordered_set<expr_t>>> &blocks_temporary_terms,
+                                  unordered_map<expr_t, tuple<int, int, int>> &reference_count) const override;
   void writeOutput(ostream &output, ExprNodeOutputType output_type, const temporary_terms_t &temporary_terms, const temporary_terms_idxs_t &temporary_terms_idxs, const deriv_node_temp_terms_t &tef_terms) const override;
   void writeJsonAST(ostream &output) const override;
   void writeJsonOutput(ostream &output, const temporary_terms_t &temporary_terms, const deriv_node_temp_terms_t &tef_terms, bool isdynamic) const override;
@@ -1418,11 +1425,11 @@ public:
   AbstractExternalFunctionNode(DataTree &datatree_arg, int idx_arg, int symb_id_arg,
                                vector<expr_t> arguments_arg);
   void computeTemporaryTerms(const pair<int, int> &derivOrder,
-                             map<pair<int, int>, temporary_terms_t> &temp_terms_map,
-                             map<expr_t, pair<int, pair<int, int>>> &reference_count,
+                             map<pair<int, int>, unordered_set<expr_t>> &temp_terms_map,
+                             unordered_map<expr_t, pair<int, pair<int, int>>> &reference_count,
                              bool is_matlab) const override;
-  void computeBlockTemporaryTerms(int blk, int eq, vector<vector<temporary_terms_t>> &blocks_temporary_terms,
-                                  map<expr_t, tuple<int, int, int>> &reference_count) const override;
+  void computeBlockTemporaryTerms(int blk, int eq, vector<vector<unordered_set<expr_t>>> &blocks_temporary_terms,
+                                  unordered_map<expr_t, tuple<int, int, int>> &reference_count) const override;
   void writeOutput(ostream &output, ExprNodeOutputType output_type, const temporary_terms_t &temporary_terms, const temporary_terms_idxs_t &temporary_terms_idxs, const deriv_node_temp_terms_t &tef_terms) const override = 0;
   void writeJsonAST(ostream &output) const override = 0;
   void writeJsonOutput(ostream &output, const temporary_terms_t &temporary_terms, const deriv_node_temp_terms_t &tef_terms, bool isdynamic = true) const override = 0;
@@ -1609,11 +1616,11 @@ public:
   const string model_name;
   SubModelNode(DataTree &datatree_arg, int idx_arg, string model_name_arg);
   void computeTemporaryTerms(const pair<int, int> &derivOrder,
-                             map<pair<int, int>, temporary_terms_t> &temp_terms_map,
-                             map<expr_t, pair<int, pair<int, int>>> &reference_count,
+                             map<pair<int, int>, unordered_set<expr_t>> &temp_terms_map,
+                             unordered_map<expr_t, pair<int, pair<int, int>>> &reference_count,
                              bool is_matlab) const override;
-  void computeBlockTemporaryTerms(int blk, int eq, vector<vector<temporary_terms_t>> &blocks_temporary_terms,
-                                  map<expr_t, tuple<int, int, int>> &reference_count) const override;
+  void computeBlockTemporaryTerms(int blk, int eq, vector<vector<unordered_set<expr_t>>> &blocks_temporary_terms,
+                                  unordered_map<expr_t, tuple<int, int, int>> &reference_count) const override;
   expr_t toStatic(DataTree &static_datatree) const override;
   expr_t computeDerivative(int deriv_id) override;
   int maxEndoLead() const override;
diff --git a/src/ModelTree.cc b/src/ModelTree.cc
index 408952eb..5ee82268 100644
--- a/src/ModelTree.cc
+++ b/src/ModelTree.cc
@@ -921,8 +921,8 @@ ModelTree::computeTemporaryTerms(bool is_matlab, bool no_tmp_terms)
   }());
 
   // Compute the temporary terms in equations and derivatives
-  map<pair<int, int>, temporary_terms_t> temp_terms_map;
-  map<expr_t, pair<int, pair<int, int>>> reference_count;
+  map<pair<int, int>, unordered_set<expr_t>> temp_terms_map;
+  unordered_map<expr_t, pair<int, pair<int, int>>> reference_count;
 
   for (auto &equation : equations)
     equation->computeTemporaryTerms({ 0, 0 },
@@ -949,7 +949,8 @@ ModelTree::computeTemporaryTerms(bool is_matlab, bool no_tmp_terms)
   temporary_terms_derivatives.clear();
   temporary_terms_derivatives.resize(derivatives.size());
   for (int order = 0; order < static_cast<int>(derivatives.size()); order++)
-    temporary_terms_derivatives[order] = move(temp_terms_map[{ order, 0 }]);
+    copy(temp_terms_map[{ order, 0 }].begin(), temp_terms_map[{ order, 0 }].end(),
+         inserter(temporary_terms_derivatives.at(order), temporary_terms_derivatives.at(order).begin()));
 
   // Compute indices in MATLAB/Julia vector
   for (int order {0}, idx {0}; order < static_cast<int>(derivatives.size()); order++)
@@ -961,12 +962,12 @@ void
 ModelTree::computeBlockTemporaryTerms(bool no_tmp_terms)
 {
   int nb_blocks = blocks.size();
-  blocks_temporary_terms.resize(nb_blocks);
 
-  map<expr_t, tuple<int, int, int>> reference_count;
+  unordered_map<expr_t, tuple<int, int, int>> reference_count;
+  vector<vector<unordered_set<expr_t>>> temp_terms(nb_blocks);
   for (int blk = 0; blk < nb_blocks; blk++)
     {
-      blocks_temporary_terms[blk].resize(blocks[blk].size + 1);
+      temp_terms[blk].resize(blocks[blk].size + 1);
       for (int eq = 0; eq < blocks[blk].size; eq++)
         {
           /* It is important to compute temporary terms of the renormalized
@@ -979,22 +980,30 @@ ModelTree::computeBlockTemporaryTerms(bool no_tmp_terms)
                || blocks[blk].simulation_type == BlockSimulationType::evaluateForward
                || eq < blocks[blk].getRecursiveSize())
               && isBlockEquationRenormalized(blk, eq))
-            getBlockEquationRenormalizedExpr(blk, eq)->computeBlockTemporaryTerms(blk, eq, blocks_temporary_terms, reference_count);
+            getBlockEquationRenormalizedExpr(blk, eq)->computeBlockTemporaryTerms(blk, eq, temp_terms, reference_count);
           else
-            getBlockEquationExpr(blk, eq)->computeBlockTemporaryTerms(blk, eq, blocks_temporary_terms, reference_count);
+            getBlockEquationExpr(blk, eq)->computeBlockTemporaryTerms(blk, eq, temp_terms, reference_count);
         }
       for (const auto &[ignore, d] : blocks_derivatives[blk])
-        d->computeBlockTemporaryTerms(blk, blocks[blk].size, blocks_temporary_terms, reference_count);
+        d->computeBlockTemporaryTerms(blk, blocks[blk].size, temp_terms, reference_count);
     }
 
   /* If the user has specified the notmpterms option, clear all temporary
      terms, except those that correspond to external functions (since they are
      not optional) */
   if (no_tmp_terms)
-    for (auto &it : blocks_temporary_terms)
+    for (auto &it : temp_terms)
       for (auto &it2 : it)
         erase_if(it2, [](expr_t e) { return !dynamic_cast<AbstractExternalFunctionNode *>(e); });
 
+  blocks_temporary_terms.resize(nb_blocks);
+  for (int blk {0}; blk < nb_blocks; blk++)
+    {
+      blocks_temporary_terms.at(blk).resize(temp_terms.at(blk).size());
+      for (size_t i {0}; i < temp_terms.at(blk).size(); i++)
+        copy(temp_terms.at(blk).at(i).begin(), temp_terms.at(blk).at(i).end(), inserter(blocks_temporary_terms.at(blk).at(i), blocks_temporary_terms.at(blk).at(i).begin()));
+    }
+
   // Compute indices in the temporary terms vector
   blocks_temporary_terms_idxs.clear();
   for (int idx{0};
@@ -1474,15 +1483,18 @@ ModelTree::computeParamsDerivatives(int paramsDerivsOrder)
 void
 ModelTree::computeParamsDerivativesTemporaryTerms()
 {
-  map<expr_t, pair<int, pair<int, int>>> reference_count;
+  unordered_map<expr_t, pair<int, pair<int, int>>> reference_count;
 
   /* The temp terms should be constructed in the same order as the for loops in
      {Static,Dynamic}Model::write{Json,}ParamsDerivativesFile() */
-  params_derivs_temporary_terms.clear();
+  map<pair<int, int>, unordered_set<expr_t>> temp_terms_map;
   for (const auto &[order, derivs] : params_derivatives)
     for (const auto &[indices, d] : derivs)
-      d->computeTemporaryTerms(order, params_derivs_temporary_terms,
-                               reference_count, true);
+      d->computeTemporaryTerms(order, temp_terms_map, reference_count, true);
+
+  for (const auto &[order, tts] : temp_terms_map)
+    copy(temp_terms_map[order].begin(), temp_terms_map[order].end(),
+         inserter(params_derivs_temporary_terms[order], params_derivs_temporary_terms[order].begin()));
 
   for (int idx {0};
        const auto &[order, tts] : params_derivs_temporary_terms)
diff --git a/src/StaticModel.cc b/src/StaticModel.cc
index 94fad6e2..7ff90b85 100644
--- a/src/StaticModel.cc
+++ b/src/StaticModel.cc
@@ -833,8 +833,8 @@ StaticModel::computeRamseyMultipliersDerivatives(int ramsey_orig_endo_nbr, bool
         ramsey_multipliers_derivatives.try_emplace({ eq, mult }, d);
 
   // Compute the temporary terms
-  map<pair<int, int>, temporary_terms_t> temp_terms_map;
-  map<expr_t, pair<int, pair<int, int>>> reference_count;
+  map<pair<int, int>, unordered_set<expr_t>> temp_terms_map;
+  unordered_map<expr_t, pair<int, pair<int, int>>> reference_count;
   for (const auto &[row_col, d] : ramsey_multipliers_derivatives)
     d->computeTemporaryTerms({ 1, 0 }, temp_terms_map, reference_count, is_matlab);
   /* If the user has specified the notmpterms option, clear all temporary
@@ -844,7 +844,8 @@ StaticModel::computeRamseyMultipliersDerivatives(int ramsey_orig_endo_nbr, bool
     for (auto &it : temp_terms_map)
       erase_if(it.second,
                [](expr_t e) { return !dynamic_cast<AbstractExternalFunctionNode *>(e); });
-  ramsey_multipliers_derivatives_temporary_terms = move(temp_terms_map[{ 1, 0 }]);
+  copy(temp_terms_map[{1, 0}].begin(), temp_terms_map[{1, 0}].end(),
+       inserter(ramsey_multipliers_derivatives_temporary_terms, ramsey_multipliers_derivatives_temporary_terms.begin()));
   for (int idx {0};
        auto it : ramsey_multipliers_derivatives_temporary_terms)
     ramsey_multipliers_derivatives_temporary_terms_idxs[it] = idx++;
-- 
GitLab