Shared term manager tested and working
authorClark Barrett <barrett@cs.nyu.edu>
Wed, 7 Jul 2010 21:55:11 +0000 (21:55 +0000)
committerClark Barrett <barrett@cs.nyu.edu>
Wed, 7 Jul 2010 21:55:11 +0000 (21:55 +0000)
It is currently tracking all asserted equalities for simplicity.
Might want to check if this is a performance hit

src/theory/arrays/theory_arrays.cpp
src/theory/arrays/theory_arrays.h
src/theory/shared_data.cpp
src/theory/shared_data.h
src/theory/shared_term_manager.cpp
src/theory/shared_term_manager.h
src/theory/theory.h
src/theory/theory_engine.cpp
test/regress/regress0/arr2.smt [new file with mode: 0644]
test/unit/Makefile.am
test/unit/theory/shared_term_manager_black.h [new file with mode: 0644]

index b84b1e5073019f72a96c8c8f809d8fd9cd061a5d..8b5f7d3e765c4d440b41db90fec645124619e4f8 100644 (file)
@@ -45,9 +45,9 @@ void TheoryArrays::addSharedTerm(TNode t) {
 }
 
 
-void TheoryArrays::notifyEq(TNode eq) {
+void TheoryArrays::notifyEq(TNode lhs, TNode rhs) {
   Debug("arrays") << "TheoryArrays::notifyEq(): "
-                  << eq << endl;
+                  << lhs << " = " << rhs << endl;
 }
 
 
index 3cb050287d53f71f9d177f43f24fd22cd038ffae..cb738d085570d8faf78f04e04ccd0dae86f74f49 100644 (file)
@@ -49,7 +49,7 @@ public:
   }
 
   void addSharedTerm(TNode t);
-  void notifyEq(TNode eq);
+  void notifyEq(TNode lhs, TNode rhs);
   void check(Effort e);
   void propagate(Effort e) { }
   void explain(TNode n, Effort e) { }
index 7a8970c2976b3406c8b4c87ae2c49dbdefbf9b89..50e91683252628a916406a079b0903a7b3f83b9a 100644 (file)
@@ -33,6 +33,7 @@ SharedData::SharedData(Context * context, TNode n, uint64_t theories) :
   d_size(1),
   d_find(this),
   d_proofNext(this),
+  d_edgeReversed(false),
   d_explainingTheory(NULL),
   d_rep(n) {
 }
@@ -49,7 +50,7 @@ void SharedData::restore(ContextObj* pContextObj) {
   d_size = data->d_size;
   d_find = data->d_find;
   d_proofNext = data->d_proofNext;
-  d_equality = data->d_equality;
+  d_edgeReversed = data->d_edgeReversed;
   d_explainingTheory = data->d_explainingTheory;
   d_rep = data->d_rep;
 }
