1 #include "cvc4_private.h"
6 #include <ext/hash_map>
9 #include "context/cdo.h"
10 #include "util/output.h"
16 struct BitSizeTraits
{
19 static const size_t id_null
; // Defined in the cpp file (GCC bug)
20 /** The null trigger id */
21 static const size_t trigger_id_null
;
23 /** Number of bits we use for the id */
24 static const size_t id_bits
= 24;
25 /** Number of bits we use for the size the equivalence class */
26 static const size_t size_bits
= 16;
27 /** Number of bits we use for the trigger id */
28 static const size_t trigger_id_bits
= 24;
36 /** The size of this equivalence class (if it's a representative) */
37 size_t d_size
: BitSizeTraits::size_bits
;
39 /** The id (in the eq-manager) of the representative equality node */
40 size_t d_findId
: BitSizeTraits::id_bits
;
42 /** The next equality node in this class */
43 size_t d_nextId
: BitSizeTraits::id_bits
;
48 * Creates a new node, which is in a list of it's own.
50 EqualityNode(size_t nodeId
= BitSizeTraits::id_null
)
51 : d_size(1), d_findId(nodeId
), d_nextId(nodeId
) {}
53 /** Initialize the equality node */
54 inline void init(size_t nodeId
) {
56 d_findId
= d_nextId
= nodeId
;
60 * Returns the next node in the class circural list.
62 inline size_t getNext() const {
67 * Returns the size of this equivalence class (only valid if this is the representative).
69 inline size_t getSize() const {
74 * Merges the two lists. If add size is true the size of this node is increased by the size of
75 * the other node, otherwise the size is decreased by the size of the other node.
77 template<bool addSize
>
78 inline void merge(EqualityNode
& other
) {
79 size_t tmp
= d_nextId
; d_nextId
= other
.d_nextId
; other
.d_nextId
= tmp
;
81 d_size
+= other
.d_size
;
83 d_size
-= other
.d_size
;
88 * Returns the class representative.
90 inline size_t getFind() const { return d_findId
; }
93 * Set the class representative.
95 inline void setFind(size_t findId
) { d_findId
= findId
; }
100 template <typename OwnerClass
, typename NotifyClass
>
101 class EqualityEngine
{
103 /** The class to notify when a representative changes for a term */
104 NotifyClass d_notify
;
106 /** Map from nodes to their ids */
107 __gnu_cxx::hash_map
<TNode
, size_t, TNodeHashFunction
> d_nodeIds
;
109 /** Map from ids to the nodes */
110 std::vector
<Node
> d_nodes
;
112 /** Map from ids to the equality nodes */
113 std::vector
<EqualityNode
> d_equalityNodes
;
115 /** Number of asserted equalities we have so far */
116 context::CDO
<size_t> d_assertedEqualitiesCount
;
119 * We keep a list of asserted equalities. Not among original terms, but
120 * among the class representatives.
123 /** Left hand side of the equality */
124 size_t lhs
: BitSizeTraits::id_bits
;
125 /** Right hand side of the equality */
126 size_t rhs
: BitSizeTraits::id_bits
;
127 /** Equality constructor */
128 Equality(size_t lhs
= BitSizeTraits::id_null
, size_t rhs
= BitSizeTraits::id_null
)
129 : lhs(lhs
), rhs(rhs
) {}
132 /** The ids of the classes we have merged */
133 std::vector
<Equality
> d_assertedEqualities
;
136 * An edge in the equality graph. This graph is an undirected graph (both edges added)
137 * containing the actual asserted equalities.
141 // The id of the RHS of this equality
142 size_t d_nodeId
: BitSizeTraits::id_bits
;
144 size_t d_nextId
: BitSizeTraits::id_bits
;
148 EqualityEdge(size_t nodeId
= BitSizeTraits::id_null
, size_t nextId
= BitSizeTraits::id_null
)
149 : d_nodeId(nodeId
), d_nextId(nextId
) {}
151 /** Returns the id of the next edge */
152 inline size_t getNext() const { return d_nextId
; }
154 /** Returns the id of the target edge node */
155 inline size_t getNodeId() const { return d_nodeId
; }
159 * All the equality edges (twice as many as the number of asserted equalities. If an equality
160 * t1 = t2 is asserted, the edges added are -> t2, -> t1 (in this order). Hance, having the index
161 * of one of the edges you can reconstruct the original equality.
163 std::vector
<EqualityEdge
> d_equalityEdges
;
166 * Map from a node to it's first edge in the equality graph. Edges are added to the front of the
167 * list which makes the insertion/backtracking easy.
169 std::vector
<size_t> d_equalityGraph
;
171 /** Add an edge to the equality graph */
172 inline void addGraphEdge(size_t t1
, size_t t2
);
174 /** Returns the equality node of the given node */
175 inline EqualityNode
& getEqualityNode(TNode node
);
177 /** Returns the equality node of the given node */
178 inline EqualityNode
& getEqualityNode(size_t nodeId
);
180 /** Returns the id of the node */
181 inline size_t getNodeId(TNode node
) const;
183 /** Merge the class2 into class1 */
184 void merge(EqualityNode
& class1
, EqualityNode
& class2
, std::vector
<size_t>& triggers
);
186 /** Undo the mereg of class2 into class1 */
187 void undoMerge(EqualityNode
& class1
, EqualityNode
& class2
, size_t class2Id
);
189 /** Backtrack the information if necessary */
193 * Data used in the BFS search through the equality graph.
197 size_t nodeId
: BitSizeTraits::id_bits
;
198 // The index of the edge we traversed
199 size_t edgeId
: BitSizeTraits::id_bits
;
200 // Index in the queue of the previous node. Shouldn't be too much of them, at most the size
201 // of the biggest equivalence class
202 size_t previousIndex
: BitSizeTraits::size_bits
;
204 BfsData(size_t nodeId
= BitSizeTraits::id_null
, size_t edgeId
= BitSizeTraits::id_null
, size_t prev
= 0)
205 : nodeId(nodeId
), edgeId(edgeId
), previousIndex(prev
) {}
209 * Trigger that will be updated
212 /** The current class id of the LHS of the trigger */
213 size_t classId
: BitSizeTraits::id_bits
;
214 /** Next trigger for class 1 */
215 size_t nextTrigger
: BitSizeTraits::id_bits
;
217 Trigger(size_t classId
, size_t nextTrigger
)
218 : classId(classId
), nextTrigger(nextTrigger
) {}
222 * Vector of triggers (persistent and not-backtrackable). Triggers come in pairs for an
223 * equality trigger (t1, t2): one at position 2k for t1, and one at position 2k + 1 for t2. When
224 * updating triggers we always know where the other one is (^1).
226 std::vector
<Trigger
> d_equalityTriggers
;
229 * Trigger lists per node. The begin id changes as we merge, but the end always points to
230 * the acutal end of the triggers for this node.
232 std::vector
<size_t> d_nodeTriggers
;
235 * Adds the trigger with triggerId to the begining of the trigger list of the node with id nodeId.
237 inline void addTriggerToList(size_t nodeId
, size_t triggerId
);
242 * Initialize the equalty engine, given the owning class. This will initialize the notifier with
243 * the owner information.
245 EqualityEngine(OwnerClass
& owner
, context::Context
* context
)
246 : d_notify(owner
), d_assertedEqualitiesCount(context
, 0) {
247 Debug("equality") << "EqualityEdge::EqualityEdge(): id_null = " << BitSizeTraits::id_null
<<
248 ", trigger_id_null = " << BitSizeTraits::trigger_id_null
<< std::endl
;
252 * Adds a term to the term database. Returns the internal id of the term.
254 size_t addTerm(TNode t
);
257 * Check whether the node is already in the database.
259 inline bool hasTerm(TNode t
) const;
262 * Adds an equality t1 = t2 to the database. Returns false if any of the triggers failed.
264 bool addEquality(TNode t1
, TNode t2
);
267 * Returns the representative of the term t.
269 inline TNode
getRepresentative(TNode t
) const;
272 * Returns true if the two nodes are in the same class.
274 inline bool areEqual(TNode t1
, TNode t2
) const;
277 * Get an explanation of the equality t1 = t2. Returns the asserted equalities that
278 * imply t1 = t2. Returns TNodes as the assertion equalities should be hashed somewhere
279 * else. TODO: mark the phantom equalities (skolems).
281 void getExplanation(TNode t1
, TNode t2
, std::vector
<TNode
>& equalities
) const;
284 * Adds a notify trigger for equality t1 = t2, i.e. when t1 = t2 the notify will be called with
287 size_t addTrigger(TNode t1
, TNode t2
);
291 template <typename OwnerClass
, typename NotifyClass
>
292 size_t EqualityEngine
<OwnerClass
, NotifyClass
>::addTerm(TNode t
) {
294 Debug("equality") << "EqualityEngine::addTerm(" << t
<< ")" << std::endl
;
296 // If term already added, retrurn it's id
297 if (hasTerm(t
)) return getNodeId(t
);
299 // Register the new id of the term
300 size_t newId
= d_nodes
.size();
301 d_nodeIds
[t
] = newId
;
302 // Add the node to it's position
303 d_nodes
.push_back(t
);
304 // Add the trigger list for this node
305 d_nodeTriggers
.push_back(BitSizeTraits::trigger_id_null
);
306 // Add it to the equality graph
307 d_equalityGraph
.push_back(BitSizeTraits::id_null
);
308 // Add the equality node to the nodes
309 if (d_equalityNodes
.size() <= newId
) {
310 d_equalityNodes
.resize(newId
+ 100);
312 d_equalityNodes
[newId
].init(newId
);
313 // Return the id of the term
317 template <typename OwnerClass
, typename NotifyClass
>
318 bool EqualityEngine
<OwnerClass
, NotifyClass
>::hasTerm(TNode t
) const {
319 return d_nodeIds
.find(t
) != d_nodeIds
.end();
322 template <typename OwnerClass
, typename NotifyClass
>
323 size_t EqualityEngine
<OwnerClass
, NotifyClass
>::getNodeId(TNode node
) const {
324 Assert(hasTerm(node
));
325 return (*d_nodeIds
.find(node
)).second
;
328 template <typename OwnerClass
, typename NotifyClass
>
329 EqualityNode
& EqualityEngine
<OwnerClass
, NotifyClass
>::getEqualityNode(TNode t
) {
330 return getEqualityNode(getNodeId(t
));
333 template <typename OwnerClass
, typename NotifyClass
>
334 EqualityNode
& EqualityEngine
<OwnerClass
, NotifyClass
>::getEqualityNode(size_t nodeId
) {
335 Assert(nodeId
< d_equalityNodes
.size());
336 return d_equalityNodes
[nodeId
];
339 template <typename OwnerClass
, typename NotifyClass
>
340 bool EqualityEngine
<OwnerClass
, NotifyClass
>::addEquality(TNode t1
, TNode t2
) {
342 Debug("equality") << "EqualityEngine::addEquality(" << t1
<< "," << t2
<< ")" << std::endl
;
344 // Backtrack if necessary
347 // Add the terms if they are not already in the database
348 size_t t1Id
= getNodeId(t1
);
349 size_t t2Id
= getNodeId(t2
);
351 // Get the representatives
352 size_t t1classId
= getEqualityNode(t1Id
).getFind();
353 size_t t2classId
= getEqualityNode(t2Id
).getFind();
355 // If already the same, we're done
356 if (t1classId
== t2classId
) return true;
358 // Get the nodes of the representatives
359 EqualityNode
& node1
= getEqualityNode(t1classId
);
360 EqualityNode
& node2
= getEqualityNode(t2classId
);
362 Assert(node1
.getFind() == t1classId
);
363 Assert(node2
.getFind() == t2classId
);
365 // Depending on the size, merge them
366 std::vector
<size_t> triggers
;
367 if (node1
.getSize() < node2
.getSize()) {
368 merge(node2
, node1
, triggers
);
369 d_assertedEqualities
.push_back(Equality(t2classId
, t1classId
));
371 merge(node1
, node2
, triggers
);
372 d_assertedEqualities
.push_back(Equality(t1classId
, t2classId
));
375 // Add the actuall equality to the equality graph
376 addGraphEdge(t1Id
, t2Id
);
378 // One more equality added
379 d_assertedEqualitiesCount
= d_assertedEqualitiesCount
+ 1;
381 Assert(2*d_assertedEqualities
.size() == d_equalityEdges
.size());
382 Assert(d_assertedEqualities
.size() == d_assertedEqualitiesCount
);
384 // Notify the triggers
385 for (size_t i
= 0, i_end
= triggers
.size(); i
< i_end
; ++ i
) {
386 // Notify the trigger and exit if it fails
387 if (!d_notify(triggers
[i
])) return false;
393 template <typename OwnerClass
, typename NotifyClass
>
394 TNode EqualityEngine
<OwnerClass
, NotifyClass
>::getRepresentative(TNode t
) const {
396 Debug("equality") << "EqualityEngine::getRepresentative(" << t
<< ")" << std::endl
;
400 // Both following commands are semantically const
401 const_cast<EqualityEngine
*>(this)->backtrack();
402 size_t representativeId
= const_cast<EqualityEngine
*>(this)->getEqualityNode(t
).getFind();
404 Debug("equality") << "EqualityEngine::getRepresentative(" << t
<< ") => " << d_nodes
[representativeId
] << std::endl
;
406 return d_nodes
[representativeId
];
409 template <typename OwnerClass
, typename NotifyClass
>
410 bool EqualityEngine
<OwnerClass
, NotifyClass
>::areEqual(TNode t1
, TNode t2
) const {
411 Debug("equality") << "EqualityEngine::areEqual(" << t1
<< "," << t2
<< ")" << std::endl
;
416 // Both following commands are semantically const
417 const_cast<EqualityEngine
*>(this)->backtrack();
418 size_t rep1
= const_cast<EqualityEngine
*>(this)->getEqualityNode(t1
).getFind();
419 size_t rep2
= const_cast<EqualityEngine
*>(this)->getEqualityNode(t2
).getFind();
421 Debug("equality") << "EqualityEngine::areEqual(" << t1
<< "," << t2
<< ") => " << (rep1
== rep2
? "true" : "false") << std::endl
;
426 template <typename OwnerClass
, typename NotifyClass
>
427 void EqualityEngine
<OwnerClass
, NotifyClass
>::merge(EqualityNode
& class1
, EqualityNode
& class2
, std::vector
<size_t>& triggers
) {
429 Debug("equality") << "EqualityEngine::merge(" << class1
.getFind() << "," << class2
.getFind() << ")" << std::endl
;
431 Assert(triggers
.empty());
433 size_t class1Id
= class1
.getFind();
434 size_t class2Id
= class2
.getFind();
436 // Update class2 representative information
437 size_t currentId
= class2Id
;
439 // Get the current node
440 EqualityNode
& currentNode
= getEqualityNode(currentId
);
442 // Update it's find to class1 id
443 currentNode
.setFind(class1Id
);
445 // Go through the triggers and inform if necessary
446 size_t currentTrigger
= d_nodeTriggers
[currentId
];
447 while (currentTrigger
!= BitSizeTraits::trigger_id_null
) {
448 Trigger
& trigger
= d_equalityTriggers
[currentTrigger
];
449 Trigger
& otherTrigger
= d_equalityTriggers
[currentTrigger
^ 1];
451 // If the two are not already in the same class
452 if (otherTrigger
.classId
!= trigger
.classId
) {
453 trigger
.classId
= class1Id
;
454 // If they became the same, call the trigger
455 if (otherTrigger
.classId
== class1Id
) {
456 // Id of the real trigger is half the internal one
457 triggers
.push_back(currentTrigger
>> 1);
461 // Go to the next trigger
462 currentTrigger
= trigger
.nextTrigger
;
465 // Move to the next node
466 currentId
= currentNode
.getNext();
468 } while (currentId
!= class2Id
);
470 // Now merge the lists
471 class1
.merge
<true>(class2
);
474 template <typename OwnerClass
, typename NotifyClass
>
475 void EqualityEngine
<OwnerClass
, NotifyClass
>::undoMerge(EqualityNode
& class1
, EqualityNode
& class2
, size_t class2Id
) {
477 Debug("equality") << "EqualityEngine::undoMerge(" << class1
.getFind() << "," << class2Id
<< ")" << std::endl
;
479 // Now unmerge the lists (same as merge)
480 class1
.merge
<false>(class2
);
482 // Update class2 representative information
483 size_t currentId
= class2Id
;
485 // Get the current node
486 EqualityNode
& currentNode
= getEqualityNode(currentId
);
488 // Update it's find to class1 id
489 currentNode
.setFind(class2Id
);
491 // Go through the trigger list (if any) and undo the class
492 size_t currentTrigger
= d_nodeTriggers
[currentId
];
493 while (currentTrigger
!= BitSizeTraits::trigger_id_null
) {
494 Trigger
& trigger
= d_equalityTriggers
[currentTrigger
];
495 trigger
.classId
= class2Id
;
496 currentTrigger
= trigger
.nextTrigger
;
499 // Move to the next node
500 currentId
= currentNode
.getNext();
502 } while (currentId
!= class2Id
);
506 template <typename OwnerClass
, typename NotifyClass
>
507 void EqualityEngine
<OwnerClass
, NotifyClass
>::backtrack() {
509 // If we need to backtrack then do it
510 if (d_assertedEqualitiesCount
< d_assertedEqualities
.size()) {
512 Debug("equality") << "EqualityEngine::backtrack(): nodes" << std::endl
;
514 for (int i
= (int)d_assertedEqualities
.size() - 1, i_end
= (int)d_assertedEqualitiesCount
; i
>= i_end
; --i
) {
515 // Get the ids of the merged classes
516 Equality
& eq
= d_assertedEqualities
[i
];
518 undoMerge(d_equalityNodes
[eq
.lhs
], d_equalityNodes
[eq
.rhs
], eq
.rhs
);
521 d_assertedEqualities
.resize(d_assertedEqualitiesCount
);
523 Debug("equality") << "EqualityEngine::backtrack(): edges" << std::endl
;
525 for (int i
= (int)d_equalityEdges
.size() - 2, i_end
= (int)(2*d_assertedEqualitiesCount
); i
>= i_end
; i
-= 2) {
526 EqualityEdge
& edge1
= d_equalityEdges
[i
];
527 EqualityEdge
& edge2
= d_equalityEdges
[i
| 1];
528 d_equalityGraph
[edge2
.getNodeId()] = edge1
.getNext();
529 d_equalityGraph
[edge1
.getNodeId()] = edge2
.getNext();
532 d_equalityEdges
.resize(2 * d_assertedEqualitiesCount
);
537 template <typename OwnerClass
, typename NotifyClass
>
538 void EqualityEngine
<OwnerClass
, NotifyClass
>::addGraphEdge(size_t t1
, size_t t2
) {
539 Debug("equality") << "EqualityEngine::addGraphEdge(" << d_nodes
[t1
] << "," << d_nodes
[t2
] << ")" << std::endl
;
540 size_t edge
= d_equalityEdges
.size();
541 d_equalityEdges
.push_back(EqualityEdge(t2
, d_equalityGraph
[t1
]));
542 d_equalityEdges
.push_back(EqualityEdge(t1
, d_equalityGraph
[t2
]));
543 d_equalityGraph
[t1
] = edge
;
544 d_equalityGraph
[t2
] = edge
| 1;
547 template <typename OwnerClass
, typename NotifyClass
>
548 void EqualityEngine
<OwnerClass
, NotifyClass
>::getExplanation(TNode t1
, TNode t2
, std::vector
<TNode
>& equalities
) const {
549 Assert(equalities
.empty());
551 Assert(getRepresentative(t1
) == getRepresentative(t2
));
553 Debug("equality") << "EqualityEngine::getExplanation(" << t1
<< "," << t2
<< ")" << std::endl
;
555 const_cast<EqualityEngine
*>(this)->backtrack();
557 // Queue for the BFS containing nodes
558 std::vector
<BfsData
> bfsQueue
;
560 // The id's of the nodes
561 size_t t1Id
= getNodeId(t1
);
562 size_t t2Id
= getNodeId(t2
);
564 // Find a path from t1 to t2 in the graph (BFS)
565 bfsQueue
.push_back(BfsData(t1Id
, BitSizeTraits::id_null
, 0));
566 size_t currentIndex
= 0;
568 // There should always be a path, and every node can be visited only once (tree)
569 Assert(currentIndex
< bfsQueue
.size());
571 // The next node to visit
572 BfsData
& current
= bfsQueue
[currentIndex
];
573 size_t currentNode
= current
.nodeId
;
575 Debug("equality") << "EqualityEngine::getExplanation(): currentNode = " << d_nodes
[currentNode
] << std::endl
;
577 // Go through the equality edges of this node
578 size_t currentEdge
= d_equalityGraph
[currentNode
];
580 while (currentEdge
!= BitSizeTraits::id_null
) {
582 const EqualityEdge
& edge
= d_equalityEdges
[currentEdge
];
584 // If not just the backwards edge
585 if ((currentEdge
| 1u) != (current
.edgeId
| 1u)) {
587 Debug("equality") << "EqualityEngine::getExplanation(): currentEdge = (" << d_nodes
[currentNode
] << "," << d_nodes
[edge
.getNodeId()] << ")" << std::endl
;
589 // Did we find the path
590 if (edge
.getNodeId() == t2Id
) {
592 Debug("equality") << "EqualityEngine::getExplanation(): path found: " << std::endl
;
594 // Reconstruct the path
596 // Get the left and right hand side from the edge
597 size_t firstEdge
= (currentEdge
>> 1) << 1;
598 size_t secondEdge
= (currentEdge
| 1);
599 TNode lhs
= d_nodes
[d_equalityEdges
[secondEdge
].getNodeId()];
600 TNode rhs
= d_nodes
[d_equalityEdges
[firstEdge
].getNodeId()];
601 // Add the actual equality to the vector
602 equalities
.push_back(lhs
.eqNode(rhs
));
604 Debug("equality") << "EqualityEngine::getExplanation(): adding: " << lhs
.eqNode(rhs
) << std::endl
;
606 // Go to the previous
607 currentEdge
= bfsQueue
[currentIndex
].edgeId
;
608 currentIndex
= bfsQueue
[currentIndex
].previousIndex
;
609 } while (currentEdge
!= BitSizeTraits::id_null
);
615 // Push to the visitation queue if it's not the backward edge
616 bfsQueue
.push_back(BfsData(edge
.getNodeId(), currentEdge
, currentIndex
));
619 // Go to the next edge
620 currentEdge
= edge
.getNext();
623 // Go to the next node to visit
628 template <typename OwnerClass
, typename NotifyClass
>
629 size_t EqualityEngine
<OwnerClass
, NotifyClass
>::addTrigger(TNode t1
, TNode t2
) {
631 Debug("equality") << "EqualityEngine::addTrigger(" << t1
<< "," << t2
<< ")" << std::endl
;
636 // Get the information about t1
637 size_t t1Id
= getNodeId(t1
);
638 size_t t1TriggerId
= d_nodeTriggers
[t1Id
];
639 size_t t1classId
= getEqualityNode(t1Id
).getFind();
641 // Get the information about t2
642 size_t t2Id
= getNodeId(t2
);
643 size_t t2TriggerId
= d_nodeTriggers
[t2Id
];
644 size_t t2classId
= getEqualityNode(t2Id
).getFind();
646 // Create the triggers
647 size_t t1NewTriggerId
= d_equalityTriggers
.size();
648 size_t t2NewTriggerId
= t1NewTriggerId
| 1;
649 d_equalityTriggers
.push_back(Trigger(t1classId
, t1TriggerId
));
650 d_equalityTriggers
.push_back(Trigger(t2classId
, t2TriggerId
));
652 // Add the trigger to the trigger graph
653 d_nodeTriggers
[t1Id
] = t1NewTriggerId
;
654 d_nodeTriggers
[t2Id
] = t2NewTriggerId
;
656 Debug("equality") << "EqualityEngine::addTrigger(" << t1
<< "," << t2
<< ") => " << t1NewTriggerId
/ 2 << std::endl
;
658 // Return the global id of the trigger
659 return t1NewTriggerId
/ 2;
663 } // Namespace theory