Closed kaibir closed 8 months ago
After some research, the problem is already stated in model.py, line 334-338
https://github.com/coin-or/python-mip/blob/master/mip/model.py#L334-L338
# TODO: some tests use empty linear constraints, which ideally should not happen
# if len(lin_expr) == 0:
# raise mip.InvalidLinExpr(
# "An empty linear expression cannot be used as a constraint."
# )
In our applications, we often auto-generate different constraints based on the user's inputs and selections. Might happen, that some of the settings are contradictory, leading to something like 1 <= 0. Gurobipy supports this case well.
Would be fantastic, if mip supports this too.
Finally,
import mip
m = mip.Model("Crash", solver_name="cbc")
x = m.add_var(name="x")
y = m.add_var(name="y")
m.add_constr(x + y <= 5, name="c1")
m.add_constr(2*x + y <= 6, name="c2")
m.add_constr(mip.xsum([]) <= 1, name="c_crash") # useless constraint
m.objective = mip.maximize(x + 2*y)
m.optimize()
for c in m.constrs:
print(c.name)
not adding those leads to a crash of underlying CBC.
Thread 0 Crashed:: Dispatch queue: com.apple.main-thread 0 libsystem_kernel.dylib 0x7ff807648112 __pthread_kill + 10 1 libsystem_pthread.dylib 0x7ff80767e233 pthread_kill + 263 2 libsystem_c.dylib 0x7ff8075cad10 abort + 123 3 cbc-c-darwin-x86-64.dylib 0x113ef6f6d Cbc_getRowName + 189
I also just encountered this bug. I find the current behaviour of MIP very counter-intuitive. Also I find there are real usecases that rely on the intuitiv behaviour of xsum([]) = 0. E.g. In our application, we have a network flow and want to enforce that flow goes through certain vertices. In this scenario, a node which has no incoming-edges is a symptom of an infeasible input. But the behaviour of MIP leads to a solution where this node has no flow.
@sebheger any idea when your fix might be deployed? I've just discovered this bug the hard way
On top of the behavior described above, there are more troubling cases.
If you add a constraint with 0.0
coefficients, they are added to the solver (CBC) at first, but removed during preprocessing, ie when you solve the model. So the number of constraints before/after solve might be different and accessing, eg shadow price, might then lead to a crash even if printing all constraint names prior to solve does not.
I think that this is solver-dependent, ie it might occur only with CBC.
Our current work-around is to add a wrapper add_constraint
method that checks for empty constraints (no variables, or all-zero coefficients) to avoid this. It's also a nice opportunity to check for unique constraint names etc.
@sebheger any idea when your fix might be deployed? I've just discovered this bug the hard way
I will see what I can do. Actually, my fix does not fully solve the problem. E.g. it will still crash when there is no variable added before. But maybe I will pragmatically release it, as it fixes 95%, which is better than 0%.
Describe the bug I have a model building constraints from a dictionary of type dict[str, list[mip.Var]]. If I now have the case, that one of these lists is empty, the constraint is not added to the model. In such a case when I want to add a constraint 1<= xsum(list) this one should lead to infeasibility, but as it is not added to the model it does not fail.
Using Gurobi to build and solve the model I get the expected infeasibility.
To Reproduce With MIP and CBC
Writing this model in a file, Constr_two is missing.
To Reproduce With Gurobi
Writing model in file.
Expected behavior I would expect a constraint with xsum([]) on lhs or rhs to be handled as something like zero, e.g. a <= xsum([]) should be equivalent to a <= 0.
Or in the descripted case: 1 <= xsum([]) ---> 1 <= 0 should lead to infeasibility.
Desktop (please complete the following information):
Additional context xsum of an empty list returns an "empty" linear expression with const = 0.0, but I am moreover not allowed to add a constraint of form 1 <= 0 because "A boolean (true/false) cannot be used as a constraint."