5
votes

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.

1
The possibility of retraction of individual constraints would block many algorithmic optimizations that belie the efficiency of modern highly optimized solvers. A naive solver would satisfy your requirements at the cost of being terribly inefficient. There are some ideas though on how this can be implemented relatively efficiently.Dmitri Chubarov

1 Answers

6
votes

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?