Unused kernels are tracked using "response literals" (e.g. assumptions). When we enable inactive kernel extracts and uses, such as
(assert (! (= x 10) :named a1))
Z3 will internally create a new boolean for the name a1 and claim
(assert (=> a1 (= x 10)))
When check-sat is called, it assumes that all of these helper variables are true. That is, Z3 is trying to show that the problem is unsat / sat modulo these assumptions. For feasible examples, it will be completed, as usual, using the model. For unsatisfactory instances, it stops whenever it generates a lemma containing only these alleged Boolean variables. The lemma has the form (or (not a_i1) ... (not a_in)) , where a_i is a subset of the proposed Boolean variables. As far as I know, this technique was introduced by the MiniSAT solver. It is described here (section 3). I really like it because itβs easy to implement, and we are essentially freeing up the base generation for free.
However, this approach has some disadvantages. First, some preprocessing steps no longer apply. If we just say that
(assert (= x 10))
Z3 will replace x with 10 everywhere. We say that the Z3 performs βvalue distribution.β This preprocessing step does not apply if the assertion is of the form
(assert (=> a1 (= x 10)))
This is just an example, many other preprocessing steps affect it. During the solution, some of the simplification steps are also disabled. If we check the Z3 source file smt_context.cpp , we find the code, for example:
void context::simplify_clauses() {
The condition m_scope_lvl > m_base_lvl) always true when "response literals" / assumptions are used. Thus, when we include an unreliable base generation, we can really affect performance. It seems that nothing is really free :)
Leonardo de Moura Feb 21 '13 at 19:36 2013-02-21 19:36
source share