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 |
7739 |
JustificationStrategy::JustificationStrategy(context::Context* c, |
27 |
|
context::UserContext* u, |
28 |
|
prop::SkolemDefManager* skdm, |
29 |
7739 |
ResourceManager* rm) |
30 |
|
: DecisionEngine(c, rm), |
31 |
|
d_skdm(skdm), |
32 |
|
d_assertions( |
33 |
|
u, |
34 |
|
c, |
35 |
7739 |
options::jhRlvOrder()), // assertions are user-context dependent |
36 |
|
d_skolemAssertions(c, c), // skolem assertions are SAT-context dependent |
37 |
|
d_justified(c), |
38 |
|
d_stack(c), |
39 |
|
d_lastDecisionLit(c), |
40 |
|
d_currStatusDec(false), |
41 |
7739 |
d_useRlvOrder(options::jhRlvOrder()), |
42 |
7739 |
d_decisionStopOnly(options::decisionMode() |
43 |
7739 |
== options::DecisionMode::STOPONLY), |
44 |
7739 |
d_jhSkMode(options::jhSkolemMode()), |
45 |
38695 |
d_jhSkRlvMode(options::jhSkolemRlvMode()) |
46 |
|
{ |
47 |
7739 |
} |
48 |
|
|
49 |
9726 |
void JustificationStrategy::presolve() |
50 |
|
{ |
51 |
9726 |
d_lastDecisionLit = Node::null(); |
52 |
9726 |
d_currUnderStatus = Node::null(); |
53 |
9726 |
d_currStatusDec = false; |
54 |
|
// reset the dynamic assertion list data |
55 |
9726 |
d_assertions.presolve(); |
56 |
9726 |
d_skolemAssertions.presolve(); |
57 |
|
// clear the stack |
58 |
9726 |
d_stack.clear(); |
59 |
9726 |
} |
60 |
|
|
61 |
1243144 |
SatLiteral JustificationStrategy::getNextInternal(bool& stopSearch) |
62 |
|
{ |
63 |
|
// ensure we have an assertion |
64 |
1243144 |
if (!refreshCurrentAssertion()) |
65 |
|
{ |
66 |
2930 |
Trace("jh-process") << "getNext, already finished" << std::endl; |
67 |
2930 |
stopSearch = true; |
68 |
2930 |
return undefSatLiteral; |
69 |
|
} |
70 |
1240214 |
Assert(d_stack.hasCurrentAssertion()); |
71 |
|
// temporary information in the loop below |
72 |
|
JustifyInfo* ji; |
73 |
2480428 |
JustifyNode next; |
74 |
|
// we start with the value implied by the last decision, if it exists |
75 |
1240214 |
SatValue lastChildVal = SAT_VALUE_UNKNOWN; |
76 |
1240214 |
Trace("jh-process") << "getNext" << std::endl; |
77 |
|
// If we had just sent a decision, then we lookup its value here. This may |
78 |
|
// correspond to a context where the decision was carried out, or |
79 |
|
// alternatively it may correspond to a case where we have backtracked and |
80 |
|
// propagated that literal with opposite polarity. Thus, we do not assume we |
81 |
|
// know the value of d_lastDecisionLit and look it up again here. The value |
82 |
|
// of lastChildVal will be used to update the justify info in the current |
83 |
|
// stack below. |
84 |
1240214 |
if (!d_lastDecisionLit.get().isNull()) |
85 |
|
{ |
86 |
2411670 |
Trace("jh-process") << "last decision = " << d_lastDecisionLit.get() |
87 |
1205835 |
<< std::endl; |
88 |
1205835 |
lastChildVal = lookupValue(d_lastDecisionLit.get()); |
89 |
1205835 |
if (lastChildVal == SAT_VALUE_UNKNOWN) |
90 |
|
{ |
91 |
|
// if the value is now unknown, we must reprocess the assertion, since |
92 |
|
// we have backtracked |
93 |
200170 |
TNode curr = d_stack.getCurrentAssertion(); |
94 |
100085 |
d_stack.clear(); |
95 |
100085 |
d_stack.reset(curr); |
96 |
|
} |
97 |
|
} |
98 |
1240214 |
d_lastDecisionLit = TNode::null(); |
99 |
|
// while we are trying to satisfy assertions |
100 |
13170118 |
do |
101 |
|
{ |
102 |
14410332 |
Assert(d_stack.getCurrent() != nullptr); |
103 |
|
// We get the next justify node, if it can be found. |
104 |
9115595 |
do |
105 |
|
{ |
106 |
|
// get the current justify info, which should be ready |
107 |
23525927 |
ji = d_stack.getCurrent(); |
108 |
23525927 |
if (ji == nullptr) |
109 |
|
{ |
110 |
6602735 |
break; |
111 |
|
} |
112 |
|
// get the next child to process from the current justification info |
113 |
|
// based on the fact that its last child processed had value lastChildVal. |
114 |
16923192 |
next = getNextJustifyNode(ji, lastChildVal); |
115 |
|
// if the current node is finished, we pop the stack |
116 |
16923192 |
if (next.first.isNull()) |
117 |
|
{ |
118 |
9115595 |
d_stack.popStack(); |
119 |
|
} |
120 |
16923192 |
} while (next.first.isNull()); |
121 |
|
|
122 |
14410332 |
if (ji == nullptr) |
123 |
|
{ |
124 |
|
// the assertion we just processed should have value true |
125 |
6602735 |
Assert(lastChildVal == SAT_VALUE_TRUE); |
126 |
6602735 |
if (!d_currUnderStatus.isNull()) |
127 |
|
{ |
128 |
|
// notify status if we are watching it |
129 |
|
DecisionStatus ds; |
130 |
6001608 |
if (d_currStatusDec) |
131 |
|
{ |
132 |
729397 |
ds = DecisionStatus::DECISION; |
133 |
729397 |
++(d_stats.d_numStatusDecision); |
134 |
|
} |
135 |
|
else |
136 |
|
{ |
137 |
5272211 |
ds = DecisionStatus::NO_DECISION; |
138 |
5272211 |
++(d_stats.d_numStatusNoDecision); |
139 |
|
} |
140 |
6001608 |
d_assertions.notifyStatus(d_currUnderStatus, ds); |
141 |
|
} |
142 |
|
// we did not find a next node for current, refresh current assertion |
143 |
6602735 |
d_stack.clear(); |
144 |
6602735 |
refreshCurrentAssertion(); |
145 |
6602735 |
lastChildVal = SAT_VALUE_UNKNOWN; |
146 |
13205470 |
Trace("jh-process") << "...exhausted assertion, now " |
147 |
6602735 |
<< d_stack.getCurrentAssertion() << std::endl; |
148 |
|
} |
149 |
|
else |
150 |
|
{ |
151 |
|
// must have requested a next child to justify |
152 |
7807597 |
Assert(!next.first.isNull()); |
153 |
7807597 |
Assert(next.second != SAT_VALUE_UNKNOWN); |
154 |
|
// Look up whether the next child already has a value |
155 |
7807597 |
lastChildVal = lookupValue(next.first); |
156 |
7807597 |
if (lastChildVal == SAT_VALUE_UNKNOWN) |
157 |
|
{ |
158 |
3867851 |
bool nextPol = next.first.getKind() != kind::NOT; |
159 |
6544028 |
TNode nextAtom = nextPol ? next.first : next.first[0]; |
160 |
3867851 |
if (isTheoryAtom(nextAtom)) |
161 |
|
{ |
162 |
|
// should be assigned a literal |
163 |
1191674 |
Assert(d_cnfStream->hasLiteral(nextAtom)); |
164 |
|
// get the SAT literal |
165 |
1191674 |
SatLiteral nsl = d_cnfStream->getLiteral(nextAtom); |
166 |
|
// flip if the atom was negated |
167 |
|
// store the last decision value here, which will be used at the |
168 |
|
// starting value on the next call to this method |
169 |
1191674 |
lastChildVal = nextPol ? next.second : invertValue(next.second); |
170 |
|
// (1) atom with unassigned value, return it as the decision, possibly |
171 |
|
// inverted |
172 |
2383348 |
Trace("jh-process") |
173 |
1191674 |
<< "...return " << nextAtom << " " << lastChildVal << std::endl; |
174 |
|
// Note that the last child of the current node we looked at does |
175 |
|
// *not* yet have a value. Although we are returning it as a decision, |
176 |
|
// we cannot set its value in d_justified, because we have yet to |
177 |
|
// push a decision level. Thus, we remember the literal we decided |
178 |
|
// on. The value of d_lastDecisionLit will be processed at the |
179 |
|
// beginning of the next call to getNext above. |
180 |
1191674 |
d_lastDecisionLit = next.first; |
181 |
|
// record that we made a decision |
182 |
1191674 |
d_currStatusDec = true; |
183 |
1191674 |
if (d_decisionStopOnly) |
184 |
|
{ |
185 |
|
// only doing stop-only, return undefSatLiteral. |
186 |
60237 |
return undefSatLiteral; |
187 |
|
} |
188 |
1131437 |
return lastChildVal == SAT_VALUE_FALSE ? ~nsl : nsl; |
189 |
|
} |
190 |
|
else |
191 |
|
{ |
192 |
|
// NOTE: it may be the case that we have yet to justify this node, |
193 |
|
// as indicated by the return of lookupValue. We may have a value |
194 |
|
// assigned to next.first by the SAT solver, but we ignore it here. |
195 |
|
// (2) unprocessed non-atom, push to the stack |
196 |
2676177 |
d_stack.pushToStack(next.first, next.second); |
197 |
2676177 |
d_stats.d_maxStackSize.maxAssign(d_stack.size()); |
198 |
|
// we have yet to process children for the next node, so lastChildVal |
199 |
|
// remains set to SAT_VALUE_UNKNOWN. |
200 |
|
} |
201 |
|
} |
202 |
|
else |
203 |
|
{ |
204 |
7879492 |
Trace("jh-debug") << "in main loop, " << next.first << " has value " |
205 |
3939746 |
<< lastChildVal << std::endl; |
206 |
|
} |
207 |
|
// (3) otherwise, next already has a value lastChildVal which will be |
208 |
|
// processed in the next iteration of the loop. |
209 |
|
} |
210 |
13218658 |
} while (d_stack.hasCurrentAssertion()); |
211 |
|
// we exhausted all assertions |
212 |
48540 |
Trace("jh-process") << "...exhausted all assertions" << std::endl; |
213 |
48540 |
stopSearch = true; |
214 |
48540 |
return undefSatLiteral; |
215 |
|
} |
216 |
|
|
217 |
16923192 |
JustifyNode JustificationStrategy::getNextJustifyNode( |
218 |
|
JustifyInfo* ji, prop::SatValue& lastChildVal) |
219 |
|
{ |
220 |
|
// get the node we are trying to justify and its desired value |
221 |
33846384 |
JustifyNode jc = ji->getNode(); |
222 |
16923192 |
Assert(!jc.first.isNull()); |
223 |
16923192 |
Assert(jc.second != SAT_VALUE_UNKNOWN); |
224 |
|
// extract the non-negated formula we are trying to justify |
225 |
16923192 |
bool currPol = jc.first.getKind() != NOT; |
226 |
33846384 |
TNode curr = currPol ? jc.first : jc.first[0]; |
227 |
16923192 |
Kind ck = curr.getKind(); |
228 |
|
// the current node should be a non-theory literal and not have double |
229 |
|
// negation, due to our invariants of what is pushed onto the stack |
230 |
16923192 |
Assert(!isTheoryAtom(curr)); |
231 |
16923192 |
Assert(ck != NOT); |
232 |
|
// get the next child index to process |
233 |
16923192 |
size_t i = ji->getNextChildIndex(); |
234 |
33846384 |
Trace("jh-debug") << "getNextJustifyNode " << curr << " / " << currPol |
235 |
16923192 |
<< ", index = " << i |
236 |
16923192 |
<< ", last child value = " << lastChildVal << std::endl; |
237 |
|
// NOTE: if i>0, we just computed the value of the (i-1)^th child |
238 |
|
// i.e. i == 0 || lastChildVal != SAT_VALUE_UNKNOWN, |
239 |
|
// however this does not hold when backtracking has occurred. |
240 |
|
// if i=0, we shouldn't have a last child value |
241 |
16923192 |
Assert(i > 0 || lastChildVal == SAT_VALUE_UNKNOWN) |
242 |
|
<< "in getNextJustifyNode, value given for non-existent last child"; |
243 |
|
// we are trying to make the value of curr equal to currDesiredVal |
244 |
16923192 |
SatValue currDesiredVal = currPol ? jc.second : invertValue(jc.second); |
245 |
|
// value is set to TRUE/FALSE if the value of curr can be determined. |
246 |
16923192 |
SatValue value = SAT_VALUE_UNKNOWN; |
247 |
|
// if we require processing the next child of curr, we set desiredVal to |
248 |
|
// value which we want that child to be to make curr's value equal to |
249 |
|
// currDesiredVal. |
250 |
16923192 |
SatValue desiredVal = SAT_VALUE_UNKNOWN; |
251 |
16923192 |
if (ck == AND || ck == OR) |
252 |
|
{ |
253 |
10635400 |
if (i == 0) |
254 |
|
{ |
255 |
|
// See if a single child with currDesiredVal forces value, which is the |
256 |
|
// case if ck / currDesiredVal in { and / false, or / true }. |
257 |
6724248 |
if ((ck == AND) == (currDesiredVal == SAT_VALUE_FALSE)) |
258 |
|
{ |
259 |
|
// lookahead to determine if already satisfied |
260 |
|
// we scan only once, when processing the first child |
261 |
13643037 |
for (const Node& c : curr) |
262 |
|
{ |
263 |
12204125 |
SatValue v = lookupValue(c); |
264 |
12204125 |
if (v == currDesiredVal) |
265 |
|
{ |
266 |
4550684 |
Trace("jh-debug") << "already forcing child " << c << std::endl; |
267 |
4550684 |
value = currDesiredVal; |
268 |
4550684 |
break; |
269 |
|
} |
270 |
|
// NOTE: if v == SAT_VALUE_UNKNOWN, then we can add this to a watch |
271 |
|
// list and short circuit processing in the children of this node. |
272 |
|
} |
273 |
|
} |
274 |
6724248 |
desiredVal = currDesiredVal; |
275 |
|
} |
276 |
5838256 |
else if ((ck == AND && lastChildVal == SAT_VALUE_FALSE) |
277 |
3696238 |
|| (ck == OR && lastChildVal == SAT_VALUE_TRUE) |
278 |
6414980 |
|| i == curr.getNumChildren()) |
279 |
|
{ |
280 |
2032816 |
Trace("jh-debug") << "current is forcing child" << std::endl; |
281 |
|
// forcing or exhausted case |
282 |
2032816 |
value = lastChildVal; |
283 |
|
} |
284 |
|
else |
285 |
|
{ |
286 |
|
// otherwise, no forced value, value of child should match the parent |
287 |
1878336 |
desiredVal = currDesiredVal; |
288 |
10635400 |
} |
289 |
|
} |
290 |
6287792 |
else if (ck == IMPLIES) |
291 |
|
{ |
292 |
2297552 |
if (i == 0) |
293 |
|
{ |
294 |
|
// lookahead to second child to determine if value already forced |
295 |
1255567 |
if (lookupValue(curr[1]) == SAT_VALUE_TRUE) |
296 |
|
{ |
297 |
546243 |
value = SAT_VALUE_TRUE; |
298 |
|
} |
299 |
|
else |
300 |
|
{ |
301 |
|
// otherwise, we request the opposite of what parent wants |
302 |
709324 |
desiredVal = invertValue(currDesiredVal); |
303 |
|
} |
304 |
|
} |
305 |
1041985 |
else if (i == 1) |
306 |
|
{ |
307 |
|
// forcing case |
308 |
701363 |
if (lastChildVal == SAT_VALUE_FALSE) |
309 |
|
{ |
310 |
345115 |
value = SAT_VALUE_TRUE; |
311 |
|
} |
312 |
|
else |
313 |
|
{ |
314 |
356248 |
desiredVal = currDesiredVal; |
315 |
|
} |
316 |
|
} |
317 |
|
else |
318 |
|
{ |
319 |
|
// exhausted case |
320 |
340622 |
value = lastChildVal; |
321 |
|
} |
322 |
|
} |
323 |
3990240 |
else if (ck == ITE) |
324 |
|
{ |
325 |
2055670 |
if (i == 0) |
326 |
|
{ |
327 |
|
// lookahead on branches |
328 |
713065 |
SatValue val1 = lookupValue(curr[1]); |
329 |
713065 |
SatValue val2 = lookupValue(curr[2]); |
330 |
713065 |
if (val1 == val2) |
331 |
|
{ |
332 |
|
// branches have no difference, value is that of branches, which may |
333 |
|
// be unknown |
334 |
253347 |
value = val1; |
335 |
|
} |
336 |
|
// if first branch is already wrong or second branch is already correct, |
337 |
|
// try to make condition false. Note that we arbitrarily choose true here |
338 |
|
// if both children are unknown. If both children have the same value |
339 |
|
// and that value is not unknown, desiredVal will be ignored, since |
340 |
|
// value is set above. |
341 |
713065 |
desiredVal = |
342 |
1227801 |
(val1 == invertValue(currDesiredVal) || val2 == currDesiredVal) |
343 |
1009827 |
? SAT_VALUE_FALSE |
344 |
|
: SAT_VALUE_TRUE; |
345 |
|
} |
346 |
1342605 |
else if (i == 1) |
347 |
|
{ |
348 |
672253 |
Assert(lastChildVal != SAT_VALUE_UNKNOWN); |
349 |
|
// we just computed the value of the condition, check if the condition |
350 |
|
// was false |
351 |
672253 |
if (lastChildVal == SAT_VALUE_FALSE) |
352 |
|
{ |
353 |
|
// this increments to the else branch |
354 |
293694 |
i = ji->getNextChildIndex(); |
355 |
|
} |
356 |
|
// make the branch equal to the desired value |
357 |
672253 |
desiredVal = currDesiredVal; |
358 |
|
} |
359 |
|
else |
360 |
|
{ |
361 |
|
// return the branch value |
362 |
670352 |
value = lastChildVal; |
363 |
|
} |
364 |
|
} |
365 |
1934570 |
else if (ck == XOR || ck == EQUAL) |
366 |
|
{ |
367 |
1934570 |
Assert(curr[0].getType().isBoolean()); |
368 |
1934570 |
if (i == 0) |
369 |
|
{ |
370 |
|
// check if the rhs forces a value |
371 |
671956 |
SatValue val1 = lookupValue(curr[1]); |
372 |
671956 |
if (val1 == SAT_VALUE_UNKNOWN) |
373 |
|
{ |
374 |
|
// not forced, arbitrarily choose true |
375 |
464781 |
desiredVal = SAT_VALUE_TRUE; |
376 |
|
} |
377 |
|
else |
378 |
|
{ |
379 |
|
// if the RHS of the XOR/EQUAL already had a value val1, then: |
380 |
|
// ck / currDesiredVal |
381 |
|
// equal / true ... LHS should have same value as RHS |
382 |
|
// equal / false ... LHS should have opposite value as RHS |
383 |
|
// xor / true ... LHS should have opposite value as RHS |
384 |
|
// xor / false ... LHS should have same value as RHS |
385 |
414350 |
desiredVal = ((ck == EQUAL) == (currDesiredVal == SAT_VALUE_TRUE)) |
386 |
207175 |
? val1 |
387 |
|
: invertValue(val1); |
388 |
|
} |
389 |
|
} |
390 |
1262614 |
else if (i == 1) |
391 |
|
{ |
392 |
644839 |
Assert(lastChildVal != SAT_VALUE_UNKNOWN); |
393 |
|
// same as above, choosing a value for RHS based on the value of LHS, |
394 |
|
// which is stored in lastChildVal. |
395 |
1289678 |
desiredVal = ((ck == EQUAL) == (currDesiredVal == SAT_VALUE_TRUE)) |
396 |
923888 |
? lastChildVal |
397 |
279049 |
: invertValue(lastChildVal); |
398 |
|
} |
399 |
|
else |
400 |
|
{ |
401 |
|
// recompute the value of the first child |
402 |
617775 |
SatValue val0 = lookupValue(curr[0]); |
403 |
617775 |
Assert(val0 != SAT_VALUE_UNKNOWN); |
404 |
617775 |
Assert(lastChildVal != SAT_VALUE_UNKNOWN); |
405 |
|
// compute the value of the equal/xor. The values for LHS/RHS are |
406 |
|
// stored in val0 and lastChildVal. |
407 |
|
// (val0 == lastChildVal) / ck |
408 |
|
// true / equal ... value of curr is true |
409 |
|
// true / xor ... value of curr is false |
410 |
|
// false / equal ... value of curr is false |
411 |
|
// false / xor ... value of curr is true |
412 |
617775 |
value = ((val0 == lastChildVal) == (ck == EQUAL)) ? SAT_VALUE_TRUE |
413 |
|
: SAT_VALUE_FALSE; |
414 |
1934570 |
} |
415 |
|
} |
416 |
|
else |
417 |
|
{ |
418 |
|
// curr should not be an atom |
419 |
|
Assert(false); |
420 |
|
} |
421 |
|
// we return null if we have determined the value of the current node |
422 |
16923192 |
if (value != SAT_VALUE_UNKNOWN) |
423 |
|
{ |
424 |
9115595 |
Assert(!isTheoryAtom(curr)); |
425 |
|
// add to justify if so |
426 |
9115595 |
d_justified.insert(curr, value); |
427 |
|
// update the last child value, which will be used by the parent of the |
428 |
|
// current node, if it exists. |
429 |
9115595 |
lastChildVal = currPol ? value : invertValue(value); |
430 |
18231190 |
Trace("jh-debug") << "getJustifyNode: return null with value " |
431 |
9115595 |
<< lastChildVal << std::endl; |
432 |
|
// return null, indicating there is nothing left to do for current |
433 |
9115595 |
return JustifyNode(TNode::null(), SAT_VALUE_UNKNOWN); |
434 |
|
} |
435 |
15615194 |
Trace("jh-debug") << "getJustifyNode: return " << curr[i] |
436 |
7807597 |
<< " with desired value " << desiredVal << std::endl; |
437 |
|
// The next child should be a valid argument in curr. Otherwise, we did not |
438 |
|
// recognize when its value could be inferred above. |
439 |
7807597 |
Assert(i < curr.getNumChildren()) << curr.getKind() << " had no value"; |
440 |
|
// should set a desired value |
441 |
7807597 |
Assert(desiredVal != SAT_VALUE_UNKNOWN) |
442 |
|
<< "Child " << i << " of " << curr.getKind() << " had no desired value"; |
443 |
|
// return the justify node |
444 |
7807597 |
return JustifyNode(curr[i], desiredVal); |
445 |
|
} |
446 |
|
|
447 |
32250582 |
prop::SatValue JustificationStrategy::lookupValue(TNode n) |
448 |
|
{ |
449 |
32250582 |
bool pol = n.getKind() != NOT; |
450 |
64501164 |
TNode atom = pol ? n : n[0]; |
451 |
32250582 |
Assert(atom.getKind() != NOT); |
452 |
|
// check if we have already determined the value |
453 |
|
// notice that d_justified may contain nodes that are not assigned SAT values, |
454 |
|
// since this class infers when the value of nodes can be determined. |
455 |
32250582 |
auto jit = d_justified.find(atom); |
456 |
32250582 |
if (jit != d_justified.end()) |
457 |
|
{ |
458 |
10508417 |
return pol ? jit->second : invertValue(jit->second); |
459 |
|
} |
460 |
|
// Notice that looking up values for non-theory atoms may lead to |
461 |
|
// an incomplete strategy where a formula is asserted but not justified |
462 |
|
// via its theory literal subterms. This is the case because the justification |
463 |
|
// heuristic is not the only source of decisions, as the theory may request |
464 |
|
// them. |
465 |
21742165 |
if (isTheoryAtom(atom)) |
466 |
|
{ |
467 |
10188073 |
SatLiteral nsl = d_cnfStream->getLiteral(atom); |
468 |
10188073 |
prop::SatValue val = d_satSolver->value(nsl); |
469 |
10188073 |
if (val != SAT_VALUE_UNKNOWN) |
470 |
|
{ |
471 |
|
// this is the moment where we realize a skolem definition is relevant, |
472 |
|
// add now. |
473 |
|
// NOTE: if we enable skolems when they are justified, we could call |
474 |
|
// a method notifyJustified(atom) here |
475 |
6131232 |
d_justified.insert(atom, val); |
476 |
6131232 |
return pol ? val : invertValue(val); |
477 |
|
} |
478 |
|
} |
479 |
15610933 |
return SAT_VALUE_UNKNOWN; |
480 |
|
} |
481 |
|
|
482 |
45498 |
bool JustificationStrategy::isDone() { return !refreshCurrentAssertion(); } |
483 |
|
|
484 |
288259 |
void JustificationStrategy::addAssertion(TNode assertion) |
485 |
|
{ |
486 |
288259 |
Trace("jh-assert") << "addAssertion " << assertion << std::endl; |
487 |
576518 |
std::vector<TNode> toProcess; |
488 |
288259 |
toProcess.push_back(assertion); |
489 |
288259 |
insertToAssertionList(toProcess, false); |
490 |
288259 |
} |
491 |
|
|
492 |
22052 |
void JustificationStrategy::addSkolemDefinition(TNode lem, TNode skolem) |
493 |
|
{ |
494 |
44104 |
Trace("jh-assert") << "addSkolemDefinition " << lem << " / " << skolem |
495 |
22052 |
<< std::endl; |
496 |
22052 |
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 |
22052 |
} |
504 |
|
|
505 |
6036725 |
void JustificationStrategy::notifyAsserted(TNode n) |
506 |
|
{ |
507 |
6036725 |
if (d_jhSkRlvMode == options::JutificationSkolemRlvMode::ASSERT) |
508 |
|
{ |
509 |
|
// assertion processed makes all skolems in assertion active, |
510 |
|
// which triggers their definitions to becoming relevant |
511 |
12073450 |
std::vector<TNode> defs; |
512 |
6036725 |
d_skdm->notifyAsserted(n, defs, true); |
513 |
6036725 |
insertToAssertionList(defs, true); |
514 |
|
} |
515 |
|
// NOTE: can update tracking triggers, pop stack to where a child implied |
516 |
|
// that a node on the current stack is justified. |
517 |
6036725 |
} |
518 |
|
|
519 |
6324984 |
void JustificationStrategy::insertToAssertionList(std::vector<TNode>& toProcess, |
520 |
|
bool useSkolemList) |
521 |
|
{ |
522 |
6324984 |
AssertionList& al = useSkolemList ? d_skolemAssertions : d_assertions; |
523 |
6324984 |
IntStat& sizeStat = |
524 |
|
useSkolemList ? d_stats.d_maxSkolemDefsSize : d_stats.d_maxAssertionsSize; |
525 |
|
// always miniscope AND and negated OR immediately |
526 |
6324984 |
size_t index = 0; |
527 |
|
// must keep some intermediate nodes below around for ref counting |
528 |
12649968 |
std::vector<Node> keep; |
529 |
7865622 |
while (index < toProcess.size()) |
530 |
|
{ |
531 |
1540638 |
TNode curr = toProcess[index]; |
532 |
770319 |
bool pol = curr.getKind() != NOT; |
533 |
1540638 |
TNode currAtom = pol ? curr : curr[0]; |
534 |
770319 |
index++; |
535 |
770319 |
Kind k = currAtom.getKind(); |
536 |
770319 |
if (k == AND && pol) |
537 |
|
{ |
538 |
17627 |
toProcess.insert(toProcess.begin() + index, curr.begin(), curr.end()); |
539 |
|
} |
540 |
752692 |
else if (k == OR && !pol) |
541 |
|
{ |
542 |
576 |
std::vector<Node> negc; |
543 |
3639 |
for (TNode c : currAtom) |
544 |
|
{ |
545 |
6702 |
Node cneg = c.negate(); |
546 |
3351 |
negc.push_back(cneg); |
547 |
|
// ensure ref counted |
548 |
3351 |
keep.push_back(cneg); |
549 |
|
} |
550 |
576 |
toProcess.insert(toProcess.begin() + index, negc.begin(), negc.end()); |
551 |
|
} |
552 |
752404 |
else if (!isTheoryAtom(currAtom)) |
553 |
|
{ |
554 |
581371 |
al.addAssertion(curr); |
555 |
|
// take stats |
556 |
581371 |
sizeStat.maxAssign(al.size()); |
557 |
|
} |
558 |
|
else |
559 |
|
{ |
560 |
|
// we skip (top-level) theory literals, since these are always propagated |
561 |
|
// NOTE: skolem definitions that are always relevant should be added to |
562 |
|
// assertions, for uniformity via a method notifyJustified(currAtom) |
563 |
|
} |
564 |
|
} |
565 |
|
// clear since toProcess may contain nodes with 0 ref count after returning |
566 |
|
// otherwise |
567 |
6324984 |
toProcess.clear(); |
568 |
6324984 |
} |
569 |
|
|
570 |
7891377 |
bool JustificationStrategy::refreshCurrentAssertion() |
571 |
|
{ |
572 |
7891377 |
Trace("jh-process") << "refreshCurrentAssertion" << std::endl; |
573 |
|
// if we already have a current assertion, nothing to be done |
574 |
15782754 |
TNode curr = d_stack.getCurrentAssertion(); |
575 |
7891377 |
if (!curr.isNull()) |
576 |
|
{ |
577 |
1249278 |
if (curr != d_currUnderStatus && !d_currUnderStatus.isNull()) |
578 |
|
{ |
579 |
38537 |
++(d_stats.d_numStatusBacktrack); |
580 |
38537 |
d_assertions.notifyStatus(d_currUnderStatus, DecisionStatus::BACKTRACK); |
581 |
|
// we've backtracked to another assertion which may be partially |
582 |
|
// processed. don't track its status? |
583 |
38537 |
d_currUnderStatus = Node::null(); |
584 |
|
// NOTE: could reset the stack here to preserve other invariants, |
585 |
|
// currently we do not. |
586 |
|
} |
587 |
1249278 |
return true; |
588 |
|
} |
589 |
6642099 |
bool skFirst = (d_jhSkMode != options::JutificationSkolemMode::LAST); |
590 |
|
// use main assertions first |
591 |
6642099 |
if (refreshCurrentAssertionFromList(skFirst)) |
592 |
|
{ |
593 |
535982 |
return true; |
594 |
|
} |
595 |
|
// if satisfied all main assertions, use the skolem assertions, which may |
596 |
|
// fail |
597 |
6106117 |
return refreshCurrentAssertionFromList(!skFirst); |
598 |
|
} |
599 |
|
|
600 |
12748216 |
bool JustificationStrategy::refreshCurrentAssertionFromList(bool useSkolemList) |
601 |
|
{ |
602 |
12748216 |
AssertionList& al = useSkolemList ? d_skolemAssertions : d_assertions; |
603 |
12748216 |
bool doWatchStatus = !useSkolemList; |
604 |
12748216 |
d_currUnderStatus = Node::null(); |
605 |
25496432 |
TNode curr = al.getNextAssertion(); |
606 |
|
SatValue currValue; |
607 |
13709538 |
while (!curr.isNull()) |
608 |
|
{ |
609 |
7061597 |
Trace("jh-process") << "Check assertion " << curr << std::endl; |
610 |
|
// we never add theory literals to our assertions lists |
611 |
7061597 |
Assert(!isTheoryLiteral(curr)); |
612 |
7061597 |
currValue = lookupValue(curr); |
613 |
7061597 |
if (currValue == SAT_VALUE_UNKNOWN) |
614 |
|
{ |
615 |
|
// if not already justified, we reset the stack and push to it |
616 |
6580936 |
d_stack.reset(curr); |
617 |
6580936 |
d_lastDecisionLit = TNode::null(); |
618 |
|
// for activity |
619 |
6580936 |
if (doWatchStatus) |
620 |
|
{ |
621 |
|
// initially, mark that we have not found a decision in this |
622 |
6044954 |
d_currUnderStatus = d_stack.getCurrentAssertion(); |
623 |
6044954 |
d_currStatusDec = false; |
624 |
|
} |
625 |
6580936 |
return true; |
626 |
|
} |
627 |
|
// assertions should all be satisfied, otherwise we are in conflict |
628 |
480661 |
Assert(currValue == SAT_VALUE_TRUE); |
629 |
480661 |
if (doWatchStatus) |
630 |
|
{ |
631 |
|
// mark that we did not find a decision in it |
632 |
480397 |
++(d_stats.d_numStatusNoDecision); |
633 |
480397 |
d_assertions.notifyStatus(curr, DecisionStatus::NO_DECISION); |
634 |
|
} |
635 |
|
// already justified, immediately skip |
636 |
480661 |
curr = al.getNextAssertion(); |
637 |
|
} |
638 |
6167280 |
return false; |
639 |
|
} |
640 |
|
|
641 |
7061597 |
bool JustificationStrategy::isTheoryLiteral(TNode n) |
642 |
|
{ |
643 |
7061597 |
return isTheoryAtom(n.getKind() == NOT ? n[0] : n); |
644 |
|
} |
645 |
|
|
646 |
59462804 |
bool JustificationStrategy::isTheoryAtom(TNode n) |
647 |
|
{ |
648 |
59462804 |
Kind k = n.getKind(); |
649 |
59462804 |
Assert(k != NOT); |
650 |
50135272 |
return k != AND && k != OR && k != IMPLIES && k != ITE && k != XOR |
651 |
74382970 |
&& (k != EQUAL || !n[0].getType().isBoolean()); |
652 |
|
} |
653 |
|
|
654 |
|
} // namespace decision |
655 |
29340 |
} // namespace cvc5 |