z3py: Retrieve branching conditions from z3 formula - z3

Let's say I have a z3py program like this one:
import z3
a = z3.Int("a")
input_0 = z3.Int("input_0")
output = z3.Int("output")
some_formula = z3.If(a < input_0, 1, z3.If(a > 1, 4, 2))
s = z3.Solver()
s.add(output == some_formula)
s.check()
m = s.model()
print(m)
Is there an elegant way for me to retrieve the branching conditions from some_formula?
So get a list like [a < input_0, a > 1]. It should work for arbitrarily deep nesting of if expressions.
I know there is some way to use cubes, but I am not able to retrieve more than two cube expressions. I am not sure how to configure the solver.
My ultimate goal is to force the solver to give me different outputs based on the constraints I push and pop. The constraints are the set of conditions I have inferred from this formula.

You can print the cubes using:
for cube in s.cube():
print cube
But this isn't going to really help you. For your example, it prints:
[If(a + -1*input_0 >= 0, If(a <= 1, 2, 4), 1) == 1]
[Not(If(a + -1*input_0 >= 0, If(a <= 1, 2, 4), 1) == 1)]
which isn't quite what you were looking for.
The easiest way to go about your problem would be to directly walk down the AST of the formula yourself, and grab the conditions as you walk along the expressions. Of course, Z3 AST is quite a hairy object (pun intended!), so this will require quite a bit of programming. But reading through the constructors (If, Var etc.) in this file can get you started: https://z3prover.github.io/api/html/z3py_8py_source.html

Alright,thanks #alias! I came up with a custom version, which gets the job done. If someone knows a more elegant way to do this, please let me know.
import z3
a = z3.Int("a")
input_0 = z3.Int("input_0")
output = z3.Int("output")
some_formula = z3.If(a < input_0, 1, z3.If(a > 1, 4, 2))
nested_formula = z3.If(some_formula == 1, 20, 10)
s = z3.Solver()
s.add(output == some_formula)
s.check()
m = s.model()
print(m)
def get_branch_conditions(z3_formula):
conditions = []
if z3.is_app_of(z3_formula, z3.Z3_OP_ITE):
# the first child is usually the condition
cond = z3_formula.children()[0]
conditions.append(cond)
for child in z3_formula.children():
conditions.extend(get_branch_conditions(child))
return conditions
conds = get_branch_conditions(some_formula)
print(conds)
conds = get_branch_conditions(nested_formula)
print(conds)

Related

Z3 - how to count matches?

I have a finite set of pairs of type (int a, int b). The exact values of the pairs are explicitly present in the knowledge base. For example it could be represented by a function (int a, int b) -> (bool exists) which is fully defined on a finite domain.
I would like to write a function f with signature (int b) -> (int count), representing the number of pairs containing the specified b value as its second member. I would like to do this in z3 python, though it would also be useful to know how to do this in the z3 language
For example, my pairs could be:
(0, 0)
(0, 1)
(1, 1)
(1, 2)
(2, 1)
then f(0) = 1, f(1) = 3, f(2) = 1
This is a bit of an odd thing to do in z3: If the exact values of the pairs are in your knowledge base, then why do you need an SMT solver? You can just search and count using your regular programming techniques, whichever language you are in.
But perhaps you have some other constraints that come into play, and want a generic answer. Here's how one would code this problem in z3py:
from z3 import *
pairs = [(0, 0), (0, 1), (1, 1), (1, 2), (2, 1)]
def count(snd):
return sum([If(snd == p[1], 1, 0) for p in pairs])
s = Solver()
searchFor = Int('searchFor')
result = Int('result')
s.add(Or(*[searchFor == d[0] for d in pairs]))
s.add(result == count(searchFor))
while s.check() == sat:
m = s.model()
print("f(" + str(m[searchFor]) + ") = " + str(m[result]))
s.add(searchFor != m[searchFor])
When run, this prints:
f(0) = 1
f(1) = 3
f(2) = 1
as you predicted.
Again; if your pairs are exactly known (i.e., they are concrete numbers), don't use z3 for this problem: Simply write a program to count as needed. If the database values, however, are not necessarily concrete but have other constraints, then above would be the way to go.
To find out how this is coded in SMTLib (the native language z3 speaks), you can insert print(s.sexpr()) in the program before the while loop starts. That's one way. Of course, if you were writing this by hand, you might want to code it differently in SMTLib; but I'd strongly recommend sticking to higher-level languages instead of SMTLib as it tends to be hard to read/write for anyone except machines.

