Z3: A better way to model? - z3

I've two SMT problem instances. The first is here:
http://gist.github.com/1232766
Z3 returns a model for this problem in about 2 minutes on my not-so-great machine, which is great.. I also have this one:
http://gist.github.com/1232769
I've ran z3 overnight on this problem, without Z3 completing. If you check the contents of these files, you'll see that the second one is identical to the first, except it has an extra assertion to "reject" the model returned by the first instance. (You can do a "diff" between them to see what I mean.) I happen to know that this problem has multiple satisfying models, and I'm trying to use z3 to find all satisfying models.
I understand that this might be completely expected, but I was curious to know why the second one is a much tougher problem for Z3 compared to the first. Is there a better way to formulate the second problem so Z3 will have an easier time?
Thanks..

It is hard to give you a precise answer without knowing more about your application.
As you suggested, modeling plays a big role in the logic you are using: AUFBV.
The strategy used by Z3 also has a big impact on the overall performance.
Z3 comes equipped with several builtin strategies. It has many parameters that can be used to influence the search.
Z3 also has a strategy specification language. This is a new feature. I’m not advertising it because it is working in progress, and the language will most likely change in the next versions.
You can access more information about the strategy language by executing the commands:
(help check-sat-using)
(help-strategy)
That being said, there is a builtin strategy in Z3 that seems to be effective on your problem.
It is the strategy used for the logic UFBV. Your problem uses arrays, but they can be avoided by transforming table0 into a function with two arguments:
(declare-fun table0 ((_ BitVec 64) (_ BitVec 64)) (_ BitVec 8))
And replacing every term of the form (select (table0 s65) t) with (table0 s65 t) where t is an arbitrary term.
Finally, you must also add the command (set-logic UFBV) in the beginning of the file. With this setting, I managed to generate 4 different models for your query.
I didn’t try to generate more than that. Each call consumed approx 75 secs.

Related

SAT queries are slowing down in Z3-Python: what about incremental SAT?

