1 /********************* */
2 /*! \file congruence_manager.cpp
4 ** Top contributors (to current version):
5 ** Tim King, Paul Meng, Dejan Jovanovic
6 ** This file is part of the CVC4 project.
7 ** Copyright (c) 2009-2017 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
12 ** \brief [[ Add one-line brief description here ]]
14 ** [[ Add lengthier description here ]]
15 ** \todo document this file
18 #include "theory/arith/congruence_manager.h"
20 #include "base/output.h"
21 #include "smt/smt_statistics_registry.h"
22 #include "theory/arith/arith_utilities.h"
23 #include "theory/arith/constraint.h"
24 #include "theory/quantifiers/equality_infer.h"
25 #include "options/arith_options.h"
31 ArithCongruenceManager::ArithCongruenceManager(context::Context
* c
, ConstraintDatabase
& cd
, SetupLiteralCallBack setup
, const ArithVariables
& avars
, RaiseEqualityEngineConflict raiseConflict
)
33 d_raiseConflict(raiseConflict
),
40 d_constraintDatabase(cd
),
41 d_setupLiteral(setup
),
43 d_ee(d_notify
, c
, "theory::arith::ArithCongruenceManager", true)
45 d_ee
.addFunctionKind(kind::NONLINEAR_MULT
);
46 d_ee
.addFunctionKind(kind::EXPONENTIAL
);
47 d_ee
.addFunctionKind(kind::SINE
);
48 //module to infer additional equalities based on normalization
49 if( options::sNormInferEq() ){
50 d_eq_infer
= new quantifiers::EqualityInference(c
, true);
51 d_true
= NodeManager::currentNM()->mkConst( true );
55 ArithCongruenceManager::~ArithCongruenceManager() {
61 ArithCongruenceManager::Statistics::Statistics():
62 d_watchedVariables("theory::arith::congruence::watchedVariables", 0),
63 d_watchedVariableIsZero("theory::arith::congruence::watchedVariableIsZero", 0),
64 d_watchedVariableIsNotZero("theory::arith::congruence::watchedVariableIsNotZero", 0),
65 d_equalsConstantCalls("theory::arith::congruence::equalsConstantCalls", 0),
66 d_propagations("theory::arith::congruence::propagations", 0),
67 d_propagateConstraints("theory::arith::congruence::propagateConstraints", 0),
68 d_conflicts("theory::arith::congruence::conflicts", 0)
70 smtStatisticsRegistry()->registerStat(&d_watchedVariables
);
71 smtStatisticsRegistry()->registerStat(&d_watchedVariableIsZero
);
72 smtStatisticsRegistry()->registerStat(&d_watchedVariableIsNotZero
);
73 smtStatisticsRegistry()->registerStat(&d_equalsConstantCalls
);
74 smtStatisticsRegistry()->registerStat(&d_propagations
);
75 smtStatisticsRegistry()->registerStat(&d_propagateConstraints
);
76 smtStatisticsRegistry()->registerStat(&d_conflicts
);
79 ArithCongruenceManager::Statistics::~Statistics(){
80 smtStatisticsRegistry()->unregisterStat(&d_watchedVariables
);
81 smtStatisticsRegistry()->unregisterStat(&d_watchedVariableIsZero
);
82 smtStatisticsRegistry()->unregisterStat(&d_watchedVariableIsNotZero
);
83 smtStatisticsRegistry()->unregisterStat(&d_equalsConstantCalls
);
84 smtStatisticsRegistry()->unregisterStat(&d_propagations
);
85 smtStatisticsRegistry()->unregisterStat(&d_propagateConstraints
);
86 smtStatisticsRegistry()->unregisterStat(&d_conflicts
);
89 ArithCongruenceManager::ArithCongruenceNotify::ArithCongruenceNotify(ArithCongruenceManager
& acm
)
93 bool ArithCongruenceManager::ArithCongruenceNotify::eqNotifyTriggerEquality(TNode equality
, bool value
) {
94 Debug("arith::congruences") << "ArithCongruenceNotify::eqNotifyTriggerEquality(" << equality
<< ", " << (value
? "true" : "false") << ")" << std::endl
;
96 return d_acm
.propagate(equality
);
98 return d_acm
.propagate(equality
.notNode());
101 bool ArithCongruenceManager::ArithCongruenceNotify::eqNotifyTriggerPredicate(TNode predicate
, bool value
) {
105 bool ArithCongruenceManager::ArithCongruenceNotify::eqNotifyTriggerTermEquality(TheoryId tag
, TNode t1
, TNode t2
, bool value
) {
106 Debug("arith::congruences") << "ArithCongruenceNotify::eqNotifyTriggerTermEquality(" << t1
<< ", " << t2
<< ", " << (value
? "true" : "false") << ")" << std::endl
;
108 return d_acm
.propagate(t1
.eqNode(t2
));
110 return d_acm
.propagate(t1
.eqNode(t2
).notNode());
113 void ArithCongruenceManager::ArithCongruenceNotify::eqNotifyConstantTermMerge(TNode t1
, TNode t2
) {
114 Debug("arith::congruences") << "ArithCongruenceNotify::eqNotifyConstantTermMerge(" << t1
<< ", " << t2
<< std::endl
;
115 d_acm
.propagate(t1
.eqNode(t2
));
117 void ArithCongruenceManager::ArithCongruenceNotify::eqNotifyNewClass(TNode t
) {
118 d_acm
.eqNotifyNewClass(t
);
120 void ArithCongruenceManager::ArithCongruenceNotify::eqNotifyPreMerge(TNode t1
, TNode t2
) {
122 void ArithCongruenceManager::ArithCongruenceNotify::eqNotifyPostMerge(TNode t1
, TNode t2
) {
123 d_acm
.eqNotifyPostMerge(t1
,t2
);
125 void ArithCongruenceManager::ArithCongruenceNotify::eqNotifyDisequal(TNode t1
, TNode t2
, TNode reason
) {
128 void ArithCongruenceManager::raiseConflict(Node conflict
){
129 Assert(!inConflict());
130 Debug("arith::conflict") << "difference manager conflict " << conflict
<< std::endl
;
131 d_inConflict
.raise();
132 d_raiseConflict
.raiseEEConflict(conflict
);
134 bool ArithCongruenceManager::inConflict() const{
135 return d_inConflict
.isRaised();
138 bool ArithCongruenceManager::hasMorePropagations() const {
139 return !d_propagatations
.empty();
141 const Node
ArithCongruenceManager::getNextPropagation() {
142 Assert(hasMorePropagations());
143 Node prop
= d_propagatations
.front();
144 d_propagatations
.dequeue();
148 bool ArithCongruenceManager::canExplain(TNode n
) const {
149 return d_explanationMap
.find(n
) != d_explanationMap
.end();
152 void ArithCongruenceManager::setMasterEqualityEngine(eq::EqualityEngine
* eq
) {
153 d_ee
.setMasterEqualityEngine(eq
);
156 Node
ArithCongruenceManager::externalToInternal(TNode n
) const{
157 Assert(canExplain(n
));
158 ExplainMap::const_iterator iter
= d_explanationMap
.find(n
);
159 size_t pos
= (*iter
).second
;
160 return d_propagatations
[pos
];
163 void ArithCongruenceManager::pushBack(TNode n
){
164 d_explanationMap
.insert(n
, d_propagatations
.size());
165 d_propagatations
.enqueue(n
);
167 ++(d_statistics
.d_propagations
);
169 void ArithCongruenceManager::pushBack(TNode n
, TNode r
){
170 d_explanationMap
.insert(r
, d_propagatations
.size());
171 d_explanationMap
.insert(n
, d_propagatations
.size());
172 d_propagatations
.enqueue(n
);
174 ++(d_statistics
.d_propagations
);
176 void ArithCongruenceManager::pushBack(TNode n
, TNode r
, TNode w
){
177 d_explanationMap
.insert(w
, d_propagatations
.size());
178 d_explanationMap
.insert(r
, d_propagatations
.size());
179 d_explanationMap
.insert(n
, d_propagatations
.size());
180 d_propagatations
.enqueue(n
);
182 ++(d_statistics
.d_propagations
);
185 void ArithCongruenceManager::watchedVariableIsZero(ConstraintCP lb
, ConstraintCP ub
){
186 Assert(lb
->isLowerBound());
187 Assert(ub
->isUpperBound());
188 Assert(lb
->getVariable() == ub
->getVariable());
189 Assert(lb
->getValue().sgn() == 0);
190 Assert(ub
->getValue().sgn() == 0);
192 ++(d_statistics
.d_watchedVariableIsZero
);
194 ArithVar s
= lb
->getVariable();
195 Node reason
= Constraint::externalExplainByAssertions(lb
,ub
);
197 d_keepAlive
.push_back(reason
);
198 assertionToEqualityEngine(true, s
, reason
);
201 void ArithCongruenceManager::watchedVariableIsZero(ConstraintCP eq
){
202 Assert(eq
->isEquality());
203 Assert(eq
->getValue().sgn() == 0);
205 ++(d_statistics
.d_watchedVariableIsZero
);
207 ArithVar s
= eq
->getVariable();
209 //Explain for conflict is correct as these proofs are generated
211 //These will be safe for propagation later as well
212 Node reason
= eq
->externalExplainByAssertions();
214 d_keepAlive
.push_back(reason
);
215 assertionToEqualityEngine(true, s
, reason
);
218 void ArithCongruenceManager::watchedVariableCannotBeZero(ConstraintCP c
){
219 ++(d_statistics
.d_watchedVariableIsNotZero
);
221 ArithVar s
= c
->getVariable();
223 //Explain for conflict is correct as these proofs are generated and stored eagerly
224 //These will be safe for propagation later as well
225 Node reason
= c
->externalExplainByAssertions();
227 d_keepAlive
.push_back(reason
);
228 assertionToEqualityEngine(false, s
, reason
);
232 bool ArithCongruenceManager::propagate(TNode x
){
233 Debug("arith::congruenceManager")<< "ArithCongruenceManager::propagate("<<x
<<")"<<std::endl
;
238 Node rewritten
= Rewriter::rewrite(x
);
240 //Need to still propagate this!
241 if(rewritten
.getKind() == kind::CONST_BOOLEAN
){
244 if(rewritten
.getConst
<bool>()){
247 ++(d_statistics
.d_conflicts
);
249 Node conf
= flattenAnd(explainInternal(x
));
251 Debug("arith::congruenceManager") << "rewritten to false "<<x
<<" with explanation "<< conf
<< std::endl
;
256 Assert(rewritten
.getKind() != kind::CONST_BOOLEAN
);
258 ConstraintP c
= d_constraintDatabase
.lookup(rewritten
);
259 if(c
== NullConstraint
){
260 //using setup as there may not be a corresponding congruence literal yet
261 d_setupLiteral(rewritten
);
262 c
= d_constraintDatabase
.lookup(rewritten
);
263 Assert(c
!= NullConstraint
);
266 Debug("arith::congruenceManager")<< "x is "
267 << c
->hasProof() << " "
268 << (x
== rewritten
) << " "
269 << c
->canBePropagated() << " "
270 << c
->negationHasProof() << std::endl
;
272 if(c
->negationHasProof()){
273 Node expC
= explainInternal(x
);
274 ConstraintCP negC
= c
->getNegation();
275 Node neg
= negC
->externalExplainByAssertions();
276 Node conf
= expC
.andNode(neg
);
277 Node final
= flattenAnd(conf
);
279 ++(d_statistics
.d_conflicts
);
280 raiseConflict(final
);
281 Debug("arith::congruenceManager") << "congruenceManager found a conflict " << final
<< std::endl
;
285 // Cases for propagation
287 // S : x == rewritten
288 // P : c can be propagated
291 // 000 : propagate x, and mark C it as being explained
292 // 001 : propagate x, and propagate c after marking it as being explained
293 // 01* : propagate x, mark c but do not propagate c
294 // 10* : propagate x, do not mark c and do not propagate c
295 // 11* : drop the constraint, do not propagate x or c
297 if(!c
->hasProof() && x
!= rewritten
){
298 if(c
->assertedToTheTheory()){
299 pushBack(x
, rewritten
, c
->getWitness());
301 pushBack(x
, rewritten
);
304 c
->setEqualityEngineProof();
305 if(c
->canBePropagated() && !c
->assertedToTheTheory()){
307 ++(d_statistics
.d_propagateConstraints
);
310 }else if(!c
->hasProof() && x
== rewritten
){
311 if(c
->assertedToTheTheory()){
312 pushBack(x
, c
->getWitness());
316 c
->setEqualityEngineProof();
317 }else if(c
->hasProof() && x
!= rewritten
){
318 if(c
->assertedToTheTheory()){
324 Assert(c
->hasProof() && x
== rewritten
);
329 void ArithCongruenceManager::explain(TNode literal
, std::vector
<TNode
>& assumptions
) {
330 if (literal
.getKind() != kind::NOT
) {
331 d_ee
.explainEquality(literal
[0], literal
[1], true, assumptions
);
333 d_ee
.explainEquality(literal
[0][0], literal
[0][1], false, assumptions
);
337 void ArithCongruenceManager::enqueueIntoNB(const std::set
<TNode
> s
, NodeBuilder
<>& nb
){
338 std::set
<TNode
>::const_iterator it
= s
.begin();
339 std::set
<TNode
>::const_iterator it_end
= s
.end();
340 for(; it
!= it_end
; ++it
) {
345 Node
ArithCongruenceManager::explainInternal(TNode internal
){
346 std::vector
<TNode
> assumptions
;
347 explain(internal
, assumptions
);
349 std::set
<TNode
> assumptionSet
;
350 assumptionSet
.insert(assumptions
.begin(), assumptions
.end());
352 if (assumptionSet
.size() == 1) {
353 // All the same, or just one
354 return assumptions
[0];
356 NodeBuilder
<> conjunction(kind::AND
);
357 enqueueIntoNB(assumptionSet
, conjunction
);
362 void ArithCongruenceManager::eqNotifyNewClass(TNode t
) {
364 d_eq_infer
->eqNotifyNewClass(t
);
368 void ArithCongruenceManager::eqNotifyPostMerge(TNode t1
, TNode t2
) {
370 d_eq_infer
->eqNotifyMerge(t1
, t2
);
375 Node
ArithCongruenceManager::explain(TNode external
){
376 Trace("arith-ee") << "Ask for explanation of " << external
<< std::endl
;
377 Node internal
= externalToInternal(external
);
378 Trace("arith-ee") << "...internal = " << internal
<< std::endl
;
379 return explainInternal(internal
);
382 void ArithCongruenceManager::explain(TNode external
, NodeBuilder
<>& out
){
383 Node internal
= externalToInternal(external
);
385 std::vector
<TNode
> assumptions
;
386 explain(internal
, assumptions
);
387 std::set
<TNode
> assumptionSet
;
388 assumptionSet
.insert(assumptions
.begin(), assumptions
.end());
390 enqueueIntoNB(assumptionSet
, out
);
393 void ArithCongruenceManager::addWatchedPair(ArithVar s
, TNode x
, TNode y
){
394 Assert(!isWatchedVariable(s
));
396 Debug("arith::congruenceManager")
397 << "addWatchedPair(" << s
<< ", " << x
<< ", " << y
<< ")" << std::endl
;
400 ++(d_statistics
.d_watchedVariables
);
402 d_watchedVariables
.add(s
);
404 Node eq
= x
.eqNode(y
);
405 d_watchedEqualities
.set(s
, eq
);
408 void ArithCongruenceManager::assertionToEqualityEngine(bool isEquality
, ArithVar s
, TNode reason
){
409 Assert(isWatchedVariable(s
));
411 TNode eq
= d_watchedEqualities
[s
];
412 Assert(eq
.getKind() == kind::EQUAL
);
414 Trace("arith-ee") << "Assert " << eq
<< ", pol " << isEquality
<< ", reason " << reason
<< std::endl
;
416 d_ee
.assertEquality(eq
, true, reason
);
418 d_ee
.assertEquality(eq
, false, reason
);
422 void ArithCongruenceManager::equalsConstant(ConstraintCP c
){
423 Assert(c
->isEquality());
425 ++(d_statistics
.d_equalsConstantCalls
);
426 Debug("equalsConstant") << "equals constant " << c
<< std::endl
;
428 ArithVar x
= c
->getVariable();
429 Node xAsNode
= d_avariables
.asNode(x
);
430 Node asRational
= mkRationalNode(c
->getValue().getNoninfinitesimalPart());
433 //No guarentee this is in normal form!
434 Node eq
= xAsNode
.eqNode(asRational
);
435 d_keepAlive
.push_back(eq
);
437 Node reason
= c
->externalExplainByAssertions();
438 d_keepAlive
.push_back(reason
);
440 Trace("arith-ee") << "Assert equalsConstant " << eq
<< ", reason " << reason
<< std::endl
;
441 d_ee
.assertEquality(eq
, true, reason
);
444 void ArithCongruenceManager::equalsConstant(ConstraintCP lb
, ConstraintCP ub
){
445 Assert(lb
->isLowerBound());
446 Assert(ub
->isUpperBound());
447 Assert(lb
->getVariable() == ub
->getVariable());
449 ++(d_statistics
.d_equalsConstantCalls
);
450 Debug("equalsConstant") << "equals constant " << lb
<< std::endl
453 ArithVar x
= lb
->getVariable();
454 Node reason
= Constraint::externalExplainByAssertions(lb
,ub
);
456 Node xAsNode
= d_avariables
.asNode(x
);
457 Node asRational
= mkRationalNode(lb
->getValue().getNoninfinitesimalPart());
459 //No guarentee this is in normal form!
460 Node eq
= xAsNode
.eqNode(asRational
);
461 d_keepAlive
.push_back(eq
);
462 d_keepAlive
.push_back(reason
);
464 Trace("arith-ee") << "Assert equalsConstant2 " << eq
<< ", reason " << reason
<< std::endl
;
465 d_ee
.assertEquality(eq
, true, reason
);
468 void ArithCongruenceManager::addSharedTerm(Node x
){
469 d_ee
.addTriggerTerm(x
, THEORY_ARITH
);
472 bool ArithCongruenceManager::fixpointInfer() {
474 while(! inConflict() && d_eqi_counter
.get()<d_eq_infer
->getNumPendingMerges() ) {
475 Trace("snorm-infer-eq-debug") << "Processing " << d_eqi_counter
.get() << " / " << d_eq_infer
->getNumPendingMerges() << std::endl
;
476 Node eq
= d_eq_infer
->getPendingMerge( d_eqi_counter
.get() );
477 Trace("snorm-infer-eq") << "ArithCongruenceManager : Infer by normalization : " << eq
<< std::endl
;
478 if( !d_ee
.areEqual( eq
[0], eq
[1] ) ){
479 Node eq_exp
= d_eq_infer
->getPendingMergeExplanation( d_eqi_counter
.get() );
480 Trace("snorm-infer-eq") << " explanation : " << eq_exp
<< std::endl
;
481 //regress explanation
482 std::vector
<TNode
> assumptions
;
483 if( eq_exp
.getKind()==kind::AND
){
484 for( unsigned i
=0; i
<eq_exp
.getNumChildren(); i
++ ){
485 explain( eq_exp
[i
], assumptions
);
487 }else if( eq_exp
.getKind()==kind::EQUAL
){
488 explain( eq_exp
, assumptions
);
490 //eq_exp should be true
491 Assert( eq_exp
==d_true
);
494 if( assumptions
.empty() ){
497 std::set
<TNode
> assumptionSet
;
498 assumptionSet
.insert(assumptions
.begin(), assumptions
.end());
499 if( assumptionSet
.size()==1 ){
500 req_exp
= assumptions
[0];
502 NodeBuilder
<> conjunction(kind::AND
);
503 enqueueIntoNB(assumptionSet
, conjunction
);
504 req_exp
= conjunction
;
507 Trace("snorm-infer-eq") << " regressed explanation : " << req_exp
<< std::endl
;
508 d_ee
.assertEquality( eq
, true, req_exp
);
509 d_keepAlive
.push_back( req_exp
);
511 Trace("snorm-infer-eq") << "...already equal." << std::endl
;
513 d_eqi_counter
= d_eqi_counter
.get() + 1;
519 }/* CVC4::theory::arith namespace */
520 }/* CVC4::theory namespace */
521 }/* CVC4 namespace */