GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/bv/bitblast/lazy_bitblaster.cpp Lines: 1 304 0.3 %
Date: 2021-09-10 Branches: 2 1089 0.2 %

Line Exec Source
1
/******************************************************************************
2
 * Top contributors (to current version):
3
 *   Liana Hadarean, Aina Niemetz, Mathias Preiner
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
 * Bitblaster for the layered BV solver.
14
 */
15
16
#include "theory/bv/bitblast/lazy_bitblaster.h"
17
18
#include "cvc5_private.h"
19
#include "options/bv_options.h"
20
#include "prop/cnf_stream.h"
21
#include "prop/sat_solver.h"
22
#include "prop/sat_solver_factory.h"
23
#include "smt/smt_engine.h"
24
#include "smt/smt_statistics_registry.h"
25
#include "theory/bv/abstraction.h"
26
#include "theory/bv/bv_solver_layered.h"
27
#include "theory/bv/theory_bv.h"
28
#include "theory/bv/theory_bv_utils.h"
29
#include "theory/rewriter.h"
30
#include "theory/theory_model.h"
31
32
namespace cvc5 {
33
namespace theory {
34
namespace bv {
35
36
namespace {
37
38
/* Determine the number of uncached nodes that a given node consists of.  */
39
uint64_t numNodes(TNode node, utils::NodeSet& seen)
40
{
41
  std::vector<TNode> stack;
42
  uint64_t res = 0;
43
44
  stack.push_back(node);
45
  while (!stack.empty())
46
  {
47
    Node n = stack.back();
48
    stack.pop_back();
49
50
    if (seen.find(n) != seen.end()) continue;
51
52
    res += 1;
53
    seen.insert(n);
54
    stack.insert(stack.end(), n.begin(), n.end());
55
  }
56
  return res;
57
}
58
}
59
60
TLazyBitblaster::TLazyBitblaster(context::Context* c,
61
                                 bv::BVSolverLayered* bv,
62
                                 const std::string name,
63
                                 bool emptyNotify)
64
    : TBitblaster<Node>(),
65
      d_bv(bv),
66
      d_ctx(c),
67
      d_nullRegistrar(new prop::NullRegistrar()),
68
      d_assertedAtoms(new (true) context::CDList<prop::SatLiteral>(c)),
69
      d_explanations(new (true) ExplanationMap(c)),
70
      d_variables(),
71
      d_bbAtoms(),
72
      d_abstraction(NULL),
73
      d_emptyNotify(emptyNotify),
74
      d_fullModelAssertionLevel(c, 0),
75
      d_name(name),
76
      d_statistics(name + "::")
77
{
78
  d_satSolver.reset(prop::SatSolverFactory::createMinisat(
79
      c, smtStatisticsRegistry(), name + "::"));
80
81
  ResourceManager* rm = smt::currentResourceManager();
82
  d_cnfStream.reset(new prop::CnfStream(d_satSolver.get(),
83
                                        d_nullRegistrar.get(),
84
                                        d_nullContext.get(),
85
                                        nullptr,
86
                                        rm,
87
                                        prop::FormulaLitPolicy::INTERNAL,
88
                                        "LazyBitblaster"));
89
90
  d_satSolverNotify.reset(
91
      d_emptyNotify
92
          ? (prop::BVSatSolverNotify*)new MinisatEmptyNotify()
93
          : (prop::BVSatSolverNotify*)new MinisatNotify(
94
                d_cnfStream.get(), bv, this));
95
96
  d_satSolver->setNotify(d_satSolverNotify.get());
97
}
98
99
void TLazyBitblaster::setAbstraction(AbstractionModule* abs) {
100
  d_abstraction = abs;
101
}
102
103
TLazyBitblaster::~TLazyBitblaster()
104
{
105
  d_assertedAtoms->deleteSelf();
106
  d_explanations->deleteSelf();
107
}
108
109
110
/**
111
 * Bitblasts the atom, assigns it a marker literal, adding it to the SAT solver
112
 * NOTE: duplicate clauses are not detected because of marker literal
113
 * @param node the atom to be bitblasted
114
 *
115
 */
116
void TLazyBitblaster::bbAtom(TNode node)
117
{
118
  NodeManager* nm = NodeManager::currentNM();
119
  node = node.getKind() == kind::NOT ? node[0] : node;
120
121
  if (hasBBAtom(node))
122
  {
123
    return;
124
  }
125
126
  // make sure it is marked as an atom
127
  addAtom(node);
128
129
  Debug("bitvector-bitblast") << "Bitblasting node " << node << "\n";
130
  ++d_statistics.d_numAtoms;
131
132
  /// if we are using bit-vector abstraction bit-blast the original
133
  /// interpretation
134
  if (options::bvAbstraction() && d_abstraction != NULL
135
      && d_abstraction->isAbstraction(node))
136
  {
137
    // node must be of the form P(args) = bv1
138
    Node expansion = Rewriter::rewrite(d_abstraction->getInterpretation(node));
139
140
    Node atom_bb;
141
    if (expansion.getKind() == kind::CONST_BOOLEAN)
142
    {
143
      atom_bb = expansion;
144
    }
145
    else
146
    {
147
      Assert(expansion.getKind() == kind::AND);
148
      std::vector<Node> atoms;
149
      for (unsigned i = 0; i < expansion.getNumChildren(); ++i)
150
      {
151
        Node normalized_i = Rewriter::rewrite(expansion[i]);
152
        Node atom_i =
153
            normalized_i.getKind() != kind::CONST_BOOLEAN
154
                ? Rewriter::rewrite(d_atomBBStrategies[normalized_i.getKind()](
155
                      normalized_i, this))
156
                : normalized_i;
157
        atoms.push_back(atom_i);
158
      }
159
      atom_bb = utils::mkAnd(atoms);
160
    }
161
    Assert(!atom_bb.isNull());
162
    Node atom_definition = nm->mkNode(kind::EQUAL, node, atom_bb);
163
    storeBBAtom(node, atom_bb);
164
    d_cnfStream->convertAndAssert(atom_definition, false, false);
165
    return;
166
  }
167
168
  // the bitblasted definition of the atom
169
  Node normalized = Rewriter::rewrite(node);
170
  Node atom_bb =
171
      normalized.getKind() != kind::CONST_BOOLEAN
172
          ? d_atomBBStrategies[normalized.getKind()](normalized, this)
173
          : normalized;
174
175
  atom_bb = Rewriter::rewrite(atom_bb);
176
177
  // asserting that the atom is true iff the definition holds
178
  Node atom_definition = nm->mkNode(kind::EQUAL, node, atom_bb);
179
  storeBBAtom(node, atom_bb);
180
  d_cnfStream->convertAndAssert(atom_definition, false, false);
181
}
182
183
void TLazyBitblaster::storeBBAtom(TNode atom, Node atom_bb) {
184
  d_bbAtoms.insert(atom);
185
}
186
187
void TLazyBitblaster::storeBBTerm(TNode node, const Bits& bits) {
188
  d_termCache.insert(std::make_pair(node, bits));
189
}
190
191
192
bool TLazyBitblaster::hasBBAtom(TNode atom) const {
193
  return d_bbAtoms.find(atom) != d_bbAtoms.end();
194
}
195
196
197
void TLazyBitblaster::makeVariable(TNode var, Bits& bits) {
198
  Assert(bits.size() == 0);
199
  for (unsigned i = 0; i < utils::getSize(var); ++i) {
200
    bits.push_back(utils::mkBitOf(var, i));
201
  }
202
  d_variables.insert(var);
203
}
204
205
uint64_t TLazyBitblaster::computeAtomWeight(TNode node, NodeSet& seen)
206
{
207
  node = node.getKind() == kind::NOT ? node[0] : node;
208
  if (!utils::isBitblastAtom(node)) { return 0; }
209
  Node atom_bb =
210
      Rewriter::rewrite(d_atomBBStrategies[node.getKind()](node, this));
211
  uint64_t size = numNodes(atom_bb, seen);
212
  return size;
213
}
214
215
// cnf conversion ensures the atom represents itself
216
Node TLazyBitblaster::getBBAtom(TNode node) const {
217
  return node;
218
}
219
220
void TLazyBitblaster::bbTerm(TNode node, Bits& bits) {
221
222
  if (hasBBTerm(node)) {
223
    getBBTerm(node, bits);
224
    return;
225
  }
226
  Assert(node.getType().isBitVector());
227
228
  d_bv->spendResource(Resource::BitblastStep);
229
  Debug("bitvector-bitblast") << "Bitblasting term " << node <<"\n";
230
  ++d_statistics.d_numTerms;
231
232
  d_termBBStrategies[node.getKind()] (node, bits,this);
233
234
  Assert(bits.size() == utils::getSize(node));
235
236
  storeBBTerm(node, bits);
237
}
238
/// Public methods
239
240
void TLazyBitblaster::addAtom(TNode atom) {
241
  d_cnfStream->ensureLiteral(atom);
242
  prop::SatLiteral lit = d_cnfStream->getLiteral(atom);
243
  d_satSolver->addMarkerLiteral(lit);
244
}
245
246
void TLazyBitblaster::explain(TNode atom, std::vector<TNode>& explanation) {
247
  prop::SatLiteral lit = d_cnfStream->getLiteral(atom);
248
249
  ++(d_statistics.d_numExplainedPropagations);
250
  if (options::bvEagerExplanations()) {
251
    Assert(d_explanations->find(lit) != d_explanations->end());
252
    const std::vector<prop::SatLiteral>& literal_explanation = (*d_explanations)[lit].get();
253
    for (unsigned i = 0; i < literal_explanation.size(); ++i) {
254
      explanation.push_back(d_cnfStream->getNode(literal_explanation[i]));
255
    }
256
    return;
257
  }
258
259
  std::vector<prop::SatLiteral> literal_explanation;
260
  d_satSolver->explain(lit, literal_explanation);
261
  for (unsigned i = 0; i < literal_explanation.size(); ++i) {
262
    explanation.push_back(d_cnfStream->getNode(literal_explanation[i]));
263
  }
264
}
265
266
267
/*
268
 * Asserts the clauses corresponding to the atom to the Sat Solver
269
 * by turning on the marker literal (i.e. setting it to false)
270
 * @param node the atom to be asserted
271
 *
272
 */
273
274
bool TLazyBitblaster::propagate() {
275
  return d_satSolver->propagate() == prop::SAT_VALUE_TRUE;
276
}
277
278
bool TLazyBitblaster::assertToSat(TNode lit, bool propagate) {
279
  // strip the not
280
  TNode atom;
281
  if (lit.getKind() == kind::NOT) {
282
    atom = lit[0];
283
  } else {
284
    atom = lit;
285
  }
286
  Assert(utils::isBitblastAtom(atom));
287
288
  Assert(hasBBAtom(atom));
289
290
  prop::SatLiteral markerLit = d_cnfStream->getLiteral(atom);
291
292
  if(lit.getKind() == kind::NOT) {
293
    markerLit = ~markerLit;
294
  }
295
296
  Debug("bitvector-bb")
297
      << "BVSolverLayered::TLazyBitblaster::assertToSat asserting node: "
298
      << atom << "\n";
299
  Debug("bitvector-bb")
300
      << "BVSolverLayered::TLazyBitblaster::assertToSat with literal:   "
301
      << markerLit << "\n";
302
303
  prop::SatValue ret = d_satSolver->assertAssumption(markerLit, propagate);
304
305
  d_assertedAtoms->push_back(markerLit);
306
307
  return ret == prop::SAT_VALUE_TRUE || ret == prop::SAT_VALUE_UNKNOWN;
308
}
309
310
/**
311
 * Calls the solve method for the Sat Solver.
312
 * passing it the marker literals to be asserted
313
 *
314
 * @return true for sat, and false for unsat
315
 */
316
317
bool TLazyBitblaster::solve() {
318
  if (Trace.isOn("bitvector")) {
319
    Trace("bitvector") << "TLazyBitblaster::solve() asserted atoms ";
320
    context::CDList<prop::SatLiteral>::const_iterator it = d_assertedAtoms->begin();
321
    for (; it != d_assertedAtoms->end(); ++it) {
322
      Trace("bitvector") << "     " << d_cnfStream->getNode(*it) << "\n";
323
    }
324
  }
325
  Debug("bitvector") << "TLazyBitblaster::solve() asserted atoms " << d_assertedAtoms->size() <<"\n";
326
  d_fullModelAssertionLevel.set(d_bv->numAssertions());
327
  return prop::SAT_VALUE_TRUE == d_satSolver->solve();
328
}
329
330
prop::SatValue TLazyBitblaster::solveWithBudget(unsigned long budget) {
331
  if (Trace.isOn("bitvector")) {
332
    Trace("bitvector") << "TLazyBitblaster::solveWithBudget() asserted atoms ";
333
    context::CDList<prop::SatLiteral>::const_iterator it = d_assertedAtoms->begin();
334
    for (; it != d_assertedAtoms->end(); ++it) {
335
      Trace("bitvector") << "     " << d_cnfStream->getNode(*it) << "\n";
336
    }
337
  }
338
  Debug("bitvector") << "TLazyBitblaster::solveWithBudget() asserted atoms " << d_assertedAtoms->size() <<"\n";
339
  return d_satSolver->solve(budget);
340
}
341
342
void TLazyBitblaster::getConflict(std::vector<TNode>& conflict)
343
{
344
  NodeManager* nm = NodeManager::currentNM();
345
  prop::SatClause conflictClause;
346
  d_satSolver->getUnsatCore(conflictClause);
347
348
  for (unsigned i = 0; i < conflictClause.size(); i++)
349
  {
350
    prop::SatLiteral lit = conflictClause[i];
351
    TNode atom = d_cnfStream->getNode(lit);
352
    Node not_atom;
353
    if (atom.getKind() == kind::NOT)
354
    {
355
      not_atom = atom[0];
356
    }
357
    else
358
    {
359
      not_atom = nm->mkNode(kind::NOT, atom);
360
    }
361
    conflict.push_back(not_atom);
362
  }
363
}
364
365
TLazyBitblaster::Statistics::Statistics(const std::string& prefix)
366
    : d_numTermClauses(
367
        smtStatisticsRegistry().registerInt(prefix + "NumTermSatClauses")),
368
      d_numAtomClauses(
369
          smtStatisticsRegistry().registerInt(prefix + "NumAtomSatClauses")),
370
      d_numTerms(
371
          smtStatisticsRegistry().registerInt(prefix + "NumBitblastedTerms")),
372
      d_numAtoms(
373
          smtStatisticsRegistry().registerInt(prefix + "NumBitblastedAtoms")),
374
      d_numExplainedPropagations(smtStatisticsRegistry().registerInt(
375
          prefix + "NumExplainedPropagations")),
376
      d_numBitblastingPropagations(smtStatisticsRegistry().registerInt(
377
          prefix + "NumBitblastingPropagations")),
378
      d_bitblastTimer(
379
          smtStatisticsRegistry().registerTimer(prefix + "BitblastTimer"))
380
{
381
}
382
383
bool TLazyBitblaster::MinisatNotify::notify(prop::SatLiteral lit) {
384
  if(options::bvEagerExplanations()) {
385
    // compute explanation
386
    if (d_lazyBB->d_explanations->find(lit) == d_lazyBB->d_explanations->end()) {
387
      std::vector<prop::SatLiteral> literal_explanation;
388
      d_lazyBB->d_satSolver->explain(lit, literal_explanation);
389
      d_lazyBB->d_explanations->insert(lit, literal_explanation);
390
    } else {
391
      // we propagated it at a lower level
392
      return true;
393
    }
394
  }
395
  ++(d_lazyBB->d_statistics.d_numBitblastingPropagations);
396
  TNode atom = d_cnf->getNode(lit);
397
  return d_bv->storePropagation(atom, SUB_BITBLAST);
398
}
399
400
void TLazyBitblaster::MinisatNotify::notify(prop::SatClause& clause) {
401
  if (clause.size() > 1) {
402
    NodeBuilder lemmab(kind::OR);
403
    for (unsigned i = 0; i < clause.size(); ++ i) {
404
      lemmab << d_cnf->getNode(clause[i]);
405
    }
406
    Node lemma = lemmab;
407
    d_bv->d_im.lemma(lemma, InferenceId::BV_LAYERED_LEMMA);
408
  } else {
409
    d_bv->d_im.lemma(d_cnf->getNode(clause[0]), InferenceId::BV_LAYERED_LEMMA);
410
  }
411
}
412
413
void TLazyBitblaster::MinisatNotify::spendResource(Resource r)
414
{
415
  d_bv->spendResource(r);
416
}
417
418
void TLazyBitblaster::MinisatNotify::safePoint(Resource r)
419
{
420
  d_bv->d_im.safePoint(r);
421
}
422
423
EqualityStatus TLazyBitblaster::getEqualityStatus(TNode a, TNode b)
424
{
425
  int numAssertions = d_bv->numAssertions();
426
  bool has_full_model =
427
      numAssertions != 0 && d_fullModelAssertionLevel.get() == numAssertions;
428
429
  Debug("bv-equality-status")
430
      << "TLazyBitblaster::getEqualityStatus " << a << " = " << b << "\n";
431
  Debug("bv-equality-status")
432
      << "BVSatSolver has full model? " << has_full_model << "\n";
433
434
  // First check if it trivially rewrites to false/true
435
  Node a_eq_b =
436
      Rewriter::rewrite(NodeManager::currentNM()->mkNode(kind::EQUAL, a, b));
437
438
  if (a_eq_b == utils::mkFalse()) return theory::EQUALITY_FALSE;
439
  if (a_eq_b == utils::mkTrue()) return theory::EQUALITY_TRUE;
440
441
  if (!has_full_model)
442
  {
443
    return theory::EQUALITY_UNKNOWN;
444
  }
445
446
  // Check if cache is valid (invalidated in check and pops)
447
  if (d_bv->d_invalidateModelCache.get())
448
  {
449
    invalidateModelCache();
450
  }
451
  d_bv->d_invalidateModelCache.set(false);
452
453
  Node a_value = getTermModel(a, true);
454
  Node b_value = getTermModel(b, true);
455
456
  Assert(a_value.isConst() && b_value.isConst());
457
458
  if (a_value == b_value)
459
  {
460
    Debug("bv-equality-status") << "theory::EQUALITY_TRUE_IN_MODEL\n";
461
    return theory::EQUALITY_TRUE_IN_MODEL;
462
  }
463
  Debug("bv-equality-status") << "theory::EQUALITY_FALSE_IN_MODEL\n";
464
  return theory::EQUALITY_FALSE_IN_MODEL;
465
}
466
467
bool TLazyBitblaster::isSharedTerm(TNode node) {
468
  return d_bv->d_sharedTermsSet.find(node) != d_bv->d_sharedTermsSet.end();
469
}
470
471
bool TLazyBitblaster::hasValue(TNode a) {
472
  Assert(hasBBTerm(a));
473
  Bits bits;
474
  getBBTerm(a, bits);
475
  for (int i = bits.size() -1; i >= 0; --i) {
476
    prop::SatValue bit_value;
477
    if (d_cnfStream->hasLiteral(bits[i])) {
478
      prop::SatLiteral bit = d_cnfStream->getLiteral(bits[i]);
479
      bit_value = d_satSolver->value(bit);
480
      if (bit_value ==  prop::SAT_VALUE_UNKNOWN)
481
        return false;
482
    } else {
483
      return false;
484
    }
485
  }
486
  return true;
487
}
488
/**
489
 * Returns the value a is currently assigned to in the SAT solver
490
 * or null if the value is completely unassigned.
491
 *
492
 * @param a
493
 * @param fullModel whether to create a "full model," i.e., add
494
 * constants to equivalence classes that don't already have them
495
 *
496
 * @return
497
 */
498
Node TLazyBitblaster::getModelFromSatSolver(TNode a, bool fullModel) {
499
  if (!hasBBTerm(a)) {
500
    return fullModel? utils::mkConst(utils::getSize(a), 0u) : Node();
501
  }
502
503
  Bits bits;
504
  getBBTerm(a, bits);
505
  Integer value(0);
506
  for (int i = bits.size() -1; i >= 0; --i) {
507
    prop::SatValue bit_value;
508
    if (d_cnfStream->hasLiteral(bits[i])) {
509
      prop::SatLiteral bit = d_cnfStream->getLiteral(bits[i]);
510
      bit_value = d_satSolver->value(bit);
511
      Assert(bit_value != prop::SAT_VALUE_UNKNOWN);
512
    } else {
513
      if (!fullModel) return Node();
514
      // unconstrained bits default to false
515
      bit_value = prop::SAT_VALUE_FALSE;
516
    }
517
    Integer bit_int = bit_value == prop::SAT_VALUE_TRUE ? Integer(1) : Integer(0);
518
    value = value * 2 + bit_int;
519
  }
520
  return utils::mkConst(bits.size(), value);
521
}
522
523
bool TLazyBitblaster::collectModelValues(TheoryModel* m,
524
                                         const std::set<Node>& termSet)
525
{
526
  for (std::set<Node>::const_iterator it = termSet.begin(); it != termSet.end(); ++it) {
527
    TNode var = *it;
528
    // not actually a leaf of the bit-vector theory
529
    if (d_variables.find(var) == d_variables.end())
530
      continue;
531
532
    Assert(Theory::theoryOf(var) == theory::THEORY_BV || isSharedTerm(var));
533
    // only shared terms could not have been bit-blasted
534
    Assert(hasBBTerm(var) || isSharedTerm(var));
535
536
    Node const_value = getModelFromSatSolver(var, true);
537
    Assert(const_value.isNull() || const_value.isConst());
538
    if(const_value != Node()) {
539
      Debug("bitvector-model")
540
          << "TLazyBitblaster::collectModelValues (assert (= " << var << " "
541
          << const_value << "))\n";
542
      if (!m->assertEquality(var, const_value, true))
543
      {
544
        return false;
545
      }
546
    }
547
  }
548
  return true;
549
}
550
551
void TLazyBitblaster::clearSolver() {
552
  Assert(d_ctx->getLevel() == 0);
553
  d_assertedAtoms->deleteSelf();
554
  d_assertedAtoms = new(true) context::CDList<prop::SatLiteral>(d_ctx);
555
  d_explanations->deleteSelf();
556
  d_explanations = new(true) ExplanationMap(d_ctx);
557
  d_bbAtoms.clear();
558
  d_variables.clear();
559
  d_termCache.clear();
560
561
  invalidateModelCache();
562
  // recreate sat solver
563
  d_satSolver.reset(
564
      prop::SatSolverFactory::createMinisat(d_ctx, smtStatisticsRegistry()));
565
  ResourceManager* rm = smt::currentResourceManager();
566
  d_cnfStream.reset(new prop::CnfStream(d_satSolver.get(),
567
                                        d_nullRegistrar.get(),
568
                                        d_nullContext.get(),
569
                                        nullptr,
570
                                        rm));
571
  d_satSolverNotify.reset(
572
      d_emptyNotify
573
          ? (prop::BVSatSolverNotify*)new MinisatEmptyNotify()
574
          : (prop::BVSatSolverNotify*)new MinisatNotify(
575
                d_cnfStream.get(), d_bv, this));
576
  d_satSolver->setNotify(d_satSolverNotify.get());
577
}
578
579
}  // namespace bv
580
}  // namespace theory
581
29502
}  // namespace cvc5