@@ -60,20 +61,23 @@ void SharedData::reverseEdges() {
 
   SharedData* parent = this;
   SharedData* current = d_proofNext;
-  Node equality = d_equality;
+  bool reversed = d_edgeReversed;
   Theory* explainingTheory = d_explainingTheory;
 
   makeCurrent();
+
+  // Make this the proof root
   d_proofNext = this;
   
-  Node tmpNode;
+  // Reverse the edges from here to the former root
+  bool tmpReversed;
   Theory* tmpTheory;
   SharedData* tmpData;
 
   while (!current->isProofRoot()) {
-    tmpNode = current->getEquality();
-    current->setEquality(equality);
-    equality = tmpNode;
+    tmpReversed = current->getEdgeReversed();
+    current->setEdgeReversed(!reversed);
+    reversed = tmpReversed;
 
     tmpTheory = current->getExplainingTheory();
     current->setExplainingTheory(explainingTheory);
@@ -84,7 +88,7 @@ void SharedData::reverseEdges() {
     parent = current;
     current = tmpData;
   }
-  current->setEquality(equality);
+  current->setEdgeReversed(!reversed);
   current->setExplainingTheory(explainingTheory);
   current->setProofNext(parent);
 }
index 64b7f5ab16f9de719c18f21273c8a98d5cc7a4f2..0b21eedae44e08ae76910f045fa9f32066733b25 100644 (file)
@@ -72,13 +72,15 @@ private:
   SharedData* d_proofNext;
 
   /**
-   * The equality that justifies this node being equal to
-   * the node at d_proofNext.  Not valid if this is proof root.
+   * In order to precisely reconstruct the equality that justifies this node
+   * being equal to the node at d_proofNext, we need to know whether this edge
+   * has been switched.  Value is meaningless at the proof root.
    */
-  Node d_equality;
+  bool d_edgeReversed;
 
   /**
-   * The theory that can explain d_equality.  Not valid if this is proof root.
+   * The theory that can explain the equality of this node and the node at
+   * d_proofNext.  Not valid if this is proof root.
    */
   theory::Theory* d_explainingTheory;
 
@@ -157,14 +159,24 @@ public:
   void setProofNext(SharedData* pData) { makeCurrent(); d_proofNext = pData; }
 
   /**
-   * Get the equality associated with the link in the proof tree.
+   * Get the edge reversed property of this node
    */
-  Node getEquality() const { return d_equality; }
+  bool getEdgeReversed() const { return d_edgeReversed; }
 
   /**
-   * Set the equality associated with the link in the proof tree.
+   * Set the edge reversed flag to value
    */
-  void setEquality(TNode eq) { makeCurrent(); d_equality = eq; }
+  void setEdgeReversed(bool value) { makeCurrent(); d_edgeReversed = value; }
+
+  /**
+   * Get the original equality that created the link from this node to the next
+   * proof node.
+   */
+  Node getEquality() const {
+    return d_edgeReversed
+      ? NodeManager::currentNM()->mkNode(kind::EQUAL, d_proofNext->getRep(), d_rep)
+      : NodeManager::currentNM()->mkNode(kind::EQUAL, d_rep, d_proofNext->getRep());
+  }
 
   /**
    * Get the explaining theory
index 55087ecbdcd7dfc0a54473b564283827f5e7ab3c..564fb776f328c2de7f68daf967a33732f67f14c4 100644 (file)
@@ -31,11 +31,16 @@ SharedTermManager::SharedTermManager(TheoryEngine* engine,
 }
 
 
-SharedData* SharedTermManager::find(SharedData* pData) {
+SharedData* SharedTermManager::find(SharedData* pData) const {
+  // Check if pData is the representative
   SharedData* next = pData->getFind();
   if (next == pData) return pData;
+
+  // Check if its successor is the representative
   SharedData* nextNext = next->getFind();
   if (nextNext == next) return next;
+
+  // Otherwise, recurse and do find path-compression
   next = find(nextNext);
   pData->setFind(next);
   return next;
@@ -59,23 +64,49 @@ void SharedTermManager::addTerm(TNode n, Theory* parent, Theory* child) {
   // theory id
   uint64_t parentTag = (1 << parent->getId());
   uint64_t childTag = (1 << child->getId());
-  uint64_t newTags = parentTag | childTag;
+  uint64_t bothTags = parentTag | childTag;
+
+  // Create or update the SharedData for n
   SharedData* pData;
   if(n.getAttribute(SharedAttr(), pData)) {
     // The attribute already exists, just update it if necessary
     uint64_t tags = pData->getTheories();
-    newTags |= tags;
+    uint64_t newTags = tags | bothTags;
     if (tags == newTags) return;
+
+    // Get the equivalence class representative -- if this is different from
+    // the current node, then we will need to notify the theories of that and
+    // update the theory tags on the representative node
+    SharedData* pDataFind = find(pData);
+
+    // Notify parent theory if necessary
     if (!(tags & parentTag)) {
       parent->addSharedTerm(n);
+      if (pData != pDataFind) {
+        parent->notifyEq(n, pDataFind->getRep());
+      }
     }
+
+    // Notify child theory if necessary
     if (!(tags & childTag)) {
       child->addSharedTerm(n);
+      if (pData != pDataFind) {
+        child->notifyEq(n, pDataFind->getRep());
+      }
     }
+
+    // Update theory tags
     pData->setTheories(newTags);
+    if (pData != pDataFind) {
+      tags = pDataFind->getTheories();
+      newTags = tags | bothTags;
+      if (tags != newTags) {
+        pDataFind->setTheories(newTags);
+      }
+    }
   } else {
     // The attribute does not exist, so it is created and set
-    pData = new (true) SharedData(d_context, n, newTags);
+    pData = new (true) SharedData(d_context, n, bothTags);
     n.setAttribute(SharedAttr(), pData);
     parent->addSharedTerm(n);
     child->addSharedTerm(n);
@@ -83,22 +114,35 @@ void SharedTermManager::addTerm(TNode n, Theory* parent, Theory* child) {
 }
 
 
-void SharedTermManager::addEq(Theory* thReason, TNode eq) {
-  Assert(eq.getKind() == kind::EQUAL &&
-         eq[0].hasAttribute(SharedAttr()) && eq[1].hasAttribute(SharedAttr()),
+void SharedTermManager::addEq(TNode eq, Theory* thReason) {
+  Assert(eq.getKind() == kind::EQUAL,
          "SharedTermManager::addEq precondition violated");
 
   TNode x = eq[0];
   TNode y = eq[1];
 
-  SharedData* pDataX = x.getAttribute(SharedAttr());
-  SharedData* pDataY = y.getAttribute(SharedAttr());
+  SharedData* pDataX;
+  SharedData* pDataY;
+
+  // Grab the SharedData for each side of the equality, create if necessary
+  if(!x.getAttribute(SharedAttr(), pDataX)) {
+    pDataX = new (true) SharedData(d_context, x, 0);
+    x.setAttribute(SharedAttr(), pDataX);
+  }
+  if(!y.getAttribute(SharedAttr(), pDataY)) {
+    pDataY = new (true) SharedData(d_context, y, 0);
+    y.setAttribute(SharedAttr(), pDataY);
+  }
 
+  // Get the representatives
   SharedData* pDataXX = find(pDataX);
   SharedData* pDataYY = find(pDataY);
 
+  // If already in the same equivalence class, nothing to do
   if(pDataXX == pDataYY) return;
 
+  // Make sure we reverse the edges in the smaller tree
+  bool switched = false;
   if(pDataXX->getSize() > pDataYY->getSize()) {
     SharedData* tmp = pDataXX;
     pDataXX = pDataYY;
@@ -107,17 +151,27 @@ void SharedTermManager::addEq(Theory* thReason, TNode eq) {
     tmp = pDataX;
     pDataX = pDataY;
     pDataY = tmp;
+
+    switched = true;
   }
 
-  pDataX->reverseEdges();
-  pDataX->setEquality(eq);
+  // Reverse the edges in the left proof tree and link the two trees
+  if(!pDataX->isProofRoot()) {
+    pDataX->reverseEdges();
+  }
+  pDataX->setEdgeReversed(switched);
   pDataX->setExplainingTheory(thReason);
   pDataX->setProofNext(pDataY);
 
+  // Set the equivalence class representative for the left node to be the right node
   pDataXX->setFind(pDataYY);
   pDataYY->setSize(pDataYY->getSize() + pDataXX->getSize());
+
+  // Update the theory tags for the new representative
   uint64_t tags = pDataXX->getTheories();
   pDataYY->setTheories(pDataYY->getTheories() | tags);
+
+  // Notify the theories that care about the left node
   int id = 0;
   while (tags != 0) {
     if (tags & 1) {
@@ -129,8 +183,29 @@ void SharedTermManager::addEq(Theory* thReason, TNode eq) {
 }
 
 
+void SharedTermManager::collectExplanations(SharedData* pData, set<Node>& s) const {
+  Theory* expTh = pData->getExplainingTheory();
+  if(expTh == NULL) {
+    // This equality is directly from SAT, no need to ask a theory for an explanation
+    s.insert(pData->getEquality());
+  }
+  else {
+    // This equality was sent by a theory - ask the theory for the explanation
+    Node n = d_engine->getExplanation(pData->getEquality(), expTh);
+    if(n.getKind() == kind::AND) {
+      for (Node::iterator it = n.begin(), iend = n.end(); it != iend; ++it) {
+        s.insert(*it);
+      }
+    }
+    else {
+      s.insert(n);
+    }
+  }
+}
+
+
 // Explain this equality
-Node SharedTermManager::explain(TNode eq) {
+Node SharedTermManager::explain(TNode eq) const {
   Assert(eq.getKind() == kind::EQUAL &&
          eq[0].hasAttribute(SharedAttr()) && eq[1].hasAttribute(SharedAttr()),
          "SharedTermManager::explain precondition violated");
@@ -138,14 +213,15 @@ Node SharedTermManager::explain(TNode eq) {
   TNode x = eq[0];
   TNode y = eq[1];
 
+  // Get the SharedData for both sides of the equality
   SharedData* pDataX = x.getAttribute(SharedAttr());
   SharedData* pDataY = y.getAttribute(SharedAttr());
 
   Assert(find(pDataX) == find(pDataY),
          "invalid equality input to SharedTermManager::explain");
 
+  // Find the path between the two nodes and build up the explanation
   std::set<Node> s;
-  Node n;
   SharedData* tmp = pDataX;
 
   // Check to see if Y is on path from X to root
@@ -155,39 +231,27 @@ Node SharedTermManager::explain(TNode eq) {
   if (tmp == pDataY) {
     // Y is on path from X to root, just traverse that path
     while (pDataX != pDataY) {
-      n = d_engine->getExplanation(pDataX->getEquality(),
-                                   pDataX->getExplainingTheory());
-      for (Node::iterator it = n.begin(), iend = n.end(); it != iend; ++it) {
-        s.insert(*it);
-      }
+      collectExplanations(pDataX, s);
       pDataX = pDataX->getProofNext();
     }
   }
   else {
     // Y is not on path from X to root, so traverse from Y to root instead
     while (!pDataY->isProofRoot() && pDataX != pDataY) {
-      n = d_engine->getExplanation(pDataY->getEquality(),
-                                   pDataY->getExplainingTheory());
-      for (Node::iterator it = n.begin(), iend = n.end(); it != iend; ++it) {
-        s.insert(*it);
-      }
+      collectExplanations(pDataY, s);
       pDataY = pDataY->getProofNext();
     }
   }
   if (pDataX != pDataY) {
     // X and Y are on different branches - have to traverse both
     while (!pDataX->isProofRoot()) {
-      n = d_engine->getExplanation(pDataX->getEquality(),
-                                   pDataX->getExplainingTheory());
-      for (Node::iterator it = n.begin(), iend = n.end(); it != iend; ++it) {
-        s.insert(*it);
-      }
+      collectExplanations(pDataX, s);
       pDataX = pDataX->getProofNext();
     }
   }
 
   // Build explanation from s
-  NodeBuilder<kind::AND> nb;
+  NodeBuilder<> nb(kind::AND);
   set<Node>::iterator it = s.begin(), iend = s.end();
   for (; it != iend; ++it) {
     nb << *it;
@@ -196,4 +260,13 @@ Node SharedTermManager::explain(TNode eq) {
 }
 
 
+Node SharedTermManager::getRep(TNode n) const {
+  Assert(n.hasAttribute(SharedAttr()),
+         "SharedTermManager::getRep precondition violated");
+
+  SharedData* pData = n.getAttribute(SharedAttr());
+  return find(pData)->getRep();
+}
+
+
 }/* CVC4 namespace */
index e6a58cdc956c4ace2153239921104f6694b11031..8b5567e4e083e6408adfb899d42b18f8fd11ea72 100644 (file)
@@ -39,25 +39,77 @@ namespace theory {
  */
 class SharedTermManager {
 
+  /**
+   * Pointer back to theory engine
+   */
   TheoryEngine* d_engine;
 
+  /**
+   * Pointer to context
+   */
   context::Context* d_context;
 
+  /**
+   * List of all theories indexed by theory id (built by calls to registerTheory)
+   */
   std::vector<theory::Theory*> d_theories;
 
-  SharedData* find(SharedData* pData);
+  /**
+   * Private method to find equivalence class representative in union-find data
+   * structure.
+   */
+  SharedData* find(SharedData* pData) const;
+
+  /**
+   * Helper function for explain: add all reasons for equality at pData to set s
+   */
+  void collectExplanations(SharedData* pData, std::set<Node>& s) const;
 
 public:
+  /**
+   * Constructor
+   */
   SharedTermManager(TheoryEngine* engine, context::Context* context);
 
+  /**
+   * Should be called once for each theory at setup time
+   */
   void registerTheory(theory::Theory* th);
 
+  /**
+   * Called by theory engine to indicate that node n is shared by theories
+   * parent and child.
+   */
   void addTerm(TNode n, theory::Theory* parent,
                theory::Theory* child);
 
-  void addEq(theory::Theory* thReason, TNode eq);
-
-  Node explain(TNode eq);
+  /**
+   * Called by theory engine or theories to notify the shared term manager that
+   * two terms are equal.
+   *
+   * @param eq the equality between shared terms
+   * @param thReason the theory that knows why, NULL means it's a SAT assertion
+   */
+  void addEq(TNode eq, theory::Theory* thReason = NULL);
+
+  /**
+   * Called by theory engine or theories to notify the shared term manager that
+   * two terms are disequal.
+   *
+   * @param eq the equality between shared terms whose negation now holds
+   * @param thReason the theory that knows why, NULL means it's a SAT assertion
+   */
+  void addDiseq(TNode eq, theory::Theory* thReason = NULL) { }
+
+  /**
+   * Get an explanation for an equality known by the SharedTermManager
+   */
+  Node explain(TNode eq) const;
+
+  /**
+   * Get the representative node in the equivalence class containing n
+   */
+  Node getRep(TNode n) const;
 
 };/* class SharedTermManager */
 
index 6da47fbd8d76a4102001b7af1fc40da3cfdbd66e..64a3b8f06f3f359f7adec0387ce86de318bbe5b3 100644 (file)
@@ -358,15 +358,15 @@ public:
   virtual void addSharedTerm(TNode n) { }
 
   /**
-   * This method is called by the shared term manager when a shared term t
+   * This method is called by the shared term manager when a shared term lhs
    * which this theory cares about (either because it received a previous
-   * addSharedTerm call with t or because it received a previous notifyEq call
-   * with t as the second argument) becomes equal to another shared term u.
+   * addSharedTerm call with lhs or because it received a previous notifyEq call
+   * with lhs as the second argument) becomes equal to another shared term rhs.
    * This call also serves as notice to the theory that the shared term manager
-   * now considers u the representative for this equivalence class of shared
-   * terms, so future notifications for this class will be based on u not t.
+   * now considers rhs the representative for this equivalence class of shared
+   * terms, so future notifications for this class will be based on rhs not lhs.
    */
-  virtual void notifyEq(TNode t, TNode u) { }
+  virtual void notifyEq(TNode lhs, TNode rhs) { }
 
   /**
    * Check the current assignment's consistency.
index d16d800901376013d010f5edb153bf1654c02160..4fae9425469881d8532b73ef4e045560a0d69a59 100644 (file)
@@ -46,6 +46,14 @@ void TheoryEngine::EngineOutputChannel::newFact(TNode fact) {
   if (fact.getKind() == kind::NOT) {
     // No need to register negations - only atoms
     fact = fact[0];
+// FIXME: In future, might want to track disequalities in shared term manager
+//     if (fact.getKind() == kind::EQUAL) {
+//       d_engine->getSharedTermManager()->addDiseq(fact);
+//     }
+  }
+  else if (fact.getKind() == kind::EQUAL) {
+    // Automatically track all asserted equalities in the shared term manager
+    d_engine->getSharedTermManager()->addEq(fact);
   }
   if(! fact.getAttribute(RegisteredAttr())) {
     std::list<TNode> toReg;
diff --git a/test/regress/regress0/arr2.smt b/test/regress/regress0/arr2.smt
new file mode 100644 (file)
index 0000000..213660c
--- /dev/null
@@ -0,0 +1,7 @@
+(benchmark simple_arr
+  :logic QF_AX
+  :status unsat
+  :extrafuns ((a Array))
+  :extrafuns ((i1 Index) (i2 Index) (i3 Index))
+  :formula (not (implies (and (= i1 i2) (= i2 i3)) (= (select a i1) (select a i3))))
+)
index c96fbccb603240a583a14df63c8b39f00dc1573a..7d6f6ff50511afe032244e4addabbc1bea215619 100644 (file)
@@ -1,5 +1,10 @@
 # All unit tests
 UNIT_TESTS = \
+       theory/shared_term_manager_black \
+       theory/theory_engine_white \
+       theory/theory_black \
+       theory/theory_uf_white \
+       theory/theory_arith_white \
        expr/expr_public \
         expr/expr_manager_public \
        expr/node_white \
@@ -21,10 +26,6 @@ UNIT_TESTS = \
        context/cdlist_black \
        context/cdmap_black \
        context/cdmap_white \
-       theory/theory_engine_white \
-       theory/theory_black \
-       theory/theory_uf_white \
-       theory/theory_arith_white \
        util/assert_white \
        util/bitvector_black \
        util/configuration_black \
diff --git a/test/unit/theory/shared_term_manager_black.h b/test/unit/theory/shared_term_manager_black.h
new file mode 100644 (file)
index 0000000..4393d99
--- /dev/null
@@ -0,0 +1,148 @@
+/*********************                                                        */
+/*! \file shared_term_manager_black.h
+ ** \verbatim
+ ** Original author: barrett
+ ** Major contributors: none
+ ** Minor contributors (to current version): none
+ ** This file is part of the CVC4 prototype.
+ ** Copyright (c) 2009, 2010  The Analysis of Computer Systems Group (ACSys)
+ ** Courant Institute of Mathematical Sciences
+ ** New York University
+ ** See the file COPYING in the top-level source directory for licensing
+ ** information.\endverbatim
+ **
+ ** \brief Black box testing of CVC4::SharedTermManager.
+ **
+ ** Black box testing of CVC4::SharedTermManager.
+ **/
+
+#include <cxxtest/TestSuite.h>
+
+#include <iostream>
+#include <string>
+#include <deque>
+
+#include "theory/theory.h"
+#include "theory/theory_engine.h"
+#include "theory/theoryof_table.h"
+#include "expr/node.h"
+#include "expr/node_manager.h"
+#include "expr/kind.h"
+#include "context/context.h"
+#include "util/rational.h"
+#include "util/integer.h"
+#include "util/Assert.h"
+
+using namespace CVC4;
+using namespace CVC4::theory;
+using namespace CVC4::expr;
+using namespace CVC4::context;
+using namespace CVC4::kind;
+
+using namespace std;
+
+/**
+ * Test the SharedTermManager.
+ */
+class SharedTermManagerBlack : public CxxTest::TestSuite {
+  Context* d_ctxt;
+
+  NodeManager* d_nm;
+  NodeManagerScope* d_scope;
+  TheoryEngine* d_theoryEngine;
+
+public:
+
+  void setUp() {
+    d_ctxt = new Context;
+
+    d_nm = new NodeManager(d_ctxt);
+    d_scope = new NodeManagerScope(d_nm);
+
+    d_theoryEngine = new TheoryEngine(d_ctxt);
+
+  }
+
+  void tearDown() {
+    delete d_theoryEngine;
+
+    delete d_scope;
+    delete d_nm;
+
+    delete d_ctxt;
+  }
+
+  void testExplanation() {
+    // Example from Barcelogic paper
+    SharedTermManager* stm = d_theoryEngine->getSharedTermManager();
+
+    Node x1 = d_nm->mkVar("x1", d_nm->integerType());
+    Node x2 = d_nm->mkVar("x2", d_nm->integerType());
+    Node x3 = d_nm->mkVar("x3", d_nm->integerType());
+    Node x4 = d_nm->mkVar("x4", d_nm->integerType());
+    Node x5 = d_nm->mkVar("x5", d_nm->integerType());
+    Node x6 = d_nm->mkVar("x6", d_nm->integerType());
+    Node x7 = d_nm->mkVar("x7", d_nm->integerType());
+    Node x8 = d_nm->mkVar("x8", d_nm->integerType());
+    Node x9 = d_nm->mkVar("x9", d_nm->integerType());
+    Node x10 = d_nm->mkVar("x10", d_nm->integerType());
+    Node x11 = d_nm->mkVar("x11", d_nm->integerType());
+    Node x12 = d_nm->mkVar("x12", d_nm->integerType());
+    Node x13 = d_nm->mkVar("x13", d_nm->integerType());
+    Node x14 = d_nm->mkVar("x14", d_nm->integerType());
+
+    Node a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12;
+
+    d_ctxt->push();
+
+    stm->addEq(a1 = d_nm->mkNode(EQUAL, x1, x8));
+    stm->addEq(a2 = d_nm->mkNode(EQUAL, x7, x2));
+    stm->addEq(a3 = d_nm->mkNode(EQUAL, x3, x13));
+    stm->addEq(a4 = d_nm->mkNode(EQUAL, x7, x1));
+    stm->addEq(a5 = d_nm->mkNode(EQUAL, x6, x7));
+    stm->addEq(a6 = d_nm->mkNode(EQUAL, x9, x5));
+    stm->addEq(a7 = d_nm->mkNode(EQUAL, x9, x3));
+    stm->addEq(a8 = d_nm->mkNode(EQUAL, x14, x11));
+    stm->addEq(a9 = d_nm->mkNode(EQUAL, x10, x4));
+    stm->addEq(a10 = d_nm->mkNode(EQUAL, x12, x9));
+    stm->addEq(a11 = d_nm->mkNode(EQUAL, x4, x11));
+    stm->addEq(a12 = d_nm->mkNode(EQUAL, x10, x7));
+
+    Node explanation = stm->explain(d_nm->mkNode(EQUAL, x1, x4));
+
+    TS_ASSERT(explanation.getNumChildren() == 3);
+
+    Node e0 = explanation[0];
+    Node e1 = explanation[1];
+    Node e2 = explanation[2];
+
+    TS_ASSERT(e0 == a4 && e1 == a9 && e2 == a12);
+
+    TS_ASSERT(stm->getRep(x8) == stm->getRep(x14));
+    TS_ASSERT(stm->getRep(x2) != stm->getRep(x12));
+
+    d_ctxt->pop();
+
+    TS_ASSERT(stm->getRep(x8) != stm->getRep(x14));
+
+    d_ctxt->push();
+
+    stm->addEq(a1 = d_nm->mkNode(EQUAL, x1, x8));
+    stm->addEq(a2 = d_nm->mkNode(EQUAL, x8, x2));
+    stm->addEq(a3 = d_nm->mkNode(EQUAL, x2, x3));
+
+    explanation = stm->explain(d_nm->mkNode(EQUAL, x1, x3));
+    TS_ASSERT(explanation.getNumChildren() == 3);
+
+    e0 = explanation[0];
+    e1 = explanation[1];
+    e2 = explanation[2];
+
+    TS_ASSERT(e0 == a1 && e1 == a2 && e2 == a3);
+
+    TS_ASSERT(stm->getRep(x8) == stm->getRep(x2));
+
+    d_ctxt->pop();
+  }
+
+};