Can I specify solution or search space in Z3? - z3

Let me explain my question with an example:
Suppose, I have a domain of possible discrete integers, e.g., -1, 0, 2, 3, 5 and 6
Now, I am looking for a solution (or model) for a variable x that will satisfy the following constraints:
(x > 0) && (x < 6) && (x != 3) && (x > 2)
The answer will be (from the solution domain) = 5, right?
How can I do this in Z3?
That is, I would like to define solution space using discrete entities and then assert few constraints and ask Z3 to check for satisfiablity. If satisfyable, then want the model.
Can anyone help me with the example?
Thank you,
--Ishtiaque

Asserting that x == -1 || x == 0 || x == 2 || x == 3 || x == 5 || x == 6 as an axiom beforehand should do it. I don't know if Z3 has a better way built into it though.
Edit:
Another solution may be to use an array, though I haven't used them before. Conceptually it should be possible to declare an array A that contains the numbers, and then say:
(exists (y Int) (=(select A y) x))`
Not sure if that is the exact syntax as I haven't used arrays before, but hopefully it should work.

Related

Lambda Functions for Z3Py

My goal is to find a construct within Z3Py which allows me to:
(1) Write propositions as a function of a variable. For eg theoretically, if I define P(x) = x < 3, then the code should allow me to access P(u) for some other variable u.
(2) And Z3 should be able to solve and find a model for such a construct.
I thought Z3's 'Lambda' function theoretically made sense. However with this construct neither can I do (1) or (2). As a concrete eg, suppose I have the following code:
u, x = Ints('u x')
P = Lambda( [x], x < 5 )
I = Lambda ([x], x < 3)
C1 = Not(Implies(P.body(), I.body() ))
s = Solver()
s.add(C1)
r = s.check()
print(r.__repr__())
s.add( Implies(P(u), u == 2) )
Run this code to get the output:
unknown
Traceback (most recent call last):
File "testfile.py", line 20, in <module>
s.add( Implies(P(u), u == 2) )
TypeError: 'QuantifierRef' object is not callable
There are two issues to fix here:
(1) Why does r._ repr_() have 'unknown' stored and not 'sat' i.e. Why isn't Z3 solving this system?
(2) In the final line, how can I get the predicate u < 5 from P i.e. in lambda calculus terminology, how do I do application of a function to a variable in Z3Py? Clearly P(u) does not work.
For this sort of modeling, you should simply use a regular python function:
from z3 import *
def P(x):
return x < 5
def I(x):
return x < 3
Then, to do the proof Q(x) => P(x), you'd use a quantifier:
dummy = Int('dummy')
C1 = ForAll([dummy], Implies(I(dummy), P(dummy)))
prove(C1)
This prints:
proved
Regarding your specific questions:
(1) Adding Implies(P.body(), Q.body()) means something completely different. If you run:
from z3 import *
x = Int('x')
P = Lambda( [x], x < 5 )
I = Lambda( [x], x < 3 )
s = Solver()
s.add(Implies(P.body(), I.body()))
print(s.sexpr())
You'll see it prints:
(assert (=> (< (:var 0) 5) (< (:var 0) 3)))
where :var is an internal free-variable generating function. This isn't an object you should be passing back and forth to z3; in fact, I think you're becoming a victim of the loosely typed nature of z3; this isn't a construct that really make much sense at all. Long story short, you should never look at P.body() or I.body() in your own code. I'd ignore the unknown result in this context; the input is more or less meaningless, and z3 spits out a nonsensical answer. A better system should've checked and complained about this; but this is not a strong point for z3's Python API.
(2) If you use a regular function, this isn't really a problem at all; because you're just doing regular application at the Python level. You can apply a lambda-bound value by directly calling it as well, though you need the notation P[u]. (Lambda's are similar to arrays in z3.) So, something like:
from z3 import *
u, x = Ints('u x')
P = Lambda([x], x < 5)
I = Lambda([x], x < 3)
s = Solver()
s.add(Implies(P[u], u == 2))
print(s.check())
print(s.model())
will print:
sat
[u = 2]
which is what you were looking for I think.
Multiple arguments
If you want to model a lambda with multiple arguments, the easiest way is to think of it as a nested construct. That is, you store a new lambda at each index. Here's an example:
from z3 import *
dummy1 = FreshInt()
dummy2 = FreshInt()
P = Lambda([dummy1], Lambda([dummy2], dummy1 < dummy2))
s = Solver()
x, y = Ints('x y')
s = Solver()
s.add(P[x][y])
print(s.check())
print(s.model())
This prints:
sat
[y = 1, x = 0]
Note that the above also demonstrates the use of the FreshInt function, which avoids name-clashes by providing a unique name each time it is called.

Dafny: no terms found to trigger on and a consequent assertion error

This is the code that I wrote for a method that returns the maximum of two integers:
predicate greater(x: int, a: int, b: int){
(x >= a) && (x >= b)
}
method Max(a: int, b: int) returns (max: int)
ensures max >= a
ensures max >= b
ensures forall x /*{:trigger greater(x,a,b)}*/ :: (greater(x,a,b)) ==> x >= max
{
if (a > b){
max := a;
}else{
max := b;
}
// assert greater(max, a, b); - trivial assertion
}
method Main(){
var res:= Max(4, 5);
assert res == 5;
}
As you can see, I have tried both the two techniques metnioned in the Wiki page (manual trigger assignment and also adding a trivial non-useful assertion in the method body. However, I still get an assertion error.
I am not sure what else to do. I have read other answers like this, this and this, but none have helped me so far.
PS: I know there is a simpler way to write the postconditions for this particular method, however, I really wish to model the postconditions in terms of the forall quantifier only.
Let's forget greater for a while and just take a look at what you're trying to achieve. After the call to Max in Main, you know the following (from the postcondition of Max):
res >= 4
res >= 5
forall x :: x >= 4 && x >= 5 ==> x >= res
You're trying to prove res == 5 from this. The second of these three things immediately gives you half of that equality, so all you need to do is obtain 5 >= res. If you instantiate the quantifier with 5 for x, you will get
5 >= 4 && 5 >= 4 ==> 5 >= res
which simplifies to 5 >= res, which is what you need, so that's the end of your proof.
In summary, the proof comes down to instantiating the quantifier with 5 for x. Next, you need to know a little about how the Dafny verifier instantiates quantifiers. Essentially, it does this by looking at the "shape" of the quantifier and looking for similar things in the context of what you're trying to prove. By "shape", I mean things like "the functions and predicates it uses". Usually, this technique works well, but in your case, the quantifier is so plain that it doesn't have any "shape" to speak of. Consequently, the verifier fails to come up with the needed instantiation.
It would be nice if we could just say "hey, try instantiating that quantifier with 5 for x". Well, we can, if we give the quantifier some "shape" that we can refer to. That's what those wiki and other guidelines are trying to say. This is where it's useful to introduce the predicate greater. (Don't try to manually write trigger annotations.)
Alright, after introducing greater, your specification says
ensures greater(max, a, b)
ensures forall x :: greater(x, a, b) ==> x >= max
This says "max satisfies greater(max, a, b)" and "among all values x that satisfy greater(x, a, b), max is the smallest". After the call to Max in Main, we then have:
greater(res, 4, 5)
forall x :: greater(x, 4, 5) ==> x >= res
Recall, I said the verifier tries to figure out quantifier instantiations by looking at the quantifier and looking at the context around your assertion, and you're trying to instantiate the quantifier with 5 for x. So, if you can add something to the context just before the assertion that tempts the verifier to do that instantiation, then you're done.
Here's the answer: you want to introduce the term greater(5, 4, 5). This has a shape much like the greater(x, 4, 5) in the quantifier. Because of this similarity, the verifier will instantiate x with 5, which gives
greater(5, 4, 5) ==> 5 >= res
And since greater(5, 4, 5) is easily proved to be true, the needed fact 5 >= res follows.
So, change the body of Main to
var res := Max(4, 5);
assert greater(5, 4, 5);
assert res == 5;
and you're done. The verifier will prove the both assertions. The first is trivial, and after proving it, the verifier gets to use the term greater(5, 4, 5) in the proof of the second assertion. That term is what triggers the quantifier, which produces the fact 5 >= res, which proves the second assertion.
I want to point out that most quantifiers we try to prove do have some shape already. In your case, the predicate greater was introduced in order to give some shape to the quantifier. The technique of adding the extra assertion (here, assert greater(5, 4, 5)) is the same whether or not greater was already defined or was introduced as a trivial predicate that provides shape.

Change Sort after defining a variable

Is it possible to change the domain of a variable after it has been defined and used in statements?. Example
s = Solver()
x = Real('x')
s.add(x < 1)
Now I want to change the domain of x to Int or Bool.
thanks!
The short answer is no.
But why do you want to do this? SMTLib is based on a many-sorted first-order logic, and variables can only have one sort. So, even if you can change the domain, it would be meaningless. (Essentially a type-error.)
Having said that, there's nothing stopping you from saying:
x = Int ('x')
at the end of that script. But the new x would be totally independent of the old x; i.e., a different name with a different sort and you'd lose access to the first one. Clearly, this is neither useful nor advisable. To wit:
from z3 import *
s = Solver ()
x = Real ('x')
s.add (x < 1)
x = Bool ('x')
s.add (x)
print s.sexpr()
print s.check()
print s.model()
This prints:
(declare-fun x () Real)
(declare-fun x () Bool)
(assert (< x 1.0))
(assert x)
sat
[x = True, x = 0]
This is very confusing to read, till you realize those two xs are totally independent of each other. (And I'd say the s.sexpr() method is rather buggy since it doesn't print out valid smt2-lib, as what it prints would be rejected by a compliant SMT-solver, but that's a different issue.)
I suspect, perhaps, you're trying to ask for something else. If you describe what you are trying to do in detail, you might get a better answer!

Prove() method, but ignore some 'free' variables?

I have 2 formulas F1 and F2. These two formulas share most variables, except some 'temporary' (or I call them 'free') variables having different names, that are there for some reasons.
Now I want to prove F1 == F2, but prove() method of Z3 always takes into account all the variables. How can I tell prove() to ignore those 'free' variables, and focuses only on a list of variables I really care about?
I mean with all the same input to the list of my variables, if at the output time, F1 and F2 have the same value of all these variables (regardless the values of 'free' variables), then I consider them 'equivalence'
I believe this problem has been solved in other researches before, but I dont know where to look for the information.
Thanks so much.
We can use existential quantifiers to capture 'temporary'/'free' variables.
For example, in the following example, the formulas F and G are not equivalent.
x, y, z, w = Ints('x y z w')
F = And(x >= y, y >= z)
G = And(x > z - 1, w < z)
prove(F == G)
The script will produce the counterexample [z = 0, y = -1, x = 0, w = -1].
If we consider y and w as 'temporary' variables, we may try to prove:
prove(Exists([y], F) == Exists([w], G))
Now, Z3 will return proved. Z3 is essentially showing that for all x and z, there is a y that makes F true if and only if there is a w that makes G true.
Here is the full example.
Remark: when we add quantifiers, we are making the problem much harder for Z3. It may return unknown for problems containing quantifiers.
Apparently, I cannot comment, so I have to add another answer. The process of "disregarding" certain variables is typically called "projection" or "forgetting". I am not familiar with it in contexts going beyond propositional logic, but if direct existential quantification is possible (which Leo described), it is conceptually the simplest way to do it.

Z3: finding all satisfying models

I am trying to retrieve all possible models for some first-order theory using Z3, an SMT solver developed by Microsoft Research. Here is a minimal working example:
(declare-const f Bool)
(assert (or (= f true) (= f false)))
In this propositional case there are two satisfying assignments: f->true and f->false. Because Z3 (and SMT solvers in general) will only try to find one satisfying model, finding all solutions is not directly possible. Here I found a useful command called (next-sat), but it seems that the latest version of Z3 no longer supports this. This is bit unfortunate for me, and in general I think the command is quite useful. Is there another way of doing this?
One way to accomplish this is using one of the APIs, along with the model generation capability. You can then use the generated model from one satisfiability check to add constraints to prevent previous model values from being used in subsequent satisfiability checks, until there are no more satisfying assignments. Of course, you have to be using finite sorts (or have some constraints ensuring this), but you could use this with infinite sorts as well if you don't want to find all possible models (i.e., stop after you generate a bunch).
Here is an example using z3py (link to z3py script: http://rise4fun.com/Z3Py/a6MC ):
a = Int('a')
b = Int('b')
s = Solver()
s.add(1 <= a)
s.add(a <= 20)
s.add(1 <= b)
s.add(b <= 20)
s.add(a >= 2*b)
while s.check() == sat:
print s.model()
s.add(Or(a != s.model()[a], b != s.model()[b])) # prevent next model from using the same assignment as a previous model
In general, using the disjunct of all the involved constants should work (e.g., a and b here). This enumerates all integer assignments for a and b (between 1 and 20) satisfying a >= 2b. For example, if we restrict a and b to lie between 1 and 5 instead, the output is:
[b = 1, a = 2]
[b = 2, a = 4]
[b = 1, a = 3]
[b = 2, a = 5]
[b = 1, a = 4]
[b = 1, a = 5]
A more general solution based on Taylors answer would be to use
while s.check() == z3.sat:
solution = "False"
m = s.model()
for i in m:
solution = f"Or(({i} != {m[i]}), {solution})"
f2 = eval(solution)
s.add(f2)
This allows more than two variables.

Resources