1 |
|
/****************************************************************************** |
2 |
|
* Top contributors (to current version): |
3 |
|
* Kshitij Bansal, Andres Noetzli, Gereon Kremer |
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 |
|
* Justification heuristic for decision making |
14 |
|
* |
15 |
|
* A ATGP-inspired justification-based decision heuristic. This code is based |
16 |
|
* on the CVC3 implementation of the same heuristic -- note below. |
17 |
|
* |
18 |
|
* It needs access to the simplified but non-clausal formula. |
19 |
|
*/ |
20 |
|
#include "justification_heuristic.h" |
21 |
|
|
22 |
|
#include "decision/decision_attributes.h" |
23 |
|
#include "decision/decision_engine_old.h" |
24 |
|
#include "expr/kind.h" |
25 |
|
#include "expr/node_manager.h" |
26 |
|
#include "options/decision_options.h" |
27 |
|
#include "smt/smt_statistics_registry.h" |
28 |
|
#include "smt/term_formula_removal.h" |
29 |
|
#include "theory/rewriter.h" |
30 |
|
#include "util/random.h" |
31 |
|
|
32 |
|
using namespace cvc5::prop; |
33 |
|
|
34 |
|
namespace cvc5 { |
35 |
|
namespace decision { |
36 |
|
|
37 |
7149 |
JustificationHeuristic::JustificationHeuristic(DecisionEngineOld* de, |
38 |
|
context::UserContext* uc, |
39 |
7149 |
context::Context* c) |
40 |
|
: ITEDecisionStrategy(de, c), |
41 |
|
d_justified(c), |
42 |
|
d_exploredThreshold(c), |
43 |
|
d_prvsIndex(c, 0), |
44 |
|
d_threshPrvsIndex(c, 0), |
45 |
|
d_helpfulness( |
46 |
14298 |
smtStatisticsRegistry().registerInt("decision::jh::helpfulness")), |
47 |
14298 |
d_giveup(smtStatisticsRegistry().registerInt("decision::jh::giveup")), |
48 |
14298 |
d_timestat(smtStatisticsRegistry().registerTimer("decision::jh::time")), |
49 |
|
d_assertions(uc), |
50 |
|
d_skolemAssertions(uc), |
51 |
|
d_skolemCache(uc), |
52 |
|
d_visited(), |
53 |
|
d_visitedComputeSkolems(), |
54 |
|
d_curDecision(), |
55 |
|
d_curThreshold(0), |
56 |
|
d_childCache(uc), |
57 |
|
d_weightCache(uc), |
58 |
35745 |
d_startIndexCache(c) |
59 |
|
{ |
60 |
7149 |
Trace("decision") << "Justification heuristic enabled" << std::endl; |
61 |
7149 |
} |
62 |
|
|
63 |
14298 |
JustificationHeuristic::~JustificationHeuristic() {} |
64 |
|
|
65 |
684271 |
cvc5::prop::SatLiteral JustificationHeuristic::getNext(bool& stopSearch) |
66 |
|
{ |
67 |
1442929 |
if(options::decisionThreshold() > 0) { |
68 |
|
bool stopSearchTmp = false; |
69 |
|
prop::SatLiteral lit = |
70 |
|
getNextThresh(stopSearchTmp, options::decisionThreshold()); |
71 |
|
if (lit != prop::undefSatLiteral) |
72 |
|
{ |
73 |
|
Assert(stopSearchTmp == false); |
74 |
|
return lit; |
75 |
|
} |
76 |
|
Assert(stopSearchTmp == true); |
77 |
|
} |
78 |
684271 |
return getNextThresh(stopSearch, 0); |
79 |
|
} |
80 |
|
|
81 |
684271 |
cvc5::prop::SatLiteral JustificationHeuristic::getNextThresh( |
82 |
|
bool& stopSearch, DecisionWeight threshold) |
83 |
|
{ |
84 |
684271 |
Trace("decision") << "JustificationHeuristic::getNextThresh(stopSearch, "<<threshold<<")" << std::endl; |
85 |
1368542 |
TimerStat::CodeTimer codeTimer(d_timestat); |
86 |
|
|
87 |
684271 |
d_visited.clear(); |
88 |
684271 |
d_curThreshold = threshold; |
89 |
|
|
90 |
684271 |
if(Trace.isOn("justified")) { |
91 |
|
for(JustifiedSet::key_iterator i = d_justified.key_begin(); |
92 |
|
i != d_justified.key_end(); ++i) { |
93 |
|
TNode n = *i; |
94 |
|
prop::SatLiteral l = d_decisionEngine->hasSatLiteral(n) |
95 |
|
? d_decisionEngine->getSatLiteral(n) |
96 |
|
: -1; |
97 |
|
prop::SatValue v = tryGetSatValue(n); |
98 |
|
Trace("justified") <<"{ "<<l<<"}" << n <<": "<<v << std::endl; |
99 |
|
} |
100 |
|
} |
101 |
|
|
102 |
7479860 |
for(unsigned i = getPrvsIndex(); i < d_assertions.size(); ++i) { |
103 |
7438332 |
Debug("decision") << "---" << std::endl << d_assertions[i] << std::endl; |
104 |
|
|
105 |
|
// Sanity check: if it was false, aren't we inconsistent? |
106 |
|
// Commenting out. See bug 374. In short, to do with how CNF stream works. |
107 |
|
// Assert( tryGetSatValue(d_assertions[i]) != SAT_VALUE_FALSE); |
108 |
|
|
109 |
7438332 |
prop::SatValue desiredVal = prop::SAT_VALUE_TRUE; |
110 |
7438332 |
prop::SatLiteral litDecision; |
111 |
|
|
112 |
7438332 |
litDecision = findSplitter(d_assertions[i], desiredVal); |
113 |
|
|
114 |
7438332 |
if (litDecision != prop::undefSatLiteral) |
115 |
|
{ |
116 |
642743 |
setPrvsIndex(i); |
117 |
642743 |
Trace("decision") << "jh: splitting on " << litDecision << std::endl; |
118 |
642743 |
++d_helpfulness; |
119 |
642743 |
return litDecision; |
120 |
|
} |
121 |
|
} |
122 |
|
|
123 |
41528 |
Trace("decision") << "jh: Nothing to split on " << std::endl; |
124 |
|
|
125 |
|
#if defined CVC5_DEBUG |
126 |
41528 |
bool alljustified = true; |
127 |
5471722 |
for(unsigned i = 0 ; i < d_assertions.size() && alljustified ; ++i) { |
128 |
10860388 |
TNode curass = d_assertions[i]; |
129 |
5602384 |
while(curass.getKind() == kind::NOT) |
130 |
86095 |
curass = curass[0]; |
131 |
5430194 |
alljustified &= checkJustified(curass); |
132 |
|
|
133 |
5430194 |
if(Debug.isOn("decision")) { |
134 |
|
if(!checkJustified(curass)) |
135 |
|
Debug("decision") << "****** Not justified [i="<<i<<"]: " |
136 |
|
<< d_assertions[i] << std::endl; |
137 |
|
} |
138 |
|
} |
139 |
41528 |
Assert(alljustified || d_curThreshold != 0); |
140 |
|
#endif |
141 |
|
|
142 |
|
// SAT solver can stop... |
143 |
41528 |
stopSearch = true; |
144 |
41528 |
if (d_curThreshold == 0) d_decisionEngine->setResult(prop::SAT_VALUE_TRUE); |
145 |
41528 |
return prop::undefSatLiteral; |
146 |
|
} |
147 |
|
|
148 |
205332 |
inline void computeXorIffDesiredValues(Kind k, |
149 |
|
prop::SatValue desiredVal, |
150 |
|
prop::SatValue& desiredVal1, |
151 |
|
prop::SatValue& desiredVal2) |
152 |
|
{ |
153 |
205332 |
Assert(k == kind::EQUAL || k == kind::XOR); |
154 |
|
|
155 |
205332 |
bool shouldInvert = |
156 |
173803 |
(desiredVal == prop::SAT_VALUE_TRUE && k == kind::EQUAL) |
157 |
248311 |
|| (desiredVal == prop::SAT_VALUE_FALSE && k == kind::XOR); |
158 |
|
|
159 |
205332 |
if (desiredVal1 == prop::SAT_VALUE_UNKNOWN |
160 |
36227 |
&& desiredVal2 == prop::SAT_VALUE_UNKNOWN) |
161 |
|
{ |
162 |
|
// CHOICE: pick one of them arbitarily |
163 |
30985 |
desiredVal1 = prop::SAT_VALUE_FALSE; |
164 |
|
} |
165 |
|
|
166 |
205332 |
if(desiredVal2 == SAT_VALUE_UNKNOWN) { |
167 |
37835 |
desiredVal2 = shouldInvert ? invertValue(desiredVal1) : desiredVal1; |
168 |
167497 |
} else if(desiredVal1 == SAT_VALUE_UNKNOWN) { |
169 |
5242 |
desiredVal1 = shouldInvert ? invertValue(desiredVal2) : desiredVal2; |
170 |
|
} |
171 |
205332 |
} |
172 |
|
|
173 |
261756 |
void JustificationHeuristic::addAssertion(TNode assertion) |
174 |
|
{ |
175 |
|
// Save all assertions locally, including the assertions generated by term |
176 |
|
// removal. We have to make sure that we assign a value to all the Boolean |
177 |
|
// term variables. To illustrate why this is, consider the case where we have |
178 |
|
// a single assertion |
179 |
|
// |
180 |
|
// (or (f a) (f b)) |
181 |
|
// |
182 |
|
// where `f` is a function `Bool -> Bool`. Given an assignment: |
183 |
|
// |
184 |
|
// (f a) -> true |
185 |
|
// (f b) -> false |
186 |
|
// a -> false |
187 |
|
// |
188 |
|
// UF will not complain and the justification heuristic considers the |
189 |
|
// assertion to be satisifed. However, we also have to make sure that we pick |
190 |
|
// a value for `b` that is not in conflict with the other assignments (we can |
191 |
|
// only choose `b` to be `true` otherwise the model is incorrect). |
192 |
261756 |
d_assertions.push_back(assertion); |
193 |
261756 |
} |
194 |
|
|
195 |
23206 |
void JustificationHeuristic::addSkolemDefinition(TNode lem, TNode skolem) |
196 |
|
{ |
197 |
46412 |
Trace("decision::jh::ite") |
198 |
23206 |
<< " jh-ite: " << skolem << " maps to " << lem << std::endl; |
199 |
23206 |
d_skolemAssertions[skolem] = lem; |
200 |
23206 |
} |
201 |
|
|
202 |
7438332 |
SatLiteral JustificationHeuristic::findSplitter(TNode node, |
203 |
|
SatValue desiredVal) |
204 |
|
{ |
205 |
7438332 |
d_curDecision = undefSatLiteral; |
206 |
7438332 |
findSplitterRec(node, desiredVal); |
207 |
7438332 |
return d_curDecision; |
208 |
|
} |
209 |
|
|
210 |
|
|
211 |
11120535 |
void JustificationHeuristic::setJustified(TNode n) |
212 |
|
{ |
213 |
11120535 |
d_justified.insert(n); |
214 |
11120535 |
} |
215 |
|
|
216 |
24389228 |
bool JustificationHeuristic::checkJustified(TNode n) |
217 |
|
{ |
218 |
24389228 |
return d_justified.find(n) != d_justified.end(); |
219 |
|
} |
220 |
|
|
221 |
13404944 |
DecisionWeight JustificationHeuristic::getExploredThreshold(TNode n) |
222 |
|
{ |
223 |
26809888 |
return d_exploredThreshold.find(n) == d_exploredThreshold.end() |
224 |
13404944 |
? std::numeric_limits<DecisionWeight>::max() |
225 |
26809888 |
: d_exploredThreshold[n]; |
226 |
|
} |
227 |
|
|
228 |
|
void JustificationHeuristic::setExploredThreshold(TNode n) |
229 |
|
{ |
230 |
|
d_exploredThreshold[n] = d_curThreshold; |
231 |
|
} |
232 |
|
|
233 |
684271 |
int JustificationHeuristic::getPrvsIndex() |
234 |
|
{ |
235 |
684271 |
if(d_curThreshold == 0) |
236 |
684271 |
return d_prvsIndex; |
237 |
|
else |
238 |
|
return d_threshPrvsIndex; |
239 |
|
} |
240 |
|
|
241 |
642743 |
void JustificationHeuristic::setPrvsIndex(int prvsIndex) |
242 |
|
{ |
243 |
642743 |
if(d_curThreshold == 0) |
244 |
642743 |
d_prvsIndex = prvsIndex; |
245 |
|
else |
246 |
|
d_threshPrvsIndex = prvsIndex; |
247 |
642743 |
} |
248 |
|
|
249 |
|
DecisionWeight JustificationHeuristic::getWeightPolarized(TNode n, SatValue satValue) |
250 |
|
{ |
251 |
|
Assert(satValue == SAT_VALUE_TRUE || satValue == SAT_VALUE_FALSE); |
252 |
|
return getWeightPolarized(n, satValue == SAT_VALUE_TRUE); |
253 |
|
} |
254 |
|
|
255 |
|
DecisionWeight JustificationHeuristic::getWeightPolarized(TNode n, bool polarity) |
256 |
|
{ |
257 |
|
if (options::decisionWeightInternal() |
258 |
|
!= options::DecisionWeightInternal::USR1) |
259 |
|
{ |
260 |
|
return getWeight(n); |
261 |
|
} |
262 |
|
|
263 |
|
if(d_weightCache.find(n) == d_weightCache.end()) { |
264 |
|
Kind k = n.getKind(); |
265 |
|
theory::TheoryId tId = theory::kindToTheoryId(k); |
266 |
|
DecisionWeight dW1, dW2; |
267 |
|
if(tId != theory::THEORY_BOOL) { |
268 |
|
dW1 = dW2 = getWeight(n); |
269 |
|
} else { |
270 |
|
|
271 |
|
if(k == kind::OR) { |
272 |
|
dW1 = std::numeric_limits<DecisionWeight>::max(), dW2 = 0; |
273 |
|
for(TNode::iterator i=n.begin(); i != n.end(); ++i) { |
274 |
|
dW1 = std::min(dW1, getWeightPolarized(*i, true)); |
275 |
|
dW2 = std::max(dW2, getWeightPolarized(*i, false)); |
276 |
|
} |
277 |
|
} else if(k == kind::AND) { |
278 |
|
dW1 = 0, dW2 = std::numeric_limits<DecisionWeight>::max(); |
279 |
|
for(TNode::iterator i=n.begin(); i != n.end(); ++i) { |
280 |
|
dW1 = std::max(dW1, getWeightPolarized(*i, true)); |
281 |
|
dW2 = std::min(dW2, getWeightPolarized(*i, false)); |
282 |
|
} |
283 |
|
} else if(k == kind::IMPLIES) { |
284 |
|
dW1 = std::min(getWeightPolarized(n[0], false), |
285 |
|
getWeightPolarized(n[1], true)); |
286 |
|
dW2 = std::max(getWeightPolarized(n[0], true), |
287 |
|
getWeightPolarized(n[1], false)); |
288 |
|
} else if(k == kind::NOT) { |
289 |
|
dW1 = getWeightPolarized(n[0], false); |
290 |
|
dW2 = getWeightPolarized(n[0], true); |
291 |
|
} else { |
292 |
|
dW1 = 0; |
293 |
|
for(TNode::iterator i=n.begin(); i != n.end(); ++i) { |
294 |
|
dW1 = std::max(dW1, getWeightPolarized(*i, true)); |
295 |
|
dW1 = std::max(dW1, getWeightPolarized(*i, false)); |
296 |
|
} |
297 |
|
dW2 = dW1; |
298 |
|
} |
299 |
|
|
300 |
|
} |
301 |
28242 |
d_weightCache[n] = std::make_pair(dW1, dW2); |
302 |
|
} |
303 |
|
return polarity ? d_weightCache[n].get().first : d_weightCache[n].get().second; |
304 |
|
} |
305 |
|
|
306 |
|
DecisionWeight JustificationHeuristic::getWeight(TNode n) { |
307 |
|
if(!n.hasAttribute(DecisionWeightAttr()) ) { |
308 |
|
options::DecisionWeightInternal combiningFn = |
309 |
|
options::decisionWeightInternal(); |
310 |
|
|
311 |
|
if (combiningFn == options::DecisionWeightInternal::OFF |
312 |
|
|| n.getNumChildren() == 0) |
313 |
|
{ |
314 |
|
if (options::decisionRandomWeight() != 0) |
315 |
|
{ |
316 |
|
n.setAttribute(DecisionWeightAttr(), |
317 |
|
Random::getRandom().pick(0, options::decisionRandomWeight()-1)); |
318 |
|
} |
319 |
|
} |
320 |
|
else if (combiningFn == options::DecisionWeightInternal::MAX) |
321 |
|
{ |
322 |
|
DecisionWeight dW = 0; |
323 |
|
for (TNode::iterator i = n.begin(); i != n.end(); ++i) |
324 |
|
dW = std::max(dW, getWeight(*i)); |
325 |
|
n.setAttribute(DecisionWeightAttr(), dW); |
326 |
|
} |
327 |
|
else if (combiningFn == options::DecisionWeightInternal::SUM |
328 |
|
|| combiningFn == options::DecisionWeightInternal::USR1) |
329 |
|
{ |
330 |
|
DecisionWeight dW = 0; |
331 |
|
for (TNode::iterator i = n.begin(); i != n.end(); ++i) |
332 |
|
dW = std::max(dW, getWeight(*i)); |
333 |
|
n.setAttribute(DecisionWeightAttr(), dW); |
334 |
|
} |
335 |
|
else |
336 |
|
{ |
337 |
|
Unreachable(); |
338 |
|
} |
339 |
|
} |
340 |
|
return n.getAttribute(DecisionWeightAttr()); |
341 |
|
} |
342 |
|
|
343 |
|
typedef std::vector<TNode> ChildList; |
344 |
10689943 |
TNode JustificationHeuristic::getChildByWeight(TNode n, int i, bool polarity) { |
345 |
23082862 |
if(options::decisionUseWeight()) { |
346 |
|
// TODO: Optimize storing & access |
347 |
|
if(d_childCache.find(n) == d_childCache.end()) { |
348 |
|
ChildList list0(n.begin(), n.end()), list1(n.begin(), n.end()); |
349 |
|
std::sort(list0.begin(), list0.end(), JustificationHeuristic::myCompareClass(this,false)); |
350 |
|
std::sort(list1.begin(), list1.end(), JustificationHeuristic::myCompareClass(this,true)); |
351 |
|
d_childCache[n] = make_pair(list0, list1); |
352 |
|
} |
353 |
|
return polarity ? d_childCache[n].get().second[i] : d_childCache[n].get().first[i]; |
354 |
|
} else { |
355 |
10689943 |
return n[i]; |
356 |
|
} |
357 |
|
} |
358 |
|
|
359 |
25032548 |
SatValue JustificationHeuristic::tryGetSatValue(Node n) |
360 |
|
{ |
361 |
25032548 |
Debug("decision") << " " << n << " has sat value " << " "; |
362 |
25032548 |
if(d_decisionEngine->hasSatLiteral(n) ) { |
363 |
19508389 |
Debug("decision") << d_decisionEngine->getSatValue(n) << std::endl; |
364 |
19508389 |
return d_decisionEngine->getSatValue(n); |
365 |
|
} else { |
366 |
5524159 |
Debug("decision") << "NO SAT LITERAL" << std::endl; |
367 |
5524159 |
return SAT_VALUE_UNKNOWN; |
368 |
|
}//end of else |
369 |
|
} |
370 |
|
|
371 |
5178092 |
JustificationHeuristic::SkolemList JustificationHeuristic::getSkolems(TNode n) |
372 |
|
{ |
373 |
5178092 |
SkolemCache::iterator it = d_skolemCache.find(n); |
374 |
5178092 |
if (it != d_skolemCache.end()) |
375 |
|
{ |
376 |
4898891 |
return (*it).second; |
377 |
|
} |
378 |
|
else |
379 |
|
{ |
380 |
|
// Compute the list of Skolems |
381 |
|
// TODO: optimize by avoiding multiple lookup for d_skolemCache[n] |
382 |
279201 |
d_visitedComputeSkolems.clear(); |
383 |
558402 |
SkolemList ilist; |
384 |
279201 |
computeSkolems(n, ilist); |
385 |
279201 |
d_skolemCache.insert(n, ilist); |
386 |
279201 |
return ilist; |
387 |
|
} |
388 |
|
} |
389 |
|
|
390 |
1962521 |
void JustificationHeuristic::computeSkolems(TNode n, SkolemList& l) |
391 |
|
{ |
392 |
1962521 |
Trace("decision::jh::skolems") << " computeSkolems( " << n << ", &l)\n"; |
393 |
1962521 |
d_visitedComputeSkolems.insert(n); |
394 |
4071946 |
for(unsigned i=0; i<n.getNumChildren(); ++i) { |
395 |
2109425 |
SkolemMap::iterator it2 = d_skolemAssertions.find(n[i]); |
396 |
2109425 |
if (it2 != d_skolemAssertions.end()) |
397 |
|
{ |
398 |
64976 |
l.push_back(std::make_pair(n[i], (*it2).second)); |
399 |
64976 |
Assert(n[i].getNumChildren() == 0); |
400 |
|
} |
401 |
2109425 |
if (d_visitedComputeSkolems.find(n[i]) == d_visitedComputeSkolems.end()) |
402 |
|
{ |
403 |
1683320 |
computeSkolems(n[i], l); |
404 |
|
} |
405 |
|
} |
406 |
1962521 |
} |
407 |
|
|
408 |
|
JustificationHeuristic::SearchResult |
409 |
18959034 |
JustificationHeuristic::findSplitterRec(TNode node, SatValue desiredVal) |
410 |
|
{ |
411 |
|
/** |
412 |
|
* Main idea |
413 |
|
* |
414 |
|
* Given a boolean formula "node", the goal is to try to make it |
415 |
|
* evaluate to "desiredVal" (true/false). for instance if "node" is a OR |
416 |
|
* formula we want to make it evaluate to true, we'd like one of the |
417 |
|
* children to be true. this is done recursively. |
418 |
|
*/ |
419 |
|
|
420 |
37918068 |
Trace("decision::jh") |
421 |
18959034 |
<< "findSplitterRec(" << node << ", " |
422 |
18959034 |
<< desiredVal << ", .. )" << std::endl; |
423 |
|
|
424 |
|
/* Handle NOT as a special case */ |
425 |
28422054 |
while (node.getKind() == kind::NOT) { |
426 |
4731510 |
desiredVal = invertValue(desiredVal); |
427 |
4731510 |
node = node[0]; |
428 |
|
} |
429 |
|
|
430 |
|
/* Base case */ |
431 |
18959034 |
if (checkJustified(node)) { |
432 |
5554090 |
Debug("decision::jh") << " justified, returning" << std::endl; |
433 |
5554090 |
return NO_SPLITTER; |
434 |
|
} |
435 |
13404944 |
if (getExploredThreshold(node) < d_curThreshold) { |
436 |
|
Debug("decision::jh") << " explored, returning" << std::endl; |
437 |
|
Assert(d_curThreshold != 0); |
438 |
|
return DONT_KNOW; |
439 |
|
} |
440 |
|
|
441 |
|
#if defined CVC5_ASSERTIONS || defined CVC5_DEBUG |
442 |
|
// We don't always have a sat literal, so remember that. Will need |
443 |
|
// it for some assertions we make. |
444 |
13404944 |
bool litPresent = d_decisionEngine->hasSatLiteral(node); |
445 |
13404944 |
if(Debug.isOn("decision")) { |
446 |
|
if(!litPresent) { |
447 |
|
Debug("decision") << "no sat literal for this node" << std::endl; |
448 |
|
} |
449 |
|
} |
450 |
|
#endif |
451 |
|
|
452 |
|
// Get value of sat literal for the node, if there is one |
453 |
13404944 |
SatValue litVal = tryGetSatValue(node); |
454 |
|
|
455 |
|
/* You'd better know what you want */ |
456 |
13404944 |
Assert(desiredVal != SAT_VALUE_UNKNOWN) << "expected known value"; |
457 |
|
|
458 |
|
/* Good luck, hope you can get what you want */ |
459 |
|
// See bug 374 |
460 |
|
// Assert(litVal == desiredVal || litVal == SAT_VALUE_UNKNOWN) << |
461 |
|
// "invariant violated"; |
462 |
|
|
463 |
|
/* What type of node is this */ |
464 |
13404944 |
Kind k = node.getKind(); |
465 |
13404944 |
theory::TheoryId tId = theory::kindToTheoryId(k); |
466 |
|
bool isAtom = |
467 |
31975074 |
(k == kind::BOOLEAN_TERM_VARIABLE ) || |
468 |
18743825 |
( (tId != theory::THEORY_BOOL) && |
469 |
21656855 |
(k != kind::EQUAL || (!node[0].getType().isBoolean())) ); |
470 |
|
|
471 |
|
/* Some debugging stuff */ |
472 |
26809888 |
Debug("decision::jh") << "kind = " << k << std::endl |
473 |
13404944 |
<< "theoryId = " << tId << std::endl |
474 |
13404944 |
<< "node = " << node << std::endl |
475 |
13404944 |
<< "litVal = " << litVal << std::endl; |
476 |
|
|
477 |
|
/** |
478 |
|
* If not in theory of booleans, check if this is something to split-on. |
479 |
|
*/ |
480 |
13404944 |
if(isAtom) { |
481 |
|
// if node has embedded skolems due to term removal, resolve that first |
482 |
5178092 |
if (handleEmbeddedSkolems(node) == FOUND_SPLITTER) return FOUND_SPLITTER; |
483 |
|
|
484 |
5064701 |
if(litVal != SAT_VALUE_UNKNOWN) { |
485 |
4421958 |
Assert(litVal == desiredVal); |
486 |
4421958 |
setJustified(node); |
487 |
4421958 |
return NO_SPLITTER; |
488 |
|
} |
489 |
|
else { |
490 |
642743 |
Assert(d_decisionEngine->hasSatLiteral(node)); |
491 |
642743 |
if(d_curThreshold != 0 && getWeightPolarized(node, desiredVal) >= d_curThreshold) |
492 |
|
return DONT_KNOW; |
493 |
|
SatVariable v = |
494 |
642743 |
d_decisionEngine->getSatLiteral(node).getSatVariable(); |
495 |
642743 |
d_curDecision = SatLiteral(v, /* negated = */ desiredVal != SAT_VALUE_TRUE ); |
496 |
1285486 |
Trace("decision-node") << "[decision-node] requesting split on " << d_curDecision |
497 |
642743 |
<< ", node: " << node |
498 |
642743 |
<< ", polarity: " << (desiredVal == SAT_VALUE_TRUE ? "true" : "false") << std::endl; |
499 |
642743 |
return FOUND_SPLITTER; |
500 |
|
} |
501 |
|
} |
502 |
|
|
503 |
8226852 |
SearchResult ret = NO_SPLITTER; |
504 |
8226852 |
switch (k) { |
505 |
|
|
506 |
21046 |
case kind::CONST_BOOLEAN: |
507 |
21046 |
Assert(node.getConst<bool>() == false || desiredVal == SAT_VALUE_TRUE); |
508 |
21046 |
Assert(node.getConst<bool>() == true || desiredVal == SAT_VALUE_FALSE); |
509 |
21046 |
break; |
510 |
|
|
511 |
1221708 |
case kind::AND: |
512 |
1221708 |
if (desiredVal == SAT_VALUE_FALSE) |
513 |
587744 |
ret = handleAndOrEasy(node, desiredVal); |
514 |
|
else |
515 |
633964 |
ret = handleAndOrHard(node, desiredVal); |
516 |
1221708 |
break; |
517 |
|
|
518 |
4934258 |
case kind::OR: |
519 |
4934258 |
if (desiredVal == SAT_VALUE_FALSE) |
520 |
87231 |
ret = handleAndOrHard(node, desiredVal); |
521 |
|
else |
522 |
4847027 |
ret = handleAndOrEasy(node, desiredVal); |
523 |
4934258 |
break; |
524 |
|
|
525 |
1328733 |
case kind::IMPLIES: |
526 |
1328733 |
if (desiredVal == SAT_VALUE_FALSE) |
527 |
262164 |
ret = handleBinaryHard(node[0], SAT_VALUE_TRUE, |
528 |
|
node[1], SAT_VALUE_FALSE); |
529 |
|
|
530 |
|
else |
531 |
1066569 |
ret = handleBinaryEasy(node[0], SAT_VALUE_FALSE, |
532 |
|
node[1], SAT_VALUE_TRUE); |
533 |
1328733 |
break; |
534 |
|
|
535 |
205332 |
case kind::XOR: |
536 |
|
case kind::EQUAL: { |
537 |
205332 |
SatValue desiredVal1 = tryGetSatValue(node[0]); |
538 |
205332 |
SatValue desiredVal2 = tryGetSatValue(node[1]); |
539 |
205332 |
computeXorIffDesiredValues(k, desiredVal, desiredVal1, desiredVal2); |
540 |
205332 |
ret = handleBinaryHard(node[0], desiredVal1, |
541 |
|
node[1], desiredVal2); |
542 |
205332 |
break; |
543 |
|
} |
544 |
|
|
545 |
515775 |
case kind::ITE: |
546 |
515775 |
ret = handleITE(node, desiredVal); |
547 |
515775 |
break; |
548 |
|
|
549 |
|
default: Assert(false) << "Unexpected Boolean operator"; break; |
550 |
|
}//end of switch(k) |
551 |
|
|
552 |
8226852 |
if(ret == DONT_KNOW) { |
553 |
|
setExploredThreshold(node); |
554 |
|
} |
555 |
|
|
556 |
8226852 |
if(ret == NO_SPLITTER) { |
557 |
6698577 |
Assert(litPresent == false || litVal == desiredVal) |
558 |
|
<< "Output should be justified"; |
559 |
6698577 |
setJustified(node); |
560 |
|
} |
561 |
8226852 |
return ret; |
562 |
|
}/* findRecSplit method */ |
563 |
|
|
564 |
|
JustificationHeuristic::SearchResult |
565 |
5434771 |
JustificationHeuristic::handleAndOrEasy(TNode node, SatValue desiredVal) |
566 |
|
{ |
567 |
5434771 |
Assert((node.getKind() == kind::AND and desiredVal == SAT_VALUE_FALSE) |
568 |
|
or (node.getKind() == kind::OR and desiredVal == SAT_VALUE_TRUE)); |
569 |
|
|
570 |
5434771 |
int numChildren = node.getNumChildren(); |
571 |
5434771 |
SatValue desiredValInverted = invertValue(desiredVal); |
572 |
8549780 |
for(int i = 0; i < numChildren; ++i) { |
573 |
11664789 |
TNode curNode = getChildByWeight(node, i, desiredVal); |
574 |
8549780 |
if ( tryGetSatValue(curNode) != desiredValInverted ) { |
575 |
5434771 |
SearchResult ret = findSplitterRec(curNode, desiredVal); |
576 |
5434771 |
if(ret != DONT_KNOW) { |
577 |
5434771 |
return ret; |
578 |
|
} |
579 |
|
} |
580 |
|
} |
581 |
|
Assert(d_curThreshold != 0) << "handleAndOrEasy: No controlling input found"; |
582 |
|
return DONT_KNOW; |
583 |
|
} |
584 |
|
|
585 |
721195 |
int JustificationHeuristic::getStartIndex(TNode node) { |
586 |
721195 |
return d_startIndexCache[node]; |
587 |
|
} |
588 |
74307 |
void JustificationHeuristic::saveStartIndex(TNode node, int val) { |
589 |
74307 |
d_startIndexCache[node] = val; |
590 |
74307 |
} |
591 |
|
|
592 |
721195 |
JustificationHeuristic::SearchResult JustificationHeuristic::handleAndOrHard(TNode node, |
593 |
|
SatValue desiredVal) { |
594 |
721195 |
Assert((node.getKind() == kind::AND and desiredVal == SAT_VALUE_TRUE) |
595 |
|
or (node.getKind() == kind::OR and desiredVal == SAT_VALUE_FALSE)); |
596 |
|
|
597 |
721195 |
int numChildren = node.getNumChildren(); |
598 |
721195 |
bool noSplitter = true; |
599 |
721195 |
int i_st = getStartIndex(node); |
600 |
2589684 |
for(int i = i_st; i < numChildren; ++i) { |
601 |
4008652 |
TNode curNode = getChildByWeight(node, i, desiredVal); |
602 |
2140163 |
SearchResult ret = findSplitterRec(curNode, desiredVal); |
603 |
2140163 |
if (ret == FOUND_SPLITTER) { |
604 |
271674 |
if(i != i_st) saveStartIndex(node, i); |
605 |
271674 |
return FOUND_SPLITTER; |
606 |
|
} |
607 |
1868489 |
noSplitter = noSplitter && (ret == NO_SPLITTER); |
608 |
|
} |
609 |
449521 |
return noSplitter ? NO_SPLITTER : DONT_KNOW; |
610 |
|
} |
611 |
|
|
612 |
1066569 |
JustificationHeuristic::SearchResult JustificationHeuristic::handleBinaryEasy(TNode node1, |
613 |
|
SatValue desiredVal1, |
614 |
|
TNode node2, |
615 |
|
SatValue desiredVal2) |
616 |
|
{ |
617 |
2133138 |
if(options::decisionUseWeight() && |
618 |
1066569 |
getWeightPolarized(node1, desiredVal1) > getWeightPolarized(node2, desiredVal2)) { |
619 |
|
std::swap(node1, node2); |
620 |
|
std::swap(desiredVal1, desiredVal2); |
621 |
|
} |
622 |
|
|
623 |
1066569 |
if ( tryGetSatValue(node1) != invertValue(desiredVal1) ) { |
624 |
183577 |
SearchResult ret = findSplitterRec(node1, desiredVal1); |
625 |
183577 |
if(ret != DONT_KNOW) |
626 |
183577 |
return ret; |
627 |
|
} |
628 |
882992 |
if ( tryGetSatValue(node2) != invertValue(desiredVal2) ) { |
629 |
882992 |
SearchResult ret = findSplitterRec(node2, desiredVal2); |
630 |
882992 |
if(ret != DONT_KNOW) |
631 |
882992 |
return ret; |
632 |
|
} |
633 |
|
Assert(d_curThreshold != 0) << "handleBinaryEasy: No controlling input found"; |
634 |
|
return DONT_KNOW; |
635 |
|
} |
636 |
|
|
637 |
467496 |
JustificationHeuristic::SearchResult JustificationHeuristic::handleBinaryHard(TNode node1, |
638 |
|
SatValue desiredVal1, |
639 |
|
TNode node2, |
640 |
|
SatValue desiredVal2) |
641 |
|
{ |
642 |
934992 |
if(options::decisionUseWeight() && |
643 |
467496 |
getWeightPolarized(node1, desiredVal1) > getWeightPolarized(node2, desiredVal2)) { |
644 |
|
std::swap(node1, node2); |
645 |
|
std::swap(desiredVal1, desiredVal2); |
646 |
|
} |
647 |
|
|
648 |
467496 |
bool noSplitter = true; |
649 |
|
SearchResult ret; |
650 |
|
|
651 |
467496 |
ret = findSplitterRec(node1, desiredVal1); |
652 |
467496 |
if (ret == FOUND_SPLITTER) |
653 |
49123 |
return FOUND_SPLITTER; |
654 |
418373 |
noSplitter &= (ret == NO_SPLITTER); |
655 |
|
|
656 |
418373 |
ret = findSplitterRec(node2, desiredVal2); |
657 |
418373 |
if (ret == FOUND_SPLITTER) |
658 |
239311 |
return FOUND_SPLITTER; |
659 |
179062 |
noSplitter &= (ret == NO_SPLITTER); |
660 |
|
|
661 |
179062 |
return noSplitter ? NO_SPLITTER : DONT_KNOW; |
662 |
|
} |
663 |
|
|
664 |
515775 |
JustificationHeuristic::SearchResult JustificationHeuristic::handleITE(TNode node, SatValue desiredVal) |
665 |
|
{ |
666 |
1031550 |
Debug("decision::jh") << " handleITE (" << node << ", " |
667 |
515775 |
<< desiredVal << std::endl; |
668 |
|
|
669 |
|
//[0]: if, [1]: then, [2]: else |
670 |
515775 |
SatValue ifVal = tryGetSatValue(node[0]); |
671 |
515775 |
if (ifVal == SAT_VALUE_UNKNOWN) { |
672 |
100912 |
SatValue trueChildVal = tryGetSatValue(node[1]); |
673 |
100912 |
SatValue falseChildVal = tryGetSatValue(node[2]); |
674 |
|
SatValue ifDesiredVal; |
675 |
|
|
676 |
100912 |
if(trueChildVal == desiredVal || falseChildVal == invertValue(desiredVal)) { |
677 |
3751 |
ifDesiredVal = SAT_VALUE_TRUE; |
678 |
290719 |
} else if(trueChildVal == invertValue(desiredVal) || |
679 |
291483 |
falseChildVal == desiredVal || |
680 |
94524 |
(options::decisionUseWeight() && |
681 |
97161 |
getWeightPolarized(node[1], true) > getWeightPolarized(node[2], false)) |
682 |
|
) { |
683 |
2637 |
ifDesiredVal = SAT_VALUE_FALSE; |
684 |
|
} else { |
685 |
94524 |
ifDesiredVal = SAT_VALUE_TRUE; |
686 |
|
} |
687 |
|
|
688 |
100912 |
if(findSplitterRec(node[0], ifDesiredVal) == FOUND_SPLITTER) |
689 |
100912 |
return FOUND_SPLITTER; |
690 |
|
|
691 |
|
Assert(d_curThreshold != 0) << "No controlling input found (6)"; |
692 |
|
return DONT_KNOW; |
693 |
|
} else { |
694 |
|
// Try to justify 'if' |
695 |
414863 |
if(findSplitterRec(node[0], ifVal) == FOUND_SPLITTER) |
696 |
18380 |
return FOUND_SPLITTER; |
697 |
|
|
698 |
|
// If that was successful, we need to go into only one of 'then' |
699 |
|
// or 'else' |
700 |
396483 |
int ch = (ifVal == SAT_VALUE_TRUE) ? 1 : 2; |
701 |
|
|
702 |
396483 |
if( findSplitterRec(node[ch], desiredVal) == FOUND_SPLITTER ) { |
703 |
23597 |
return FOUND_SPLITTER; |
704 |
|
} |
705 |
|
|
706 |
372886 |
return NO_SPLITTER; |
707 |
|
}// else (...ifVal...) |
708 |
|
} |
709 |
|
|
710 |
|
JustificationHeuristic::SearchResult |
711 |
5178092 |
JustificationHeuristic::handleEmbeddedSkolems(TNode node) |
712 |
|
{ |
713 |
10356184 |
const SkolemList l = getSkolems(node); |
714 |
5178092 |
Trace("decision::jh::skolems") << " skolems size = " << l.size() << std::endl; |
715 |
|
|
716 |
5178092 |
bool noSplitter = true; |
717 |
6427836 |
for (SkolemList::const_iterator i = l.begin(); i != l.end(); ++i) |
718 |
|
{ |
719 |
1363135 |
if(d_visited.find((*i).first) == d_visited.end()) { |
720 |
1081072 |
d_visited.insert((*i).first); |
721 |
1081072 |
SearchResult ret = findSplitterRec((*i).second, SAT_VALUE_TRUE); |
722 |
1081072 |
if (ret == FOUND_SPLITTER) |
723 |
113391 |
return FOUND_SPLITTER; |
724 |
967681 |
noSplitter = noSplitter && (ret == NO_SPLITTER); |
725 |
967681 |
d_visited.erase((*i).first); |
726 |
|
} |
727 |
|
} |
728 |
5064701 |
return noSplitter ? NO_SPLITTER : DONT_KNOW; |
729 |
|
} |
730 |
|
|
731 |
|
} /* namespace decision */ |
732 |
13179768 |
} // namespace cvc5 |