I'm attempting to get Z3 to find all possible permutations of a sequence of fixed size that satisfy some constraints. However, I've run into a timeout error with the code I've developed so far:
(set-option :produce-unsat-cores true)
(set-option :produce-models true)
; --------------- Basic Definitions -------------------
(declare-datatypes () ((Obj A B C)))
; --------------- Predicates -------------------------------
(define-sort MyList () (Seq Obj))
(define-fun in_list ((o Obj) (l MyList)) Bool (seq.contains l (seq.unit o)))
(define-fun permutation ((l1 MyList) (l2 MyList)) Bool
(forall ((o Obj)) (= (in_list o l1) (in_list o l2))))
; Two difference permutations of the same list
(declare-const l0 MyList)
(declare-const l1 MyList)
(assert (= 2 (seq.len l0)))
(assert (= 2 (seq.len l1)))
(assert (not (= l1 l0)))
(assert (permutation l0 l1))
; --------------- Verify -------------------
(check-sat)
(get-model)
It seems like this should be a pretty trivial solve (even a brute force should take milliseconds), so I'm pretty baffled what is causing the timeout. Any help?
You're running into limits of what Z3 can do when quantifiers are present.
You might want to look at this question: Defining a Theory of Sets with Z3/SMT-LIB2
In this case, the question is about general set operations, but I think you'll find the answers to apply to your case as well. (In short, disable MBQI, and see if you can use functions instead of sequences.)
Related
I am using Z3 to solve my horn clauses. In the body of Horn clauses uninterpreted predicates should be positive. However, I need negation of some of uninterpreted predicates.
I have seen some examples in which negation works fine. For instance Z3 would return sat for the following example:
(set-logic HORN)
(declare-fun inv (Int) Bool)
(assert (inv 0))
(assert (forall ((k Int)) (or (> k 10) (not (inv k)) (inv (+ k 1)))))
(check-sat)
But my example looks like the following for which Z3 returns unknown.
(set-logic HORN)
(declare-fun inv (Int ) Bool)
(declare-fun s ( Int ) Bool)
(assert (forall ((k Int) (pc Int))(=>(and (= pc 1)(= k 0)) (inv k ))))
(assert (forall ((k Int)(k_p Int)(pc Int)(pc_p Int))
(=>(and (inv k )(= pc 1)(= pc_p 2)(= k_p (+ k 1))(not (s pc ))(s pc_p ))
(inv k_p ))))
(check-sat)
I wonder if there is a way to rewrite my clauses to Horn clause fragment of Z3.
Your clauses are not in the Horn fragment because the predicate s is used with both polarities in the last assertion. So there are two occurrences of a predicate with positive polarity (both (s pc) and (inv k_p) are positive polarity).
A basic method to avoid polarity issues is to introduce an extra argument to s of type Bool. Consequently, you would also have to say what is the specification of s using Horn clauses so it all makes sense. The typical scenario is that s encodes the behavior of a recursive procedure and the extra Boolean argument to s would be the return value of the procedure s. Of course this encoding doesn't ensure that s is total or functional.
There is a second approach, which is to add an extra argument to "inv", where you let 's' be an array. Then the occurrences (not (s pc)) becomes (not (select s pc)), etc.
It all depends on the intent of your encoding for what makes sense.
I have a z3 model written in the smt2-lib format. I noticed that when I use:
(declare-const flat1 (Seq Dummy))
(assert (= flat1 (unroll dummyFormula1)))
The model is sat, while when I use:
(define-fun flat1 () (Seq Dummy) (unroll dummyFormula1))
the model is reported as unknown.
Why is the difference important? If it helps, I can produce a minimal version of my model.
Edit #1 - A minimal example
Be sure to run it using z3 from github master because of this bug. You can swap between the two versions I indicated with A) and B) below.
(set-option :produce-models true)
; --------------- Basic Definitions -------------------
(declare-datatype Dummy (A B))
(declare-datatype Formula
((Base (forB Dummy))
(And (andB1 Formula) (andB2 Formula))
(Or (orB1 Formula) (orB2 Formula))
(Not (notB Formula))))
(declare-const dummyFormula1 Formula)
(assert (= dummyFormula1 (Base A)))
(declare-const dummyFormula2 Formula)
(assert (= dummyFormula2 (And (Base A) (Base A))))
; --------------- Some functions -----------------------
(define-fun
in_list ((o Dummy) (l (Seq Dummy))) Bool
(seq.contains l (seq.unit o)))
(define-fun
permutation ((l1 (Seq Dummy)) (l2 (Seq Dummy))) Bool
(forall ((o Dummy)) (= (in_list o l1) (in_list o l2))))
(define-fun-rec unroll ((f Formula)) (Seq Dummy)
(match f
(((Base j) (seq.unit j))
((And f1 f2) (seq.++ (unroll f1) (unroll f2)))
((Or f1 f2) (seq.++ (unroll f1) (unroll f2)))
((Not f1) (unroll f1)))))
; -------------- The question -------------------------
;; Here are two versions that should express the same idea, but try commenting
;; the first one and uncommenting the second one!
;; A)
(declare-const flat1 (Seq Dummy))
(assert (= flat1 (unroll dummyFormula1)))
;; B)
; (define-fun flat1 () (Seq Dummy) (unroll dummyFormula1))
; -----------------------------------------------------
(declare-const flat2 (Seq Dummy))
(assert (= flat2 (unroll dummyFormula2)))
(assert (permutation flat1 flat2))
; --------------- Verify -------------------
(check-sat)
(get-model)
Hard to say without looking at the internals of z3. But I want to point out that while the two constructs are very similar, there's a subtle difference.
If you look at page 62 of the standard (http://smtlib.cs.uiowa.edu/papers/smt-lib-reference-v2.6-r2017-07-18.pdf), it says:
(define-fun f ((x1 σ1) · · · (xn σn)) σ t)
with n ≥ 0 and t not containing f is semantically equivalent to the command sequence
(declare-fun f (σ1 · · · σn) σ)
(assert (forall ((x1 σ1) · · · (xn σn)) (= ( f x1 · · · xn) t)).
So, when you use the define-fun form, you are explicitly putting in a quantified formula. When you use the declare-const/assert as you did manually, this quantification does not exist.
Now you can argue that there are no parameters in your case, so there should be no difference, and I'd agree with you. But you're also using quite new features like match, define-fun-rec etc., so obviously z3 is tripping over something here. Since you have a minimal example already, why not post it to the z3 github issues site and get some feedback there. I suspect perhaps the macro-finder is missing a case and can't instantiate this particular case, but really hard to say and there might also be a good reason for it.
If you do post there and get a good answer, please update this question so we know what's going on!
I am currently trying to use Z3 to encode a simple program logic for an untyped language with sets.
My symbolic execution engine needs to prove the validity of the following formula:
To this end, we ask Z3 to check the satisfiability of:
which we then encode as the following SMT-LIB formula:
(define-sort Set () (Array Real Bool))
(define-fun singleton ((x Real)) Set
(store
((as const (Array Real Bool)) false)
x
true))
(define-fun set-union ((x Set) (y Set)) Set
((_ map (or (Bool Bool) Bool)) x y))
(declare-const head Real)
(declare-const tail Set)
(declare-const result Set)
(declare-const value Real)
(assert (forall ((x Real)) (=> (select tail x) (> x head))))
(assert (> head value))
(assert
(forall ((result Set))
(let ((phi1
(forall ((x Real)) (=> (select result x) (> x value))))
(phi2
(= result (union (singleton head) tail))))
(not (and phi1 phi2)))))
(check-sat)
When given this formula, the solver immediately outputs unknown.
My guess is that the problem lies on quantifying over a variable that is bound to a set.
To check this, I simplified the formula above, obtaining:
which we then encode as the following SMT-LIB formula:
(define-sort Set () (Array Real Bool))
(define-fun singleton ((x Real)) Set
(store
((as const (Array Real Bool)) false)
x
true))
(define-fun set-union ((x Set) (y Set)) Set
((_ map (or (Bool Bool) Bool)) x y))
(declare-const head Real)
(declare-const tail Set)
(declare-const result Set)
(declare-const value Real)
(assert (forall ((x Real))(=> (select tail x) (> x head))))
(assert (> head value))
(assert
(not
(forall ((x Real))
(=> (select (union (singleton head) tail) x)
(not (<= x value))))))
(check-sat)
When given this formula, the solver immediately outputs
unsat.
This confirms my guess that the problem lies on the quantification
over a variable that is bound to a set.
My question is whether or not Z3 supports formulae that include
quantification over sets. And, if so, what am I doing wrong?
Quantifier reasoning is always hard for SMT solvers, and in this case you have nested quantifiers. I'm not surprised to hear Z3 simply said Unknown in the first case. Also note that you are quantifying over what's essentially a function (Sets as you implemented are really functions), which makes it even more difficult. But even if you quantified over simpler things, nested quantifiers are never going to be easy to discharge.
Did you try skolemizing your formula, putting it into prenex-normal form, and getting rid of the existentials? That might get you a bit further though you might have to come up with appropriate patterns for instantiation.
Is there a way to concat two lists in z3? Similar to the # operator in ML? I was thinking of defining it myself but I don't think z3 supports recursive function definitions, i.e.,
define-fun concat ( (List l1) (List l2) List
(ite (isNil l1) (l2) (concat (tail l1) (insert (head l1) l2)) )
)
2021 Update
Below answer was written in 2012; 9 years ago. It largely remains still correct; except SMTLib now explicitly allows for recursive-function definitions, via define-fun-rec construct. However, solver support is still very weak, and most properties of interest regarding such functions can still not be proven out-of-the-box. Bottom line remains that such recursive definitions lead to inductive proofs, and SMT-solvers are simply not equipped to do induction. Perhaps in another 9 years they will be able to do so, presumably allowing users to specify their own invariants. For the time being, theorem-provers such as Isabelle, Coq, ACL2, HOL, Lean, etc., remain the best tools to handle these sorts of problems.
Answer from 2012
You are correct that SMT-Lib2 does not allow recursive function definitions. (In SMT-Lib2, function definitions are more like macros, they are good for abbreviations.)
The usual trick is to declare such symbols as uninterpreted functions, and then assert the defining equations as quantified axioms. Of course, as soon as quantifiers come into play the solver can start returning unknown or timeout for "difficult" queries. However, Z3 is pretty good at many goals arising from typical software verification tasks, so it should be able to prove many properties of interest.
Here's an example illustrating how you can define len and append over lists, and then prove some theorems about them. Note that if a proof requires induction, then Z3 is likely to time-out (as in the second example below), but future versions of Z3 might be able to handle inductive proofs as well.
Here's the permalink for this example on the Z3 web-site if you'd like to play around: http://rise4fun.com/Z3/RYmx
; declare len as an uninterpreted function
(declare-fun len ((List Int)) Int)
; assert defining equations for len as an axiom
(assert (forall ((xs (List Int)))
(ite (= nil xs)
(= 0 (len xs))
(= (+ 1 (len (tail xs))) (len xs)))))
; declare append as an uninterpreted function
(declare-fun append ((List Int) (List Int)) (List Int))
; assert defining equations for append as an axiom
(assert (forall ((xs (List Int)) (ys (List Int)))
(ite (= nil xs)
(= (append xs ys) ys)
(= (append xs ys) (insert (head xs) (append (tail xs) ys))))))
; declare some existential constants
(declare-fun x () Int)
(declare-fun xs () (List Int))
(declare-fun ys () (List Int))
; prove len (insert x xs) = 1 + len xs
; note that we assert the negation, so unsat means the theorem is valid
(push)
(assert (not (= (+ 1 (len xs)) (len (insert x xs)))))
(check-sat)
(pop)
; prove (len (append xs ys)) = len xs + len ys
; note that Z3 will time out since this proof requires induction
; future versions might very well be able to deal with it..
(push)
(assert (not (= (len (append xs ys)) (+ (len xs) (len ys)))))
(check-sat)
(pop)
While Levent's code works, if you're willing to set a bound on the recursion depth, Z3 normally has much less trouble with your assertions. You don't even need to rely on MBQI, which often takes far too much time to be practical. Conceptually, you'll want to do:
; the macro finder can figure out when universal declarations are macros
(set-option :macro-finder true)
(declare-fun len0 ((List Int)) Int)
(assert (forall ((xs (List Int))) (= (len0 xs) 0)))
(declare-fun len1 ((List Int)) Int)
(assert (forall ((xs (List Int))) (ite (= xs nil)
0
(+ 1 (len0 (tail xs))))))
(declare-fun len2 ((List Int)) Int)
(assert (forall ((xs (List Int))) (ite (= xs nil)
0
(+ 1 (len1 (tail xs))))))
... and so on. Writing all of this down manually will probably be a pain, so I'd recommend using a programmatic API. (Shameless plug: I've been working on Racket bindings and here's how you'd do it there.)
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).