Calculate minimum value of Bitvector in Z3 - z3

I have the following piece of code:
(declare-const L4 (_ BitVec 6))
(declare-const L1 (_ BitVec 6))
(declare-const L0 (_ BitVec 6))
(declare-const l2 (_ BitVec 6))
(assert (= l2 (_ bv8 6)))
;; All is encoding the set that contains {0, 1, 2, 3, 4, 5}
(define-const All (_ BitVec 6) #b111111)
;; Empty is encoding the empty set
(define-const Empty (_ BitVec 6) #b000000)
(define-fun LT_l ((S (_ BitVec 6)) (l (_ BitVec 6))) Bool
;; True if for all x in S x < l
(= (bvand (bvshl All l) S) Empty))
(define-fun is_in ((e (_ BitVec 6)) (S (_ BitVec 6))) Bool
;; True if e is an element of the "set" S.
(not (= (bvand (bvshl (_ bv1 6) e) S) Empty)))
(define-fun is_minimal ((e (_ BitVec 6)) (S (_ BitVec 6))) Bool
;; True if e is the minimal element of S
(and (is_in e S) ;; S contains e
;; (1 << e) - 1 represents the set of elements that are smaller than e
(= (bvand (bvsub (bvshl (_ bv1 6) e) (_ bv1 6)) S) Empty)))
;; To encode that forall x in L0 and forall y in L1. x < y
(define-fun LT ((L0 (_ BitVec 6)) (L1 (_ BitVec 6))) Bool
; True if forall x in L0 and forall y in L1, x < y
(or (= L0 Empty)
(= L1 Empty)
(exists ((min (_ BitVec 6))) (and (is_minimal min L1) (LT_l L0 min)))))
(assert (not (= L0 Empty)))
(assert (not (= L1 Empty)))
(assert (not (= L4 Empty)))
(assert (LT_l L4 l2))
(assert (LT L0 L1))
(check-sat)
(get-model)
(assert (LT L1 L0))
(check-sat)
When I run this code I get the model is:
sat
(model
(define-fun min!0 () (_ BitVec 6)
#b000011)
(define-fun L1 () (_ BitVec 6)
#b001000)
(define-fun L0 () (_ BitVec 6)
#b000100)
(define-fun L4 () (_ BitVec 6)
#b000100)
(define-fun l2 () (_ BitVec 6)
#b001000)
)
unsat
Why is the result of min is:
(define-fun min!0 () (_ BitVec 6)
#b000011)
and not b001000 since the smallest value of L1 is this and not b000011.
Someone can explain me?
Finally, I define the function Lt_l that checks if for all x in S x < l, but now I wanted to do GT_l that checks if for all x in S l < x. I have the following code:
(define-fun GT_l ((S (_ BitVec 6)) (l (_ BitVec 6))) Bool
(= (bvand (bvneg (bvshl (_ bv0 6) l)) S) Empty))
But this does not work why?
Thanks

In your example, you are representing sets using bit-vectors. For example, the bit-vector #b101000 represents the set {5, 3}. The output (define-fun L1 () (_ BitVec 6) #b001000) is essentially saying that L1 is the "set" {3}. One possible confusion is that bit-vectors are being used to represent sets and elements. The bit-vector min!0 represents an element. The output (define-fun min!0 () (_ BitVec 6) #b000011) is saying that min!0 is the value 3, and it is indeed the "minimal value" in L1.

Related

z3 bitvector operation simplified answer

Is there an easier way to get the immediate answers when doing bit-vector ops eg. a = 1 million, b = 0, what is a & b (answer: 0)
This method works but have to introduce dummy variable to store answer:
(declare-const a (_ BitVec 64))
(declare-const b (_ BitVec 64))
(declare-const ans (_ BitVec 64))
(assert (= a (_ bv1000000 64)))
(assert (= b (_ bv0000000 64)))
(assert (= ans (bvand a b)))
(check-sat)
(get-model)
This method is what I'd like but my code gives a demorgan identity:
(declare-const a (_ BitVec 64))
(declare-const b (_ BitVec 64))
(simplify (bvand a b))
You can use the model to evaluate arbitrary expressions, for instance like this:
(declare-const a (_ BitVec 64))
(declare-const b (_ BitVec 64))
(assert (= a (_ bv1000000 64)))
(assert (= b (_ bv0000000 64)))
(check-sat)
(eval (bvand a b))
says
sat
#x0000000000000000
I didn't test, but something like (apply (then propagate-values simplify)) should do the trick

Bug in Z3 Datalog

For the datalog program in Z3 at the bottom, the result of query
(query (CallGraph invo heap):print-answer true)
given by Z3 is:
sat
(and (= (:var 0) #b011) (= (:var 1) #b1))
However, the answer should be
sat
(and (= (:var 0) #b1) (= (:var 1) #b011))
am I right? is it a bug in Z3?
(set-option :fixedpoint.engine datalog)
(declare-rel VarPointsTo ( (_ BitVec 4) (_ BitVec 3)))
(declare-rel Reachable ( (_ BitVec 3)))
(declare-rel Alloc ( (_ BitVec 4) (_ BitVec 3) (_ BitVec 3)))
(declare-rel Move ( (_ BitVec 4) (_ BitVec 4)))
(declare-rel FldPointsTo ( (_ BitVec 3) (_ BitVec 1) (_ BitVec 3)))
(declare-rel Store ( (_ BitVec 4) (_ BitVec 4) (_ BitVec 4)))
(declare-rel StrMap ( (_ BitVec 4) (_ BitVec 1)))
(declare-rel Load ( (_ BitVec 4) (_ BitVec 4) (_ BitVec 4)))
(declare-rel VCall ( (_ BitVec 4) (_ BitVec 1) (_ BitVec 3)))
(declare-rel CallGraph ( (_ BitVec 1) (_ BitVec 3)))
(declare-rel InterProcAssign ( (_ BitVec 4) (_ BitVec 4)))
(declare-rel FormalArg ( (_ BitVec 3) (_ BitVec 1) (_ BitVec 4)))
(declare-rel ActualArg ( (_ BitVec 1) (_ BitVec 1) (_ BitVec 4)))
(declare-rel FormalReturn ( (_ BitVec 3) (_ BitVec 4)))
(declare-rel ActualReturn ( (_ BitVec 1) (_ BitVec 4)))
(declare-var var (_ BitVec 4))
(declare-var heap (_ BitVec 3))
(declare-var methHeap (_ BitVec 3))
(declare-var to (_ BitVec 4))
(declare-var from (_ BitVec 4))
(declare-var baseH (_ BitVec 3))
(declare-var fld (_ BitVec 1))
(declare-var base (_ BitVec 4))
(declare-var toMethHeap (_ BitVec 3))
(declare-var invo (_ BitVec 1))
(declare-var inMethHeap (_ BitVec 3))
(declare-var n (_ BitVec 1))
(rule (=> (and (Reachable methHeap) (Alloc var heap methHeap) )(VarPointsTo var heap)))
(rule (=> (and (Move to from) (VarPointsTo from heap) )(VarPointsTo to heap)))
(rule (=> (and (Store base var from) (and (VarPointsTo from heap) (and (VarPointsTo base baseH) (StrMap var fld) )))(FldPointsTo baseH fld heap)))
(rule (=> (and (Load to var base) (and (VarPointsTo base baseH) (and (FldPointsTo baseH fld heap) (StrMap var fld) )))(VarPointsTo to heap)))
(rule (=> (and (VCall var invo inMethHeap) (and (Reachable inMethHeap) (VarPointsTo var toMethHeap) ))(Reachable toMethHeap)))
(rule (=> (and (VCall var invo inMethHeap) (and (Reachable inMethHeap) (VarPointsTo var toMethHeap) ))(CallGraph invo toMethHeap)))
(rule (=> (and (CallGraph invo methHeap) (and (FormalArg methHeap n to) (ActualArg invo n from) ))(InterProcAssign to from)))
(rule (=> (and (CallGraph invo methHeap) (and (FormalReturn methHeap from) (ActualReturn invo to) ))(InterProcAssign to from)))
(rule (=> (and (InterProcAssign to from) (VarPointsTo from heap) )(VarPointsTo to heap)))
(rule (Alloc #b0001 #b001 #b010))
(rule (Alloc #b0010 #b001 #b010))
(rule (Reachable #b001))
(rule (Reachable #b010))
(rule (Alloc #b0011 #b011 #b001))
(rule (Alloc #b0100 #b100 #b011))
(rule (Move #b0101 #b0100))
(rule (StrMap #b0110 #b1))
(rule (Store #b0001 #b0110 #b0011))
(rule (StrMap #b0111 #b1))
(rule (Load #b1000 #b0111 #b0001))
(rule (VCall #b1000 #b1 #b001))
(rule (ActualReturn #b1 #b1001))
(query (VarPointsTo var heap):print-answer true)
(query (CallGraph invo heap):print-answer true)
(query (Reachable heap):print-answer true)
This does not appear to reproduce in the version of Z3 checked into GitHub/z3prover/z3 master branch.
The way the engine associates variable indices with variable names has been brittle and there might still be a way to trigger this bug with the newest version of Z3, although I cannot reproduce it.
The binary API exposes a more reliable API: one poses a query given one or more predicate declarations (The function from the C API is called Z3_fixedpoint_query_relations, and the other supported programming languages support similarly named functions).

BitVector in Z3 - functions for different bits

I have this code to check if other elements are contained sets.
;; All is encoding the set that contains {0, 1, 2, 3, 4, 5}
(define-const All (_ BitVec 6) #b111111)
;; Empty is encoding the empty set
(define-const Empty (_ BitVec 6) #b000000)
(define-fun LT_l ((S (_ BitVec 6)) (l (_ BitVec 6))) Bool
;; True if for all x in S x < l
(= (bvand (bvshl All l) S) Empty))
(define-fun GT_l ((l (_ BitVec 6)) (S (_ BitVec 6))) Bool
;; True if for all x in S l < x
(= (bvand (bvnot (bvshl All l)) S) Empty))
(define-fun is_in ((e (_ BitVec 6)) (S (_ BitVec 6))) Bool
;; True if e is an element of the "set" S.
(not (= (bvand (bvshl (_ bv1 6) e) S) Empty)))
(define-fun is_minimal ((e (_ BitVec 6)) (S (_ BitVec 6))) Bool
(and (is_in e S)
(= (bvand (bvsub (bvshl (_ bv1 6) e) (_ bv1 6)) S) Empty)))
(define-fun LT ((L0 (_ BitVec 6)) (L1 (_ BitVec 6))) Bool
; True if forall x in L0 and forall y in L1, x < y
(or (= L0 Empty)
(= L1 Empty)
(exists ((min (_ BitVec 6))) (and (is_minimal min L1) (LT_l L0 min)))))
(declare-const consoleLock (_ BitVec 6))
(declare-const l1 (_ BitVec 6))
(declare-const l2 (_ BitVec 6))
( assert (distinct consoleLock l1 l2 ) )
( assert (or (= l1 (_ bv0 6)) (= l1 (_ bv1 6)) (= l1 (_ bv2 6)) (= l1 (_ bv4 6)) ))
( assert (or (= l2 (_ bv0 6)) (= l2 (_ bv1 6)) (= l2 (_ bv2 6)) (= l2 (_ bv4 6)) ))
( assert (or (= consoleLock (_ bv0 6)) (= consoleLock (_ bv1 6)) (= consoleLock (_ bv2 6)) (= consoleLock (_ bv4 6)) ))
(declare-const L4 (_ BitVec 6))
(declare-const L1 (_ BitVec 6))
(declare-const L0 (_ BitVec 6))
(declare-const L5 (_ BitVec 6))
(assert (LT_l L0 l1))
(assert (LT L0 L1))
(assert (GT_l L1 l1))
(assert (LT_l L4 l2))
(assert (LT L4 L5))
(assert (GT_l L5 l2))
(declare-const T1 (_ BitVec 6))
(assert (= T1 l1))
(assert (LT_l T1 l2))
(declare-const T2 (_ BitVec 6))
(assert (= T2 l2))
(assert (LT_l T2 l1))
(check-sat)
(get-model)
My problem is that you want to use this code also for vectors with 8-bit and 16-bit but it doesn't work.
For example, if I replace all (_ BitVec 6) by (_ BitVec 8), the above code does not work well, because the result should be unsat but it sat.
As if to 6-bit vectors works well.
How can I make it work for different sizes of bit vectors?
We also have to adjust the constant occurring in the example: #b111111, #b000000, (_ bv1 6), etc. That being said, SMT-LIB 2.0 format is not very convenient for writing parametric problems. I think the programmatic API is easier to use to encode parametric problems.
Here is the same example encoded using the Z3 Python API. It is also available online here. We can change the size of the bit-vectors by replacing SZ = 6 with SZ = 8 or SZ = 16.
def All(sz):
return BitVecVal(2**sz - 1, sz)
def Empty(sz):
return BitVecVal(0, sz)
def LT_l(S, l):
sz = S.size()
return (All(sz) << l) & S == Empty(sz)
def GT_l(l, S):
sz = S.size()
return (~(All(sz) << l)) & S == Empty(sz)
def is_in(e, S):
sz = S.size()
one = BitVecVal(1, sz)
return (1 << e) & S != Empty(sz)
def is_minimal(e, S):
sz = S.size()
return And(is_in(e, S), ((1 << e) - 1) & S == Empty(sz))
def LT(L0, L1):
sz = L0.size()
min = BitVec('min', sz)
return Or(L0 == Empty(sz), L1 == Empty(sz), Exists([min], And(is_minimal(min, L1), LT_l(L0, min))))
SZ=6
consoleLock = BitVec('consoleLock', SZ)
l1 = BitVec('l1', SZ)
l2 = BitVec('l2', SZ)
s = Solver()
s.add(Distinct(consoleLock, l1, l2))
s.add(Or(l1 == 0, l1 == 1, l1 == 2, l1 == 4))
s.add(Or(l2 == 0, l2 == 1, l2 == 2, l2 == 4))
s.add(Or(consoleLock == 0, consoleLock == 1, consoleLock == 2, consoleLock == 4))
L4 = BitVec('L4', SZ)
L1 = BitVec('L1', SZ)
L0 = BitVec('L0', SZ)
L5 = BitVec('L5', SZ)
s.add(LT_l(L0, l1))
s.add(LT(L0, L1))
s.add(GT_l(L1, l1))
s.add(LT_l(L4, l2))
s.add(LT(L4, L5))
s.add(GT_l(L5, l2))
T1 = BitVec('T1', SZ)
s.add(T1 == l1)
s.add(LT_l(T1, l2))
T2 = BitVec('T2', SZ)
s.add(T2 == l2)
s.add(LT_l(T2, l1))
print s.check()

Member of a bitvector in Z3

I need to know how to do the following in z3:
forall x in L4 . x < l2
forall x in L0 and forall y in L1. x < y
(declare-const L4 (_ BitVec 6))
(declare-const L1 (_ BitVec 6))
(declare-const L0 (_ BitVec 6))
(declare-const l2 Int)
(declare-const l1 Int)
assert L0 and L1 such that x < y
(check-sat)
Result is Sat
assert L0 and L1 such that y < x
(check-sat)
Result is UnSat
I'm assuming you are using bit-vectors to encode finite sets.
So, here is a possible encoding (it is also available online here):
;; You use bit-vectors of size 6. That is, we are encoding sets that are subsets of {0, 1, 2, ... 5}
(declare-const L4 (_ BitVec 6))
(declare-const L1 (_ BitVec 6))
(declare-const L0 (_ BitVec 6))
;; I will also encode l1 and l2 as Bit-Vectors.
;; Thus, I don't need to convert them into Bit-vectors to be able to use them in Bit-vector operations.
(declare-const l1 (_ BitVec 6))
(declare-const l2 (_ BitVec 6))
;; To make the problems more interesting let us assume that l1 and l2 are <= 5
(assert (bvsle l1 (_ bv5 6)))
(assert (bvsle l2 (_ bv5 6)))
;; All is encoding the set that contains {0, 1, 2, 3, 4, 5}
(define-const All (_ BitVec 6) #b111111)
;; Empty is encoding the empty set
(define-const Empty (_ BitVec 6) #b000000)
(define-fun LT_l ((S (_ BitVec 6)) (l (_ BitVec 6))) Bool
;; True if for all x in S x < l
;; Note that: All << l is the set of values greater than l
;; In SMT-LIB, bvshl is the left shift operator <<
(= (bvand (bvshl All l) S) Empty))
;; We can use shifting to encode the set that contains all values in {0, 1, 2, 3, 4, 5} that greater than or equal to l2
(define-const GE_l2 (_ BitVec 6) (bvshl All l2))
;; To assert that forall x in L4. x < l2, we assert that the intersection of L4 and GE_l2 is empty
(assert (= (bvand GE_l2 L4) Empty))
(check-sat)
(get-model)
(define-fun is_in ((e (_ BitVec 6)) (S (_ BitVec 6))) Bool
;; True if e is an element of the "set" S.
;; That is, it checks whether the (1 << e) && S is 0
(not (= (bvand (bvshl (_ bv1 6) e) S) Empty)))
(echo "is_in tests")
;; Test: is 2 in the set {0,3}
(simplify (is_in (_ bv2 6) #b001001))
;; Test: is 0 in the set {0, 3}
(simplify (is_in (_ bv0 6) #b001001))
;; Test: is 3 in the set {0, 3}
(simplify (is_in (_ bv3 6) #b001001))
;; Test: is 4 in the set {0, 3}
(simplify (is_in (_ bv4 6) #b001001))
(echo "end is_in tests")
(define-fun is_minimal ((e (_ BitVec 6)) (S (_ BitVec 6))) Bool
;; True if e is the minimal element of S
(and (is_in e S) ;; S contains e
;; (1 << e) - 1 represents the set of elements that are smaller than e
(= (bvand (bvsub (bvshl (_ bv1 6) e) (_ bv1 6)) S) Empty)))
(echo "is_minimal tests")
;; is 2 the minimal element in {5, 3}
(simplify (is_minimal (_ bv2 6) #b101000))
;; is 3 the minimal element in {5, 3}
(simplify (is_minimal (_ bv3 6) #b101000))
;; is 4 the minimal element in {5, 3}
(simplify (is_minimal (_ bv4 6) #b101000))
;; is 0 the minimal element in {4, 0}
(simplify (is_minimal (_ bv0 6) #b010001))
;; is 4 the minimal element in {4, 0}
(simplify (is_minimal (_ bv4 6) #b010001))
(echo "end is_minimal tests")
;; To encode that forall x in L0 and forall y in L1. x < y
(define-fun LT ((L0 (_ BitVec 6)) (L1 (_ BitVec 6))) Bool
; True if forall x in L0 and forall y in L1, x < y
; We encode that by saying that
; - L0 is empty OR
; - L1 is empty OR
; - Or Let min be the minimal value of L1 and Forall x in L0, x < min
(or (= L0 Empty)
(= L1 Empty)
(exists ((min (_ BitVec 6))) (and (is_minimal min L1) (LT_l L0 min)))))
;; To make the problem non-trivial, let us assume that L0 and L1 are not empty
(assert (not (= L0 Empty)))
(assert (not (= L1 Empty)))
(assert (LT L0 L1))
(check-sat)
(get-model)
(assert (LT L1 L0))
(check-sat)

Z3 QBVF questions

Following up to the previous discussion: Z3: Extracting existential model-values
Is there a difference between:
(declare-fun sx ((_ BitVec 16)) (_ BitVec 16))
(assert (forall ((y (_ BitVec 16))) (bvuge y (sx y))))
And
(declare-fun sx ((_ BitVec 16)) (_ BitVec 16))
(declare-fun y () (_ BitVec 16))
(assert (bvuge y (sx y)))
As far as Z3 is concerned? That is, will I still get the QBVF solver for the latter automatically?
Also, upon experimentation I found that if I issue:
(eval (sx #x8000))
After a (check-sat) call, it works fine (which is great). What would be better is if I could also say:
(eval (sx (get-value (y))))
Alas, for that query Z3 complains that it's an invalid function application. Is there a way to use the eval function in that manner?
Thanks!
The scripts
(declare-fun sx ((_ BitVec 16)) (_ BitVec 16))
(assert (forall ((y (_ BitVec 16))) (bvuge y (sx y))))
and
(declare-fun sx ((_ BitVec 16)) (_ BitVec 16))
(declare-fun y () (_ BitVec 16))
(assert (bvuge y (sx y)))
are not equivalent. The second is actually equisatisfiable to
(declare-fun sx ((_ BitVec 16)) (_ BitVec 16))
(assert (exists ((y (_ BitVec 16))) (bvuge y (sx y))))
Regarding the eval command, you can reference any uninterpreted constant and function symbol. Thus, you can write:
(declare-fun sx ((_ BitVec 16)) (_ BitVec 16))
(declare-fun y () (_ BitVec 16))
(assert (bvuge y (sx y)))
(check-sat)
(eval (sx y))
The command (eval (sx y)) will not work for the first script because y is a universal variable.

Resources