How to alter assertions in a solver without having to repeatedly create a new solver in z3 Python API - z3

I am currently running into the problem that I create a large SMT formula (that I get from an external source) and I run Solver.check() with it. If the call fails I perform a rewrite on some assertions in the solver using the rewrite(s,f,t) presented here.
Now I am wondering how I can change the assertions in the solver that failed to the new ones obtained after the rewrite. That is assertions that still contain the same function defintions/declarations etc. as the previous solver except with the updated assertions that have been rewritten.
For example, this is how I would do it now. I wonder if there was a better/more efficient way:
solver = z3.Solver()
f = z3.Function(f, z3.Int(),z3.Int())
solver.add(f(3)== 5)
solver.check()
solver.model()
# I don't like the model let's rewrite
new_assertions = solver.assertions().rewrite(...)
new_solver = z3.Solver()
new_solver.add(new_assertions)
new_solver.check()
...

This is what the push, and pop statements are for:
push: Creates a back-tracking point that you can jump back to
pop: Goes back to the last push'ed point
That is, you push before you add your assertions, and when you want to change them pop back to where you were. You can create as many backtracking points as you want.
This style of solver usage is called "incremental" and is described in Section 4.1.4 of the SMTLib document. To simplify programming with your rewrite function, you might want to keep the assertions in a list of your own, so they are easy to manipulate; but that's more or less tangential to the discussion at hand here.

Related

Updating z3 Variable or Linear Solving

I'm new to Z3, so this is likely a silly question. I'm trying to model an execution flow of a program. Doing this through manual z3 calls for now. That said, I end up trying to model something like the following:
x = 1
x += 1
Performing the following commands gives me unsat, and I understand why.
x = z3.Int('x')
s.add(x == 1)
s.add(x == x + 1)
In small scale, it might be reasonable to manually change x == 1 to x == 2. My question is, is there a way to do this in z3 where I don't have to go back and attempt to modify the variables I put into the solver? The equations obviously would get much harrier than just +1, and attempting to work through that logic manually seems error prone and sloppy.
EDIT: After adjusting my program to use SSA as suggested, it works very easily now. I did opt to keep multiple versions of the variable, but that didn't turn into too much extra work.
You can rename variables such that they are in SSA form (https://en.wikipedia.org/wiki/Static_single_assignment_form).
Also, you probably don't need to introduce names for intermediate expressions. The only Z3 variables should be program inputs or things like that.

Use Z3 to determine difficulty of quantifier elimination for BV-queries

I'm currently using the Z3 C++ API for solving queries over bitvectors. Some queries may contain an existential quantifier at the top level.
Often times the quantifier elimination is simple and can be performed by Z3 quickly. However, in those cases where the quantifier elimination falls back to enumerating thousands of feasible solutions I'd like to abort this tactic and handle the query myself in some other way.
I've tried wrapping the 'qe'-tactic with a 'try-for'-tactic, hoping that if quantifier elimination fails (in say 100ms) I'd know that I'd better handle the query in some other way. Unfortunately, the 'try-for'-tactic fails to cancel the quantifier elimination (for any time bound).
In an old post a similar issue is discussed and the 'smt' tactic is being blamed for being not responsive. Does the same reasoning apply to the 'qe' tactic? The same post indicates that 'future' versions should be more responsive though. Is there any way or heuristic to determine whether quantifier elimination would take long (besides running the solver in a separate thread and killing it on timeout)?
I've attached a minimal example so you can try it yourselves:
z3::context ctx;
z3::expr bv1 = ctx.bv_const("bv1", 10);
z3::expr bv2 = ctx.bv_const("bv2", 10);
z3::goal goal(ctx);
goal.add(z3::exists(bv1, bv1 != bv2));
z3::tactic t = z3::try_for(z3::tactic(ctx,"qe"), 100);
auto res = t.apply(goal);
std::cout << res << std::endl;
Thanks!
The timeout cancellations have to be checked periodically by the tactic that is running.
We basically have to ensure that the code checks for cancellations and does not descend into a long running loop without checking. You can probably identify the code segment that fails to check for cancellation by running your code in a debugger, break and then determine which procedures it is in. Then file a bug on GitHub to have cancellation flag checked in the place that will help.
Overall, the quantifier elimination tactic is currently fairly simplistic when it comes to bit-vectors so it would be better to avoid qe for all but simple cases.

Measure and bound time spent in arithmetic sub-solvers

Q1: Is it possible to query the times Z3 spent in different sub-solvers?
Calling (get-info :all-statistics) gives the overall run time of Z3, but I would like to break it down into individual sub-solvers.
I am particularly interested in the time spent in arithmetic-related sub-solver, more precisely, in those that give rise to the statistics grobner and nonlinear-horner.
Q2: Furthermore, is it possible to put a timeout on sub-solver?
I could imagine something like defining a timeout per check-sat and sub-solver that bounds the time Z3 can spent in that sub-solver. Z3 would repeatedly call n different sub-solvers, and if the time bound of one of them is reached it continues, but only uses the remaining n-1 sub-solvers.
I read the tactics tutorial and got the impression that this might actually be possible by something along the lines of
(repeat
(par-or
(try-for <arithmetic-solvers> 500)
<all-other-solvers>))
but I couldn't figure out which solvers to use.
For Q1: No, you'd have to add your own timers on that and I would expect this to be nontrivial as it's not clear what exactly should and shouldn't be counted.
Q2: Yes, you can build your own custom strategies/tactics. Note that par-or means parallel or, i.e., it will try to run the provided tactics in parallel.
Not everything we call a "solver" has it's own tactic, so this might require some fiddling. Note that "solver" in this context is not necessarily the same as the Z3 C++ object called "solver". Some "solvers" are also integral parts of the SMT kernel.

What are the benefits of incremental solving?

If "pop" completely destroys context (i.e., learned lemmas) in
incremental constraint solving what is the purpose of using "stack
mode"?
Rationale: I imagine that if I have just 1 constraint (several
conjuncts) it would be preferable to make a single query as opposed
to stacking the conjuncts in separate frames onto the stack. If I
have more than 1 constraint and decide to use incremental solving with
stack, then I would need to (make at least one) pop after querying one
constraint and that would presumably "destroy learned lemmas". So,
what is the advantage of using incremental solving (with stacks)?
What "destroying learned lemmas in pop" really means?
Observation: My experiments indicate this is really beneficial but I
find the indication (see smt formulas, there are in total of 500 queries, incremental solving finished in 0.01sec, while noninc. solving finished in 16sec. ) contradictory with
this observation.
When push/pop commands are present, Z3 essentially switches to a completely different solver, because it detects that it needs support for incrementality. The incremental solver is usually (but not always) slower on non-incremental queries, but in turn can take advantage of incrementality. See also here: Incremental calls to Z3 on UFBV with and without push calls, Soft/Hard constraints in Z3.
Destroying learned lemmas means that those lemmas that are not valid after pop will be removed. They become invalid because they depend on some constraints within the innermost scope and therefore all the lemmas that follow from them are now invalid. There may be some exceptions, but usually Z3 will try to destroy only invalidated lemmas.
Sorry if there was any confusion that may have arisen from a previous post (Efficiency of constraint strengthening in SMT solvers). That post was not completely clear about which lemmas are removed and has since been updated.

Z3, solver: push an empty level on assertion-stack, as defined in smtlib2: push(n=1)

currently I am using Z3 with Python.
I want to create an assertion-stack, where I can push a level and pop it later.
It should work exactly like any other "stack" with push- and pop-operations.
Therefore the SMTLIB2-standard defines two functions "push(n)" and "pop(n)" with optional numbers n. In my case n would always be 1.
But there seems to be some strange behaviour in Z3.
Why does following code result in "index out of bounds"?
s = Solver()
s.push() # expected: one new level on the stack, reality: emtpy stack
s.pop(1) # expected: stack is empty, reality: exception (index out of bounds)
If I add an assertions, Z3 works as expected.
s = Solver()
s.push()
s.add(True) # now there is one level on the stack,
s.pop(1) # pop is successful
Even this works correct:
s = Solver()
s.add(True)
s.push() # now there is one level on the stack,
s.pop(1) # pop is successful
The problem is, that I do not know, how many levels and how many assertions are created in my program. It is possible, that there is no assertion at all and only one level. Then the program would crash (or catch the exception). A workaround would be adding some simple formula like "True" always as a first step, but this seems ugly.
Is this a bug in Z3 or is this behaviour correct?
This bug has been fixed in the unstable (working-in-progress) branch.
It will be available in the next official release. In the meantime, here are some instructions on how to compile the unstable branch. The fix is also available in the nightly builds available at codeplex.

Resources