What alternatives exist for representing sets in Z3? - z3

Per this answer the Z3 set sort is implemented using arrays, which makes sense given the SetAdd and SetDel methods available in the API. It is also claimed here that if the array modification functions are never used, it's wasteful overhead to use arrays instead of uninterpreted functions. Given that, if my only uses of a set are to apply constraints with IsMember (either on individual values or as part of a quantification), is it a better idea to use an uninterpreted function mapping from the underlying element sort to booleans? So:
from z3 import *
s = Solver()
string_set = SetSort(StringSort())
x = String('x')
s.add(IsMember(x, string_set))
becomes
from z3 import *
s = Solver()
string_set = Function('string_set', StringSort(), BoolSort())
x = String('x')
s.add(string_set(x))
Are there any drawbacks to this approach? Alternative representations with even less overhead?

Those are really your only options, as long as you want to restrict yourself to the standard interface. In the past, I also had luck with representing sets (and in general relations) outside of the solver, keeping the processing completely outside. Here's what I mean:
from z3 import *
def FSet_Empty():
return lambda x: False
def FSet_Insert(val, s):
return lambda x: If(x == val, True, s(val))
def FSet_Delete(val, s):
return lambda x: If(x == val, False, s(val))
def FSet_Member(val, s):
return s(val)
x, y, z = Ints('x y z')
myset = FSet_Insert(x, FSet_Insert(y, FSet_Insert(z, FSet_Empty())))
s = Solver()
s.add(FSet_Member(2, myset))
print(s.check())
print(s.model())
Note how we model sets by unary relations, i.e., functions from values to booleans. You can generalize this to arbitrary relations and the ideas carry over. This prints:
sat
[x = 2, z = 4, y = 3]
You can easily add union (essentially Or), intersection (essentially And), and complement (essentially Not) operations. Doing cardinality is harder, especially in the presence of complement, but that's true for all the other approaches too.
As is usual with these sorts of modeling questions, there's no single approach that will work best across all problems. They'll all have their strengths and weaknesses. I'd recommend creating a single API, and implementing it using all three of these ideas, and benchmarking your problem domain to see what works the best; keeping in mind if you start working on a different problem the answer might be different. Please report your findings!

Related

How to compare two expression in z3?

I am wondering how to compare two expressions in C++ z3. The following code generates two equal expressions, but the result shows they do not share the same id, which is different from this post. A way to do this is to simplify before checking but the speed is slow due to the simplify overhead. Is there an efficient way to solve it?
z3::context c;
z3::expr z1 = c.bool_const("z1");
z3::expr z2 = c.bool_const("z2");
z3::expr z11 = z1 && z2;
z3::expr z22 = z2 && z1;
auto res = Z3_is_eq_ast(c, z11, z22);
Simple answer: No.
Note that two terms that are semantically identical can still yield False, even after a call to simplify. The only way to check equivalence for sure is to call check_sat.
The way to think about Z3_is_eq_ast is that if it says True, then you absolutely have the same term. If it says False, then it may or may not be the same term, you just don't know. (It's essentially hash-consing, an old idea, and all the caveats apply. See here: https://en.wikipedia.org/wiki/Hash_consing).

What is the most efficient way of checking N-way equation equivalence in Z3?

Suppose I have a set of Z3 expressions:
exprs = [A, B, C, D, E, F]
I want to check whether any of them are equivalent and, if so, determine which. The most obvious way is just an N×N comparison (assume exprs is composed of some arbitrarily-complicated boolean expressions instead of the simple numbers in the example):
from z3 import *
exprs = [IntVal(1), IntVal(2), IntVal(3), IntVal(4), IntVal(3)]
for i in range(len(exprs) - 1):
for j in range(i+1, len(exprs)):
s = Solver()
s.add(exprs[i] != exprs[j])
if unsat == s.check():
quit(f'{(i, j)} are equivalent')
Is this the most efficient method, or is there some way of quantifying over a set of arbitrary expressions? It would also be acceptable for this to be a two-step process where I first learn whether any of the expressions are equivalent, and then do a longer check to see which specific expressions are equivalent.
As with anything performance related, the answer is "it depends." Before delving into options, though, note that z3 supports Distinct, which can check whether any number of expressions are all different: https://z3prover.github.io/api/html/namespacez3py.html#a9eae89dd394c71948e36b5b01a7f3cd0
Though of course, you've a more complicated query here. I think the following two algorithms are your options:
Explicit pairwise checks
Depending on your constraints, the simplest thing to do might be to call the solver multiple times, as you alluded to. To start with, use Distinct and make a call to see if its negation is satisfiable. (i.e., check if some of these expressions can be made equal.) If the answer comes unsat, you know you can't make any equal. Otherwise, go with your loop as before till you hit the pair that can be made equal to each other.
Doing multiple checks together
You can also solve your problem using a modified algorithm, though with more complicated constraints, and hopefully faster.
To do so, create Nx(N-1)/2 booleans, one for each pair, which is equal to that pair not being equivalent. To illustrate, let's say you have the expressions A, B, and C. Create:
X0 = A != B
X1 = A != C
X2 = B != C
Now loop:
Ask if X0 || X1 || X2 is satisfiable.
If the solver comes back unsat, then all of A, B, and C are equivalent. You're done.
If the solver comes back sat, then at least one of the disjuncts X0, X1 or X2 is true. Use the model the solver gives you to determine which ones are false, and continue with those until you get unsat.
Here's a simple concrete example. Let's say the expressions are {1, 1, 2}:
Ask if 1 != 1 || 1 != 2 || 1 != 2 is sat.
It'll be sat. In the model, you'll have at least one of these disjuncts true, and it won't be the first one! In this case the last two. Drop them from your list, leaving you with 1 != 1.
Ask again if 1 != 1 is satisfiable. The answer will be unsat and you're done.
In the worst case you'll make Nx(N-1)/2 calls to the solver, if it happens that none of them can be made equivalent with you eliminating one at a time. This is where the first call to Not (Distinct(A, B, C, ...)) is important; i.e., you will start knowing that some pair is equivalent; hopefully iterating faster.
Summary
My initial hunch is that the second algorithm above will be more performant; though it really depends on what your expressions really look like. I suggest some experimentation to find out what works the best in your particular case.
A Python solution
Here's the algorithm coded:
from z3 import *
exprs = [IntVal(i) for i in [1, 2, 3, 4, 3, 2, 10, 10, 1]]
s = Solver()
bools = []
for i in range(len(exprs) - 1):
for j in range(i+1, len(exprs)):
b = Bool(f'eq_{i}_{j}')
bools.append(b)
s.add(b == (exprs[i] != exprs[j]))
# First check if they're all distinct
s.push()
s.add(Not(Distinct(*exprs)))
if(s.check()== unsat):
quit("They're all distinct")
s.pop()
while True:
# Be defensive, bools should not ever become empty here.
if not bools:
quit("This shouldn't have happened! Something is wrong.")
if s.check(Or(*bools)) == unsat:
print("Equivalent expressions:")
for b in bools:
print(f' {b}')
quit('Done')
else:
# Use the model to keep bools that are false:
m = s.model()
bools = [b for b in bools if not(m.evaluate(b, model_completion=True))]
This prints:
Equivalent expressions:
eq_0_8
eq_1_5
eq_2_4
eq_6_7
Done
which looks correct to me! Note that this should work correctly even if you have 3 (or more) items that are equivalent; of course you'll see the output one-pair at a time. So, some post-processing might be needed to clean that up, depending on the needs of the upstream algorithm.
Note that I only tested this for a few test values; there might be corner case gotchas. Please do a more thorough test and report if there're any bugs!

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