1 /********************* */
2 /*! \file theory_uf.cpp
4 ** Original author: dejan
5 ** Major contributors: mdeters
6 ** Minor contributors (to current version): none
7 ** This file is part of the CVC4 prototype.
8 ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys)
9 ** Courant Institute of Mathematical Sciences
10 ** New York University
11 ** See the file COPYING in the top-level source directory for licensing
12 ** information.\endverbatim
14 ** \brief This is the interface to TheoryUF implementations
16 ** This is the interface to TheoryUF implementations. All
17 ** implementations of TheoryUF should inherit from this class.
20 #include "theory/uf/theory_uf.h"
21 #include "theory/uf/theory_uf_instantiator.h"
22 #include "theory/uf/theory_uf_strong_solver.h"
23 #include "theory/model.h"
27 using namespace CVC4::theory
;
28 using namespace CVC4::theory::uf
;
30 /** Constructs a new instance of TheoryUF w.r.t. the provided context.*/
31 TheoryUF::TheoryUF(context::Context
* c
, context::UserContext
* u
, OutputChannel
& out
, Valuation valuation
, const LogicInfo
& logicInfo
, QuantifiersEngine
* qe
) :
32 Theory(THEORY_UF
, c
, u
, out
, valuation
, logicInfo
, qe
),
34 d_equalityEngine(d_notify
, c
, "theory::uf::TheoryUF"),
36 d_literalsToPropagate(c
),
37 d_literalsToPropagateIndex(c
, 0),
40 // The kinds we are treating as function application in congruence
41 d_equalityEngine
.addFunctionKind(kind::APPLY_UF
);
43 if (Options::current()->finiteModelFind
) {
44 d_thss
= new StrongSolverTheoryUf(c
, u
, out
, this);
48 }/* TheoryUF::TheoryUF() */
50 static Node
mkAnd(const std::vector
<TNode
>& conjunctions
) {
51 Assert(conjunctions
.size() > 0);
54 all
.insert(conjunctions
.begin(), conjunctions
.end());
56 if (all
.size() == 1) {
57 // All the same, or just one
58 return conjunctions
[0];
61 NodeBuilder
<> conjunction(kind::AND
);
62 std::set
<TNode
>::const_iterator it
= all
.begin();
63 std::set
<TNode
>::const_iterator it_end
= all
.end();
64 while (it
!= it_end
) {
72 void TheoryUF::check(Effort level
) {
74 while (!done() && !d_conflict
)
76 // Get all the assertions
77 Assertion assertion
= get();
78 TNode fact
= assertion
.assertion
;
80 Debug("uf") << "TheoryUF::check(): processing " << fact
<< std::endl
;
82 bool isDecision
= d_valuation
.isSatLiteral(fact
) && d_valuation
.isDecision(fact
);
83 d_thss
->assertNode(fact
, isDecision
);
87 bool polarity
= fact
.getKind() != kind::NOT
;
88 TNode atom
= polarity
? fact
: fact
[0];
89 if (atom
.getKind() == kind::EQUAL
) {
90 d_equalityEngine
.assertEquality(atom
, polarity
, fact
);
91 } else if (atom
.getKind() == kind::CARDINALITY_CONSTRAINT
) {
94 d_equalityEngine
.assertPredicate(atom
, polarity
, fact
);
101 d_thss
->check(level
);
105 }/* TheoryUF::check() */
107 void TheoryUF::preRegisterTerm(TNode node
) {
108 Debug("uf") << "TheoryUF::preRegisterTerm(" << node
<< ")" << std::endl
;
110 if (d_thss
!= NULL
) {
111 d_thss
->preRegisterTerm(node
);
114 switch (node
.getKind()) {
116 // Add the trigger for equality
117 d_equalityEngine
.addTriggerEquality(node
);
120 // Maybe it's a predicate
121 if (node
.getType().isBoolean()) {
122 // Get triggered for both equal and dis-equal
123 d_equalityEngine
.addTriggerPredicate(node
);
125 // Function applications/predicates
126 d_equalityEngine
.addTerm(node
);
128 // Remember the function and predicate terms
129 d_functionsTerms
.push_back(node
);
133 d_equalityEngine
.addTerm(node
);
136 }/* TheoryUF::preRegisterTerm() */
138 bool TheoryUF::propagate(TNode literal
) {
139 Debug("uf::propagate") << "TheoryUF::propagate(" << literal
<< ")" << std::endl
;
140 // If already in conflict, no more propagation
142 Debug("uf::propagate") << "TheoryUF::propagate(" << literal
<< "): already in conflict" << std::endl
;
146 bool ok
= d_out
->propagate(literal
);
151 }/* TheoryUF::propagate(TNode) */
153 void TheoryUF::propagate(Effort effort
) {
154 if (d_thss
!= NULL
) {
155 return d_thss
->propagate(effort
);
159 void TheoryUF::explain(TNode literal
, std::vector
<TNode
>& assumptions
) {
161 bool polarity
= literal
.getKind() != kind::NOT
;
162 TNode atom
= polarity
? literal
: literal
[0];
163 if (atom
.getKind() == kind::EQUAL
|| atom
.getKind() == kind::IFF
) {
164 d_equalityEngine
.explainEquality(atom
[0], atom
[1], polarity
, assumptions
);
166 d_equalityEngine
.explainPredicate(atom
, polarity
, assumptions
);
170 Node
TheoryUF::explain(TNode literal
) {
171 Debug("uf") << "TheoryUF::explain(" << literal
<< ")" << std::endl
;
172 std::vector
<TNode
> assumptions
;
173 explain(literal
, assumptions
);
174 return mkAnd(assumptions
);
177 void TheoryUF::collectModelInfo( TheoryModel
* m
){
178 m
->assertEqualityEngine( &d_equalityEngine
);
181 void TheoryUF::presolve() {
182 // TimerStat::CodeTimer codeTimer(d_presolveTimer);
184 Debug("uf") << "uf: begin presolve()" << endl
;
185 if(Options::current()->ufSymmetryBreaker
) {
186 vector
<Node
> newClauses
;
187 d_symb
.apply(newClauses
);
188 for(vector
<Node
>::const_iterator i
= newClauses
.begin();
189 i
!= newClauses
.end();
194 Debug("uf") << "uf: end presolve()" << endl
;
197 void TheoryUF::ppStaticLearn(TNode n
, NodeBuilder
<>& learned
) {
198 //TimerStat::CodeTimer codeTimer(d_staticLearningTimer);
200 vector
<TNode
> workList
;
201 workList
.push_back(n
);
202 __gnu_cxx::hash_set
<TNode
, TNodeHashFunction
> processed
;
204 while(!workList
.empty()) {
207 bool unprocessedChildren
= false;
208 for(TNode::iterator i
= n
.begin(), iend
= n
.end(); i
!= iend
; ++i
) {
209 if(processed
.find(*i
) == processed
.end()) {
211 workList
.push_back(*i
);
212 unprocessedChildren
= true;
216 if(unprocessedChildren
) {
221 // has node n been processed in the meantime ?
222 if(processed
.find(n
) != processed
.end()) {
229 Debug("diamonds") << "===================== looking at" << endl
232 // binary OR of binary ANDs of EQUALities
233 if(n
.getKind() == kind::OR
&& n
.getNumChildren() == 2 &&
234 n
[0].getKind() == kind::AND
&& n
[0].getNumChildren() == 2 &&
235 n
[1].getKind() == kind::AND
&& n
[1].getNumChildren() == 2 &&
236 (n
[0][0].getKind() == kind::EQUAL
|| n
[0][0].getKind() == kind::IFF
) &&
237 (n
[0][1].getKind() == kind::EQUAL
|| n
[0][1].getKind() == kind::IFF
) &&
238 (n
[1][0].getKind() == kind::EQUAL
|| n
[1][0].getKind() == kind::IFF
) &&
239 (n
[1][1].getKind() == kind::EQUAL
|| n
[1][1].getKind() == kind::IFF
)) {
240 // now we have (a = b && c = d) || (e = f && g = h)
242 Debug("diamonds") << "has form of a diamond!" << endl
;
245 a
= n
[0][0][0], b
= n
[0][0][1],
246 c
= n
[0][1][0], d
= n
[0][1][1],
247 e
= n
[1][0][0], f
= n
[1][0][1],
248 g
= n
[1][1][0], h
= n
[1][1][1];
250 // test that one of {a, b} = one of {c, d}, and make "b" the
251 // shared node (i.e. put in the form (a = b && b = d))
252 // note we don't actually care about the shared ones, so the
253 // "swaps" below are one-sided, ignoring b and c
264 // condition not satisfied
265 Debug("diamonds") << "+ A fails" << endl
;
269 Debug("diamonds") << "+ A holds" << endl
;
271 // same: one of {e, f} = one of {g, h}, and make "f" the
272 // shared node (i.e. put in the form (e = f && f = h))
283 // condition not satisfied
284 Debug("diamonds") << "+ B fails" << endl
;
288 Debug("diamonds") << "+ B holds" << endl
;
290 // now we have (a = b && b = d) || (e = f && f = h)
291 // test that {a, d} == {e, h}
292 if( (a
== e
&& d
== h
) ||
293 (a
== h
&& d
== e
) ) {
294 // learn: n implies a == d
295 Debug("diamonds") << "+ C holds" << endl
;
296 Node newEquality
= a
.getType().isBoolean() ? a
.iffNode(d
) : a
.eqNode(d
);
297 Debug("diamonds") << " ==> " << newEquality
<< endl
;
298 learned
<< n
.impNode(newEquality
);
300 Debug("diamonds") << "+ C fails" << endl
;
305 if(Options::current()->ufSymmetryBreaker
) {
306 d_symb
.assertFormula(n
);
308 }/* TheoryUF::ppStaticLearn() */
310 EqualityStatus
TheoryUF::getEqualityStatus(TNode a
, TNode b
) {
312 // Check for equality (simplest)
313 if (d_equalityEngine
.areEqual(a
, b
)) {
314 // The terms are implied to be equal
315 return EQUALITY_TRUE
;
318 // Check for disequality
319 if (d_equalityEngine
.areDisequal(a
, b
, false)) {
320 // The terms are implied to be dis-equal
321 return EQUALITY_FALSE
;
324 // All other terms we interpret as dis-equal in the model
325 return EQUALITY_FALSE_IN_MODEL
;
328 void TheoryUF::addSharedTerm(TNode t
) {
329 Debug("uf::sharing") << "TheoryUF::addSharedTerm(" << t
<< ")" << std::endl
;
330 d_equalityEngine
.addTriggerTerm(t
, THEORY_UF
);
333 void TheoryUF::computeCareGraph() {
335 if (d_sharedTerms
.size() > 0) {
337 vector
< pair
<TNode
, TNode
> > currentPairs
;
339 // Go through the function terms and see if there are any to split on
340 unsigned functionTerms
= d_functionsTerms
.size();
341 for (unsigned i
= 0; i
< functionTerms
; ++ i
) {
343 TNode f1
= d_functionsTerms
[i
];
344 Node op
= f1
.getOperator();
346 for (unsigned j
= i
+ 1; j
< functionTerms
; ++ j
) {
348 TNode f2
= d_functionsTerms
[j
];
350 // If the operators are not the same, we can skip this pair
351 if (f2
.getOperator() != op
) {
355 Debug("uf::sharing") << "TheoryUf::computeCareGraph(): checking function " << f1
<< " and " << f2
<< std::endl
;
357 // If the terms are already known to be equal, we are also in good shape
358 if (d_equalityEngine
.areEqual(f1
, f2
)) {
359 Debug("uf::sharing") << "TheoryUf::computeCareGraph(): equal, skipping" << std::endl
;
363 // We have two functions f(x1, ..., xn) and f(y1, ..., yn) no known to be equal
364 // We split on the argument pairs that are are not known, unless there is some
365 // argument pair that is already dis-equal.
366 bool somePairIsDisequal
= false;
367 currentPairs
.clear();
368 for (unsigned k
= 0; k
< f1
.getNumChildren(); ++ k
) {
373 Debug("uf::sharing") << "TheoryUf::computeCareGraph(): checking arguments " << x
<< " and " << y
<< std::endl
;
375 if (d_equalityEngine
.areDisequal(x
, y
, false)) {
376 // Mark that there is a dis-equal pair and break
377 somePairIsDisequal
= true;
378 Debug("uf::sharing") << "TheoryUf::computeCareGraph(): disequal, disregarding all" << std::endl
;
382 if (d_equalityEngine
.areEqual(x
, y
)) {
383 // We don't need this one
384 Debug("uf::sharing") << "TheoryUf::computeCareGraph(): equal" << std::endl
;
388 if (!d_equalityEngine
.isTriggerTerm(x
, THEORY_UF
) || !d_equalityEngine
.isTriggerTerm(y
, THEORY_UF
)) {
389 // Not connected to shared terms, we don't care
393 // Get representative trigger terms
394 TNode x_shared
= d_equalityEngine
.getTriggerTermRepresentative(x
, THEORY_UF
);
395 TNode y_shared
= d_equalityEngine
.getTriggerTermRepresentative(y
, THEORY_UF
);
397 EqualityStatus eqStatusDomain
= d_valuation
.getEqualityStatus(x_shared
, y_shared
);
398 switch (eqStatusDomain
) {
399 case EQUALITY_FALSE_AND_PROPAGATED
:
401 case EQUALITY_FALSE_IN_MODEL
:
402 somePairIsDisequal
= true;
410 // Otherwise, we need to figure it out
411 Debug("uf::sharing") << "TheoryUf::computeCareGraph(): adding to care-graph" << std::endl
;
412 currentPairs
.push_back(make_pair(x_shared
, y_shared
));
415 if (!somePairIsDisequal
) {
416 for (unsigned i
= 0; i
< currentPairs
.size(); ++ i
) {
417 addCarePair(currentPairs
[i
].first
, currentPairs
[i
].second
);
423 }/* TheoryUF::computeCareGraph() */
425 void TheoryUF::conflict(TNode a
, TNode b
) {
426 if (a
.getKind() == kind::CONST_BOOLEAN
) {
427 d_conflictNode
= explain(a
.iffNode(b
));
429 d_conflictNode
= explain(a
.eqNode(b
));
431 d_out
->conflict(d_conflictNode
);
435 void TheoryUF::eqNotifyNewClass(TNode t
) {
436 if (d_thss
!= NULL
) {
437 d_thss
->newEqClass(t
);
439 // this can be called very early, during initialization
440 if (!getLogicInfo().isLocked() || getLogicInfo().isQuantified()) {
441 ((InstantiatorTheoryUf
*) getInstantiator())->newEqClass(t
);
445 void TheoryUF::eqNotifyPreMerge(TNode t1
, TNode t2
) {
446 if (getLogicInfo().isQuantified()) {
447 ((InstantiatorTheoryUf
*) getInstantiator())->merge(t1
, t2
);
451 void TheoryUF::eqNotifyPostMerge(TNode t1
, TNode t2
) {
452 if (d_thss
!= NULL
) {
453 d_thss
->merge(t1
, t2
);
457 void TheoryUF::eqNotifyDisequal(TNode t1
, TNode t2
, TNode reason
) {
458 if (d_thss
!= NULL
) {
459 d_thss
->assertDisequal(t1
, t2
, reason
);
461 if (getLogicInfo().isQuantified()) {
462 ((InstantiatorTheoryUf
*) getInstantiator())->assertDisequal(t1
, t2
, reason
);
466 Node
TheoryUF::ppRewrite(TNode node
) {
468 if (node
.getKind() != kind::APPLY_UF
) {
472 // perform the callbacks requested by TheoryUF::registerPpRewrite()
473 RegisterPpRewrites::iterator c
= d_registeredPpRewrites
.find(node
.getOperator());
474 if (c
== d_registeredPpRewrites
.end()) {
477 Node res
= c
->second
->ppRewrite(node
);
479 return ppRewrite(res
);