Implicit vs explicit existential quantification in SMT formulas - z3

Given the following code:
from z3 import *
a,b,c = BitVecs('a b c', 32)
f1 = Exists([a, b, c], And(a + b == c, a < b, c == 1337))
f2 = And(a + b == c, a < b, c == 1337)
prove(f1 == f2)
I would assume that z3 implicitly existential quantifies a, b and c, in this example. Why aren't the two formulas equal, what is the difference?

The way you formulated your query doesn't really check whether f1 equals f2. Your query is essentially asking the solver to find a, b, c such that the following fails to hold:
Exists([a, b, c], And(a + b == c, a < b, c == 1337))
=
And(a + b == c, a < b, c == 1337))
And indeed, you can instantiate the outer a, b, and c such that the right hand-side is false; but the left hand side is an existential which is true; thus failing the equivalence you asked for.
It might be easier to see this with a simpler example; with just one boolean variable. You're essentially asking:
x == (Exists [x], x)
You see that those xs are actually different, so we can rename the inner one to (say) y; we get:
x == (Exist [y]. y)
Now, the right-hand-side is clearly true since there is a y that makes (Exist [y]. y) true. So, you are essentially asking the prover to establish that no matter what x you pick, it is true. Which is definitely not the case when you pick x to be false.
Indeed, you can ask Z3 to give you the formula it's trying to prove, and this is what it returns for your original query:
(set-info :status unknown)
(declare-fun c () (_ BitVec 32))
(declare-fun b () (_ BitVec 32))
(declare-fun a () (_ BitVec 32))
(assert
(let (($x24 (exists ((a (_ BitVec 32))
(b (_ BitVec 32))
(c (_ BitVec 32)) )
(and (= (bvadd a b) c) (bvslt a b) (= c (_ bv1337 32))))))
(let (($x57 (= $x24 (and (= (bvadd a b) c) (bvslt a b) (= c (_ bv1337 32))))))
(and $x57))))
(check-sat)
Which is clearly satisfiable, by the above reasoning.
(See Z3: convert Z3py expression to SMT-LIB2 from Solver object for the code that converts a z3-python query to smt-lib.)

Related

3-SAT formulas as an SMT-LIB

I try to find whether there are such values of A-F that make the Boolean expression TRUE.
I use online z3 solver (https://jfmc.github.io/z3-play/)
It gives error
Error: (error "line 11 column 12: Wrong number of arguments (4) passed to function (declare-fun A () Bool)")
sat
This is my code:
(set-logic QF_LIA)
(declare-const A Bool)
(declare-const B Bool)
(declare-const C Bool)
(declare-const D Bool)
(declare-const E Bool)
(declare-const F Bool)
(assert
(and
(A or B or C)
(D or E or F)
))
(check-sat)
(get-model)
(exit)
In SMTLib, functions are written in prefix-notation. So, instead of:
(assert
(and
(A or B or C)
(D or E or F)
))
you should use:
(assert (and (or A B C)
(or D E F)
)
)
This is similar to languages like Lisp/Scheme, where prefix-notation makes the syntax simple to parse/manipulate by programs.

Handling overflow in SMT-LIB/Z3

I'm trying to model the following expression using SMT a + b - c - d, all constants a,b,c,d are bitvecs of the same size, constraint with the following assert a + b >= c + d. I want to model it in such a way that overflows/underflows don't happen.
This is what I have tried so far:
(declare-const a (_ BitVec 4))
(declare-const b (_ BitVec 4))
(declare-const c (_ BitVec 4))
(declare-const d (_ BitVec 4))
(assert (bvuge (bvadd a b) (bvadd c d)))
; this is an inaccurate, only checks the last operation for underflow
; (a+b-c-d) >= d
; (assert (bvuge (bvsub (bvsub (bvadd a b) c) d) d))
;
; this should model that either both sides overflow, neither,
; or only the expression to the left of the inequality
; (
; (a + b <= a and c + d <= c and a + b >= c + d) or
; (a + b >= a and c + d <= c) or
; (a + b >= a and c + d >= c and a + b >= c + d)
; )
;(assert (or
; (and (bvule (bvadd a b) a)
; (bvule (bvadd c d) c)
; (bvuge (bvadd a b) (bvadd c d)))
; (and (bvuge (bvadd a b) a)
; (bvule (bvadd c d) c))
; (and (bvuge (bvadd a b) a)
; (bvuge (bvadd c d) c)
; (bvuge (bvadd a b) (bvadd c d)))))
(assert (bvuge (bvsub (bvsub (bvadd a b) c) d) #x0))
(check-sat)
(get-model)
But I'm not sure if the constraint are sufficient.
A better strategy is to "extend" to larger bit-vectors and check that the result fits in the bit-vector size you want. Note that proper underflow/overflow checking for bit-vectors can be surprisingly tricky, especially in the presence of multiplication.
Luckily, there's an excellent paper that describes how to do this properly: https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/z3prefix.pdf
(There are a few minor issues with the paper: a few typos here and there, and the signed-multiplication overflow/underflow formula is outdated. But it's an excellent resource to start with!)
Furthermore, Z3 also provides out-of-the-box checker predicates for multiplication under/over-flows.
Once you review the paper, feel free to ask further questions regarding its contents if you still have issues!

Negation in z3 datalog for expressing optimality

I use Z3 with :fixedpoint.engine set to datalog.
I have an enumerated filter relation (f pos min max). Let us say that we have (f #x10 #x100000 #x200000), (f #x20 #x150000 #x200000) and (f #x20 #x300000 #x500000).
For a given x, I search the greatest pos such that (f pos min max) and min <= x <= max (Here I use intervals but filters can be arbitrarily complex). Priority (sort t) and values (sort s) are BitVectors but x, min and max are on a rather large space (eg. 24 bits).
; configuration
(set-option :fixedpoint.engine datalog)
; sorts
(define-sort s () (_ BitVec 24))
(define-sort t () (_ BitVec 8))
; Relations
(declare-rel f (t s s))
(declare-rel match (t s))
(declare-rel better (t s))
(declare-rel best (t s))
(declare-rel a (t))
(declare-rel b ())
(declare-rel c ())
(declare-var x s)
(declare-var xmin s)
(declare-var xmax s)
(declare-var p t)
(declare-var q t)
; Facts (EDB)
(rule (f #x10 #x100000 #x200000))
(rule (f #x20 #x150000 #x200000))
(rule (f #x20 #x300000 #x500000))
; Rules
(rule (=> (and (f p xmin xmax) (bvule xmin x) (bvule x xmax))
(match p x)))
(rule (=> (and (match q x) (bvugt q p))
(better p x)))
(rule (=> (and (match p x) (not (better p x)))
(best p x)))
; Queries
(rule (=> (match p #x170000) (a p)))
(rule (=> (better #x10 #x170000) b))
(rule (=> (best #x10 #x170000) c))
; output: sat
; (or (= (:var 0) #x20) (= (:var 0) #x10))
(query (a p) :print-answer true)
; output: sat
(query b)
; Output 'WARNING: creating large table of size 16777216 for relation better' and fails
(query c)
(match p x) codes the fact that a filter at priority p filters x.
(better p x) if a rule with a better priority than p filters x.
(best p x) codes that the best filter matching x has priority p.
If I query (match p #x170000), I quickly get #x10 and #x20. If I ask (better #x10 #x170000) I quickly get
an answer same for priority #20. But the query on (best p #x170000) fails to execute in reasonable time and reasonable space.
It seems that (not (better p x)) is computed independently of (match p x) and so is represented by a very large table (the possible values of x are not forwarded). In some cases I can restrict x by some tricks in better (sometimes I know that I am only interested by x that explicitly appear in other relations) so that the space is reduced but this is not a real generic solutions and sometimes I am stuck.
How should I rephrase the problem or which options should I use to avoid this problem ?
Z3's default Datalog tables are over concrete values, so if you use large bit-vectors, Z3 may end-up creating huge tables.
You can try a simpler table data-structure, that supports fewer operations but it's sparse (uses "don't care" bits).
You can try it out with: z3 fixedpoint.engine=datalog fixedpoint.datalog.default_relation=doc file.smt2

Converting Z3 QBF formula directly to pcnf

I'm using Z3 theorem prover (using Z3Py: the Z3 API in Python) to create QBF (Quantified Boolean formula).
Is there any way in Z3 to directly convert your qbf formula into Prenex normal form ?
I don't think there's a tactic to convert to Prenex, but you can surely apply the quantifier-elimination tactic and further process your formulas. Note that the transformed formulas will not really look like the originals, as they are mechanically generated.
Here's an example:
from z3 import *
f = Function('f', IntSort(), IntSort(), IntSort())
x, y = Ints('x y')
p = ForAll(x, Or(x == 2, Exists(y, f (x, y) == 0)))
print Tactic('qe')(p)
Here qe is the quantifier elimination tactic. This produces:
[[Not(Exists(x!0,
Not(Or(x!0 == 2,
Exists(x!1,
And(f(x!0, x!1) <= 0,
f(x!0, x!1) >= 0))))))]]
For a nice tutorial on tactics, see here: http://ericpony.github.io/z3py-tutorial/strategies-examples.htm
You could use the skolemize tactic (snf) which will by definition be in prenex form. However it will also eliminate existential quantifiers which is not what you want. Here's an example.
(declare-fun a (Int) Bool)
(declare-fun b (Int) Bool)
(declare-fun c (Int) Bool)
(assert
(forall ((x Int))
(or
(exists ((y Int))
(a y)
)
(exists ((z Int))
(=>
(b z)
(c x)
)
)
)
)
)
(apply
(and-then
; mode (symbol) NNF translation mode: skolem (skolem normal form), quantifiers (skolem normal form + quantifiers in NNF), full (default: skolem)
(using-params snf :mode skolem)
)
:print_benchmark true
:print false
)
When Z3 is given the above it will responds with something like
(declare-fun c (Int) Bool)
(declare-fun b (Int) Bool)
(declare-fun a (Int) Bool)
(assert (forall ((x Int))
(or (exists ((y Int)) (a y)) (exists ((z Int)) (=> (b z) (c x))))))
(check-sat)
You can see the available tactics by running
echo "(help-tactic)" | ./z3 -in | less
from a bash shell.
Unfortunately I can't see one that states it does conversion to prenex.

Skolemization in Z3

I am trying to remove existential quantifiers in my theory using Skolemization. This means that I replace existential quantifiers with functions that are parameterized by the universally quantified variables in the scope of the existential quantifiers.
Here I found an explanation how to do this in Z3, but I am still having troubles doing it. Suppose the following two functions:
(define-fun f1 ((t Int)) Bool (= t 3))
(define-fun f2 () Bool (exists ((t Int)) (f1 t)))
I believe that f2 should be true, because there exists an integer t such that (f1 t) is true, namely t=3. I apply Skolemization by introducing a constant for the existentially quantified formula:
(define-const c Int)
Then the formula with the existential quantifier is rewritten to:
(define-fun f2 () Bool (f1 c))
This does not work, that is, the constant c does not have the value 3. I suspect it is because we have not given an interpretation to the constant c, because if we add (assert (= c 3)) it works fine, but this takes away the whole idea of the existential quantifier. Is there a way in which I give a less explicit interpretation to c so that this will work?
So, I think you have it about right actually, here's the script I used with automatic (via Z3's SNF tactic) and manual (via adding the constant c) skolemization, which gave the value 3 in the model for the skolem constant as expected (smt-lib script: http://rise4fun.com/Z3/YJy2 ):
(define-fun f1 ((t Int)) Bool (= t 3))
(define-fun f2 () Bool (exists ((t Int)) (f1 t)))
(declare-const c Int)
(define-fun f2a () Bool (f1 c))
(push)
(assert f2)
(check-sat) ; sat
(get-model) ; model gives t!0 = 3 (automatic skolemization in Z3)
(pop)
(push)
(assert f2a)
(check-sat) ; sat
(get-model) ; model gives c = 3 after manual skolemization
(pop)
Also, note that Z3 has a Skolem normal form (SNF) conversion tactic built in, and here's an example in z3py (link to script: http://rise4fun.com/Z3Py/ZY2D ):
s = Solver()
f1 = Function('f1', IntSort(), BoolSort())
t = Int('t')
f2 = Exists(t, f1(t))
f1p = ForAll(t, f1(t) == (t == 3)) # expanded define-fun macro (define-fun f1 ((t Int)) Bool (= t 3))
s.add(f1p)
s.add(f2)
print f1p
print f2
print s.check()
print s.model() # model has skolem constant = 3
g = Goal()
g.add(f1p)
g.add(f2)
t = Tactic('snf') # use tactic to convert to SNF
res = t(g)
print res.as_expr()
s = Solver()
s.add( res.as_expr() )
print s.check()
print s.model() # model has skolem constant = 3

Resources