From 1233f0e5f4bb565587bd44989f2d3af0a66327e3 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Fri, 28 Jun 2024 08:26:47 +0900 Subject: [PATCH] Constant constraints (#82) Constraint without any decision variable like following causes segmentation fault in CBC solver with Python-MIP 1.15. https://github.com/Jij-Inc/ommx/actions/runs/9698846012/job/26766373894 ```python x = DecisionVariable.continuous(0) instance = Instance.from_components( decision_variables=[x], objective=x, constraints=[ # 1 >= 0 is always true Linear(terms={}, constant=1) >= 0, x <= 1, ], sense=Instance.MAXIMIZE, ) ``` This has been reported in https://github.com/coin-or/python-mip/issues/213 and some linked issues, and fixed in https://github.com/coin-or/python-mip/pull/237. We use latest commit of Python-MIP https://github.com/coin-or/python-mip/commit/0ccb81115543e737ab74a4f1309891ce5650c8d5 until the next version is released https://github.com/coin-or/python-mip/issues/384 --- python/ommx-python-mip-adapter/pyproject.toml | 2 +- .../tests/test_constant_constraint.py | 40 +++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 python/ommx-python-mip-adapter/tests/test_constant_constraint.py diff --git a/python/ommx-python-mip-adapter/pyproject.toml b/python/ommx-python-mip-adapter/pyproject.toml index e7770879..fa5ce6be 100644 --- a/python/ommx-python-mip-adapter/pyproject.toml +++ b/python/ommx-python-mip-adapter/pyproject.toml @@ -25,7 +25,7 @@ classifiers = [ ] dependencies = [ "ommx >= 0.5.0, < 0.6.0", - "mip", + "mip @ git+https://github.com/coin-or/python-mip.git@0ccb81115543e737ab74a4f1309891ce5650c8d5", ] [project.urls] diff --git a/python/ommx-python-mip-adapter/tests/test_constant_constraint.py b/python/ommx-python-mip-adapter/tests/test_constant_constraint.py new file mode 100644 index 00000000..59f712c8 --- /dev/null +++ b/python/ommx-python-mip-adapter/tests/test_constant_constraint.py @@ -0,0 +1,40 @@ +import ommx_python_mip_adapter as adapter +from ommx.v1 import Instance, DecisionVariable, Linear + + +def test_constant_constraint_feasible(): + x = DecisionVariable.continuous(0) + instance = Instance.from_components( + decision_variables=[x], + objective=x, + constraints=[ + # 1 >= 0 is always true + Linear(terms={}, constant=1) >= 0, + x <= 1, + ], + sense=Instance.MAXIMIZE, + ) + result = adapter.solve(instance) + assert result.HasField("solution") + + solution = result.solution + assert solution.state.entries == {0: 1.0} + assert solution.objective == 1.0 + + assert len(solution.evaluated_constraints) == 2 + + +def test_constant_constraint_infeasible(): + x = DecisionVariable.continuous(0) + instance = Instance.from_components( + decision_variables=[x], + objective=x, + constraints=[ + # -1 >= 0 is always false + Linear(terms={}, constant=-1) >= 0, + x <= 1, + ], + sense=Instance.MAXIMIZE, + ) + result = adapter.solve(instance) + assert result.HasField("infeasible")