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