Search code examples
z3bitvector

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


Solution

  • 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)