z3 cannot prove transitive identity - z3

I am trying z3 what can it do.
So far so good, but I noticed that z3 fails on very trivial expression:
from z3 import *
a = Int("a")
b = Int("b")
c = Int("c")
prove(((a == b) and (b == c)) == ((a == c) and (c == b)))
$ python p.py
counterexample
[c = 1, b = 0, a = 0]

Python's and is not symbolic-aware. Instead, use z3's And method:
from z3 import *
a = Int("a")
b = Int("b")
c = Int("c")
prove(And(a == b, b == c) == (And(a == c, c == b)))
This prints:
proved

Related

Trying to find all solutions to a boolean formula using Z3 in python

I'm new to Z3, and trying to make a solver which returns every satisfiable solution to a boolean formula. Taking notes from other SO-posts, I've coded what I hoped would work, but isn't. The problem seems to that by adding the previous solutions, I remove some of the variables, but then they return in later solutions?
Currently I am just trying to solve a or b or c.
If I explained poorly, let me know and I will try to explain further.
Thanks in advance for the response :)
My code:
from z3 import *
a, b, c = Bools('a b c')
s = Solver()
s.add(Or([a, b, c]))
while (s.check() == sat):
print(s.check())
print(s)
print(s.model())
print(s.model().decls())
print("\n")
s.add(Or([ f() != s.model()[f] for f in s.model().decls() if f.arity() == 0]))
My output:
sat
[Or(a, b, c)]
[c = False, b = False, a = True]
[c, b, a]
sat
[Or(a, b, c), Or(c != False, b != False, a != True)]
[b = True, a = False]
[b, a]
sat
[Or(a, b, c),
Or(c != False, b != False, a != True),
Or(b != True, a != False)]
[b = True, a = True]
[b, a]
sat
[Or(a, b, c),
Or(c != False, b != False, a != True),
Or(b != True, a != False),
Or(b != True, a != True)]
[b = False, c = True]
[b, c]
The typical way to code such problems is as follows:
from z3 import *
a, b, c = Bools('a b c')
s = Solver()
s.add(Or([a, b, c]))
res = s.check()
while (res == sat):
m = s.model()
print(m)
block = []
for var in m:
block.append(var() != m[var])
s.add(Or(block))
res = s.check()
This prints:
[b = True, a = False, c = False]
[a = True]
[c = True, a = False]
You'll notice that not all models are "complete." This is because z3 will typically "stop" assigning variables once it decides the problem is sat, as the other variables are irrelevant.
I suppose your confusion is that there should be 7 models to your problem: Aside from the all-False assignment, you should have a model. If you want to get the values of all your variables in this way, then you should explicitly query for them, like this:
from z3 import *
a, b, c = Bools('a b c')
s = Solver()
s.add(Or([a, b, c]))
myvars = [a, b, c]
res = s.check()
while (res == sat):
m = s.model()
block = []
for var in myvars:
v = m.evaluate(var, model_completion=True)
print("%s = %s " % (var, v)),
block.append(var != v)
s.add(Or(block))
print("\n")
res = s.check()
This prints:
a = False b = True c = False
a = True b = False c = False
a = True b = True c = False
a = True b = True c = True
a = True b = False c = True
a = False b = False c = True
a = False b = True c = True
And there are exactly 7 models as you would've expected.
Note the model_completion parameter. This is a common pitfall for newcomers as there isn't a "out-of-the-box" method in z3 for getting all possible assignments, so you have to be careful coding it yourself like above. The reason why there isn't such a function is that it's really hard to implement it in general: Think about how it should work if your variables were functions, arrays, user-defined data-types, etc. as opposed to simple booleans. It can get really tricky to implement a generic all-sat function with all these possibilities handled correctly and efficiently. So, it's left to the user, as most of the time you only care about a specific notion of all-sat that's typically not hard to code once you learn the basic idioms.

Z3Python: ForAll causes my code hangup, or returns Unsat, why?

I am still struggling with the problem of findiong a value so that a * b == b with all value of b. The expected result is a == 1. I have two solutions below.
(A) I implemented this with ForAll quantifier in below code (correct me if there is a solution without using any quantifier). The idea is to prove f and g are equivalent.
from z3 import *
a, b, a1, tmp1 = BitVecs('a b a1 tmp1', 32)
f = True
f = And(f, tmp1 == b)
f = And(f, a1 == a * tmp1)
g= True
g = And(g, a1 == b)
s = Solver()
s.add(ForAll([b, tmp1, a1], f == g))
if s.check() == sat:
print 'a =', s.model()[a]
else:
print 'Unsat'
However, this simple code runs forever without giving back result. I think that is because of the ForAll. Any idea on how to fix the problem?
(B) I tried again with another version. This time I dont prove two formulas to be equivalent, but put them all into one formula f. Logically, I think this is true, but please correct me if I am wrong here:
from z3 import *
a, b, a1, tmp = BitVecs('a b a1 tmp', 32)
f = True
f = And(f, tmp == b)
f = And(f, a1 == a * tmp)
f = And(f, a1 == b)
s = Solver()
s.add(ForAll([b, a1], f))
if s.check() == sat:
print 'a =', s.model()[a]
else:
print 'Unsat'
This time the code does not hang, but immediately returns 'Unsat'. Any idea on how to fix this?
Thanks a lot.
The direct formulation of your problem provides the answer you were expecting: http://rise4fun.com/Z3Py/N07sW
The versions you propose use auxiliary variables, a1, tmp, tmp1 and you
use universal quantification over these variables. This does not correspond
to the formula you intended.

