1 |
|
/****************************************************************************** |
2 |
|
* Top contributors (to current version): |
3 |
|
* 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 |
|
* Implementation of the justification SAT decision strategy |
14 |
|
*/ |
15 |
|
|
16 |
|
#include "decision/justification_strategy.h" |
17 |
|
|
18 |
|
#include "prop/skolem_def_manager.h" |
19 |
|
|
20 |
|
using namespace cvc5::kind; |
21 |
|
using namespace cvc5::prop; |
22 |
|
|
23 |
|
namespace cvc5 { |
24 |
|
namespace decision { |
25 |
|
|
26 |
12754 |
JustificationStrategy::JustificationStrategy(Env& env) |
27 |
|
: DecisionEngine(env), |
28 |
|
d_assertions( |
29 |
12754 |
userContext(), |
30 |
|
context(), |
31 |
12754 |
options() |
32 |
12754 |
.decision.jhRlvOrder), // assertions are user-context dependent |
33 |
|
d_skolemAssertions( |
34 |
|
context(), context()), // skolem assertions are SAT-context dependent |
35 |
|
d_justified(context()), |
36 |
|
d_stack(context()), |
37 |
|
d_lastDecisionLit(context()), |
38 |
|
d_currStatusDec(false), |
39 |
12754 |
d_useRlvOrder(options().decision.jhRlvOrder), |
40 |
12754 |
d_decisionStopOnly(options().decision.decisionMode |
41 |
12754 |
== options::DecisionMode::STOPONLY), |
42 |
12754 |
d_jhSkMode(options().decision.jhSkolemMode), |
43 |
76524 |
d_jhSkRlvMode(options().decision.jhSkolemRlvMode) |
44 |
|
{ |
45 |
12754 |
} |
46 |
|
|
47 |
14708 |
void JustificationStrategy::presolve() |
48 |
|
{ |
49 |
14708 |
d_lastDecisionLit = Node::null(); |
50 |
14708 |
d_currUnderStatus = Node::null(); |
51 |
14708 |
d_currStatusDec = false; |
52 |
|
// reset the dynamic assertion list data |
53 |
14708 |
d_assertions.presolve(); |
54 |
14708 |
d_skolemAssertions.presolve(); |
55 |
|
// clear the stack |
56 |
14708 |
d_stack.clear(); |
57 |
14708 |
} |
58 |
|
|
59 |
1399205 |
SatLiteral JustificationStrategy::getNextInternal(bool& stopSearch) |
60 |
|
{ |
61 |
|
// ensure we have an assertion |
62 |
1399205 |
if (!refreshCurrentAssertion()) |
63 |
|
{ |
64 |
3787 |
Trace("jh-process") << "getNext, already finished" << std::endl; |
65 |
3787 |
stopSearch = true; |
66 |
3787 |
return undefSatLiteral; |
67 |
|
} |
68 |
1395418 |
Assert(d_stack.hasCurrentAssertion()); |
69 |
|
// temporary information in the loop below |
70 |
|
JustifyInfo* ji; |
71 |
2790836 |
JustifyNode next; |
72 |
|
// we start with the value implied by the last decision, if it exists |
73 |
1395418 |
SatValue lastChildVal = SAT_VALUE_UNKNOWN; |
74 |
1395418 |
Trace("jh-process") << "getNext" << std::endl; |
75 |
|
// If we had just sent a decision, then we lookup its value here. This may |
76 |
|
// correspond to a context where the decision was carried out, or |
77 |
|
// alternatively it may correspond to a case where we have backtracked and |
78 |
|
// propagated that literal with opposite polarity. Thus, we do not assume we |
79 |
|
// know the value of d_lastDecisionLit and look it up again here. The value |
80 |
|
// of lastChildVal will be used to update the justify info in the current |
81 |
|
// stack below. |
82 |
1395418 |
if (!d_lastDecisionLit.get().isNull()) |
83 |
|
{ |
84 |
2713600 |
Trace("jh-process") << "last decision = " << d_lastDecisionLit.get() |
85 |
1356800 |
<< std::endl; |
86 |
1356800 |
lastChildVal = lookupValue(d_lastDecisionLit.get()); |
87 |
1356800 |
if (lastChildVal == SAT_VALUE_UNKNOWN) |
88 |
|
{ |
89 |
|
// if the value is now unknown, we must reprocess the assertion, since |
90 |
|
// we have backtracked |
91 |
211326 |
TNode curr = d_stack.getCurrentAssertion(); |
92 |
105663 |
d_stack.clear(); |
93 |
105663 |
d_stack.reset(curr); |
94 |
|
} |
95 |
|
} |
96 |
1395418 |
d_lastDecisionLit = TNode::null(); |
97 |
|
// while we are trying to satisfy assertions |
98 |
17336905 |
do |
99 |
|
{ |
100 |
18732323 |
Assert(d_stack.getCurrent() != nullptr); |
101 |
|
// We get the next justify node, if it can be found. |
102 |
12785286 |
do |
103 |
|
{ |
104 |
|
// get the current justify info, which should be ready |
105 |
31517609 |
ji = d_stack.getCurrent(); |
106 |
31517609 |
if (ji == nullptr) |
107 |
|
{ |
108 |
9422936 |
break; |
109 |
|
} |
110 |
|
// get the next child to process from the current justification info |
111 |
|
// based on the fact that its last child processed had value lastChildVal. |
112 |
22094673 |
next = getNextJustifyNode(ji, lastChildVal); |
113 |
|
// if the current node is finished, we pop the stack |
114 |
22094673 |
if (next.first.isNull()) |
115 |
|
{ |
116 |
12785286 |
d_stack.popStack(); |
117 |
|
} |
118 |
22094673 |
} while (next.first.isNull()); |
119 |
|
|
120 |
18732323 |
if (ji == nullptr) |
121 |
|
{ |
122 |
|
// the assertion we just processed should have value true |
123 |
9422936 |
Assert(lastChildVal == SAT_VALUE_TRUE); |
124 |
9422936 |
if (!d_currUnderStatus.isNull()) |
125 |
|
{ |
126 |
|
// notify status if we are watching it |
127 |
|
DecisionStatus ds; |
128 |
8814263 |
if (d_currStatusDec) |
129 |
|
{ |
130 |
871634 |
ds = DecisionStatus::DECISION; |
131 |
871634 |
++(d_stats.d_numStatusDecision); |
132 |
|
} |
133 |
|
else |
134 |
|
{ |
135 |
7942629 |
ds = DecisionStatus::NO_DECISION; |
136 |
7942629 |
++(d_stats.d_numStatusNoDecision); |
137 |
|
} |
138 |
8814263 |
d_assertions.notifyStatus(d_currUnderStatus, ds); |
139 |
|
} |
140 |
|
// we did not find a next node for current, refresh current assertion |
141 |
9422936 |
d_stack.clear(); |
142 |
9422936 |
refreshCurrentAssertion(); |
143 |
9422936 |
lastChildVal = SAT_VALUE_UNKNOWN; |
144 |
18845872 |
Trace("jh-process") << "...exhausted assertion, now " |
145 |
9422936 |
<< d_stack.getCurrentAssertion() << std::endl; |
146 |
|
} |
147 |
|
else |
148 |
|
{ |
149 |
|
// must have requested a next child to justify |
150 |
9309387 |
Assert(!next.first.isNull()); |
151 |
9309387 |
Assert(next.second != SAT_VALUE_UNKNOWN); |
152 |
|
// Look up whether the next child already has a value |
153 |
9309387 |
lastChildVal = lookupValue(next.first); |
154 |
9309387 |
if (lastChildVal == SAT_VALUE_UNKNOWN) |
155 |
|
{ |
156 |
4864859 |
bool nextPol = next.first.getKind() != kind::NOT; |
157 |
8387423 |
TNode nextAtom = nextPol ? next.first : next.first[0]; |
158 |
4864859 |
if (isTheoryAtom(nextAtom)) |
159 |
|
{ |
160 |
|
// should be assigned a literal |
161 |
1342295 |
Assert(d_cnfStream->hasLiteral(nextAtom)); |
162 |
|
// get the SAT literal |
163 |
1342295 |
SatLiteral nsl = d_cnfStream->getLiteral(nextAtom); |
164 |
|
// flip if the atom was negated |
165 |
|
// store the last decision value here, which will be used at the |
166 |
|
// starting value on the next call to this method |
167 |
1342295 |
lastChildVal = nextPol ? next.second : invertValue(next.second); |
168 |
|
// (1) atom with unassigned value, return it as the decision, possibly |
169 |
|
// inverted |
170 |
2684590 |
Trace("jh-process") |
171 |
1342295 |
<< "...return " << nextAtom << " " << lastChildVal << std::endl; |
172 |
|
// Note that the last child of the current node we looked at does |
173 |
|
// *not* yet have a value. Although we are returning it as a decision, |
174 |
|
// we cannot set its value in d_justified, because we have yet to |
175 |
|
// push a decision level. Thus, we remember the literal we decided |
176 |
|
// on. The value of d_lastDecisionLit will be processed at the |
177 |
|
// beginning of the next call to getNext above. |
178 |
1342295 |
d_lastDecisionLit = next.first; |
179 |
|
// record that we made a decision |
180 |
1342295 |
d_currStatusDec = true; |
181 |
1342295 |
if (d_decisionStopOnly) |
182 |
|
{ |
183 |
|
// only doing stop-only, return undefSatLiteral. |
184 |
60664 |
return undefSatLiteral; |
185 |
|
} |
186 |
1281631 |
return lastChildVal == SAT_VALUE_FALSE ? ~nsl : nsl; |
187 |
|
} |
188 |
|
else |
189 |
|
{ |
190 |
|
// NOTE: it may be the case that we have yet to justify this node, |
191 |
|
// as indicated by the return of lookupValue. We may have a value |
192 |
|
// assigned to next.first by the SAT solver, but we ignore it here. |
193 |
|
// (2) unprocessed non-atom, push to the stack |
194 |
3522564 |
d_stack.pushToStack(next.first, next.second); |
195 |
3522564 |
d_stats.d_maxStackSize.maxAssign(d_stack.size()); |
196 |
|
// we have yet to process children for the next node, so lastChildVal |
197 |
|
// remains set to SAT_VALUE_UNKNOWN. |
198 |
|
} |
199 |
|
} |
200 |
|
else |
201 |
|
{ |
202 |
8889056 |
Trace("jh-debug") << "in main loop, " << next.first << " has value " |
203 |
4444528 |
<< lastChildVal << std::endl; |
204 |
|
} |
205 |
|
// (3) otherwise, next already has a value lastChildVal which will be |
206 |
|
// processed in the next iteration of the loop. |
207 |
|
} |
208 |
17390028 |
} while (d_stack.hasCurrentAssertion()); |
209 |
|
// we exhausted all assertions |
210 |
53123 |
Trace("jh-process") << "...exhausted all assertions" << std::endl; |
211 |
53123 |
stopSearch = true; |
212 |
53123 |
return undefSatLiteral; |
213 |
|
} |
214 |
|
|
215 |
22094673 |
JustifyNode JustificationStrategy::getNextJustifyNode( |
216 |
|
JustifyInfo* ji, prop::SatValue& lastChildVal) |
217 |
|
{ |
218 |
|
// get the node we are trying to justify and its desired value |
219 |
44189346 |
JustifyNode jc = ji->getNode(); |
220 |
22094673 |
Assert(!jc.first.isNull()); |
221 |
22094673 |
Assert(jc.second != SAT_VALUE_UNKNOWN); |
222 |
|
// extract the non-negated formula we are trying to justify |
223 |
22094673 |
bool currPol = jc.first.getKind() != NOT; |
224 |
44189346 |
TNode curr = currPol ? jc.first : jc.first[0]; |
225 |
22094673 |
Kind ck = curr.getKind(); |
226 |
|
// the current node should be a non-theory literal and not have double |
227 |
|
// negation, due to our invariants of what is pushed onto the stack |
228 |
22094673 |
Assert(!isTheoryAtom(curr)); |
229 |
22094673 |
Assert(ck != NOT); |
230 |
|
// get the next child index to process |
231 |
22094673 |
size_t i = ji->getNextChildIndex(); |
232 |
44189346 |
Trace("jh-debug") << "getNextJustifyNode " << curr << " / " << currPol |
233 |
22094673 |
<< ", index = " << i |
234 |
22094673 |
<< ", last child value = " << lastChildVal << std::endl; |
235 |
|
// NOTE: if i>0, we just computed the value of the (i-1)^th child |
236 |
|
// i.e. i == 0 || lastChildVal != SAT_VALUE_UNKNOWN, |
237 |
|
// however this does not hold when backtracking has occurred. |
238 |
|
// if i=0, we shouldn't have a last child value |
239 |
22094673 |
Assert(i > 0 || lastChildVal == SAT_VALUE_UNKNOWN) |
240 |
|
<< "in getNextJustifyNode, value given for non-existent last child"; |
241 |
|
// we are trying to make the value of curr equal to currDesiredVal |
242 |
22094673 |
SatValue currDesiredVal = currPol ? jc.second : invertValue(jc.second); |
243 |
|
// value is set to TRUE/FALSE if the value of curr can be determined. |
244 |
22094673 |
SatValue value = SAT_VALUE_UNKNOWN; |
245 |
|
// if we require processing the next child of curr, we set desiredVal to |
246 |
|
// value which we want that child to be to make curr's value equal to |
247 |
|
// currDesiredVal. |
248 |
22094673 |
SatValue desiredVal = SAT_VALUE_UNKNOWN; |
249 |
22094673 |
if (ck == AND || ck == OR) |
250 |
|
{ |
251 |
14623536 |
if (i == 0) |
252 |
|
{ |
253 |
|
// See if a single child with currDesiredVal forces value, which is the |
254 |
|
// case if ck / currDesiredVal in { and / false, or / true }. |
255 |
9728254 |
if ((ck == AND) == (currDesiredVal == SAT_VALUE_FALSE)) |
256 |
|
{ |
257 |
|
// lookahead to determine if already satisfied |
258 |
|
// we scan only once, when processing the first child |
259 |
20302694 |
for (const Node& c : curr) |
260 |
|
{ |
261 |
18301633 |
SatValue v = lookupValue(c); |
262 |
18301633 |
if (v == currDesiredVal) |
263 |
|
{ |
264 |
6906186 |
Trace("jh-debug") << "already forcing child " << c << std::endl; |
265 |
6906186 |
value = currDesiredVal; |
266 |
6906186 |
break; |
267 |
|
} |
268 |
|
// NOTE: if v == SAT_VALUE_UNKNOWN, then we can add this to a watch |
269 |
|
// list and short circuit processing in the children of this node. |
270 |
|
} |
271 |
|
} |
272 |
9728254 |
desiredVal = currDesiredVal; |
273 |
|
} |
274 |
7217914 |
else if ((ck == AND && lastChildVal == SAT_VALUE_FALSE) |
275 |
4637850 |
|| (ck == OR && lastChildVal == SAT_VALUE_TRUE) |
276 |
7857690 |
|| i == curr.getNumChildren()) |
277 |
|
{ |
278 |
2677268 |
Trace("jh-debug") << "current is forcing child" << std::endl; |
279 |
|
// forcing or exhausted case |
280 |
2677268 |
value = lastChildVal; |
281 |
|
} |
282 |
|
else |
283 |
|
{ |
284 |
|
// otherwise, no forced value, value of child should match the parent |
285 |
2218014 |
desiredVal = currDesiredVal; |
286 |
14623536 |
} |
287 |
|
} |
288 |
7471137 |
else if (ck == IMPLIES) |
289 |
|
{ |
290 |
3368620 |
if (i == 0) |
291 |
|
{ |
292 |
|
// lookahead to second child to determine if value already forced |
293 |
1887628 |
if (lookupValue(curr[1]) == SAT_VALUE_TRUE) |
294 |
|
{ |
295 |
762913 |
value = SAT_VALUE_TRUE; |
296 |
|
} |
297 |
|
else |
298 |
|
{ |
299 |
|
// otherwise, we request the opposite of what parent wants |
300 |
1124715 |
desiredVal = invertValue(currDesiredVal); |
301 |
|
} |
302 |
|
} |
303 |
1480992 |
else if (i == 1) |
304 |
|
{ |
305 |
|
// forcing case |
306 |
1115481 |
if (lastChildVal == SAT_VALUE_FALSE) |
307 |
|
{ |
308 |
734399 |
value = SAT_VALUE_TRUE; |
309 |
|
} |
310 |
|
else |
311 |
|
{ |
312 |
381082 |
desiredVal = currDesiredVal; |
313 |
|
} |
314 |
|
} |
315 |
|
else |
316 |
|
{ |
317 |
|
// exhausted case |
318 |
365511 |
value = lastChildVal; |
319 |
|
} |
320 |
|
} |
321 |
4102517 |
else if (ck == ITE) |
322 |
|
{ |
323 |
2124136 |
if (i == 0) |
324 |
|
{ |
325 |
|
// lookahead on branches |
326 |
735514 |
SatValue val1 = lookupValue(curr[1]); |
327 |
735514 |
SatValue val2 = lookupValue(curr[2]); |
328 |
735514 |
if (val1 == val2) |
329 |
|
{ |
330 |
|
// branches have no difference, value is that of branches, which may |
331 |
|
// be unknown |
332 |
263497 |
value = val1; |
333 |
|
} |
334 |
|
// if first branch is already wrong or second branch is already correct, |
335 |
|
// try to make condition false. Note that we arbitrarily choose true here |
336 |
|
// if both children are unknown. If both children have the same value |
337 |
|
// and that value is not unknown, desiredVal will be ignored, since |
338 |
|
// value is set above. |
339 |
735514 |
desiredVal = |
340 |
1273755 |
(val1 == invertValue(currDesiredVal) || val2 == currDesiredVal) |
341 |
1039280 |
? SAT_VALUE_FALSE |
342 |
|
: SAT_VALUE_TRUE; |
343 |
|
} |
344 |
1388622 |
else if (i == 1) |
345 |
|
{ |
346 |
695249 |
Assert(lastChildVal != SAT_VALUE_UNKNOWN); |
347 |
|
// we just computed the value of the condition, check if the condition |
348 |
|
// was false |
349 |
695249 |
if (lastChildVal == SAT_VALUE_FALSE) |
350 |
|
{ |
351 |
|
// this increments to the else branch |
352 |
302888 |
i = ji->getNextChildIndex(); |
353 |
|
} |
354 |
|
// make the branch equal to the desired value |
355 |
695249 |
desiredVal = currDesiredVal; |
356 |
|
} |
357 |
|
else |
358 |
|
{ |
359 |
|
// return the branch value |
360 |
693373 |
value = lastChildVal; |
361 |
|
} |
362 |
|
} |
363 |
1978381 |
else if (ck == XOR || ck == EQUAL) |
364 |
|
{ |
365 |
1978381 |
Assert(curr[0].getType().isBoolean()); |
366 |
1978381 |
if (i == 0) |
367 |
|
{ |
368 |
|
// check if the rhs forces a value |
369 |
685262 |
SatValue val1 = lookupValue(curr[1]); |
370 |
685262 |
if (val1 == SAT_VALUE_UNKNOWN) |
371 |
|
{ |
372 |
|
// not forced, arbitrarily choose true |
373 |
464915 |
desiredVal = SAT_VALUE_TRUE; |
374 |
|
} |
375 |
|
else |
376 |
|
{ |
377 |
|
// if the RHS of the XOR/EQUAL already had a value val1, then: |
378 |
|
// ck / currDesiredVal |
379 |
|
// equal / true ... LHS should have same value as RHS |
380 |
|
// equal / false ... LHS should have opposite value as RHS |
381 |
|
// xor / true ... LHS should have opposite value as RHS |
382 |
|
// xor / false ... LHS should have same value as RHS |
383 |
440694 |
desiredVal = ((ck == EQUAL) == (currDesiredVal == SAT_VALUE_TRUE)) |
384 |
220347 |
? val1 |
385 |
|
: invertValue(val1); |
386 |
|
} |
387 |
|
} |
388 |
1293119 |
else if (i == 1) |
389 |
|
{ |
390 |
659401 |
Assert(lastChildVal != SAT_VALUE_UNKNOWN); |
391 |
|
// same as above, choosing a value for RHS based on the value of LHS, |
392 |
|
// which is stored in lastChildVal. |
393 |
1318802 |
desiredVal = ((ck == EQUAL) == (currDesiredVal == SAT_VALUE_TRUE)) |
394 |
937165 |
? lastChildVal |
395 |
277764 |
: invertValue(lastChildVal); |
396 |
|
} |
397 |
|
else |
398 |
|
{ |
399 |
|
// recompute the value of the first child |
400 |
633718 |
SatValue val0 = lookupValue(curr[0]); |
401 |
633718 |
Assert(val0 != SAT_VALUE_UNKNOWN); |
402 |
633718 |
Assert(lastChildVal != SAT_VALUE_UNKNOWN); |
403 |
|
// compute the value of the equal/xor. The values for LHS/RHS are |
404 |
|
// stored in val0 and lastChildVal. |
405 |
|
// (val0 == lastChildVal) / ck |
406 |
|
// true / equal ... value of curr is true |
407 |
|
// true / xor ... value of curr is false |
408 |
|
// false / equal ... value of curr is false |
409 |
|
// false / xor ... value of curr is true |
410 |
633718 |
value = ((val0 == lastChildVal) == (ck == EQUAL)) ? SAT_VALUE_TRUE |
411 |
|
: SAT_VALUE_FALSE; |
412 |
1978381 |
} |
413 |
|
} |
414 |
|
else |
415 |
|
{ |
416 |
|
// curr should not be an atom |
417 |
|
Assert(false); |
418 |
|
} |
419 |
|
// we return null if we have determined the value of the current node |
420 |
22094673 |
if (value != SAT_VALUE_UNKNOWN) |
421 |
|
{ |
422 |
12785286 |
Assert(!isTheoryAtom(curr)); |
423 |
|
// add to justify if so |
424 |
12785286 |
d_justified.insert(curr, value); |
425 |
|
// update the last child value, which will be used by the parent of the |
426 |
|
// current node, if it exists. |
427 |
12785286 |
lastChildVal = currPol ? value : invertValue(value); |
428 |
25570572 |
Trace("jh-debug") << "getJustifyNode: return null with value " |
429 |
12785286 |
<< lastChildVal << std::endl; |
430 |
|
// return null, indicating there is nothing left to do for current |
431 |
12785286 |
return JustifyNode(TNode::null(), SAT_VALUE_UNKNOWN); |
432 |
|
} |
433 |
18618774 |
Trace("jh-debug") << "getJustifyNode: return " << curr[i] |
434 |
9309387 |
<< " with desired value " << desiredVal << std::endl; |
435 |
|
// The next child should be a valid argument in curr. Otherwise, we did not |
436 |
|
// recognize when its value could be inferred above. |
437 |
9309387 |
Assert(i < curr.getNumChildren()) << curr.getKind() << " had no value"; |
438 |
|
// should set a desired value |
439 |
9309387 |
Assert(desiredVal != SAT_VALUE_UNKNOWN) |
440 |
|
<< "Child " << i << " of " << curr.getKind() << " had no desired value"; |
441 |
|
// return the justify node |
442 |
9309387 |
return JustifyNode(curr[i], desiredVal); |
443 |
|
} |
444 |
|
|
445 |
43573540 |
prop::SatValue JustificationStrategy::lookupValue(TNode n) |
446 |
|
{ |
447 |
43573540 |
bool pol = n.getKind() != NOT; |
448 |
87147080 |
TNode atom = pol ? n : n[0]; |
449 |
43573540 |
Assert(atom.getKind() != NOT); |
450 |
|
// check if we have already determined the value |
451 |
|
// notice that d_justified may contain nodes that are not assigned SAT values, |
452 |
|
// since this class infers when the value of nodes can be determined. |
453 |
43573540 |
auto jit = d_justified.find(atom); |
454 |
43573540 |
if (jit != d_justified.end()) |
455 |
|
{ |
456 |
15580095 |
return pol ? jit->second : invertValue(jit->second); |
457 |
|
} |
458 |
|
// Notice that looking up values for non-theory atoms may lead to |
459 |
|
// an incomplete strategy where a formula is asserted but not justified |
460 |
|
// via its theory literal subterms. This is the case because the justification |
461 |
|
// heuristic is not the only source of decisions, as the theory may request |
462 |
|
// them. |
463 |
27993445 |
if (isTheoryAtom(atom)) |
464 |
|
{ |
465 |
12168087 |
SatLiteral nsl = d_cnfStream->getLiteral(atom); |
466 |
12168087 |
prop::SatValue val = d_satSolver->value(nsl); |
467 |
12168087 |
if (val != SAT_VALUE_UNKNOWN) |
468 |
|
{ |
469 |
|
// this is the moment where we realize a skolem definition is relevant, |
470 |
|
// add now. |
471 |
|
// NOTE: if we enable skolems when they are justified, we could call |
472 |
|
// a method notifyJustified(atom) here |
473 |
7300676 |
d_justified.insert(atom, val); |
474 |
7300676 |
return pol ? val : invertValue(val); |
475 |
|
} |
476 |
|
} |
477 |
20692769 |
return SAT_VALUE_UNKNOWN; |
478 |
|
} |
479 |
|
|
480 |
51125 |
bool JustificationStrategy::isDone() { return !refreshCurrentAssertion(); } |
481 |
|
|
482 |
325715 |
void JustificationStrategy::addAssertion(TNode assertion, bool isLemma) |
483 |
|
{ |
484 |
325715 |
Trace("jh-assert") << "addAssertion " << assertion << std::endl; |
485 |
651430 |
std::vector<TNode> toProcess; |
486 |
325715 |
toProcess.push_back(assertion); |
487 |
325715 |
insertToAssertionList(toProcess, false); |
488 |
325715 |
} |
489 |
|
|
490 |
21598 |
void JustificationStrategy::addSkolemDefinition(TNode lem, |
491 |
|
TNode skolem, |
492 |
|
bool isLemma) |
493 |
|
{ |
494 |
43196 |
Trace("jh-assert") << "addSkolemDefinition " << lem << " / " << skolem |
495 |
21598 |
<< std::endl; |
496 |
21598 |
if (d_jhSkRlvMode == options::JutificationSkolemRlvMode::ALWAYS) |
497 |
|
{ |
498 |
|
// just add to main assertions list |
499 |
|
std::vector<TNode> toProcess; |
500 |
|
toProcess.push_back(lem); |
501 |
|
insertToAssertionList(toProcess, false); |
502 |
|
} |
503 |
21598 |
} |
504 |
12754 |
bool JustificationStrategy::needsActiveSkolemDefs() const |
505 |
|
{ |
506 |
12754 |
return d_jhSkRlvMode == options::JutificationSkolemRlvMode::ASSERT; |
507 |
|
} |
508 |
|
|
509 |
7218483 |
void JustificationStrategy::notifyActiveSkolemDefs(std::vector<TNode>& defs) |
510 |
|
{ |
511 |
7218483 |
Assert(d_jhSkRlvMode == options::JutificationSkolemRlvMode::ASSERT); |
512 |
|
// assertion processed makes all skolems in assertion active, |
513 |
|
// which triggers their definitions to becoming relevant |
514 |
7218483 |
insertToAssertionList(defs, true); |
515 |
|
// NOTE: if we had a notifyAsserted callback, we could update tracking |
516 |
|
// triggers, pop stack to where a child implied that a node on the current |
517 |
|
// stack is justified. |
518 |
7218483 |
} |
519 |
|
|
520 |
7544198 |
void JustificationStrategy::insertToAssertionList(std::vector<TNode>& toProcess, |
521 |
|
bool useSkolemList) |
522 |
|
{ |
523 |
7544198 |
AssertionList& al = useSkolemList ? d_skolemAssertions : d_assertions; |
524 |
7544198 |
IntStat& sizeStat = |
525 |
|
useSkolemList ? d_stats.d_maxSkolemDefsSize : d_stats.d_maxAssertionsSize; |
526 |
|
// always miniscope AND and negated OR immediately |
527 |
7544198 |
size_t index = 0; |
528 |
|
// must keep some intermediate nodes below around for ref counting |
529 |
15088396 |
std::vector<Node> keep; |
530 |
9238350 |
while (index < toProcess.size()) |
531 |
|
{ |
532 |
1694152 |
TNode curr = toProcess[index]; |
533 |
847076 |
bool pol = curr.getKind() != NOT; |
534 |
1694152 |
TNode currAtom = pol ? curr : curr[0]; |
535 |
847076 |
index++; |
536 |
847076 |
Kind k = currAtom.getKind(); |
537 |
847076 |
if (k == AND && pol) |
538 |
|
{ |
539 |
21028 |
toProcess.insert(toProcess.begin() + index, curr.begin(), curr.end()); |
540 |
|
} |
541 |
826048 |
else if (k == OR && !pol) |
542 |
|
{ |
543 |
478 |
std::vector<Node> negc; |
544 |
3143 |
for (TNode c : currAtom) |
545 |
|
{ |
546 |
5808 |
Node cneg = c.negate(); |
547 |
2904 |
negc.push_back(cneg); |
548 |
|
// ensure ref counted |
549 |
2904 |
keep.push_back(cneg); |
550 |
|
} |
551 |
478 |
toProcess.insert(toProcess.begin() + index, negc.begin(), negc.end()); |
552 |
|
} |
553 |
825809 |
else if (!isTheoryAtom(currAtom)) |
554 |
|
{ |
555 |
615083 |
al.addAssertion(curr); |
556 |
|
// take stats |
557 |
615083 |
sizeStat.maxAssign(al.size()); |
558 |
|
} |
559 |
|
else |
560 |
|
{ |
561 |
|
// we skip (top-level) theory literals, since these are always propagated |
562 |
|
// NOTE: skolem definitions that are always relevant should be added to |
563 |
|
// assertions, for uniformity via a method notifyJustified(currAtom) |
564 |
|
} |
565 |
|
} |
566 |
|
// clear since toProcess may contain nodes with 0 ref count after returning |
567 |
|
// otherwise |
568 |
7544198 |
toProcess.clear(); |
569 |
7544198 |
} |
570 |
|
|
571 |
10873266 |
bool JustificationStrategy::refreshCurrentAssertion() |
572 |
|
{ |
573 |
10873266 |
Trace("jh-process") << "refreshCurrentAssertion" << std::endl; |
574 |
|
// if we already have a current assertion, nothing to be done |
575 |
21746532 |
TNode curr = d_stack.getCurrentAssertion(); |
576 |
10873266 |
if (!curr.isNull()) |
577 |
|
{ |
578 |
1404053 |
if (curr != d_currUnderStatus && !d_currUnderStatus.isNull()) |
579 |
|
{ |
580 |
44189 |
++(d_stats.d_numStatusBacktrack); |
581 |
44189 |
d_assertions.notifyStatus(d_currUnderStatus, DecisionStatus::BACKTRACK); |
582 |
|
// we've backtracked to another assertion which may be partially |
583 |
|
// processed. don't track its status? |
584 |
44189 |
d_currUnderStatus = Node::null(); |
585 |
|
// NOTE: could reset the stack here to preserve other invariants, |
586 |
|
// currently we do not. |
587 |
|
} |
588 |
1404053 |
return true; |
589 |
|
} |
590 |
9469213 |
bool skFirst = (d_jhSkMode != options::JutificationSkolemMode::LAST); |
591 |
|
// use main assertions first |
592 |
9469213 |
if (refreshCurrentAssertionFromList(skFirst)) |
593 |
|
{ |
594 |
536047 |
return true; |
595 |
|
} |
596 |
|
// if satisfied all main assertions, use the skolem assertions, which may |
597 |
|
// fail |
598 |
8933166 |
return refreshCurrentAssertionFromList(!skFirst); |
599 |
|
} |
600 |
|
|
601 |
18402379 |
bool JustificationStrategy::refreshCurrentAssertionFromList(bool useSkolemList) |
602 |
|
{ |
603 |
18402379 |
AssertionList& al = useSkolemList ? d_skolemAssertions : d_assertions; |
604 |
18402379 |
bool doWatchStatus = !useSkolemList; |
605 |
18402379 |
d_currUnderStatus = Node::null(); |
606 |
36804758 |
TNode curr = al.getNextAssertion(); |
607 |
|
SatValue currValue; |
608 |
19458897 |
while (!curr.isNull()) |
609 |
|
{ |
610 |
9928084 |
Trace("jh-process") << "Check assertion " << curr << std::endl; |
611 |
|
// we never add theory literals to our assertions lists |
612 |
9928084 |
Assert(!isTheoryLiteral(curr)); |
613 |
9928084 |
currValue = lookupValue(curr); |
614 |
9928084 |
if (currValue == SAT_VALUE_UNKNOWN) |
615 |
|
{ |
616 |
|
// if not already justified, we reset the stack and push to it |
617 |
9399825 |
d_stack.reset(curr); |
618 |
9399825 |
d_lastDecisionLit = TNode::null(); |
619 |
|
// for activity |
620 |
9399825 |
if (doWatchStatus) |
621 |
|
{ |
622 |
|
// initially, mark that we have not found a decision in this |
623 |
8863778 |
d_currUnderStatus = d_stack.getCurrentAssertion(); |
624 |
8863778 |
d_currStatusDec = false; |
625 |
|
} |
626 |
9399825 |
return true; |
627 |
|
} |
628 |
|
// assertions should all be satisfied, otherwise we are in conflict |
629 |
528259 |
Assert(currValue == SAT_VALUE_TRUE); |
630 |
528259 |
if (doWatchStatus) |
631 |
|
{ |
632 |
|
// mark that we did not find a decision in it |
633 |
527995 |
++(d_stats.d_numStatusNoDecision); |
634 |
527995 |
d_assertions.notifyStatus(curr, DecisionStatus::NO_DECISION); |
635 |
|
} |
636 |
|
// already justified, immediately skip |
637 |
528259 |
curr = al.getNextAssertion(); |
638 |
|
} |
639 |
9002554 |
return false; |
640 |
|
} |
641 |
|
|
642 |
9928084 |
bool JustificationStrategy::isTheoryLiteral(TNode n) |
643 |
|
{ |
644 |
9928084 |
return isTheoryAtom(n.getKind() == NOT ? n[0] : n); |
645 |
|
} |
646 |
|
|
647 |
78492156 |
bool JustificationStrategy::isTheoryAtom(TNode n) |
648 |
|
{ |
649 |
78492156 |
Kind k = n.getKind(); |
650 |
78492156 |
Assert(k != NOT); |
651 |
64272088 |
return k != AND && k != OR && k != IMPLIES && k != ITE && k != XOR |
652 |
95719479 |
&& (k != EQUAL || !n[0].getType().isBoolean()); |
653 |
|
} |
654 |
|
|
655 |
|
} // namespace decision |
656 |
31125 |
} // namespace cvc5 |