1 /******************************************************************************
2 * Top contributors (to current version):
3 * Mathias Preiner, Gereon Kremer, Andres Noetzli
5 * This file is part of the cvc5 project.
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 * ****************************************************************************
13 * Bit-blasting solver that supports multiple SAT backends.
16 #include "theory/bv/bv_solver_bitblast.h"
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"
30 * Notifies the BV solver when assertions were reset.
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.
36 class NotifyResetAssertions
: public context::ContextNotifyObj
39 NotifyResetAssertions(context::Context
* c
)
40 : context::ContextNotifyObj(c
, false),
42 d_doneResetAssertions(false)
46 bool doneResetAssertions() { return d_doneResetAssertions
; }
48 void reset() { d_doneResetAssertions
= false; }
51 void contextNotifyPop() override
53 // If the user-context level reaches 0 it means that reset-assertions was
55 if (d_context
->getLevel() == 0)
57 d_doneResetAssertions
= true;
62 /** The user-context. */
63 context::Context
* d_context
;
65 /** Flag to notify whether reset assertions was called. */
66 bool d_doneResetAssertions
;
70 * Bit-blasting registrar.
72 * The CnfStream calls preRegister() if it encounters a theory atom.
73 * This registrar bit-blasts given atom and remembers which bit-vector atoms
76 * This registrar is needed when --bitblast=eager is enabled.
78 class BBRegistrar
: public prop::Registrar
81 BBRegistrar(NodeBitblaster
* bb
) : d_bitblaster(bb
) {}
83 void preRegister(Node n
) override
85 if (d_registeredAtoms
.find(n
) != d_registeredAtoms
.end())
89 /* We are only interested in bit-vector atoms. */
90 if ((n
.getKind() == kind::EQUAL
&& n
[0].getType().isBitVector())
91 || n
.getKind() == kind::BITVECTOR_ULT
92 || n
.getKind() == kind::BITVECTOR_ULE
93 || n
.getKind() == kind::BITVECTOR_SLT
94 || n
.getKind() == kind::BITVECTOR_SLE
)
96 d_registeredAtoms
.insert(n
);
97 d_bitblaster
->bbAtom(n
);
101 std::unordered_set
<TNode
>& getRegisteredAtoms() { return d_registeredAtoms
; }
104 /** The bitblaster used. */
105 NodeBitblaster
* d_bitblaster
;
107 /** Stores bit-vector atoms encounterd on preRegister(). */
108 std::unordered_set
<TNode
> d_registeredAtoms
;
111 BVSolverBitblast::BVSolverBitblast(TheoryState
* s
,
112 TheoryInferenceManager
& inferMgr
,
113 ProofNodeManager
* pnm
)
114 : BVSolver(*s
, inferMgr
),
115 d_bitblaster(new NodeBitblaster(s
)),
116 d_bbRegistrar(new BBRegistrar(d_bitblaster
.get())),
117 d_nullContext(new context::Context()),
118 d_bbFacts(s
->getSatContext()),
119 d_bbInputFacts(s
->getSatContext()),
120 d_assumptions(s
->getSatContext()),
121 d_assertions(s
->getSatContext()),
122 d_epg(pnm
? new EagerProofGenerator(pnm
, s
->getUserContext(), "")
124 d_factLiteralCache(s
->getSatContext()),
125 d_literalFactCache(s
->getSatContext()),
126 d_propagate(options::bitvectorPropagate()),
127 d_resetNotify(new NotifyResetAssertions(s
->getUserContext()))
131 d_bvProofChecker
.registerTo(pnm
->getChecker());
137 void BVSolverBitblast::postCheck(Theory::Effort level
)
139 if (level
!= Theory::Effort::EFFORT_FULL
)
141 /* Do bit-level propagation only if the SAT solver supports it. */
142 if (!d_propagate
|| !d_satSolver
->setPropagateOnly())
148 // If we permanently added assertions to the SAT solver and the assertions
149 // were reset, we have to reset the SAT solver and the CNF stream.
150 if (options::bvAssertInput() && d_resetNotify
->doneResetAssertions())
152 d_satSolver
.reset(nullptr);
153 d_cnfStream
.reset(nullptr);
155 d_resetNotify
->reset();
158 NodeManager
* nm
= NodeManager::currentNM();
160 /* Process input assertions bit-blast queue. */
161 while (!d_bbInputFacts
.empty())
163 Node fact
= d_bbInputFacts
.front();
164 d_bbInputFacts
.pop();
165 /* Bit-blast fact and cache literal. */
166 if (d_factLiteralCache
.find(fact
) == d_factLiteralCache
.end())
168 if (fact
.getKind() == kind::BITVECTOR_EAGER_ATOM
)
170 handleEagerAtom(fact
, true);
174 d_bitblaster
->bbAtom(fact
);
175 Node bb_fact
= d_bitblaster
->getStoredBBAtom(fact
);
176 d_cnfStream
->convertAndAssert(bb_fact
, false, false);
179 d_assertions
.push_back(fact
);
182 /* Process bit-blast queue and store SAT literals. */
183 while (!d_bbFacts
.empty())
185 Node fact
= d_bbFacts
.front();
187 /* Bit-blast fact and cache literal. */
188 if (d_factLiteralCache
.find(fact
) == d_factLiteralCache
.end())
190 prop::SatLiteral lit
;
191 if (fact
.getKind() == kind::BITVECTOR_EAGER_ATOM
)
193 handleEagerAtom(fact
, false);
194 lit
= d_cnfStream
->getLiteral(fact
[0]);
198 d_bitblaster
->bbAtom(fact
);
199 Node bb_fact
= d_bitblaster
->getStoredBBAtom(fact
);
200 d_cnfStream
->ensureLiteral(bb_fact
);
201 lit
= d_cnfStream
->getLiteral(bb_fact
);
203 d_factLiteralCache
[fact
] = lit
;
204 d_literalFactCache
[lit
] = fact
;
206 d_assumptions
.push_back(d_factLiteralCache
[fact
]);
209 std::vector
<prop::SatLiteral
> assumptions(d_assumptions
.begin(),
210 d_assumptions
.end());
211 prop::SatValue val
= d_satSolver
->solve(assumptions
);
213 if (val
== prop::SatValue::SAT_VALUE_FALSE
)
215 std::vector
<prop::SatLiteral
> unsat_assumptions
;
216 d_satSolver
->getUnsatAssumptions(unsat_assumptions
);
219 // Unsat assumptions produce conflict.
220 if (unsat_assumptions
.size() > 0)
222 std::vector
<Node
> conf
;
223 for (const prop::SatLiteral
& lit
: unsat_assumptions
)
225 conf
.push_back(d_literalFactCache
[lit
]);
227 << "unsat assumption (" << lit
<< "): " << conf
.back() << std::endl
;
229 conflict
= nm
->mkAnd(conf
);
231 else // Input assertions produce conflict.
233 std::vector
<Node
> assertions(d_assertions
.begin(), d_assertions
.end());
234 conflict
= nm
->mkAnd(assertions
);
236 d_im
.conflict(conflict
, InferenceId::BV_BITBLAST_CONFLICT
);
240 bool BVSolverBitblast::preNotifyFact(
241 TNode atom
, bool pol
, TNode fact
, bool isPrereg
, bool isInternal
)
243 Valuation
& val
= d_state
.getValuation();
246 * Check whether `fact` is an input assertion on user-level 0.
248 * If this is the case we can assert `fact` to the SAT solver instead of
251 if (options::bvAssertInput() && val
.isSatLiteral(fact
)
252 && val
.getDecisionLevel(fact
) == 0 && val
.getIntroLevel(fact
) == 0)
254 Assert(!val
.isDecision(fact
));
255 d_bbInputFacts
.push_back(fact
);
259 d_bbFacts
.push_back(fact
);
262 return false; // Return false to enable equality engine reasoning in Theory.
265 TrustNode
BVSolverBitblast::explain(TNode n
)
267 Debug("bv-bitblast") << "explain called on " << n
<< std::endl
;
268 return d_im
.explainLit(n
);
271 void BVSolverBitblast::computeRelevantTerms(std::set
<Node
>& termSet
)
273 /* BITVECTOR_EAGER_ATOM wraps input assertions that may also contain
274 * equalities. As a result, these equalities are not handled by the equality
275 * engine and terms below these equalities do not appear in `termSet`.
276 * We need to make sure that we compute model values for all relevant terms
277 * in BitblastMode::EAGER and therefore add all variables from the
278 * bit-blaster to `termSet`.
280 if (options::bitblastMode() == options::BitblastMode::EAGER
)
282 d_bitblaster
->computeRelevantTerms(termSet
);
286 bool BVSolverBitblast::collectModelValues(TheoryModel
* m
,
287 const std::set
<Node
>& termSet
)
289 for (const auto& term
: termSet
)
291 if (!d_bitblaster
->isVariable(term
))
296 Node value
= getValue(term
, true);
297 Assert(value
.isConst());
298 if (!m
->assertEquality(term
, value
, true))
304 // In eager bitblast mode we also have to collect the model values for
305 // Boolean variables in the CNF stream.
306 if (options::bitblastMode() == options::BitblastMode::EAGER
)
308 NodeManager
* nm
= NodeManager::currentNM();
309 std::vector
<TNode
> vars
;
310 d_cnfStream
->getBooleanVariables(vars
);
311 for (TNode var
: vars
)
313 Assert(d_cnfStream
->hasLiteral(var
));
314 prop::SatLiteral bit
= d_cnfStream
->getLiteral(var
);
315 prop::SatValue value
= d_satSolver
->value(bit
);
316 Assert(value
!= prop::SAT_VALUE_UNKNOWN
);
317 if (!m
->assertEquality(
318 var
, nm
->mkConst(value
== prop::SAT_VALUE_TRUE
), true))
328 void BVSolverBitblast::initSatSolver()
330 switch (options::bvSatSolver())
332 case options::SatSolverMode::CRYPTOMINISAT
:
333 d_satSolver
.reset(prop::SatSolverFactory::createCryptoMinisat(
334 smtStatisticsRegistry(), "theory::bv::BVSolverBitblast::"));
337 d_satSolver
.reset(prop::SatSolverFactory::createCadical(
338 smtStatisticsRegistry(), "theory::bv::BVSolverBitblast::"));
340 d_cnfStream
.reset(new prop::CnfStream(d_satSolver
.get(),
344 smt::currentResourceManager(),
345 prop::FormulaLitPolicy::INTERNAL
,
346 "theory::bv::BVSolverBitblast"));
349 Node
BVSolverBitblast::getValue(TNode node
, bool initialize
)
356 if (!d_bitblaster
->hasBBTerm(node
))
358 return initialize
? utils::mkConst(utils::getSize(node
), 0u) : Node();
361 std::vector
<Node
> bits
;
362 d_bitblaster
->getBBTerm(node
, bits
);
363 Integer
value(0), one(1), zero(0), bit
;
364 for (size_t i
= 0, size
= bits
.size(), j
= size
- 1; i
< size
; ++i
, --j
)
366 if (d_cnfStream
->hasLiteral(bits
[j
]))
368 prop::SatLiteral lit
= d_cnfStream
->getLiteral(bits
[j
]);
369 prop::SatValue val
= d_satSolver
->modelValue(lit
);
370 bit
= val
== prop::SatValue::SAT_VALUE_TRUE
? one
: zero
;
374 if (!initialize
) return Node();
377 value
= value
* 2 + bit
;
379 return utils::mkConst(bits
.size(), value
);
382 void BVSolverBitblast::handleEagerAtom(TNode fact
, bool assertFact
)
384 Assert(fact
.getKind() == kind::BITVECTOR_EAGER_ATOM
);
388 d_cnfStream
->convertAndAssert(fact
[0], false, false);
392 d_cnfStream
->ensureLiteral(fact
[0]);
395 /* convertAndAssert() does not make the connection between the bit-vector
396 * atom and it's bit-blasted form (it only calls preRegister() from the
397 * registrar). Thus, we add the equalities now. */
398 auto& registeredAtoms
= d_bbRegistrar
->getRegisteredAtoms();
399 for (auto atom
: registeredAtoms
)
401 Node bb_atom
= d_bitblaster
->getStoredBBAtom(atom
);
402 d_cnfStream
->convertAndAssert(atom
.eqNode(bb_atom
), false, false);
404 // Clear cache since we only need to do this once per bit-blasted atom.
405 registeredAtoms
.clear();
409 } // namespace theory