Z3-solver throws 'model is not available' exception on python 3 - z3

For solving a SAT-problem I decided to use the Z3-solver from Microsoft and Python 3. The aim is to take a long model (up to 500,000 features) and find all possible solutions. To find them I want to add the first solution S1 to the initial equation and exclude S1 it and so forth. I will do it using a while-loop.
Solving a SAT-problem is important for me, because I wanna analyse feature models.
But I'm facing a problem with adding sth to the initial equation. I will share a minimal example:
# Import statements
import sys
sys.path.insert(0,'/.../z3/bin')
from z3 import * # https://github.com/Z3Prover/z3/wiki
def main():
'''
Solves after transformation a given boolean equation by using the Z3-Solver from Microsoft.
'''
fd = dict()
fd['_r'] = Bool('_r')
fd['_r_1'] = Bool('_r_1')
equation = '''And(fd.get("_r"),Or(Not(fd.get("_r")),fd.get("_r_1")))'''
# Solve the equation
s = Solver()
exec('s.add(' + equation + ')')
s.check()
print(s.model())
###################################
# Successfull until here.
###################################
s.add(Or(fd['_r'] != bool(s.model()[fd.get('_r')])))
# s.add(Or(fd['_r'] != False))
s.check()
print(s.model())
if __name__=='__main__':
main()
The first coded line after # Successfull... throws an z3types.Z3Exception: model is not available error. So i tried the line above adding simply false to the model. That works just fine.
I'm stucked here. I believe the error is easy to solve, but I don't see the solution. Does one of you? Thanks!

Models become available only after s.check() returns 'sat'.
The model maps Boolean propositions to {True, False} and
generally maps constants and functions to fixed values.
The requirement is that the model provides an interpretation
that satisfies the formula that is added to the solver 's'.
We don't know whether the solver state is satisfiable before
we have called 's.check()'.
Suppose you want to say:
s.add(Or(fd['_r'] != bool(s.model()[fd.get('_r')])))
meaning that in the model that satisfies the constraint should
have the property that if '_r' is true under the model, then
fd['_r'] != True, and if '_r' is false under the model, then
fd['_r'] != False. This is equivalent to saying
fd['_r'] != '_r'. So it isn't really necessary to access the value
of '_r' in whatever model may evaluate '_r' in order to say something
about the evaluation of it.

Related

How do I create additional constraints from a model obtained by a solver in z3 Python API?

