gurobi improved MUS#993
Conversation
IgnaceBleukx
left a comment
There was a problem hiding this comment.
I'm still getting to grips with the code, so also some comments on the original implementation.
If I understand correctly, the tool works as follows:
You want to find an MUS for a set of soft CPMpy constraints; there can be hard CPMpy constraints as well.
A single CPMpy constraint may require multiple Gurobi-constraints. So, we introduce a Boolean variable a to represent the group, and add the hard constraints
a -> a_1 /\ a_2 /\ ... a_n
a_1 -> grb_transformed_constraint_1
a_2 -> grb_transformed_constraint_2
...
the soft constraint is then a = 1 ?
There seems to be too much bookkeeping going on. What is the difference between grb_force_cons and grb_hard_cons?
| additional_hard_constraint = assumption.implies(cp.all(soft_con_tf)) | ||
| for tf_con in s.transform(additional_hard_constraint): | ||
| grb_hard_cons.append(s._add_transformed(tf_con)) | ||
| grb_force_cons.append(s._add_transformed(tf_con)) |
There was a problem hiding this comment.
This very much feels like you're doing things backwards.
Wouldn't it make more sense to first do all of this soft-constraints stuff, and just add the mapping you get here to the original hard constraint list?
There was a problem hiding this comment.
I think that would also work
There was a problem hiding this comment.
I'm not sure I get what you mean. The hard constraint machinery comes after this. Only the transformation of the hard constraint to gurobi comes before which is fine to be moved but not sure if that's what you really meant.
|
Mostly correct, in my mind it is simpler: for MIP solvers (or at least for gurobi) the main interface difference with the assumption-based solvers is that instead of only Boolean literals we can directly make a MUS out of supported "soft" constraints. It's a little more general in that sense. The transformation process is identical though, we just make an assumption model (reify There are two optimizations we then added, 1) since we can post a supported soft constraint directly, we don't introduce Maybe some things can be reshuffled, however, note that we should only do model.update once as it's expensive. I believe this is what causes some of the extra bookkeeping. I think the grb_hard_cons was just renamed, but the original instantiation was left in. |
hbierlee
left a comment
There was a problem hiding this comment.
The new optimization looks good to me. Ignace's comments can further simplify the implementation which would be good
| additional_hard_constraint = assumption.implies(cp.all(soft_con_tf)) | ||
| for tf_con in s.transform(additional_hard_constraint): | ||
| grb_hard_cons.append(s._add_transformed(tf_con)) | ||
| grb_force_cons.append(s._add_transformed(tf_con)) |
There was a problem hiding this comment.
I think that would also work
|
Just to add to what @hbierlee already said. We indeed do not "need" the assumption variables for a MIP model. A MIP solver can just compute an IIS for the constraints as they are. The need for the assumption variables (and here it makes more sense to call them indicator variables) is to group gurobi constraints as CPMpy level constraints. So in practice a soft constraint such as There is indeed quite a bit of bookkeeping going on with different type of gurobi constraints which all have different API calls. The Now why do the reified constraints need to be hard constraints? Essentially just using the same indicator variable does not force the grouping by itself. Consider the problem where we have |
|
Also I've gone over the requested changes and also fixed a small bug that was left behind. Feel free to have another look. |
Assuming this example is really a single soft constraint (a little ambiguous due to the &), the soft constraint p >= 1 & p <= 2 is added as hard constraint a-> (p >= 1 & p <= 2) which is then hopefully transformed to (a-> (p >= 1)) & (a-> (p <= 2)). If you mean these are two soft constraints you'd get two a's. However my main point is that this is all no different from assumption-based solvers when you do make_assump_model (and the reified constraints therein also become 'hard constraints ' ; it's just the assumptions which need not all hold) |
|
Yes, you are correct. Though it doesn't really break the example as the transformed constraints will typically still be considered as separate soft constraints internally. So unless a solver supports a conjunctive constraints they should still be hard constraints. But this discussion is more relevant for other solvers/PRs anyway so no need to go to deep into the details. |
This implement the suggested improvement of #979