I'm working on a project for program verification. I have the following statement to be modeled in Z3:
temp = a[i];
a[i] = a[j];
a[j] = temp;
All the variables are of integer type. Would someone give me some hints on how to build constraints to model the above statements?
Here is a general "recipe".
We represent array updates using (store a i v). The result is a new array equal to a, but at position i contains the value v.
An array access a[i] should be encoded as (select a i).
Assignments such as i = i + 1 are usually encoded by creating Z3 variables that represent the value of i before and after the assignment. That is, we would encode it as (= i1 (+ i0 1)).
Keep in mind that the formula (= i (+ i 1)) is equivalent to false.
Here is the example above encode in SMT 2.0 format.
http://www.rise4fun.com/Z3/G5Zk
Here is the script found in the link above.
(declare-const a0 (Array Int Int))
(declare-const a1 (Array Int Int))
(declare-const a2 (Array Int Int))
(declare-const temp0 Int)
(declare-const temp1 Int)
(declare-const i0 Int)
(declare-const j0 Int)
(assert (= temp0 (select a0 i0)))
(assert (= a1 (store a0 i0 (select a0 j0))))
(assert (= a2 (store a1 j0 temp0)))
(check-sat)
(get-model)
Related
So I'm trying to check whether all values in an array is unique with the following Z3 code.
(declare-const A (Array Int Int))
(declare-const n Int)
(assert (forall ((i Int) (j Int)) (and (and (and (>= i 0) (< i n)) (and (>= j 0) (< j n)))
(implies (= (select A i) (select A j)) (= i j)))))
(check-sat)
I'm quite new to Z3 so I don't quite understand the grammar and stuff, but can anyone tell me whether this code is right, and if not, where's the problem?
The problem as you wrote is unsat, because it says whenever 0 <= i < n and 0 <= j < n, if A[i] = A[j], then i = j. There is no array and a particular n you can pick to satisfy this constraint.
What you really want to write is the following instead:
(declare-const A (Array Int Int))
(declare-const n Int)
(assert (forall ((i Int) (j Int)) (implies (and (>= i 0) (< i n)
(>= j 0) (< j n)
(= (select A i) (select A j)))
(= i j))))
(check-sat)
(get-model)
The above says If it's the case that i and j are within bounds, and array elements are the same, then i must equal j. And this variant would be satisifiable for any n; and indeed here's what z3 reports:
sat
(
(define-fun n () Int
0)
(define-fun A () (Array Int Int)
((as const (Array Int Int)) 0))
)
But note that z3 simply picked n = 0, which made it easy to satisfy the formula. Let's make sure we get a more interesting model, by adding:
(assert (> n 2))
Now we get:
sat
(
(define-fun n () Int
3)
(define-fun A () (Array Int Int)
(lambda ((x!1 Int))
(let ((a!1 (ite (and (<= 1 x!1) (not (<= 2 x!1))) 7 8)))
(ite (<= 1 x!1) (ite (and (<= 1 x!1) (<= 2 x!1)) 6 a!1) 5))))
)
and we see that z3 picked the array to have 3 elements with distinct values at positions we care about.
Note that this sort of reasoning with quantifiers is a soft-spot for SMT solvers; while z3 is able to find models for these cases, if you keep adding quantified axioms you'll likely get unknown as the answer, or z3 (or any other SMT solver for that matter) will take longer and longer time to respond.
As far as I know, there are three ways to assgin values to an array in z3.
use assert to assgin values to some of the cells:
(declare-const a1 (Array Int Int))
(declare-const a2 (Array Int Int))
(assert (= 1 (select a1 0)))
(assert (= 2 (select a2 0)))
z3 returns unsat when the constraint (assert (= a1 a2)) is added.
use as const to initialize the array first and then assgin values to specific cells:
(declare-const a3 (Array Int Int))
(assert
(=
(store ((as const (Array Int Int)) 64) 0 3)
a3
)
)
(declare-const a4 (Array Int Int))
(assert
(=
(store ((as const (Array Int Int)) 64) 0 4)
a4
)
)
Add (assert (= a3 a4)) and we obtain unsat again.
define the array via a function:
(define-const a5 (Array Int Int)
(lambda ((i Int))
(ite (= i 0) 5 64)))
(define-const a6 (Array Int Int)
(lambda ((i Int))
(ite (= i 0) 6 64)))
But if we add (assert (= a5 a6)), z3 returns sat. Why?
By the way, is there any (better) way to assign values to an array in z3?
It's a bug. Check out this issue.
I am trying to solve models for array expressions, where default values for array is equal to 0.
For example, I am trying to solve this example, but I get unknown results all the time
(declare-const arr (Array Int Int))
(declare-const arr2 (Array Int Int))
(declare-const a Int)
(declare-const b Int)
(assert (forall ((x Int)) (= (select arr x) 0)))
(assert (> a 0))
(assert (<= a 10))
(assert (= arr2 (store arr a 1337)))
(assert (> b 0))
(assert (<= b 10))
(assert (= (select arr2 b) 0))
(check-sat)
(get-model)
Patrick's advice on not using quantifiers is spot on! They'll make your life harder. However, you're in luck, because z3 supports constant-arrays for your use case, which is quite common. The syntax is:
(assert (= arr ((as const (Array Int Int)) 0)))
This makes sure arr will have all its entries as 0; no quantification needed and z3 handles it internally just fine.
So, your benchmark will be:
(declare-const arr (Array Int Int))
(declare-const arr2 (Array Int Int))
(declare-const a Int)
(declare-const b Int)
(assert (= arr ((as const (Array Int Int)) 0)))
(assert (> a 0))
(assert (<= a 10))
(assert (= arr2 (store arr a 1337)))
(assert (> b 0))
(assert (<= b 10))
(assert (= (select arr2 b) 0))
(check-sat)
(get-model)
which is solved in no time. This way, you can have the entire array start with 0, and modify the range you're interested in; which can depend on variables as usual and is not required to be known ahead of time.
I want a way to, given an invariant and one or more operation's effects, check if, after the operation's execution, the invariant still holds.
Any ideas on how to accomplish this?
Using Z3 I was thinking of doing something similar to
(declare-const a Int)
(declare-const b Int)
(declare-const c Int)
(declare-const d Int)
(define-fun invariant () Bool
(= a b c d 2)
)
(assert invariant)
(assert (= a 1)) ;operation1
(assert (= b 2)) ;operation2
(assert (not invariant))
(check-sat)
If (check-sat) returns unsat then I conclude that the system's state is valid after the operations.
I obviously can't do the above since
(assert invariant)
(assert (not invariant))
always make the theorem unsat.
But I need to assert that the initial state is valid so that the parts of the system that aren't changed by the operations are valid when I run (assert (not invariant)).
I assume that your operations mutate some kind of state (local variables, a program heap, ...), and your invariant should therefore be a function of the state.
As a small example, consider this hypothetical imperative program with local variables:
var start: Int := 0
var end: Int := 0
var arr: Array[Int] := new Array(10) // Array of ints, size 10
fill(arr, 0) // Fill the array with zeros
def invariant() =
(0 < start <= end)
&& forall i in [start, end - 1) :: arr(i) < arr(i + 1) // Sorted
assert invariant() // holds
end := end + 1
assert invariant() // holds
end := end + 1
assert invariant() // fails
arr(start + 1) := arr(start + 1) + 1
assert invariant() // holds
It could be encoded as follows, where the mutated local variables are represented in static single assignment form:
(define-fun invariant ((start Int) (end Int) (arr (Array Int Int))) Bool
(and
(<= 0 start)
(<= start end)
(forall ((i Int))
(implies
(and (<= start i) (< i (- end 1)))
(< (select arr i) (select arr (+ i 1)))))))
(declare-const start0 Int)
(declare-const end0 Int)
(declare-const arr0 (Array Int Int))
(assert (= start0 0))
(assert (= end0 0))
(assert (= arr0 ((as const (Array Int Int)) 0)))
(push)
(assert (not (invariant start0 end0 arr0)))
(check-sat) ;; UNSAT --> Invariant holds
(pop)
;; Operation: end := end + 1
(declare-const end1 Int)
(assert (= end1 (+ end0 1)))
(push)
(assert (not (invariant start0 end1 arr0)))
(check-sat) ; UNSAT --> Invariant still holds
(pop)
;; Operation: end := end + 1
(declare-const end2 Int)
(assert (= end2 (+ end1 1)))
(push)
(assert (not (invariant start0 end2 arr0)))
(check-sat) ; SAT --> Invariant has been broken!
(pop)
;; Operation: arr[start + 1] := arr[start + 1] + 1
(declare-const arr1 (Array Int Int))
(assert (= arr1 (store arr0 (+ start0 1) (+ (select arr0 (+ start0 1)) 1))))
(push)
(assert (not (invariant start0 end2 arr1)))
(check-sat) ; UNSAT --> Invariant has been restored
(pop)
If I want to check if a set has elements of another set is also possible?
For example (contains Set1 Set2):
contains [1,2] [3,5] -> is false
contains [1] [2,3, 1] -> is true
The sets are finite. And the maximum value of the sets is five and the minimum is 0, the sets may not have values greater than 5 neither smaller than 0.
For example:
[1,5,3] -> valid set
[1,8,2] -> invalid set
If it were only for a set and check whether a value exist in the set was easy. It was in the following way:
( declare-sort Set 0 )
( declare-fun contains (Set Int) bool )
( declare-const set Set )
( declare-const A Int )
( assert ( contains set A ))
( assert ( not (contains set 0 )))
( check-sat )
But for two sets I do not see how it's done.
Thank you for your attention.
The operation (contains S1 S2) that you describe in your message is the subset relation. If we encode sets of integers as functions from Int to Boolean (like in: max value in set z3), then S1 and S2 are declared as:
(declare-fun S1 (Int) Bool)
(declare-fun S2 (Int) Bool)
Then, we can say that S1 is a subset of S2 by asserting
(assert (forall ((x Int)) (=> (S1 x) (S2 x))))
We just saying that any element in S1 is also an element of S2.
EDIT
We can use the expression (exists ((x Int)) (and (S1 x) (S2 x))) to check whether the sets S1 and S2 have an element in common or not
END EDIT
The minimal element of a set can be encoded as we did in max value in set z3.
For example, suppose the minimal element of S1 is min_S1.
; Now, let min_S1 be the min value in S1
(declare-const min_S1 Int)
; Then, we now that min_S1 is an element of S1, that is
(assert (S1 min_S1))
; All elements in S1 are bigger than or equal to min_S1
(assert (forall ((x Int)) (=> (S1 x) (not (<= x (- min_S1 1))))))
If the minimal values of the sets you are encoding are known at "encoding time" (and are small). We can use yet another encoding based on Bit-vectors.
In this encoding, a set is a Bit-vector. If the sets only contain values between 0 and 5, then we can use a Bit-vector of size 6. The idea is: if bit i is true iff i is an element of the set.
Here is an example with the main operation:
(declare-const S1 (_ BitVec 6))
(declare-const S2 (_ BitVec 6))
(declare-const S3 (_ BitVec 6))
; set equality is just bit-vector equality
(assert (= S1 S2))
; set intersection, union, and complement are encoded using bit-wise operations
; S3 is S1 union S2
(assert (= S3 (bvor S1 S2)))
; S3 is S1 intersection of S2
(assert (= S3 (bvand S1 S2)))
; S3 is the complement of S1
(assert (= S3 (bvnot S1)))
; S1 is a subset of S2 if S1 = (S1 intersection S2), that is
(assert (= S1 (bvand S1 S2)))
; S1 is the empty set if it is the 0 bit-vector
(assert (= S1 #b000000))
; To build a set that contains only i we can use the left shift
; Here, we assume that i is also a bit-vector
(declare-const i (_ BitVec 6))
; S1 is the set that contains only i
; We are also assuming that i is a value in [0, 5]
(assert (= S1 (bvshl #b000001 i)))