Tactics for Solving Z3 Quantification - z3

I need to solve the following quantified formula, and I have tried some combination of tactics, but Z3 always returns unknown. I obtained the formula from symbolic state subsumption check that we newly implemented on KLEE, which essentially is establishing the validity of entailment of two quantified bitvector formulas representing strongest postconditions. The following formula is the simplified form of the unsatisfiability test to establish an entailment validity. Does anyone know of the right combination of tactics to solve it or is there any reformulation that I could do to help Z3?
(set-logic ABV)
(declare-fun y () (Array (_ BitVec 32) (_ BitVec 8)))
(assert (bvsle (concat (select y #x00000003)
(concat (select y #x00000002)
(concat (select y #x00000001)
(select y #x00000000))))
#x00000000))
(assert (forall ((x (Array (_ BitVec 32) (_ BitVec 8))))
(let ((a!1 (concat (select x #x00000003)
(concat (select x #x00000002)
(concat (select x #x00000001)
(select x #x00000000)))))
(a!2 (concat (select y #x00000003)
(concat (select y #x00000002)
(concat (select y #x00000001)
(select y #x00000000))))))
(not (and (bvsle a!1 #x00000000)
(= ((_ extract 31 0) (bvadd #x00000004 a!1))
((_ extract 31 0) (bvadd #x00000003 a!2))))))))
(check-sat-using (then qe simplify solve-eqs bit-blast sat))
Thank you in advance.

Related

How can I apply strategies and tactics in optimizing z3?

I have a set of optimization problems in QF_BV. How can I efficiently solve them? For example, how can I use (check-sat-using (then simplify bit-blast) ...?
What follows is a fragment of a problem.
(declare-const p1_1 (_ BitVec 36))
(declare-const ps1_1 (_ BitVec 36))
(declare-const pe1_1 (_ BitVec 36))
(assert (= (_ bv0 36) (bvand ps1_1 (bvadd ps1_1 (_ bv1 36)))))
(assert (= (_ bv0 36) (bvand pe1_1 (bvadd pe1_1 (_ bv1 36)))))
(assert (and
(= p1_1 (bvxor ps1_1 pe1_1))
(not (= (_ bv0 36) (bvlshr ps1_1 (_ bv4 36))))
(=> (not (= p1_1 (_ bv0 36))) (= (concat ps1_1 #b1) (bvlshr (concat pe1_1 #b1) (_ bv4 37))))
))
(assert-soft (not (= p1_1 (_ bv0 36))) :weight 1)
(check-sat)
(get-model)
(get-objectives)
The optimizing engine (i.e., the engine that's used when you have assert-soft) doesn't work with tactics as far as I'm aware. It has its own solver and thus doesn't benefit from the general machinery of tactics.
You might want to ask and double check at https://github.com/Z3Prover/z3/discussions to see if there might be some other trick to orchestrate the behavior of the optimizing solver. (Though I doubt such a facility exists as of today.) Please report back here what you find!

How to declare predicates over arbitrary sized bit bit vectors in z3?

I encountered a problem while trying to define some rules over bit vectors in z3. If I try to solve the following file with z3
(declare-rel FunnyFun ((_ BitVec 64)))
(declare-var A (_ BitVec 64))
(declare-var B (_ BitVec 64))
(rule (=> (= B (bvadd A #x0000000000000001))
(FunnyFun B)))
(declare-rel q1 ())
(rule (=> (FunnyFun #x0000000000000001) q1))
(query q1)
I get the error
(error "query failed: Rule contains infinite sorts in rule <null>:
FunnyFun(#0) :-
(= (:var 0) (bvadd (:var 1) #x0000000000000001)).
")
Funnily enough z3 instantly gives the expected result (sat) when using smaller bit widths:
(declare-rel FunnyFun ((_ BitVec 60)))
(declare-var A (_ BitVec 60))
(declare-var B (_ BitVec 60))
(rule (=> (= B (bvadd A #x000000000000001))
(FunnyFun B)))
(declare-rel q1 ())
(rule (=> (FunnyFun #x000000000000001) q1))
(query q1)
Is this a bug or am I missing some constraints (I assumed BitVec can have an arbitrary bit width)?
I tried different z3 version (4.6.0, 4.8.3 and 4.8.5) and all of them showed this behavior.
This limitation is by design. See here: https://github.com/Z3Prover/z3/issues/1698

mixing reals and bit-vectors

I've two SMT2-Lib scripts using reals, which are morally equivalent. The only difference is that one also uses bit-vectors while the other does not.
Here's the version that uses both reals and bit-vectors:
; uses both reals and bit-vectors
(set-option :produce-models true)
(define-fun s2 () Real (root-obj (+ (^ x 2) (- 2)) 2))
(define-fun s3 () Real 0.0)
(define-fun s6 () Real (/ 1.0 1.0))
(declare-fun s0 () (_ BitVec 1))
(declare-fun s1 () (_ BitVec 1))
(assert
(let ((s4 (- s3 s2)))
(let ((s5 (ite (= #b1 s1) s2 s4)))
(let ((s7 (+ s5 s6)))
(let ((s8 (- s5 s6)))
(let ((s9 (ite (= #b1 s0) s7 s8)))
(let ((s10 (ite (>= s9 s3) #b1 #b0)))
(= s10 #b1))))))))
(check-sat)
(get-model)
Here's the morally equivalent script, using Bool instead of a bit-vector of size 1, otherwise it's essentially the same:
; uses reals only
(set-option :produce-models true)
(define-fun s2 () Real (root-obj (+ (^ x 2) (- 2)) 2))
(define-fun s3 () Real 0.0)
(define-fun s6 () Real (/ 1.0 1.0))
(declare-fun s0 () (Bool))
(declare-fun s1 () (Bool))
(assert
(let ((s4 (- s3 s2)))
(let ((s5 (ite s1 s2 s4)))
(let ((s7 (+ s5 s6)))
(let ((s8 (- s5 s6)))
(let ((s9 (ite s0 s7 s8)))
(let ((s10 (ite (>= s9 s3) #b1 #b0)))
(= s10 #b1))))))))
(check-sat)
(get-model)
For the former I get unknown from z3 (v4.1 on Mac), while the latter nicely produces sat and a model.
While SMT-Lib2 doesn't allow mixing reals and bit-vectors, I thought Z3 handled these combinations just fine. Am I mistaken? Is there a workaround?
(Note that these are generated scripts, so just using Bool instead of (_ BitVec 1) is rather costly, as it requires quite a bit of changes elsewhere.)
The new nonlinear solver is not integrated with other theories yet. It supports only real variables and Booleans. Actually, it also allows integer variables, but it is very limited support for them. It actually solves nonlinear integer problems as real problems, and just checks in the end whether each integer variable is assigned to an integer value. Moreover, this solver is the only complete procedure for nonlinear (real) arithmetic available in Z3.
Since your first problem contains Bit-vectors, the nonlinear solver is not used by Z3. Instead, Z3 uses a general purpose solver that combines many theories, but it is incomplete for nonlinear arithmetic.
That being said, I understand this is a limitation, and I'm working on that. In the (not so near) future, Z3 will have a new solver that integrates nonlinear arithmetic, arrays, bit-vectors, etc.
Finally, the bit-vector theory is a very special case, since we can easily reduce it to propositional logic in Z3.
Z3 has tactic bit-blast that applies this reduction. This tactic can reduce any nonlinear+bit-vector problem into a problem that contains only reals and Booleans. Here is an example (http://rise4fun.com/Z3/0xl).
; uses both reals and bit-vectors
(set-option :produce-models true)
(define-fun s2 () Real (root-obj (+ (^ x 2) (- 2)) 2))
(define-fun s3 () Real 0.0)
(define-fun s6 () Real (/ 1.0 1.0))
(declare-fun s0 () (_ BitVec 1))
(declare-fun s1 () (_ BitVec 1))
(declare-fun v2 () (_ BitVec 8))
(assert
(let ((s4 (- s3 s2)))
(let ((s5 (ite (= #b1 s1) s2 s4)))
(let ((s7 (+ s5 s6)))
(let ((s8 (- s5 s6)))
(let ((s9 (ite (= #b1 s0) s7 s8)))
(let ((s10 (ite (>= s9 s3) #b1 #b0)))
(= s10 #b1))))))))
(assert (or (and (not (= v2 #x00)) (not (= v2 #x01))) (bvslt v2 #x00)))
(assert (distinct (bvnot v2) #x00))
(check-sat-using (then simplify bit-blast qfnra))
(get-model)

a datatype contains a set in Z3

how can I make a datatype that contains a set of another objects. Basically, I am doing the following code:
(define-sort Set(T) (Array Int T))
(declare-datatypes () ((A f1 (cons (value Int) (b (Set B))))
(B f2 (cons (id Int) (a (Set A))))
))
But Z3 tells me unknown sort for A and B. If I remove "Set" it works just as the guide states.
I was trying to use List instead but it does not work. Anyone knows how to make it work?
You are addressing a question that comes up on a regular basis:
how can I mix data-types and arrays (as sets, multi-sets or
data-types in the range)?
As stated above Z3 does not support mixing data-types
and arrays in a single declaration.
A solution is to develop a custom solver for the
mixed datatype + array theory. Z3 contains programmatic
APIs for developing custom solvers.
It is still useful to develop this example
to illustrate the capabilities and limitations
of encoding theories with quantifiers and triggers.
Let me simplify your example by just using A.
As a work-around you can define an auxiliary sort.
The workaround is not ideal, though. It illustrates some
axiom 'hacking'. It relies on the operational semantics
of how quantifiers are instantiated during search.
(set-option :model true) ; We are going to display models.
(set-option :auto-config false)
(set-option :mbqi false) ; Model-based quantifier instantiation is too powerful here
(declare-sort SetA) ; Declare a custom fresh sort SetA
(declare-datatypes () ((A f1 (cons (value Int) (a SetA)))))
(define-sort Set (T) (Array T Bool))
Then define bijections between (Set A), SetA.
(declare-fun injSA ((Set A)) SetA)
(declare-fun projSA (SetA) (Set A))
(assert (forall ((x SetA)) (= (injSA (projSA x)) x)))
(assert (forall ((x (Set A))) (= (projSA (injSA x)) x)))
This is almost what the data-type declaration states.
To enforce well-foundedness you can associate an ordinal with members of A
and enforce that members of SetA are smaller in the well-founded ordering:
(declare-const v Int)
(declare-const s1 SetA)
(declare-const a1 A)
(declare-const sa1 (Set A))
(declare-const s2 SetA)
(declare-const a2 A)
(declare-const sa2 (Set A))
With the axioms so far, a1 can be a member of itself.
(push)
(assert (select sa1 a1))
(assert (= s1 (injSA sa1)))
(assert (= a1 (cons v s1)))
(check-sat)
(get-model)
(pop)
We now associate an ordinal number with the members of A.
(declare-fun ord (A) Int)
(assert (forall ((x SetA) (v Int) (a A))
(=> (select (projSA x) a)
(> (ord (cons v x)) (ord a)))))
(assert (forall ((x A)) (> (ord x) 0)))
By default quantifier instantiation in Z3 is pattern-based.
The first quantified assert above will not be instantiated on all
relevant instances. One can instead assert:
(assert (forall ((x1 SetA) (x2 (Set A)) (v Int) (a A))
(! (=> (and (= (projSA x1) x2) (select x2 a))
(> (ord (cons v x1)) (ord a)))
:pattern ((select x2 a) (cons v x1)))))
Axioms like these, that use two patterns (called a multi-pattern)
are quite expensive. They produce instantiations for every pair
of (select x2 a) and (cons v x1)
The membership constraint from before is now unsatisfiable.
(push)
(assert (select sa1 a1))
(assert (= s1 (injSA sa1)))
(assert (= a1 (cons v s1)))
(check-sat)
(pop)
but models are not necessarily well formed yet.
the default value of the set is 'true', which
would mean that the model implies there is a membership cycle
when there isn't one.
(push)
(assert (not (= (cons v s1) a1)))
(assert (= (projSA s1) sa1))
(assert (select sa1 a1))
(check-sat)
(get-model)
(pop)
We can approximate more faithful models by using
the following approach to enforce that sets that are
used in data-types are finite.
For example, whenever there is a membership check on a set x2,
we enforce that the 'default' value of the set is 'false'.
(assert (forall ((x2 (Set A)) (a A))
(! (not (default x2))
:pattern ((select x2 a)))))
Alternatively, whenever a set occurs in a data-type constructor
it is finite
(assert (forall ((v Int) (x1 SetA))
(! (not (default (projSA x1)))
:pattern ((cons v x1)))))
(push)
(assert (not (= (cons v s1) a1)))
(assert (= (projSA s1) sa1))
(assert (select sa1 a1))
(check-sat)
(get-model)
(pop)
Throughout the inclusion of additional axioms,
Z3 produces the answer 'unknown' and furthermore
the model that is produced indicates that the domain SetA
is finite (a singleton). So while we could patch the defaults
this model still does not satisfy the axioms. It satisfies
the axioms modulo instantiation only.
This is not supported in Z3. You can use arrays in datatype declarations, but they can't contain "references" to the datatypes you are declaring. For example, it is ok to use (Set Int).

Z3: Extracting existential model-values

I'm playing around with Z3's QBVF solver, and wondering if it's possible to extract values from an existential assertion. To wit, let's say I have the following:
(assert (exists ((x (_ BitVec 16))) (forall ((y (_ BitVec 16))) (bvuge y x))))
This basically says that there is a "least" 16-bit unsigned value. Then, I can say:
(check-sat)
(get-model)
And Z3-3.0 happily responds:
sat
(model (define-fun x!0 () (_ BitVec 16)
#x0000)
)
Which is really cool. But what I want to do is to be able to extract pieces of that model via get-value. Unsurprisingly, none of the following seem to work
(get-value (x))
(get-value (x!0))
In each case Z3 rightly complains there's no such constant. Clearly Z3 has that information as evidenced by the response to the (check-sat) call. Is there any way to access the existential value automatically via get-value, or some other mechanism?
Thanks..
In Z3, get-value only allows the user to reference “global” declarations.
The existential variable x is a local declaration. Thus, it can’t be accessed using get-value.
By default, Z3 eliminates existential variables using a process called “skolemization”.
The idea is to replace existential variables with fresh constants and function symbols.
For example, the formula
exists x. forall y. exists z. P(x, y, z)
is converted into
forall y. P(x!1, y, z!1(y))
Note that z becomes a function because the choice of z may depend on y.
Wikipedia has an entry on skolem normal form
That being said, I never found a satisfactory solution for the problem you described.
For example, a formula may have many different existential variables with the same name.
So, it is not clear how to reference each instance in the get-value command in a non-ambiguous way.
A possible workaround for this limitation is to apply the skolemization step “by hand”, or at least for the variables you want to know the value.
For example,
(assert (exists ((x (_ BitVec 16))) (forall ((y (_ BitVec 16))) (bvuge y x))))
is written as:
(declare-const x (_ BitVec 16))
(assert (forall ((y (_ BitVec 16))) (bvuge y x)))
(check-sat)
(get-value x)
If the existential variable is nested in a universal quantifier such as:
(assert (forall ((y (_ BitVec 16))) (exists ((x (_ BitVec 16))) (bvuge y x))))
(check-sat)
(get-model)
A fresh skolem function can be used to obtain the value of x for each y.
The example above becomes:
(declare-fun sx ((_ BitVec 16)) (_ BitVec 16))
(assert (forall ((y (_ BitVec 16))) (bvuge y (sx y))))
(check-sat)
(get-model)
In this example, sx is the fresh function. The model, produced by Z3, will assign an interpretation for sx. In version 3.0, the interpretation is the identity function. This function can be used to obtain the value of x for each y.

Resources