Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add_constr returns broken object when adding constraint without variables #364

Open
mjdv opened this issue Jan 13, 2024 · 3 comments
Open

Comments

@mjdv
Copy link

mjdv commented Jan 13, 2024

Describe the bug
You can pass a trivial constraint, like xsum([]) == 0 or xsum([]) == 1, to a Model object's add_constr function. This returns an object which is broken in some way (using it causes errors or even segmentation faults).

To Reproduce
The simplest code that produces the bug is

from mip import *

model = Model()
print(model.add_constr(xsum([])) == 0)

This produces an error:

Traceback (most recent call last):
  File "/home/mees/minimal_example.py", line 4, in <module>
    print(model.add_constr(xsum([])) == 0)
  File "/home/mees/.local/lib/python3.10/site-packages/mip/model.py", line 339, in add_constr
    return self.constrs.add(lin_expr, name, priority)
  File "/home/mees/.local/lib/python3.10/site-packages/mip/lists.py", line 153, in add
    self.__model.solver.add_constr(lin_expr, name)
  File "/home/mees/.local/lib/python3.10/site-packages/mip/cbc.py", line 1429, in add_constr
    cbclib.Cbc_addRow(mp, namestr, numnz, self.iidx, self.dvec, sense, rhs)
TypeError: initializer for ctype 'char' must be a bytes of length 1, not bytes

Other actions on the object also cause errors. For instance, the following code also fails:

from mip import *

model = Model()
constraint = model.add_constr(xsum([]) == 0)
model.remove(constraint)

In this case, the error is a segmentation fault, although in context of a larger program I have also seen other errors.

Expected behavior
Ideally, these dummy constraints should just be accepted and added to the model, be printable and removable, etc.

If this is not possible due to the nature of the underlying solver(s), the add_constr should give a useful error when such a constraint is added to it, rather than return a broken object.

Desktop (please complete the following information):

  • Operating System, version: Ubuntu 22.04.3 LTS
  • Python version: Python 3.10.12
  • Python-MIP version (we recommend you to test with the latest version): 1.15.0

Additional context
Of course it is unusual to add dummy constraints like the one I did above. In my use case, I am modelling a dynamic logical problem, and regularly want to check for consistency of the full model. In particular, I have lists of binary variables that may grow or shrink during the computation, and regularly want to check if it is still consistent for there to be a variable in some list with value 1. Occasionally these lists are empty, which results in a constraint like xsum([]) == 1. It would be convenient if that did not present a special case.

@rschwarz
Copy link
Contributor

This is a known problem, see, eg, #184.

The problem is that the solvers (here, CBC?) don't actually add a row to their internal representation, which means that python-mip and the solver no longer have a consistent understanding of the number of constraints, leading to out-of-bounds indexing.

It can be even worse, when you add constraints that are empty, not only after simple preprocessing is applied (eg 0*x == 0), because it means that the constraint is dropped only during the actual solve call.

@flipz357
Copy link

@rschwarz But then, wouldn't this suggest a solution of python-mip simply issuing a warning that there's an empty constraint and it's gonna be removed, then it removes these constraints, and then there will be no inconsistency any more between the CBC(?) and python-mip?

@rschwarz
Copy link
Contributor

Yes, I think this is done in the PR #237.

One could go further and also check for expressions that have some terms in them, but with coefficients 0.0, which might be removed later by the solver. We have sometimes checked for this manually before adding constraints, but it turns out that this can take significant time, so it's not always a good default behaviour.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants