Use integers for assertion ids - #706
Conversation
kfriedberger
left a comment
There was a problem hiding this comment.
The change is small and might not harm existing implementations. lgtm.
However, I doubt that reuse across solvers will be a use-case.
| @Override | ||
| public void pop() { | ||
| itpProver.pop(); | ||
| lastId = getDelegateAsAbstractProver().getAssertedConstraintIds().size(); |
There was a problem hiding this comment.
no cleanup/deletion of the popped id?
There was a problem hiding this comment.
Thanks for the fast review! I've now added some code to remove outdated ids when a frame is popped
|
I value JavaSMT as a layer that provides direct access to solvers without introducing unnecessary indirections and performance / memory overhead while abstracting over the different solvers. The current implementation of interpolation ids achieves this, and why should there now be additional overhead added? Reusing interpolation ids across contexts or solvers would first require to expose the concrete type in the API. But this would be bad, because That for every new interpolation environment (and even after a pop) old ids are reused and their values become valid again also makes the API much more error prone. Furthermore, I also think it is a bad idea to tell users of JavaSMT that interpolation ids can and should be reused across contexts and solvers. This goes directly against what users of JavaSMT need to know and follow for other objects, in particular formulas, possibly leading to confusion and more accidental reuse of formula objects. Besides these problems, I also don't think there is much value in reusing interpolation ids. And users who want this can easily add such a mapping themselves. |
|
I think that the general idea of this is a good thing, if done right. This means that this layer needs to be cheap and follow consistent rules with clear benefits over what the solvers do. That being said; we need to tackle this problem in a structured way. This is by no means a small change! We already have Id solutions for some solvers (due to inconsistencies in how they handle them). This solutions needs to be able to replace the old ones in my opinion, otherwise we start piling multiple solutions on top of each other, reducing maintainability and increasing our own work in the future. I think what we need is a structured investigation into:
Also, we need to test and evaluate this, making sure it works as intended, and does not cause a big overhead! Also, I agree with Philipp in most points (besides that we should not do this. I am a big fan of solver independent and/or convenience features):
I have little hopes of finding a general solution for this to be honest. We should focus on the actual problem that you try to tackle here; re-building a context, with formulas and prover states. Because that is much easier. I would start this way:
|
| // TODO: do we want a common method to calculate partition B out of the asserted formulas | ||
| // efficiently? We currently have several distinct solutions. | ||
| return itpProver.getInterpolant(formulasOfA); | ||
| return itpProver.getInterpolant(formulaIds); |
There was a problem hiding this comment.
This should be formulaIdsOfA.
| public void pop() { | ||
| itpProver.pop(); | ||
| lastId = getDelegateAsAbstractProver().getAssertedConstraintIds().size(); | ||
| assertionIds = new HashMap<>(Maps.filterKeys(assertionIds, k -> k <= lastId)); |
There was a problem hiding this comment.
This re-builds the map from the filtered view. In general; either use ImmutableMap, as it would be cleaner here and communicate how we use the map, or use the mutability of the map, which would be more efficient.
Since CPAchecker (as an example) has cases in which it holds 20.000+ formulas, meaning we need to copy all of them here in the worst case, we should use a mutable data-structure with O(1) access, addition and deletion, i.e. HashMap.
There was a problem hiding this comment.
Since CPAchecker (as an example) has cases in which it holds 20.000+ formulas
Ok, that's a lot more than I was excpecting 😅
If performance is an issue, we could simply use an ArrayList here. I don't think it necessarily needs to be updated either when frames are popped from the prover. Outdated ids in the list are invalid, and should throw an exception in InterpolatingProverDelegate when used. It just seemed like a good idea to have this check as early as possible
| public @Nullable T addConstraint(BooleanFormula constraint) throws InterruptedException { | ||
| return itpProver.addConstraint(constraint); | ||
| public @Nullable Integer addConstraint(BooleanFormula constraint) throws InterruptedException { | ||
| var newId = ++lastId; |
| private final InterpolatingProverEnvironment<T> itpProver; | ||
|
|
||
| private Map<Integer, T> assertionIds = new HashMap<>(); | ||
| private int lastId = 0; |
There was a problem hiding this comment.
Why not use our UniqueIdGenerator?
There was a problem hiding this comment.
UniqueIdGenerator creates fresh ids for every assertion that is pushed. However, if we want to rebuild solver stack, popped ids need to be reused. Otherwise we may end up with non-consecutive ids:
push(f1) // [id1 = f1]
push(f2) // [id1 = f1, id2 = f2]
pop() // [id1 = f1]
push(f3) // [id1 = f1, id3 = f3]
These "gaps" are skipped when rebuilding the prover:
push(f1) // [id1 = f1]
push(f3) // [id1 = f1, id2 = f3]
As a result, we can no longer use the old id for f3 with the new prover
Why do you think that is the case at all? I would hope that IDs are recycled as rarely as possible, even for the same formula, because this helps to detect bugs when one accidentally reuses old interpolation ids.
I disagree, for the mentioned reasons.
This would still be worse then the current solution, which does not only prevent mixing ids between solvers, but also between mixing them between instances of interpolation environments. Several of your goals actually contradict each other and would be unsolvable together. I would not bother with attempting to do this. |
I don't. That is why i want it investigated.
This view is certainly valid. But are you sure that using IDs from a distinct context lead to errors in all cases immediately?
The goal here is to allow this. But i think it is clear that there are multiple views on this. So we should close this PR for now and discuss this critically before implementing something! |
Hello,
this PR adds integer formula ids for all solvers with interpolation support. Internal solver ids will be mapped to theses integer ids. This helps when rebuilding a prover stack, possible with a different solver, by allowing existing ids to be reused by other provers
The ids returned are simply the position of the asserted formula on the stack. They start at 1 and go up to the size of the assertion stack, while 0 can be used to mark an invalid id. When popping a solver frame, and then pushing new formulas, old ids are reassigned