1 /********************* */
2 /*! \file equality_engine.cpp
3 ** \verbatim
4 ** Top contributors (to current version):
5 ** Dejan Jovanovic, Guy Katz, Andrew Reynolds
6 ** This file is part of the CVC4 project.
7 ** Copyright (c) 2009-2019 by the authors listed in the file AUTHORS
8 ** in the top-level source directory) and their institutional affiliations.
9 ** All rights reserved. See the file COPYING in the top-level source
10 ** directory for licensing information.\endverbatim
11 **
12 ** \brief [[ Add one-line brief description here ]]
13 **
14 ** [[ Add lengthier description here ]]
15 ** \todo document this file
16 **/
17
18 #include "theory/uf/equality_engine.h"
19
20 #include "smt/smt_statistics_registry.h"
21
22 namespace CVC4 {
23 namespace theory {
24 namespace eq {
25
Statistics(std::string name)26 EqualityEngine::Statistics::Statistics(std::string name)
27 : mergesCount(name + "::mergesCount", 0),
28 termsCount(name + "::termsCount", 0),
29 functionTermsCount(name + "::functionTermsCount", 0),
30 constantTermsCount(name + "::constantTermsCount", 0)
31 {
32 smtStatisticsRegistry()->registerStat(&mergesCount);
33 smtStatisticsRegistry()->registerStat(&termsCount);
34 smtStatisticsRegistry()->registerStat(&functionTermsCount);
35 smtStatisticsRegistry()->registerStat(&constantTermsCount);
36 }
37
~Statistics()38 EqualityEngine::Statistics::~Statistics() {
39 smtStatisticsRegistry()->unregisterStat(&mergesCount);
40 smtStatisticsRegistry()->unregisterStat(&termsCount);
41 smtStatisticsRegistry()->unregisterStat(&functionTermsCount);
42 smtStatisticsRegistry()->unregisterStat(&constantTermsCount);
43 }
44
45 /**
46 * Data used in the BFS search through the equality graph.
47 */
48 struct BfsData {
49 // The current node
50 EqualityNodeId nodeId;
51 // The index of the edge we traversed
52 EqualityEdgeId edgeId;
53 // Index in the queue of the previous node. Shouldn't be too much of them, at most the size
54 // of the biggest equivalence class
55 size_t previousIndex;
56
BfsDataCVC4::theory::eq::BfsData57 BfsData(EqualityNodeId nodeId = null_id, EqualityEdgeId edgeId = null_edge, size_t prev = 0)
58 : nodeId(nodeId), edgeId(edgeId), previousIndex(prev) {}
59 };
60
61 class ScopedBool {
62 bool& watch;
63 bool oldValue;
64 public:
ScopedBool(bool & watch,bool newValue)65 ScopedBool(bool& watch, bool newValue)
66 : watch(watch), oldValue(watch) {
67 watch = newValue;
68 }
~ScopedBool()69 ~ScopedBool() {
70 watch = oldValue;
71 }
72 };
73
74 EqualityEngineNotifyNone EqualityEngine::s_notifyNone;
75
init()76 void EqualityEngine::init() {
77 Debug("equality") << "EqualityEdge::EqualityEngine(): id_null = " << +null_id << std::endl;
78 Debug("equality") << "EqualityEdge::EqualityEngine(): edge_null = " << +null_edge << std::endl;
79 Debug("equality") << "EqualityEdge::EqualityEngine(): trigger_null = " << +null_trigger << std::endl;
80
81 d_true = NodeManager::currentNM()->mkConst<bool>(true);
82 d_false = NodeManager::currentNM()->mkConst<bool>(false);
83
84 d_triggerDatabaseAllocatedSize = 100000;
85 d_triggerDatabase = (char*) malloc(d_triggerDatabaseAllocatedSize);
86
87 //We can't notify during the initialization because it notifies
88 // QuantifiersEngine.AddTermToDatabase that try to access to the uf
89 // instantiator that currently doesn't exist.
90 ScopedBool sb(d_performNotify, false);
91 addTermInternal(d_true);
92 addTermInternal(d_false);
93
94 d_trueId = getNodeId(d_true);
95 d_falseId = getNodeId(d_false);
96
97 d_freshMergeReasonType = eq::NUMBER_OF_MERGE_REASONS;
98 }
99
~EqualityEngine()100 EqualityEngine::~EqualityEngine() {
101 free(d_triggerDatabase);
102 }
103
104
EqualityEngine(context::Context * context,std::string name,bool constantsAreTriggers)105 EqualityEngine::EqualityEngine(context::Context* context, std::string name, bool constantsAreTriggers)
106 : ContextNotifyObj(context)
107 , d_masterEqualityEngine(0)
108 , d_context(context)
109 , d_done(context, false)
110 , d_performNotify(true)
111 , d_notify(s_notifyNone)
112 , d_applicationLookupsCount(context, 0)
113 , d_nodesCount(context, 0)
114 , d_assertedEqualitiesCount(context, 0)
115 , d_equalityTriggersCount(context, 0)
116 , d_subtermEvaluatesSize(context, 0)
117 , d_stats(name)
118 , d_inPropagate(false)
119 , d_constantsAreTriggers(constantsAreTriggers)
120 , d_triggerDatabaseSize(context, 0)
121 , d_triggerTermSetUpdatesSize(context, 0)
122 , d_deducedDisequalitiesSize(context, 0)
123 , d_deducedDisequalityReasonsSize(context, 0)
124 , d_propagatedDisequalities(context)
125 , d_name(name)
126 {
127 init();
128 }
129
EqualityEngine(EqualityEngineNotify & notify,context::Context * context,std::string name,bool constantsAreTriggers)130 EqualityEngine::EqualityEngine(EqualityEngineNotify& notify, context::Context* context, std::string name, bool constantsAreTriggers)
131 : ContextNotifyObj(context)
132 , d_masterEqualityEngine(0)
133 , d_context(context)
134 , d_done(context, false)
135 , d_performNotify(true)
136 , d_notify(notify)
137 , d_applicationLookupsCount(context, 0)
138 , d_nodesCount(context, 0)
139 , d_assertedEqualitiesCount(context, 0)
140 , d_equalityTriggersCount(context, 0)
141 , d_subtermEvaluatesSize(context, 0)
142 , d_stats(name)
143 , d_inPropagate(false)
144 , d_constantsAreTriggers(constantsAreTriggers)
145 , d_triggerDatabaseSize(context, 0)
146 , d_triggerTermSetUpdatesSize(context, 0)
147 , d_deducedDisequalitiesSize(context, 0)
148 , d_deducedDisequalityReasonsSize(context, 0)
149 , d_propagatedDisequalities(context)
150 , d_name(name)
151 {
152 init();
153 }
154
setMasterEqualityEngine(EqualityEngine * master)155 void EqualityEngine::setMasterEqualityEngine(EqualityEngine* master) {
156 Assert(d_masterEqualityEngine == 0);
157 d_masterEqualityEngine = master;
158 }
159
enqueue(const MergeCandidate & candidate,bool back)160 void EqualityEngine::enqueue(const MergeCandidate& candidate, bool back) {
161 Debug("equality") << d_name << "::eq::enqueue(" << d_nodes[candidate.t1Id] << ", " << d_nodes[candidate.t2Id] << ", " << candidate.type << "). reason: " << candidate.reason << std::endl;
162 if (back) {
163 d_propagationQueue.push_back(candidate);
164 } else {
165 d_propagationQueue.push_front(candidate);
166 }
167 }
168
newApplicationNode(TNode original,EqualityNodeId t1,EqualityNodeId t2,FunctionApplicationType type)169 EqualityNodeId EqualityEngine::newApplicationNode(TNode original, EqualityNodeId t1, EqualityNodeId t2, FunctionApplicationType type) {
170 Debug("equality") << d_name << "::eq::newApplicationNode(" << original << ", " << t1 << ", " << t2 << ")" << std::endl;
171
172 ++ d_stats.functionTermsCount;
173
174 // Get another id for this
175 EqualityNodeId funId = newNode(original);
176 FunctionApplication funOriginal(type, t1, t2);
177 // The function application we're creating
178 EqualityNodeId t1ClassId = getEqualityNode(t1).getFind();
179 EqualityNodeId t2ClassId = getEqualityNode(t2).getFind();
180 FunctionApplication funNormalized(type, t1ClassId, t2ClassId);
181
182 // We add the original version
183 d_applications[funId] = FunctionApplicationPair(funOriginal, funNormalized);
184
185 // Add the lookup data, if it's not already there
186 ApplicationIdsMap::iterator find = d_applicationLookup.find(funNormalized);
187 if (find == d_applicationLookup.end()) {
188 Debug("equality") << d_name << "::eq::newApplicationNode(" << original << ", " << t1 << ", " << t2 << "): no lookup, setting up" << std::endl;
189 // Mark the normalization to the lookup
190 storeApplicationLookup(funNormalized, funId);
191 } else {
192 // If it's there, we need to merge these two
193 Debug("equality") << d_name << "::eq::newApplicationNode(" << original << ", " << t1 << ", " << t2 << "): lookup exists, adding to queue" << std::endl;
194 Debug("equality") << d_name << "::eq::newApplicationNode(" << original << ", " << t1 << ", " << t2 << "): lookup = " << d_nodes[find->second] << std::endl;
195 enqueue(MergeCandidate(funId, find->second, MERGED_THROUGH_CONGRUENCE, TNode::null()));
196 }
197
198 // Add to the use lists
199 Debug("equality") << d_name << "::eq::newApplicationNode(" << original << ", " << t1 << ", " << t2 << "): adding " << original << " to the uselist of " << d_nodes[t1] << std::endl;
200 d_equalityNodes[t1].usedIn(funId, d_useListNodes);
201 Debug("equality") << d_name << "::eq::newApplicationNode(" << original << ", " << t1 << ", " << t2 << "): adding " << original << " to the uselist of " << d_nodes[t2] << std::endl;
202 d_equalityNodes[t2].usedIn(funId, d_useListNodes);
203
204 // Return the new id
205 Debug("equality") << d_name << "::eq::newApplicationNode(" << original << ", " << t1 << ", " << t2 << ") => " << funId << std::endl;
206
207 return funId;
208 }
209
newNode(TNode node)210 EqualityNodeId EqualityEngine::newNode(TNode node) {
211
212 Debug("equality") << d_name << "::eq::newNode(" << node << ")" << std::endl;
213
214 ++ d_stats.termsCount;
215
216 // Register the new id of the term
217 EqualityNodeId newId = d_nodes.size();
218 d_nodeIds[node] = newId;
219 // Add the node to it's position
220 d_nodes.push_back(node);
221 // Note if this is an application or not
222 d_applications.push_back(FunctionApplicationPair());
223 // Add the trigger list for this node
224 d_nodeTriggers.push_back(+null_trigger);
225 // Add it to the equality graph
226 d_equalityGraph.push_back(+null_edge);
227 // Mark the no-individual trigger
228 d_nodeIndividualTrigger.push_back(+null_set_id);
229 // Mark non-constant by default
230 d_isConstant.push_back(false);
231 // No terms to evaluate by defaul
232 d_subtermsToEvaluate.push_back(0);
233 // Mark equality nodes
234 d_isEquality.push_back(false);
235 // Mark the node as internal by default
236 d_isInternal.push_back(true);
237 // Add the equality node to the nodes
238 d_equalityNodes.push_back(EqualityNode(newId));
239
240 // Increase the counters
241 d_nodesCount = d_nodesCount + 1;
242
243 Debug("equality") << d_name << "::eq::newNode(" << node << ") => " << newId << std::endl;
244
245 // notify e.g. the UF theory strong solver
246 if (d_performNotify) {
247 d_notify.eqNotifyNewClass(node);
248 }
249
250 return newId;
251 }
252
addFunctionKind(Kind fun,bool interpreted,bool extOperator)253 void EqualityEngine::addFunctionKind(Kind fun, bool interpreted, bool extOperator) {
254 d_congruenceKinds |= fun;
255 if (fun != kind::EQUAL) {
256 if (interpreted) {
257 Debug("equality::evaluation") << d_name << "::eq::addFunctionKind(): " << fun << " is interpreted " << std::endl;
258 d_congruenceKindsInterpreted |= fun;
259 }
260 if (extOperator) {
261 Debug("equality::extoperator") << d_name << "::eq::addFunctionKind(): " << fun << " is an external operator kind " << std::endl;
262 d_congruenceKindsExtOperators |= fun;
263 }
264 }
265 }
266
subtermEvaluates(EqualityNodeId id)267 void EqualityEngine::subtermEvaluates(EqualityNodeId id) {
268 Debug("equality::evaluation") << d_name << "::eq::subtermEvaluates(" << d_nodes[id] << "): " << d_subtermsToEvaluate[id] << std::endl;
269 Assert(!d_isInternal[id]);
270 Assert(d_subtermsToEvaluate[id] > 0);
271 if ((-- d_subtermsToEvaluate[id]) == 0) {
272 d_evaluationQueue.push(id);
273 }
274 d_subtermEvaluates.push_back(id);
275 d_subtermEvaluatesSize = d_subtermEvaluates.size();
276 Debug("equality::evaluation") << d_name << "::eq::subtermEvaluates(" << d_nodes[id] << "): new " << d_subtermsToEvaluate[id] << std::endl;
277 }
278
addTermInternal(TNode t,bool isOperator)279 void EqualityEngine::addTermInternal(TNode t, bool isOperator) {
280
281 Debug("equality") << d_name << "::eq::addTermInternal(" << t << ")" << std::endl;
282
283 // If there already, we're done
284 if (hasTerm(t)) {
285 Debug("equality") << d_name << "::eq::addTermInternal(" << t << "): already there" << std::endl;
286 return;
287 }
288
289 if (d_done) {
290 return;
291 }
292
293 EqualityNodeId result;
294
295 if (t.getKind() == kind::EQUAL) {
296 addTermInternal(t[0]);
297 addTermInternal(t[1]);
298 EqualityNodeId t0id = getNodeId(t[0]);
299 EqualityNodeId t1id = getNodeId(t[1]);
300 result = newApplicationNode(t, t0id, t1id, APP_EQUALITY);
301 d_isInternal[result] = false;
302 d_isConstant[result] = false;
303 } else if (t.getNumChildren() > 0 && d_congruenceKinds[t.getKind()]) {
304 TNode tOp = t.getOperator();
305 // Add the operator
306 addTermInternal(tOp, !isExternalOperatorKind(t.getKind()));
307 result = getNodeId(tOp);
308 // Add all the children and Curryfy
309 bool isInterpreted = isInterpretedFunctionKind(t.getKind());
310 for (unsigned i = 0; i < t.getNumChildren(); ++ i) {
311 // Add the child
312 addTermInternal(t[i]);
313 EqualityNodeId tiId = getNodeId(t[i]);
314 // Add the application
315 result = newApplicationNode(t, result, tiId, isInterpreted ? APP_INTERPRETED : APP_UNINTERPRETED);
316 }
317 d_isInternal[result] = false;
318 d_isConstant[result] = t.isConst();
319 // If interpreted, set the number of non-interpreted children
320 if (isInterpreted) {
321 // How many children are not constants yet
322 d_subtermsToEvaluate[result] = t.getNumChildren();
323 for (unsigned i = 0; i < t.getNumChildren(); ++ i) {
324 if (isConstant(getNodeId(t[i]))) {
325 Debug("equality::evaluation") << d_name << "::eq::addTermInternal(" << t << "): evaluates " << t[i] << std::endl;
326 subtermEvaluates(result);
327 }
328 }
329 }
330 } else {
331 // Otherwise we just create the new id
332 result = newNode(t);
333 // Is this an operator
334 d_isInternal[result] = isOperator;
335 d_isConstant[result] = !isOperator && t.isConst();
336 }
337
338 if (t.getKind() == kind::EQUAL) {
339 // We set this here as this only applies to actual terms, not the
340 // intermediate application terms
341 d_isEquality[result] = true;
342 } else if (d_constantsAreTriggers && d_isConstant[result]) {
343 // Non-Boolean constants are trigger terms for all tags
344 EqualityNodeId tId = getNodeId(t);
345 // Setup the new set
346 Theory::Set newSetTags = 0;
347 EqualityNodeId newSetTriggers[THEORY_LAST];
348 unsigned newSetTriggersSize = THEORY_LAST;
349 for (TheoryId currentTheory = THEORY_FIRST; currentTheory != THEORY_LAST; ++ currentTheory) {
350 newSetTags = Theory::setInsert(currentTheory, newSetTags);
351 newSetTriggers[currentTheory] = tId;
352 }
353 // Add it to the list for backtracking
354 d_triggerTermSetUpdates.push_back(TriggerSetUpdate(tId, null_set_id));
355 d_triggerTermSetUpdatesSize = d_triggerTermSetUpdatesSize + 1;
356 // Mark the the new set as a trigger
357 d_nodeIndividualTrigger[tId] = newTriggerTermSet(newSetTags, newSetTriggers, newSetTriggersSize);
358 }
359
360 // If this is not an internal node, add it to the master
361 if (d_masterEqualityEngine && !d_isInternal[result]) {
362 d_masterEqualityEngine->addTermInternal(t);
363 }
364
365 // Empty the queue
366 propagate();
367
368 Assert(hasTerm(t));
369
370 Debug("equality") << d_name << "::eq::addTermInternal(" << t << ") => " << result << std::endl;
371 }
372
hasTerm(TNode t) const373 bool EqualityEngine::hasTerm(TNode t) const {
374 return d_nodeIds.find(t) != d_nodeIds.end();
375 }
376
getNodeId(TNode node) const377 EqualityNodeId EqualityEngine::getNodeId(TNode node) const {
378 Assert(hasTerm(node), node.toString().c_str());
379 return (*d_nodeIds.find(node)).second;
380 }
381
getEqualityNode(TNode t)382 EqualityNode& EqualityEngine::getEqualityNode(TNode t) {
383 return getEqualityNode(getNodeId(t));
384 }
385
getEqualityNode(EqualityNodeId nodeId)386 EqualityNode& EqualityEngine::getEqualityNode(EqualityNodeId nodeId) {
387 Assert(nodeId < d_equalityNodes.size());
388 return d_equalityNodes[nodeId];
389 }
390
getEqualityNode(TNode t) const391 const EqualityNode& EqualityEngine::getEqualityNode(TNode t) const {
392 return getEqualityNode(getNodeId(t));
393 }
394
getEqualityNode(EqualityNodeId nodeId) const395 const EqualityNode& EqualityEngine::getEqualityNode(EqualityNodeId nodeId) const {
396 Assert(nodeId < d_equalityNodes.size());
397 return d_equalityNodes[nodeId];
398 }
399
assertEqualityInternal(TNode t1,TNode t2,TNode reason,unsigned pid)400 void EqualityEngine::assertEqualityInternal(TNode t1, TNode t2, TNode reason, unsigned pid) {
401
402 Debug("equality") << d_name << "::eq::addEqualityInternal(" << t1 << "," << t2 << "), reason = " << reason << ", pid = " << pid << std::endl;
403
404 if (d_done) {
405 return;
406 }
407
408 // Add the terms if they are not already in the database
409 addTermInternal(t1);
410 addTermInternal(t2);
411
412 // Add to the queue and propagate
413 EqualityNodeId t1Id = getNodeId(t1);
414 EqualityNodeId t2Id = getNodeId(t2);
415 enqueue(MergeCandidate(t1Id, t2Id, pid, reason));
416 }
417
assertPredicate(TNode t,bool polarity,TNode reason,unsigned pid)418 void EqualityEngine::assertPredicate(TNode t, bool polarity, TNode reason, unsigned pid) {
419 Debug("equality") << d_name << "::eq::addPredicate(" << t << "," << (polarity ? "true" : "false") << ")" << std::endl;
420 Assert(t.getKind() != kind::EQUAL, "Use assertEquality instead");
421 assertEqualityInternal(t, polarity ? d_true : d_false, reason, pid);
422 propagate();
423 }
424
mergePredicates(TNode p,TNode q,TNode reason)425 void EqualityEngine::mergePredicates(TNode p, TNode q, TNode reason) {
426 Debug("equality") << d_name << "::eq::mergePredicates(" << p << "," << q << ")" << std::endl;
427 assertEqualityInternal(p, q, reason);
428 propagate();
429 }
430
assertEquality(TNode eq,bool polarity,TNode reason,unsigned pid)431 void EqualityEngine::assertEquality(TNode eq, bool polarity, TNode reason, unsigned pid) {
432 Debug("equality") << d_name << "::eq::addEquality(" << eq << "," << (polarity ? "true" : "false") << ")" << std::endl;
433 if (polarity) {
434 // If two terms are already equal, don't assert anything
435 if (hasTerm(eq[0]) && hasTerm(eq[1]) && areEqual(eq[0], eq[1])) {
436 return;
437 }
438 // Add equality between terms
439 assertEqualityInternal(eq[0], eq[1], reason, pid);
440 propagate();
441 } else {
442 // If two terms are already dis-equal, don't assert anything
443 if (hasTerm(eq[0]) && hasTerm(eq[1]) && areDisequal(eq[0], eq[1], false)) {
444 return;
445 }
446
447 // notify the theory
448 if (d_performNotify) {
449 d_notify.eqNotifyDisequal(eq[0], eq[1], reason);
450 }
451
452 Debug("equality::trigger") << d_name << "::eq::addEquality(" << eq << "," << (polarity ? "true" : "false") << ")" << std::endl;
453
454 assertEqualityInternal(eq, d_false, reason, pid);
455 propagate();
456
457 if (d_done) {
458 return;
459 }
460
461 // If both have constant representatives, we don't notify anyone
462 EqualityNodeId a = getNodeId(eq[0]);
463 EqualityNodeId b = getNodeId(eq[1]);
464 EqualityNodeId aClassId = getEqualityNode(a).getFind();
465 EqualityNodeId bClassId = getEqualityNode(b).getFind();
466 if (d_isConstant[aClassId] && d_isConstant[bClassId]) {
467 return;
468 }
469
470 // If we are adding a disequality, notify of the shared term representatives
471 EqualityNodeId eqId = getNodeId(eq);
472 TriggerTermSetRef aTriggerRef = d_nodeIndividualTrigger[aClassId];
473 TriggerTermSetRef bTriggerRef = d_nodeIndividualTrigger[bClassId];
474 if (aTriggerRef != +null_set_id && bTriggerRef != +null_set_id) {
475 Debug("equality::trigger") << d_name << "::eq::addEquality(" << eq << "," << (polarity ? "true" : "false") << ": have triggers" << std::endl;
476 // The sets of trigger terms
477 TriggerTermSet& aTriggerTerms = getTriggerTermSet(aTriggerRef);
478 TriggerTermSet& bTriggerTerms = getTriggerTermSet(bTriggerRef);
479 // Go through and notify the shared dis-equalities
480 Theory::Set aTags = aTriggerTerms.tags;
481 Theory::Set bTags = bTriggerTerms.tags;
482 TheoryId aTag = Theory::setPop(aTags);
483 TheoryId bTag = Theory::setPop(bTags);
484 int a_i = 0, b_i = 0;
485 while (aTag != THEORY_LAST && bTag != THEORY_LAST) {
486 if (aTag < bTag) {
487 aTag = Theory::setPop(aTags);
488 ++ a_i;
489 } else if (aTag > bTag) {
490 bTag = Theory::setPop(bTags);
491 ++ b_i;
492 } else {
493 // Same tags, notify
494 EqualityNodeId aSharedId = aTriggerTerms.triggers[a_i++];
495 EqualityNodeId bSharedId = bTriggerTerms.triggers[b_i++];
496 // Propagate
497 if (!hasPropagatedDisequality(aTag, aSharedId, bSharedId)) {
498 // Store a proof if not there already
499 if (!hasPropagatedDisequality(aSharedId, bSharedId)) {
500 d_deducedDisequalityReasons.push_back(EqualityPair(aSharedId, a));
501 d_deducedDisequalityReasons.push_back(EqualityPair(bSharedId, b));
502 d_deducedDisequalityReasons.push_back(EqualityPair(eqId, d_falseId));
503 }
504 // Store the propagation
505 storePropagatedDisequality(aTag, aSharedId, bSharedId);
506 // Notify
507 Debug("equality::trigger") << d_name << "::eq::addEquality(" << eq << "," << (polarity ? "true" : "false") << ": notifying " << aTag << " for " << d_nodes[aSharedId] << " != " << d_nodes[bSharedId] << std::endl;
508 if (!d_notify.eqNotifyTriggerTermEquality(aTag, d_nodes[aSharedId], d_nodes[bSharedId], false)) {
509 break;
510 }
511 }
512 // Pop the next tags
513 aTag = Theory::setPop(aTags);
514 bTag = Theory::setPop(bTags);
515 }
516 }
517 }
518 }
519 }
520
getRepresentative(TNode t) const521 TNode EqualityEngine::getRepresentative(TNode t) const {
522 Debug("equality::internal") << d_name << "::eq::getRepresentative(" << t << ")" << std::endl;
523 Assert(hasTerm(t));
524 EqualityNodeId representativeId = getEqualityNode(t).getFind();
525 Assert(!d_isInternal[representativeId]);
526 Debug("equality::internal") << d_name << "::eq::getRepresentative(" << t << ") => " << d_nodes[representativeId] << std::endl;
527 return d_nodes[representativeId];
528 }
529
merge(EqualityNode & class1,EqualityNode & class2,std::vector<TriggerId> & triggersFired)530 bool EqualityEngine::merge(EqualityNode& class1, EqualityNode& class2, std::vector<TriggerId>& triggersFired) {
531
532 Debug("equality") << d_name << "::eq::merge(" << class1.getFind() << "," << class2.getFind() << ")" << std::endl;
533
534 Assert(triggersFired.empty());
535
536 ++ d_stats.mergesCount;
537
538 EqualityNodeId class1Id = class1.getFind();
539 EqualityNodeId class2Id = class2.getFind();
540
541 Node n1 = d_nodes[class1Id];
542 Node n2 = d_nodes[class2Id];
543 EqualityNode cc1 = getEqualityNode(n1);
544 EqualityNode cc2 = getEqualityNode(n2);
545 bool doNotify = false;
546 // notify the theory
547 // the second part of this check is needed due to the internal implementation of this class.
548 // It ensures that we are merging terms and not operators.
549 if (d_performNotify && class1Id==cc1.getFind() && class2Id==cc2.getFind()) {
550 doNotify = true;
551 }
552 if (doNotify) {
553 d_notify.eqNotifyPreMerge(n1, n2);
554 }
555
556 // Check for constant merges
557 bool class1isConstant = d_isConstant[class1Id];
558 bool class2isConstant = d_isConstant[class2Id];
559 Assert(class1isConstant || !class2isConstant, "Should always merge into constants");
560 Assert(!class1isConstant || !class2isConstant, "Don't merge constants");
561
562 // Trigger set of class 1
563 TriggerTermSetRef class1triggerRef = d_nodeIndividualTrigger[class1Id];
564 Theory::Set class1Tags = class1triggerRef == null_set_id ? 0 : getTriggerTermSet(class1triggerRef).tags;
565 // Trigger set of class 2
566 TriggerTermSetRef class2triggerRef = d_nodeIndividualTrigger[class2Id];
567 Theory::Set class2Tags = class2triggerRef == null_set_id ? 0 : getTriggerTermSet(class2triggerRef).tags;
568
569 // Disequalities coming from class2
570 TaggedEqualitiesSet class2disequalitiesToNotify;
571 // Disequalities coming from class1
572 TaggedEqualitiesSet class1disequalitiesToNotify;
573
574 // Individual tags
575 Theory::Set class1OnlyTags = Theory::setDifference(class1Tags, class2Tags);
576 Theory::Set class2OnlyTags = Theory::setDifference(class2Tags, class1Tags);
577
578 // Only get disequalities if they are not both constant
579 if (!class1isConstant || !class2isConstant) {
580 getDisequalities(!class1isConstant, class2Id, class1OnlyTags, class2disequalitiesToNotify);
581 getDisequalities(!class2isConstant, class1Id, class2OnlyTags, class1disequalitiesToNotify);
582 }
583
584 // Update class2 representative information
585 Debug("equality") << d_name << "::eq::merge(" << class1.getFind() << "," << class2.getFind() << "): updating class " << class2Id << std::endl;
586 EqualityNodeId currentId = class2Id;
587 do {
588 // Get the current node
589 EqualityNode& currentNode = getEqualityNode(currentId);
590
591 // Update it's find to class1 id
592 Debug("equality") << d_name << "::eq::merge(" << class1.getFind() << "," << class2.getFind() << "): " << currentId << "->" << class1Id << std::endl;
593 currentNode.setFind(class1Id);
594
595 // Go through the triggers and inform if necessary
596 TriggerId currentTrigger = d_nodeTriggers[currentId];
597 while (currentTrigger != null_trigger) {
598 Trigger& trigger = d_equalityTriggers[currentTrigger];
599 Trigger& otherTrigger = d_equalityTriggers[currentTrigger ^ 1];
600
601 // If the two are not already in the same class
602 if (otherTrigger.classId != trigger.classId) {
603 trigger.classId = class1Id;
604 // If they became the same, call the trigger
605 if (otherTrigger.classId == class1Id) {
606 // Id of the real trigger is half the internal one
607 triggersFired.push_back(currentTrigger);
608 }
609 }
610
611 // Go to the next trigger
612 currentTrigger = trigger.nextTrigger;
613 }
614
615 // Move to the next node
616 currentId = currentNode.getNext();
617
618 } while (currentId != class2Id);
619
620 // Update class2 table lookup and information if not a boolean
621 // since booleans can't be in an application
622 if (!d_isEquality[class2Id]) {
623 Debug("equality") << d_name << "::eq::merge(" << class1.getFind() << "," << class2.getFind() << "): updating lookups of " << class2Id << std::endl;
624 do {
625 // Get the current node
626 EqualityNode& currentNode = getEqualityNode(currentId);
627 Debug("equality") << d_name << "::eq::merge(" << class1.getFind() << "," << class2.getFind() << "): updating lookups of node " << currentId << std::endl;
628
629 // Go through the uselist and check for congruences
630 UseListNodeId currentUseId = currentNode.getUseList();
631 while (currentUseId != null_uselist_id) {
632 // Get the node of the use list
633 UseListNode& useNode = d_useListNodes[currentUseId];
634 // Get the function application
635 EqualityNodeId funId = useNode.getApplicationId();
636 Debug("equality") << d_name << "::eq::merge(" << class1.getFind() << "," << class2.getFind() << "): " << d_nodes[currentId] << " in " << d_nodes[funId] << std::endl;
637 const FunctionApplication& fun = d_applications[useNode.getApplicationId()].normalized;
638 // If it's interpreted and we can interpret
639 if (fun.isInterpreted() && class1isConstant && !d_isInternal[currentId]) {
640 // Get the actual term id
641 TNode term = d_nodes[funId];
642 subtermEvaluates(getNodeId(term));
643 }
644 // Check if there is an application with find arguments
645 EqualityNodeId aNormalized = getEqualityNode(fun.a).getFind();
646 EqualityNodeId bNormalized = getEqualityNode(fun.b).getFind();
647 FunctionApplication funNormalized(fun.type, aNormalized, bNormalized);
648 ApplicationIdsMap::iterator find = d_applicationLookup.find(funNormalized);
649 if (find != d_applicationLookup.end()) {
650 // Applications fun and the funNormalized can be merged due to congruence
651 if (getEqualityNode(funId).getFind() != getEqualityNode(find->second).getFind()) {
652 enqueue(MergeCandidate(funId, find->second, MERGED_THROUGH_CONGRUENCE, TNode::null()));
653 }
654 } else {
655 // There is no representative, so we can add one, we remove this when backtracking
656 storeApplicationLookup(funNormalized, funId);
657 }
658
659 // Go to the next one in the use list
660 currentUseId = useNode.getNext();
661 }
662
663 // Move to the next node
664 currentId = currentNode.getNext();
665 } while (currentId != class2Id);
666 }
667
668 // Now merge the lists
669 class1.merge<true>(class2);
670
671 // notify the theory
672 if (doNotify) {
673 d_notify.eqNotifyPostMerge(n1, n2);
674 }
675
676 // Go through the trigger term disequalities and propagate
677 if (!propagateTriggerTermDisequalities(class1OnlyTags, class1triggerRef, class2disequalitiesToNotify)) {
678 return false;
679 }
680 if (!propagateTriggerTermDisequalities(class2OnlyTags, class2triggerRef, class1disequalitiesToNotify)) {
681 return false;
682 }
683
684 // Notify the trigger term merges
685 if (class2triggerRef != +null_set_id) {
686 if (class1triggerRef == +null_set_id) {
687 // If class1 doesn't have individual triggers, but class2 does, mark it
688 d_nodeIndividualTrigger[class1Id] = class2triggerRef;
689 // Add it to the list for backtracking
690 d_triggerTermSetUpdates.push_back(TriggerSetUpdate(class1Id, +null_set_id));
691 d_triggerTermSetUpdatesSize = d_triggerTermSetUpdatesSize + 1;
692 } else {
693 // Get the triggers
694 TriggerTermSet& class1triggers = getTriggerTermSet(class1triggerRef);
695 TriggerTermSet& class2triggers = getTriggerTermSet(class2triggerRef);
696
697 // Initialize the merged set
698 Theory::Set newSetTags = Theory::setUnion(class1triggers.tags, class2triggers.tags);
699 EqualityNodeId newSetTriggers[THEORY_LAST];
700 unsigned newSetTriggersSize = 0;
701
702 int i1 = 0;
703 int i2 = 0;
704 Theory::Set tags1 = class1triggers.tags;
705 Theory::Set tags2 = class2triggers.tags;
706 TheoryId tag1 = Theory::setPop(tags1);
707 TheoryId tag2 = Theory::setPop(tags2);
708
709 // Comparing the THEORY_LAST is OK because all other theories are
710 // smaller, and will therefore be preferred
711 while (tag1 != THEORY_LAST || tag2 != THEORY_LAST)
712 {
713 if (tag1 < tag2) {
714 // copy tag1
715 newSetTriggers[newSetTriggersSize++] = class1triggers.triggers[i1++];
716 tag1 = Theory::setPop(tags1);
717 } else if (tag1 > tag2) {
718 // copy tag2
719 newSetTriggers[newSetTriggersSize++] = class2triggers.triggers[i2++];
720 tag2 = Theory::setPop(tags2);
721 } else {
722 // copy tag1
723 EqualityNodeId tag1id = newSetTriggers[newSetTriggersSize++] = class1triggers.triggers[i1++];
724 // since they are both tagged notify of merge
725 if (d_performNotify) {
726 EqualityNodeId tag2id = class2triggers.triggers[i2++];
727 if (!d_notify.eqNotifyTriggerTermEquality(tag1, d_nodes[tag1id], d_nodes[tag2id], true)) {
728 return false;
729 }
730 }
731 // Next tags
732 tag1 = Theory::setPop(tags1);
733 tag2 = Theory::setPop(tags2);
734 }
735 }
736
737 // Add the new trigger set, if different from previous one
738 if (class1triggers.tags != class2triggers.tags) {
739 // Add it to the list for backtracking
740 d_triggerTermSetUpdates.push_back(TriggerSetUpdate(class1Id, class1triggerRef));
741 d_triggerTermSetUpdatesSize = d_triggerTermSetUpdatesSize + 1;
742 // Mark the the new set as a trigger
743 d_nodeIndividualTrigger[class1Id] = newTriggerTermSet(newSetTags, newSetTriggers, newSetTriggersSize);
744 }
745 }
746 }
747
748 // Everything fine
749 return true;
750 }
751
undoMerge(EqualityNode & class1,EqualityNode & class2,EqualityNodeId class2Id)752 void EqualityEngine::undoMerge(EqualityNode& class1, EqualityNode& class2, EqualityNodeId class2Id) {
753
754 Debug("equality") << d_name << "::eq::undoMerge(" << class1.getFind() << "," << class2Id << ")" << std::endl;
755
756 // Now unmerge the lists (same as merge)
757 class1.merge<false>(class2);
758
759 // Update class2 representative information
760 EqualityNodeId currentId = class2Id;
761 Debug("equality") << d_name << "::eq::undoMerge(" << class1.getFind() << "," << class2Id << "): undoing representative info" << std::endl;
762 do {
763 // Get the current node
764 EqualityNode& currentNode = getEqualityNode(currentId);
765
766 // Update it's find to class1 id
767 currentNode.setFind(class2Id);
768
769 // Go through the trigger list (if any) and undo the class
770 TriggerId currentTrigger = d_nodeTriggers[currentId];
771 while (currentTrigger != null_trigger) {
772 Trigger& trigger = d_equalityTriggers[currentTrigger];
773 trigger.classId = class2Id;
774 currentTrigger = trigger.nextTrigger;
775 }
776
777 // Move to the next node
778 currentId = currentNode.getNext();
779
780 } while (currentId != class2Id);
781
782 }
783
backtrack()784 void EqualityEngine::backtrack() {
785
786 Debug("equality::backtrack") << "backtracking" << std::endl;
787
788 // If we need to backtrack then do it
789 if (d_assertedEqualitiesCount < d_assertedEqualities.size()) {
790
791 // Clear the propagation queue
792 while (!d_propagationQueue.empty()) {
793 d_propagationQueue.pop_front();
794 }
795
796 Debug("equality") << d_name << "::eq::backtrack(): nodes" << std::endl;
797
798 for (int i = (int)d_assertedEqualities.size() - 1, i_end = (int)d_assertedEqualitiesCount; i >= i_end; --i) {
799 // Get the ids of the merged classes
800 Equality& eq = d_assertedEqualities[i];
801 // Undo the merge
802 if (eq.lhs != null_id) {
803 undoMerge(d_equalityNodes[eq.lhs], d_equalityNodes[eq.rhs], eq.rhs);
804 }
805 }
806
807 d_assertedEqualities.resize(d_assertedEqualitiesCount);
808
809 Debug("equality") << d_name << "::eq::backtrack(): edges" << std::endl;
810
811 for (int i = (int)d_equalityEdges.size() - 2, i_end = (int)(2*d_assertedEqualitiesCount); i >= i_end; i -= 2) {
812 EqualityEdge& edge1 = d_equalityEdges[i];
813 EqualityEdge& edge2 = d_equalityEdges[i | 1];
814 d_equalityGraph[edge2.getNodeId()] = edge1.getNext();
815 d_equalityGraph[edge1.getNodeId()] = edge2.getNext();
816 }
817
818 d_equalityEdges.resize(2 * d_assertedEqualitiesCount);
819 }
820
821 if (d_triggerTermSetUpdates.size() > d_triggerTermSetUpdatesSize) {
822 // Unset the individual triggers
823 for (int i = d_triggerTermSetUpdates.size() - 1, i_end = d_triggerTermSetUpdatesSize; i >= i_end; -- i) {
824 const TriggerSetUpdate& update = d_triggerTermSetUpdates[i];
825 d_nodeIndividualTrigger[update.classId] = update.oldValue;
826 }
827 d_triggerTermSetUpdates.resize(d_triggerTermSetUpdatesSize);
828 }
829
830 if (d_equalityTriggers.size() > d_equalityTriggersCount) {
831 // Unlink the triggers from the lists
832 for (int i = d_equalityTriggers.size() - 1, i_end = d_equalityTriggersCount; i >= i_end; -- i) {
833 const Trigger& trigger = d_equalityTriggers[i];
834 d_nodeTriggers[trigger.classId] = trigger.nextTrigger;
835 }
836 // Get rid of the triggers
837 d_equalityTriggers.resize(d_equalityTriggersCount);
838 d_equalityTriggersOriginal.resize(d_equalityTriggersCount);
839 }
840
841 if (d_applicationLookups.size() > d_applicationLookupsCount) {
842 for (int i = d_applicationLookups.size() - 1, i_end = (int) d_applicationLookupsCount; i >= i_end; -- i) {
843 d_applicationLookup.erase(d_applicationLookups[i]);
844 }
845 d_applicationLookups.resize(d_applicationLookupsCount);
846 }
847
848 if (d_subtermEvaluates.size() > d_subtermEvaluatesSize) {
849 for(int i = d_subtermEvaluates.size() - 1, i_end = (int)d_subtermEvaluatesSize; i >= i_end; --i) {
850 d_subtermsToEvaluate[d_subtermEvaluates[i]] ++;
851 }
852 d_subtermEvaluates.resize(d_subtermEvaluatesSize);
853 }
854
855 if (d_nodes.size() > d_nodesCount) {
856 // Go down the nodes, check the application nodes and remove them from use-lists
857 for(int i = d_nodes.size() - 1, i_end = (int)d_nodesCount; i >= i_end; -- i) {
858 // Remove from the node -> id map
859 Debug("equality") << d_name << "::eq::backtrack(): removing node " << d_nodes[i] << std::endl;
860 d_nodeIds.erase(d_nodes[i]);
861
862 const FunctionApplication& app = d_applications[i].original;
863 if (!app.isNull()) {
864 // Remove b from use-list
865 getEqualityNode(app.b).removeTopFromUseList(d_useListNodes);
866 // Remove a from use-list
867 getEqualityNode(app.a).removeTopFromUseList(d_useListNodes);
868 }
869 }
870
871 // Now get rid of the nodes and the rest
872 d_nodes.resize(d_nodesCount);
873 d_applications.resize(d_nodesCount);
874 d_nodeTriggers.resize(d_nodesCount);
875 d_nodeIndividualTrigger.resize(d_nodesCount);
876 d_isConstant.resize(d_nodesCount);
877 d_subtermsToEvaluate.resize(d_nodesCount);
878 d_isEquality.resize(d_nodesCount);
879 d_isInternal.resize(d_nodesCount);
880 d_equalityGraph.resize(d_nodesCount);
881 d_equalityNodes.resize(d_nodesCount);
882 }
883
884 if (d_deducedDisequalities.size() > d_deducedDisequalitiesSize) {
885 for(int i = d_deducedDisequalities.size() - 1, i_end = (int)d_deducedDisequalitiesSize; i >= i_end; -- i) {
886 EqualityPair pair = d_deducedDisequalities[i];
887 Assert(d_disequalityReasonsMap.find(pair) != d_disequalityReasonsMap.end());
888 // Remove from the map
889 d_disequalityReasonsMap.erase(pair);
890 std::swap(pair.first, pair.second);
891 d_disequalityReasonsMap.erase(pair);
892 }
893 d_deducedDisequalityReasons.resize(d_deducedDisequalityReasonsSize);
894 d_deducedDisequalities.resize(d_deducedDisequalitiesSize);
895 }
896
897 }
898
addGraphEdge(EqualityNodeId t1,EqualityNodeId t2,unsigned type,TNode reason)899 void EqualityEngine::addGraphEdge(EqualityNodeId t1, EqualityNodeId t2, unsigned type, TNode reason) {
900 Debug("equality") << d_name << "::eq::addGraphEdge(" << d_nodes[t1] << "," << d_nodes[t2] << "," << reason << ")" << std::endl;
901 EqualityEdgeId edge = d_equalityEdges.size();
902 d_equalityEdges.push_back(EqualityEdge(t2, d_equalityGraph[t1], type, reason));
903 d_equalityEdges.push_back(EqualityEdge(t1, d_equalityGraph[t2], type, reason));
904 d_equalityGraph[t1] = edge;
905 d_equalityGraph[t2] = edge | 1;
906
907 if (Debug.isOn("equality::internal")) {
908 debugPrintGraph();
909 }
910 }
911
edgesToString(EqualityEdgeId edgeId) const912 std::string EqualityEngine::edgesToString(EqualityEdgeId edgeId) const {
913 std::stringstream out;
914 bool first = true;
915 if (edgeId == null_edge) {
916 out << "null";
917 } else {
918 while (edgeId != null_edge) {
919 const EqualityEdge& edge = d_equalityEdges[edgeId];
920 if (!first) out << ",";
921 out << d_nodes[edge.getNodeId()];
922 edgeId = edge.getNext();
923 first = false;
924 }
925 }
926 return out.str();
927 }
928
explainEquality(TNode t1,TNode t2,bool polarity,std::vector<TNode> & equalities,EqProof * eqp) const929 void EqualityEngine::explainEquality(TNode t1, TNode t2, bool polarity,
930 std::vector<TNode>& equalities,
931 EqProof* eqp) const {
932 Debug("equality") << d_name << "::eq::explainEquality(" << t1 << ", " << t2
933 << ", " << (polarity ? "true" : "false") << ")"
934 << ", proof = " << (eqp ? "ON" : "OFF") << std::endl;
935
936 // The terms must be there already
937 Assert(hasTerm(t1) && hasTerm(t2));;
938
939 // Get the ids
940 EqualityNodeId t1Id = getNodeId(t1);
941 EqualityNodeId t2Id = getNodeId(t2);
942
943 if (polarity) {
944 // Get the explanation
945 getExplanation(t1Id, t2Id, equalities, eqp);
946 } else {
947 if (eqp) {
948 eqp->d_id = eq::MERGED_THROUGH_TRANS;
949 eqp->d_node = d_nodes[t1Id].eqNode(d_nodes[t2Id]).notNode();
950 }
951
952 // Get the reason for this disequality
953 EqualityPair pair(t1Id, t2Id);
954 Assert(d_disequalityReasonsMap.find(pair) != d_disequalityReasonsMap.end(), "Don't ask for stuff I didn't notify you about");
955 DisequalityReasonRef reasonRef = d_disequalityReasonsMap.find(pair)->second;
956
957 for (unsigned i = reasonRef.mergesStart; i < reasonRef.mergesEnd; ++ i) {
958
959 EqualityPair toExplain = d_deducedDisequalityReasons[i];
960 std::shared_ptr<EqProof> eqpc;
961
962 // If we're constructing a (transitivity) proof, we don't need to include an explanation for x=x.
963 if (eqp && toExplain.first != toExplain.second) {
964 eqpc = std::make_shared<EqProof>();
965 }
966
967 getExplanation(toExplain.first, toExplain.second, equalities, eqpc.get());
968
969 if (eqpc) {
970 Debug("pf::ee") << "Child proof is:" << std::endl;
971 eqpc->debug_print("pf::ee", 1);
972
973 if (eqpc->d_id == eq::MERGED_THROUGH_TRANS) {
974 std::vector<std::shared_ptr<EqProof>> orderedChildren;
975 bool nullCongruenceFound = false;
976 for (unsigned i = 0; i < eqpc->d_children.size(); ++i) {
977 if (eqpc->d_children[i]->d_id==eq::MERGED_THROUGH_CONGRUENCE &&
978 eqpc->d_children[i]->d_node.isNull()) {
979 nullCongruenceFound = true;
980 Debug("pf::ee") << "Have congruence with empty d_node. Splitting..." << std::endl;
981 orderedChildren.insert(orderedChildren.begin(), eqpc->d_children[i]->d_children[0]);
982 orderedChildren.push_back(eqpc->d_children[i]->d_children[1]);
983 } else {
984 orderedChildren.push_back(eqpc->d_children[i]);
985 }
986 }
987
988 if (nullCongruenceFound) {
989 eqpc->d_children = orderedChildren;
990 Debug("pf::ee") << "Child proof's children have been reordered. It is now:" << std::endl;
991 eqpc->debug_print("pf::ee", 1);
992 }
993 }
994
995 eqp->d_children.push_back(eqpc);
996 }
997 }
998
999 if (eqp) {
1000 if (eqp->d_children.size() == 0) {
1001 // Corner case where this is actually a disequality between two constants
1002 Debug("pf::ee") << "Encountered a constant disequality (not a transitivity proof): "
1003 << eqp->d_node << std::endl;
1004 Assert(eqp->d_node[0][0].isConst());
1005 Assert(eqp->d_node[0][1].isConst());
1006 eqp->d_id = MERGED_THROUGH_CONSTANTS;
1007 } else if (eqp->d_children.size() == 1) {
1008 // The transitivity proof has just one child. Simplify.
1009 std::shared_ptr<EqProof> temp = eqp->d_children[0];
1010 eqp->d_children.clear();
1011 *eqp = *temp;
1012 }
1013
1014 Debug("pf::ee") << "Disequality explanation final proof: " << std::endl;
1015 eqp->debug_print("pf::ee", 1);
1016 }
1017 }
1018 }
1019
explainPredicate(TNode p,bool polarity,std::vector<TNode> & assertions,EqProof * eqp) const1020 void EqualityEngine::explainPredicate(TNode p, bool polarity,
1021 std::vector<TNode>& assertions,
1022 EqProof* eqp) const {
1023 Debug("equality") << d_name << "::eq::explainPredicate(" << p << ")"
1024 << std::endl;
1025 // Must have the term
1026 Assert(hasTerm(p));
1027 // Get the explanation
1028 getExplanation(getNodeId(p), polarity ? d_trueId : d_falseId, assertions,
1029 eqp);
1030 }
1031
getExplanation(EqualityNodeId t1Id,EqualityNodeId t2Id,std::vector<TNode> & equalities,EqProof * eqp) const1032 void EqualityEngine::getExplanation(EqualityNodeId t1Id, EqualityNodeId t2Id,
1033 std::vector<TNode>& equalities,
1034 EqProof* eqp) const {
1035 Debug("equality") << d_name << "::eq::getExplanation(" << d_nodes[t1Id] << "," << d_nodes[t2Id] << ")" << std::endl;
1036
1037 // We can only explain the nodes that got merged
1038 #ifdef CVC4_ASSERTIONS
1039 bool canExplain = getEqualityNode(t1Id).getFind() == getEqualityNode(t2Id).getFind()
1040 || (d_done && isConstant(t1Id) && isConstant(t2Id));
1041
1042 if (!canExplain) {
1043 Warning() << "Can't explain equality:" << std::endl;
1044 Warning() << d_nodes[t1Id] << " with find " << d_nodes[getEqualityNode(t1Id).getFind()] << std::endl;
1045 Warning() << d_nodes[t2Id] << " with find " << d_nodes[getEqualityNode(t2Id).getFind()] << std::endl;
1046 }
1047 Assert(canExplain);
1048 #endif
1049
1050 // If the nodes are the same, we're done
1051 if (t1Id == t2Id){
1052 if( eqp ) {
1053 if ((d_nodes[t1Id].getKind() == kind::BUILTIN) && (d_nodes[t1Id].getConst<Kind>() == kind::SELECT)) {
1054 std::vector<Node> no_children;
1055 eqp->d_node = NodeManager::currentNM()->mkNode(kind::PARTIAL_SELECT_0, no_children);
1056 } else {
1057 eqp->d_node = ProofManager::currentPM()->mkOp(d_nodes[t1Id]);
1058 }
1059 }
1060 return;
1061 }
1062
1063
1064 if (Debug.isOn("equality::internal")) {
1065 debugPrintGraph();
1066 }
1067
1068 // Queue for the BFS containing nodes
1069 std::vector<BfsData> bfsQueue;
1070
1071 // Find a path from t1 to t2 in the graph (BFS)
1072 bfsQueue.push_back(BfsData(t1Id, null_id, 0));
1073 size_t currentIndex = 0;
1074 while (true) {
1075 // There should always be a path, and every node can be visited only once (tree)
1076 Assert(currentIndex < bfsQueue.size());
1077
1078 // The next node to visit
1079 BfsData current = bfsQueue[currentIndex];
1080 EqualityNodeId currentNode = current.nodeId;
1081
1082 Debug("equality") << d_name << "::eq::getExplanation(): currentNode = " << d_nodes[currentNode] << std::endl;
1083
1084 // Go through the equality edges of this node
1085 EqualityEdgeId currentEdge = d_equalityGraph[currentNode];
1086 if (Debug.isOn("equality")) {
1087 Debug("equality") << d_name << "::eq::getExplanation(): edgesId = " << currentEdge << std::endl;
1088 Debug("equality") << d_name << "::eq::getExplanation(): edges = " << edgesToString(currentEdge) << std::endl;
1089 }
1090
1091 while (currentEdge != null_edge) {
1092 // Get the edge
1093 const EqualityEdge& edge = d_equalityEdges[currentEdge];
1094
1095 // If not just the backwards edge
1096 if ((currentEdge | 1u) != (current.edgeId | 1u)) {
1097
1098 Debug("equality") << d_name << "::eq::getExplanation(): currentEdge = (" << d_nodes[currentNode] << "," << d_nodes[edge.getNodeId()] << ")" << std::endl;
1099
1100 // Did we find the path
1101 if (edge.getNodeId() == t2Id) {
1102
1103 Debug("equality") << d_name << "::eq::getExplanation(): path found: " << std::endl;
1104
1105 std::vector<std::shared_ptr<EqProof>> eqp_trans;
1106
1107 // Reconstruct the path
1108 do {
1109 // The current node
1110 currentNode = bfsQueue[currentIndex].nodeId;
1111 EqualityNodeId edgeNode = d_equalityEdges[currentEdge].getNodeId();
1112 unsigned reasonType = d_equalityEdges[currentEdge].getReasonType();
1113 Node reason = d_equalityEdges[currentEdge].getReason();
1114
1115 Debug("equality") << d_name << "::eq::getExplanation(): currentEdge = " << currentEdge << ", currentNode = " << currentNode << std::endl;
1116 Debug("equality") << d_name << " targetNode = " << d_nodes[edgeNode] << std::endl;
1117 Debug("equality") << d_name << " in currentEdge = (" << d_nodes[currentNode] << "," << d_nodes[edge.getNodeId()] << ")" << std::endl;
1118 Debug("equality") << d_name << " reason type = " << reasonType << std::endl;
1119
1120 std::shared_ptr<EqProof> eqpc;;
1121 // Make child proof if a proof is being constructed
1122 if (eqp) {
1123 eqpc = std::make_shared<EqProof>();
1124 eqpc->d_id = reasonType;
1125 }
1126
1127 // Add the actual equality to the vector
1128 switch (reasonType) {
1129 case MERGED_THROUGH_CONGRUENCE: {
1130 // f(x1, x2) == f(y1, y2) because x1 = y1 and x2 = y2
1131 Debug("equality") << d_name << "::eq::getExplanation(): due to congruence, going deeper" << std::endl;
1132 const FunctionApplication& f1 = d_applications[currentNode].original;
1133 const FunctionApplication& f2 = d_applications[edgeNode].original;
1134
1135 Debug("equality") << push;
1136 Debug("equality") << "Explaining left hand side equalities" << std::endl;
1137 std::shared_ptr<EqProof> eqpc1 =
1138 eqpc ? std::make_shared<EqProof>() : nullptr;
1139 getExplanation(f1.a, f2.a, equalities, eqpc1.get());
1140 Debug("equality") << "Explaining right hand side equalities" << std::endl;
1141 std::shared_ptr<EqProof> eqpc2 =
1142 eqpc ? std::make_shared<EqProof>() : nullptr;
1143 getExplanation(f1.b, f2.b, equalities, eqpc2.get());
1144 if( eqpc ){
1145 eqpc->d_children.push_back( eqpc1 );
1146 eqpc->d_children.push_back( eqpc2 );
1147 if( d_nodes[currentNode].getKind()==kind::EQUAL ){
1148 //leave node null for now
1149 eqpc->d_node = Node::null();
1150 } else {
1151 if(d_nodes[f1.a].getKind() == kind::APPLY_UF ||
1152 d_nodes[f1.a].getKind() == kind::SELECT ||
1153 d_nodes[f1.a].getKind() == kind::STORE) {
1154 eqpc->d_node = d_nodes[f1.a];
1155 } else {
1156 if (d_nodes[f1.a].getKind() == kind::BUILTIN && d_nodes[f1.a].getConst<Kind>() == kind::SELECT) {
1157 eqpc->d_node = NodeManager::currentNM()->mkNode(kind::PARTIAL_SELECT_1, d_nodes[f1.b]);
1158 // The first child is a PARTIAL_SELECT_0.
1159 // Give it a child so that we know what kind of (read) it is, when we dump to LFSC.
1160 Assert(eqpc->d_children[0]->d_node.getKind() == kind::PARTIAL_SELECT_0);
1161 Assert(eqpc->d_children[0]->d_children.size() == 0);
1162
1163 eqpc->d_children[0]->d_node = NodeManager::currentNM()->mkNode(kind::PARTIAL_SELECT_0,
1164 d_nodes[f1.b]);
1165 } else {
1166 eqpc->d_node = NodeManager::currentNM()->mkNode(kind::PARTIAL_APPLY_UF,
1167 ProofManager::currentPM()->mkOp(d_nodes[f1.a]),
1168 d_nodes[f1.b]);
1169 }
1170 }
1171 }
1172 }
1173 Debug("equality") << pop;
1174 break;
1175 }
1176
1177 case MERGED_THROUGH_REFLEXIVITY: {
1178 // x1 == x1
1179 Debug("equality") << d_name << "::eq::getExplanation(): due to reflexivity, going deeper" << std::endl;
1180 EqualityNodeId eqId = currentNode == d_trueId ? edgeNode : currentNode;
1181 const FunctionApplication& eq = d_applications[eqId].original;
1182 Assert(eq.isEquality(), "Must be an equality");
1183
1184 // Explain why a = b constant
1185 Debug("equality") << push;
1186 std::shared_ptr<EqProof> eqpc1 =
1187 eqpc ? std::make_shared<EqProof>() : nullptr;
1188 getExplanation(eq.a, eq.b, equalities, eqpc1.get());
1189 if( eqpc ){
1190 eqpc->d_children.push_back( eqpc1 );
1191 }
1192 Debug("equality") << pop;
1193
1194 break;
1195 }
1196
1197 case MERGED_THROUGH_CONSTANTS: {
1198 // f(c1, ..., cn) = c semantically, we can just ignore it
1199 Debug("equality") << d_name << "::eq::getExplanation(): due to constants, explain the constants" << std::endl;
1200 Debug("equality") << push;
1201
1202 // Get the node we interpreted
1203 TNode interpreted = d_nodes[currentNode];
1204 if (interpreted.isConst()) {
1205 interpreted = d_nodes[edgeNode];
1206 }
1207
1208 // Explain why a is a constant by explaining each argument
1209 for (unsigned i = 0; i < interpreted.getNumChildren(); ++ i) {
1210 EqualityNodeId childId = getNodeId(interpreted[i]);
1211 Assert(isConstant(childId));
1212 std::shared_ptr<EqProof> eqpcc =
1213 eqpc ? std::make_shared<EqProof>() : nullptr;
1214 getExplanation(childId, getEqualityNode(childId).getFind(),
1215 equalities, eqpcc.get());
1216 if( eqpc ) {
1217 eqpc->d_children.push_back( eqpcc );
1218
1219 Debug("pf::ee") << "MERGED_THROUGH_CONSTANTS. Dumping the child proof" << std::endl;
1220 eqpc->debug_print("pf::ee", 1);
1221 }
1222 }
1223
1224 Debug("equality") << pop;
1225 break;
1226 }
1227
1228 default: {
1229 // Construct the equality
1230 Debug("equality") << d_name << "::eq::getExplanation(): adding: "
1231 << reason << std::endl;
1232 Debug("equality") << d_name << "::eq::getExplanation(): reason type = " << reasonType << std::endl;
1233 Node a = d_nodes[currentNode];
1234 Node b = d_nodes[d_equalityEdges[currentEdge].getNodeId()];
1235
1236 if (eqpc) {
1237 //apply proof reconstruction processing (when eqpc is non-null)
1238 if (d_pathReconstructionTriggers.find(reasonType) != d_pathReconstructionTriggers.end()) {
1239 d_pathReconstructionTriggers.find(reasonType)
1240 ->second->notify(reasonType, reason, a, b, equalities,
1241 eqpc.get());
1242 }
1243 if (reasonType == MERGED_THROUGH_EQUALITY) {
1244 eqpc->d_node = reason;
1245 } else {
1246 // The LFSC translator prefers (not (= a b)) over (= (= a b) false)
1247
1248 if (a == NodeManager::currentNM()->mkConst(false)) {
1249 eqpc->d_node = b.notNode();
1250 } else if (b == NodeManager::currentNM()->mkConst(false)) {
1251 eqpc->d_node = a.notNode();
1252 } else {
1253 eqpc->d_node = b.eqNode(a);
1254 }
1255 }
1256 eqpc->d_id = reasonType;
1257 }
1258
1259 equalities.push_back(reason);
1260 break;
1261 }
1262 }
1263
1264 // Go to the previous
1265 currentEdge = bfsQueue[currentIndex].edgeId;
1266 currentIndex = bfsQueue[currentIndex].previousIndex;
1267
1268 //---from Morgan---
1269 if (eqpc != NULL && eqpc->d_id == MERGED_THROUGH_REFLEXIVITY) {
1270 if(eqpc->d_node.isNull()) {
1271 Assert(eqpc->d_children.size() == 1);
1272 std::shared_ptr<EqProof> p = eqpc;
1273 eqpc = p->d_children[0];
1274 } else {
1275 Assert(eqpc->d_children.empty());
1276 }
1277 }
1278 //---end from Morgan---
1279
1280 eqp_trans.push_back(eqpc);
1281 } while (currentEdge != null_id);
1282
1283 if (eqp) {
1284 if(eqp_trans.size() == 1) {
1285 *eqp = *eqp_trans[0];
1286 } else {
1287 eqp->d_id = MERGED_THROUGH_TRANS;
1288 eqp->d_children.insert( eqp->d_children.end(), eqp_trans.begin(), eqp_trans.end() );
1289 eqp->d_node = NodeManager::currentNM()->mkNode(kind::EQUAL, d_nodes[t1Id], d_nodes[t2Id]);
1290 }
1291
1292 eqp->debug_print("pf::ee", 1);
1293 }
1294
1295 // Done
1296 return;
1297 }
1298
1299 // Push to the visitation queue if it's not the backward edge
1300 bfsQueue.push_back(BfsData(edge.getNodeId(), currentEdge, currentIndex));
1301 }
1302
1303 // Go to the next edge
1304 currentEdge = edge.getNext();
1305 }
1306
1307 // Go to the next node to visit
1308 ++ currentIndex;
1309 }
1310 }
1311
addTriggerEquality(TNode eq)1312 void EqualityEngine::addTriggerEquality(TNode eq) {
1313 Assert(eq.getKind() == kind::EQUAL);
1314
1315 if (d_done) {
1316 return;
1317 }
1318
1319 // Add the terms
1320 addTermInternal(eq[0]);
1321 addTermInternal(eq[1]);
1322
1323 bool skipTrigger = false;
1324
1325 // If they are equal or disequal already, no need for the trigger
1326 if (areEqual(eq[0], eq[1])) {
1327 d_notify.eqNotifyTriggerEquality(eq, true);
1328 skipTrigger = true;
1329 }
1330 if (areDisequal(eq[0], eq[1], true)) {
1331 d_notify.eqNotifyTriggerEquality(eq, false);
1332 skipTrigger = true;
1333 }
1334
1335 if (skipTrigger) {
1336 return;
1337 }
1338
1339 // Add the equality
1340 addTermInternal(eq);
1341
1342 // Positive trigger
1343 addTriggerEqualityInternal(eq[0], eq[1], eq, true);
1344 // Negative trigger
1345 addTriggerEqualityInternal(eq, d_false, eq, false);
1346 }
1347
addTriggerPredicate(TNode predicate)1348 void EqualityEngine::addTriggerPredicate(TNode predicate) {
1349 Assert(predicate.getKind() != kind::NOT && predicate.getKind() != kind::EQUAL);
1350 Assert(d_congruenceKinds.tst(predicate.getKind()), "No point in adding non-congruence predicates");
1351
1352 if (d_done) {
1353 return;
1354 }
1355
1356 // Add the term
1357 addTermInternal(predicate);
1358
1359 bool skipTrigger = false;
1360
1361 // If it's know already, no need for the trigger
1362 if (areEqual(predicate, d_true)) {
1363 d_notify.eqNotifyTriggerPredicate(predicate, true);
1364 skipTrigger = true;
1365 }
1366 if (areEqual(predicate, d_false)) {
1367 d_notify.eqNotifyTriggerPredicate(predicate, false);
1368 skipTrigger = true;
1369 }
1370
1371 if (skipTrigger) {
1372 return;
1373 }
1374
1375 // Positive trigger
1376 addTriggerEqualityInternal(predicate, d_true, predicate, true);
1377 // Negative trigger
1378 addTriggerEqualityInternal(predicate, d_false, predicate, false);
1379 }
1380
addTriggerEqualityInternal(TNode t1,TNode t2,TNode trigger,bool polarity)1381 void EqualityEngine::addTriggerEqualityInternal(TNode t1, TNode t2, TNode trigger, bool polarity) {
1382
1383 Debug("equality") << d_name << "::eq::addTrigger(" << t1 << ", " << t2 << ", " << trigger << ")" << std::endl;
1384
1385 Assert(hasTerm(t1));
1386 Assert(hasTerm(t2));
1387
1388 if (d_done) {
1389 return;
1390 }
1391
1392 // Get the information about t1
1393 EqualityNodeId t1Id = getNodeId(t1);
1394 EqualityNodeId t1classId = getEqualityNode(t1Id).getFind();
1395 // We will attach it to the class representative, since then we know how to backtrack it
1396 TriggerId t1TriggerId = d_nodeTriggers[t1classId];
1397
1398 // Get the information about t2
1399 EqualityNodeId t2Id = getNodeId(t2);
1400 EqualityNodeId t2classId = getEqualityNode(t2Id).getFind();
1401 // We will attach it to the class representative, since then we know how to backtrack it
1402 TriggerId t2TriggerId = d_nodeTriggers[t2classId];
1403
1404 Debug("equality") << d_name << "::eq::addTrigger(" << trigger << "): " << t1Id << " (" << t1classId << ") = " << t2Id << " (" << t2classId << ")" << std::endl;
1405
1406 // Create the triggers
1407 TriggerId t1NewTriggerId = d_equalityTriggers.size();
1408 d_equalityTriggers.push_back(Trigger(t1classId, t1TriggerId));
1409 d_equalityTriggersOriginal.push_back(TriggerInfo(trigger, polarity));
1410 TriggerId t2NewTriggerId = d_equalityTriggers.size();
1411 d_equalityTriggers.push_back(Trigger(t2classId, t2TriggerId));
1412 d_equalityTriggersOriginal.push_back(TriggerInfo(trigger, polarity));
1413
1414 // Update the counters
1415 d_equalityTriggersCount = d_equalityTriggers.size();
1416 Assert(d_equalityTriggers.size() == d_equalityTriggersOriginal.size());
1417 Assert(d_equalityTriggers.size() % 2 == 0);
1418
1419 // Add the trigger to the trigger graph
1420 d_nodeTriggers[t1classId] = t1NewTriggerId;
1421 d_nodeTriggers[t2classId] = t2NewTriggerId;
1422
1423 if (Debug.isOn("equality::internal")) {
1424 debugPrintGraph();
1425 }
1426
1427 Debug("equality") << d_name << "::eq::addTrigger(" << t1 << "," << t2 << ") => (" << t1NewTriggerId << ", " << t2NewTriggerId << ")" << std::endl;
1428 }
1429
evaluateTerm(TNode node)1430 Node EqualityEngine::evaluateTerm(TNode node) {
1431 Debug("equality::evaluation") << d_name << "::eq::evaluateTerm(" << node << ")" << std::endl;
1432 NodeBuilder<> builder;
1433 builder << node.getKind();
1434 if (node.getMetaKind() == kind::metakind::PARAMETERIZED) {
1435 builder << node.getOperator();
1436 }
1437 for (unsigned i = 0; i < node.getNumChildren(); ++ i) {
1438 TNode child = node[i];
1439 TNode childRep = getRepresentative(child);
1440 Debug("equality::evaluation") << d_name << "::eq::evaluateTerm: " << child << " -> " << childRep << std::endl;
1441 Assert(childRep.isConst());
1442 builder << childRep;
1443 }
1444 Node newNode = builder;
1445 return Rewriter::rewrite(newNode);
1446 }
1447
processEvaluationQueue()1448 void EqualityEngine::processEvaluationQueue() {
1449
1450 Debug("equality::evaluation") << d_name << "::eq::processEvaluationQueue(): start" << std::endl;
1451
1452 while (!d_evaluationQueue.empty()) {
1453 // Get the node
1454 EqualityNodeId id = d_evaluationQueue.front();
1455 d_evaluationQueue.pop();
1456
1457 // Replace the children with their representatives (must be constants)
1458 Node nodeEvaluated = evaluateTerm(d_nodes[id]);
1459 Debug("equality::evaluation") << d_name << "::eq::processEvaluationQueue(): " << d_nodes[id] << " evaluates to " << nodeEvaluated << std::endl;
1460 Assert(nodeEvaluated.isConst());
1461 addTermInternal(nodeEvaluated);
1462 EqualityNodeId nodeEvaluatedId = getNodeId(nodeEvaluated);
1463
1464 // Enqueue the semantic equality
1465 enqueue(MergeCandidate(id, nodeEvaluatedId, MERGED_THROUGH_CONSTANTS, TNode::null()));
1466 }
1467
1468 Debug("equality::evaluation") << d_name << "::eq::processEvaluationQueue(): done" << std::endl;
1469 }
1470
propagate()1471 void EqualityEngine::propagate() {
1472
1473 if (d_inPropagate) {
1474 // We're already in propagate, go back
1475 return;
1476 }
1477
1478 // Make sure we don't get in again
1479 ScopedBool inPropagate(d_inPropagate, true);
1480
1481 Debug("equality") << d_name << "::eq::propagate()" << std::endl;
1482
1483 while (!d_propagationQueue.empty() || !d_evaluationQueue.empty()) {
1484
1485 if (d_done) {
1486 // If we're done, just empty the queue
1487 while (!d_propagationQueue.empty()) d_propagationQueue.pop_front();
1488 while (!d_evaluationQueue.empty()) d_evaluationQueue.pop();
1489 continue;
1490 }
1491
1492 // Process any evaluation requests
1493 if (!d_evaluationQueue.empty()) {
1494 processEvaluationQueue();
1495 continue;
1496 }
1497
1498 // The current merge candidate
1499 const MergeCandidate current = d_propagationQueue.front();
1500 d_propagationQueue.pop_front();
1501
1502 // Get the representatives
1503 EqualityNodeId t1classId = getEqualityNode(current.t1Id).getFind();
1504 EqualityNodeId t2classId = getEqualityNode(current.t2Id).getFind();
1505
1506 // If already the same, we're done
1507 if (t1classId == t2classId) {
1508 continue;
1509 }
1510
1511 Debug("equality::internal") << d_name << "::eq::propagate(): t1: " << (d_isInternal[t1classId] ? "internal" : "proper") << std::endl;
1512 Debug("equality::internal") << d_name << "::eq::propagate(): t2: " << (d_isInternal[t2classId] ? "internal" : "proper") << std::endl;
1513
1514 // Get the nodes of the representatives
1515 EqualityNode& node1 = getEqualityNode(t1classId);
1516 EqualityNode& node2 = getEqualityNode(t2classId);
1517
1518 Assert(node1.getFind() == t1classId);
1519 Assert(node2.getFind() == t2classId);
1520
1521 // Add the actual equality to the equality graph
1522 addGraphEdge(current.t1Id, current.t2Id, current.type, current.reason);
1523
1524 // If constants are being merged we're done
1525 if (d_isConstant[t1classId] && d_isConstant[t2classId]) {
1526 // When merging constants we are inconsistent, hence done
1527 d_done = true;
1528 // But in order to keep invariants (edges = 2*equalities) we put an equalities in
1529 // Note that we can explain this merge as we have a graph edge
1530 d_assertedEqualities.push_back(Equality(null_id, null_id));
1531 d_assertedEqualitiesCount = d_assertedEqualitiesCount + 1;
1532 // Notify
1533 if (d_performNotify) {
1534 d_notify.eqNotifyConstantTermMerge(d_nodes[t1classId], d_nodes[t2classId]);
1535 }
1536 // Empty the queue and exit
1537 continue;
1538 }
1539
1540 // Vector to collect the triggered events
1541 std::vector<TriggerId> triggers;
1542
1543 // Figure out the merge preference
1544 EqualityNodeId mergeInto = t1classId;
1545 if (d_isInternal[t2classId] != d_isInternal[t1classId]) {
1546 // We always keep non-internal nodes as representatives: if any node in
1547 // the class is non-internal, then the representative will be non-internal
1548 if (d_isInternal[t1classId]) {
1549 mergeInto = t2classId;
1550 } else {
1551 mergeInto = t1classId;
1552 }
1553 } else if (d_isConstant[t2classId] != d_isConstant[t1classId]) {
1554 // We always keep constants as representatives: if any (at most one) node
1555 // in the class in a constant, then the representative will be a constant
1556 if (d_isConstant[t2classId]) {
1557 mergeInto = t2classId;
1558 } else {
1559 mergeInto = t1classId;
1560 }
1561 } else if (node2.getSize() > node1.getSize()) {
1562 // We always merge into the bigger class to reduce the amount of traversing
1563 // we need to do
1564 mergeInto = t2classId;
1565 }
1566
1567 if (mergeInto == t2classId) {
1568 Debug("equality") << d_name << "::eq::propagate(): merging " << d_nodes[current.t1Id]<< " into " << d_nodes[current.t2Id] << std::endl;
1569 d_assertedEqualities.push_back(Equality(t2classId, t1classId));
1570 d_assertedEqualitiesCount = d_assertedEqualitiesCount + 1;
1571 if (!merge(node2, node1, triggers)) {
1572 d_done = true;
1573 }
1574 } else {
1575 Debug("equality") << d_name << "::eq::propagate(): merging " << d_nodes[current.t2Id] << " into " << d_nodes[current.t1Id] << std::endl;
1576 d_assertedEqualities.push_back(Equality(t1classId, t2classId));
1577 d_assertedEqualitiesCount = d_assertedEqualitiesCount + 1;
1578 if (!merge(node1, node2, triggers)) {
1579 d_done = true;
1580 }
1581 }
1582
1583 // If not merging internal nodes, notify the master
1584 if (d_masterEqualityEngine && !d_isInternal[t1classId] && !d_isInternal[t2classId]) {
1585 d_masterEqualityEngine->assertEqualityInternal(d_nodes[t1classId], d_nodes[t2classId], TNode::null());
1586 d_masterEqualityEngine->propagate();
1587 }
1588
1589 // Notify the triggers
1590 if (d_performNotify && !d_done) {
1591 for (size_t trigger_i = 0, trigger_end = triggers.size(); trigger_i < trigger_end && !d_done; ++ trigger_i) {
1592 const TriggerInfo& triggerInfo = d_equalityTriggersOriginal[triggers[trigger_i]];
1593 if (triggerInfo.trigger.getKind() == kind::EQUAL) {
1594 // Special treatment for disequalities
1595 if (!triggerInfo.polarity) {
1596 // Store that we are propagating a diseauality
1597 TNode equality = triggerInfo.trigger;
1598 EqualityNodeId original = getNodeId(equality);
1599 TNode lhs = equality[0];
1600 TNode rhs = equality[1];
1601 EqualityNodeId lhsId = getNodeId(lhs);
1602 EqualityNodeId rhsId = getNodeId(rhs);
1603 // We use the THEORY_LAST as a marker for "marked as propagated, reasons stored".
1604 // This tag is added to an internal theories set that is only inserted in, so this is
1605 // safe. Had we iterated over, or done other set operations this might be dangerous.
1606 if (!hasPropagatedDisequality(THEORY_LAST, lhsId, rhsId)) {
1607 if (!hasPropagatedDisequality(lhsId, rhsId)) {
1608 d_deducedDisequalityReasons.push_back(EqualityPair(original, d_falseId));
1609 }
1610 storePropagatedDisequality(THEORY_LAST, lhsId, rhsId);
1611 if (!d_notify.eqNotifyTriggerEquality(triggerInfo.trigger, triggerInfo.polarity)) {
1612 d_done = true;
1613 }
1614 }
1615 } else {
1616 // Equalities are simple
1617 if (!d_notify.eqNotifyTriggerEquality(triggerInfo.trigger, triggerInfo.polarity)) {
1618 d_done = true;
1619 }
1620 }
1621 } else {
1622 if (!d_notify.eqNotifyTriggerPredicate(triggerInfo.trigger, triggerInfo.polarity)) {
1623 d_done = true;
1624 }
1625 }
1626 }
1627 }
1628 }
1629 }
1630
debugPrintGraph() const1631 void EqualityEngine::debugPrintGraph() const {
1632 Debug("equality::graph") << std::endl << "Dumping graph" << std::endl;
1633 for (EqualityNodeId nodeId = 0; nodeId < d_nodes.size(); ++ nodeId) {
1634
1635 Debug("equality::graph") << d_nodes[nodeId] << " " << nodeId << "(" << getEqualityNode(nodeId).getFind() << "):";
1636
1637 EqualityEdgeId edgeId = d_equalityGraph[nodeId];
1638 while (edgeId != null_edge) {
1639 const EqualityEdge& edge = d_equalityEdges[edgeId];
1640 Debug("equality::graph") << " [" << edge.getNodeId() << "] " << d_nodes[edge.getNodeId()] << ":" << edge.getReason();
1641 edgeId = edge.getNext();
1642 }
1643
1644 Debug("equality::graph") << std::endl;
1645 }
1646 Debug("equality::graph") << std::endl;
1647 }
1648
areEqual(TNode t1,TNode t2) const1649 bool EqualityEngine::areEqual(TNode t1, TNode t2) const {
1650 Debug("equality") << d_name << "::eq::areEqual(" << t1 << "," << t2 << ")";
1651
1652 Assert(hasTerm(t1));
1653 Assert(hasTerm(t2));
1654
1655 bool result = getEqualityNode(t1).getFind() == getEqualityNode(t2).getFind();
1656 Debug("equality") << (result ? "\t(YES)" : "\t(NO)") << std::endl;
1657 return result;
1658 }
1659
areDisequal(TNode t1,TNode t2,bool ensureProof) const1660 bool EqualityEngine::areDisequal(TNode t1, TNode t2, bool ensureProof) const
1661 {
1662 Debug("equality") << d_name << "::eq::areDisequal(" << t1 << "," << t2 << ")";
1663
1664 // Add the terms
1665 Assert(hasTerm(t1));
1666 Assert(hasTerm(t2));
1667
1668 // Get ids
1669 EqualityNodeId t1Id = getNodeId(t1);
1670 EqualityNodeId t2Id = getNodeId(t2);
1671
1672 // If we propagated this disequality we're true
1673 if (hasPropagatedDisequality(t1Id, t2Id)) {
1674 Debug("equality") << "\t(YES)" << std::endl;
1675 return true;
1676 }
1677
1678 // Get equivalence classes
1679 EqualityNodeId t1ClassId = getEqualityNode(t1Id).getFind();
1680 EqualityNodeId t2ClassId = getEqualityNode(t2Id).getFind();
1681
1682 // We are semantically const, for remembering stuff
1683 EqualityEngine* nonConst = const_cast<EqualityEngine*>(this);
1684
1685 // Check for constants
1686 if (d_isConstant[t1ClassId] && d_isConstant[t2ClassId] && t1ClassId != t2ClassId) {
1687 if (ensureProof) {
1688 nonConst->d_deducedDisequalityReasons.push_back(EqualityPair(t1Id, t1ClassId));
1689 nonConst->d_deducedDisequalityReasons.push_back(EqualityPair(t2Id, t2ClassId));
1690 nonConst->storePropagatedDisequality(THEORY_LAST, t1Id, t2Id);
1691 }
1692 Debug("equality") << "\t(YES)" << std::endl;
1693 return true;
1694 }
1695
1696 // Create the equality
1697 FunctionApplication eqNormalized(APP_EQUALITY, t1ClassId, t2ClassId);
1698 ApplicationIdsMap::const_iterator find = d_applicationLookup.find(eqNormalized);
1699 if (find != d_applicationLookup.end()) {
1700 if (getEqualityNode(find->second).getFind() == getEqualityNode(d_falseId).getFind()) {
1701 if (ensureProof) {
1702 const FunctionApplication original = d_applications[find->second].original;
1703 nonConst->d_deducedDisequalityReasons.push_back(EqualityPair(t1Id, original.a));
1704 nonConst->d_deducedDisequalityReasons.push_back(EqualityPair(find->second, d_falseId));
1705 nonConst->d_deducedDisequalityReasons.push_back(EqualityPair(t2Id, original.b));
1706 nonConst->storePropagatedDisequality(THEORY_LAST, t1Id, t2Id);
1707 }
1708 Debug("equality") << "\t(YES)" << std::endl;
1709 return true;
1710 }
1711 }
1712
1713 // Check the symmetric disequality
1714 std::swap(eqNormalized.a, eqNormalized.b);
1715 find = d_applicationLookup.find(eqNormalized);
1716 if (find != d_applicationLookup.end()) {
1717 if (getEqualityNode(find->second).getFind() == getEqualityNode(d_falseId).getFind()) {
1718 if (ensureProof) {
1719 const FunctionApplication original = d_applications[find->second].original;
1720 nonConst->d_deducedDisequalityReasons.push_back(EqualityPair(t2Id, original.a));
1721 nonConst->d_deducedDisequalityReasons.push_back(EqualityPair(find->second, d_falseId));
1722 nonConst->d_deducedDisequalityReasons.push_back(EqualityPair(t1Id, original.b));
1723 nonConst->storePropagatedDisequality(THEORY_LAST, t1Id, t2Id);
1724 }
1725 Debug("equality") << "\t(YES)" << std::endl;
1726 return true;
1727 }
1728 }
1729
1730 // Couldn't deduce dis-equalityReturn whether the terms are disequal
1731 Debug("equality") << "\t(NO)" << std::endl;
1732 return false;
1733 }
1734
getSize(TNode t)1735 size_t EqualityEngine::getSize(TNode t) {
1736 // Add the term
1737 addTermInternal(t);
1738 return getEqualityNode(getEqualityNode(t).getFind()).getSize();
1739 }
1740
1741
addPathReconstructionTrigger(unsigned trigger,const PathReconstructionNotify * notify)1742 void EqualityEngine::addPathReconstructionTrigger(unsigned trigger, const PathReconstructionNotify* notify) {
1743 // Currently we can only inform one callback per trigger
1744 Assert(d_pathReconstructionTriggers.find(trigger) == d_pathReconstructionTriggers.end());
1745 d_pathReconstructionTriggers[trigger] = notify;
1746 }
1747
getFreshMergeReasonType()1748 unsigned EqualityEngine::getFreshMergeReasonType() {
1749 return d_freshMergeReasonType++;
1750 }
1751
addTriggerTerm(TNode t,TheoryId tag)1752 void EqualityEngine::addTriggerTerm(TNode t, TheoryId tag)
1753 {
1754 Debug("equality::trigger") << d_name << "::eq::addTriggerTerm(" << t << ", " << tag << ")" << std::endl;
1755
1756 Assert(tag != THEORY_LAST);
1757
1758 if (d_done) {
1759 return;
1760 }
1761
1762 // Add the term if it's not already there
1763 addTermInternal(t);
1764
1765 // Get the node id
1766 EqualityNodeId eqNodeId = getNodeId(t);
1767 EqualityNode& eqNode = getEqualityNode(eqNodeId);
1768 EqualityNodeId classId = eqNode.getFind();
1769
1770 // Possibly existing set of triggers
1771 TriggerTermSetRef triggerSetRef = d_nodeIndividualTrigger[classId];
1772 if (triggerSetRef != +null_set_id && getTriggerTermSet(triggerSetRef).hasTrigger(tag)) {
1773 // If the term already is in the equivalence class that a tagged representative, just notify
1774 if (d_performNotify) {
1775 EqualityNodeId triggerId = getTriggerTermSet(triggerSetRef).getTrigger(tag);
1776 Debug("equality::trigger") << d_name << "::eq::addTriggerTerm(" << t << ", " << tag << "): already have this trigger in class with " << d_nodes[triggerId] << std::endl;
1777 if (eqNodeId != triggerId && !d_notify.eqNotifyTriggerTermEquality(tag, t, d_nodes[triggerId], true)) {
1778 d_done = true;
1779 }
1780 }
1781 } else {
1782
1783 // Check for disequalities by going through the equivalence class looking for equalities in the
1784 // uselists that have been asserted to false. All the representatives appearing on the other
1785 // side of such disequalities, that have the tag on, are put in a set.
1786 TaggedEqualitiesSet disequalitiesToNotify;
1787 Theory::Set tags = Theory::setInsert(tag);
1788 getDisequalities(!d_isConstant[classId], classId, tags, disequalitiesToNotify);
1789
1790 // Trigger data
1791 Theory::Set newSetTags;
1792 EqualityNodeId newSetTriggers[THEORY_LAST];
1793 unsigned newSetTriggersSize;
1794
1795 // Setup the data for the new set
1796 if (triggerSetRef != null_set_id) {
1797 // Get the existing set
1798 TriggerTermSet& triggerSet = getTriggerTermSet(triggerSetRef);
1799 // Initialize the new set for copy/insert
1800 newSetTags = Theory::setInsert(tag, triggerSet.tags);
1801 newSetTriggersSize = 0;
1802 // Copy into to new one, and insert the new tag/id
1803 unsigned i = 0;
1804 Theory::Set tags = newSetTags;
1805 TheoryId current;
1806 while ((current = Theory::setPop(tags)) != THEORY_LAST) {
1807 // Remove from the tags
1808 tags = Theory::setRemove(current, tags);
1809 // Insert the id into the triggers
1810 newSetTriggers[newSetTriggersSize++] =
1811 current == tag ? eqNodeId : triggerSet.triggers[i++];
1812 }
1813 } else {
1814 // Setup a singleton
1815 newSetTags = Theory::setInsert(tag);
1816 newSetTriggers[0] = eqNodeId;
1817 newSetTriggersSize = 1;
1818 }
1819
1820 // Add it to the list for backtracking
1821 d_triggerTermSetUpdates.push_back(TriggerSetUpdate(classId, triggerSetRef));
1822 d_triggerTermSetUpdatesSize = d_triggerTermSetUpdatesSize + 1;
1823 // Mark the the new set as a trigger
1824 d_nodeIndividualTrigger[classId] = triggerSetRef = newTriggerTermSet(newSetTags, newSetTriggers, newSetTriggersSize);
1825
1826 // Propagate trigger term disequalities we remembered
1827 Debug("equality::trigger") << d_name << "::eq::addTriggerTerm(" << t << ", " << tag << "): propagating " << disequalitiesToNotify.size() << " disequalities " << std::endl;
1828 propagateTriggerTermDisequalities(tags, triggerSetRef, disequalitiesToNotify);
1829 }
1830 }
1831
isTriggerTerm(TNode t,TheoryId tag) const1832 bool EqualityEngine::isTriggerTerm(TNode t, TheoryId tag) const {
1833 if (!hasTerm(t)) return false;
1834 EqualityNodeId classId = getEqualityNode(t).getFind();
1835 TriggerTermSetRef triggerSetRef = d_nodeIndividualTrigger[classId];
1836 return triggerSetRef != +null_set_id && getTriggerTermSet(triggerSetRef).hasTrigger(tag);
1837 }
1838
1839
getTriggerTermRepresentative(TNode t,TheoryId tag) const1840 TNode EqualityEngine::getTriggerTermRepresentative(TNode t, TheoryId tag) const {
1841 Assert(isTriggerTerm(t, tag));
1842 EqualityNodeId classId = getEqualityNode(t).getFind();
1843 const TriggerTermSet& triggerSet = getTriggerTermSet(d_nodeIndividualTrigger[classId]);
1844 unsigned i = 0;
1845 Theory::Set tags = triggerSet.tags;
1846 while (Theory::setPop(tags) != tag) {
1847 ++ i;
1848 }
1849 return d_nodes[triggerSet.triggers[i]];
1850 }
1851
storeApplicationLookup(FunctionApplication & funNormalized,EqualityNodeId funId)1852 void EqualityEngine::storeApplicationLookup(FunctionApplication& funNormalized, EqualityNodeId funId) {
1853 Assert(d_applicationLookup.find(funNormalized) == d_applicationLookup.end());
1854 d_applicationLookup[funNormalized] = funId;
1855 d_applicationLookups.push_back(funNormalized);
1856 d_applicationLookupsCount = d_applicationLookupsCount + 1;
1857 Debug("equality::backtrack") << "d_applicationLookupsCount = " << d_applicationLookupsCount << std::endl;
1858 Debug("equality::backtrack") << "d_applicationLookups.size() = " << d_applicationLookups.size() << std::endl;
1859 Assert(d_applicationLookupsCount == d_applicationLookups.size());
1860
1861 // If an equality over constants we merge to false
1862 if (funNormalized.isEquality()) {
1863 if (funNormalized.a == funNormalized.b) {
1864 enqueue(MergeCandidate(funId, d_trueId, MERGED_THROUGH_REFLEXIVITY, TNode::null()));
1865 } else if (d_isConstant[funNormalized.a] && d_isConstant[funNormalized.b]) {
1866 enqueue(MergeCandidate(funId, d_falseId, MERGED_THROUGH_CONSTANTS, TNode::null()));
1867 }
1868 }
1869 }
1870
getUseListTerms(TNode t,std::set<TNode> & output)1871 void EqualityEngine::getUseListTerms(TNode t, std::set<TNode>& output) {
1872 if (hasTerm(t)) {
1873 // Get the equivalence class
1874 EqualityNodeId classId = getEqualityNode(t).getFind();
1875 // Go through the equivalence class and get where t is used in
1876 EqualityNodeId currentId = classId;
1877 do {
1878 // Get the current node
1879 EqualityNode& currentNode = getEqualityNode(currentId);
1880 // Go through the use-list
1881 UseListNodeId currentUseId = currentNode.getUseList();
1882 while (currentUseId != null_uselist_id) {
1883 // Get the node of the use list
1884 UseListNode& useNode = d_useListNodes[currentUseId];
1885 // Get the function application
1886 EqualityNodeId funId = useNode.getApplicationId();
1887 output.insert(d_nodes[funId]);
1888 // Go to the next one in the use list
1889 currentUseId = useNode.getNext();
1890 }
1891 // Move to the next node
1892 currentId = currentNode.getNext();
1893 } while (currentId != classId);
1894 }
1895 }
1896
newTriggerTermSet(Theory::Set newSetTags,EqualityNodeId * newSetTriggers,unsigned newSetTriggersSize)1897 EqualityEngine::TriggerTermSetRef EqualityEngine::newTriggerTermSet(Theory::Set newSetTags, EqualityNodeId* newSetTriggers, unsigned newSetTriggersSize) {
1898 // Size of the required set
1899 size_t size = sizeof(TriggerTermSet) + newSetTriggersSize*sizeof(EqualityNodeId);
1900 // Align the size
1901 size = (size + 7) & ~((size_t)7);
1902 // Reallocate if necessary
1903 if (d_triggerDatabaseSize + size > d_triggerDatabaseAllocatedSize) {
1904 d_triggerDatabaseAllocatedSize *= 2;
1905 d_triggerDatabase = (char*) realloc(d_triggerDatabase, d_triggerDatabaseAllocatedSize);
1906 }
1907 // New reference
1908 TriggerTermSetRef newTriggerSetRef = d_triggerDatabaseSize;
1909 // Update the size
1910 d_triggerDatabaseSize = d_triggerDatabaseSize + size;
1911 // Copy the information
1912 TriggerTermSet& newSet = getTriggerTermSet(newTriggerSetRef);
1913 newSet.tags = newSetTags;
1914 for (unsigned i = 0; i < newSetTriggersSize; ++i) {
1915 newSet.triggers[i] = newSetTriggers[i];
1916 }
1917 // Return the new reference
1918 return newTriggerSetRef;
1919 }
1920
hasPropagatedDisequality(EqualityNodeId lhsId,EqualityNodeId rhsId) const1921 bool EqualityEngine::hasPropagatedDisequality(EqualityNodeId lhsId, EqualityNodeId rhsId) const {
1922 EqualityPair eq(lhsId, rhsId);
1923 bool propagated = d_propagatedDisequalities.find(eq) != d_propagatedDisequalities.end();
1924 #ifdef CVC4_ASSERTIONS
1925 bool stored = d_disequalityReasonsMap.find(eq) != d_disequalityReasonsMap.end();
1926 Assert(propagated == stored, "These two should be in sync");
1927 #endif
1928 Debug("equality::disequality") << d_name << "::eq::hasPropagatedDisequality(" << d_nodes[lhsId] << ", " << d_nodes[rhsId] << ") => " << (propagated ? "true" : "false") << std::endl;
1929 return propagated;
1930 }
1931
hasPropagatedDisequality(TheoryId tag,EqualityNodeId lhsId,EqualityNodeId rhsId) const1932 bool EqualityEngine::hasPropagatedDisequality(TheoryId tag, EqualityNodeId lhsId, EqualityNodeId rhsId) const {
1933
1934 EqualityPair eq(lhsId, rhsId);
1935
1936 PropagatedDisequalitiesMap::const_iterator it = d_propagatedDisequalities.find(eq);
1937 if (it == d_propagatedDisequalities.end()) {
1938 Assert(d_disequalityReasonsMap.find(eq) == d_disequalityReasonsMap.end(), "Why do we have a proof if not propagated");
1939 Debug("equality::disequality") << d_name << "::eq::hasPropagatedDisequality(" << tag << ", " << d_nodes[lhsId] << ", " << d_nodes[rhsId] << ") => false" << std::endl;
1940 return false;
1941 }
1942 Assert(d_disequalityReasonsMap.find(eq) != d_disequalityReasonsMap.end(), "We propagated but there is no proof");
1943 bool result = Theory::setContains(tag, (*it).second);
1944 Debug("equality::disequality") << d_name << "::eq::hasPropagatedDisequality(" << tag << ", " << d_nodes[lhsId] << ", " << d_nodes[rhsId] << ") => " << (result ? "true" : "false") << std::endl;
1945 return result;
1946 }
1947
1948
storePropagatedDisequality(TheoryId tag,EqualityNodeId lhsId,EqualityNodeId rhsId)1949 void EqualityEngine::storePropagatedDisequality(TheoryId tag, EqualityNodeId lhsId, EqualityNodeId rhsId) {
1950
1951 Assert(!hasPropagatedDisequality(tag, lhsId, rhsId), "Check before you store it");
1952 Assert(lhsId != rhsId, "Wow, wtf!");
1953
1954 Debug("equality::disequality") << d_name << "::eq::storePropagatedDisequality(" << tag << ", " << d_nodes[lhsId] << ", " << d_nodes[rhsId] << ")" << std::endl;
1955
1956 EqualityPair pair1(lhsId, rhsId);
1957 EqualityPair pair2(rhsId, lhsId);
1958
1959 // Store the fact that we've propagated this already
1960 Theory::Set notified = 0;
1961 PropagatedDisequalitiesMap::const_iterator find = d_propagatedDisequalities.find(pair1);
1962 if (find == d_propagatedDisequalities.end()) {
1963 notified = Theory::setInsert(tag);
1964 } else {
1965 notified = Theory::setInsert(tag, (*find).second);
1966 }
1967 d_propagatedDisequalities[pair1] = notified;
1968 d_propagatedDisequalities[pair2] = notified;
1969
1970 // Store the proof if provided
1971 if (d_deducedDisequalityReasons.size() > d_deducedDisequalityReasonsSize) {
1972 Debug("equality::disequality") << d_name << "::eq::storePropagatedDisequality(" << tag << ", " << d_nodes[lhsId] << ", " << d_nodes[rhsId] << "): storing proof" << std::endl;
1973 Assert(d_disequalityReasonsMap.find(pair1) == d_disequalityReasonsMap.end(), "There can't be a proof if you're adding a new one");
1974 DisequalityReasonRef ref(d_deducedDisequalityReasonsSize, d_deducedDisequalityReasons.size());
1975 #ifdef CVC4_ASSERTIONS
1976 // Check that the reasons are valid
1977 for (unsigned i = ref.mergesStart; i < ref.mergesEnd; ++ i) {
1978 Assert(getEqualityNode(d_deducedDisequalityReasons[i].first).getFind() == getEqualityNode(d_deducedDisequalityReasons[i].second).getFind());
1979 }
1980 #endif
1981 if (Debug.isOn("equality::disequality")) {
1982 for (unsigned i = ref.mergesStart; i < ref.mergesEnd; ++ i) {
1983 TNode lhs = d_nodes[d_deducedDisequalityReasons[i].first];
1984 TNode rhs = d_nodes[d_deducedDisequalityReasons[i].second];
1985 Debug("equality::disequality") << d_name << "::eq::storePropagatedDisequality(): because " << lhs << " == " << rhs << std::endl;
1986 }
1987
1988 }
1989
1990 // Store for backtracking
1991 d_deducedDisequalities.push_back(pair1);
1992 d_deducedDisequalitiesSize = d_deducedDisequalities.size();
1993 d_deducedDisequalityReasonsSize = d_deducedDisequalityReasons.size();
1994 // Store the proof reference
1995 d_disequalityReasonsMap[pair1] = ref;
1996 d_disequalityReasonsMap[pair2] = ref;
1997 } else {
1998 Assert(d_disequalityReasonsMap.find(pair1) != d_disequalityReasonsMap.end(), "You must provide a proof initially");
1999 }
2000 }
2001
getDisequalities(bool allowConstants,EqualityNodeId classId,Theory::Set inputTags,TaggedEqualitiesSet & out)2002 void EqualityEngine::getDisequalities(bool allowConstants, EqualityNodeId classId, Theory::Set inputTags, TaggedEqualitiesSet& out) {
2003 // Must be empty on input
2004 Assert(out.size() == 0);
2005 // The class we are looking for, shouldn't have any of the tags we are looking for already set
2006 Assert(d_nodeIndividualTrigger[classId] == null_set_id || Theory::setIntersection(getTriggerTermSet(d_nodeIndividualTrigger[classId]).tags, inputTags) == 0);
2007
2008 if (inputTags == 0) {
2009 return;
2010 }
2011
2012 // Set of already (through disequalities) visited equivalence classes
2013 std::set<EqualityNodeId> alreadyVisited;
2014
2015 // Go through the equivalence class
2016 EqualityNodeId currentId = classId;
2017 do {
2018
2019 Debug("equality::trigger") << d_name << "::getDisequalities() : going through uselist of " << d_nodes[currentId] << std::endl;
2020
2021 // Current node in the equivalence class
2022 EqualityNode& currentNode = getEqualityNode(currentId);
2023
2024 // Go through the uselist and look for disequalities
2025 UseListNodeId currentUseId = currentNode.getUseList();
2026 while (currentUseId != null_uselist_id) {
2027 UseListNode& useListNode = d_useListNodes[currentUseId];
2028 EqualityNodeId funId = useListNode.getApplicationId();
2029
2030 Debug("equality::trigger") << d_name << "::getDisequalities() : checking " << d_nodes[funId] << std::endl;
2031
2032 const FunctionApplication& fun = d_applications[useListNode.getApplicationId()].original;
2033 // If it's an equality asserted to false, we do the work
2034 if (fun.isEquality() && getEqualityNode(funId).getFind() == getEqualityNode(d_false).getFind()) {
2035 // Get the other equality member
2036 bool lhs = false;
2037 EqualityNodeId toCompare = fun.b;
2038 if (toCompare == currentId) {
2039 toCompare = fun.a;
2040 lhs = true;
2041 }
2042 // Representative of the other member
2043 EqualityNodeId toCompareRep = getEqualityNode(toCompare).getFind();
2044 if (toCompareRep == classId) {
2045 // We're in conflict, so we will send it out from merge
2046 out.clear();
2047 return;
2048 }
2049 // Check if we already have this one
2050 if (alreadyVisited.count(toCompareRep) == 0) {
2051 // Mark as visited
2052 alreadyVisited.insert(toCompareRep);
2053 // Get the trigger set
2054 TriggerTermSetRef toCompareTriggerSetRef = d_nodeIndividualTrigger[toCompareRep];
2055 // We only care if we're not both constants and there are trigger terms in the other class
2056 if ((allowConstants || !d_isConstant[toCompareRep]) && toCompareTriggerSetRef != null_set_id) {
2057 // Tags of the other gey
2058 TriggerTermSet& toCompareTriggerSet = getTriggerTermSet(toCompareTriggerSetRef);
2059 // We only care if there are things in inputTags that is also in toCompareTags
2060 Theory::Set commonTags = Theory::setIntersection(inputTags, toCompareTriggerSet.tags);
2061 if (commonTags) {
2062 out.push_back(TaggedEquality(funId, toCompareTriggerSetRef, lhs));
2063 }
2064 }
2065 }
2066 }
2067 // Go to the next one in the use list
2068 currentUseId = useListNode.getNext();
2069 }
2070 // Next in equivalence class
2071 currentId = currentNode.getNext();
2072 } while (!d_done && currentId != classId);
2073
2074 }
2075
propagateTriggerTermDisequalities(Theory::Set tags,TriggerTermSetRef triggerSetRef,const TaggedEqualitiesSet & disequalitiesToNotify)2076 bool EqualityEngine::propagateTriggerTermDisequalities(Theory::Set tags, TriggerTermSetRef triggerSetRef, const TaggedEqualitiesSet& disequalitiesToNotify) {
2077
2078 // No tags, no food
2079 if (!tags) {
2080 return !d_done;
2081 }
2082
2083 Assert(triggerSetRef != null_set_id);
2084
2085 // This is the class trigger set
2086 const TriggerTermSet& triggerSet = getTriggerTermSet(triggerSetRef);
2087 // Go through the disequalities and notify
2088 TaggedEqualitiesSet::const_iterator it = disequalitiesToNotify.begin();
2089 TaggedEqualitiesSet::const_iterator it_end = disequalitiesToNotify.end();
2090 for (; !d_done && it != it_end; ++ it) {
2091 // The information about the equality that is asserted to false
2092 const TaggedEquality& disequalityInfo = *it;
2093 const TriggerTermSet& disequalityTriggerSet = getTriggerTermSet(disequalityInfo.triggerSetRef);
2094 Theory::Set commonTags = Theory::setIntersection(disequalityTriggerSet.tags, tags);
2095 Assert(commonTags);
2096 // This is the actual function
2097 const FunctionApplication& fun = d_applications[disequalityInfo.equalityId].original;
2098 // Figure out who we are comparing to in the original equality
2099 EqualityNodeId toCompare = disequalityInfo.lhs ? fun.a : fun.b;
2100 EqualityNodeId myCompare = disequalityInfo.lhs ? fun.b : fun.a;
2101 if (getEqualityNode(toCompare).getFind() == getEqualityNode(myCompare).getFind()) {
2102 // We're propagating a != a, which means we're inconsistent, just bail and let it go into
2103 // a regular conflict
2104 return !d_done;
2105 }
2106 // Go through the tags, and add the disequalities
2107 TheoryId currentTag;
2108 while (!d_done && ((currentTag = Theory::setPop(commonTags)) != THEORY_LAST)) {
2109 // Get the tag representative
2110 EqualityNodeId tagRep = disequalityTriggerSet.getTrigger(currentTag);
2111 EqualityNodeId myRep = triggerSet.getTrigger(currentTag);
2112 // Propagate
2113 if (!hasPropagatedDisequality(currentTag, myRep, tagRep)) {
2114 // Construct the proof if not there already
2115 if (!hasPropagatedDisequality(myRep, tagRep)) {
2116 d_deducedDisequalityReasons.push_back(EqualityPair(myCompare, myRep));
2117 d_deducedDisequalityReasons.push_back(EqualityPair(toCompare, tagRep));
2118 d_deducedDisequalityReasons.push_back(EqualityPair(disequalityInfo.equalityId, d_falseId));
2119 }
2120 // Store the propagation
2121 storePropagatedDisequality(currentTag, myRep, tagRep);
2122 // Notify
2123 if (d_performNotify) {
2124 if (!d_notify.eqNotifyTriggerTermEquality(currentTag, d_nodes[myRep], d_nodes[tagRep], false)) {
2125 d_done = true;
2126 }
2127 }
2128 }
2129 }
2130 }
2131
2132 return !d_done;
2133 }
2134
EqClassesIterator()2135 EqClassesIterator::EqClassesIterator() :
2136 d_ee(NULL), d_it(0) {
2137 }
2138
EqClassesIterator(const eq::EqualityEngine * ee)2139 EqClassesIterator::EqClassesIterator(const eq::EqualityEngine* ee)
2140 : d_ee(ee)
2141 {
2142 Assert(d_ee->consistent());
2143 d_it = 0;
2144 // Go to the first non-internal node that is it's own representative
2145 if(d_it < d_ee->d_nodesCount && (d_ee->d_isInternal[d_it] || d_ee->getEqualityNode(d_it).getFind() != d_it)) {
2146 ++d_it;
2147 }
2148 }
2149
operator *() const2150 Node EqClassesIterator::operator*() const {
2151 return d_ee->d_nodes[d_it];
2152 }
2153
operator ==(const EqClassesIterator & i) const2154 bool EqClassesIterator::operator==(const EqClassesIterator& i) const {
2155 return d_ee == i.d_ee && d_it == i.d_it;
2156 }
2157
operator !=(const EqClassesIterator & i) const2158 bool EqClassesIterator::operator!=(const EqClassesIterator& i) const {
2159 return !(*this == i);
2160 }
2161
operator ++()2162 EqClassesIterator& EqClassesIterator::operator++() {
2163 ++d_it;
2164 while(d_it < d_ee->d_nodesCount && (d_ee->d_isInternal[d_it] || d_ee->getEqualityNode(d_it).getFind() != d_it)) {
2165 ++d_it;
2166 }
2167 return *this;
2168 }
2169
operator ++(int)2170 EqClassesIterator EqClassesIterator::operator++(int) {
2171 EqClassesIterator i = *this;
2172 ++*this;
2173 return i;
2174 }
2175
isFinished() const2176 bool EqClassesIterator::isFinished() const {
2177 return d_it >= d_ee->d_nodesCount;
2178 }
2179
EqClassIterator()2180 EqClassIterator::EqClassIterator()
2181 : d_ee(NULL)
2182 , d_start(null_id)
2183 , d_current(null_id)
2184 {}
2185
EqClassIterator(Node eqc,const eq::EqualityEngine * ee)2186 EqClassIterator::EqClassIterator(Node eqc, const eq::EqualityEngine* ee)
2187 : d_ee(ee)
2188 {
2189 Assert(d_ee->consistent());
2190 d_current = d_start = d_ee->getNodeId(eqc);
2191 Assert(d_start == d_ee->getEqualityNode(d_start).getFind());
2192 Assert (!d_ee->d_isInternal[d_start]);
2193 }
2194
operator *() const2195 Node EqClassIterator::operator*() const {
2196 return d_ee->d_nodes[d_current];
2197 }
2198
operator ==(const EqClassIterator & i) const2199 bool EqClassIterator::operator==(const EqClassIterator& i) const {
2200 return d_ee == i.d_ee && d_current == i.d_current;
2201 }
2202
operator !=(const EqClassIterator & i) const2203 bool EqClassIterator::operator!=(const EqClassIterator& i) const {
2204 return !(*this == i);
2205 }
2206
operator ++()2207 EqClassIterator& EqClassIterator::operator++() {
2208 Assert(!isFinished());
2209
2210 Assert(d_start == d_ee->getEqualityNode(d_current).getFind());
2211 Assert(!d_ee->d_isInternal[d_current]);
2212
2213 // Find the next one
2214 do {
2215 d_current = d_ee->getEqualityNode(d_current).getNext();
2216 } while(d_ee->d_isInternal[d_current]);
2217
2218 Assert(d_start == d_ee->getEqualityNode(d_current).getFind());
2219 Assert(!d_ee->d_isInternal[d_current]);
2220
2221 if(d_current == d_start) {
2222 // we end when we have cycled back to the original representative
2223 d_current = null_id;
2224 }
2225 return *this;
2226 }
2227
operator ++(int)2228 EqClassIterator EqClassIterator::operator++(int) {
2229 EqClassIterator i = *this;
2230 ++*this;
2231 return i;
2232 }
2233
isFinished() const2234 bool EqClassIterator::isFinished() const {
2235 return d_current == null_id;
2236 }
2237
debug_print(const char * c,unsigned tb,PrettyPrinter * prettyPrinter) const2238 void EqProof::debug_print(const char* c, unsigned tb, PrettyPrinter* prettyPrinter) const {
2239 for(unsigned i=0; i<tb; i++) { Debug( c ) << " "; }
2240
2241 if (prettyPrinter)
2242 Debug( c ) << prettyPrinter->printTag(d_id);
2243 else
2244 Debug( c ) << d_id;
2245
2246 Debug( c ) << "(";
2247 if( !d_children.empty() || !d_node.isNull() ){
2248 if( !d_node.isNull() ){
2249 Debug( c ) << std::endl;
2250 for( unsigned i=0; i<tb+1; i++ ) { Debug( c ) << " "; }
2251 Debug( c ) << d_node;
2252 }
2253 for( unsigned i=0; i<d_children.size(); i++ ){
2254 if( i>0 || !d_node.isNull() ) Debug( c ) << ",";
2255 Debug( c ) << std::endl;
2256 d_children[i]->debug_print( c, tb+1, prettyPrinter );
2257 }
2258 }
2259 Debug( c ) << ")" << std::endl;
2260 }
2261
2262 } // Namespace uf
2263 } // Namespace theory
2264 } // Namespace CVC4
2265