Incorrect behaviour of .check() in z3py

Consider a set of constraints F = [a + b > 10, a*a + b + 10 < 50].
When I run it using:
s = Solver()
s.add(F)
s.check()
I get sat solution.
If I run it with:
s = Solver()
s.check(F)
I get an unknown solution. Can someone explain why this is happening?
Let's see:
from z3 import *
a = Int('a')
b = Int('b')
F = [a + b > 10, a*a + b + 10 < 50]
s = Solver()
s.add(F)
print (s.check())
print (s.model())
This prints:
sat
[b = 15, a = -4]
That looks good to me.
Let's try your second variant:
from z3 import *
a = Int('a')
b = Int('b')
F = [a + b > 10, a*a + b + 10 < 50]
s = Solver()
print (s.check(F))
print (s.model())
This prints:
sat
[b = 7, a = 4]
That looks good to me too.
So, I don't know how you're getting the unknown answer. Maybe you have an old version of z3; or you've some other things in your program you're not telling us about.
The important thing to note, however, is that s.add(F); s.check() AND s.check(F) are different operations:
s.add(F); s.check() means: Assert the constraints in F; check that they are satisfiable.
s.check(F) means: Check that all the other constraints are satisfiable, assuming F is. In particular, it does not assert F. (This is important if you do further asserts/checks later on.)
So, in general these two different ways of using check are used for different purposes; and can yield different answers. But in the presence of no other assertions around, you'll get a solution for both, though of course the models might be different.
Aside One reason you can get unknown is in the presence of non-linear constraints. And your a*a+b+10 < 50 is non-linear, since it does have a multiplication of a variable by itself. You can deal with that either by using a bit-vector instead of an Int (if applicable), or using the nonlinear-solver; which can still give you unknown, but might perform better. But just looking at your question as you asked it, z3 is just fine handling it.
To find out what is going on within s.check(F), you can do the following:
from z3 import *
import inspect
a = Int('a')
b = Int('b')
F = [a + b > 10, a*a + b + 10 < 50]
s = Solver()
print (s.check(F))
print (s.model())
source_check = inspect.getsource(s.check)
print(source_check)
The resulting output:
sat
[b = 10, a = 1]
def check(self, *assumptions):
"""Check whether the assertions in the given solver plus the optional assumptions are consistent or not.
>>> x = Int('x')
>>> s = Solver()
>>> s.check()
sat
>>> s.add(x > 0, x < 2)
>>> s.check()
sat
>>> s.model().eval(x)
1
>>> s.add(x < 1)
>>> s.check()
unsat
>>> s.reset()
>>> s.add(2**x == 4)
>>> s.check()
unknown
"""
s = BoolSort(self.ctx)
assumptions = _get_args(assumptions)
num = len(assumptions)
_assumptions = (Ast * num)()
for i in range(num):
_assumptions[i] = s.cast(assumptions[i]).as_ast()
r = Z3_solver_check_assumptions(self.ctx.ref(), self.solver, num, _assumptions)
return CheckSatResult(r)
The semantics of assumptions vs. assertions are discussed here and here. But if have to admit that they are not really clear to me yet.

Z3py how to solve a problem with many possible path (k out of n potential actions, order matters) efficiently

I am trying to solve a problem that consists of n actions (n >= 8). A path consists k (k == 4 for now) actions. I would like to check if there exists any path, which satisfies the set of constraints I defined.
I have made two attempts to solve this problem:
Attempt 1: Brute force, try all permutations
Attempt 2: Code a path selection matrix M [k x n], such that each row contains one and only one element greater than 0, and all other elements equal to 0.
For instance if k == 2, n == 2, M = [[0.9, 0], [0, 0.7]] represents perform action 1 first, then action 2.
Then my state transition was coded as:
S1 = a2(a1(S0, M[1][1]), M[1][2]) = a2(a1(S0, 0.9), 0)
S2 = a2(a1(S1, M[2][1]), M[2][2]) = a2(a1(S1, 0), 0.7)
Note: I made sure that S == a(S,0), so that in each step only one action is executed.
Then constraints were checked on S2
I was hoping this to be faster than the permutation way of doing it. Unfortunately, this turns out to be slower. Just wondering if there is any better way to solve this problem?
Code:
_path = [[Real(f'step_{_i}_action_{_j}') for _j in range(len(actions))] for _i in range(number_of_steps)]
_states: List[State] = [self.s0]
for _i in range(number_of_steps):
_new_state = copy.deepcopy(_states[-1])
for _a, _p in zip(actions, _path[_i]):
self.solver.add(_a.constraints(_states[-1], _p))
_new_state = _a.execute(_new_state, _p)
_states.append(_new_state)

