Conversation
|
I agree a time limit is useful, I have implemented a version with a time limit as well for the generalised MUS paper where I could strip the necessary changes from |
|
I seem to have run into an edgecase. import cpmpy as cp
from cpmpy.tools.explain.mus import mus, mus_iis
from cpmpy.transformations.get_variables import get_variables_model
import time
model = cp.Model.from_file("/from/local/path")
print(model)
for var in get_variables_model(model):
print(f"{var}: {var.lb=}, {var.ub=}")
res_iis = mus_iis(model.constraints, solver="gurobi")
print(res_iis)
print(len(res_iis))
res_true = mus(res_iis, solver="exact")
print(res_true)
assert len(res_true) == len(res_iis)This give the following: I've been able to reproduce this bug on other instances that also have disjunctive constraints. |
|
Thanks, there was indeed a bug! Fixed now. Although your test does not exactly show this, it is good to have a real MUS check. My test suite is currently using: # Helper function to verify MUS properties
def is_valid_mus(mus_result, hard=[]):
"""
Verify that a MUS result is:
1. Hard constraints alone are SAT (precondition for valid MUS)
2. UNSAT (the constraints together are unsatisfiable)
3. Minimal (removing any single constraint makes it SAT)
"""
# Check hard constraints alone are SAT
if hard and not cp.Model(hard).solve():
return False, "Hard constraints alone are UNSAT (invalid problem)"
# Check UNSAT
if cp.Model(hard + mus_result).solve():
return False, "MUS is not UNSAT"
# Check minimality
for i in range(len(mus_result)):
subset = mus_result[:i] + mus_result[i+1:]
if not cp.Model(hard + subset).solve():
return False, f"MUS is not minimal: removing constraint {i} still UNSAT"
return True, "Valid MUS"It may be good (in a new PR) to adopt my test suite for |
a2d967d to
f15c6bd
Compare
f15c6bd to
0c8ce0c
Compare
|
I removed the time limit from this PR for simplicity. I will make a separate issue to discuss MUS time limits, which may be closed as no-fix |
|
A little description of how it now works, suppose we have: Then we compute an IIS by introducing one assumption variable per soft constraint. Since Gurobi does not work with assumptions, we essentially compute IIS/MUS on this: Note only the assumption variables are soft constraints, while hard constraints now include the reified soft constraints. One potential performance improvement we could do is that no assumption variable is needed for any soft constraint represented by a single linear constraint, e.g. we could compute IIS on: |
|
As discussed I found some bugs while benchmarking this. There are a couple pb instances which gurobi reports to be satisfiable contradicting other solvers and also given an error when trying to compute the IIS. Interestingly the actual error is not always the same. (This also shows gurobi does an initial check, although it doesn't seem to worsen performance on the instances where it hurts other solvers..) There are also some XCSP instances where I get an error that a variable has not yet been added to the model. This does seem like a bug in the IIS algorithm perhaps. One such instance is added as an attachment. |
|
The "added variables" bug has been fixed, the incorrect solution from gurobi is an unrelated issue for which there is now issue #887 Indeed, gurobi will give an exception if a feasible model is given. Not sure if it's done upfront as an initial check, or if it's something that can be determined as the algorithm goes (if so, then that might explain why performance is not worsened as much). I've tried to put in the above mentioned optimization (#880 (comment)) but it's turning out to be non-trivial. Perhaps ok to leave for now, the results were decent on the PB competition instances, namely slightly worse than deletion-based/base Exact, however much worse on XCSP. I think from that point of view, this PR should be mergeable. From here, then we re-benchmark once we have resolved the tolerance bug, perhaps using expression trees of gurobi which may improve performance further along with other linearization improvements. If there's further interest, we could improve the IIS-based method by generalizing SAT-based MUS extensions such as e.g. model rotation, either by implementing IIS ourselves, or seeing if gurobi's callbacks are flexible enough for this purpose. The available callback evens are here, interestingly, they also have estimates on the min/max size of the IIS, meaning this approach could have different anytime qualities compared to our other MUS methods (discussed a bit in in #881) |
|
From offline discussion
|
|
In a weird twist, our "safe" assumption method is also not clearing that particular unit test. This may be an upstream bug. I've made a forum post. In the meantime, it should be ok to continue with the optimization. |
|
Now added the singleton group optimization, requiring an added private function to get to the Gurobi constraints. The alternative there would be to use getConstr in-between adding soft constraints, which would then require I think it can be reviewed, but if approved, we should probably wait with merging until we resolved the (potentially upstream) issue that I linked in the previous comment. |
d271b8d to
2fe3437
Compare
2fe3437 to
8d69d79
Compare
tias
left a comment
There was a problem hiding this comment.
good; I do have some requests wrt code readability/maintainability
| for con in cons: | ||
| con.setAttr(attr, 1) | ||
|
|
||
| # now add each soft constraint or the assumption representing it |
There was a problem hiding this comment.
I'm confused here... you are already adding constraints on line 549?
or no wait, you only add the reified, not the soft one?
OK, this is too far away from the real logic...
you declare grb_soft on line 541 before the loop, so please build it during that loop... I mean, add s._add_transfo(c) to grb_soft instead of first adding a c and then overwriting it here 10 lines below
There was a problem hiding this comment.
I've now done some effort to avoid this to make the code more readable, see latest version. There is a good reason we first just collect soft constraints without adding them to Gurobi, namely because I first add all hard constraints so I can set their IISConstrForce attribute by iterating over all Gurobi constraints, and then add the soft constraints.
Please see for an updated version which does it the old way below. I'm fine with either, some parts are simpler, others more complex. Note we have to should call model.update() as it can be expensive (but is required before changing constraint attributes). Let me know which version you prefer, the current one or the old one (in which case I can revert).
Lines 535 to 593 in 8b72ba3
1ae2385 to
8b72ba3
Compare
|
@tias I've processed the review, see one outstanding comment. I think you can just say A or B, then approve, then I will merge. |
|
There is now a ticket (https://support.gurobi.com/hc/en-us/requests/116323) for the confirmed IIS bug in Gurobi (wrt forcing constraints into the IIS). May be worth just waiting until it's resolved before we merge/release IIS-based MUS. Even if fixed in gurobi 13.0.2, if we this, it may be worth putting a warning in case someone has gurobi<13.0.2 (or more extremely, upping the bound for gurobi>=13.0.2, but it'd prefer the warning). |
Use computeIIS of Gurobi to compute a MUS. We still need to make an assump model to ensure that each soft constraint, represented as a group of linear constraints, is individually turned off or on in the IIS. I don't think there is a way around it.
It would be very interesting to benchmark it (I did on pigeonhole v pysat, but there is no contest there of course).
Unlike the other MUS functions, it also has time/mem limit. Let me know if either of these should be omitted to be consistent with the other approaches which do not have time/mem limits, or if I should add a separate PR to add such limits to the other algorithms. Obviously, at least a time limit would be a usefull feature, I think. [edit: remove mem_limit for simplicity]