diff --git a/src/DynamicModel.cc b/src/DynamicModel.cc
index f06f80940a047d12ab0b7c9eeac0fc3870512e17..bb25c2d26bff51fae1787ae528ede5661dc3d973 100644
--- a/src/DynamicModel.cc
+++ b/src/DynamicModel.cc
@@ -5391,7 +5391,7 @@ DynamicModel::substituteUnaryOps(StaticModel &static_model, set<string> &var_mod
 void
 DynamicModel::substituteUnaryOps(StaticModel &static_model, vector<int> &eqnumbers)
 {
-  diff_table_t nodes;
+  unary_op_aux_var_table_t nodes;
 
   // Find matching unary ops that may be outside of diffs (i.e., those with different lags)
   set<int> used_local_vars;
diff --git a/src/ExprNode.cc b/src/ExprNode.cc
index 4e98111f68b51c815a8db36820cd42846090055c..5f628e4ad76934caf3fa3ed846656847c8c5945c 100644
--- a/src/ExprNode.cc
+++ b/src/ExprNode.cc
@@ -553,7 +553,7 @@ NumConstNode::findDiffNodes(DataTree &static_datatree, diff_table_t &diff_table)
 }
 
 void
-NumConstNode::findUnaryOpNodesForAuxVarCreation(DataTree &static_datatree, diff_table_t &nodes) const
+NumConstNode::findUnaryOpNodesForAuxVarCreation(DataTree &static_datatree, unary_op_aux_var_table_t &nodes) const
 {
 }
 
@@ -564,7 +564,7 @@ NumConstNode::substituteDiff(DataTree &static_datatree, diff_table_t &diff_table
 }
 
 expr_t
-NumConstNode::substituteUnaryOpNodes(DataTree &static_datatree, diff_table_t &nodes, subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const
+NumConstNode::substituteUnaryOpNodes(DataTree &static_datatree, unary_op_aux_var_table_t &nodes, subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const
 {
   return const_cast<NumConstNode *>(this);
 }
@@ -675,6 +675,19 @@ NumConstNode::substituteStaticAuxiliaryVariable() const
   return const_cast<NumConstNode *>(this);
 }
 
+bool
+NumConstNode::compareExprConstLag(expr_t expr, int const_lag) const
+{
+  NumConstNode *ncn = dynamic_cast<NumConstNode *>(expr);
+  if (ncn == nullptr)
+    return false;
+
+  if (datatree.num_constants.getDouble(id) == datatree.num_constants.getDouble(ncn->get_id()))
+    return true;
+
+  return false;
+}
+
 VariableNode::VariableNode(DataTree &datatree_arg, int symb_id_arg, int lag_arg) :
   ExprNode(datatree_arg),
   symb_id(symb_id_arg),
@@ -1440,7 +1453,7 @@ VariableNode::findDiffNodes(DataTree &static_datatree, diff_table_t &diff_table)
 }
 
 void
-VariableNode::findUnaryOpNodesForAuxVarCreation(DataTree &static_datatree, diff_table_t &nodes) const
+VariableNode::findUnaryOpNodesForAuxVarCreation(DataTree &static_datatree, unary_op_aux_var_table_t &nodes) const
 {
 }
 
@@ -1452,7 +1465,7 @@ VariableNode::substituteDiff(DataTree &static_datatree, diff_table_t &diff_table
 }
 
 expr_t
-VariableNode::substituteUnaryOpNodes(DataTree &static_datatree, diff_table_t &nodes, subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const
+VariableNode::substituteUnaryOpNodes(DataTree &static_datatree, unary_op_aux_var_table_t &nodes, subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const
 {
   return const_cast<VariableNode *>(this);
 }
@@ -1845,6 +1858,20 @@ VariableNode::getEndosAndMaxLags(map<string, int> &model_endos_and_lags) const
       model_endos_and_lags[varname] = lag;
 }
 
+bool
+VariableNode::compareExprConstLag(expr_t expr, int const_lag) const
+{
+  VariableNode *vn = dynamic_cast<VariableNode *>(expr);
+  if (vn == nullptr)
+    return false;
+
+  if (symb_id == vn->get_symb_id())
+    if (lag - vn->get_lag() == const_lag)
+      return true;
+
+  return false;
+}
+
 UnaryOpNode::UnaryOpNode(DataTree &datatree_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) :
   ExprNode(datatree_arg),
   arg(arg_arg),
@@ -3063,27 +3090,30 @@ UnaryOpNode::createAuxVarForUnaryOpNode() const
 }
 
 void
-UnaryOpNode::findUnaryOpNodesForAuxVarCreation(DataTree &static_datatree, diff_table_t &nodes) const
+UnaryOpNode::findUnaryOpNodesForAuxVarCreation(DataTree &static_datatree, unary_op_aux_var_table_t &nodes) const
 {
   arg->findUnaryOpNodesForAuxVarCreation(static_datatree, nodes);
-
   if (!this->createAuxVarForUnaryOpNode())
     return;
 
-  expr_t sthis = this->toStatic(static_datatree);
-  int arg_max_lag = -arg->maxLag();
-  // TODO: implement recursive expression comparison, ensuring that the difference in the lags is constant across nodes
-  auto it = nodes.find(sthis);
-  if (it != nodes.end())
-    {
-      for (map<int, expr_t>::const_iterator it1 = it->second.begin();
-           it1 != it->second.end(); it1++)
-        if (arg == it1->second)
-          return;
-      it->second[arg_max_lag] = const_cast<UnaryOpNode *>(this);
-    }
-  else
-    nodes[sthis][arg_max_lag] = const_cast<UnaryOpNode *>(this);
+  if (nodes.find(const_cast<UnaryOpNode *>(this)) != nodes.end())
+    return;
+
+  int arg_max_lag = arg->maxLag();
+  for (auto & it : nodes)
+    if (this->compareExprConstLag(it.first, it.first->maxLag() - arg_max_lag))
+      {
+        it.second.insert(arg_max_lag);
+        if (it.first->maxLag() > arg_max_lag)
+          {
+            set<int> si = it.second;
+            nodes.erase(it.first);
+            nodes[const_cast<UnaryOpNode *>(this)] = si;
+          }
+        return;
+      }
+
+  nodes[const_cast<UnaryOpNode *>(this)] = set<int> {arg_max_lag};
 }
 
 void
@@ -3189,41 +3219,50 @@ UnaryOpNode::substituteDiff(DataTree &static_datatree, diff_table_t &diff_table,
 }
 
 expr_t
-UnaryOpNode::substituteUnaryOpNodes(DataTree &static_datatree, diff_table_t &nodes, subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const
+UnaryOpNode::substituteUnaryOpNodes(DataTree &static_datatree, unary_op_aux_var_table_t &nodes, subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const
 {
   subst_table_t::const_iterator sit = subst_table.find(this);
   if (sit != subst_table.end())
     return const_cast<VariableNode *>(sit->second);
 
-  auto *sthis = dynamic_cast<UnaryOpNode *>(this->toStatic(static_datatree));
-  auto it = nodes.find(sthis);
   expr_t argsubst = arg->substituteUnaryOpNodes(static_datatree, nodes, subst_table, neweqs);
-  if (it == nodes.end())
-    return buildSimilarUnaryOpNode(argsubst, datatree);
 
-  int base_aux_lag = 0;
-  VariableNode *aux_var = nullptr;
-  for (auto rit = it->second.rbegin(); rit != it->second.rend(); rit++)
-    if (rit == it->second.rbegin())
+  int arg_max_lag = this->maxLag();
+  UnaryOpNode *base_uon = nullptr;
+  set<int> base_lags;
+  for (auto & it : nodes)
+    if (this->compareExprConstLag(it.first, it.first->maxLag() - arg_max_lag))
       {
-        int symb_id;
-        auto *vn = dynamic_cast<VariableNode *>(argsubst);
-        if (vn == nullptr)
-            symb_id = datatree.symbol_table.addUnaryOpAuxiliaryVar(this->idx, dynamic_cast<UnaryOpNode *>(rit->second));
-        else
-            symb_id = datatree.symbol_table.addUnaryOpAuxiliaryVar(this->idx, dynamic_cast<UnaryOpNode *>(rit->second),
-                                                                   vn->get_symb_id(), vn->get_lag());
-        aux_var = datatree.AddVariable(symb_id, 0);
-        neweqs.push_back(dynamic_cast<BinaryOpNode *>(datatree.AddEqual(aux_var,
-                                                                        dynamic_cast<UnaryOpNode *>(rit->second))));
-        subst_table[rit->second] = dynamic_cast<VariableNode *>(aux_var);
-        base_aux_lag = rit->first;
+        base_uon = it.first;
+        base_lags = it.second;
+        break;
       }
-    else
-      subst_table[rit->second] = dynamic_cast<VariableNode *>(aux_var->decreaseLeadsLags(base_aux_lag - rit->first));
 
-  sit = subst_table.find(this);
-  return const_cast<VariableNode *>(sit->second);
+  if (base_uon == nullptr)
+    return buildSimilarUnaryOpNode(argsubst, datatree);
+
+  // distance between this lag and the base lag
+  int lag_distance = arg_max_lag - *(base_lags.begin());
+
+  // If Aux Var already exists for this type of node at firstlag
+  sit = subst_table.find(base_uon);
+  if (sit != subst_table.end())
+    return dynamic_cast<VariableNode *>(sit->second->decreaseLeadsLags(lag_distance));
+  else
+    {
+      int symb_id;
+      argsubst = base_uon->get_arg()->substituteUnaryOpNodes(static_datatree, nodes, subst_table, neweqs);
+      auto *vn = dynamic_cast<VariableNode *>(argsubst);
+      if (vn == nullptr)
+        symb_id = datatree.symbol_table.addUnaryOpAuxiliaryVar(this->idx, const_cast<UnaryOpNode *>(this));
+      else
+        symb_id = datatree.symbol_table.addUnaryOpAuxiliaryVar(this->idx, const_cast<UnaryOpNode *>(this),
+                                                               vn->get_symb_id(), vn->get_lag());
+      VariableNode *aux_var = datatree.AddVariable(symb_id, 0);
+      neweqs.push_back(dynamic_cast<BinaryOpNode *>(datatree.AddEqual(aux_var, base_uon)));
+      subst_table[base_uon] = dynamic_cast<VariableNode *>(aux_var);
+      return dynamic_cast<VariableNode *>(aux_var->decreaseLeadsLags(lag_distance));
+    }
 }
 
 expr_t
@@ -3451,6 +3490,37 @@ UnaryOpNode::substituteStaticAuxiliaryVariable() const
     return buildSimilarUnaryOpNode(argsubst, datatree);
 }
 
+bool
+UnaryOpNode::compareExprConstLag(expr_t expr, int const_lag) const
+{
+  UnaryOpNode *uon = dynamic_cast<UnaryOpNode *>(expr);
+  if (uon == nullptr)
+    return false;
+
+  if (op_code != uon->get_op_code())
+    return false;
+
+  if (expectation_information_set != uon->get_expectation_information_set())
+    return false;
+
+  if (param1_symb_id != uon->get_param1_symb_id()
+      || param2_symb_id != uon->get_param2_symb_id())
+    return false;
+
+  if (adl_param_name.compare(uon->get_adl_param_name()) != 0)
+    return false;
+
+  vector<int> uon_adl_lags = uon->get_adl_lags();
+  if (adl_lags.size() != uon_adl_lags.size())
+    return false;
+
+  for (int i = 0; i < (int) adl_lags.size(); i++)
+    if (adl_lags.at(i) != uon_adl_lags.at(i))
+      return false;
+
+  return arg->compareExprConstLag(uon->get_arg(), const_lag);
+}
+
 BinaryOpNode::BinaryOpNode(DataTree &datatree_arg, const expr_t arg1_arg,
                            BinaryOpcode op_code_arg, const expr_t arg2_arg) :
   ExprNode(datatree_arg),
@@ -4886,7 +4956,7 @@ BinaryOpNode::substituteAdl() const
 }
 
 void
-BinaryOpNode::findUnaryOpNodesForAuxVarCreation(DataTree &static_datatree, diff_table_t &nodes) const
+BinaryOpNode::findUnaryOpNodesForAuxVarCreation(DataTree &static_datatree, unary_op_aux_var_table_t &nodes) const
 {
   arg1->findUnaryOpNodesForAuxVarCreation(static_datatree, nodes);
   arg2->findUnaryOpNodesForAuxVarCreation(static_datatree, nodes);
@@ -4909,7 +4979,7 @@ BinaryOpNode::substituteDiff(DataTree &static_datatree, diff_table_t &diff_table
 }
 
 expr_t
-BinaryOpNode::substituteUnaryOpNodes(DataTree &static_datatree, diff_table_t &nodes, subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const
+BinaryOpNode::substituteUnaryOpNodes(DataTree &static_datatree, unary_op_aux_var_table_t &nodes, subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const
 {
   expr_t arg1subst = arg1->substituteUnaryOpNodes(static_datatree, nodes, subst_table, neweqs);
   expr_t arg2subst = arg2->substituteUnaryOpNodes(static_datatree, nodes, subst_table, neweqs);
@@ -5114,6 +5184,28 @@ BinaryOpNode::substituteStaticAuxiliaryDefinition() const
   return buildSimilarBinaryOpNode(arg1, arg2subst, datatree);
 }
 
+bool
+BinaryOpNode::compareExprConstLag(expr_t expr, int const_lag) const
+{
+  BinaryOpNode *bon = dynamic_cast<BinaryOpNode *>(expr);
+  if (bon == nullptr)
+    return false;
+
+  if (op_code != bon->get_op_code())
+    // NB: DO NOT match when commutative operations are not in the same order
+    // e.g. this == -a + b && bon == b - a will not match
+    return false;
+
+  if (powerDerivOrder != bon->get_power_deriv_order())
+    return false;
+
+  if (adlparam.compare(bon->get_adlparam()) != 0)
+    return false;
+
+  return (arg1->compareExprConstLag(bon->get_arg1(), const_lag) && arg2->compareExprConstLag(bon->get_arg2(), const_lag))
+    || (arg2->compareExprConstLag(bon->get_arg1(), const_lag) && arg1->compareExprConstLag(bon->get_arg2(), const_lag));
+}
+
 TrinaryOpNode::TrinaryOpNode(DataTree &datatree_arg, const expr_t arg1_arg,
                              TrinaryOpcode op_code_arg, const expr_t arg2_arg, const expr_t arg3_arg) :
   ExprNode(datatree_arg),
@@ -5803,7 +5895,7 @@ TrinaryOpNode::findDiffNodes(DataTree &static_datatree, diff_table_t &diff_table
 }
 
 void
-TrinaryOpNode::findUnaryOpNodesForAuxVarCreation(DataTree &static_datatree, diff_table_t &nodes) const
+TrinaryOpNode::findUnaryOpNodesForAuxVarCreation(DataTree &static_datatree, unary_op_aux_var_table_t &nodes) const
 {
   arg1->findUnaryOpNodesForAuxVarCreation(static_datatree, nodes);
   arg2->findUnaryOpNodesForAuxVarCreation(static_datatree, nodes);
@@ -5821,7 +5913,7 @@ TrinaryOpNode::substituteDiff(DataTree &static_datatree, diff_table_t &diff_tabl
 }
 
 expr_t
-TrinaryOpNode::substituteUnaryOpNodes(DataTree &static_datatree, diff_table_t &nodes, subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const
+TrinaryOpNode::substituteUnaryOpNodes(DataTree &static_datatree, unary_op_aux_var_table_t &nodes, subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const
 {
   expr_t arg1subst = arg1->substituteUnaryOpNodes(static_datatree, nodes, subst_table, neweqs);
   expr_t arg2subst = arg2->substituteUnaryOpNodes(static_datatree, nodes, subst_table, neweqs);
@@ -5973,6 +6065,25 @@ TrinaryOpNode::substituteStaticAuxiliaryVariable() const
   return buildSimilarTrinaryOpNode(arg1subst, arg2subst, arg3subst, datatree);
 }
 
+bool
+TrinaryOpNode::compareExprConstLag(expr_t expr, int const_lag) const
+{
+  TrinaryOpNode *ton = dynamic_cast<TrinaryOpNode *>(expr);
+  if (ton == nullptr)
+    return false;
+
+  if (op_code != ton->get_op_code())
+    return false;
+
+  return
+    (arg1->compareExprConstLag(ton->get_arg1(), const_lag) && arg2->compareExprConstLag(ton->get_arg2(), const_lag) && arg3->compareExprConstLag(ton->get_arg3(), const_lag))
+    || (arg1->compareExprConstLag(ton->get_arg1(), const_lag) && arg3->compareExprConstLag(ton->get_arg2(), const_lag) && arg2->compareExprConstLag(ton->get_arg3(), const_lag))
+    || (arg2->compareExprConstLag(ton->get_arg1(), const_lag) && arg1->compareExprConstLag(ton->get_arg2(), const_lag) && arg3->compareExprConstLag(ton->get_arg3(), const_lag))
+    || (arg3->compareExprConstLag(ton->get_arg1(), const_lag) && arg1->compareExprConstLag(ton->get_arg2(), const_lag) && arg2->compareExprConstLag(ton->get_arg3(), const_lag))
+    || (arg2->compareExprConstLag(ton->get_arg1(), const_lag) && arg3->compareExprConstLag(ton->get_arg2(), const_lag) && arg1->compareExprConstLag(ton->get_arg3(), const_lag))
+    || (arg3->compareExprConstLag(ton->get_arg1(), const_lag) && arg2->compareExprConstLag(ton->get_arg2(), const_lag) && arg1->compareExprConstLag(ton->get_arg3(), const_lag));
+}
+
 AbstractExternalFunctionNode::AbstractExternalFunctionNode(DataTree &datatree_arg,
                                                            int symb_id_arg,
                                                            vector<expr_t> arguments_arg) :
@@ -6237,7 +6348,7 @@ AbstractExternalFunctionNode::findDiffNodes(DataTree &static_datatree, diff_tabl
 }
 
 void
-AbstractExternalFunctionNode::findUnaryOpNodesForAuxVarCreation(DataTree &static_datatree, diff_table_t &nodes) const
+AbstractExternalFunctionNode::findUnaryOpNodesForAuxVarCreation(DataTree &static_datatree, unary_op_aux_var_table_t &nodes) const
 {
   for (auto argument : arguments)
     argument->findUnaryOpNodesForAuxVarCreation(static_datatree, nodes);
@@ -6254,7 +6365,7 @@ AbstractExternalFunctionNode::substituteDiff(DataTree &static_datatree, diff_tab
 }
 
 expr_t
-AbstractExternalFunctionNode::substituteUnaryOpNodes(DataTree &static_datatree, diff_table_t &nodes, subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const
+AbstractExternalFunctionNode::substituteUnaryOpNodes(DataTree &static_datatree, unary_op_aux_var_table_t &nodes, subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const
 {
   vector<expr_t> arguments_subst;
   for (auto argument : arguments)
@@ -6538,6 +6649,20 @@ AbstractExternalFunctionNode::substituteStaticAuxiliaryVariable() const
   return buildSimilarExternalFunctionNode(arguments_subst, datatree);
 }
 
+bool
+AbstractExternalFunctionNode::compareExprConstLag(expr_t expr, int const_lag) const
+{
+  AbstractExternalFunctionNode *aefn = dynamic_cast<AbstractExternalFunctionNode *>(expr);
+  if (aefn == nullptr)
+    return false;
+
+  if (symb_id == aefn->get_symb_id())
+    // Come back to check in arguments
+    return true;
+
+  return false;
+}
+
 ExternalFunctionNode::ExternalFunctionNode(DataTree &datatree_arg,
                                            int symb_id_arg,
                                            const vector<expr_t> &arguments_arg) :
@@ -7837,7 +7962,7 @@ VarExpectationNode::findDiffNodes(DataTree &static_datatree, diff_table_t &diff_
 }
 
 void
-VarExpectationNode::findUnaryOpNodesForAuxVarCreation(DataTree &static_datatree, diff_table_t &nodes) const
+VarExpectationNode::findUnaryOpNodesForAuxVarCreation(DataTree &static_datatree, unary_op_aux_var_table_t &nodes) const
 {
 }
 
@@ -7849,7 +7974,7 @@ VarExpectationNode::substituteDiff(DataTree &static_datatree, diff_table_t &diff
 }
 
 expr_t
-VarExpectationNode::substituteUnaryOpNodes(DataTree &static_datatree, diff_table_t &nodes, subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const
+VarExpectationNode::substituteUnaryOpNodes(DataTree &static_datatree, unary_op_aux_var_table_t &nodes, subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const
 {
   return const_cast<VarExpectationNode *>(this);
 }
@@ -7979,6 +8104,28 @@ VarExpectationNode::writeJsonOutput(ostream &output,
          << ")";
 }
 
+bool
+VarExpectationNode::compareExprConstLag(expr_t expr, int const_lag) const
+{
+  VarExpectationNode *ven = dynamic_cast<VarExpectationNode *>(expr);
+  if (ven == nullptr)
+    return false;
+
+  if (symb_id != ven->get_symb_id())
+    return false;
+
+  if (forecast_horizon != ven->get_forecast_horizon())
+    return false;
+
+  if (model_name.compare(ven->get_model_name()) != 0)
+    return false;
+
+  if (yidx != ven->get_yidx())
+    return false;
+
+  return true;
+}
+
 PacExpectationNode::PacExpectationNode(DataTree &datatree_arg,
                                        string model_name_arg) :
   ExprNode(datatree_arg),
@@ -8292,7 +8439,7 @@ PacExpectationNode::findDiffNodes(DataTree &static_datatree, diff_table_t &diff_
 }
 
 void
-PacExpectationNode::findUnaryOpNodesForAuxVarCreation(DataTree &static_datatree, diff_table_t &nodes) const
+PacExpectationNode::findUnaryOpNodesForAuxVarCreation(DataTree &static_datatree, unary_op_aux_var_table_t &nodes) const
 {
 }
 
@@ -8304,7 +8451,7 @@ PacExpectationNode::substituteDiff(DataTree &static_datatree, diff_table_t &diff
 }
 
 expr_t
-PacExpectationNode::substituteUnaryOpNodes(DataTree &static_datatree, diff_table_t &nodes, subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const
+PacExpectationNode::substituteUnaryOpNodes(DataTree &static_datatree, unary_op_aux_var_table_t &nodes, subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const
 {
   return const_cast<PacExpectationNode *>(this);
 }
@@ -8511,3 +8658,14 @@ PacExpectationNode::substitutePacExpectation(map<const PacExpectationNode *, con
 
   return subExpr;
 }
+
+bool
+PacExpectationNode::compareExprConstLag(expr_t expr, int const_lag) const
+{
+  PacExpectationNode *pen = dynamic_cast<PacExpectationNode *>(expr);
+  if (pen == nullptr)
+    return false;
+
+  // Always return false for now
+  return false;
+}
diff --git a/src/ExprNode.hh b/src/ExprNode.hh
index 0498ad7e86f08bf81e8f3787e40d5258e1e773b3..ae31f44fd2e94c6c5d394581223f759dbca8cec1 100644
--- a/src/ExprNode.hh
+++ b/src/ExprNode.hh
@@ -66,6 +66,8 @@ using deriv_node_temp_terms_t = map<pair<int, vector<expr_t>>, int>;
 //! diff_table[static_expr_t][lag] -> [dynamic_expr_t]
 using diff_table_t = map<expr_t, map<int, expr_t>>;
 
+using unary_op_aux_var_table_t = map<UnaryOpNode *, set<int>>;
+
 //! Possible types of output when writing ExprNode(s)
 enum ExprNodeOutputType
   {
@@ -506,9 +508,9 @@ class ExprNode
 
       //! Substitute diff operator
       virtual void findDiffNodes(DataTree &static_datatree, diff_table_t &diff_table) const = 0;
-      virtual void findUnaryOpNodesForAuxVarCreation(DataTree &static_datatree, diff_table_t &nodes) const = 0;
+      virtual void findUnaryOpNodesForAuxVarCreation(DataTree &static_datatree, unary_op_aux_var_table_t &nodes) const = 0;
       virtual expr_t substituteDiff(DataTree &static_datatree, diff_table_t &diff_table, subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const = 0;
-      virtual expr_t substituteUnaryOpNodes(DataTree &static_datatree, diff_table_t &nodes, subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const = 0;
+      virtual expr_t substituteUnaryOpNodes(DataTree &static_datatree, unary_op_aux_var_table_t &nodes, subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const = 0;
 
       //! Substitute pac_expectation operator
       virtual expr_t substitutePacExpectation(map<const PacExpectationNode *, const BinaryOpNode *> &subst_table) = 0;
@@ -545,7 +547,12 @@ class ExprNode
 
       //! Fills map
       virtual void getEndosAndMaxLags(map<string, int> &model_endos_and_lags) const = 0;
-    };
+
+      //! compares two expr nodes to see if they match with a consistent difference in lag
+      //! e.g. comparing `log(x(-3) - y(-5))` and `log(-y(-4) + x(-2))` returns true
+      //! To see if two nodes are equal, simply call with const_lag == 0
+      virtual bool compareExprConstLag(expr_t expr, int const_lag) const = 0;
+};
 
 //! Object used to compare two nodes (using their indexes)
 /*! Note that in this ordering, a subexpression is always less than the
@@ -606,9 +613,9 @@ public:
   expr_t substituteExpectation(subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs, bool partial_information_model) const override;
   expr_t substituteAdl() const override;
   void findDiffNodes(DataTree &static_datatree, diff_table_t &diff_table) const override;
-  void findUnaryOpNodesForAuxVarCreation(DataTree &static_datatree, diff_table_t &nodes) const override;
+  void findUnaryOpNodesForAuxVarCreation(DataTree &static_datatree, unary_op_aux_var_table_t &nodes) const override;
   expr_t substituteDiff(DataTree &static_datatree, diff_table_t &diff_table, subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const override;
-  expr_t substituteUnaryOpNodes(DataTree &static_datatree, diff_table_t &nodes, subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const override;
+  expr_t substituteUnaryOpNodes(DataTree &static_datatree, unary_op_aux_var_table_t &nodes, subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const override;
   expr_t substitutePacExpectation(map<const PacExpectationNode *, const BinaryOpNode *> &subst_table) override;
   expr_t decreaseLeadsLagsPredeterminedVariables() const override;
   expr_t differentiateForwardVars(const vector<string> &subset, subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const override;
@@ -630,6 +637,7 @@ public:
   bool isVarModelReferenced(const string &model_info_name) const override;
   void getEndosAndMaxLags(map<string, int> &model_endos_and_lags) const override;
   expr_t substituteStaticAuxiliaryVariable() const override;
+  bool compareExprConstLag(expr_t expr, int const_lag) const override;
 };
 
 //! Symbol or variable node
@@ -697,9 +705,9 @@ public:
   expr_t substituteExpectation(subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs, bool partial_information_model) const override;
   expr_t substituteAdl() const override;
   void findDiffNodes(DataTree &static_datatree, diff_table_t &diff_table) const override;
-  void findUnaryOpNodesForAuxVarCreation(DataTree &static_datatree, diff_table_t &nodes) const override;
+  void findUnaryOpNodesForAuxVarCreation(DataTree &static_datatree, unary_op_aux_var_table_t &nodes) const override;
   expr_t substituteDiff(DataTree &static_datatree, diff_table_t &diff_table, subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const override;
-  expr_t substituteUnaryOpNodes(DataTree &static_datatree, diff_table_t &nodes, subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const override;
+  expr_t substituteUnaryOpNodes(DataTree &static_datatree, unary_op_aux_var_table_t &nodes, subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const override;
   expr_t substitutePacExpectation(map<const PacExpectationNode *, const BinaryOpNode *> &subst_table) override;
   expr_t decreaseLeadsLagsPredeterminedVariables() const override;
   expr_t differentiateForwardVars(const vector<string> &subset, subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const override;
@@ -722,6 +730,7 @@ public:
   void getEndosAndMaxLags(map<string, int> &model_endos_and_lags) const override;
   //! Substitute auxiliary variables by their expression in static model
   expr_t substituteStaticAuxiliaryVariable() const override;
+  bool compareExprConstLag(expr_t expr, int const_lag) const override;
 };
 
 //! Unary operator node
@@ -787,6 +796,31 @@ public:
   {
     return (op_code);
   };
+  int
+  get_expectation_information_set() const
+  {
+    return (expectation_information_set);
+  };
+  int
+  get_param1_symb_id() const
+  {
+    return (param1_symb_id);
+  };
+  int
+  get_param2_symb_id() const
+  {
+    return (param1_symb_id);
+  };
+  string
+  get_adl_param_name() const
+  {
+    return (adl_param_name);
+  };
+  vector<int>
+  get_adl_lags() const
+  {
+    return (adl_lags);
+  };
   expr_t toStatic(DataTree &static_datatree) const override;
   void computeXrefs(EquationInfo &ei) const override;
   pair<int, expr_t> normalizeEquation(int symb_id_endo, vector<pair<int, pair<expr_t, expr_t>>>  &List_of_Op_RHS) const override;
@@ -812,9 +846,9 @@ public:
   expr_t substituteAdl() const override;
   void findDiffNodes(DataTree &static_datatree, diff_table_t &diff_table) const override;
   bool createAuxVarForUnaryOpNode() const;
-  void findUnaryOpNodesForAuxVarCreation(DataTree &static_datatree, diff_table_t &nodes) const override;
+  void findUnaryOpNodesForAuxVarCreation(DataTree &static_datatree, unary_op_aux_var_table_t &nodes) const override;
   expr_t substituteDiff(DataTree &static_datatree, diff_table_t &diff_table, subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const override;
-  expr_t substituteUnaryOpNodes(DataTree &static_datatree, diff_table_t &nodes, subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const override;
+  expr_t substituteUnaryOpNodes(DataTree &static_datatree, unary_op_aux_var_table_t &nodes, subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const override;
   expr_t substitutePacExpectation(map<const PacExpectationNode *, const BinaryOpNode *> &subst_table) override;
   expr_t decreaseLeadsLagsPredeterminedVariables() const override;
   expr_t differentiateForwardVars(const vector<string> &subset, subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const override;
@@ -837,6 +871,7 @@ public:
   void getEndosAndMaxLags(map<string, int> &model_endos_and_lags) const override;
   //! Substitute auxiliary variables by their expression in static model
   expr_t substituteStaticAuxiliaryVariable() const override;
+  bool compareExprConstLag(expr_t expr, int const_lag) const override;
 };
 
 //! Binary operator node
@@ -914,7 +949,12 @@ public:
   get_power_deriv_order() const
   {
     return powerDerivOrder;
-  }
+  };
+  string
+  get_adlparam() const
+  {
+    return (adlparam);
+  };
   void walkPacParametersHelper(const expr_t arg1, const expr_t arg2,
                                pair<int, int> &lhs,
                                set<pair<int, pair<int, int>>> &ec_params_and_vars,
@@ -943,9 +983,9 @@ public:
   expr_t substituteExpectation(subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs, bool partial_information_model) const override;
   expr_t substituteAdl() const override;
   void findDiffNodes(DataTree &static_datatree, diff_table_t &diff_table) const override;
-  void findUnaryOpNodesForAuxVarCreation(DataTree &static_datatree, diff_table_t &nodes) const override;
+  void findUnaryOpNodesForAuxVarCreation(DataTree &static_datatree, unary_op_aux_var_table_t &nodes) const override;
   expr_t substituteDiff(DataTree &static_datatree, diff_table_t &diff_table, subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const override;
-  expr_t substituteUnaryOpNodes(DataTree &static_datatree, diff_table_t &nodes, subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const override;
+  expr_t substituteUnaryOpNodes(DataTree &static_datatree, unary_op_aux_var_table_t &nodes, subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const override;
   expr_t substitutePacExpectation(map<const PacExpectationNode *, const BinaryOpNode *> &subst_table) override;
   expr_t decreaseLeadsLagsPredeterminedVariables() const override;
   expr_t differentiateForwardVars(const vector<string> &subset, subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const override;
@@ -976,6 +1016,7 @@ public:
   expr_t substituteStaticAuxiliaryVariable() const override;
   //! Substitute auxiliary variables by their expression in static model auxiliary variable definition
   virtual expr_t substituteStaticAuxiliaryDefinition() const;
+  bool compareExprConstLag(expr_t expr, int const_lag) const override;
 };
 
 //! Trinary operator node
@@ -1023,6 +1064,26 @@ public:
   void collectVARLHSVariable(set<expr_t> &result) const override;
   void collectDynamicVariables(SymbolType type_arg, set<pair<int, int>> &result) const override;
   void collectTemporary_terms(const temporary_terms_t &temporary_terms, temporary_terms_inuse_t &temporary_terms_inuse, int Curr_Block) const override;
+  TrinaryOpcode
+  get_op_code() const
+  {
+    return (op_code);
+  };
+  expr_t
+  get_arg1() const
+  {
+    return (arg1);
+  };
+  expr_t
+  get_arg2() const
+  {
+    return (arg2);
+  };
+  expr_t
+  get_arg3() const
+  {
+    return (arg3);
+  };
   static double eval_opcode(double v1, TrinaryOpcode op_code, double v2, double v3) noexcept(false);
   double eval(const eval_context_t &eval_context) const noexcept(false) override;
   void compile(ostream &CompileCode, unsigned int &instruction_number, bool lhs_rhs, const temporary_terms_t &temporary_terms, const map_idx_t &map_idx, bool dynamic, bool steady_dynamic, const deriv_node_temp_terms_t &tef_terms) const override;
@@ -1050,9 +1111,9 @@ public:
   expr_t substituteExpectation(subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs, bool partial_information_model) const override;
   expr_t substituteAdl() const override;
   void findDiffNodes(DataTree &static_datatree, diff_table_t &diff_table) const override;
-  void findUnaryOpNodesForAuxVarCreation(DataTree &static_datatree, diff_table_t &nodes) const override;
+  void findUnaryOpNodesForAuxVarCreation(DataTree &static_datatree, unary_op_aux_var_table_t &nodes) const override;
   expr_t substituteDiff(DataTree &static_datatree, diff_table_t &diff_table, subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const override;
-  expr_t substituteUnaryOpNodes(DataTree &static_datatree, diff_table_t &nodes, subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const override;
+  expr_t substituteUnaryOpNodes(DataTree &static_datatree, unary_op_aux_var_table_t &nodes, subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const override;
   expr_t substitutePacExpectation(map<const PacExpectationNode *, const BinaryOpNode *> &subst_table) override;
   expr_t decreaseLeadsLagsPredeterminedVariables() const override;
   expr_t differentiateForwardVars(const vector<string> &subset, subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const override;
@@ -1075,6 +1136,7 @@ public:
   void getEndosAndMaxLags(map<string, int> &model_endos_and_lags) const override;
   //! Substitute auxiliary variables by their expression in static model
   expr_t substituteStaticAuxiliaryVariable() const override;
+  bool compareExprConstLag(expr_t expr, int const_lag) const override;
 };
 
 //! External function node
@@ -1137,7 +1199,11 @@ public:
                                                 bool lhs_rhs, const temporary_terms_t &temporary_terms,
                                                 const map_idx_t &map_idx, bool dynamic, bool steady_dynamic,
                                                 const deriv_node_temp_terms_t &tef_terms) const;
-
+  int
+  get_symb_id() const
+  {
+    return (symb_id);
+  };
   void compile(ostream &CompileCode, unsigned int &instruction_number, bool lhs_rhs, const temporary_terms_t &temporary_terms, const map_idx_t &map_idx, bool dynamic, bool steady_dynamic, const deriv_node_temp_terms_t &tef_terms) const override = 0;
   expr_t toStatic(DataTree &static_datatree) const override = 0;
   void computeXrefs(EquationInfo &ei) const override = 0;
@@ -1161,9 +1227,9 @@ public:
   expr_t substituteExpectation(subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs, bool partial_information_model) const override;
   expr_t substituteAdl() const override;
   void findDiffNodes(DataTree &static_datatree, diff_table_t &diff_table) const override;
-  void findUnaryOpNodesForAuxVarCreation(DataTree &static_datatree, diff_table_t &nodes) const override;
+  void findUnaryOpNodesForAuxVarCreation(DataTree &static_datatree, unary_op_aux_var_table_t &nodes) const override;
   expr_t substituteDiff(DataTree &static_datatree, diff_table_t &diff_table, subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const override;
-  expr_t substituteUnaryOpNodes(DataTree &static_datatree, diff_table_t &nodes, subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const override;
+  expr_t substituteUnaryOpNodes(DataTree &static_datatree, unary_op_aux_var_table_t &nodes, subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const override;
   expr_t substitutePacExpectation(map<const PacExpectationNode *, const BinaryOpNode *> &subst_table) override;
   virtual expr_t buildSimilarExternalFunctionNode(vector<expr_t> &alt_args, DataTree &alt_datatree) const = 0;
   expr_t decreaseLeadsLagsPredeterminedVariables() const override;
@@ -1188,6 +1254,7 @@ public:
   void getEndosAndMaxLags(map<string, int> &model_endos_and_lags) const override;
   //! Substitute auxiliary variables by their expression in static model
   expr_t substituteStaticAuxiliaryVariable() const override;
+  bool compareExprConstLag(expr_t expr, int const_lag) const override;
 };
 
 class ExternalFunctionNode : public AbstractExternalFunctionNode
@@ -1333,6 +1400,26 @@ public:
                                      int Curr_block,
                                      vector< vector<temporary_terms_t>> &v_temporary_terms,
                                      int equation) const override;
+  int
+  get_symb_id() const
+  {
+    return (symb_id);
+  };
+  int
+  get_forecast_horizon() const
+  {
+    return (forecast_horizon);
+  };
+  int
+  get_yidx() const
+  {
+    return (yidx);
+  };
+  string
+  get_model_name() const
+  {
+    return (model_name);
+  }
   expr_t toStatic(DataTree &static_datatree) const override;
   expr_t cloneDynamic(DataTree &dynamic_datatree) const override;
   int maxEndoLead() const override;
@@ -1359,9 +1446,9 @@ public:
   expr_t substituteExpectation(subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs, bool partial_information_model) const override;
   expr_t substituteAdl() const override;
   void findDiffNodes(DataTree &static_datatree, diff_table_t &diff_table) const override;
-  void findUnaryOpNodesForAuxVarCreation(DataTree &static_datatree, diff_table_t &nodes) const override;
+  void findUnaryOpNodesForAuxVarCreation(DataTree &static_datatree, unary_op_aux_var_table_t &nodes) const override;
   expr_t substituteDiff(DataTree &static_datatree, diff_table_t &diff_table, subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const override;
-  expr_t substituteUnaryOpNodes(DataTree &static_datatree, diff_table_t &nodes, subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const override;
+  expr_t substituteUnaryOpNodes(DataTree &static_datatree, unary_op_aux_var_table_t &nodes, subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const override;
   expr_t substitutePacExpectation(map<const PacExpectationNode *, const BinaryOpNode *> &subst_table) override;
   pair<int, expr_t> normalizeEquation(int symb_id_endo, vector<pair<int, pair<expr_t, expr_t>>>  &List_of_Op_RHS) const override;
   void compile(ostream &CompileCode, unsigned int &instruction_number,
@@ -1391,6 +1478,7 @@ public:
   void getEndosAndMaxLags(map<string, int> &model_endos_and_lags) const override;
   expr_t substituteStaticAuxiliaryVariable() const override;
   void writeJsonOutput(ostream &output, const temporary_terms_t &temporary_terms, const deriv_node_temp_terms_t &tef_terms, const bool isdynamic) const override;
+  bool compareExprConstLag(expr_t expr, int const_lag) const override;
 };
 
 class PacExpectationNode : public ExprNode
@@ -1445,9 +1533,9 @@ public:
   expr_t substituteExpectation(subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs, bool partial_information_model) const override;
   expr_t substituteAdl() const override;
   void findDiffNodes(DataTree &static_datatree, diff_table_t &diff_table) const override;
-  void findUnaryOpNodesForAuxVarCreation(DataTree &static_datatree, diff_table_t &nodes) const override;
+  void findUnaryOpNodesForAuxVarCreation(DataTree &static_datatree, unary_op_aux_var_table_t &nodes) const override;
   expr_t substituteDiff(DataTree &static_datatree, diff_table_t &diff_table, subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const override;
-  expr_t substituteUnaryOpNodes(DataTree &static_datatree, diff_table_t &nodes, subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const override;
+  expr_t substituteUnaryOpNodes(DataTree &static_datatree, unary_op_aux_var_table_t &nodes, subst_table_t &subst_table, vector<BinaryOpNode *> &neweqs) const override;
   expr_t substitutePacExpectation(map<const PacExpectationNode *, const BinaryOpNode *> &subst_table) override;
   pair<int, expr_t> normalizeEquation(int symb_id_endo, vector<pair<int, pair<expr_t, expr_t>>>  &List_of_Op_RHS) const override;
   void compile(ostream &CompileCode, unsigned int &instruction_number,
@@ -1477,6 +1565,7 @@ public:
   void getEndosAndMaxLags(map<string, int> &model_endos_and_lags) const override;
   expr_t substituteStaticAuxiliaryVariable() const override;
   void writeJsonOutput(ostream &output, const temporary_terms_t &temporary_terms, const deriv_node_temp_terms_t &tef_terms, const bool isdynamic) const override;
+  bool compareExprConstLag(expr_t expr, int const_lag) const override;
 };
 
 #endif