GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/decision/justification_heuristic.cpp Lines: 2 386 0.5 %
Date: 2021-11-07 Branches: 2 1509 0.1 %

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