Finding counterexamples for a "Horn" solver object - z3

I'm trying to learn how to write code for giving a counterexample to a horn clause and it's guessed interpretation. In the below code, let I be the uninterpreted function (it's a trivial loop invariant). The first 3 s.add() add the condition requirements for I(x), and the fourth one is a guess candidate for I. I try to use the s.prove directive to get a counterexample to my guessed candidate for I. I seem to be getting a huge error log on running this code, could anyone tell me what's wrong?
s = SolverFor("HORN")
I = Function('I', IntSort(), BoolSort())
x, x2 = Ints('x x2')
s.set("produce-proofs", True)
s.add( ForAll( [x] ,Implies( x == 0 , I(x))) )
s.add( ForAll( [x, x2] , Implies ( And( I(x), x2 == x + 1 , x < 5) , I(x2) ) ) )
s.add( ForAll( [x] ,Implies( And( I(x), Not(x < 5) ) , x == 5 ) ) )
s.add( ForAll( [x], And( Implies( I(x) , (x == 2) ), Implies( (x == 2) , I(x) ) ) ) ) #Adding guessed invariant here!
assert unsat == s.check()
print(s.proof())

You've a couple of mistakes in the script; perhaps this dates back from a while ago with a different version of z3? In any case, the following goes through with z3 version 4.8.14:
from z3 import *
s = SolverFor("HORN")
I = Function('I', IntSort(), BoolSort())
x, x2 = Ints('x x2')
s.set("proof", True)
s.add( ForAll( [x] ,Implies( x == 0 , I(x))) )
s.add( ForAll( [x, x2] , Implies ( And( I(x), x2 == x + 1 , x < 5) , I(x2) ) ) )
s.add( ForAll( [x] ,Implies( And( I(x), Not(x < 5) ) , x == 5 ) ) )
s.add( ForAll( [x], And( Implies( I(x) , (x == 2) ), Implies( (x == 2) , I(x) ) ) ) ) #Adding guessed invariant here!
print(s.check())
Unfortunately it prints:
unknown
This means the solver wasn't able to determine if the input is satisfiable or not; it gave up and said unknown. An earlier version of the solver might've been able to solve this successfully, providing an unsatisfiability proof, or a model should it be satisfiable.
I recommend tracking down where you got this example from and find out which version of z3 they were using. Going back to that version might help you make progress. Or, if you think the problem should be solvable as is, you can file a bug report at https://github.com/Z3Prover/z3/issues

Related

A Skolem model in Z3 should be unique, but is printing several (and repeated)

I am testing how similar are "assignment"-like models and "Skolem-function"-like models in Z3.
Thus, I proposed an experiment: I will create a formula for which the unique model is y=2; and try to "imitate" this formula so that the (unique) model is a Skolem function f(x)=2. I did this by using ExistsForall quantification for the y=2 case and ForallExists quantification for the f(x)=2 case.
Thus, I first performed the following (note that the y is existentially quantified from the top-level declaration):
from z3 import *
x,y = Ints('x y')
ct_0 = (x >= 2)
ct_1 = (y > 1)
ct_2 = (y <= x)
phi = ForAll([x], Implies(ct_0, And(ct_1,ct_2)))
s = Solver()
s.add(phi)
print(s.check())
print(s.model())
for i in range(0, 5):
if s.check() == sat:
m = s.model()[y]
print(m)
s.add(And(y != m))
This code successfully prints out y=2 as a unique model (no matter we asked for 5 more). Now, I tried the same for f(x)=2 (note that there is no y):
skolem = Function('skolem', IntSort(), IntSort())
x = Int('x')
ct0 = (x >= 2)
ct1 = (skolem(x) > 1)
ct2 = (skolem(x) <= x)
phi1 = ForAll([x], Implies(ct0, And(ct1,ct2)))
s = Solver()
s.add(phi1)
for i in range(0, 5):
if s.check() == sat:
m = s.model()
print(m)
s.add(skolem(x) != i)
This prints:
[skolem = [else -> 2]]
[x = 0, skolem = [else -> If(2 <= Var(0), 2, 1)]]
[x = 0, skolem = [else -> If(2 <= Var(0), 2, -1)]]
[x = 0, skolem = [else -> If(2 <= Var(0), 2, -1)]]
[x = 0, skolem = [else -> If(2 <= Var(0), 2, -1)]]
My question is: why is the y=2 unique, whereas we get several Skolem functions? In Skolem functions, we get (and repeatedly) some functions in which the antecedent of phi1 (i.e., (x >= 2)) is negated (e.g., x=0); but in models, we do not get stuff like x=0 implies y=1, we only get y=2 because that is the unique model that does not depend on x. In the same way, [skolem = [else -> 2]] should be the unique "Skolem model" that does not depend on x.
There's a fundamental difference between these two queries. In the first one, you're looking for a single y that acts as the value that satisfies the property. And indeed y == 2 is the only choice.
But when you have a skolem function, you have an infinite number of witnesses. The very first one is:
skolem(x) = 2
i.e., the function that maps everything to 2. (You're internally equating this to the model y=2 in the first problem, but that's misleading.)
But there are other functions too. Here's the second one:
skolem(x) = if 2 <= x then 2 else 1
You can convince yourself this is perfectly fine, since it does give you the skolem function that provides a valid value for y (i.e., 2), when the consequent matters. What it returns in the else case is immaterial. (i.e., when x < 2). And similarly, you can simply do different things when x < 2, giving you an infinite number of skolem functions that work. (Of course, the difference is not interesting, but different nonetheless.)
What you really are trying to say, I guess, is there's nothing "else" that's interesting. Unfortunately that's harder to automate, since it's hard to get a Python function back from a z3 model. But you can do it manually:
from z3 import *
skolem = Function('skolem', IntSort(), IntSort())
x = Int('x')
ct0 = (x >= 2)
ct1 = (skolem(x) > 1)
ct2 = (skolem(x) <= x)
phi1 = ForAll([x], Implies(ct0, And(ct1,ct2)))
s = Solver()
s.add(phi1)
print(s.check())
print(s.model())
# The above gives you the model [else -> 2], i.e., the function that maps everything to 2.
# Let's add a constraint that says we want something "different" in the interesting case of "x >= 2":
s.add(ForAll([x], Implies(x >= 2, skolem(x) != 2)))
print(s.check())
This prints:
sat
[skolem = [else -> 2]]
unsat
which attests to the uniqueness of the skolem-function in the "interesting" case.

Why is Z3 giving me unsat for the following formula?

I have the following formula and Python code trying to find the largest n satisfying some property P:
x, u, n, n2 = Ints('x u n n2')
def P(u):
return Implies(And(2 <= x, x <= u), And(x >= 1, x <= 10))
nIsLargest = ForAll(n2, Implies(P(n2), n2 <= n))
exp = ForAll(x, And(P(n), nIsLargest))
s = SolverFor("LIA")
s.reset()
s.add(exp)
print(s.check())
if s.check() == sat:
print(s.model())
My expectation was that it would return n=10, yet Z3 returns unsat. What am I missing?
You're using the optimization API incorrectly; and your question is a bit confusing since your predicate P has a free variable x: Obviously, the value that maximizes it will depend on both x and u.
Here's a simpler example that can get you started, showing how to use the API correctly:
from z3 import *
def P(x):
return And(x >= 1, x <= 10)
n = Int('n')
opt = Optimize()
opt.add(P(n))
maxN = opt.maximize(n)
r = opt.check()
print(r)
if r == sat:
print("maxN =", maxN.value())
This prints:
sat
maxN = 10
Hopefully you can take this example and extend it your use case.

Use Z3 to find counterexamples for a 'guess solution' to a particular CHC system?

Suppose I have the following CHC system (I(x) is an unkown predicate):
(x = 0) -> I(x)
(x < 5) /\ I(x) /\ (x' = x + 1) -> I(x')
I(x) /\ (x >= 5) -> x = 5
Now suppose I guess a solution for I(x) as x < 2 (actually an incorrect guess). Can I write code for Z3Py to (i) check if it is a valid solution and (ii) if it's incorrect find a counterexample, i.e. a value which satisfies x < 2 but does not satisfy atleast one of the above 3 equations? (For eg: x = 1, is a counterexample as it does not satisfy the 2nd equation?)
Sure. The way to do this sort of reasoning is to assert the negation of the conjunction of all your constraints, and ask z3 if it can satisfy it. If the negation comes back satisfiable, then you have a counter-example. If it is unsat, then you know that your invariant is good.
Here's one way to code this idea, in a generic way, parameterized by the constraint generator and the guessed invariant:
from z3 import *
def Check(mkConstraints, I):
s = Solver()
# Add the negation of the conjunction of constraints
s.add(Not(mkConstraints(I)))
r = s.check()
if r == sat:
print("Not a valid invariant. Counter-example:")
print(s.model())
elif r == unsat:
print("Invariant is valid")
else:
print("Solver said: %s" % r)
Given this, we can code up your particular case in a function:
def System(I):
x, xp = Ints('x xp')
# (x = 0) -> I(x)
c1 = Implies(x == 0, I(x))
# (x < 5) /\ I(x) /\ (x' = x + 1) -> I(x')
c2 = Implies(And(x < 5, I(x), xp == x+1), I(xp))
# I(x) /\ (x >= 5) -> x = 5
c3 = Implies(And(I(x), x >= 5), x == 5)
return And(c1, c2, c3)
Now we can query it:
Check(System, lambda x: x < 2)
The above prints:
Not a valid invariant. Counter-example:
[xp = 2, x = 1]
showing that x=1 violates the constraints. (You can code so that it tells you exactly which constraint is violated as well, but I digress.)
What happens if you provide a valid solution? Let's see:
Check(System, lambda x: x <= 5)
and this prints:
Invariant is valid
Note that we didn't need any quantifiers, as top-level variables act as existentials in z3 and all we needed to do was to find if there's an assignment that violated the constraints.

add iteratively in z3

I want to check the value of a, b, c, and if value 'a' equals to 1, 'x' is added one. We continue the process for values 'b' and 'c'.
So if a=1, b=1, c=1, the result of x should be 3.
if a=1, b=1, c=0, so the result of x should be 2.
Any methods to be implemented in z3?
The source code looks like this:
from z3 import *
a, b, c = Ints('a b c')
x, y = Ints('x y')
s = Solver()
s.add(If(a==1, x=x + 1, y = y-1))
s.add(If(b==1, x=x + 1, y = y-1))
s.add(If(c==1, x=x + 1, y = y-1))
s.check()
print s.model()
Any suggestions about what I can do?
This sort of "iterative" processing is usually modeled by unrolling the assignments and creating what's known as SSA form. (Static single assignment.) In this format, every variable is assigned precisely once, but can be used many times. This is usually done by some underlying tool as it is rather tedious, but you can do it by hand as well. Applied to your problem, it'd look something like:
from z3 import *
s = Solver()
a, b, c = Ints('a b c')
x0, x1, x2, x3 = Ints('x0 x1 x2 x3')
s.add(x0 == 0)
s.add(x1 == If(a == 1, x0+1, x0))
s.add(x2 == If(b == 1, x1+1, x1))
s.add(x3 == If(c == 1, x2+1, x2))
# Following asserts are not part of your problem, but
# they make the output interesting
s.add(b == 1)
s.add(c == 0)
# Find the model
if s.check() == sat:
m = s.model()
print("a=%d, b=%d, c=%d, x=%d" % (m[a].as_long(), m[b].as_long(), m[c].as_long(), m[x3].as_long()))
else:
print "no solution"
SSA transformation is applied to the variable x, creating as many instances as necessary to model the assignments. When run, this program produces:
a=0, b=1, c=0, x=1
Hope that helps!
Note that z3 has many functions. One you could use here is Sum() for the sum of a list. Inside the list you can put simple variables, but also expression. Here an example for both a simple and a more complex sum:
from z3 import *
a, b, c = Ints('a b c')
x, y = Ints('x y')
s = Solver()
s.add(a==1, b==0, c==1)
s.add(x==Sum([a,b,c]))
s.add(y==Sum([If(a==1,-1,0),If(b==1,-1,0),If(c==1,-1,0)]))
if s.check() == sat:
print ("solution:", s.model())
else:
print ("no solution possible")
Result:
solution: [y = 2, x = 2, c = 1, b = 0, a = 1]
If your problem is more complex, using BitVecs instead of Ints can make it run a little faster.
edit: Instead of Sum() you could also simply use addition as in
s.add(x==a+b+c)
s.add(y==If(a==1,-1,0)+If(b==1,-1,0)+If(c==1,-1,0))
Sum() makes sense towards readability when you have a longer list of variables, or when the variables already are in a list.

Incorrect model of max value in Z3Py

I want to find a maximal interval in which an expression e is true for all x. A way to write such a formula should be: Exists d : ForAll x in (-d,d) . e and ForAll x not in (-d,d) . !e.
To get such a d, the formula f in Z3 (looking at the one above) could be the following:
from __future__ import division
from z3 import *
x = Real('x')
delta = Real('d')
s = Solver()
e = And(1/10000*x**2 > 0, 1/5000*x**3 + -1/5000*x**2 < 0)
f = ForAll(x,
And(Implies(And(delta > 0,
-delta < x, x < delta,
x != 0),
e),
Implies(And(delta > 0,
Or(x > delta, x < -delta),
x != 0),
Not(e))
)
)
s.add(Not(f))
s.check()
print s.model()
Which outputs [d = 1/4].
To check it, I set delta = RealVal('1/4'), drop the ForAll quantifier from f and I get x = 1/2. I replace delta with 1/2 and get 3/4, then 7/8 and so on. The bound should be 1. Can I get Z3 to output that immediately?
If you do the math yourself, you can see that the solution is x != 0, x < 1. Or you can simply ask Wolfram Alpha to do it for you. So, there's no such delta.
The issue you're having is that you're asserting:
s.add(Not(f))
This turns the universal quantification on x into an existential; asking z3 to find a delta such that there is some x that fits the bill. (That is, you're negating your whole formula.) Instead, you should do:
s.add(delta > 0, f)
which also makes sure that delta is positive. With that change, z3 will correctly respond:
unsat
(And then you'll get an error for the call to s.model(), you should only call s.model() if the previous call to s.check() returns sat.)

Resources