Once I have a constraint problem, I would like to see if it is satisfiable. Based on the returned model (when it is sat) I would like to add assertions and then run the solver again. However, it seems like I am misunderstanding some of the types/values contained in the returned model. Consider the following example:
solv = z3.Solver()
n = z3.Int("n")
solv.add(n >= 42)
solv.check() # This is satisfiable
model = solv.model()
for var in model:
# do something
solv.add(var == model[var])
solv.check() # This is unsat
I would expect that after the loop i essentially have the two constraints n >= 42 and n == 42, assuming of course that z3 produces the model n=42 in the first call. Despite this, in the second call check() returns unsat. What am I missing?
Sidenote: when replacing solv.add(var == model[var]) with solv.add(var >= model[var]) I get a z3.z3types.Z3Exception: Python value cannot be used as a Z3 integer. Why is that?
When you loop over a model, you do not get a variable that you can directly query. What you get is an internal representation, which can correspond to a constant, or it can correspond to something more complicated like a function or an array. Typically, you should query the model with the variables you have, i.e., with n. (As in model[n].)
You can fix your immediate problem like this:
for var in model:
solve.add(var() == model[var()])
but this'll only work assuming you have simple variables in the model, i.e., no uninterpreted-functions, arrays, or other objects. See this question for a detailed discussion: https://stackoverflow.com/a/11869410/936310
Similarly, your second expression throws an exception because while == is defined over arbitrary objects (though doing the wrong thing here), >= isn't. So, in a sense it's the "right" thing to do to throw an exception here. (That is, == should've thrown an exception as well.) Alas, the Python bindings are loosely typed, meaning it'll try to make sense of what you wrote, not necessarily always doing what you intended along the way.

Modifying the divide and conquer SAT search in Z3-Python

I am trying to modify the divide and conquer SAT search in Z3 (Python), but feel completely lost. I will explain my question in three parts.
Introduction
I am performing a SAT search loop in Z3 using its default incremental options (i.e. using add).
However, I was made aware that my search is a simple enumeration of type "find me all-solutions", which consists on "add the negation of the previous model and iterate". Like the following:
...
phi = a_formula
s = Solver()
s.add(phi)
while s.check() == sat:
s.check()
m = s.model()
phi = add_modelNegation(m)
s.add(phi) #in order not to explore the same model again
...
This is clearly a 'brute-force' implementation.
Also I was noted that, instead of it, I could use a "divide-and-conquer" approach, in order to make the search faster. To do so, I was offered the following link (Z3Py) checking all solutions for equation (see the last answer by #alias). I tested this code works at In Z3-Python, I get "builtin_function_or_method' object is not iterable" when performing model search. The code is essentially the following:
def all_smt(s, initial_terms):
def block_term(s, m, t):
s.add(t != m.eval(t, model_completion=True))
def fix_term(s, m, t):
s.add(t == m.eval(t, model_completion=True))
def all_smt_rec(terms):
if sat == s.check():
m = s.model()
yield m
for i in range(len(terms)):
s.push()
block_term(s, m, terms[i])
for j in range(i):
fix_term(s, m, terms[j])
yield from all_smt_rec(terms[i:])
s.pop()
yield from all_smt_rec(list(initial_terms))
The problem
My SAT problem is not that 'basic' one in which I just enumerate (or rapidly enumerate, if I follow the code above) all solutions. I have a 'booster' that allows me to add more restrictions if a given model holds some properties. This booster is a kind of heuristic. The schema is as follows:
...
s = Solver()
s.add(True)
while s.check() == sat:
s.check()
m = s.model()
phi = add_modelNegation(m)
s.add(phi) #in order not to explore the same model again
if holds_property(m): #if the model holds a property
add_moreConstraints(s,m) #add other constrains to the formula
...
I would like to add the all_smt ideas to this version, accurately making use of push and pop. My problem is that I do not perfectly understand the usage of push and pop and, since my code does not fix variables like the fast one does, I do not not how to 'merge' both ideas. How can I adapt my code to be faster?
A concrete coding example:
I offer a concrete example below. This is an example made in order to facilitate your coding, but it makes no sense in reality.
For instance, imagine that the booster or heuristic is that if the number of False assignments in the model is greater than 1, then we can add the 'reversed' model to the solver, where the 'reversed' model just changes the value of each assignment to the opposite. For instance, if the model is phi=[c0=True, c1=False, c2=False, c3=True], then phi holds the property and, thus, its 'reverse' is added, where phi_reversed = [c0=False, c1=True, c2=True, c3=False]. On the other side, phi'=[c0=True, c1=True, c2=False, c3=True] does not hold the property, so nothing additional is added to the solver.
I repeat that this booster makes no sense, but it is enough for the sake of coding. So let us make that function:
def holds_property(m, varss):
numFalses = 0
for x in varss:
if m[x] == False:
numFalses = numFalses + 1
if numFalses > 1:
return True
else:
return False
And also the function that creates the 'reversed' version of a model:
def make_reverse(m, varss):
for x in varss:
if m[x] == True:
m[x] = False
elif m[x] == False:
m[x] = True
return m
I will also invent a formula and a set of variables. Thus, my 'brute-force' implementation is (this is not pseudo-code):
a,b,c = Bools('a b 'c)
varss = [a,b,c]
phi = And(a, Or(b,c))
s = Solver()
s.add(phi)
models = []
while s.check() == sat:
s.check()
m = s.model()
models.append(m)
phi = add_modelNegation(m)
s.add(phi)
if holds_property(m, varss):
s.add(make_reverse(m, varss))
return models
The question is, again: how can I insert (or modify) the ideas of the 'fast' version to this one? I would like to add 'fixing' with push and pop, basically.
Any help? I know the question is dense but tried to explain it calmly and with examples :) Anyway, do not hesitate to ask more and I will edit the post.
When you push, you're creating a nesting level in your assertions. That is, the solver puts a marker on the assertion stack, which you can jump back to later on with a call to pop. What this means, of course, is that when you do the pop, you'll lose all the assertions that you inserted after the last push.
Since you have extra constraints, simply keep track of them, and manage them between calls to push and pop. Something like this:
from z3 import *
def pruned_all_smt(s, initial_terms, holds_property, more_constraints):
global extra_constraints
extra_constraints = []
def block_term(m, t):
s.add(t != m.eval(t, model_completion=True))
def fix_term(m, t):
s.add(t == m.eval(t, model_completion=True))
def custom_pop():
global extra_constraints
s.pop()
for ec in extra_constraints:
s.add(ec)
def custom_model():
global extra_constraints
m = s.model()
if holds_property(m):
extra_constraints = more_constraints(m) + extra_constraints
for ec in extra_constraints:
s.add(ec)
return m
def pruned_all_smt_rec(terms):
if sat == s.check():
m = custom_model()
yield m
for i in range(len(terms)):
s.push()
block_term(m, terms[i])
for j in range(i):
fix_term(m, terms[j])
yield from pruned_all_smt_rec(terms[i:])
custom_pop()
yield from pruned_all_smt_rec(list(initial_terms))
Note that we made the the hold_property and more_constraint parameters, so you need to supply those separately. Here's a template use:
def check_prop(m):
return True;
def more_constraints(m):
return []
Given this, you'd use it as:
print(list(pruned_all_smt(s, varss, check_prop, more_constraints)))
Give this a try and see how far it takes you. I haven't really tested this with anything meaningful, so there might be some gotchas!

how to use z3 to get valid range of a variable

I want to find the range of valid values that a variable can have, given some constraints. Eg,
x = Int('x')
s = Solver()
s.add(x >= 1)
s.add(x < 5+2)
Is there some way that I can get z3 to print 1..6 for this variable?
I tried using the following, but range() applies only to declarations.
print("x.range():", x.range()) # this does not work
Note: 1. This question seems to ask the same, but I did not understand its answers, and I am looking for python answer.
in reply to #Malte: I am not looking for all the answers, I just want to simplify multiple constraints in to a valid range. If constraints on both sides of the variable cannot be merged, then at least only on one side as is mentioned in above mentioned question.
This question comes up occasionally, and the answer isn't very trivial, unfortunately. It really depends on what your constraints are and exactly what you are trying to do. See:
Is it possible to get a legit range info when using a SMT constraint with Z3
And
(Sub)optimal way to get a legit range info when using a SMT constraint with Z3
Essentially, the problem is too difficult (and I'd say not even well defined) if you have multiple variables. If you have exactly one variable, you can use the optimizer to some extent, assuming the variable is indeed bounded. In case you have multiple variables, one idea might be to fix all but one to satisfying constants, and compute the range of that last variable based on the constant assignment to the others. But again, it depends on what you're really trying to achieve.
Please take a look at the above two answers and see if it helps you. If not, please show us what you tried: Stack-overflow works the best when you post some code and see how it can be improved/fixed.
As a SAT/SMT solver, Z3 "only" needs to find a single model (satisfying assignment) to show that a formula is satisfiable. Finding all models is therefore not directly supported.
The question comes up regularly, though, and the solution is to repeatedly find and then block (assume in negated form) models until no further model can be found. For example, for your snippet of code:
x = Int('x')
s = Solver()
s.add(x >= 1)
s.add(x < 5+2)
result = s.check()
while result == sat:
m = s.model()
print("Model: ", m)
v_x = m.eval(x, model_completion=True)
s.add(x != v_x)
result = s.check()
print(result, "--> no further models")
Executing the script yields the solution you asked for, albeit in a less concise form:
Model: [x = 1]
Model: [x = 2]
Model: [x = 3]
Model: [x = 4]
Model: [x = 5]
Model: [x = 6]
unsat --> no further models
In general,
you would have iterate over all variables (here: just x)
model completion is necessary for variables whose value doesn't affect satisfiability; since any value will do, they won't be explicit in the model
Related questions whose answers provide additional details:
(Z3Py) checking all solutions for equation
Why Z3Py does not provide all possible solutions
Getting all solutions of a boolean expression in Z3Py never ends

Z3: implementing "Model Checking Using SMT and Theory of Lists" solver hanging

I'm trying to implement some code from this paper: Model Checking Using SMT and Theory of Lists to prove facts about a simple machine. I wrote the following code using the Python Z3 API, mirroring the code described in the paper: the code and problem was intentionally simplified in order to show the problem better:
from z3 import *
MachineIntSort = BitVecSort(16)
MachineInt = lambda x: BitVec(x, 16)
def DeclareLinkedList(sort):
LinkedList = Datatype(f'{sort.name()}_LinkedList')
LinkedList.declare('nil')
LinkedList.declare('cons', ('car', sort), ('cdr', LinkedList))
return LinkedList.create()
State = Datatype('State')
State.declare('state',
('A', MachineIntSort),
('B', MachineIntSort),
('C', MachineIntSort),
('D', MachineIntSort))
State = State.create()
StateList = DeclareLinkedList(State)
def transition_condition(initial, next):
return State.A(next) == State.A(initial) + 1
def final_condition(lst):
return State.A(StateList.car(lst)) == 2
solver = Solver()
check_execution_trace = Function('check_execution_trace', StateList, BoolSort())
execution_list = Const('execution_list', StateList)
solver.add(ForAll(execution_list, check_execution_trace(execution_list) ==
If(And(execution_list != StateList.nil, StateList.cdr(execution_list) != StateList.nil),
And(
transition_condition(StateList.car(execution_list), StateList.car(StateList.cdr(execution_list))),
check_execution_trace(StateList.cdr(execution_list)),
If(final_condition(StateList.cdr(execution_list)),
StateList.nil == StateList.cdr(StateList.cdr(execution_list)),
StateList.nil != StateList.cdr(StateList.cdr(execution_list))
)
),
True), # If False, unsat but incorrect. If True, it hangs
))
states = Const('states', StateList)
# Execution trace cannot be empty
solver.add(StateList.nil != states)
# Initial condition
solver.add(State.A(StateList.car(states)) == 0)
# Transition axiom
solver.add(check_execution_trace(states))
print(solver.check())
print(solver.model())
The problem is that model step hangs instead of giving the (trivial) solution. I think I might not have implemented everything the paper describes: I don't understand what "Finally, it is important to stress the purpose of the instantiation pattern ( PAT:
{check tr (lst)} ) in the FORALL clause. This axiom states something about all
lists. However, it would be impossible for the SMT solver to try to prove that the
statement indeed holds for all possible lists. Instead, the common approach is to
provide an instantiation pattern to basically say in which cases the axiom should
be instantiated and therefore enforced by the solver." means, so I didn't implement it.
My goal now is not to have pretty code (I know the star-import is ugly, ...) but to have working code.
Quantified formulas are hard for SMT solvers to deal with, as they make the logic semi-decidable. SMT solvers usually rely on "heuristics" to deal with such problems. Patterns are one way to "help" those heuristics to converge faster, when dealing with quantifiers.
You might want to read Section 13.2 of http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.225.8231&rep=rep1&type=pdf
To see an example of how to add patterns in the z3py bindings, look at this page: https://ericpony.github.io/z3py-tutorial/advanced-examples.htm (Search for "Patterns" when the page comes up.)

Why can't Z3 propose a sort with no elements?

Using Z3Py, I tried to build a program which Z3 would decide means that the sort Human is empty.
from z3 import *
from z3_helper import Z3Helper
Human = DeclareSort("Human")
is_mortal = Function("is_mortal", Human, BoolSort())
h = Const('h', Human)
s = Solver()
s.add([
ForAll([h], And(is_mortal(h), Not(is_mortal(h))))
])
print s.check()
s.model()
But instead of returning a model where Human is empty, it returns unsat. Why is this?
If I remove the "all men are mortal" axiom, it returns an empty set as the model.
Is the problem that the existence of const h means that the existence of at least one Human is required?
SMT-LIB and Z3 take the view that simply typed first-order logic assumes that all sorts are non-empty. See also http://smtlib.cs.uiowa.edu/papers/smt-lib-reference-v2.6-draft-3.pdf, section 5.1 onwards.

Resources