1 /*! \file congruence_manager.cpp
3 ** Original author: Tim King
4 ** Major contributors: none
5 ** Minor contributors (to current version): Morgan Deters, Dejan Jovanovic
6 ** This file is part of the CVC4 project.
7 ** Copyright (c) 2009-2014 New York University and The University of Iowa
8 ** See the file COPYING in the top-level source directory for licensing
9 ** information.\endverbatim
11 ** \brief [[ Add one-line brief description here ]]
13 ** [[ Add lengthier description here ]]
14 ** \todo document this file
17 #include "theory/arith/congruence_manager.h"
19 #include "theory/arith/constraint.h"
20 #include "theory/arith/arith_utilities.h"
26 ArithCongruenceManager::ArithCongruenceManager(context::Context
* c
, ConstraintDatabase
& cd
, SetupLiteralCallBack setup
, const ArithVariables
& avars
, RaiseEqualityEngineConflict raiseConflict
)
28 d_raiseConflict(raiseConflict
),
33 d_constraintDatabase(cd
),
34 d_setupLiteral(setup
),
36 d_ee(d_notify
, c
, "theory::arith::ArithCongruenceManager", true)
39 ArithCongruenceManager::Statistics::Statistics():
40 d_watchedVariables("theory::arith::congruence::watchedVariables", 0),
41 d_watchedVariableIsZero("theory::arith::congruence::watchedVariableIsZero", 0),
42 d_watchedVariableIsNotZero("theory::arith::congruence::watchedVariableIsNotZero", 0),
43 d_equalsConstantCalls("theory::arith::congruence::equalsConstantCalls", 0),
44 d_propagations("theory::arith::congruence::propagations", 0),
45 d_propagateConstraints("theory::arith::congruence::propagateConstraints", 0),
46 d_conflicts("theory::arith::congruence::conflicts", 0)
48 StatisticsRegistry::registerStat(&d_watchedVariables
);
49 StatisticsRegistry::registerStat(&d_watchedVariableIsZero
);
50 StatisticsRegistry::registerStat(&d_watchedVariableIsNotZero
);
51 StatisticsRegistry::registerStat(&d_equalsConstantCalls
);
52 StatisticsRegistry::registerStat(&d_propagations
);
53 StatisticsRegistry::registerStat(&d_propagateConstraints
);
54 StatisticsRegistry::registerStat(&d_conflicts
);
57 ArithCongruenceManager::Statistics::~Statistics(){
58 StatisticsRegistry::unregisterStat(&d_watchedVariables
);
59 StatisticsRegistry::unregisterStat(&d_watchedVariableIsZero
);
60 StatisticsRegistry::unregisterStat(&d_watchedVariableIsNotZero
);
61 StatisticsRegistry::unregisterStat(&d_equalsConstantCalls
);
62 StatisticsRegistry::unregisterStat(&d_propagations
);
63 StatisticsRegistry::unregisterStat(&d_propagateConstraints
);
64 StatisticsRegistry::unregisterStat(&d_conflicts
);
67 ArithCongruenceManager::ArithCongruenceNotify::ArithCongruenceNotify(ArithCongruenceManager
& acm
)
71 bool ArithCongruenceManager::ArithCongruenceNotify::eqNotifyTriggerEquality(TNode equality
, bool value
) {
72 Debug("arith::congruences") << "ArithCongruenceNotify::eqNotifyTriggerEquality(" << equality
<< ", " << (value
? "true" : "false") << ")" << std::endl
;
74 return d_acm
.propagate(equality
);
76 return d_acm
.propagate(equality
.notNode());
79 bool ArithCongruenceManager::ArithCongruenceNotify::eqNotifyTriggerPredicate(TNode predicate
, bool value
) {
83 bool ArithCongruenceManager::ArithCongruenceNotify::eqNotifyTriggerTermEquality(TheoryId tag
, TNode t1
, TNode t2
, bool value
) {
84 Debug("arith::congruences") << "ArithCongruenceNotify::eqNotifyTriggerTermEquality(" << t1
<< ", " << t2
<< ", " << (value
? "true" : "false") << ")" << std::endl
;
86 return d_acm
.propagate(t1
.eqNode(t2
));
88 return d_acm
.propagate(t1
.eqNode(t2
).notNode());
91 void ArithCongruenceManager::ArithCongruenceNotify::eqNotifyConstantTermMerge(TNode t1
, TNode t2
) {
92 Debug("arith::congruences") << "ArithCongruenceNotify::eqNotifyConstantTermMerge(" << t1
<< ", " << t2
<< std::endl
;
93 if (t1
.getKind() == kind::CONST_BOOLEAN
) {
94 d_acm
.propagate(t1
.iffNode(t2
));
96 d_acm
.propagate(t1
.eqNode(t2
));
99 void ArithCongruenceManager::ArithCongruenceNotify::eqNotifyNewClass(TNode t
) {
101 void ArithCongruenceManager::ArithCongruenceNotify::eqNotifyPreMerge(TNode t1
, TNode t2
) {
103 void ArithCongruenceManager::ArithCongruenceNotify::eqNotifyPostMerge(TNode t1
, TNode t2
) {
105 void ArithCongruenceManager::ArithCongruenceNotify::eqNotifyDisequal(TNode t1
, TNode t2
, TNode reason
) {
108 void ArithCongruenceManager::raiseConflict(Node conflict
){
109 Assert(!inConflict());
110 Debug("arith::conflict") << "difference manager conflict " << conflict
<< std::endl
;
111 d_inConflict
.raise();
112 d_raiseConflict
.raiseEEConflict(conflict
);
114 bool ArithCongruenceManager::inConflict() const{
115 return d_inConflict
.isRaised();
118 bool ArithCongruenceManager::hasMorePropagations() const {
119 return !d_propagatations
.empty();
121 const Node
ArithCongruenceManager::getNextPropagation() {
122 Assert(hasMorePropagations());
123 Node prop
= d_propagatations
.front();
124 d_propagatations
.dequeue();
128 bool ArithCongruenceManager::canExplain(TNode n
) const {
129 return d_explanationMap
.find(n
) != d_explanationMap
.end();
132 void ArithCongruenceManager::setMasterEqualityEngine(eq::EqualityEngine
* eq
) {
133 d_ee
.setMasterEqualityEngine(eq
);
136 Node
ArithCongruenceManager::externalToInternal(TNode n
) const{
137 Assert(canExplain(n
));
138 ExplainMap::const_iterator iter
= d_explanationMap
.find(n
);
139 size_t pos
= (*iter
).second
;
140 return d_propagatations
[pos
];
143 void ArithCongruenceManager::pushBack(TNode n
){
144 d_explanationMap
.insert(n
, d_propagatations
.size());
145 d_propagatations
.enqueue(n
);
147 ++(d_statistics
.d_propagations
);
149 void ArithCongruenceManager::pushBack(TNode n
, TNode r
){
150 d_explanationMap
.insert(r
, d_propagatations
.size());
151 d_explanationMap
.insert(n
, d_propagatations
.size());
152 d_propagatations
.enqueue(n
);
154 ++(d_statistics
.d_propagations
);
156 void ArithCongruenceManager::pushBack(TNode n
, TNode r
, TNode w
){
157 d_explanationMap
.insert(w
, d_propagatations
.size());
158 d_explanationMap
.insert(r
, d_propagatations
.size());
159 d_explanationMap
.insert(n
, d_propagatations
.size());
160 d_propagatations
.enqueue(n
);
162 ++(d_statistics
.d_propagations
);
165 void ArithCongruenceManager::watchedVariableIsZero(ConstraintCP lb
, ConstraintCP ub
){
166 Assert(lb
->isLowerBound());
167 Assert(ub
->isUpperBound());
168 Assert(lb
->getVariable() == ub
->getVariable());
169 Assert(lb
->getValue().sgn() == 0);
170 Assert(ub
->getValue().sgn() == 0);
172 ++(d_statistics
.d_watchedVariableIsZero
);
174 ArithVar s
= lb
->getVariable();
175 Node reason
= Constraint::externalExplainByAssertions(lb
,ub
);
177 d_keepAlive
.push_back(reason
);
178 assertionToEqualityEngine(true, s
, reason
);
181 void ArithCongruenceManager::watchedVariableIsZero(ConstraintCP eq
){
182 Assert(eq
->isEquality());
183 Assert(eq
->getValue().sgn() == 0);
185 ++(d_statistics
.d_watchedVariableIsZero
);
187 ArithVar s
= eq
->getVariable();
189 //Explain for conflict is correct as these proofs are generated
191 //These will be safe for propagation later as well
192 Node reason
= eq
->externalExplainByAssertions();
194 d_keepAlive
.push_back(reason
);
195 assertionToEqualityEngine(true, s
, reason
);
198 void ArithCongruenceManager::watchedVariableCannotBeZero(ConstraintCP c
){
199 ++(d_statistics
.d_watchedVariableIsNotZero
);
201 ArithVar s
= c
->getVariable();
203 //Explain for conflict is correct as these proofs are generated and stored eagerly
204 //These will be safe for propagation later as well
205 Node reason
= c
->externalExplainByAssertions();
207 d_keepAlive
.push_back(reason
);
208 assertionToEqualityEngine(false, s
, reason
);
212 bool ArithCongruenceManager::propagate(TNode x
){
213 Debug("arith::congruenceManager")<< "ArithCongruenceManager::propagate("<<x
<<")"<<std::endl
;
218 Node rewritten
= Rewriter::rewrite(x
);
220 //Need to still propagate this!
221 if(rewritten
.getKind() == kind::CONST_BOOLEAN
){
224 if(rewritten
.getConst
<bool>()){
227 ++(d_statistics
.d_conflicts
);
229 Node conf
= flattenAnd(explainInternal(x
));
231 Debug("arith::congruenceManager") << "rewritten to false "<<x
<<" with explanation "<< conf
<< std::endl
;
236 Assert(rewritten
.getKind() != kind::CONST_BOOLEAN
);
238 ConstraintP c
= d_constraintDatabase
.lookup(rewritten
);
239 if(c
== NullConstraint
){
240 //using setup as there may not be a corresponding congruence literal yet
241 d_setupLiteral(rewritten
);
242 c
= d_constraintDatabase
.lookup(rewritten
);
243 Assert(c
!= NullConstraint
);
246 Debug("arith::congruenceManager")<< "x is "
247 << c
->hasProof() << " "
248 << (x
== rewritten
) << " "
249 << c
->canBePropagated() << " "
250 << c
->negationHasProof() << std::endl
;
252 if(c
->negationHasProof()){
253 Node expC
= explainInternal(x
);
254 ConstraintCP negC
= c
->getNegation();
255 Node neg
= negC
->externalExplainByAssertions();
256 Node conf
= expC
.andNode(neg
);
257 Node final
= flattenAnd(conf
);
259 ++(d_statistics
.d_conflicts
);
260 raiseConflict(final
);
261 Debug("arith::congruenceManager") << "congruenceManager found a conflict " << final
<< std::endl
;
265 // Cases for propagation
267 // S : x == rewritten
268 // P : c can be propagated
271 // 000 : propagate x, and mark C it as being explained
272 // 001 : propagate x, and propagate c after marking it as being explained
273 // 01* : propagate x, mark c but do not propagate c
274 // 10* : propagate x, do not mark c and do not propagate c
275 // 11* : drop the constraint, do not propagate x or c
277 if(!c
->hasProof() && x
!= rewritten
){
278 if(c
->assertedToTheTheory()){
279 pushBack(x
, rewritten
, c
->getWitness());
281 pushBack(x
, rewritten
);
284 c
->setEqualityEngineProof();
285 if(c
->canBePropagated() && !c
->assertedToTheTheory()){
287 ++(d_statistics
.d_propagateConstraints
);
290 }else if(!c
->hasProof() && x
== rewritten
){
291 if(c
->assertedToTheTheory()){
292 pushBack(x
, c
->getWitness());
296 c
->setEqualityEngineProof();
297 }else if(c
->hasProof() && x
!= rewritten
){
298 if(c
->assertedToTheTheory()){
299 pushBack(x
, rewritten
, c
->getWitness());
301 pushBack(x
, rewritten
);
304 Assert(c
->hasProof() && x
== rewritten
);
309 void ArithCongruenceManager::explain(TNode literal
, std::vector
<TNode
>& assumptions
) {
310 if (literal
.getKind() != kind::NOT
) {
311 d_ee
.explainEquality(literal
[0], literal
[1], true, assumptions
);
313 d_ee
.explainEquality(literal
[0][0], literal
[0][1], false, assumptions
);
317 void ArithCongruenceManager::enqueueIntoNB(const std::set
<TNode
> s
, NodeBuilder
<>& nb
){
318 std::set
<TNode
>::const_iterator it
= s
.begin();
319 std::set
<TNode
>::const_iterator it_end
= s
.end();
320 for(; it
!= it_end
; ++it
) {
325 Node
ArithCongruenceManager::explainInternal(TNode internal
){
326 std::vector
<TNode
> assumptions
;
327 explain(internal
, assumptions
);
329 std::set
<TNode
> assumptionSet
;
330 assumptionSet
.insert(assumptions
.begin(), assumptions
.end());
332 if (assumptionSet
.size() == 1) {
333 // All the same, or just one
334 return assumptions
[0];
336 NodeBuilder
<> conjunction(kind::AND
);
337 enqueueIntoNB(assumptionSet
, conjunction
);
341 Node
ArithCongruenceManager::explain(TNode external
){
342 Node internal
= externalToInternal(external
);
343 return explainInternal(internal
);
346 void ArithCongruenceManager::explain(TNode external
, NodeBuilder
<>& out
){
347 Node internal
= externalToInternal(external
);
349 std::vector
<TNode
> assumptions
;
350 explain(internal
, assumptions
);
351 std::set
<TNode
> assumptionSet
;
352 assumptionSet
.insert(assumptions
.begin(), assumptions
.end());
354 enqueueIntoNB(assumptionSet
, out
);
357 void ArithCongruenceManager::addWatchedPair(ArithVar s
, TNode x
, TNode y
){
358 Assert(!isWatchedVariable(s
));
360 Debug("arith::congruenceManager")
361 << "addWatchedPair(" << s
<< ", " << x
<< ", " << y
<< ")" << std::endl
;
364 ++(d_statistics
.d_watchedVariables
);
366 d_watchedVariables
.add(s
);
368 Node eq
= x
.eqNode(y
);
369 d_watchedEqualities
.set(s
, eq
);
372 void ArithCongruenceManager::assertionToEqualityEngine(bool isEquality
, ArithVar s
, TNode reason
){
373 Assert(isWatchedVariable(s
));
375 TNode eq
= d_watchedEqualities
[s
];
376 Assert(eq
.getKind() == kind::EQUAL
);
379 d_ee
.assertEquality(eq
, true, reason
);
381 d_ee
.assertEquality(eq
, false, reason
);
385 void ArithCongruenceManager::equalsConstant(ConstraintCP c
){
386 Assert(c
->isEquality());
388 ++(d_statistics
.d_equalsConstantCalls
);
389 Debug("equalsConstant") << "equals constant " << c
<< std::endl
;
391 ArithVar x
= c
->getVariable();
392 Node xAsNode
= d_avariables
.asNode(x
);
393 Node asRational
= mkRationalNode(c
->getValue().getNoninfinitesimalPart());
396 //No guarentee this is in normal form!
397 Node eq
= xAsNode
.eqNode(asRational
);
398 d_keepAlive
.push_back(eq
);
400 Node reason
= c
->externalExplainByAssertions();
401 d_keepAlive
.push_back(reason
);
403 d_ee
.assertEquality(eq
, true, reason
);
406 void ArithCongruenceManager::equalsConstant(ConstraintCP lb
, ConstraintCP ub
){
407 Assert(lb
->isLowerBound());
408 Assert(ub
->isUpperBound());
409 Assert(lb
->getVariable() == ub
->getVariable());
411 ++(d_statistics
.d_equalsConstantCalls
);
412 Debug("equalsConstant") << "equals constant " << lb
<< std::endl
415 ArithVar x
= lb
->getVariable();
416 Node reason
= Constraint::externalExplainByAssertions(lb
,ub
);
418 Node xAsNode
= d_avariables
.asNode(x
);
419 Node asRational
= mkRationalNode(lb
->getValue().getNoninfinitesimalPart());
421 //No guarentee this is in normal form!
422 Node eq
= xAsNode
.eqNode(asRational
);
423 d_keepAlive
.push_back(eq
);
424 d_keepAlive
.push_back(reason
);
427 d_ee
.assertEquality(eq
, true, reason
);
430 void ArithCongruenceManager::addSharedTerm(Node x
){
431 d_ee
.addTriggerTerm(x
, THEORY_ARITH
);
434 }/* CVC4::theory::arith namespace */
435 }/* CVC4::theory namespace */
436 }/* CVC4 namespace */