Reducing an integer set in z3 over addition

I'm experimenting with (and failing at) reducing sets in z3 over operations like addition. The idea is eventually to prove stuff about arbitrary reductions over reasonably-sized fixed-sized sets.
The first of the two examples below seems like it should yield unsat, but it doesn't. The second does work, but I would prefer not to use it as it requires incrementally fiddling with the model.
def test_reduce():
LIM = 5
VARS = 10
poss = [Int('i%d'%x) for x in range(VARS)]
i = Int('i')
s = Solver()
arr = Array('arr', IntSort(), BoolSort())
s.add(arr == Lambda(i, And(i < LIM, i >= 0)))
a = arr
for x in range(len(poss)):
s.add(Implies(a != EmptySet(IntSort()), arr[poss[x]]))
a = SetDel(a, poss[x])
def final_stmt(l):
if len(l) == 0: return 0
return If(Not(arr[l[0]]), 0, l[0] + (0 if len(l) == 1 else final_stmt(l[1:])))
sm = final_stmt(poss)
s.push()
s.add(sm == 1)
assert s.check() == unsat
Interestingly, the example below works much better, but I'm not sure why...
def test_reduce_with_loop_model():
s = Solver()
i = Int('i')
arr = Array('arr', IntSort(), BoolSort())
LIM = 1000
s.add(arr == Lambda(i, And(i < LIM, i >= 0)))
sm = 0
f = Int(str(uuid4()))
while True:
s.push()
s.add(arr[f])
chk = s.check()
if chk == unsat:
s.pop()
break
tmp = s.model()[f]
sm = sm + tmp
s.pop()
s.add(f != tmp)
s.push()
s.add(sm == sum(range(LIM)))
assert s.check() == sat
s.pop()
s.push()
s.add(sm == 11)
assert s.check() == unsat
Note that your call to:
f = Int(str(uuid4()))
Is inside the loop in the first case, and is outside the loop in the second case. So, the second case simply works on one variable, and thus converges quickly. While the first one keeps creating variables and creates a much harder problem for z3. It's not surprising at all that these two behave significantly differently, as they encode entirely different constraints.
As a general note, reducing an array of elements with an operation is just not going to be an easy problem for z3. First, you have to assume an upper bound on the elements. And if that's the case, then why bother with Lambda or Array at all? Simply create a Python list of that many variables, and ignore the array logic completely. That is:
elts = [Int("s%d"%i) for i in range(100)]
And then to access the elements of your 'array', simply use Python accessor notation elts[12].
Note that this only works if all your accesses are with a constant integer; i.e., your index cannot be symbolic. But if you're looking for proving reduction properties, that should suffice; and would be much more efficient.

Find how many of the first n elements of an Array satisfy a condition in z3

I have a z3 Array:
x = Array('x', IntSort(), IntSort())
A fixed number n:
n = 10
And a filtering condition based on simple arithmetic:
lambda i: Or(i == 0, i > 2)
What I want is to know the total number of elements from index 0 to index n which satisfy this condition (something that, if this were a normal python list, would look like len(filter(lambda i: i == 0 or i > 2, x)).
I can't figure out how to do this in z3. I tried
y == Int('y')
solver.add(y == sum([1 if Or(x[i] == 0, x[i] > 2) else 0 for i in range(0,n)]))
but get the following error:
z3.z3types.Z3Exception: Symbolic expressions cannot be cast to concrete Boolean values.
Is there a way to proceed? Many thanks!
It'd be best to post the exact code you tried. For one thing, the if-then-else should be coded using the If construct and the sum is best expressed using Sum. The following works fine, for instance:
from z3 import *
x = Array ('x', IntSort(), IntSort())
solver = Solver()
n = 10
y = Int('y')
solver.add(y == Sum([If(Or(x[i] == 0, x[i] > 2), 1, 0) for i in range(0,n)]))
print solver.check()
print solver.model()
But this'll be satisfiable for all values of y since there is no constraint on the contents of the array x.

Resources