1 /********************* */
2 /*! \file congruence_manager.cpp
4 ** Top contributors (to current version):
5 ** Tim King, Alex Ozdemir, Andrew Reynolds
6 ** This file is part of the CVC4 project.
7 ** Copyright (c) 2009-2021 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 "expr/proof_node.h"
22 #include "expr/proof_node_manager.h"
23 #include "smt/smt_statistics_registry.h"
24 #include "theory/arith/arith_utilities.h"
25 #include "theory/arith/constraint.h"
26 #include "theory/arith/partial_model.h"
27 #include "theory/ee_setup_info.h"
28 #include "theory/rewriter.h"
29 #include "theory/uf/equality_engine.h"
30 #include "theory/uf/proof_equality_engine.h"
31 #include "options/arith_options.h"
37 ArithCongruenceManager::ArithCongruenceManager(
39 context::UserContext
* u
,
40 ConstraintDatabase
& cd
,
41 SetupLiteralCallBack setup
,
42 const ArithVariables
& avars
,
43 RaiseEqualityEngineConflict raiseConflict
,
44 ProofNodeManager
* pnm
)
46 d_raiseConflict(raiseConflict
),
51 d_constraintDatabase(cd
),
52 d_setupLiteral(setup
),
58 // Construct d_pfGenEe with the SAT context, since its proof include
59 // unclosed assumptions of theory literals.
61 new EagerProofGenerator(pnm
, c
, "ArithCongruenceManager::pfGenEe")),
62 // Construct d_pfGenEe with the USER context, since its proofs are closed.
63 d_pfGenExplain(new EagerProofGenerator(
64 pnm
, u
, "ArithCongruenceManager::pfGenExplain")),
69 ArithCongruenceManager::~ArithCongruenceManager() {}
71 bool ArithCongruenceManager::needsEqualityEngine(EeSetupInfo
& esi
)
73 esi
.d_notify
= &d_notify
;
74 esi
.d_name
= "theory::arith::ArithCongruenceManager";
78 void ArithCongruenceManager::finishInit(eq::EqualityEngine
* ee
,
79 eq::ProofEqEngine
* pfee
)
81 Assert(ee
!= nullptr);
83 d_ee
->addFunctionKind(kind::NONLINEAR_MULT
);
84 d_ee
->addFunctionKind(kind::EXPONENTIAL
);
85 d_ee
->addFunctionKind(kind::SINE
);
86 d_ee
->addFunctionKind(kind::IAND
);
87 // have proof equality engine only if proofs are enabled
88 Assert(isProofEnabled() == (pfee
!= nullptr));
92 ArithCongruenceManager::Statistics::Statistics():
93 d_watchedVariables("theory::arith::congruence::watchedVariables", 0),
94 d_watchedVariableIsZero("theory::arith::congruence::watchedVariableIsZero", 0),
95 d_watchedVariableIsNotZero("theory::arith::congruence::watchedVariableIsNotZero", 0),
96 d_equalsConstantCalls("theory::arith::congruence::equalsConstantCalls", 0),
97 d_propagations("theory::arith::congruence::propagations", 0),
98 d_propagateConstraints("theory::arith::congruence::propagateConstraints", 0),
99 d_conflicts("theory::arith::congruence::conflicts", 0)
101 smtStatisticsRegistry()->registerStat(&d_watchedVariables
);
102 smtStatisticsRegistry()->registerStat(&d_watchedVariableIsZero
);
103 smtStatisticsRegistry()->registerStat(&d_watchedVariableIsNotZero
);
104 smtStatisticsRegistry()->registerStat(&d_equalsConstantCalls
);
105 smtStatisticsRegistry()->registerStat(&d_propagations
);
106 smtStatisticsRegistry()->registerStat(&d_propagateConstraints
);
107 smtStatisticsRegistry()->registerStat(&d_conflicts
);
110 ArithCongruenceManager::Statistics::~Statistics(){
111 smtStatisticsRegistry()->unregisterStat(&d_watchedVariables
);
112 smtStatisticsRegistry()->unregisterStat(&d_watchedVariableIsZero
);
113 smtStatisticsRegistry()->unregisterStat(&d_watchedVariableIsNotZero
);
114 smtStatisticsRegistry()->unregisterStat(&d_equalsConstantCalls
);
115 smtStatisticsRegistry()->unregisterStat(&d_propagations
);
116 smtStatisticsRegistry()->unregisterStat(&d_propagateConstraints
);
117 smtStatisticsRegistry()->unregisterStat(&d_conflicts
);
120 ArithCongruenceManager::ArithCongruenceNotify::ArithCongruenceNotify(ArithCongruenceManager
& acm
)
124 bool ArithCongruenceManager::ArithCongruenceNotify::eqNotifyTriggerPredicate(
125 TNode predicate
, bool value
)
127 Assert(predicate
.getKind() == kind::EQUAL
);
128 Debug("arith::congruences")
129 << "ArithCongruenceNotify::eqNotifyTriggerPredicate(" << predicate
<< ", "
130 << (value
? "true" : "false") << ")" << std::endl
;
132 return d_acm
.propagate(predicate
);
134 return d_acm
.propagate(predicate
.notNode());
137 bool ArithCongruenceManager::ArithCongruenceNotify::eqNotifyTriggerTermEquality(TheoryId tag
, TNode t1
, TNode t2
, bool value
) {
138 Debug("arith::congruences") << "ArithCongruenceNotify::eqNotifyTriggerTermEquality(" << t1
<< ", " << t2
<< ", " << (value
? "true" : "false") << ")" << std::endl
;
140 return d_acm
.propagate(t1
.eqNode(t2
));
142 return d_acm
.propagate(t1
.eqNode(t2
).notNode());
145 void ArithCongruenceManager::ArithCongruenceNotify::eqNotifyConstantTermMerge(TNode t1
, TNode t2
) {
146 Debug("arith::congruences") << "ArithCongruenceNotify::eqNotifyConstantTermMerge(" << t1
<< ", " << t2
<< std::endl
;
147 d_acm
.propagate(t1
.eqNode(t2
));
149 void ArithCongruenceManager::ArithCongruenceNotify::eqNotifyNewClass(TNode t
) {
151 void ArithCongruenceManager::ArithCongruenceNotify::eqNotifyMerge(TNode t1
,
155 void ArithCongruenceManager::ArithCongruenceNotify::eqNotifyDisequal(TNode t1
, TNode t2
, TNode reason
) {
158 void ArithCongruenceManager::raiseConflict(Node conflict
,
159 std::shared_ptr
<ProofNode
> pf
)
161 Assert(!inConflict());
162 Debug("arith::conflict") << "difference manager conflict " << conflict
<< std::endl
;
163 d_inConflict
.raise();
164 d_raiseConflict
.raiseEEConflict(conflict
, pf
);
166 bool ArithCongruenceManager::inConflict() const{
167 return d_inConflict
.isRaised();
170 bool ArithCongruenceManager::hasMorePropagations() const {
171 return !d_propagatations
.empty();
173 const Node
ArithCongruenceManager::getNextPropagation() {
174 Assert(hasMorePropagations());
175 Node prop
= d_propagatations
.front();
176 d_propagatations
.dequeue();
180 bool ArithCongruenceManager::canExplain(TNode n
) const {
181 return d_explanationMap
.find(n
) != d_explanationMap
.end();
184 Node
ArithCongruenceManager::externalToInternal(TNode n
) const{
185 Assert(canExplain(n
));
186 ExplainMap::const_iterator iter
= d_explanationMap
.find(n
);
187 size_t pos
= (*iter
).second
;
188 return d_propagatations
[pos
];
191 void ArithCongruenceManager::pushBack(TNode n
){
192 d_explanationMap
.insert(n
, d_propagatations
.size());
193 d_propagatations
.enqueue(n
);
195 ++(d_statistics
.d_propagations
);
197 void ArithCongruenceManager::pushBack(TNode n
, TNode r
){
198 d_explanationMap
.insert(r
, d_propagatations
.size());
199 d_explanationMap
.insert(n
, d_propagatations
.size());
200 d_propagatations
.enqueue(n
);
202 ++(d_statistics
.d_propagations
);
204 void ArithCongruenceManager::pushBack(TNode n
, TNode r
, TNode w
){
205 d_explanationMap
.insert(w
, d_propagatations
.size());
206 d_explanationMap
.insert(r
, d_propagatations
.size());
207 d_explanationMap
.insert(n
, d_propagatations
.size());
208 d_propagatations
.enqueue(n
);
210 ++(d_statistics
.d_propagations
);
213 void ArithCongruenceManager::watchedVariableIsZero(ConstraintCP lb
, ConstraintCP ub
){
214 Assert(lb
->isLowerBound());
215 Assert(ub
->isUpperBound());
216 Assert(lb
->getVariable() == ub
->getVariable());
217 Assert(lb
->getValue().sgn() == 0);
218 Assert(ub
->getValue().sgn() == 0);
220 ++(d_statistics
.d_watchedVariableIsZero
);
222 ArithVar s
= lb
->getVariable();
223 TNode eq
= d_watchedEqualities
[s
];
224 ConstraintCP eqC
= d_constraintDatabase
.getConstraint(
225 s
, ConstraintType::Equality
, lb
->getValue());
226 NodeBuilder
reasonBuilder(Kind::AND
);
227 auto pfLb
= lb
->externalExplainByAssertions(reasonBuilder
);
228 auto pfUb
= ub
->externalExplainByAssertions(reasonBuilder
);
229 Node reason
= safeConstructNary(reasonBuilder
);
230 std::shared_ptr
<ProofNode
> pf
{};
231 if (isProofEnabled())
234 PfRule::ARITH_TRICHOTOMY
, {pfLb
, pfUb
}, {eqC
->getProofLiteral()});
235 pf
= d_pnm
->mkNode(PfRule::MACRO_SR_PRED_TRANSFORM
, {pf
}, {eq
});
238 d_keepAlive
.push_back(reason
);
239 Trace("arith-ee") << "Asserting an equality on " << s
<< ", on trichotomy"
241 Trace("arith-ee") << " based on " << lb
<< std::endl
;
242 Trace("arith-ee") << " based on " << ub
<< std::endl
;
243 assertionToEqualityEngine(true, s
, reason
, pf
);
246 void ArithCongruenceManager::watchedVariableIsZero(ConstraintCP eq
){
247 Debug("arith::cong") << "Cong::watchedVariableIsZero: " << *eq
<< std::endl
;
249 Assert(eq
->isEquality());
250 Assert(eq
->getValue().sgn() == 0);
252 ++(d_statistics
.d_watchedVariableIsZero
);
254 ArithVar s
= eq
->getVariable();
256 //Explain for conflict is correct as these proofs are generated
258 //These will be safe for propagation later as well
259 NodeBuilder
nb(Kind::AND
);
260 // An open proof of eq from literals now in reason.
261 if (Debug
.isOn("arith::cong"))
263 eq
->printProofTree(Debug("arith::cong"));
265 auto pf
= eq
->externalExplainByAssertions(nb
);
266 if (isProofEnabled())
269 PfRule::MACRO_SR_PRED_TRANSFORM
, {pf
}, {d_watchedEqualities
[s
]});
271 Node reason
= safeConstructNary(nb
);
273 d_keepAlive
.push_back(reason
);
274 assertionToEqualityEngine(true, s
, reason
, pf
);
277 void ArithCongruenceManager::watchedVariableCannotBeZero(ConstraintCP c
){
278 Debug("arith::cong::notzero")
279 << "Cong::watchedVariableCannotBeZero " << *c
<< std::endl
;
280 ++(d_statistics
.d_watchedVariableIsNotZero
);
282 ArithVar s
= c
->getVariable();
283 Node disEq
= d_watchedEqualities
[s
].negate();
285 //Explain for conflict is correct as these proofs are generated and stored eagerly
286 //These will be safe for propagation later as well
287 NodeBuilder
nb(Kind::AND
);
288 // An open proof of eq from literals now in reason.
289 auto pf
= c
->externalExplainByAssertions(nb
);
290 if (Debug
.isOn("arith::cong::notzero"))
292 Debug("arith::cong::notzero") << " original proof ";
293 pf
->printDebug(Debug("arith::cong::notzero"));
294 Debug("arith::cong::notzero") << std::endl
;
296 Node reason
= safeConstructNary(nb
);
297 if (isProofEnabled())
299 if (c
->getType() == ConstraintType::Disequality
)
301 Assert(c
->getLiteral() == d_watchedEqualities
[s
].negate());
302 // We have to prove equivalence to the watched disequality.
303 pf
= d_pnm
->mkNode(PfRule::MACRO_SR_PRED_TRANSFORM
, {pf
}, {disEq
});
307 Debug("arith::cong::notzero")
308 << " proof modification needed" << std::endl
;
311 // c has form x_i = d, d > 0 => multiply c by -1 in Farkas proof
312 // c has form x_i = d, d > 0 => multiply c by 1 in Farkas proof
313 // c has form x_i <= d, d < 0 => multiply c by 1 in Farkas proof
314 // c has form x_i >= d, d > 0 => multiply c by -1 in Farkas proof
315 const bool scaleCNegatively
= c
->getType() == ConstraintType::LowerBound
316 || (c
->getType() == ConstraintType::Equality
317 && c
->getValue().sgn() > 0);
318 const int cSign
= scaleCNegatively
? -1 : 1;
319 TNode isZero
= d_watchedEqualities
[s
];
320 const auto isZeroPf
= d_pnm
->mkAssume(isZero
);
321 const auto nm
= NodeManager::currentNM();
322 const auto sumPf
= d_pnm
->mkNode(
323 PfRule::ARITH_SCALE_SUM_UPPER_BOUNDS
,
325 // Trick for getting correct, opposing signs.
326 {nm
->mkConst(Rational(-1 * cSign
)), nm
->mkConst(Rational(cSign
))});
327 const auto botPf
= d_pnm
->mkNode(
328 PfRule::MACRO_SR_PRED_TRANSFORM
, {sumPf
}, {nm
->mkConst(false)});
329 std::vector
<Node
> assumption
= {isZero
};
330 pf
= d_pnm
->mkScope(botPf
, assumption
, false);
331 Debug("arith::cong::notzero") << " new proof ";
332 pf
->printDebug(Debug("arith::cong::notzero"));
333 Debug("arith::cong::notzero") << std::endl
;
335 Assert(pf
->getResult() == disEq
);
337 d_keepAlive
.push_back(reason
);
338 assertionToEqualityEngine(false, s
, reason
, pf
);
342 bool ArithCongruenceManager::propagate(TNode x
){
343 Debug("arith::congruenceManager")<< "ArithCongruenceManager::propagate("<<x
<<")"<<std::endl
;
348 Node rewritten
= Rewriter::rewrite(x
);
350 //Need to still propagate this!
351 if(rewritten
.getKind() == kind::CONST_BOOLEAN
){
354 if(rewritten
.getConst
<bool>()){
357 // x rewrites to false.
358 ++(d_statistics
.d_conflicts
);
359 TrustNode trn
= explainInternal(x
);
360 Node conf
= flattenAnd(trn
.getNode());
361 Debug("arith::congruenceManager") << "rewritten to false "<<x
<<" with explanation "<< conf
<< std::endl
;
362 if (isProofEnabled())
364 auto pf
= trn
.getGenerator()->getProofFor(trn
.getProven());
365 auto confPf
= d_pnm
->mkNode(
366 PfRule::MACRO_SR_PRED_TRANSFORM
, {pf
}, {conf
.negate()});
367 raiseConflict(conf
, confPf
);
377 Assert(rewritten
.getKind() != kind::CONST_BOOLEAN
);
379 ConstraintP c
= d_constraintDatabase
.lookup(rewritten
);
380 if(c
== NullConstraint
){
381 //using setup as there may not be a corresponding congruence literal yet
382 d_setupLiteral(rewritten
);
383 c
= d_constraintDatabase
.lookup(rewritten
);
384 Assert(c
!= NullConstraint
);
387 Debug("arith::congruenceManager")<< "x is "
388 << c
->hasProof() << " "
389 << (x
== rewritten
) << " "
390 << c
->canBePropagated() << " "
391 << c
->negationHasProof() << std::endl
;
393 if(c
->negationHasProof()){
394 TrustNode texpC
= explainInternal(x
);
395 Node expC
= texpC
.getNode();
396 ConstraintCP negC
= c
->getNegation();
397 Node neg
= Constraint::externalExplainByAssertions({negC
});
398 Node conf
= expC
.andNode(neg
);
399 Node final
= flattenAnd(conf
);
401 ++(d_statistics
.d_conflicts
);
402 raiseConflict(final
);
403 Debug("arith::congruenceManager") << "congruenceManager found a conflict " << final
<< std::endl
;
407 // Cases for propagation
409 // S : x == rewritten
410 // P : c can be propagated
413 // 000 : propagate x, and mark C it as being explained
414 // 001 : propagate x, and propagate c after marking it as being explained
415 // 01* : propagate x, mark c but do not propagate c
416 // 10* : propagate x, do not mark c and do not propagate c
417 // 11* : drop the constraint, do not propagate x or c
419 if(!c
->hasProof() && x
!= rewritten
){
420 if(c
->assertedToTheTheory()){
421 pushBack(x
, rewritten
, c
->getWitness());
423 pushBack(x
, rewritten
);
426 c
->setEqualityEngineProof();
427 if(c
->canBePropagated() && !c
->assertedToTheTheory()){
429 ++(d_statistics
.d_propagateConstraints
);
432 }else if(!c
->hasProof() && x
== rewritten
){
433 if(c
->assertedToTheTheory()){
434 pushBack(x
, c
->getWitness());
438 c
->setEqualityEngineProof();
439 }else if(c
->hasProof() && x
!= rewritten
){
440 if(c
->assertedToTheTheory()){
446 Assert(c
->hasProof() && x
== rewritten
);
451 void ArithCongruenceManager::explain(TNode literal
, std::vector
<TNode
>& assumptions
) {
452 if (literal
.getKind() != kind::NOT
) {
453 d_ee
->explainEquality(literal
[0], literal
[1], true, assumptions
);
455 d_ee
->explainEquality(literal
[0][0], literal
[0][1], false, assumptions
);
459 void ArithCongruenceManager::enqueueIntoNB(const std::set
<TNode
> s
,
462 std::set
<TNode
>::const_iterator it
= s
.begin();
463 std::set
<TNode
>::const_iterator it_end
= s
.end();
464 for(; it
!= it_end
; ++it
) {
469 TrustNode
ArithCongruenceManager::explainInternal(TNode internal
)
471 if (isProofEnabled())
473 return d_pfee
->explain(internal
);
475 // otherwise, explain without proof generator
476 Node exp
= d_ee
->mkExplainLit(internal
);
477 return TrustNode::mkTrustPropExp(internal
, exp
, nullptr);
480 TrustNode
ArithCongruenceManager::explain(TNode external
)
482 Trace("arith-ee") << "Ask for explanation of " << external
<< std::endl
;
483 Node internal
= externalToInternal(external
);
484 Trace("arith-ee") << "...internal = " << internal
<< std::endl
;
485 TrustNode trn
= explainInternal(internal
);
486 if (isProofEnabled() && trn
.getProven()[1] != external
)
488 Assert(trn
.getKind() == TrustNodeKind::PROP_EXP
);
489 Assert(trn
.getProven().getKind() == Kind::IMPLIES
);
490 Assert(trn
.getGenerator() != nullptr);
491 Trace("arith-ee") << "tweaking proof to prove " << external
<< " not "
492 << trn
.getProven()[1] << std::endl
;
493 std::vector
<std::shared_ptr
<ProofNode
>> assumptionPfs
;
494 std::vector
<Node
> assumptions
= andComponents(trn
.getNode());
495 assumptionPfs
.push_back(trn
.toProofNode());
496 for (const auto& a
: assumptions
)
498 assumptionPfs
.push_back(
499 d_pnm
->mkNode(PfRule::TRUE_INTRO
, {d_pnm
->mkAssume(a
)}, {}));
501 auto litPf
= d_pnm
->mkNode(
502 PfRule::MACRO_SR_PRED_TRANSFORM
, {assumptionPfs
}, {external
});
503 auto extPf
= d_pnm
->mkScope(litPf
, assumptions
);
504 return d_pfGenExplain
->mkTrustedPropagation(external
, trn
.getNode(), extPf
);
509 void ArithCongruenceManager::explain(TNode external
, NodeBuilder
& out
)
511 Node internal
= externalToInternal(external
);
513 std::vector
<TNode
> assumptions
;
514 explain(internal
, assumptions
);
515 std::set
<TNode
> assumptionSet
;
516 assumptionSet
.insert(assumptions
.begin(), assumptions
.end());
518 enqueueIntoNB(assumptionSet
, out
);
521 void ArithCongruenceManager::addWatchedPair(ArithVar s
, TNode x
, TNode y
){
522 Assert(!isWatchedVariable(s
));
524 Debug("arith::congruenceManager")
525 << "addWatchedPair(" << s
<< ", " << x
<< ", " << y
<< ")" << std::endl
;
528 ++(d_statistics
.d_watchedVariables
);
530 d_watchedVariables
.add(s
);
532 Node eq
= x
.eqNode(y
);
533 d_watchedEqualities
.set(s
, eq
);
536 void ArithCongruenceManager::assertLitToEqualityEngine(
537 Node lit
, TNode reason
, std::shared_ptr
<ProofNode
> pf
)
539 bool isEquality
= lit
.getKind() != Kind::NOT
;
540 Node eq
= isEquality
? lit
: lit
[0];
541 Assert(eq
.getKind() == Kind::EQUAL
);
543 Trace("arith-ee") << "Assert to Eq " << lit
<< ", reason " << reason
545 if (isProofEnabled())
547 if (CDProof::isSame(lit
, reason
))
549 Trace("arith-pfee") << "Asserting only, b/c implied by symm" << std::endl
;
550 // The equality engine doesn't ref-count for us...
551 d_keepAlive
.push_back(eq
);
552 d_keepAlive
.push_back(reason
);
553 d_ee
->assertEquality(eq
, isEquality
, reason
);
555 else if (hasProofFor(lit
))
557 Trace("arith-pfee") << "Skipping b/c already done" << std::endl
;
561 setProofFor(lit
, pf
);
562 Trace("arith-pfee") << "Actually asserting" << std::endl
;
563 if (Debug
.isOn("arith-pfee"))
565 Trace("arith-pfee") << "Proof: ";
566 pf
->printDebug(Trace("arith-pfee"));
567 Trace("arith-pfee") << std::endl
;
569 // The proof equality engine *does* ref-count for us...
570 d_pfee
->assertFact(lit
, reason
, d_pfGenEe
.get());
575 // The equality engine doesn't ref-count for us...
576 d_keepAlive
.push_back(eq
);
577 d_keepAlive
.push_back(reason
);
578 d_ee
->assertEquality(eq
, isEquality
, reason
);
582 void ArithCongruenceManager::assertionToEqualityEngine(
583 bool isEquality
, ArithVar s
, TNode reason
, std::shared_ptr
<ProofNode
> pf
)
585 Assert(isWatchedVariable(s
));
587 TNode eq
= d_watchedEqualities
[s
];
588 Assert(eq
.getKind() == kind::EQUAL
);
590 Node lit
= isEquality
? Node(eq
) : eq
.notNode();
591 Trace("arith-ee") << "Assert to Eq " << eq
<< ", pol " << isEquality
592 << ", reason " << reason
<< std::endl
;
593 assertLitToEqualityEngine(lit
, reason
, pf
);
596 bool ArithCongruenceManager::hasProofFor(TNode f
) const
598 Assert(isProofEnabled());
599 if (d_pfGenEe
->hasProofFor(f
))
603 Node sym
= CDProof::getSymmFact(f
);
604 Assert(!sym
.isNull());
605 return d_pfGenEe
->hasProofFor(sym
);
608 void ArithCongruenceManager::setProofFor(TNode f
,
609 std::shared_ptr
<ProofNode
> pf
) const
611 Assert(!hasProofFor(f
));
612 d_pfGenEe
->mkTrustNode(f
, pf
);
613 Node symF
= CDProof::getSymmFact(f
);
614 auto symPf
= d_pnm
->mkNode(PfRule::SYMM
, {pf
}, {});
615 d_pfGenEe
->mkTrustNode(symF
, symPf
);
618 void ArithCongruenceManager::equalsConstant(ConstraintCP c
){
619 Assert(c
->isEquality());
621 ++(d_statistics
.d_equalsConstantCalls
);
622 Debug("equalsConstant") << "equals constant " << c
<< std::endl
;
624 ArithVar x
= c
->getVariable();
625 Node xAsNode
= d_avariables
.asNode(x
);
626 Node asRational
= mkRationalNode(c
->getValue().getNoninfinitesimalPart());
628 // No guarentee this is in normal form!
629 // Note though, that it happens to be in proof normal form!
630 Node eq
= xAsNode
.eqNode(asRational
);
631 d_keepAlive
.push_back(eq
);
633 NodeBuilder
nb(Kind::AND
);
634 auto pf
= c
->externalExplainByAssertions(nb
);
635 Node reason
= safeConstructNary(nb
);
636 d_keepAlive
.push_back(reason
);
638 Trace("arith-ee") << "Assert equalsConstant " << eq
<< ", reason " << reason
<< std::endl
;
639 assertLitToEqualityEngine(eq
, reason
, pf
);
642 void ArithCongruenceManager::equalsConstant(ConstraintCP lb
, ConstraintCP ub
){
643 Assert(lb
->isLowerBound());
644 Assert(ub
->isUpperBound());
645 Assert(lb
->getVariable() == ub
->getVariable());
647 ++(d_statistics
.d_equalsConstantCalls
);
648 Debug("equalsConstant") << "equals constant " << lb
<< std::endl
651 ArithVar x
= lb
->getVariable();
652 NodeBuilder
nb(Kind::AND
);
653 auto pfLb
= lb
->externalExplainByAssertions(nb
);
654 auto pfUb
= ub
->externalExplainByAssertions(nb
);
655 Node reason
= safeConstructNary(nb
);
657 Node xAsNode
= d_avariables
.asNode(x
);
658 Node asRational
= mkRationalNode(lb
->getValue().getNoninfinitesimalPart());
660 // No guarentee this is in normal form!
661 // Note though, that it happens to be in proof normal form!
662 Node eq
= xAsNode
.eqNode(asRational
);
663 std::shared_ptr
<ProofNode
> pf
;
664 if (isProofEnabled())
666 pf
= d_pnm
->mkNode(PfRule::ARITH_TRICHOTOMY
, {pfLb
, pfUb
}, {eq
});
668 d_keepAlive
.push_back(eq
);
669 d_keepAlive
.push_back(reason
);
671 Trace("arith-ee") << "Assert equalsConstant2 " << eq
<< ", reason " << reason
<< std::endl
;
673 assertLitToEqualityEngine(eq
, reason
, pf
);
676 bool ArithCongruenceManager::isProofEnabled() const { return d_pnm
!= nullptr; }
678 std::vector
<Node
> andComponents(TNode an
)
680 auto nm
= NodeManager::currentNM();
681 if (an
== nm
->mkConst(true))
685 else if (an
.getKind() != Kind::AND
)
689 std::vector
<Node
> a
{};
690 a
.reserve(an
.getNumChildren());
691 a
.insert(a
.end(), an
.begin(), an
.end());
696 } // namespace theory