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 "options/arith_options.h" |
23 |
|
#include "proof/proof_node.h" |
24 |
|
#include "proof/proof_node_manager.h" |
25 |
|
#include "smt/smt_statistics_registry.h" |
26 |
|
#include "theory/arith/arith_utilities.h" |
27 |
|
#include "theory/arith/constraint.h" |
28 |
|
#include "theory/arith/partial_model.h" |
29 |
|
#include "theory/ee_setup_info.h" |
30 |
|
#include "theory/rewriter.h" |
31 |
|
#include "theory/uf/equality_engine.h" |
32 |
|
#include "theory/uf/proof_equality_engine.h" |
33 |
|
|
34 |
|
namespace cvc5 { |
35 |
|
namespace theory { |
36 |
|
namespace arith { |
37 |
|
|
38 |
9851 |
ArithCongruenceManager::ArithCongruenceManager( |
39 |
|
context::Context* c, |
40 |
|
context::UserContext* u, |
41 |
|
ConstraintDatabase& cd, |
42 |
|
SetupLiteralCallBack setup, |
43 |
|
const ArithVariables& avars, |
44 |
|
RaiseEqualityEngineConflict raiseConflict, |
45 |
9851 |
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 |
9851 |
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 |
9851 |
pnm, u, "ArithCongruenceManager::pfGenExplain")), |
66 |
29553 |
d_pfee(nullptr) |
67 |
|
{ |
68 |
9851 |
} |
69 |
|
|
70 |
9851 |
ArithCongruenceManager::~ArithCongruenceManager() {} |
71 |
|
|
72 |
9812 |
bool ArithCongruenceManager::needsEqualityEngine(EeSetupInfo& esi) |
73 |
|
{ |
74 |
9812 |
Assert(!options::arithEqSolver()); |
75 |
9812 |
esi.d_notify = &d_notify; |
76 |
9812 |
esi.d_name = "arithCong::ee"; |
77 |
9812 |
return true; |
78 |
|
} |
79 |
|
|
80 |
9812 |
void ArithCongruenceManager::finishInit(eq::EqualityEngine* ee) |
81 |
|
{ |
82 |
9812 |
if (options::arithEqSolver()) |
83 |
|
{ |
84 |
|
// use our own copy |
85 |
|
d_allocEe.reset( |
86 |
|
new eq::EqualityEngine(d_notify, d_satContext, "arithCong::ee", true)); |
87 |
|
d_ee = d_allocEe.get(); |
88 |
|
if (d_pnm != nullptr) |
89 |
|
{ |
90 |
|
// allocate an internal proof equality engine |
91 |
|
d_allocPfee.reset( |
92 |
|
new eq::ProofEqEngine(d_satContext, d_userContext, *d_ee, d_pnm)); |
93 |
|
d_ee->setProofEqualityEngine(d_allocPfee.get()); |
94 |
|
} |
95 |
|
} |
96 |
|
else |
97 |
|
{ |
98 |
9812 |
Assert(ee != nullptr); |
99 |
|
// otherwise, we use the official one |
100 |
9812 |
d_ee = ee; |
101 |
|
} |
102 |
|
// set the congruence kinds on the separate equality engine |
103 |
9812 |
d_ee->addFunctionKind(kind::NONLINEAR_MULT); |
104 |
9812 |
d_ee->addFunctionKind(kind::EXPONENTIAL); |
105 |
9812 |
d_ee->addFunctionKind(kind::SINE); |
106 |
9812 |
d_ee->addFunctionKind(kind::IAND); |
107 |
9812 |
d_ee->addFunctionKind(kind::POW2); |
108 |
|
// the proof equality engine is the one from the equality engine |
109 |
9812 |
d_pfee = d_ee->getProofEqualityEngine(); |
110 |
|
// have proof equality engine only if proofs are enabled |
111 |
9812 |
Assert(isProofEnabled() == (d_pfee != nullptr)); |
112 |
9812 |
} |
113 |
|
|
114 |
9851 |
ArithCongruenceManager::Statistics::Statistics() |
115 |
9851 |
: d_watchedVariables(smtStatisticsRegistry().registerInt( |
116 |
19702 |
"theory::arith::congruence::watchedVariables")), |
117 |
9851 |
d_watchedVariableIsZero(smtStatisticsRegistry().registerInt( |
118 |
19702 |
"theory::arith::congruence::watchedVariableIsZero")), |
119 |
9851 |
d_watchedVariableIsNotZero(smtStatisticsRegistry().registerInt( |
120 |
19702 |
"theory::arith::congruence::watchedVariableIsNotZero")), |
121 |
9851 |
d_equalsConstantCalls(smtStatisticsRegistry().registerInt( |
122 |
19702 |
"theory::arith::congruence::equalsConstantCalls")), |
123 |
9851 |
d_propagations(smtStatisticsRegistry().registerInt( |
124 |
19702 |
"theory::arith::congruence::propagations")), |
125 |
9851 |
d_propagateConstraints(smtStatisticsRegistry().registerInt( |
126 |
19702 |
"theory::arith::congruence::propagateConstraints")), |
127 |
9851 |
d_conflicts(smtStatisticsRegistry().registerInt( |
128 |
68957 |
"theory::arith::congruence::conflicts")) |
129 |
|
{ |
130 |
9851 |
} |
131 |
|
|
132 |
9851 |
ArithCongruenceManager::ArithCongruenceNotify::ArithCongruenceNotify(ArithCongruenceManager& acm) |
133 |
9851 |
: d_acm(acm) |
134 |
9851 |
{} |
135 |
|
|
136 |
|
bool ArithCongruenceManager::ArithCongruenceNotify::eqNotifyTriggerPredicate( |
137 |
|
TNode predicate, bool value) |
138 |
|
{ |
139 |
|
Assert(predicate.getKind() == kind::EQUAL); |
140 |
|
Debug("arith::congruences") |
141 |
|
<< "ArithCongruenceNotify::eqNotifyTriggerPredicate(" << predicate << ", " |
142 |
|
<< (value ? "true" : "false") << ")" << std::endl; |
143 |
|
if (value) { |
144 |
|
return d_acm.propagate(predicate); |
145 |
|
} |
146 |
|
return d_acm.propagate(predicate.notNode()); |
147 |
|
} |
148 |
|
|
149 |
826130 |
bool ArithCongruenceManager::ArithCongruenceNotify::eqNotifyTriggerTermEquality(TheoryId tag, TNode t1, TNode t2, bool value) { |
150 |
826130 |
Debug("arith::congruences") << "ArithCongruenceNotify::eqNotifyTriggerTermEquality(" << t1 << ", " << t2 << ", " << (value ? "true" : "false") << ")" << std::endl; |
151 |
826130 |
if (value) { |
152 |
688212 |
return d_acm.propagate(t1.eqNode(t2)); |
153 |
|
} else { |
154 |
137918 |
return d_acm.propagate(t1.eqNode(t2).notNode()); |
155 |
|
} |
156 |
|
} |
157 |
1814 |
void ArithCongruenceManager::ArithCongruenceNotify::eqNotifyConstantTermMerge(TNode t1, TNode t2) { |
158 |
1814 |
Debug("arith::congruences") << "ArithCongruenceNotify::eqNotifyConstantTermMerge(" << t1 << ", " << t2 << std::endl; |
159 |
1814 |
d_acm.propagate(t1.eqNode(t2)); |
160 |
1814 |
} |
161 |
1237902 |
void ArithCongruenceManager::ArithCongruenceNotify::eqNotifyNewClass(TNode t) { |
162 |
1237902 |
} |
163 |
1578911 |
void ArithCongruenceManager::ArithCongruenceNotify::eqNotifyMerge(TNode t1, |
164 |
|
TNode t2) |
165 |
|
{ |
166 |
1578911 |
} |
167 |
316047 |
void ArithCongruenceManager::ArithCongruenceNotify::eqNotifyDisequal(TNode t1, TNode t2, TNode reason) { |
168 |
316047 |
} |
169 |
|
|
170 |
1983 |
void ArithCongruenceManager::raiseConflict(Node conflict, |
171 |
|
std::shared_ptr<ProofNode> pf) |
172 |
|
{ |
173 |
1983 |
Assert(!inConflict()); |
174 |
1983 |
Debug("arith::conflict") << "difference manager conflict " << conflict << std::endl; |
175 |
1983 |
d_inConflict.raise(); |
176 |
1983 |
d_raiseConflict.raiseEEConflict(conflict, pf); |
177 |
1983 |
} |
178 |
829927 |
bool ArithCongruenceManager::inConflict() const{ |
179 |
829927 |
return d_inConflict.isRaised(); |
180 |
|
} |
181 |
|
|
182 |
3859900 |
bool ArithCongruenceManager::hasMorePropagations() const { |
183 |
3859900 |
return !d_propagatations.empty(); |
184 |
|
} |
185 |
663486 |
const Node ArithCongruenceManager::getNextPropagation() { |
186 |
663486 |
Assert(hasMorePropagations()); |
187 |
663486 |
Node prop = d_propagatations.front(); |
188 |
663486 |
d_propagatations.dequeue(); |
189 |
663486 |
return prop; |
190 |
|
} |
191 |
|
|
192 |
25533 |
bool ArithCongruenceManager::canExplain(TNode n) const { |
193 |
25533 |
return d_explanationMap.find(n) != d_explanationMap.end(); |
194 |
|
} |
195 |
|
|
196 |
15825 |
Node ArithCongruenceManager::externalToInternal(TNode n) const{ |
197 |
15825 |
Assert(canExplain(n)); |
198 |
15825 |
ExplainMap::const_iterator iter = d_explanationMap.find(n); |
199 |
15825 |
size_t pos = (*iter).second; |
200 |
15825 |
return d_propagatations[pos]; |
201 |
|
} |
202 |
|
|
203 |
588099 |
void ArithCongruenceManager::pushBack(TNode n){ |
204 |
588099 |
d_explanationMap.insert(n, d_propagatations.size()); |
205 |
588099 |
d_propagatations.enqueue(n); |
206 |
|
|
207 |
588099 |
++(d_statistics.d_propagations); |
208 |
588099 |
} |
209 |
112722 |
void ArithCongruenceManager::pushBack(TNode n, TNode r){ |
210 |
112722 |
d_explanationMap.insert(r, d_propagatations.size()); |
211 |
112722 |
d_explanationMap.insert(n, d_propagatations.size()); |
212 |
112722 |
d_propagatations.enqueue(n); |
213 |
|
|
214 |
112722 |
++(d_statistics.d_propagations); |
215 |
112722 |
} |
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); |
221 |
|
|
222 |
|
++(d_statistics.d_propagations); |
223 |
|
} |
224 |
|
|
225 |
308181 |
void ArithCongruenceManager::watchedVariableIsZero(ConstraintCP lb, ConstraintCP ub){ |
226 |
308181 |
Assert(lb->isLowerBound()); |
227 |
308181 |
Assert(ub->isUpperBound()); |
228 |
308181 |
Assert(lb->getVariable() == ub->getVariable()); |
229 |
308181 |
Assert(lb->getValue().sgn() == 0); |
230 |
308181 |
Assert(ub->getValue().sgn() == 0); |
231 |
|
|
232 |
308181 |
++(d_statistics.d_watchedVariableIsZero); |
233 |
|
|
234 |
308181 |
ArithVar s = lb->getVariable(); |
235 |
616362 |
TNode eq = d_watchedEqualities[s]; |
236 |
308181 |
ConstraintCP eqC = d_constraintDatabase.getConstraint( |
237 |
308181 |
s, ConstraintType::Equality, lb->getValue()); |
238 |
616362 |
NodeBuilder reasonBuilder(Kind::AND); |
239 |
616362 |
auto pfLb = lb->externalExplainByAssertions(reasonBuilder); |
240 |
616362 |
auto pfUb = ub->externalExplainByAssertions(reasonBuilder); |
241 |
616362 |
Node reason = safeConstructNary(reasonBuilder); |
242 |
616362 |
std::shared_ptr<ProofNode> pf{}; |
243 |
308181 |
if (isProofEnabled()) |
244 |
|
{ |
245 |
87436 |
pf = d_pnm->mkNode( |
246 |
65577 |
PfRule::ARITH_TRICHOTOMY, {pfLb, pfUb}, {eqC->getProofLiteral()}); |
247 |
21859 |
pf = d_pnm->mkNode(PfRule::MACRO_SR_PRED_TRANSFORM, {pf}, {eq}); |
248 |
|
} |
249 |
|
|
250 |
308181 |
d_keepAlive.push_back(reason); |
251 |
616362 |
Trace("arith-ee") << "Asserting an equality on " << s << ", on trichotomy" |
252 |
308181 |
<< std::endl; |
253 |
308181 |
Trace("arith-ee") << " based on " << lb << std::endl; |
254 |
308181 |
Trace("arith-ee") << " based on " << ub << std::endl; |
255 |
308181 |
assertionToEqualityEngine(true, s, reason, pf); |
256 |
308181 |
} |
257 |
|
|
258 |
223280 |
void ArithCongruenceManager::watchedVariableIsZero(ConstraintCP eq){ |
259 |
223280 |
Debug("arith::cong") << "Cong::watchedVariableIsZero: " << *eq << std::endl; |
260 |
|
|
261 |
223280 |
Assert(eq->isEquality()); |
262 |
223280 |
Assert(eq->getValue().sgn() == 0); |
263 |
|
|
264 |
223280 |
++(d_statistics.d_watchedVariableIsZero); |
265 |
|
|
266 |
223280 |
ArithVar s = eq->getVariable(); |
267 |
|
|
268 |
|
//Explain for conflict is correct as these proofs are generated |
269 |
|
//and stored eagerly |
270 |
|
//These will be safe for propagation later as well |
271 |
446560 |
NodeBuilder nb(Kind::AND); |
272 |
|
// An open proof of eq from literals now in reason. |
273 |
223280 |
if (Debug.isOn("arith::cong")) |
274 |
|
{ |
275 |
|
eq->printProofTree(Debug("arith::cong")); |
276 |
|
} |
277 |
446560 |
auto pf = eq->externalExplainByAssertions(nb); |
278 |
223280 |
if (isProofEnabled()) |
279 |
|
{ |
280 |
78000 |
pf = d_pnm->mkNode( |
281 |
52000 |
PfRule::MACRO_SR_PRED_TRANSFORM, {pf}, {d_watchedEqualities[s]}); |
282 |
|
} |
283 |
446560 |
Node reason = safeConstructNary(nb); |
284 |
|
|
285 |
223280 |
d_keepAlive.push_back(reason); |
286 |
223280 |
assertionToEqualityEngine(true, s, reason, pf); |
287 |
223280 |
} |
288 |
|
|
289 |
679005 |
void ArithCongruenceManager::watchedVariableCannotBeZero(ConstraintCP c){ |
290 |
1358010 |
Debug("arith::cong::notzero") |
291 |
679005 |
<< "Cong::watchedVariableCannotBeZero " << *c << std::endl; |
292 |
679005 |
++(d_statistics.d_watchedVariableIsNotZero); |
293 |
|
|
294 |
679005 |
ArithVar s = c->getVariable(); |
295 |
1358010 |
Node disEq = d_watchedEqualities[s].negate(); |
296 |
|
|
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 |
1358010 |
NodeBuilder nb(Kind::AND); |
300 |
|
// An open proof of eq from literals now in reason. |
301 |
1358010 |
auto pf = c->externalExplainByAssertions(nb); |
302 |
679005 |
if (Debug.isOn("arith::cong::notzero")) |
303 |
|
{ |
304 |
|
Debug("arith::cong::notzero") << " original proof "; |
305 |
|
pf->printDebug(Debug("arith::cong::notzero")); |
306 |
|
Debug("arith::cong::notzero") << std::endl; |
307 |
|
} |
308 |
1358010 |
Node reason = safeConstructNary(nb); |
309 |
679005 |
if (isProofEnabled()) |
310 |
|
{ |
311 |
54878 |
if (c->getType() == ConstraintType::Disequality) |
312 |
|
{ |
313 |
17024 |
Assert(c->getLiteral() == d_watchedEqualities[s].negate()); |
314 |
|
// We have to prove equivalence to the watched disequality. |
315 |
17024 |
pf = d_pnm->mkNode(PfRule::MACRO_SR_PRED_TRANSFORM, {pf}, {disEq}); |
316 |
|
} |
317 |
|
else |
318 |
|
{ |
319 |
75708 |
Debug("arith::cong::notzero") |
320 |
37854 |
<< " proof modification needed" << std::endl; |
321 |
|
|
322 |
|
// Four cases: |
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 |
37854 |
const bool scaleCNegatively = c->getType() == ConstraintType::LowerBound |
328 |
53808 |
|| (c->getType() == ConstraintType::Equality |
329 |
41939 |
&& c->getValue().sgn() > 0); |
330 |
37854 |
const int cSign = scaleCNegatively ? -1 : 1; |
331 |
75708 |
TNode isZero = d_watchedEqualities[s]; |
332 |
75708 |
const auto isZeroPf = d_pnm->mkAssume(isZero); |
333 |
37854 |
const auto nm = NodeManager::currentNM(); |
334 |
37854 |
const auto sumPf = d_pnm->mkNode( |
335 |
|
PfRule::MACRO_ARITH_SCALE_SUM_UB, |
336 |
|
{isZeroPf, pf}, |
337 |
|
// Trick for getting correct, opposing signs. |
338 |
75708 |
{nm->mkConst(Rational(-1 * cSign)), nm->mkConst(Rational(cSign))}); |
339 |
37854 |
const auto botPf = d_pnm->mkNode( |
340 |
75708 |
PfRule::MACRO_SR_PRED_TRANSFORM, {sumPf}, {nm->mkConst(false)}); |
341 |
75708 |
std::vector<Node> assumption = {isZero}; |
342 |
37854 |
pf = d_pnm->mkScope(botPf, assumption, false); |
343 |
37854 |
Debug("arith::cong::notzero") << " new proof "; |
344 |
37854 |
pf->printDebug(Debug("arith::cong::notzero")); |
345 |
37854 |
Debug("arith::cong::notzero") << std::endl; |
346 |
|
} |
347 |
54878 |
Assert(pf->getResult() == disEq); |
348 |
|
} |
349 |
679005 |
d_keepAlive.push_back(reason); |
350 |
679005 |
assertionToEqualityEngine(false, s, reason, pf); |
351 |
679005 |
} |
352 |
|
|
353 |
|
|
354 |
827944 |
bool ArithCongruenceManager::propagate(TNode x){ |
355 |
827944 |
Debug("arith::congruenceManager")<< "ArithCongruenceManager::propagate("<<x<<")"<<std::endl; |
356 |
827944 |
if(inConflict()){ |
357 |
|
return true; |
358 |
|
} |
359 |
|
|
360 |
1655888 |
Node rewritten = Rewriter::rewrite(x); |
361 |
|
|
362 |
|
//Need to still propagate this! |
363 |
827944 |
if(rewritten.getKind() == kind::CONST_BOOLEAN){ |
364 |
1961 |
pushBack(x); |
365 |
|
|
366 |
1961 |
if(rewritten.getConst<bool>()){ |
367 |
147 |
return true; |
368 |
|
}else{ |
369 |
|
// x rewrites to false. |
370 |
1814 |
++(d_statistics.d_conflicts); |
371 |
3628 |
TrustNode trn = explainInternal(x); |
372 |
3628 |
Node conf = flattenAnd(trn.getNode()); |
373 |
1814 |
Debug("arith::congruenceManager") << "rewritten to false "<<x<<" with explanation "<< conf << std::endl; |
374 |
1814 |
if (isProofEnabled()) |
375 |
|
{ |
376 |
758 |
auto pf = trn.getGenerator()->getProofFor(trn.getProven()); |
377 |
379 |
auto confPf = d_pnm->mkNode( |
378 |
758 |
PfRule::MACRO_SR_PRED_TRANSFORM, {pf}, {conf.negate()}); |
379 |
379 |
raiseConflict(conf, confPf); |
380 |
|
} |
381 |
|
else |
382 |
|
{ |
383 |
1435 |
raiseConflict(conf); |
384 |
|
} |
385 |
1814 |
return false; |
386 |
|
} |
387 |
|
} |
388 |
|
|
389 |
825983 |
Assert(rewritten.getKind() != kind::CONST_BOOLEAN); |
390 |
|
|
391 |
825983 |
ConstraintP c = d_constraintDatabase.lookup(rewritten); |
392 |
825983 |
if(c == NullConstraint){ |
393 |
|
//using setup as there may not be a corresponding congruence literal yet |
394 |
19500 |
d_setupLiteral(rewritten); |
395 |
19500 |
c = d_constraintDatabase.lookup(rewritten); |
396 |
19500 |
Assert(c != NullConstraint); |
397 |
|
} |
398 |
|
|
399 |
1651966 |
Debug("arith::congruenceManager")<< "x is " |
400 |
1651966 |
<< c->hasProof() << " " |
401 |
1651966 |
<< (x == rewritten) << " " |
402 |
1651966 |
<< c->canBePropagated() << " " |
403 |
825983 |
<< c->negationHasProof() << std::endl; |
404 |
|
|
405 |
825983 |
if(c->negationHasProof()){ |
406 |
338 |
TrustNode texpC = explainInternal(x); |
407 |
338 |
Node expC = texpC.getNode(); |
408 |
169 |
ConstraintCP negC = c->getNegation(); |
409 |
338 |
Node neg = Constraint::externalExplainByAssertions({negC}); |
410 |
338 |
Node conf = expC.andNode(neg); |
411 |
338 |
Node final = flattenAnd(conf); |
412 |
|
|
413 |
169 |
++(d_statistics.d_conflicts); |
414 |
169 |
raiseConflict(final); |
415 |
169 |
Debug("arith::congruenceManager") << "congruenceManager found a conflict " << final << std::endl; |
416 |
169 |
return false; |
417 |
|
} |
418 |
|
|
419 |
|
// Cases for propagation |
420 |
|
// C : c has a proof |
421 |
|
// S : x == rewritten |
422 |
|
// P : c can be propagated |
423 |
|
// |
424 |
|
// CSP |
425 |
|
// 000 : propagate x, and mark C it as being explained |
426 |
|
// 001 : propagate x, and propagate c after marking it as being explained |
427 |
|
// 01* : propagate x, mark c but do not propagate c |
428 |
|
// 10* : propagate x, do not mark c and do not propagate c |
429 |
|
// 11* : drop the constraint, do not propagate x or c |
430 |
|
|
431 |
825814 |
if(!c->hasProof() && x != rewritten){ |
432 |
112722 |
if(c->assertedToTheTheory()){ |
433 |
|
pushBack(x, rewritten, c->getWitness()); |
434 |
|
}else{ |
435 |
112722 |
pushBack(x, rewritten); |
436 |
|
} |
437 |
|
|
438 |
112722 |
c->setEqualityEngineProof(); |
439 |
112722 |
if(c->canBePropagated() && !c->assertedToTheTheory()){ |
440 |
|
|
441 |
18171 |
++(d_statistics.d_propagateConstraints); |
442 |
18171 |
c->propagate(); |
443 |
|
} |
444 |
713092 |
}else if(!c->hasProof() && x == rewritten){ |
445 |
87894 |
if(c->assertedToTheTheory()){ |
446 |
|
pushBack(x, c->getWitness()); |
447 |
|
}else{ |
448 |
87894 |
pushBack(x); |
449 |
|
} |
450 |
87894 |
c->setEqualityEngineProof(); |
451 |
625198 |
}else if(c->hasProof() && x != rewritten){ |
452 |
498244 |
if(c->assertedToTheTheory()){ |
453 |
496524 |
pushBack(x); |
454 |
|
}else{ |
455 |
1720 |
pushBack(x); |
456 |
|
} |
457 |
|
}else{ |
458 |
126954 |
Assert(c->hasProof() && x == rewritten); |
459 |
|
} |
460 |
825814 |
return true; |
461 |
|
} |
462 |
|
|
463 |
|
void ArithCongruenceManager::explain(TNode literal, std::vector<TNode>& assumptions) { |
464 |
|
if (literal.getKind() != kind::NOT) { |
465 |
|
d_ee->explainEquality(literal[0], literal[1], true, assumptions); |
466 |
|
} else { |
467 |
|
d_ee->explainEquality(literal[0][0], literal[0][1], false, assumptions); |
468 |
|
} |
469 |
|
} |
470 |
|
|
471 |
|
void ArithCongruenceManager::enqueueIntoNB(const std::set<TNode> s, |
472 |
|
NodeBuilder& nb) |
473 |
|
{ |
474 |
|
std::set<TNode>::const_iterator it = s.begin(); |
475 |
|
std::set<TNode>::const_iterator it_end = s.end(); |
476 |
|
for(; it != it_end; ++it) { |
477 |
|
nb << *it; |
478 |
|
} |
479 |
|
} |
480 |
|
|
481 |
17808 |
TrustNode ArithCongruenceManager::explainInternal(TNode internal) |
482 |
|
{ |
483 |
17808 |
if (isProofEnabled()) |
484 |
|
{ |
485 |
4291 |
return d_pfee->explain(internal); |
486 |
|
} |
487 |
|
// otherwise, explain without proof generator |
488 |
27034 |
Node exp = d_ee->mkExplainLit(internal); |
489 |
13517 |
return TrustNode::mkTrustPropExp(internal, exp, nullptr); |
490 |
|
} |
491 |
|
|
492 |
15825 |
TrustNode ArithCongruenceManager::explain(TNode external) |
493 |
|
{ |
494 |
15825 |
Trace("arith-ee") << "Ask for explanation of " << external << std::endl; |
495 |
31650 |
Node internal = externalToInternal(external); |
496 |
15825 |
Trace("arith-ee") << "...internal = " << internal << std::endl; |
497 |
31650 |
TrustNode trn = explainInternal(internal); |
498 |
15825 |
if (isProofEnabled() && trn.getProven()[1] != external) |
499 |
|
{ |
500 |
557 |
Assert(trn.getKind() == TrustNodeKind::PROP_EXP); |
501 |
557 |
Assert(trn.getProven().getKind() == Kind::IMPLIES); |
502 |
557 |
Assert(trn.getGenerator() != nullptr); |
503 |
1114 |
Trace("arith-ee") << "tweaking proof to prove " << external << " not " |
504 |
557 |
<< trn.getProven()[1] << std::endl; |
505 |
1114 |
std::vector<std::shared_ptr<ProofNode>> assumptionPfs; |
506 |
1114 |
std::vector<Node> assumptions = andComponents(trn.getNode()); |
507 |
557 |
assumptionPfs.push_back(trn.toProofNode()); |
508 |
2151 |
for (const auto& a : assumptions) |
509 |
|
{ |
510 |
1594 |
assumptionPfs.push_back( |
511 |
3188 |
d_pnm->mkNode(PfRule::TRUE_INTRO, {d_pnm->mkAssume(a)}, {})); |
512 |
|
} |
513 |
557 |
auto litPf = d_pnm->mkNode( |
514 |
1114 |
PfRule::MACRO_SR_PRED_TRANSFORM, {assumptionPfs}, {external}); |
515 |
1114 |
auto extPf = d_pnm->mkScope(litPf, assumptions); |
516 |
557 |
return d_pfGenExplain->mkTrustedPropagation(external, trn.getNode(), extPf); |
517 |
|
} |
518 |
15268 |
return trn; |
519 |
|
} |
520 |
|
|
521 |
|
void ArithCongruenceManager::explain(TNode external, NodeBuilder& out) |
522 |
|
{ |
523 |
|
Node internal = externalToInternal(external); |
524 |
|
|
525 |
|
std::vector<TNode> assumptions; |
526 |
|
explain(internal, assumptions); |
527 |
|
std::set<TNode> assumptionSet; |
528 |
|
assumptionSet.insert(assumptions.begin(), assumptions.end()); |
529 |
|
|
530 |
|
enqueueIntoNB(assumptionSet, out); |
531 |
|
} |
532 |
|
|
533 |
61988 |
void ArithCongruenceManager::addWatchedPair(ArithVar s, TNode x, TNode y){ |
534 |
61988 |
Assert(!isWatchedVariable(s)); |
535 |
|
|
536 |
123976 |
Debug("arith::congruenceManager") |
537 |
61988 |
<< "addWatchedPair(" << s << ", " << x << ", " << y << ")" << std::endl; |
538 |
|
|
539 |
|
|
540 |
61988 |
++(d_statistics.d_watchedVariables); |
541 |
|
|
542 |
61988 |
d_watchedVariables.add(s); |
543 |
|
|
544 |
123976 |
Node eq = x.eqNode(y); |
545 |
61988 |
d_watchedEqualities.set(s, eq); |
546 |
61988 |
} |
547 |
|
|
548 |
2105165 |
void ArithCongruenceManager::assertLitToEqualityEngine( |
549 |
|
Node lit, TNode reason, std::shared_ptr<ProofNode> pf) |
550 |
|
{ |
551 |
2105165 |
bool isEquality = lit.getKind() != Kind::NOT; |
552 |
4210330 |
Node eq = isEquality ? lit : lit[0]; |
553 |
2105165 |
Assert(eq.getKind() == Kind::EQUAL); |
554 |
|
|
555 |
4210330 |
Trace("arith-ee") << "Assert to Eq " << lit << ", reason " << reason |
556 |
2105165 |
<< std::endl; |
557 |
2105165 |
if (isProofEnabled()) |
558 |
|
{ |
559 |
213010 |
if (CDProof::isSame(lit, reason)) |
560 |
|
{ |
561 |
118666 |
Trace("arith-pfee") << "Asserting only, b/c implied by symm" << std::endl; |
562 |
|
// The equality engine doesn't ref-count for us... |
563 |
118666 |
d_keepAlive.push_back(eq); |
564 |
118666 |
d_keepAlive.push_back(reason); |
565 |
118666 |
d_ee->assertEquality(eq, isEquality, reason); |
566 |
|
} |
567 |
94344 |
else if (hasProofFor(lit)) |
568 |
|
{ |
569 |
1260 |
Trace("arith-pfee") << "Skipping b/c already done" << std::endl; |
570 |
|
} |
571 |
|
else |
572 |
|
{ |
573 |
93084 |
setProofFor(lit, pf); |
574 |
93084 |
Trace("arith-pfee") << "Actually asserting" << std::endl; |
575 |
93084 |
if (Debug.isOn("arith-pfee")) |
576 |
|
{ |
577 |
|
Trace("arith-pfee") << "Proof: "; |
578 |
|
pf->printDebug(Trace("arith-pfee")); |
579 |
|
Trace("arith-pfee") << std::endl; |
580 |
|
} |
581 |
|
// The proof equality engine *does* ref-count for us... |
582 |
93084 |
d_pfee->assertFact(lit, reason, d_pfGenEe.get()); |
583 |
|
} |
584 |
|
} |
585 |
|
else |
586 |
|
{ |
587 |
|
// The equality engine doesn't ref-count for us... |
588 |
1892155 |
d_keepAlive.push_back(eq); |
589 |
1892155 |
d_keepAlive.push_back(reason); |
590 |
1892155 |
d_ee->assertEquality(eq, isEquality, reason); |
591 |
|
} |
592 |
2105165 |
} |
593 |
|
|
594 |
1210466 |
void ArithCongruenceManager::assertionToEqualityEngine( |
595 |
|
bool isEquality, ArithVar s, TNode reason, std::shared_ptr<ProofNode> pf) |
596 |
|
{ |
597 |
1210466 |
Assert(isWatchedVariable(s)); |
598 |
|
|
599 |
2420932 |
TNode eq = d_watchedEqualities[s]; |
600 |
1210466 |
Assert(eq.getKind() == kind::EQUAL); |
601 |
|
|
602 |
2420932 |
Node lit = isEquality ? Node(eq) : eq.notNode(); |
603 |
2420932 |
Trace("arith-ee") << "Assert to Eq " << eq << ", pol " << isEquality |
604 |
1210466 |
<< ", reason " << reason << std::endl; |
605 |
1210466 |
assertLitToEqualityEngine(lit, reason, pf); |
606 |
1210466 |
} |
607 |
|
|
608 |
187428 |
bool ArithCongruenceManager::hasProofFor(TNode f) const |
609 |
|
{ |
610 |
187428 |
Assert(isProofEnabled()); |
611 |
187428 |
if (d_pfGenEe->hasProofFor(f)) |
612 |
|
{ |
613 |
1260 |
return true; |
614 |
|
} |
615 |
372336 |
Node sym = CDProof::getSymmFact(f); |
616 |
186168 |
Assert(!sym.isNull()); |
617 |
186168 |
return d_pfGenEe->hasProofFor(sym); |
618 |
|
} |
619 |
|
|
620 |
93084 |
void ArithCongruenceManager::setProofFor(TNode f, |
621 |
|
std::shared_ptr<ProofNode> pf) const |
622 |
|
{ |
623 |
93084 |
Assert(!hasProofFor(f)); |
624 |
93084 |
d_pfGenEe->mkTrustNode(f, pf); |
625 |
186168 |
Node symF = CDProof::getSymmFact(f); |
626 |
186168 |
auto symPf = d_pnm->mkNode(PfRule::SYMM, {pf}, {}); |
627 |
93084 |
d_pfGenEe->mkTrustNode(symF, symPf); |
628 |
93084 |
} |
629 |
|
|
630 |
708287 |
void ArithCongruenceManager::equalsConstant(ConstraintCP c){ |
631 |
708287 |
Assert(c->isEquality()); |
632 |
|
|
633 |
708287 |
++(d_statistics.d_equalsConstantCalls); |
634 |
708287 |
Debug("equalsConstant") << "equals constant " << c << std::endl; |
635 |
|
|
636 |
708287 |
ArithVar x = c->getVariable(); |
637 |
1416574 |
Node xAsNode = d_avariables.asNode(x); |
638 |
1416574 |
Node asRational = mkRationalNode(c->getValue().getNoninfinitesimalPart()); |
639 |
|
|
640 |
|
// No guarentee this is in normal form! |
641 |
|
// Note though, that it happens to be in proof normal form! |
642 |
1416574 |
Node eq = xAsNode.eqNode(asRational); |
643 |
708287 |
d_keepAlive.push_back(eq); |
644 |
|
|
645 |
1416574 |
NodeBuilder nb(Kind::AND); |
646 |
1416574 |
auto pf = c->externalExplainByAssertions(nb); |
647 |
1416574 |
Node reason = safeConstructNary(nb); |
648 |
708287 |
d_keepAlive.push_back(reason); |
649 |
|
|
650 |
708287 |
Trace("arith-ee") << "Assert equalsConstant " << eq << ", reason " << reason << std::endl; |
651 |
708287 |
assertLitToEqualityEngine(eq, reason, pf); |
652 |
708287 |
} |
653 |
|
|
654 |
186412 |
void ArithCongruenceManager::equalsConstant(ConstraintCP lb, ConstraintCP ub){ |
655 |
186412 |
Assert(lb->isLowerBound()); |
656 |
186412 |
Assert(ub->isUpperBound()); |
657 |
186412 |
Assert(lb->getVariable() == ub->getVariable()); |
658 |
|
|
659 |
186412 |
++(d_statistics.d_equalsConstantCalls); |
660 |
372824 |
Debug("equalsConstant") << "equals constant " << lb << std::endl |
661 |
186412 |
<< ub << std::endl; |
662 |
|
|
663 |
186412 |
ArithVar x = lb->getVariable(); |
664 |
372824 |
NodeBuilder nb(Kind::AND); |
665 |
372824 |
auto pfLb = lb->externalExplainByAssertions(nb); |
666 |
372824 |
auto pfUb = ub->externalExplainByAssertions(nb); |
667 |
372824 |
Node reason = safeConstructNary(nb); |
668 |
|
|
669 |
372824 |
Node xAsNode = d_avariables.asNode(x); |
670 |
372824 |
Node asRational = mkRationalNode(lb->getValue().getNoninfinitesimalPart()); |
671 |
|
|
672 |
|
// No guarentee this is in normal form! |
673 |
|
// Note though, that it happens to be in proof normal form! |
674 |
372824 |
Node eq = xAsNode.eqNode(asRational); |
675 |
372824 |
std::shared_ptr<ProofNode> pf; |
676 |
186412 |
if (isProofEnabled()) |
677 |
|
{ |
678 |
17826 |
pf = d_pnm->mkNode(PfRule::ARITH_TRICHOTOMY, {pfLb, pfUb}, {eq}); |
679 |
|
} |
680 |
186412 |
d_keepAlive.push_back(eq); |
681 |
186412 |
d_keepAlive.push_back(reason); |
682 |
|
|
683 |
186412 |
Trace("arith-ee") << "Assert equalsConstant2 " << eq << ", reason " << reason << std::endl; |
684 |
|
|
685 |
186412 |
assertLitToEqualityEngine(eq, reason, pf); |
686 |
186412 |
} |
687 |
|
|
688 |
3734730 |
bool ArithCongruenceManager::isProofEnabled() const { return d_pnm != nullptr; } |
689 |
|
|
690 |
557 |
std::vector<Node> andComponents(TNode an) |
691 |
|
{ |
692 |
557 |
auto nm = NodeManager::currentNM(); |
693 |
557 |
if (an == nm->mkConst(true)) |
694 |
|
{ |
695 |
|
return {}; |
696 |
|
} |
697 |
557 |
else if (an.getKind() != Kind::AND) |
698 |
|
{ |
699 |
|
return {an}; |
700 |
|
} |
701 |
1114 |
std::vector<Node> a{}; |
702 |
557 |
a.reserve(an.getNumChildren()); |
703 |
557 |
a.insert(a.end(), an.begin(), an.end()); |
704 |
557 |
return a; |
705 |
|
} |
706 |
|
|
707 |
|
} // namespace arith |
708 |
|
} // namespace theory |
709 |
29337 |
} // namespace cvc5 |