In Z3 (Python) my SAT queries inside a loop are slowing down, can I use incremental SAT to overcome this problem?
The problem is the following: I am performing a concrete SAT search inside a loop. On each iteration, I get a model (of course, I store the negation of the model in order not to explore the same model again). And also, if that model satisfies a certain property, then I also add a subquery of it and add other restrictions to the formula. And iterate again, until UNSAT (i.e. "no more models") is obtained.
I offer an orientative snapshot of the code:
...
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
s = add_moreConstraints(s,m) #add other constrains to the formula
...
The question is that, as the formula that s has to solve gets greater, Z3 is starting to have more trouble to find those models. That is okay: this should happen, since finding a model is now more difficult because of the added restrictions. However, in my case, it is happening too much: the computation speed has been even halved; i.e. the time that the solver needs to find a new model is the double after some iterations.
Thus, I would like to implement some kind of incremental solving and wondered whether there are native methods in Z3 to do so.
I have been reading about this in many pages (see, for instance, How incremental solving works in Z3?), but only found this response in How to use incremental solving with z3py interesting:
The Python API is automatically "incremental". This simply means the ability to call the command check() multiple times, without the solver forgetting what it has seen before (i.e., call check(), assert more facts, call check() again; the second check() will take into account all the assertions from the very beginning).
I am not sure I understand, thus I make a simple question: that the response mean that the incremental SAT is indeed used in Z3's SAT? The point I think I am looking for another incrementality; for example: if in the SAT iteration number 230 it is inevitable that a variable (say b1) is true, then that is a fact that will not change afterwards, you can set it to 1, simplify the formula and not re-reason anything to do with b1, because all models if any will have b1. Is this incremental SAT of Z3 considering these kind of cases?
In case not, how could I implement this?
I know there are some implementations in PySat or in MiniSat, but I would like to do it in Z3.
As with anything related to performance of z3 solving, there's no one size fits all. Each specific problem can benefit from different ideas.
Incremental Solving The term "incremental solving" has a very specific meaning in the SAT/SMT context. It means that you can continue to add assertions to the system after a call to check, without it forgetting the assertions you added before hand. This is what makes it incremental. Additionally, you can set jump-points; i.e., you can tell the solver to "forget" the assertions you put in after a certain point in your program, essentially moving through a stack of assertions. For details, see Section 3.9 of https://smtlib.cs.uiowa.edu/papers/smt-lib-reference-v2.6-r2021-05-12.pdf, specifically the part where it talks about the "Assertion Stack."
And, as noted before, you don't have to do anything specific for z3 to be incremental. It is incremental by default, i.e., you can simply add new assertions after calling check, or use push/pop calls etc. (Compare this to, for instance, CVC4; which is by default not incremental. If you want to use CVC4 in incremental mode, you have to pass a specific command line argument.) The main reason for this is that incremental mode requires extra bookkeeping, which CVC4 isn't willing to pay for unless you explicitly ask it to do so. For z3, the developers decided to always make it incremental without any command line switches.
Regarding your particular question about what happens if b1 is true: Well, if you somehow determined b1 is always true, simply assert it. And the solver will automatically take advantage of this; nothing special needs to be done. Note that z3 learns a ton of lemmas as it works through your program such as these and adds them to its internal database anyhow. But if you have some external mechanism that lets you deduce a particular constraint, just go ahead and add it. (Of course, the soundness of this will be on you, not on z3; but that's a different question.)
One specific "trick" in speeding up enumerating "find me all-solutions" loops like you are doing is to do a divide-and-conquer approach, instead of the "add the negation of the previous model and iterate." In practice this can make a significant difference in performance. I think you should try this idea. It's explained here: https://theory.stanford.edu/~nikolaj/programmingz3.html#sec-blocking-evaluations As you can see, the all_smt function defined at the end of that section takes specific advantage of incrementality (note the calls to push/pop) to speed up the model-search process, by carefully dividing the search space into disjoint segments, instead of doing a random-walk. Using this might give you the speed-up you need. But, again, as with anything performance specific, you'll need to tell us more about exactly what problem you are solving: None of these methods can avoid performance problems caused by modeling issues. (For instance, using integers to model booleans is one common pitfall.) See this answer for some generic advice: https://stackoverflow.com/a/57661441/936310

Incremental SMT solver with ability to drop specific constraint

Is there an incremental SMT solver or an API for some incremental SMT solver where I can add constraints incrementally, where I can uniquely identify each constraint by some label/name?
The reason I want to identify the constraints uniquely is so that I can drop them later by specifying that label/name.
The need for dropping constraints is due to the fact that my earlier constraints become irrelevant with time.
I see that with Z3 I cannot use the push/pop based incremental approach because it follows a stack based idea whereas my requirement is to drop specific earlier/old constraints.
With the other incremental approach of Z3 based on assumptions, I would have to perform check-sat of the format "(check-sat p1 p2 p3)" i.e. if I had three assertions to check then I would require three boolean constants p1,p2,p3, but in my implementation I would have thousands of assertions to check at a time, indirectly requiring thousands of boolean constants.
I also checked JavaSMT, a Java API for SMT solvers, to see if the API provides some better way of handling this requirement, but I see only way to add constraints by "addConstraint" or "push" and was unable to find any way of dropping or removing specific constraints since the pop is the only option available.
I would like to know if there is any incremental solver where I can add or drop constraints uniquely identified by names, or an API where there is an alternative way to handle it. I would appreciate any suggestion or comments.
The "stack" based approach is pretty much ingrained into SMTLib, so I think it'll be tough to find a solver that does exactly what you want. Although I do agree it would be a nice feature.
Having said that, I can think of two solutions. But neither will serve your particular use-case well, though they will both work. It comes down to the fact that you want to be able to cherry-pick your constraints at each call to check-sat. Unfortunately this is going to be expensive. Each time the solver does a check-sat it learns a lot of lemmas based on all the present assertions, and a lot of internal data-structures are correspondingly modified. The stack-based approach essentially allows the solver to "backtrack" to one of those learned states. But of course, that does not allow cherry-picking as you observed.
So, I think you're left with one of the following:
Using check-sat-assuming
This is essentially what you described already. But to recap, instead of asserting booleans, you simply give them names. So, this:
(assert complicated_expression)
becomes
; for each constraint ci, do this:
(declare-const ci Bool)
(assert (= ci complicated_expression))
; then, check with whatever subset you want
(check-sat-assuming (ci cj ck..))
This does increase the number of boolean constants you have to manage, but in a sense these are the "names" you want anyhow. I understand you do not like this as it introduces a lot of variables; and that is indeed the case. And there's a good reason for that. See this discussion here: https://github.com/Z3Prover/z3/issues/1048
Using reset-assertions and :global-declarations
This is the variant that allows you to arbitrarily cherry-pick the assertions at each call to check-sat. But it will not be cheap. In particular, the solver will forget everything it learned each time you follow this recipe. But it will do precisely what you wanted. First issue:
(set-option :global-declarations true)
And somehow keep track of all these yourself in your wrapper. Now, if you want to arbitrarily "add" a constraint, you don't need to do anything. Just add it. If you want to remove something, then you say:
(reset-assertions)
(assert your-tracked-assertion-1)
(assert your-tracked-assertion-2)
;(assert your-tracked-assertion-3) <-- Note the comment, we're skipping
(assert your-tracked-assertion-4)
..etc
etc. That is, you "remove" the ones you don't want. Note that the :global-declarations call is important since it'll make sure all your data-declarations and other bindings stay intact when you call reset-assertions, which tells the solver to start from a clean-slate of what it assumed and learned.
Effectively, you're managing your own constraints, as you wanted in the first place.
Summary
Neither of these solutions is precisely what you wanted, but they will work. There's simply no SMTLib compliant way to do what you want without resorting to one of these two solutions. Individual solvers, however, might have other tricks up their sleeve. You might want to check with their developers to see if they might have something custom for this use case. While I doubt that is the case, it would be nice to find out!
Also see this previous answer from Nikolaj which is quite related: How incremental solving works in Z3?

what's the difference between "simplify" and "ctx-solver-simplify" in z3

since current version, there is some problem in "ctx-solver-simplify"like in the example http://rise4fun.com/Z3/CqRv z3 gives the wrong answer. I replace "ctx-solver-simplify" by "simplify" like http://rise4fun.com/Z3/x9X4
I am wondering, what's the difference between these two tactic "simplify" and "ctx-solver-simplify"?
The tactic simplify only performs "local simplifications". For every term t, we have that simplify(t) is a new term equivalent to t. Moreover, the result of simplify(t) does not depend on the context where t occurs. By context, I meant the assertion F where t occurs and all other assertions. Since, simplify is local, it is very efficient. The implementation is essentially based on a bottom up application of simplification rules. Moreover, since the result of simplify(t) does not depend on contextual information, we can cache it. Thus, even if t occurs N times in a formula F, we only need to simplify it once. All builtin solvers in Z3 apply this kind of simplification. Thus, tactics such as simplify have been extensively tested.
The tactic ctx-solver-simplify uses the context where t occurs to apply simplifications. The basic idea is to simplify a formula F by traversing it using a solver S. The solver S essentially contains the "context". Whenever S.check() returns unsat, we know the current context is inconsistent, then we can replace the current formula by false. The ctx-solver-simplify is much more expensive. First, it performs many calls to S.check(). Each one of these calls is potentially very expensive. It is also much harder to cache intermediate results. Z3 may have to simplify a subformula t many times because it occurs in different contexts.
The bug you reported in your question have been fixed. The fix will be available in the next release (version 4.1). If you need we can provide you a pre-release version of Z3 4.1

Incremental calls to Z3 on UFBV with and without push calls

I am running Z3 on UFBV queries. Currently the query contains 2 calls check-sat.
If I put push 1 just after check-sat, Z3 solves the query in 30sec. If I don't put any push 1 at all, thus have two calls check-sat without any push 1 between them, then Z3 solves it in 200sec.
Interesting. Any specific reasons or just a coincidence?
Z3 3.x has a "strategy specification language" based on tactics and tacticals. I'm not "advertising" that yet because it is working in progress.
The basic idea is described in this slide deck.
We have a different built-in strategy for each logic. The strategies usually do not support incremental solving, because they may apply transformations that use a "closed-world" assumption. Example, we have transformations that map 0-1 linear integer arithmetic into SAT. Whenever Z3 detects that the user "wants" incremental solving (e.g., multiple check-sat commands, push&pop commands), it switches to a general purpose solver. In future versions, we will provide more features for controlling Z3 behavior.
BTW, if you have two consecutive (check-sat) (check-sat) commands, Z3 not necessarily enters in incremental mode. It will enter only if there is an assert or push command between the two calls.
Now, suppose your query is of the form: (check-sat) <assertions> (check-sat), and your second query is of the form (check-sat) <assertions> (push) (check-sat). In both cases, Z3 will be in incremental mode in the second (check-sat). However, the behavior is still not the same. The incremental solver "compiles" the asserted formulas to an internal format, and its behavior is affected if a push command has been executed. For example, it will use a more efficient encoding of binary clauses only if there is no user scope. By user scope, I mean, the number of push commands - number of pop commands. It does that because the data-structure used in the more efficient encoding does not have an efficient undo/inverse operation.

The "pull-nested-quantifiers" option seems to cause problems in the context for UFBV?

I am currently experimenting with Z3 as bounded engine for specifications written in Alloy (a relational logic/language). I am using the UFBV as target language.
I detect a problem using the Z3 option (set-option :pull-nested-quantifiers true).
For two semantically identical SMT specifications Spec1 and Spec2, Z3 times out (200 sec) for proving Spec1 but proves Spec2.
The only different between Spec1 and Spec2 is that they have different function identifiers (because I use java hash names). Can this be related to a bug?
The second observation I would like to share and discuss, is the "iff" operator in the context of UFBV. This operator is not supported, if (set-logic UFBV) is set. My solution was to use "=" instead but this do not work well if the operands contains deeply nested quantifiers and the "pull-nested-quantifiers" is set. The other saver solution is to use double implication.
Now the question:
Is there any other better solution for model "iff" in UFBV, because I think, that using double implication will in general loose maybe useable semantic information for improvement/simplifications.
http://i12www.ira.uka.de/~elghazi/tmp/
you can find: spec1 and spec2 the tow (I think) semantically identical SMT specifications, and spec3 an SMT specification using "=" to model "iff", for which z3 times out.
The default strategy for the UFBV logic is not effective for your problems. Actually, the default strategy solves all of them in less than 1 sec. To force Z3 to use the default strategy, you just need to comment the following lines in your script.
; (set-logic UFBV)
; (set-option :pull-nested-quantifiers true)
; (set-option :macro-finder true)
If the warning messages are bothering you, you can add:
(set-option :print-warning false)
That being said, I will try to address the issues you raised.
Does identifier names affect the behavior of Z3? Yes, they do.
Starting at version 3.0, we started using a total order on Z3 expressions for performing operations such as: sorting the arguments of associative-commutative operators.
This total order is based on the identifier names.
Ironically, this modification was motivated by user feedback. In previous versions, we used an internal ID for performing operations such as sorting, and breaking ties in many different heuristics. However, these IDs are based on the order Z3 creates/deletes expressions, which is based on the order users declare symbols. So, Z3 2.x behavior would be affected by trivial modifications such as removing unused declarations.
Regarding iff, it is not part of SMT-LIB 2.0 standard. In SMT-LIB 2.0, = is used for formulas and terms. To make sure Z3 is fully compliant with the SMT-LIB 2.0 standard, whenever users specify a SMT-LIB supported logic (or soon to be supported such as UFBV), Z3 only "loads" the symbols defined in it. When, a logic is not specified, Z3 assumes the user is using the "Z3 logic" that contains all supported theories in Z3, and many extra aliases such as: iff for Boolean =, if for ite, etc.

Resources