1 /******************************************************************************
2 * Top contributors (to current version):
3 * Tim King, Alex Ozdemir, Andrew Reynolds
5 * This file is part of the cvc5 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.
11 * ****************************************************************************
13 * [[ Add one-line brief description here ]]
15 * [[ Add lengthier description here ]]
16 * \todo document this file
19 #include "theory/arith/congruence_manager.h"
21 #include "base/output.h"
22 #include "options/arith_options.h"
23 #include "proof/proof_node.h"
24 #include "proof/proof_node_manager.h"
26 #include "smt/smt_statistics_registry.h"
27 #include "theory/arith/arith_utilities.h"
28 #include "theory/arith/constraint.h"
29 #include "theory/arith/partial_model.h"
30 #include "theory/ee_setup_info.h"
31 #include "theory/rewriter.h"
32 #include "theory/uf/equality_engine.h"
33 #include "theory/uf/proof_equality_engine.h"
35 using namespace cvc5::kind
;
41 ArithCongruenceManager::ArithCongruenceManager(
43 ConstraintDatabase
& cd
,
44 SetupLiteralCallBack setup
,
45 const ArithVariables
& avars
,
46 RaiseEqualityEngineConflict raiseConflict
)
48 d_inConflict(context()),
49 d_raiseConflict(raiseConflict
),
51 d_keepAlive(context()),
52 d_propagatations(context()),
53 d_explanationMap(context()),
54 d_constraintDatabase(cd
),
55 d_setupLiteral(setup
),
58 d_pnm(d_env
.isTheoryProofProducing() ? d_env
.getProofNodeManager()
60 // Construct d_pfGenEe with the SAT context, since its proof include
61 // unclosed assumptions of theory literals.
62 d_pfGenEe(new EagerProofGenerator(
63 d_pnm
, context(), "ArithCongruenceManager::pfGenEe")),
64 // Construct d_pfGenEe with the USER context, since its proofs are closed.
65 d_pfGenExplain(new EagerProofGenerator(
66 d_pnm
, userContext(), "ArithCongruenceManager::pfGenExplain")),
71 ArithCongruenceManager::~ArithCongruenceManager() {}
73 bool ArithCongruenceManager::needsEqualityEngine(EeSetupInfo
& esi
)
75 Assert(!options().arith
.arithEqSolver
);
76 esi
.d_notify
= &d_notify
;
77 esi
.d_name
= "arithCong::ee";
81 void ArithCongruenceManager::finishInit(eq::EqualityEngine
* ee
)
83 if (options().arith
.arithEqSolver
)
86 d_allocEe
= std::make_unique
<eq::EqualityEngine
>(
87 d_env
, context(), d_notify
, "arithCong::ee", true);
88 d_ee
= d_allocEe
.get();
91 // allocate an internal proof equality engine
92 d_allocPfee
= std::make_unique
<eq::ProofEqEngine
>(d_env
, *d_ee
);
93 d_ee
->setProofEqualityEngine(d_allocPfee
.get());
98 Assert(ee
!= nullptr);
99 // otherwise, we use the official one
102 // set the congruence kinds on the separate equality engine
103 d_ee
->addFunctionKind(kind::NONLINEAR_MULT
);
104 d_ee
->addFunctionKind(kind::EXPONENTIAL
);
105 d_ee
->addFunctionKind(kind::SINE
);
106 d_ee
->addFunctionKind(kind::IAND
);
107 d_ee
->addFunctionKind(kind::POW2
);
108 // the proof equality engine is the one from the equality engine
109 d_pfee
= d_ee
->getProofEqualityEngine();
110 // have proof equality engine only if proofs are enabled
111 Assert(isProofEnabled() == (d_pfee
!= nullptr));
114 ArithCongruenceManager::Statistics::Statistics()
115 : d_watchedVariables(smtStatisticsRegistry().registerInt(
116 "theory::arith::congruence::watchedVariables")),
117 d_watchedVariableIsZero(smtStatisticsRegistry().registerInt(
118 "theory::arith::congruence::watchedVariableIsZero")),
119 d_watchedVariableIsNotZero(smtStatisticsRegistry().registerInt(
120 "theory::arith::congruence::watchedVariableIsNotZero")),
121 d_equalsConstantCalls(smtStatisticsRegistry().registerInt(
122 "theory::arith::congruence::equalsConstantCalls")),
123 d_propagations(smtStatisticsRegistry().registerInt(
124 "theory::arith::congruence::propagations")),
125 d_propagateConstraints(smtStatisticsRegistry().registerInt(
126 "theory::arith::congruence::propagateConstraints")),
127 d_conflicts(smtStatisticsRegistry().registerInt(
128 "theory::arith::congruence::conflicts"))
132 ArithCongruenceManager::ArithCongruenceNotify::ArithCongruenceNotify(ArithCongruenceManager
& acm
)
136 bool ArithCongruenceManager::ArithCongruenceNotify::eqNotifyTriggerPredicate(
137 TNode predicate
, bool value
)
139 Assert(predicate
.getKind() == kind::EQUAL
);
140 Debug("arith::congruences")
141 << "ArithCongruenceNotify::eqNotifyTriggerPredicate(" << predicate
<< ", "
142 << (value
? "true" : "false") << ")" << std::endl
;
144 return d_acm
.propagate(predicate
);
146 return d_acm
.propagate(predicate
.notNode());
149 bool ArithCongruenceManager::ArithCongruenceNotify::eqNotifyTriggerTermEquality(TheoryId tag
, TNode t1
, TNode t2
, bool value
) {
150 Debug("arith::congruences") << "ArithCongruenceNotify::eqNotifyTriggerTermEquality(" << t1
<< ", " << t2
<< ", " << (value
? "true" : "false") << ")" << std::endl
;
152 return d_acm
.propagate(t1
.eqNode(t2
));
154 return d_acm
.propagate(t1
.eqNode(t2
).notNode());
157 void ArithCongruenceManager::ArithCongruenceNotify::eqNotifyConstantTermMerge(TNode t1
, TNode t2
) {
158 Debug("arith::congruences") << "ArithCongruenceNotify::eqNotifyConstantTermMerge(" << t1
<< ", " << t2
<< std::endl
;
159 d_acm
.propagate(t1
.eqNode(t2
));
161 void ArithCongruenceManager::ArithCongruenceNotify::eqNotifyNewClass(TNode t
) {
163 void ArithCongruenceManager::ArithCongruenceNotify::eqNotifyMerge(TNode t1
,
167 void ArithCongruenceManager::ArithCongruenceNotify::eqNotifyDisequal(TNode t1
, TNode t2
, TNode reason
) {
170 void ArithCongruenceManager::raiseConflict(Node conflict
,
171 std::shared_ptr
<ProofNode
> pf
)
173 Assert(!inConflict());
174 Debug("arith::conflict") << "difference manager conflict " << conflict
<< std::endl
;
175 d_inConflict
.raise();
176 d_raiseConflict
.raiseEEConflict(conflict
, pf
);
178 bool ArithCongruenceManager::inConflict() const{
179 return d_inConflict
.isRaised();
182 bool ArithCongruenceManager::hasMorePropagations() const {
183 return !d_propagatations
.empty();
185 const Node
ArithCongruenceManager::getNextPropagation() {
186 Assert(hasMorePropagations());
187 Node prop
= d_propagatations
.front();
188 d_propagatations
.dequeue();
192 bool ArithCongruenceManager::canExplain(TNode n
) const {
193 return d_explanationMap
.find(n
) != d_explanationMap
.end();
196 Node
ArithCongruenceManager::externalToInternal(TNode n
) const{
197 Assert(canExplain(n
));
198 ExplainMap::const_iterator iter
= d_explanationMap
.find(n
);
199 size_t pos
= (*iter
).second
;
200 return d_propagatations
[pos
];
203 void ArithCongruenceManager::pushBack(TNode n
){
204 d_explanationMap
.insert(n
, d_propagatations
.size());
205 d_propagatations
.enqueue(n
);
207 ++(d_statistics
.d_propagations
);
209 void ArithCongruenceManager::pushBack(TNode n
, TNode r
){
210 d_explanationMap
.insert(r
, d_propagatations
.size());
211 d_explanationMap
.insert(n
, d_propagatations
.size());
212 d_propagatations
.enqueue(n
);
214 ++(d_statistics
.d_propagations
);
216 void ArithCongruenceManager::pushBack(TNode n
, TNode r
, TNode w
){
217 d_explanationMap
.insert(w
, d_propagatations
.size());
218 d_explanationMap
.insert(r
, d_propagatations
.size());
219 d_explanationMap
.insert(n
, d_propagatations
.size());
220 d_propagatations
.enqueue(n
);
222 ++(d_statistics
.d_propagations
);
225 void ArithCongruenceManager::watchedVariableIsZero(ConstraintCP lb
, ConstraintCP ub
){
226 Assert(lb
->isLowerBound());
227 Assert(ub
->isUpperBound());
228 Assert(lb
->getVariable() == ub
->getVariable());
229 Assert(lb
->getValue().sgn() == 0);
230 Assert(ub
->getValue().sgn() == 0);
232 ++(d_statistics
.d_watchedVariableIsZero
);
234 ArithVar s
= lb
->getVariable();
235 TNode eq
= d_watchedEqualities
[s
];
236 ConstraintCP eqC
= d_constraintDatabase
.getConstraint(
237 s
, ConstraintType::Equality
, lb
->getValue());
238 NodeBuilder
reasonBuilder(Kind::AND
);
239 auto pfLb
= lb
->externalExplainByAssertions(reasonBuilder
);
240 auto pfUb
= ub
->externalExplainByAssertions(reasonBuilder
);
241 Node reason
= mkAndFromBuilder(reasonBuilder
);
242 std::shared_ptr
<ProofNode
> pf
{};
243 if (isProofEnabled())
246 PfRule::ARITH_TRICHOTOMY
, {pfLb
, pfUb
}, {eqC
->getProofLiteral()});
247 pf
= d_pnm
->mkNode(PfRule::MACRO_SR_PRED_TRANSFORM
, {pf
}, {eq
});
250 d_keepAlive
.push_back(reason
);
251 Trace("arith-ee") << "Asserting an equality on " << s
<< ", on trichotomy"
253 Trace("arith-ee") << " based on " << lb
<< std::endl
;
254 Trace("arith-ee") << " based on " << ub
<< std::endl
;
255 assertionToEqualityEngine(true, s
, reason
, pf
);
258 void ArithCongruenceManager::watchedVariableIsZero(ConstraintCP eq
){
259 Debug("arith::cong") << "Cong::watchedVariableIsZero: " << *eq
<< std::endl
;
261 Assert(eq
->isEquality());
262 Assert(eq
->getValue().sgn() == 0);
264 ++(d_statistics
.d_watchedVariableIsZero
);
266 ArithVar s
= eq
->getVariable();
268 //Explain for conflict is correct as these proofs are generated
270 //These will be safe for propagation later as well
271 NodeBuilder
nb(Kind::AND
);
272 // An open proof of eq from literals now in reason.
273 if (Debug
.isOn("arith::cong"))
275 eq
->printProofTree(Debug("arith::cong"));
277 auto pf
= eq
->externalExplainByAssertions(nb
);
278 if (isProofEnabled())
281 PfRule::MACRO_SR_PRED_TRANSFORM
, {pf
}, {d_watchedEqualities
[s
]});
283 Node reason
= mkAndFromBuilder(nb
);
285 d_keepAlive
.push_back(reason
);
286 assertionToEqualityEngine(true, s
, reason
, pf
);
289 void ArithCongruenceManager::watchedVariableCannotBeZero(ConstraintCP c
){
290 Debug("arith::cong::notzero")
291 << "Cong::watchedVariableCannotBeZero " << *c
<< std::endl
;
292 ++(d_statistics
.d_watchedVariableIsNotZero
);
294 ArithVar s
= c
->getVariable();
295 Node disEq
= d_watchedEqualities
[s
].negate();
297 //Explain for conflict is correct as these proofs are generated and stored eagerly
298 //These will be safe for propagation later as well
299 NodeBuilder
nb(Kind::AND
);
300 // An open proof of eq from literals now in reason.
301 auto pf
= c
->externalExplainByAssertions(nb
);
302 if (Debug
.isOn("arith::cong::notzero"))
304 Debug("arith::cong::notzero") << " original proof ";
305 pf
->printDebug(Debug("arith::cong::notzero"));
306 Debug("arith::cong::notzero") << std::endl
;
308 Node reason
= mkAndFromBuilder(nb
);
309 if (isProofEnabled())
311 if (c
->getType() == ConstraintType::Disequality
)
313 Assert(c
->getLiteral() == d_watchedEqualities
[s
].negate());
314 // We have to prove equivalence to the watched disequality.
315 pf
= d_pnm
->mkNode(PfRule::MACRO_SR_PRED_TRANSFORM
, {pf
}, {disEq
});
319 Debug("arith::cong::notzero")
320 << " proof modification needed" << std::endl
;
323 // c has form x_i = d, d > 0 => multiply c by -1 in Farkas proof
324 // c has form x_i = d, d > 0 => multiply c by 1 in Farkas proof
325 // c has form x_i <= d, d < 0 => multiply c by 1 in Farkas proof
326 // c has form x_i >= d, d > 0 => multiply c by -1 in Farkas proof
327 const bool scaleCNegatively
= c
->getType() == ConstraintType::LowerBound
328 || (c
->getType() == ConstraintType::Equality
329 && c
->getValue().sgn() > 0);
330 const int cSign
= scaleCNegatively
? -1 : 1;
331 TNode isZero
= d_watchedEqualities
[s
];
332 const auto isZeroPf
= d_pnm
->mkAssume(isZero
);
333 const auto nm
= NodeManager::currentNM();
335 d_pnm
->mkNode(PfRule::MACRO_ARITH_SCALE_SUM_UB
,
337 // Trick for getting correct, opposing signs.
338 {nm
->mkConst(CONST_RATIONAL
, Rational(-1 * cSign
)),
339 nm
->mkConst(CONST_RATIONAL
, Rational(cSign
))});
340 const auto botPf
= d_pnm
->mkNode(
341 PfRule::MACRO_SR_PRED_TRANSFORM
, {sumPf
}, {nm
->mkConst(false)});
342 std::vector
<Node
> assumption
= {isZero
};
343 pf
= d_pnm
->mkScope(botPf
, assumption
, false);
344 Debug("arith::cong::notzero") << " new proof ";
345 pf
->printDebug(Debug("arith::cong::notzero"));
346 Debug("arith::cong::notzero") << std::endl
;
348 Assert(pf
->getResult() == disEq
);
350 d_keepAlive
.push_back(reason
);
351 assertionToEqualityEngine(false, s
, reason
, pf
);
355 bool ArithCongruenceManager::propagate(TNode x
){
356 Debug("arith::congruenceManager")<< "ArithCongruenceManager::propagate("<<x
<<")"<<std::endl
;
361 Node rewritten
= rewrite(x
);
363 //Need to still propagate this!
364 if(rewritten
.getKind() == kind::CONST_BOOLEAN
){
367 if(rewritten
.getConst
<bool>()){
370 // x rewrites to false.
371 ++(d_statistics
.d_conflicts
);
372 TrustNode trn
= explainInternal(x
);
373 Node conf
= flattenAnd(trn
.getNode());
374 Debug("arith::congruenceManager") << "rewritten to false "<<x
<<" with explanation "<< conf
<< std::endl
;
375 if (isProofEnabled())
377 auto pf
= trn
.getGenerator()->getProofFor(trn
.getProven());
378 auto confPf
= d_pnm
->mkNode(
379 PfRule::MACRO_SR_PRED_TRANSFORM
, {pf
}, {conf
.negate()});
380 raiseConflict(conf
, confPf
);
390 Assert(rewritten
.getKind() != kind::CONST_BOOLEAN
);
392 ConstraintP c
= d_constraintDatabase
.lookup(rewritten
);
393 if(c
== NullConstraint
){
394 //using setup as there may not be a corresponding congruence literal yet
395 d_setupLiteral(rewritten
);
396 c
= d_constraintDatabase
.lookup(rewritten
);
397 Assert(c
!= NullConstraint
);
400 Debug("arith::congruenceManager")<< "x is "
401 << c
->hasProof() << " "
402 << (x
== rewritten
) << " "
403 << c
->canBePropagated() << " "
404 << c
->negationHasProof() << std::endl
;
406 if(c
->negationHasProof()){
407 TrustNode texpC
= explainInternal(x
);
408 Node expC
= texpC
.getNode();
409 ConstraintCP negC
= c
->getNegation();
410 Node neg
= Constraint::externalExplainByAssertions({negC
});
411 Node conf
= expC
.andNode(neg
);
412 Node final
= flattenAnd(conf
);
414 ++(d_statistics
.d_conflicts
);
415 raiseConflict(final
);
416 Debug("arith::congruenceManager") << "congruenceManager found a conflict " << final
<< std::endl
;
420 // Cases for propagation
422 // S : x == rewritten
423 // P : c can be propagated
426 // 000 : propagate x, and mark C it as being explained
427 // 001 : propagate x, and propagate c after marking it as being explained
428 // 01* : propagate x, mark c but do not propagate c
429 // 10* : propagate x, do not mark c and do not propagate c
430 // 11* : drop the constraint, do not propagate x or c
432 if(!c
->hasProof() && x
!= rewritten
){
433 if(c
->assertedToTheTheory()){
434 pushBack(x
, rewritten
, c
->getWitness());
436 pushBack(x
, rewritten
);
439 c
->setEqualityEngineProof();
440 if(c
->canBePropagated() && !c
->assertedToTheTheory()){
442 ++(d_statistics
.d_propagateConstraints
);
445 }else if(!c
->hasProof() && x
== rewritten
){
446 if(c
->assertedToTheTheory()){
447 pushBack(x
, c
->getWitness());
451 c
->setEqualityEngineProof();
452 }else if(c
->hasProof() && x
!= rewritten
){
453 if(c
->assertedToTheTheory()){
459 Assert(c
->hasProof() && x
== rewritten
);
464 void ArithCongruenceManager::explain(TNode literal
, std::vector
<TNode
>& assumptions
) {
465 if (literal
.getKind() != kind::NOT
) {
466 d_ee
->explainEquality(literal
[0], literal
[1], true, assumptions
);
468 d_ee
->explainEquality(literal
[0][0], literal
[0][1], false, assumptions
);
472 void ArithCongruenceManager::enqueueIntoNB(const std::set
<TNode
> s
,
475 std::set
<TNode
>::const_iterator it
= s
.begin();
476 std::set
<TNode
>::const_iterator it_end
= s
.end();
477 for(; it
!= it_end
; ++it
) {
482 TrustNode
ArithCongruenceManager::explainInternal(TNode internal
)
484 if (isProofEnabled())
486 return d_pfee
->explain(internal
);
488 // otherwise, explain without proof generator
489 Node exp
= d_ee
->mkExplainLit(internal
);
490 return TrustNode::mkTrustPropExp(internal
, exp
, nullptr);
493 TrustNode
ArithCongruenceManager::explain(TNode external
)
495 Trace("arith-ee") << "Ask for explanation of " << external
<< std::endl
;
496 Node internal
= externalToInternal(external
);
497 Trace("arith-ee") << "...internal = " << internal
<< std::endl
;
498 TrustNode trn
= explainInternal(internal
);
499 if (isProofEnabled() && trn
.getProven()[1] != external
)
501 Assert(trn
.getKind() == TrustNodeKind::PROP_EXP
);
502 Assert(trn
.getProven().getKind() == Kind::IMPLIES
);
503 Assert(trn
.getGenerator() != nullptr);
504 Trace("arith-ee") << "tweaking proof to prove " << external
<< " not "
505 << trn
.getProven()[1] << std::endl
;
506 std::vector
<std::shared_ptr
<ProofNode
>> assumptionPfs
;
507 std::vector
<Node
> assumptions
= andComponents(trn
.getNode());
508 assumptionPfs
.push_back(trn
.toProofNode());
509 for (const auto& a
: assumptions
)
511 assumptionPfs
.push_back(
512 d_pnm
->mkNode(PfRule::TRUE_INTRO
, {d_pnm
->mkAssume(a
)}, {}));
514 auto litPf
= d_pnm
->mkNode(
515 PfRule::MACRO_SR_PRED_TRANSFORM
, {assumptionPfs
}, {external
});
516 auto extPf
= d_pnm
->mkScope(litPf
, assumptions
);
517 return d_pfGenExplain
->mkTrustedPropagation(external
, trn
.getNode(), extPf
);
522 void ArithCongruenceManager::explain(TNode external
, NodeBuilder
& out
)
524 Node internal
= externalToInternal(external
);
526 std::vector
<TNode
> assumptions
;
527 explain(internal
, assumptions
);
528 std::set
<TNode
> assumptionSet
;
529 assumptionSet
.insert(assumptions
.begin(), assumptions
.end());
531 enqueueIntoNB(assumptionSet
, out
);
534 void ArithCongruenceManager::addWatchedPair(ArithVar s
, TNode x
, TNode y
){
535 Assert(!isWatchedVariable(s
));
537 Debug("arith::congruenceManager")
538 << "addWatchedPair(" << s
<< ", " << x
<< ", " << y
<< ")" << std::endl
;
541 ++(d_statistics
.d_watchedVariables
);
543 d_watchedVariables
.add(s
);
545 Node eq
= x
.eqNode(y
);
546 d_watchedEqualities
.set(s
, eq
);
549 void ArithCongruenceManager::assertLitToEqualityEngine(
550 Node lit
, TNode reason
, std::shared_ptr
<ProofNode
> pf
)
552 bool isEquality
= lit
.getKind() != Kind::NOT
;
553 Node eq
= isEquality
? lit
: lit
[0];
554 Assert(eq
.getKind() == Kind::EQUAL
);
556 Trace("arith-ee") << "Assert to Eq " << lit
<< ", reason " << reason
558 if (isProofEnabled())
560 if (CDProof::isSame(lit
, reason
))
562 Trace("arith-pfee") << "Asserting only, b/c implied by symm" << std::endl
;
563 // The equality engine doesn't ref-count for us...
564 d_keepAlive
.push_back(eq
);
565 d_keepAlive
.push_back(reason
);
566 d_ee
->assertEquality(eq
, isEquality
, reason
);
568 else if (hasProofFor(lit
))
570 Trace("arith-pfee") << "Skipping b/c already done" << std::endl
;
574 setProofFor(lit
, pf
);
575 Trace("arith-pfee") << "Actually asserting" << std::endl
;
576 if (Debug
.isOn("arith-pfee"))
578 Trace("arith-pfee") << "Proof: ";
579 pf
->printDebug(Trace("arith-pfee"));
580 Trace("arith-pfee") << std::endl
;
582 // The proof equality engine *does* ref-count for us...
583 d_pfee
->assertFact(lit
, reason
, d_pfGenEe
.get());
588 // The equality engine doesn't ref-count for us...
589 d_keepAlive
.push_back(eq
);
590 d_keepAlive
.push_back(reason
);
591 d_ee
->assertEquality(eq
, isEquality
, reason
);
595 void ArithCongruenceManager::assertionToEqualityEngine(
596 bool isEquality
, ArithVar s
, TNode reason
, std::shared_ptr
<ProofNode
> pf
)
598 Assert(isWatchedVariable(s
));
600 TNode eq
= d_watchedEqualities
[s
];
601 Assert(eq
.getKind() == kind::EQUAL
);
603 Node lit
= isEquality
? Node(eq
) : eq
.notNode();
604 Trace("arith-ee") << "Assert to Eq " << eq
<< ", pol " << isEquality
605 << ", reason " << reason
<< std::endl
;
606 assertLitToEqualityEngine(lit
, reason
, pf
);
609 bool ArithCongruenceManager::hasProofFor(TNode f
) const
611 Assert(isProofEnabled());
612 if (d_pfGenEe
->hasProofFor(f
))
616 Node sym
= CDProof::getSymmFact(f
);
617 Assert(!sym
.isNull());
618 return d_pfGenEe
->hasProofFor(sym
);
621 void ArithCongruenceManager::setProofFor(TNode f
,
622 std::shared_ptr
<ProofNode
> pf
) const
624 Assert(!hasProofFor(f
));
625 d_pfGenEe
->mkTrustNode(f
, pf
);
626 Node symF
= CDProof::getSymmFact(f
);
627 auto symPf
= d_pnm
->mkNode(PfRule::SYMM
, {pf
}, {});
628 d_pfGenEe
->mkTrustNode(symF
, symPf
);
631 void ArithCongruenceManager::equalsConstant(ConstraintCP c
){
632 Assert(c
->isEquality());
634 ++(d_statistics
.d_equalsConstantCalls
);
635 Debug("equalsConstant") << "equals constant " << c
<< std::endl
;
637 ArithVar x
= c
->getVariable();
638 Node xAsNode
= d_avariables
.asNode(x
);
639 NodeManager
* nm
= NodeManager::currentNM();
640 Node asRational
= nm
->mkConstRealOrInt(
641 xAsNode
.getType(), c
->getValue().getNoninfinitesimalPart());
643 // No guarentee this is in normal form!
644 // Note though, that it happens to be in proof normal form!
645 Node eq
= xAsNode
.eqNode(asRational
);
646 d_keepAlive
.push_back(eq
);
648 NodeBuilder
nb(Kind::AND
);
649 auto pf
= c
->externalExplainByAssertions(nb
);
650 Node reason
= mkAndFromBuilder(nb
);
651 d_keepAlive
.push_back(reason
);
653 Trace("arith-ee") << "Assert equalsConstant " << eq
<< ", reason " << reason
<< std::endl
;
654 assertLitToEqualityEngine(eq
, reason
, pf
);
657 void ArithCongruenceManager::equalsConstant(ConstraintCP lb
, ConstraintCP ub
){
658 Assert(lb
->isLowerBound());
659 Assert(ub
->isUpperBound());
660 Assert(lb
->getVariable() == ub
->getVariable());
662 ++(d_statistics
.d_equalsConstantCalls
);
663 Debug("equalsConstant") << "equals constant " << lb
<< std::endl
666 ArithVar x
= lb
->getVariable();
667 NodeBuilder
nb(Kind::AND
);
668 auto pfLb
= lb
->externalExplainByAssertions(nb
);
669 auto pfUb
= ub
->externalExplainByAssertions(nb
);
670 Node reason
= mkAndFromBuilder(nb
);
672 Node xAsNode
= d_avariables
.asNode(x
);
673 NodeManager
* nm
= NodeManager::currentNM();
674 Node asRational
= nm
->mkConstRealOrInt(
675 xAsNode
.getType(), lb
->getValue().getNoninfinitesimalPart());
677 // No guarentee this is in normal form!
678 // Note though, that it happens to be in proof normal form!
679 Node eq
= xAsNode
.eqNode(asRational
);
680 std::shared_ptr
<ProofNode
> pf
;
681 if (isProofEnabled())
683 pf
= d_pnm
->mkNode(PfRule::ARITH_TRICHOTOMY
, {pfLb
, pfUb
}, {eq
});
685 d_keepAlive
.push_back(eq
);
686 d_keepAlive
.push_back(reason
);
688 Trace("arith-ee") << "Assert equalsConstant2 " << eq
<< ", reason " << reason
<< std::endl
;
690 assertLitToEqualityEngine(eq
, reason
, pf
);
693 bool ArithCongruenceManager::isProofEnabled() const { return d_pnm
!= nullptr; }
695 std::vector
<Node
> andComponents(TNode an
)
697 auto nm
= NodeManager::currentNM();
698 if (an
== nm
->mkConst(true))
702 else if (an
.getKind() != Kind::AND
)
706 std::vector
<Node
> a
{};
707 a
.reserve(an
.getNumChildren());
708 a
.insert(a
.end(), an
.begin(), an
.end());
713 } // namespace theory