1 |
|
/****************************************************************************** |
2 |
|
* Top contributors (to current version): |
3 |
|
* Tim King, Alex Ozdemir, Andrew Reynolds |
4 |
|
* |
5 |
|
* This file is part of the cvc5 project. |
6 |
|
* |
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 |
|
* **************************************************************************** |
12 |
|
* |
13 |
|
* [[ Add one-line brief description here ]] |
14 |
|
* |
15 |
|
* [[ Add lengthier description here ]] |
16 |
|
* \todo document this file |
17 |
|
*/ |
18 |
|
|
19 |
|
#include "theory/arith/congruence_manager.h" |
20 |
|
|
21 |
|
#include "base/output.h" |
22 |
|
#include "expr/proof_node.h" |
23 |
|
#include "expr/proof_node_manager.h" |
24 |
|
#include "smt/smt_statistics_registry.h" |
25 |
|
#include "theory/arith/arith_utilities.h" |
26 |
|
#include "theory/arith/constraint.h" |
27 |
|
#include "theory/arith/partial_model.h" |
28 |
|
#include "theory/ee_setup_info.h" |
29 |
|
#include "theory/rewriter.h" |
30 |
|
#include "theory/uf/equality_engine.h" |
31 |
|
#include "theory/uf/proof_equality_engine.h" |
32 |
|
#include "options/arith_options.h" |
33 |
|
|
34 |
|
namespace cvc5 { |
35 |
|
namespace theory { |
36 |
|
namespace arith { |
37 |
|
|
38 |
9459 |
ArithCongruenceManager::ArithCongruenceManager( |
39 |
|
context::Context* c, |
40 |
|
context::UserContext* u, |
41 |
|
ConstraintDatabase& cd, |
42 |
|
SetupLiteralCallBack setup, |
43 |
|
const ArithVariables& avars, |
44 |
|
RaiseEqualityEngineConflict raiseConflict, |
45 |
9459 |
ProofNodeManager* pnm) |
46 |
|
: d_inConflict(c), |
47 |
|
d_raiseConflict(raiseConflict), |
48 |
|
d_notify(*this), |
49 |
|
d_keepAlive(c), |
50 |
|
d_propagatations(c), |
51 |
|
d_explanationMap(c), |
52 |
|
d_constraintDatabase(cd), |
53 |
|
d_setupLiteral(setup), |
54 |
|
d_avariables(avars), |
55 |
|
d_ee(nullptr), |
56 |
|
d_satContext(c), |
57 |
|
d_userContext(u), |
58 |
|
d_pnm(pnm), |
59 |
|
// Construct d_pfGenEe with the SAT context, since its proof include |
60 |
|
// unclosed assumptions of theory literals. |
61 |
|
d_pfGenEe( |
62 |
9459 |
new EagerProofGenerator(pnm, c, "ArithCongruenceManager::pfGenEe")), |
63 |
|
// Construct d_pfGenEe with the USER context, since its proofs are closed. |
64 |
|
d_pfGenExplain(new EagerProofGenerator( |
65 |
9459 |
pnm, u, "ArithCongruenceManager::pfGenExplain")), |
66 |
28377 |
d_pfee(nullptr) |
67 |
|
{ |
68 |
9459 |
} |
69 |
|
|
70 |
9459 |
ArithCongruenceManager::~ArithCongruenceManager() {} |
71 |
|
|
72 |
9459 |
bool ArithCongruenceManager::needsEqualityEngine(EeSetupInfo& esi) |
73 |
|
{ |
74 |
9459 |
esi.d_notify = &d_notify; |
75 |
9459 |
esi.d_name = "theory::arith::ArithCongruenceManager"; |
76 |
9459 |
return true; |
77 |
|
} |
78 |
|
|
79 |
9459 |
void ArithCongruenceManager::finishInit(eq::EqualityEngine* ee, |
80 |
|
eq::ProofEqEngine* pfee) |
81 |
|
{ |
82 |
9459 |
Assert(ee != nullptr); |
83 |
9459 |
d_ee = ee; |
84 |
9459 |
d_ee->addFunctionKind(kind::NONLINEAR_MULT); |
85 |
9459 |
d_ee->addFunctionKind(kind::EXPONENTIAL); |
86 |
9459 |
d_ee->addFunctionKind(kind::SINE); |
87 |
9459 |
d_ee->addFunctionKind(kind::IAND); |
88 |
|
// have proof equality engine only if proofs are enabled |
89 |
9459 |
Assert(isProofEnabled() == (pfee != nullptr)); |
90 |
9459 |
d_pfee = pfee; |
91 |
9459 |
} |
92 |
|
|
93 |
9459 |
ArithCongruenceManager::Statistics::Statistics() |
94 |
9459 |
: d_watchedVariables(smtStatisticsRegistry().registerInt( |
95 |
18918 |
"theory::arith::congruence::watchedVariables")), |
96 |
9459 |
d_watchedVariableIsZero(smtStatisticsRegistry().registerInt( |
97 |
18918 |
"theory::arith::congruence::watchedVariableIsZero")), |
98 |
9459 |
d_watchedVariableIsNotZero(smtStatisticsRegistry().registerInt( |
99 |
18918 |
"theory::arith::congruence::watchedVariableIsNotZero")), |
100 |
9459 |
d_equalsConstantCalls(smtStatisticsRegistry().registerInt( |
101 |
18918 |
"theory::arith::congruence::equalsConstantCalls")), |
102 |
9459 |
d_propagations(smtStatisticsRegistry().registerInt( |
103 |
18918 |
"theory::arith::congruence::propagations")), |
104 |
9459 |
d_propagateConstraints(smtStatisticsRegistry().registerInt( |
105 |
18918 |
"theory::arith::congruence::propagateConstraints")), |
106 |
9459 |
d_conflicts(smtStatisticsRegistry().registerInt( |
107 |
66213 |
"theory::arith::congruence::conflicts")) |
108 |
|
{ |
109 |
9459 |
} |
110 |
|
|
111 |
9459 |
ArithCongruenceManager::ArithCongruenceNotify::ArithCongruenceNotify(ArithCongruenceManager& acm) |
112 |
9459 |
: d_acm(acm) |
113 |
9459 |
{} |
114 |
|
|
115 |
|
bool ArithCongruenceManager::ArithCongruenceNotify::eqNotifyTriggerPredicate( |
116 |
|
TNode predicate, bool value) |
117 |
|
{ |
118 |
|
Assert(predicate.getKind() == kind::EQUAL); |
119 |
|
Debug("arith::congruences") |
120 |
|
<< "ArithCongruenceNotify::eqNotifyTriggerPredicate(" << predicate << ", " |
121 |
|
<< (value ? "true" : "false") << ")" << std::endl; |
122 |
|
if (value) { |
123 |
|
return d_acm.propagate(predicate); |
124 |
|
} |
125 |
|
return d_acm.propagate(predicate.notNode()); |
126 |
|
} |
127 |
|
|
128 |
592435 |
bool ArithCongruenceManager::ArithCongruenceNotify::eqNotifyTriggerTermEquality(TheoryId tag, TNode t1, TNode t2, bool value) { |
129 |
592435 |
Debug("arith::congruences") << "ArithCongruenceNotify::eqNotifyTriggerTermEquality(" << t1 << ", " << t2 << ", " << (value ? "true" : "false") << ")" << std::endl; |
130 |
592435 |
if (value) { |
131 |
485857 |
return d_acm.propagate(t1.eqNode(t2)); |
132 |
|
} else { |
133 |
106578 |
return d_acm.propagate(t1.eqNode(t2).notNode()); |
134 |
|
} |
135 |
|
} |
136 |
1792 |
void ArithCongruenceManager::ArithCongruenceNotify::eqNotifyConstantTermMerge(TNode t1, TNode t2) { |
137 |
1792 |
Debug("arith::congruences") << "ArithCongruenceNotify::eqNotifyConstantTermMerge(" << t1 << ", " << t2 << std::endl; |
138 |
1792 |
d_acm.propagate(t1.eqNode(t2)); |
139 |
1792 |
} |
140 |
1209507 |
void ArithCongruenceManager::ArithCongruenceNotify::eqNotifyNewClass(TNode t) { |
141 |
1209507 |
} |
142 |
1313104 |
void ArithCongruenceManager::ArithCongruenceNotify::eqNotifyMerge(TNode t1, |
143 |
|
TNode t2) |
144 |
|
{ |
145 |
1313104 |
} |
146 |
279434 |
void ArithCongruenceManager::ArithCongruenceNotify::eqNotifyDisequal(TNode t1, TNode t2, TNode reason) { |
147 |
279434 |
} |
148 |
|
|
149 |
1943 |
void ArithCongruenceManager::raiseConflict(Node conflict, |
150 |
|
std::shared_ptr<ProofNode> pf) |
151 |
|
{ |
152 |
1943 |
Assert(!inConflict()); |
153 |
1943 |
Debug("arith::conflict") << "difference manager conflict " << conflict << std::endl; |
154 |
1943 |
d_inConflict.raise(); |
155 |
1943 |
d_raiseConflict.raiseEEConflict(conflict, pf); |
156 |
1943 |
} |
157 |
596170 |
bool ArithCongruenceManager::inConflict() const{ |
158 |
596170 |
return d_inConflict.isRaised(); |
159 |
|
} |
160 |
|
|
161 |
3093167 |
bool ArithCongruenceManager::hasMorePropagations() const { |
162 |
3093167 |
return !d_propagatations.empty(); |
163 |
|
} |
164 |
465003 |
const Node ArithCongruenceManager::getNextPropagation() { |
165 |
465003 |
Assert(hasMorePropagations()); |
166 |
465003 |
Node prop = d_propagatations.front(); |
167 |
465003 |
d_propagatations.dequeue(); |
168 |
465003 |
return prop; |
169 |
|
} |
170 |
|
|
171 |
25048 |
bool ArithCongruenceManager::canExplain(TNode n) const { |
172 |
25048 |
return d_explanationMap.find(n) != d_explanationMap.end(); |
173 |
|
} |
174 |
|
|
175 |
14694 |
Node ArithCongruenceManager::externalToInternal(TNode n) const{ |
176 |
14694 |
Assert(canExplain(n)); |
177 |
14694 |
ExplainMap::const_iterator iter = d_explanationMap.find(n); |
178 |
14694 |
size_t pos = (*iter).second; |
179 |
14694 |
return d_propagatations[pos]; |
180 |
|
} |
181 |
|
|
182 |
418668 |
void ArithCongruenceManager::pushBack(TNode n){ |
183 |
418668 |
d_explanationMap.insert(n, d_propagatations.size()); |
184 |
418668 |
d_propagatations.enqueue(n); |
185 |
|
|
186 |
418668 |
++(d_statistics.d_propagations); |
187 |
418668 |
} |
188 |
83316 |
void ArithCongruenceManager::pushBack(TNode n, TNode r){ |
189 |
83316 |
d_explanationMap.insert(r, d_propagatations.size()); |
190 |
83316 |
d_explanationMap.insert(n, d_propagatations.size()); |
191 |
83316 |
d_propagatations.enqueue(n); |
192 |
|
|
193 |
83316 |
++(d_statistics.d_propagations); |
194 |
83316 |
} |
195 |
|
void ArithCongruenceManager::pushBack(TNode n, TNode r, TNode w){ |
196 |
|
d_explanationMap.insert(w, d_propagatations.size()); |
197 |
|
d_explanationMap.insert(r, d_propagatations.size()); |
198 |
|
d_explanationMap.insert(n, d_propagatations.size()); |
199 |
|
d_propagatations.enqueue(n); |
200 |
|
|
201 |
|
++(d_statistics.d_propagations); |
202 |
|
} |
203 |
|
|
204 |
299633 |
void ArithCongruenceManager::watchedVariableIsZero(ConstraintCP lb, ConstraintCP ub){ |
205 |
299633 |
Assert(lb->isLowerBound()); |
206 |
299633 |
Assert(ub->isUpperBound()); |
207 |
299633 |
Assert(lb->getVariable() == ub->getVariable()); |
208 |
299633 |
Assert(lb->getValue().sgn() == 0); |
209 |
299633 |
Assert(ub->getValue().sgn() == 0); |
210 |
|
|
211 |
299633 |
++(d_statistics.d_watchedVariableIsZero); |
212 |
|
|
213 |
299633 |
ArithVar s = lb->getVariable(); |
214 |
599266 |
TNode eq = d_watchedEqualities[s]; |
215 |
299633 |
ConstraintCP eqC = d_constraintDatabase.getConstraint( |
216 |
299633 |
s, ConstraintType::Equality, lb->getValue()); |
217 |
599266 |
NodeBuilder reasonBuilder(Kind::AND); |
218 |
599266 |
auto pfLb = lb->externalExplainByAssertions(reasonBuilder); |
219 |
599266 |
auto pfUb = ub->externalExplainByAssertions(reasonBuilder); |
220 |
599266 |
Node reason = safeConstructNary(reasonBuilder); |
221 |
599266 |
std::shared_ptr<ProofNode> pf{}; |
222 |
299633 |
if (isProofEnabled()) |
223 |
|
{ |
224 |
88948 |
pf = d_pnm->mkNode( |
225 |
66711 |
PfRule::ARITH_TRICHOTOMY, {pfLb, pfUb}, {eqC->getProofLiteral()}); |
226 |
22237 |
pf = d_pnm->mkNode(PfRule::MACRO_SR_PRED_TRANSFORM, {pf}, {eq}); |
227 |
|
} |
228 |
|
|
229 |
299633 |
d_keepAlive.push_back(reason); |
230 |
599266 |
Trace("arith-ee") << "Asserting an equality on " << s << ", on trichotomy" |
231 |
299633 |
<< std::endl; |
232 |
299633 |
Trace("arith-ee") << " based on " << lb << std::endl; |
233 |
299633 |
Trace("arith-ee") << " based on " << ub << std::endl; |
234 |
299633 |
assertionToEqualityEngine(true, s, reason, pf); |
235 |
299633 |
} |
236 |
|
|
237 |
162817 |
void ArithCongruenceManager::watchedVariableIsZero(ConstraintCP eq){ |
238 |
162817 |
Debug("arith::cong") << "Cong::watchedVariableIsZero: " << *eq << std::endl; |
239 |
|
|
240 |
162817 |
Assert(eq->isEquality()); |
241 |
162817 |
Assert(eq->getValue().sgn() == 0); |
242 |
|
|
243 |
162817 |
++(d_statistics.d_watchedVariableIsZero); |
244 |
|
|
245 |
162817 |
ArithVar s = eq->getVariable(); |
246 |
|
|
247 |
|
//Explain for conflict is correct as these proofs are generated |
248 |
|
//and stored eagerly |
249 |
|
//These will be safe for propagation later as well |
250 |
325634 |
NodeBuilder nb(Kind::AND); |
251 |
|
// An open proof of eq from literals now in reason. |
252 |
162817 |
if (Debug.isOn("arith::cong")) |
253 |
|
{ |
254 |
|
eq->printProofTree(Debug("arith::cong")); |
255 |
|
} |
256 |
325634 |
auto pf = eq->externalExplainByAssertions(nb); |
257 |
162817 |
if (isProofEnabled()) |
258 |
|
{ |
259 |
51768 |
pf = d_pnm->mkNode( |
260 |
34512 |
PfRule::MACRO_SR_PRED_TRANSFORM, {pf}, {d_watchedEqualities[s]}); |
261 |
|
} |
262 |
325634 |
Node reason = safeConstructNary(nb); |
263 |
|
|
264 |
162817 |
d_keepAlive.push_back(reason); |
265 |
162817 |
assertionToEqualityEngine(true, s, reason, pf); |
266 |
162817 |
} |
267 |
|
|
268 |
595223 |
void ArithCongruenceManager::watchedVariableCannotBeZero(ConstraintCP c){ |
269 |
1190446 |
Debug("arith::cong::notzero") |
270 |
595223 |
<< "Cong::watchedVariableCannotBeZero " << *c << std::endl; |
271 |
595223 |
++(d_statistics.d_watchedVariableIsNotZero); |
272 |
|
|
273 |
595223 |
ArithVar s = c->getVariable(); |
274 |
1190446 |
Node disEq = d_watchedEqualities[s].negate(); |
275 |
|
|
276 |
|
//Explain for conflict is correct as these proofs are generated and stored eagerly |
277 |
|
//These will be safe for propagation later as well |
278 |
1190446 |
NodeBuilder nb(Kind::AND); |
279 |
|
// An open proof of eq from literals now in reason. |
280 |
1190446 |
auto pf = c->externalExplainByAssertions(nb); |
281 |
595223 |
if (Debug.isOn("arith::cong::notzero")) |
282 |
|
{ |
283 |
|
Debug("arith::cong::notzero") << " original proof "; |
284 |
|
pf->printDebug(Debug("arith::cong::notzero")); |
285 |
|
Debug("arith::cong::notzero") << std::endl; |
286 |
|
} |
287 |
1190446 |
Node reason = safeConstructNary(nb); |
288 |
595223 |
if (isProofEnabled()) |
289 |
|
{ |
290 |
44008 |
if (c->getType() == ConstraintType::Disequality) |
291 |
|
{ |
292 |
11191 |
Assert(c->getLiteral() == d_watchedEqualities[s].negate()); |
293 |
|
// We have to prove equivalence to the watched disequality. |
294 |
11191 |
pf = d_pnm->mkNode(PfRule::MACRO_SR_PRED_TRANSFORM, {pf}, {disEq}); |
295 |
|
} |
296 |
|
else |
297 |
|
{ |
298 |
65634 |
Debug("arith::cong::notzero") |
299 |
32817 |
<< " proof modification needed" << std::endl; |
300 |
|
|
301 |
|
// Four cases: |
302 |
|
// c has form x_i = d, d > 0 => multiply c by -1 in Farkas proof |
303 |
|
// c has form x_i = d, d > 0 => multiply c by 1 in Farkas proof |
304 |
|
// c has form x_i <= d, d < 0 => multiply c by 1 in Farkas proof |
305 |
|
// c has form x_i >= d, d > 0 => multiply c by -1 in Farkas proof |
306 |
32817 |
const bool scaleCNegatively = c->getType() == ConstraintType::LowerBound |
307 |
45782 |
|| (c->getType() == ConstraintType::Equality |
308 |
35768 |
&& c->getValue().sgn() > 0); |
309 |
32817 |
const int cSign = scaleCNegatively ? -1 : 1; |
310 |
65634 |
TNode isZero = d_watchedEqualities[s]; |
311 |
65634 |
const auto isZeroPf = d_pnm->mkAssume(isZero); |
312 |
32817 |
const auto nm = NodeManager::currentNM(); |
313 |
32817 |
const auto sumPf = d_pnm->mkNode( |
314 |
|
PfRule::MACRO_ARITH_SCALE_SUM_UB, |
315 |
|
{isZeroPf, pf}, |
316 |
|
// Trick for getting correct, opposing signs. |
317 |
65634 |
{nm->mkConst(Rational(-1 * cSign)), nm->mkConst(Rational(cSign))}); |
318 |
32817 |
const auto botPf = d_pnm->mkNode( |
319 |
65634 |
PfRule::MACRO_SR_PRED_TRANSFORM, {sumPf}, {nm->mkConst(false)}); |
320 |
65634 |
std::vector<Node> assumption = {isZero}; |
321 |
32817 |
pf = d_pnm->mkScope(botPf, assumption, false); |
322 |
32817 |
Debug("arith::cong::notzero") << " new proof "; |
323 |
32817 |
pf->printDebug(Debug("arith::cong::notzero")); |
324 |
32817 |
Debug("arith::cong::notzero") << std::endl; |
325 |
|
} |
326 |
44008 |
Assert(pf->getResult() == disEq); |
327 |
|
} |
328 |
595223 |
d_keepAlive.push_back(reason); |
329 |
595223 |
assertionToEqualityEngine(false, s, reason, pf); |
330 |
595223 |
} |
331 |
|
|
332 |
|
|
333 |
594227 |
bool ArithCongruenceManager::propagate(TNode x){ |
334 |
594227 |
Debug("arith::congruenceManager")<< "ArithCongruenceManager::propagate("<<x<<")"<<std::endl; |
335 |
594227 |
if(inConflict()){ |
336 |
|
return true; |
337 |
|
} |
338 |
|
|
339 |
1188454 |
Node rewritten = Rewriter::rewrite(x); |
340 |
|
|
341 |
|
//Need to still propagate this! |
342 |
594227 |
if(rewritten.getKind() == kind::CONST_BOOLEAN){ |
343 |
1892 |
pushBack(x); |
344 |
|
|
345 |
1892 |
if(rewritten.getConst<bool>()){ |
346 |
100 |
return true; |
347 |
|
}else{ |
348 |
|
// x rewrites to false. |
349 |
1792 |
++(d_statistics.d_conflicts); |
350 |
3584 |
TrustNode trn = explainInternal(x); |
351 |
3584 |
Node conf = flattenAnd(trn.getNode()); |
352 |
1792 |
Debug("arith::congruenceManager") << "rewritten to false "<<x<<" with explanation "<< conf << std::endl; |
353 |
1792 |
if (isProofEnabled()) |
354 |
|
{ |
355 |
756 |
auto pf = trn.getGenerator()->getProofFor(trn.getProven()); |
356 |
378 |
auto confPf = d_pnm->mkNode( |
357 |
756 |
PfRule::MACRO_SR_PRED_TRANSFORM, {pf}, {conf.negate()}); |
358 |
378 |
raiseConflict(conf, confPf); |
359 |
|
} |
360 |
|
else |
361 |
|
{ |
362 |
1414 |
raiseConflict(conf); |
363 |
|
} |
364 |
1792 |
return false; |
365 |
|
} |
366 |
|
} |
367 |
|
|
368 |
592335 |
Assert(rewritten.getKind() != kind::CONST_BOOLEAN); |
369 |
|
|
370 |
592335 |
ConstraintP c = d_constraintDatabase.lookup(rewritten); |
371 |
592335 |
if(c == NullConstraint){ |
372 |
|
//using setup as there may not be a corresponding congruence literal yet |
373 |
16610 |
d_setupLiteral(rewritten); |
374 |
16610 |
c = d_constraintDatabase.lookup(rewritten); |
375 |
16610 |
Assert(c != NullConstraint); |
376 |
|
} |
377 |
|
|
378 |
1184670 |
Debug("arith::congruenceManager")<< "x is " |
379 |
1184670 |
<< c->hasProof() << " " |
380 |
1184670 |
<< (x == rewritten) << " " |
381 |
1184670 |
<< c->canBePropagated() << " " |
382 |
592335 |
<< c->negationHasProof() << std::endl; |
383 |
|
|
384 |
592335 |
if(c->negationHasProof()){ |
385 |
302 |
TrustNode texpC = explainInternal(x); |
386 |
302 |
Node expC = texpC.getNode(); |
387 |
151 |
ConstraintCP negC = c->getNegation(); |
388 |
302 |
Node neg = Constraint::externalExplainByAssertions({negC}); |
389 |
302 |
Node conf = expC.andNode(neg); |
390 |
302 |
Node final = flattenAnd(conf); |
391 |
|
|
392 |
151 |
++(d_statistics.d_conflicts); |
393 |
151 |
raiseConflict(final); |
394 |
151 |
Debug("arith::congruenceManager") << "congruenceManager found a conflict " << final << std::endl; |
395 |
151 |
return false; |
396 |
|
} |
397 |
|
|
398 |
|
// Cases for propagation |
399 |
|
// C : c has a proof |
400 |
|
// S : x == rewritten |
401 |
|
// P : c can be propagated |
402 |
|
// |
403 |
|
// CSP |
404 |
|
// 000 : propagate x, and mark C it as being explained |
405 |
|
// 001 : propagate x, and propagate c after marking it as being explained |
406 |
|
// 01* : propagate x, mark c but do not propagate c |
407 |
|
// 10* : propagate x, do not mark c and do not propagate c |
408 |
|
// 11* : drop the constraint, do not propagate x or c |
409 |
|
|
410 |
592184 |
if(!c->hasProof() && x != rewritten){ |
411 |
83316 |
if(c->assertedToTheTheory()){ |
412 |
|
pushBack(x, rewritten, c->getWitness()); |
413 |
|
}else{ |
414 |
83316 |
pushBack(x, rewritten); |
415 |
|
} |
416 |
|
|
417 |
83316 |
c->setEqualityEngineProof(); |
418 |
83316 |
if(c->canBePropagated() && !c->assertedToTheTheory()){ |
419 |
|
|
420 |
9743 |
++(d_statistics.d_propagateConstraints); |
421 |
9743 |
c->propagate(); |
422 |
|
} |
423 |
508868 |
}else if(!c->hasProof() && x == rewritten){ |
424 |
68114 |
if(c->assertedToTheTheory()){ |
425 |
|
pushBack(x, c->getWitness()); |
426 |
|
}else{ |
427 |
68114 |
pushBack(x); |
428 |
|
} |
429 |
68114 |
c->setEqualityEngineProof(); |
430 |
440754 |
}else if(c->hasProof() && x != rewritten){ |
431 |
348662 |
if(c->assertedToTheTheory()){ |
432 |
347352 |
pushBack(x); |
433 |
|
}else{ |
434 |
1310 |
pushBack(x); |
435 |
|
} |
436 |
|
}else{ |
437 |
92092 |
Assert(c->hasProof() && x == rewritten); |
438 |
|
} |
439 |
592184 |
return true; |
440 |
|
} |
441 |
|
|
442 |
|
void ArithCongruenceManager::explain(TNode literal, std::vector<TNode>& assumptions) { |
443 |
|
if (literal.getKind() != kind::NOT) { |
444 |
|
d_ee->explainEquality(literal[0], literal[1], true, assumptions); |
445 |
|
} else { |
446 |
|
d_ee->explainEquality(literal[0][0], literal[0][1], false, assumptions); |
447 |
|
} |
448 |
|
} |
449 |
|
|
450 |
|
void ArithCongruenceManager::enqueueIntoNB(const std::set<TNode> s, |
451 |
|
NodeBuilder& nb) |
452 |
|
{ |
453 |
|
std::set<TNode>::const_iterator it = s.begin(); |
454 |
|
std::set<TNode>::const_iterator it_end = s.end(); |
455 |
|
for(; it != it_end; ++it) { |
456 |
|
nb << *it; |
457 |
|
} |
458 |
|
} |
459 |
|
|
460 |
16637 |
TrustNode ArithCongruenceManager::explainInternal(TNode internal) |
461 |
|
{ |
462 |
16637 |
if (isProofEnabled()) |
463 |
|
{ |
464 |
4348 |
return d_pfee->explain(internal); |
465 |
|
} |
466 |
|
// otherwise, explain without proof generator |
467 |
24578 |
Node exp = d_ee->mkExplainLit(internal); |
468 |
12289 |
return TrustNode::mkTrustPropExp(internal, exp, nullptr); |
469 |
|
} |
470 |
|
|
471 |
14694 |
TrustNode ArithCongruenceManager::explain(TNode external) |
472 |
|
{ |
473 |
14694 |
Trace("arith-ee") << "Ask for explanation of " << external << std::endl; |
474 |
29388 |
Node internal = externalToInternal(external); |
475 |
14694 |
Trace("arith-ee") << "...internal = " << internal << std::endl; |
476 |
29388 |
TrustNode trn = explainInternal(internal); |
477 |
14694 |
if (isProofEnabled() && trn.getProven()[1] != external) |
478 |
|
{ |
479 |
439 |
Assert(trn.getKind() == TrustNodeKind::PROP_EXP); |
480 |
439 |
Assert(trn.getProven().getKind() == Kind::IMPLIES); |
481 |
439 |
Assert(trn.getGenerator() != nullptr); |
482 |
878 |
Trace("arith-ee") << "tweaking proof to prove " << external << " not " |
483 |
439 |
<< trn.getProven()[1] << std::endl; |
484 |
878 |
std::vector<std::shared_ptr<ProofNode>> assumptionPfs; |
485 |
878 |
std::vector<Node> assumptions = andComponents(trn.getNode()); |
486 |
439 |
assumptionPfs.push_back(trn.toProofNode()); |
487 |
1641 |
for (const auto& a : assumptions) |
488 |
|
{ |
489 |
1202 |
assumptionPfs.push_back( |
490 |
2404 |
d_pnm->mkNode(PfRule::TRUE_INTRO, {d_pnm->mkAssume(a)}, {})); |
491 |
|
} |
492 |
439 |
auto litPf = d_pnm->mkNode( |
493 |
878 |
PfRule::MACRO_SR_PRED_TRANSFORM, {assumptionPfs}, {external}); |
494 |
878 |
auto extPf = d_pnm->mkScope(litPf, assumptions); |
495 |
439 |
return d_pfGenExplain->mkTrustedPropagation(external, trn.getNode(), extPf); |
496 |
|
} |
497 |
14255 |
return trn; |
498 |
|
} |
499 |
|
|
500 |
|
void ArithCongruenceManager::explain(TNode external, NodeBuilder& out) |
501 |
|
{ |
502 |
|
Node internal = externalToInternal(external); |
503 |
|
|
504 |
|
std::vector<TNode> assumptions; |
505 |
|
explain(internal, assumptions); |
506 |
|
std::set<TNode> assumptionSet; |
507 |
|
assumptionSet.insert(assumptions.begin(), assumptions.end()); |
508 |
|
|
509 |
|
enqueueIntoNB(assumptionSet, out); |
510 |
|
} |
511 |
|
|
512 |
55619 |
void ArithCongruenceManager::addWatchedPair(ArithVar s, TNode x, TNode y){ |
513 |
55619 |
Assert(!isWatchedVariable(s)); |
514 |
|
|
515 |
111238 |
Debug("arith::congruenceManager") |
516 |
55619 |
<< "addWatchedPair(" << s << ", " << x << ", " << y << ")" << std::endl; |
517 |
|
|
518 |
|
|
519 |
55619 |
++(d_statistics.d_watchedVariables); |
520 |
|
|
521 |
55619 |
d_watchedVariables.add(s); |
522 |
|
|
523 |
111238 |
Node eq = x.eqNode(y); |
524 |
55619 |
d_watchedEqualities.set(s, eq); |
525 |
55619 |
} |
526 |
|
|
527 |
1769230 |
void ArithCongruenceManager::assertLitToEqualityEngine( |
528 |
|
Node lit, TNode reason, std::shared_ptr<ProofNode> pf) |
529 |
|
{ |
530 |
1769230 |
bool isEquality = lit.getKind() != Kind::NOT; |
531 |
3538460 |
Node eq = isEquality ? lit : lit[0]; |
532 |
1769230 |
Assert(eq.getKind() == Kind::EQUAL); |
533 |
|
|
534 |
3538460 |
Trace("arith-ee") << "Assert to Eq " << lit << ", reason " << reason |
535 |
1769230 |
<< std::endl; |
536 |
1769230 |
if (isProofEnabled()) |
537 |
|
{ |
538 |
166557 |
if (CDProof::isSame(lit, reason)) |
539 |
|
{ |
540 |
78871 |
Trace("arith-pfee") << "Asserting only, b/c implied by symm" << std::endl; |
541 |
|
// The equality engine doesn't ref-count for us... |
542 |
78871 |
d_keepAlive.push_back(eq); |
543 |
78871 |
d_keepAlive.push_back(reason); |
544 |
78871 |
d_ee->assertEquality(eq, isEquality, reason); |
545 |
|
} |
546 |
87686 |
else if (hasProofFor(lit)) |
547 |
|
{ |
548 |
1189 |
Trace("arith-pfee") << "Skipping b/c already done" << std::endl; |
549 |
|
} |
550 |
|
else |
551 |
|
{ |
552 |
86497 |
setProofFor(lit, pf); |
553 |
86497 |
Trace("arith-pfee") << "Actually asserting" << std::endl; |
554 |
86497 |
if (Debug.isOn("arith-pfee")) |
555 |
|
{ |
556 |
|
Trace("arith-pfee") << "Proof: "; |
557 |
|
pf->printDebug(Trace("arith-pfee")); |
558 |
|
Trace("arith-pfee") << std::endl; |
559 |
|
} |
560 |
|
// The proof equality engine *does* ref-count for us... |
561 |
86497 |
d_pfee->assertFact(lit, reason, d_pfGenEe.get()); |
562 |
|
} |
563 |
|
} |
564 |
|
else |
565 |
|
{ |
566 |
|
// The equality engine doesn't ref-count for us... |
567 |
1602673 |
d_keepAlive.push_back(eq); |
568 |
1602673 |
d_keepAlive.push_back(reason); |
569 |
1602673 |
d_ee->assertEquality(eq, isEquality, reason); |
570 |
|
} |
571 |
1769230 |
} |
572 |
|
|
573 |
1057673 |
void ArithCongruenceManager::assertionToEqualityEngine( |
574 |
|
bool isEquality, ArithVar s, TNode reason, std::shared_ptr<ProofNode> pf) |
575 |
|
{ |
576 |
1057673 |
Assert(isWatchedVariable(s)); |
577 |
|
|
578 |
2115346 |
TNode eq = d_watchedEqualities[s]; |
579 |
1057673 |
Assert(eq.getKind() == kind::EQUAL); |
580 |
|
|
581 |
2115346 |
Node lit = isEquality ? Node(eq) : eq.notNode(); |
582 |
2115346 |
Trace("arith-ee") << "Assert to Eq " << eq << ", pol " << isEquality |
583 |
1057673 |
<< ", reason " << reason << std::endl; |
584 |
1057673 |
assertLitToEqualityEngine(lit, reason, pf); |
585 |
1057673 |
} |
586 |
|
|
587 |
174183 |
bool ArithCongruenceManager::hasProofFor(TNode f) const |
588 |
|
{ |
589 |
174183 |
Assert(isProofEnabled()); |
590 |
174183 |
if (d_pfGenEe->hasProofFor(f)) |
591 |
|
{ |
592 |
1189 |
return true; |
593 |
|
} |
594 |
345988 |
Node sym = CDProof::getSymmFact(f); |
595 |
172994 |
Assert(!sym.isNull()); |
596 |
172994 |
return d_pfGenEe->hasProofFor(sym); |
597 |
|
} |
598 |
|
|
599 |
86497 |
void ArithCongruenceManager::setProofFor(TNode f, |
600 |
|
std::shared_ptr<ProofNode> pf) const |
601 |
|
{ |
602 |
86497 |
Assert(!hasProofFor(f)); |
603 |
86497 |
d_pfGenEe->mkTrustNode(f, pf); |
604 |
172994 |
Node symF = CDProof::getSymmFact(f); |
605 |
172994 |
auto symPf = d_pnm->mkNode(PfRule::SYMM, {pf}, {}); |
606 |
86497 |
d_pfGenEe->mkTrustNode(symF, symPf); |
607 |
86497 |
} |
608 |
|
|
609 |
531183 |
void ArithCongruenceManager::equalsConstant(ConstraintCP c){ |
610 |
531183 |
Assert(c->isEquality()); |
611 |
|
|
612 |
531183 |
++(d_statistics.d_equalsConstantCalls); |
613 |
531183 |
Debug("equalsConstant") << "equals constant " << c << std::endl; |
614 |
|
|
615 |
531183 |
ArithVar x = c->getVariable(); |
616 |
1062366 |
Node xAsNode = d_avariables.asNode(x); |
617 |
1062366 |
Node asRational = mkRationalNode(c->getValue().getNoninfinitesimalPart()); |
618 |
|
|
619 |
|
// No guarentee this is in normal form! |
620 |
|
// Note though, that it happens to be in proof normal form! |
621 |
1062366 |
Node eq = xAsNode.eqNode(asRational); |
622 |
531183 |
d_keepAlive.push_back(eq); |
623 |
|
|
624 |
1062366 |
NodeBuilder nb(Kind::AND); |
625 |
1062366 |
auto pf = c->externalExplainByAssertions(nb); |
626 |
1062366 |
Node reason = safeConstructNary(nb); |
627 |
531183 |
d_keepAlive.push_back(reason); |
628 |
|
|
629 |
531183 |
Trace("arith-ee") << "Assert equalsConstant " << eq << ", reason " << reason << std::endl; |
630 |
531183 |
assertLitToEqualityEngine(eq, reason, pf); |
631 |
531183 |
} |
632 |
|
|
633 |
180374 |
void ArithCongruenceManager::equalsConstant(ConstraintCP lb, ConstraintCP ub){ |
634 |
180374 |
Assert(lb->isLowerBound()); |
635 |
180374 |
Assert(ub->isUpperBound()); |
636 |
180374 |
Assert(lb->getVariable() == ub->getVariable()); |
637 |
|
|
638 |
180374 |
++(d_statistics.d_equalsConstantCalls); |
639 |
360748 |
Debug("equalsConstant") << "equals constant " << lb << std::endl |
640 |
180374 |
<< ub << std::endl; |
641 |
|
|
642 |
180374 |
ArithVar x = lb->getVariable(); |
643 |
360748 |
NodeBuilder nb(Kind::AND); |
644 |
360748 |
auto pfLb = lb->externalExplainByAssertions(nb); |
645 |
360748 |
auto pfUb = ub->externalExplainByAssertions(nb); |
646 |
360748 |
Node reason = safeConstructNary(nb); |
647 |
|
|
648 |
360748 |
Node xAsNode = d_avariables.asNode(x); |
649 |
360748 |
Node asRational = mkRationalNode(lb->getValue().getNoninfinitesimalPart()); |
650 |
|
|
651 |
|
// No guarentee this is in normal form! |
652 |
|
// Note though, that it happens to be in proof normal form! |
653 |
360748 |
Node eq = xAsNode.eqNode(asRational); |
654 |
360748 |
std::shared_ptr<ProofNode> pf; |
655 |
180374 |
if (isProofEnabled()) |
656 |
|
{ |
657 |
17417 |
pf = d_pnm->mkNode(PfRule::ARITH_TRICHOTOMY, {pfLb, pfUb}, {eq}); |
658 |
|
} |
659 |
180374 |
d_keepAlive.push_back(eq); |
660 |
180374 |
d_keepAlive.push_back(reason); |
661 |
|
|
662 |
180374 |
Trace("arith-ee") << "Assert equalsConstant2 " << eq << ", reason " << reason << std::endl; |
663 |
|
|
664 |
180374 |
assertLitToEqualityEngine(eq, reason, pf); |
665 |
180374 |
} |
666 |
|
|
667 |
3224042 |
bool ArithCongruenceManager::isProofEnabled() const { return d_pnm != nullptr; } |
668 |
|
|
669 |
439 |
std::vector<Node> andComponents(TNode an) |
670 |
|
{ |
671 |
439 |
auto nm = NodeManager::currentNM(); |
672 |
439 |
if (an == nm->mkConst(true)) |
673 |
|
{ |
674 |
|
return {}; |
675 |
|
} |
676 |
439 |
else if (an.getKind() != Kind::AND) |
677 |
|
{ |
678 |
|
return {an}; |
679 |
|
} |
680 |
878 |
std::vector<Node> a{}; |
681 |
439 |
a.reserve(an.getNumChildren()); |
682 |
439 |
a.insert(a.end(), an.begin(), an.end()); |
683 |
439 |
return a; |
684 |
|
} |
685 |
|
|
686 |
|
} // namespace arith |
687 |
|
} // namespace theory |
688 |
28191 |
} // namespace cvc5 |