GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/bv/bv_solver_bitblast.cpp Lines: 157 165 95.2 %
Date: 2021-09-15 Branches: 293 606 48.3 %

Line Exec Source
1
/******************************************************************************
2
 * Top contributors (to current version):
3
 *   Mathias Preiner, Gereon Kremer, Andres Noetzli
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
 * Bit-blasting solver that supports multiple SAT backends.
14
 */
15
16
#include "theory/bv/bv_solver_bitblast.h"
17
18
#include "options/bv_options.h"
19
#include "prop/sat_solver_factory.h"
20
#include "smt/smt_statistics_registry.h"
21
#include "theory/bv/theory_bv.h"
22
#include "theory/bv/theory_bv_utils.h"
23
#include "theory/theory_model.h"
24
25
namespace cvc5 {
26
namespace theory {
27
namespace bv {
28
29
/**
30
 * Notifies the BV solver when assertions were reset.
31
 *
32
 * This class is notified after every user-context pop and maintains a flag
33
 * that indicates whether assertions have been reset. If the user-context level
34
 * reaches level 0 it means that the assertions were reset.
35
 */
36
12314
class NotifyResetAssertions : public context::ContextNotifyObj
37
{
38
 public:
39
6160
  NotifyResetAssertions(context::Context* c)
40
6160
      : context::ContextNotifyObj(c, false),
41
        d_context(c),
42
6160
        d_doneResetAssertions(false)
43
  {
44
6160
  }
45
46
8
  bool doneResetAssertions() { return d_doneResetAssertions; }
47
48
2
  void reset() { d_doneResetAssertions = false; }
49
50
 protected:
51
9364
  void contextNotifyPop() override
52
  {
53
    // If the user-context level reaches 0 it means that reset-assertions was
54
    // called.
55
9364
    if (d_context->getLevel() == 0)
56
    {
57
6202
      d_doneResetAssertions = true;
58
    }
59
9364
  }
60
61
 private:
62
  /** The user-context. */
63
  context::Context* d_context;
64
65
  /** Flag to notify whether reset assertions was called. */
66
  bool d_doneResetAssertions;
67
};
68
69
/**
70
 * Bit-blasting registrar.
71
 *
72
 * The CnfStream calls preRegister() if it encounters a theory atom.
73
 * This registrar bit-blasts given atom and remembers which bit-vector atoms
74
 * were bit-blasted.
75
 *
76
 * This registrar is needed when --bitblast=eager is enabled.
77
 */
78
12314
class BBRegistrar : public prop::Registrar
79
{
80
 public:
81
6160
  BBRegistrar(NodeBitblaster* bb) : d_bitblaster(bb) {}
82
83
148959
  void preRegister(Node n) override
84
  {
85
148959
    if (d_registeredAtoms.find(n) != d_registeredAtoms.end())
86
    {
87
      return;
88
    }
89
    /* We are only interested in bit-vector atoms. */
90
298104
    if ((n.getKind() == kind::EQUAL && n[0].getType().isBitVector())
91
148773
        || n.getKind() == kind::BITVECTOR_ULT
92
148718
        || n.getKind() == kind::BITVECTOR_ULE
93
148718
        || n.getKind() == kind::BITVECTOR_SLT
94
446636
        || n.getKind() == kind::BITVECTOR_SLE)
95
    {
96
241
      d_registeredAtoms.insert(n);
97
241
      d_bitblaster->bbAtom(n);
98
    }
99
  }
100
101
142
  std::unordered_set<TNode>& getRegisteredAtoms() { return d_registeredAtoms; }
102
103
 private:
104
  /** The bitblaster used. */
105
  NodeBitblaster* d_bitblaster;
106
107
  /** Stores bit-vector atoms encounterd on preRegister(). */
108
  std::unordered_set<TNode> d_registeredAtoms;
109
};
110
111
6160
BVSolverBitblast::BVSolverBitblast(Env& env,
112
                                   TheoryState* s,
113
                                   TheoryInferenceManager& inferMgr,
114
6160
                                   ProofNodeManager* pnm)
115
    : BVSolver(env, *s, inferMgr),
116
6160
      d_bitblaster(new NodeBitblaster(env, s)),
117
6160
      d_bbRegistrar(new BBRegistrar(d_bitblaster.get())),
118
6160
      d_nullContext(new context::Context()),
119
      d_bbFacts(context()),
120
      d_bbInputFacts(context()),
121
      d_assumptions(context()),
122
      d_assertions(context()),
123
20
      d_epg(pnm ? new EagerProofGenerator(pnm, userContext(), "")
124
                : nullptr),
125
      d_factLiteralCache(context()),
126
      d_literalFactCache(context()),
127
6160
      d_propagate(options().bv.bitvectorPropagate),
128
30820
      d_resetNotify(new NotifyResetAssertions(userContext()))
129
{
130
6160
  if (pnm != nullptr)
131
  {
132
10
    d_bvProofChecker.registerTo(pnm->getChecker());
133
  }
134
135
6160
  initSatSolver();
136
6160
}
137
138
728501
void BVSolverBitblast::postCheck(Theory::Effort level)
139
{
140
728501
  if (level != Theory::Effort::EFFORT_FULL)
141
  {
142
    /* Do bit-level propagation only if the SAT solver supports it. */
143
695494
    if (!d_propagate || !d_satSolver->setPropagateOnly())
144
    {
145
695494
      return;
146
    }
147
  }
148
149
  // If we permanently added assertions to the SAT solver and the assertions
150
  // were reset, we have to reset the SAT solver and the CNF stream.
151
33007
  if (options().bv.bvAssertInput && d_resetNotify->doneResetAssertions())
152
  {
153
2
    d_satSolver.reset(nullptr);
154
2
    d_cnfStream.reset(nullptr);
155
2
    initSatSolver();
156
2
    d_resetNotify->reset();
157
  }
158
159
33007
  NodeManager* nm = NodeManager::currentNM();
160
161
  /* Process input assertions bit-blast queue. */
162
33023
  while (!d_bbInputFacts.empty())
163
  {
164
16
    Node fact = d_bbInputFacts.front();
165
8
    d_bbInputFacts.pop();
166
    /* Bit-blast fact and cache literal. */
167
8
    if (d_factLiteralCache.find(fact) == d_factLiteralCache.end())
168
    {
169
8
      if (fact.getKind() == kind::BITVECTOR_EAGER_ATOM)
170
      {
171
        handleEagerAtom(fact, true);
172
      }
173
      else
174
      {
175
8
        d_bitblaster->bbAtom(fact);
176
16
        Node bb_fact = d_bitblaster->getStoredBBAtom(fact);
177
8
        d_cnfStream->convertAndAssert(bb_fact, false, false);
178
      }
179
    }
180
8
    d_assertions.push_back(fact);
181
  }
182
183
  /* Process bit-blast queue and store SAT literals. */
184
5076417
  while (!d_bbFacts.empty())
185
  {
186
5043410
    Node fact = d_bbFacts.front();
187
2521705
    d_bbFacts.pop();
188
    /* Bit-blast fact and cache literal. */
189
2521705
    if (d_factLiteralCache.find(fact) == d_factLiteralCache.end())
190
    {
191
2521705
      prop::SatLiteral lit;
192
2521705
      if (fact.getKind() == kind::BITVECTOR_EAGER_ATOM)
193
      {
194
142
        handleEagerAtom(fact, false);
195
142
        lit = d_cnfStream->getLiteral(fact[0]);
196
      }
197
      else
198
      {
199
2521563
        d_bitblaster->bbAtom(fact);
200
5043126
        Node bb_fact = d_bitblaster->getStoredBBAtom(fact);
201
2521563
        d_cnfStream->ensureLiteral(bb_fact);
202
2521563
        lit = d_cnfStream->getLiteral(bb_fact);
203
      }
204
2521705
      d_factLiteralCache[fact] = lit;
205
2521705
      d_literalFactCache[lit] = fact;
206
    }
207
2521705
    d_assumptions.push_back(d_factLiteralCache[fact]);
208
  }
209
210
  std::vector<prop::SatLiteral> assumptions(d_assumptions.begin(),
211
66014
                                            d_assumptions.end());
212
33007
  prop::SatValue val = d_satSolver->solve(assumptions);
213
214
33007
  if (val == prop::SatValue::SAT_VALUE_FALSE)
215
  {
216
28436
    std::vector<prop::SatLiteral> unsat_assumptions;
217
14218
    d_satSolver->getUnsatAssumptions(unsat_assumptions);
218
219
28436
    Node conflict;
220
    // Unsat assumptions produce conflict.
221
14218
    if (unsat_assumptions.size() > 0)
222
    {
223
28436
      std::vector<Node> conf;
224
123782
      for (const prop::SatLiteral& lit : unsat_assumptions)
225
      {
226
109564
        conf.push_back(d_literalFactCache[lit]);
227
219128
        Debug("bv-bitblast")
228
109564
            << "unsat assumption (" << lit << "): " << conf.back() << std::endl;
229
      }
230
14218
      conflict = nm->mkAnd(conf);
231
    }
232
    else  // Input assertions produce conflict.
233
    {
234
      std::vector<Node> assertions(d_assertions.begin(), d_assertions.end());
235
      conflict = nm->mkAnd(assertions);
236
    }
237
14218
    d_im.conflict(conflict, InferenceId::BV_BITBLAST_CONFLICT);
238
  }
239
}
240
241
1210656
bool BVSolverBitblast::preNotifyFact(
242
    TNode atom, bool pol, TNode fact, bool isPrereg, bool isInternal)
243
{
244
1210656
  Valuation& val = d_state.getValuation();
245
246
  /**
247
   * Check whether `fact` is an input assertion on user-level 0.
248
   *
249
   * If this is the case we can assert `fact` to the SAT solver instead of
250
   * using assumptions.
251
   */
252
2421328
  if (options().bv.bvAssertInput && val.isSatLiteral(fact)
253
2421328
      && val.getDecisionLevel(fact) == 0 && val.getIntroLevel(fact) == 0)
254
  {
255
8
    Assert(!val.isDecision(fact));
256
8
    d_bbInputFacts.push_back(fact);
257
  }
258
  else
259
  {
260
1210648
    d_bbFacts.push_back(fact);
261
  }
262
263
1210656
  return false;  // Return false to enable equality engine reasoning in Theory.
264
}
265
266
4785
TrustNode BVSolverBitblast::explain(TNode n)
267
{
268
4785
  Debug("bv-bitblast") << "explain called on " << n << std::endl;
269
4785
  return d_im.explainLit(n);
270
}
271
272
5956
void BVSolverBitblast::computeRelevantTerms(std::set<Node>& termSet)
273
{
274
  /* BITVECTOR_EAGER_ATOM wraps input assertions that may also contain
275
   * equalities. As a result, these equalities are not handled by the equality
276
   * engine and terms below these equalities do not appear in `termSet`.
277
   * We need to make sure that we compute model values for all relevant terms
278
   * in BitblastMode::EAGER and therefore add all variables from the
279
   * bit-blaster to `termSet`.
280
   */
281
5956
  if (options().bv.bitblastMode == options::BitblastMode::EAGER)
282
  {
283
11
    d_bitblaster->computeRelevantTerms(termSet);
284
  }
285
5956
}
286
287
5956
bool BVSolverBitblast::collectModelValues(TheoryModel* m,
288
                                          const std::set<Node>& termSet)
289
{
290
192526
  for (const auto& term : termSet)
291
  {
292
186570
    if (!d_bitblaster->isVariable(term))
293
    {
294
158723
      continue;
295
    }
296
297
55694
    Node value = getValue(term, true);
298
27847
    Assert(value.isConst());
299
27847
    if (!m->assertEquality(term, value, true))
300
    {
301
      return false;
302
    }
303
  }
304
305
  // In eager bitblast mode we also have to collect the model values for
306
  // Boolean variables in the CNF stream.
307
5956
  if (options().bv.bitblastMode == options::BitblastMode::EAGER)
308
  {
309
11
    NodeManager* nm = NodeManager::currentNM();
310
22
    std::vector<TNode> vars;
311
11
    d_cnfStream->getBooleanVariables(vars);
312
17
    for (TNode var : vars)
313
    {
314
6
      Assert(d_cnfStream->hasLiteral(var));
315
6
      prop::SatLiteral bit = d_cnfStream->getLiteral(var);
316
6
      prop::SatValue value = d_satSolver->value(bit);
317
6
      Assert(value != prop::SAT_VALUE_UNKNOWN);
318
12
      if (!m->assertEquality(
319
12
              var, nm->mkConst(value == prop::SAT_VALUE_TRUE), true))
320
      {
321
        return false;
322
      }
323
    }
324
  }
325
326
5956
  return true;
327
}
328
329
6162
void BVSolverBitblast::initSatSolver()
330
{
331
6162
  switch (options().bv.bvSatSolver)
332
  {
333
13
    case options::SatSolverMode::CRYPTOMINISAT:
334
13
      d_satSolver.reset(prop::SatSolverFactory::createCryptoMinisat(
335
          smtStatisticsRegistry(), "theory::bv::BVSolverBitblast::"));
336
13
      break;
337
6149
    default:
338
6149
      d_satSolver.reset(prop::SatSolverFactory::createCadical(
339
          smtStatisticsRegistry(), "theory::bv::BVSolverBitblast::"));
340
  }
341
18486
  d_cnfStream.reset(new prop::CnfStream(d_satSolver.get(),
342
6162
                                        d_bbRegistrar.get(),
343
6162
                                        d_nullContext.get(),
344
                                        nullptr,
345
6162
                                        smt::currentResourceManager(),
346
                                        prop::FormulaLitPolicy::INTERNAL,
347
6162
                                        "theory::bv::BVSolverBitblast"));
348
6162
}
349
350
28945
Node BVSolverBitblast::getValue(TNode node, bool initialize)
351
{
352
28945
  if (node.isConst())
353
  {
354
    return node;
355
  }
356
357
28945
  if (!d_bitblaster->hasBBTerm(node))
358
  {
359
634
    return initialize ? utils::mkConst(utils::getSize(node), 0u) : Node();
360
  }
361
362
56622
  std::vector<Node> bits;
363
28311
  d_bitblaster->getBBTerm(node, bits);
364
56622
  Integer value(0), one(1), zero(0), bit;
365
611158
  for (size_t i = 0, size = bits.size(), j = size - 1; i < size; ++i, --j)
366
  {
367
582939
    if (d_cnfStream->hasLiteral(bits[j]))
368
    {
369
576698
      prop::SatLiteral lit = d_cnfStream->getLiteral(bits[j]);
370
576698
      prop::SatValue val = d_satSolver->modelValue(lit);
371
576698
      bit = val == prop::SatValue::SAT_VALUE_TRUE ? one : zero;
372
    }
373
    else
374
    {
375
6241
      if (!initialize) return Node();
376
6149
      bit = zero;
377
    }
378
582847
    value = value * 2 + bit;
379
  }
380
28219
  return utils::mkConst(bits.size(), value);
381
}
382
383
142
void BVSolverBitblast::handleEagerAtom(TNode fact, bool assertFact)
384
{
385
142
  Assert(fact.getKind() == kind::BITVECTOR_EAGER_ATOM);
386
387
142
  if (assertFact)
388
  {
389
    d_cnfStream->convertAndAssert(fact[0], false, false);
390
  }
391
  else
392
  {
393
142
    d_cnfStream->ensureLiteral(fact[0]);
394
  }
395
396
  /* convertAndAssert() does not make the connection between the bit-vector
397
   * atom and it's bit-blasted form (it only calls preRegister() from the
398
   * registrar). Thus, we add the equalities now. */
399
142
  auto& registeredAtoms = d_bbRegistrar->getRegisteredAtoms();
400
383
  for (auto atom : registeredAtoms)
401
  {
402
482
    Node bb_atom = d_bitblaster->getStoredBBAtom(atom);
403
241
    d_cnfStream->convertAndAssert(atom.eqNode(bb_atom), false, false);
404
  }
405
  // Clear cache since we only need to do this once per bit-blasted atom.
406
142
  registeredAtoms.clear();
407
142
}
408
409
}  // namespace bv
410
}  // namespace theory
411
29577
}  // namespace cvc5