Find logical conditions for 2 formulas to be equivalent?

I posted a related question, but then I think it was not very clear. I would like to rephrase the problem like this:
Two formulas a1 == a + b (1) and a1 == b (2) are equivalent if a == 0. Given these formulas (1) and (2), how can I use Z3 python to find out this required condition (a == 0) so the above formulas become equivalent?
I suppose that a1, a and b are all in the format of BitVecs(32).
Edit: I came up with the code like this:
from z3 import *
a, b = BitVecs('a b', 32)
a1 = BitVec('a1', 32)
s = Solver()
s.add(ForAll(b, a + b == b))
if s.check() == sat:
print 'a =', s.model()[a]
else:
print 'Not Equ'
The output is: a = 0, as expected.
However, when I modified the code a bit to use two formulas, it doesnt work anymore:
from z3 import *
a, b = BitVecs('a b', 32)
a1 = BitVec('a1', 32)
f = True
f = And(f, a1 == a * b)
g = True
g = And(g, a1 == b)
s = Solver()
s.add(ForAll(b, f == g))
if s.check() == sat:
print 'a =', s.model()[a]
else:
print 'Not Equ'
The output now is different: a = 1314914305
So the questions are:
(1) Why the second code produces different (wrong) result?
(2) Is there any way to do this without using ForAll (or quantifier) at all?
Thanks
The two codes produce the same correct answer a = 0. You have a typo: you are writing
a1 = a*b and it must be a1 = a + b . Do you agree?
Possible code without using ForAll:
a, b = BitVecs('a b', 32)
a1 = BitVec('a1', 32)
s = Solver()
s.add(a + b == b)
if s.check() == sat:
print 'a =', s.model()[a]
else:
print 'Not Equ'
s1 = Solver()
s1.add(a==0, Not(a + b == b))
print s1.check()
Output:
a = 0
unsat

why this code returns Unsat?

Given c == a + 4 and t == c + b, if b == -4, then t == a. I am trying to do the opposite, meaning given the above 2 equations and t == a, I try to find value of b.
This is pretty similar to the related question, but this time I only switch a and b, and I am really confused that the code returns different result.
Following the code posted at above link, I have below code (similar, only a and b swiched):
#!/usr/bin/python
from z3 import *
a, b, c, t = BitVecs('a b c t', 32)
g = True
g = And(g, c == (a + 4))
g = And(g, t == (c + b))
s = Solver()
s.add(ForAll([t, a, c], Implies(t == a, g)))
if s.check() == sat:
print s.model()[b]
else:
print 'Unsat'
However, on Ubuntu, running the above code returns unexpected result Unsat, but not value -4 (or 0xfffffffc)
Any idea why this is wrong?
Thanks so much.
Z3 is actually returning unknown. The method check returns: sat, unsat or unknown.
Here is a custom tactic that shows that the formula is indeed unsat.
#!/usr/bin/python
from z3 import *
a, b, c, t = BitVecs('a b c t', 32)
g = True
g = And(g, c == (a + 4))
g = And(g, t == (c + b))
s = Goal()
s.add(ForAll([t, a, c], Implies(t == a, g)))
T = Then("simplify", "der", "distribute-forall")
# print the simplified formula. Note that it is unsat
print T(s)
# Create a solver using the tactic above and qe
s = Then("simplify", "der", "distribute-forall", "qe", "smt").solver()
s.add(ForAll([t, a, c], Implies(t == a, g)))
print s.check()
Update
The formula is of the form
forall t, a, c: t == a ==> c == (a + 4) and t == (c + b).
This formula is logically equivalent to:
forall a, c: c == (a + 4) and a == (c + b).
which is logically equivalent to
(forall a, c: c == (a + 4)) and (forall a, c: a == (c + b)).
Both subformulas are logically equivalent to false.
This is why the formula is unsatisfiable.
The comment you wrote suggests that you believe you created the slightly different formula
forall t, a, c: t == a ==> c == (a + 4) ==> t == (c + b).
This formula is sat. To create this formula we have to replace
g = True
g = And(g, c == (a + 4))
g = And(g, t == (c + b))
with
g = Implies(c == (a + 4), t == (c + b))
The updated example is available here.

Z3py: how to extend and trunc variables?

I have two variables 'a' and 'b' of different size, see below. I have few questions:
(1) How can I copy value of 'a' to 'b'? (i.e extend operation)
(2) How can I copy value of 'b' to 'a'? (i.e trunc operation)
Thanks.
a = BitVec('a', 1)
b = BitVec('b', 32)
For extending, we use ZeroExt or SignExt. The ZeroExt will add "zeros", and SignExt will "copy" the sign bit (i.e., the most significant bit). For truncation we use Extract, it can extract any subsequence of bits. Here is a small example (also available online at rise4fun).
a = BitVec('a', 1)
b = BitVec('b', 32)
solve(ZeroExt(31, a) == b)
solve(b > 10, Extract(0,0,b) == a)
EDIT: we can use p == (x == 1) to 'assign' a Boolean variable p with the value of a Bit-vector x of size 1, and vice-versa. The formula p == (x == 1) is just stating that p is true if and only if x is 1. Here is an example (also available online here)
x = BitVec('x', 1)
p = Bool('p')
solve(p == (x == 1), x == 0)
solve(p == (x == 1), x == 1)
solve(p == (x == 1), Not(p))
solve(p == (x == 1), p)

Resources