ommx-python-mip-adapter 1.9.5__tar.gz → 2.0.0b2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {ommx_python_mip_adapter-1.9.5 → ommx_python_mip_adapter-2.0.0b2}/PKG-INFO +2 -2
- {ommx_python_mip_adapter-1.9.5 → ommx_python_mip_adapter-2.0.0b2}/ommx_python_mip_adapter/adapter.py +25 -25
- {ommx_python_mip_adapter-1.9.5 → ommx_python_mip_adapter-2.0.0b2}/ommx_python_mip_adapter/python_mip_to_ommx.py +24 -21
- {ommx_python_mip_adapter-1.9.5 → ommx_python_mip_adapter-2.0.0b2}/ommx_python_mip_adapter.egg-info/PKG-INFO +2 -2
- ommx_python_mip_adapter-2.0.0b2/ommx_python_mip_adapter.egg-info/requires.txt +2 -0
- {ommx_python_mip_adapter-1.9.5 → ommx_python_mip_adapter-2.0.0b2}/pyproject.toml +2 -2
- ommx_python_mip_adapter-2.0.0b2/tests/test_adapter.py +41 -0
- {ommx_python_mip_adapter-1.9.5 → ommx_python_mip_adapter-2.0.0b2}/tests/test_integration.py +2 -3
- {ommx_python_mip_adapter-1.9.5 → ommx_python_mip_adapter-2.0.0b2}/tests/test_model_to_instance.py +60 -81
- ommx_python_mip_adapter-1.9.5/ommx_python_mip_adapter.egg-info/requires.txt +0 -2
- ommx_python_mip_adapter-1.9.5/tests/test_adapter.py +0 -90
- {ommx_python_mip_adapter-1.9.5 → ommx_python_mip_adapter-2.0.0b2}/README.md +0 -0
- {ommx_python_mip_adapter-1.9.5 → ommx_python_mip_adapter-2.0.0b2}/ommx_python_mip_adapter/__init__.py +0 -0
- {ommx_python_mip_adapter-1.9.5 → ommx_python_mip_adapter-2.0.0b2}/ommx_python_mip_adapter/exception.py +0 -0
- {ommx_python_mip_adapter-1.9.5 → ommx_python_mip_adapter-2.0.0b2}/ommx_python_mip_adapter.egg-info/SOURCES.txt +0 -0
- {ommx_python_mip_adapter-1.9.5 → ommx_python_mip_adapter-2.0.0b2}/ommx_python_mip_adapter.egg-info/dependency_links.txt +0 -0
- {ommx_python_mip_adapter-1.9.5 → ommx_python_mip_adapter-2.0.0b2}/ommx_python_mip_adapter.egg-info/top_level.txt +0 -0
- {ommx_python_mip_adapter-1.9.5 → ommx_python_mip_adapter-2.0.0b2}/setup.cfg +0 -0
- {ommx_python_mip_adapter-1.9.5 → ommx_python_mip_adapter-2.0.0b2}/tests/test_constant_constraint.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ommx_python_mip_adapter
|
|
3
|
-
Version:
|
|
3
|
+
Version: 2.0.0b2
|
|
4
4
|
Summary: An adapter for the Python-MIP from/to OMMX.
|
|
5
5
|
Author-email: "Jij Inc." <info@j-ij.com>
|
|
6
6
|
Project-URL: Repository, https://github.com/Jij-Inc/ommx
|
|
@@ -15,7 +15,7 @@ Classifier: License :: OSI Approved :: Apache Software License
|
|
|
15
15
|
Classifier: License :: OSI Approved :: MIT License
|
|
16
16
|
Requires-Python: >=3.9
|
|
17
17
|
Description-Content-Type: text/markdown
|
|
18
|
-
Requires-Dist: ommx<
|
|
18
|
+
Requires-Dist: ommx<3.0.0,>=2.0.0b2
|
|
19
19
|
Requires-Dist: mip<2.0.0,>=1.15.0
|
|
20
20
|
|
|
21
21
|
OMMX adapter for Python-MIP
|
{ommx_python_mip_adapter-1.9.5 → ommx_python_mip_adapter-2.0.0b2}/ommx_python_mip_adapter/adapter.py
RENAMED
|
@@ -5,9 +5,7 @@ from typing import Optional
|
|
|
5
5
|
import mip
|
|
6
6
|
|
|
7
7
|
from ommx.adapter import SolverAdapter, InfeasibleDetected, UnboundedDetected
|
|
8
|
-
from ommx.v1 import Instance, Constraint, DecisionVariable, Solution, State,
|
|
9
|
-
from ommx.v1.function_pb2 import Function
|
|
10
|
-
from ommx.v1.solution_pb2 import Relaxation
|
|
8
|
+
from ommx.v1 import Instance, Constraint, DecisionVariable, Solution, State, Function
|
|
11
9
|
|
|
12
10
|
from .exception import OMMXPythonMIPAdapterError
|
|
13
11
|
|
|
@@ -80,7 +78,6 @@ class OMMXPythonMIPAdapter(SolverAdapter):
|
|
|
80
78
|
.. doctest::
|
|
81
79
|
|
|
82
80
|
>>> from ommx.v1 import Instance, DecisionVariable
|
|
83
|
-
>>> from ommx.v1.solution_pb2 import Optimality
|
|
84
81
|
>>> from ommx_python_mip_adapter import OMMXPythonMIPAdapter
|
|
85
82
|
|
|
86
83
|
>>> p = [10, 13, 18, 32, 7, 15]
|
|
@@ -103,7 +100,7 @@ class OMMXPythonMIPAdapter(SolverAdapter):
|
|
|
103
100
|
[(0, 1.0), (1, 0.0), (2, 0.0), (3, 1.0), (4, 0.0), (5, 0.0)]
|
|
104
101
|
>>> solution.feasible
|
|
105
102
|
True
|
|
106
|
-
>>> assert solution.optimality ==
|
|
103
|
+
>>> assert solution.optimality == Solution.OPTIMAL
|
|
107
104
|
|
|
108
105
|
p[0] + p[3] = 42
|
|
109
106
|
w[0] + w[3] = 46 <= 47
|
|
@@ -256,10 +253,10 @@ class OMMXPythonMIPAdapter(SolverAdapter):
|
|
|
256
253
|
constraint.dual_variable = dual_variables[id]
|
|
257
254
|
|
|
258
255
|
if data.status == mip.OptimizationStatus.OPTIMAL:
|
|
259
|
-
solution.raw.optimality =
|
|
256
|
+
solution.raw.optimality = Solution.OPTIMAL
|
|
260
257
|
|
|
261
258
|
if self._relax:
|
|
262
|
-
solution.raw.relaxation =
|
|
259
|
+
solution.raw.relaxation = Solution.LP_RELAXED
|
|
263
260
|
return solution
|
|
264
261
|
|
|
265
262
|
def decode_to_state(self, data: mip.Model) -> State:
|
|
@@ -302,13 +299,13 @@ class OMMXPythonMIPAdapter(SolverAdapter):
|
|
|
302
299
|
|
|
303
300
|
return State(
|
|
304
301
|
entries={
|
|
305
|
-
|
|
306
|
-
for var in self.instance.raw.decision_variables
|
|
302
|
+
var_id: data.var_by_name(str(var_id)).x # type: ignore
|
|
303
|
+
for var_id, var in self.instance.raw.decision_variables.items()
|
|
307
304
|
}
|
|
308
305
|
)
|
|
309
306
|
|
|
310
307
|
def _set_decision_variables(self):
|
|
311
|
-
for var in self.instance.raw.decision_variables:
|
|
308
|
+
for var_id, var in self.instance.raw.decision_variables.items():
|
|
312
309
|
if var.kind == DecisionVariable.BINARY:
|
|
313
310
|
self.model.add_var(
|
|
314
311
|
name=str(var.id),
|
|
@@ -341,26 +338,29 @@ class OMMXPythonMIPAdapter(SolverAdapter):
|
|
|
341
338
|
"""
|
|
342
339
|
Translate ommx.v1.Function to `mip.LinExpr` or `float`.
|
|
343
340
|
"""
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
341
|
+
degree = f.degree()
|
|
342
|
+
constant = f.constant_term
|
|
343
|
+
if degree > 1:
|
|
344
|
+
raise OMMXPythonMIPAdapterError(
|
|
345
|
+
f"Function with degree {degree} is not supported. "
|
|
346
|
+
"Only linear (degree 1) and constant (degree 0) functions are supported."
|
|
347
|
+
)
|
|
348
|
+
if degree == 0:
|
|
349
|
+
return mip.LinExpr(const=constant) # type: ignore
|
|
350
|
+
assert degree == 1
|
|
351
|
+
return (
|
|
352
|
+
mip.xsum(
|
|
353
|
+
coeff * self.model.vars[str(var_id)] # type: ignore
|
|
354
|
+
for var_id, coeff in f.linear_terms.items()
|
|
355
|
+
)
|
|
356
|
+
+ constant
|
|
357
357
|
)
|
|
358
358
|
|
|
359
359
|
def _set_objective(self):
|
|
360
|
-
self.model.objective = self._as_lin_expr(self.instance.
|
|
360
|
+
self.model.objective = self._as_lin_expr(self.instance.objective)
|
|
361
361
|
|
|
362
362
|
def _set_constraints(self):
|
|
363
|
-
for constraint in self.instance.
|
|
363
|
+
for constraint in self.instance.get_constraints():
|
|
364
364
|
lin_expr = self._as_lin_expr(constraint.function)
|
|
365
365
|
if constraint.equality == Constraint.EQUAL_TO_ZERO:
|
|
366
366
|
constr_expr = lin_expr == 0
|
|
@@ -4,10 +4,7 @@ from typing import final
|
|
|
4
4
|
import mip
|
|
5
5
|
|
|
6
6
|
from mip.exceptions import ParameterNotAvailable
|
|
7
|
-
from ommx.v1
|
|
8
|
-
from ommx.v1.function_pb2 import Function
|
|
9
|
-
from ommx.v1.linear_pb2 import Linear
|
|
10
|
-
from ommx.v1 import Instance, DecisionVariable
|
|
7
|
+
from ommx.v1 import Instance, DecisionVariable, Constraint, Function, Linear
|
|
11
8
|
|
|
12
9
|
from .exception import OMMXPythonMIPAdapterError
|
|
13
10
|
|
|
@@ -27,35 +24,41 @@ class OMMXInstanceBuilder:
|
|
|
27
24
|
decision_variables = []
|
|
28
25
|
for var in self.model.vars:
|
|
29
26
|
if var.var_type == mip.BINARY:
|
|
30
|
-
|
|
27
|
+
decision_variables.append(
|
|
28
|
+
DecisionVariable.binary(var.idx, name=var.name)
|
|
29
|
+
)
|
|
31
30
|
elif var.var_type == mip.INTEGER:
|
|
32
|
-
|
|
31
|
+
decision_variables.append(
|
|
32
|
+
DecisionVariable.integer(
|
|
33
|
+
var.idx, lower=var.lb, upper=var.ub, name=var.name
|
|
34
|
+
)
|
|
35
|
+
)
|
|
33
36
|
elif var.var_type == mip.CONTINUOUS:
|
|
34
|
-
|
|
37
|
+
decision_variables.append(
|
|
38
|
+
DecisionVariable.continuous(
|
|
39
|
+
var.idx, lower=var.lb, upper=var.ub, name=var.name
|
|
40
|
+
)
|
|
41
|
+
)
|
|
35
42
|
else:
|
|
36
43
|
raise OMMXPythonMIPAdapterError(
|
|
37
44
|
f"Not supported variable type. "
|
|
38
45
|
f"idx: {var.idx} name: {var.name}, type: {var.var_type}"
|
|
39
46
|
)
|
|
40
|
-
decision_variables.append(
|
|
41
|
-
DecisionVariable.of_type(
|
|
42
|
-
kind, var.idx, lower=var.lb, upper=var.ub, name=var.name
|
|
43
|
-
)
|
|
44
|
-
)
|
|
45
47
|
return decision_variables
|
|
46
48
|
|
|
47
49
|
def as_ommx_function(self, lin_expr: mip.LinExpr) -> Function:
|
|
48
|
-
terms =
|
|
49
|
-
|
|
50
|
+
terms = {
|
|
51
|
+
var.idx: coefficient # type: ignore
|
|
50
52
|
for var, coefficient in lin_expr.expr.items()
|
|
51
|
-
|
|
53
|
+
}
|
|
52
54
|
constant: float = lin_expr.const # type: ignore
|
|
53
55
|
|
|
54
56
|
# If the terms are empty, the function is a constant.
|
|
55
57
|
if len(terms) == 0:
|
|
56
|
-
return Function(constant
|
|
58
|
+
return Function(constant)
|
|
57
59
|
else:
|
|
58
|
-
|
|
60
|
+
linear = Linear(terms=terms, constant=constant) # type: ignore
|
|
61
|
+
return Function(linear)
|
|
59
62
|
|
|
60
63
|
def objective(self) -> Function:
|
|
61
64
|
# In Python-MIP, it is allowed not to set the objective function.
|
|
@@ -65,7 +68,7 @@ class OMMXInstanceBuilder:
|
|
|
65
68
|
try:
|
|
66
69
|
objective = self.model.objective
|
|
67
70
|
except ParameterNotAvailable:
|
|
68
|
-
return Function(
|
|
71
|
+
return Function(0.0)
|
|
69
72
|
|
|
70
73
|
return self.as_ommx_function(objective)
|
|
71
74
|
|
|
@@ -80,15 +83,15 @@ class OMMXInstanceBuilder:
|
|
|
80
83
|
if lin_expr.sense == "=":
|
|
81
84
|
constraint = Constraint(
|
|
82
85
|
id=id,
|
|
83
|
-
equality=Equality.EQUALITY_EQUAL_TO_ZERO,
|
|
84
86
|
function=self.as_ommx_function(lin_expr),
|
|
87
|
+
equality=Constraint.EQUAL_TO_ZERO,
|
|
85
88
|
name=name,
|
|
86
89
|
)
|
|
87
90
|
elif lin_expr.sense == "<":
|
|
88
91
|
constraint = Constraint(
|
|
89
92
|
id=id,
|
|
90
|
-
equality=Equality.EQUALITY_LESS_THAN_OR_EQUAL_TO_ZERO,
|
|
91
93
|
function=self.as_ommx_function(lin_expr),
|
|
94
|
+
equality=Constraint.LESS_THAN_OR_EQUAL_TO_ZERO,
|
|
92
95
|
name=name,
|
|
93
96
|
)
|
|
94
97
|
elif lin_expr.sense == ">":
|
|
@@ -96,8 +99,8 @@ class OMMXInstanceBuilder:
|
|
|
96
99
|
# So multiply the linear expression by -1.
|
|
97
100
|
constraint = Constraint(
|
|
98
101
|
id=id,
|
|
99
|
-
equality=Equality.EQUALITY_LESS_THAN_OR_EQUAL_TO_ZERO,
|
|
100
102
|
function=self.as_ommx_function(-lin_expr),
|
|
103
|
+
equality=Constraint.LESS_THAN_OR_EQUAL_TO_ZERO,
|
|
101
104
|
name=name,
|
|
102
105
|
)
|
|
103
106
|
else:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ommx_python_mip_adapter
|
|
3
|
-
Version:
|
|
3
|
+
Version: 2.0.0b2
|
|
4
4
|
Summary: An adapter for the Python-MIP from/to OMMX.
|
|
5
5
|
Author-email: "Jij Inc." <info@j-ij.com>
|
|
6
6
|
Project-URL: Repository, https://github.com/Jij-Inc/ommx
|
|
@@ -15,7 +15,7 @@ Classifier: License :: OSI Approved :: Apache Software License
|
|
|
15
15
|
Classifier: License :: OSI Approved :: MIT License
|
|
16
16
|
Requires-Python: >=3.9
|
|
17
17
|
Description-Content-Type: text/markdown
|
|
18
|
-
Requires-Dist: ommx<
|
|
18
|
+
Requires-Dist: ommx<3.0.0,>=2.0.0b2
|
|
19
19
|
Requires-Dist: mip<2.0.0,>=1.15.0
|
|
20
20
|
|
|
21
21
|
OMMX adapter for Python-MIP
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "ommx_python_mip_adapter"
|
|
7
|
-
version = "
|
|
7
|
+
version = "2.0.0b2"
|
|
8
8
|
|
|
9
9
|
description = "An adapter for the Python-MIP from/to OMMX."
|
|
10
10
|
authors = [{ name = "Jij Inc.", email = "info@j-ij.com" }]
|
|
@@ -27,7 +27,7 @@ classifiers = [
|
|
|
27
27
|
# To support Python 3.12 and Apple silicon systems, this requires latest version of Python-MIP (will be 1.16.0?),
|
|
28
28
|
# which does not release yet. https://github.com/coin-or/python-mip/issues/384
|
|
29
29
|
# This project uses dev-dependency to use the latest version of Python-MIP not to include pre-release version in the release.
|
|
30
|
-
dependencies = ["ommx >=
|
|
30
|
+
dependencies = ["ommx >= 2.0.0b2, < 3.0.0", "mip >= 1.15.0, < 2.0.0"]
|
|
31
31
|
|
|
32
32
|
[project.urls]
|
|
33
33
|
Repository = "https://github.com/Jij-Inc/ommx"
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
from ommx.v1 import Instance, DecisionVariable
|
|
3
|
+
from ommx_python_mip_adapter import OMMXPythonMIPAdapter
|
|
4
|
+
from ommx_python_mip_adapter.exception import OMMXPythonMIPAdapterError
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def test_error_nonlinear_objective():
|
|
8
|
+
# Objective function: 2.3 * x * x (variable ID should match)
|
|
9
|
+
x = DecisionVariable.continuous(0)
|
|
10
|
+
ommx_instance = Instance.from_components(
|
|
11
|
+
decision_variables=[x],
|
|
12
|
+
objective=2.3 * x * x,
|
|
13
|
+
constraints=[],
|
|
14
|
+
sense=Instance.MINIMIZE,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
with pytest.raises(OMMXPythonMIPAdapterError) as e:
|
|
18
|
+
OMMXPythonMIPAdapter(ommx_instance)
|
|
19
|
+
assert (
|
|
20
|
+
"Function with degree 2 is not supported. Only linear (degree 1) and constant (degree 0) functions are supported."
|
|
21
|
+
in str(e.value)
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def test_error_nonlinear_constraint():
|
|
26
|
+
# Objective function: 0
|
|
27
|
+
# Constraint: 2.3 * x * x = 0
|
|
28
|
+
x = DecisionVariable.continuous(1)
|
|
29
|
+
ommx_instance = Instance.from_components(
|
|
30
|
+
decision_variables=[x],
|
|
31
|
+
objective=0.0,
|
|
32
|
+
constraints=[(2.3 * x * x == 0).set_id(0)],
|
|
33
|
+
sense=Instance.MINIMIZE,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
with pytest.raises(OMMXPythonMIPAdapterError) as e:
|
|
37
|
+
OMMXPythonMIPAdapter(ommx_instance)
|
|
38
|
+
assert (
|
|
39
|
+
"Function with degree 2 is not supported. Only linear (degree 1) and constant (degree 0) functions are supported."
|
|
40
|
+
in str(e.value)
|
|
41
|
+
)
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import pytest
|
|
2
2
|
|
|
3
|
-
from ommx.v1 import Instance, DecisionVariable
|
|
4
|
-
from ommx.v1.solution_pb2 import Optimality
|
|
3
|
+
from ommx.v1 import Instance, DecisionVariable, Solution
|
|
5
4
|
from ommx.testing import SingleFeasibleLPGenerator, DataType
|
|
6
5
|
|
|
7
6
|
from ommx_python_mip_adapter import OMMXPythonMIPAdapter
|
|
@@ -74,4 +73,4 @@ def test_solution_optimality():
|
|
|
74
73
|
)
|
|
75
74
|
|
|
76
75
|
solution = OMMXPythonMIPAdapter.solve(ommx_instance)
|
|
77
|
-
assert solution.optimality ==
|
|
76
|
+
assert solution.optimality == Solution.OPTIMAL
|
{ommx_python_mip_adapter-1.9.5 → ommx_python_mip_adapter-2.0.0b2}/tests/test_model_to_instance.py
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import mip
|
|
2
2
|
|
|
3
|
-
from ommx.v1 import
|
|
3
|
+
from ommx.v1 import DecisionVariable, Instance, Constraint
|
|
4
4
|
|
|
5
5
|
from ommx_python_mip_adapter import model_to_instance
|
|
6
6
|
|
|
@@ -43,25 +43,25 @@ def test_milp():
|
|
|
43
43
|
model.add_constr(-7 * x1 + 8 * x3 - 9 <= 0) # type: ignore
|
|
44
44
|
model.add_constr(10 * x2 - 11 * x3 + 12 >= 0) # type: ignore
|
|
45
45
|
|
|
46
|
-
ommx_instance = model_to_instance(model)
|
|
46
|
+
ommx_instance = model_to_instance(model)
|
|
47
47
|
|
|
48
48
|
assert ommx_instance.sense == Instance.MINIMIZE
|
|
49
49
|
|
|
50
50
|
# Check the decision variables
|
|
51
|
-
assert len(ommx_instance.
|
|
52
|
-
decision_variables_x1 = ommx_instance.
|
|
51
|
+
assert len(ommx_instance.get_decision_variables()) == 3
|
|
52
|
+
decision_variables_x1 = ommx_instance.get_decision_variable(0)
|
|
53
53
|
assert decision_variables_x1.id == 0
|
|
54
54
|
assert decision_variables_x1.kind == DecisionVariable.CONTINUOUS
|
|
55
55
|
assert decision_variables_x1.bound.lower == CONTINUOUS_LOWER_BOUND
|
|
56
56
|
assert decision_variables_x1.bound.upper == CONTINUOUS_UPPER_BOUND
|
|
57
57
|
assert decision_variables_x1.name == "1"
|
|
58
|
-
decision_variables_x2 = ommx_instance.
|
|
58
|
+
decision_variables_x2 = ommx_instance.get_decision_variable(1)
|
|
59
59
|
assert decision_variables_x2.id == 1
|
|
60
60
|
assert decision_variables_x2.kind == DecisionVariable.INTEGER
|
|
61
61
|
assert decision_variables_x2.bound.lower == INTEGER_LOWER_BOUND
|
|
62
62
|
assert decision_variables_x2.bound.upper == INTEGER_UPPER_BOUND
|
|
63
63
|
assert decision_variables_x2.name == "2"
|
|
64
|
-
decision_variables_x3 = ommx_instance.
|
|
64
|
+
decision_variables_x3 = ommx_instance.get_decision_variable(2)
|
|
65
65
|
assert decision_variables_x3.id == 2
|
|
66
66
|
assert decision_variables_x3.kind == DecisionVariable.BINARY
|
|
67
67
|
assert decision_variables_x3.bound.lower == 0
|
|
@@ -69,58 +69,43 @@ def test_milp():
|
|
|
69
69
|
assert decision_variables_x3.name == "3"
|
|
70
70
|
|
|
71
71
|
# Check the objective function
|
|
72
|
-
assert ommx_instance.objective.
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
assert len(
|
|
76
|
-
|
|
77
|
-
assert
|
|
78
|
-
assert
|
|
79
|
-
objective_term_x2 = objective.terms[1]
|
|
80
|
-
assert objective_term_x2.id == 1
|
|
81
|
-
assert objective_term_x2.coefficient == -2
|
|
82
|
-
objective_term_x3 = objective.terms[2]
|
|
83
|
-
assert objective_term_x3.id == 2
|
|
84
|
-
assert objective_term_x3.coefficient == -3
|
|
72
|
+
assert ommx_instance.objective.degree() == 1
|
|
73
|
+
assert ommx_instance.objective.constant_term == 0
|
|
74
|
+
linear_terms = ommx_instance.objective.linear_terms
|
|
75
|
+
assert len(linear_terms) == 3
|
|
76
|
+
assert linear_terms[0] == -1
|
|
77
|
+
assert linear_terms[1] == -2
|
|
78
|
+
assert linear_terms[2] == -3
|
|
85
79
|
|
|
86
80
|
# Check the constraints
|
|
87
|
-
assert len(ommx_instance.
|
|
81
|
+
assert len(ommx_instance.get_constraints()) == 3
|
|
88
82
|
|
|
89
|
-
constraint1 = ommx_instance.
|
|
83
|
+
constraint1 = ommx_instance.get_constraint(0)
|
|
90
84
|
assert constraint1.equality == Constraint.EQUAL_TO_ZERO
|
|
91
|
-
assert constraint1.function.
|
|
92
|
-
assert constraint1.function.
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
assert
|
|
96
|
-
assert
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
assert constraint1_term_x2.coefficient == -5
|
|
100
|
-
|
|
101
|
-
constraint2 = ommx_instance.constraints[1]
|
|
85
|
+
assert constraint1.function.degree() == 1
|
|
86
|
+
assert constraint1.function.constant_term == 6
|
|
87
|
+
constraint1_terms = constraint1.function.linear_terms
|
|
88
|
+
assert len(constraint1_terms) == 2
|
|
89
|
+
assert constraint1_terms[0] == 4
|
|
90
|
+
assert constraint1_terms[1] == -5
|
|
91
|
+
|
|
92
|
+
constraint2 = ommx_instance.get_constraint(1)
|
|
102
93
|
assert constraint2.equality == Constraint.LESS_THAN_OR_EQUAL_TO_ZERO
|
|
103
|
-
assert constraint2.function.
|
|
104
|
-
assert constraint2.function.
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
assert
|
|
108
|
-
assert
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
assert constraint2_term_x3.coefficient == 8
|
|
112
|
-
|
|
113
|
-
constraint3 = ommx_instance.constraints[2]
|
|
94
|
+
assert constraint2.function.degree() == 1
|
|
95
|
+
assert constraint2.function.constant_term == -9
|
|
96
|
+
constraint2_terms = constraint2.function.linear_terms
|
|
97
|
+
assert len(constraint2_terms) == 2
|
|
98
|
+
assert constraint2_terms[0] == -7
|
|
99
|
+
assert constraint2_terms[2] == 8
|
|
100
|
+
|
|
101
|
+
constraint3 = ommx_instance.get_constraint(2)
|
|
114
102
|
assert constraint3.equality == Constraint.LESS_THAN_OR_EQUAL_TO_ZERO
|
|
115
|
-
assert constraint3.function.
|
|
116
|
-
assert constraint3.function.
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
assert
|
|
120
|
-
assert
|
|
121
|
-
constraint3_term_x3 = constraint3.function.linear.terms[1]
|
|
122
|
-
assert constraint3_term_x3.id == 2
|
|
123
|
-
assert constraint3_term_x3.coefficient == 11
|
|
103
|
+
assert constraint3.function.degree() == 1
|
|
104
|
+
assert constraint3.function.constant_term == -12
|
|
105
|
+
constraint3_terms = constraint3.function.linear_terms
|
|
106
|
+
assert len(constraint3_terms) == 2
|
|
107
|
+
assert constraint3_terms[1] == -10
|
|
108
|
+
assert constraint3_terms[2] == 11
|
|
124
109
|
|
|
125
110
|
|
|
126
111
|
def test_no_objective_model():
|
|
@@ -151,52 +136,46 @@ def test_no_objective_model():
|
|
|
151
136
|
model.add_constr(1 * x1 + 2 * x2 - 5 == 0) # type: ignore
|
|
152
137
|
model.add_constr(4 * x1 + 3 * x2 - 10 == 0) # type: ignore
|
|
153
138
|
|
|
154
|
-
ommx_instance = model_to_instance(model)
|
|
139
|
+
ommx_instance = model_to_instance(model)
|
|
155
140
|
|
|
156
141
|
assert ommx_instance.sense == Instance.MAXIMIZE
|
|
157
142
|
|
|
158
143
|
# Check the decision variables
|
|
159
|
-
assert len(ommx_instance.
|
|
160
|
-
decision_variables_x1 = ommx_instance.
|
|
144
|
+
assert len(ommx_instance.get_decision_variables()) == 2
|
|
145
|
+
decision_variables_x1 = ommx_instance.get_decision_variable(0)
|
|
161
146
|
assert decision_variables_x1.id == 0
|
|
162
147
|
assert decision_variables_x1.kind == DecisionVariable.CONTINUOUS
|
|
163
148
|
assert decision_variables_x1.bound.lower == LOWER_BOUND
|
|
164
149
|
assert decision_variables_x1.bound.upper == UPPER_BOUND
|
|
165
150
|
assert decision_variables_x1.name == "1"
|
|
166
|
-
decision_variables_x2 = ommx_instance.
|
|
151
|
+
decision_variables_x2 = ommx_instance.get_decision_variable(1)
|
|
167
152
|
assert decision_variables_x2.id == 1
|
|
168
153
|
assert decision_variables_x2.kind == DecisionVariable.CONTINUOUS
|
|
169
154
|
assert decision_variables_x2.bound.lower == LOWER_BOUND
|
|
170
155
|
assert decision_variables_x2.bound.upper == UPPER_BOUND
|
|
171
156
|
assert decision_variables_x2.name == "2"
|
|
172
157
|
|
|
173
|
-
# check the objective function
|
|
174
|
-
assert ommx_instance.objective.
|
|
175
|
-
assert ommx_instance.objective.
|
|
158
|
+
# check the objective function - should be a zero constant
|
|
159
|
+
assert ommx_instance.objective.degree() == 0
|
|
160
|
+
assert ommx_instance.objective.num_terms() == 0 # Zero function has 0 terms
|
|
176
161
|
|
|
177
162
|
# Check the constraints
|
|
178
|
-
assert len(ommx_instance.
|
|
163
|
+
assert len(ommx_instance.get_constraints()) == 2
|
|
179
164
|
|
|
180
|
-
constraint1 = ommx_instance.
|
|
165
|
+
constraint1 = ommx_instance.get_constraint(0)
|
|
181
166
|
assert constraint1.equality == Constraint.EQUAL_TO_ZERO
|
|
182
|
-
assert constraint1.function.
|
|
183
|
-
assert constraint1.function.
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
assert
|
|
187
|
-
assert
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
assert constraint1_term_x2.coefficient == 2
|
|
191
|
-
|
|
192
|
-
constraint2 = ommx_instance.constraints[1]
|
|
167
|
+
assert constraint1.function.degree() == 1
|
|
168
|
+
assert constraint1.function.constant_term == -5
|
|
169
|
+
constraint1_terms = constraint1.function.linear_terms
|
|
170
|
+
assert len(constraint1_terms) == 2
|
|
171
|
+
assert constraint1_terms[0] == 1
|
|
172
|
+
assert constraint1_terms[1] == 2
|
|
173
|
+
|
|
174
|
+
constraint2 = ommx_instance.get_constraint(1)
|
|
193
175
|
assert constraint2.equality == Constraint.EQUAL_TO_ZERO
|
|
194
|
-
assert constraint2.function.
|
|
195
|
-
assert constraint2.function.
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
assert
|
|
199
|
-
assert
|
|
200
|
-
constraint2_term_x3 = constraint2.function.linear.terms[1]
|
|
201
|
-
assert constraint2_term_x3.id == 1
|
|
202
|
-
assert constraint2_term_x3.coefficient == 3
|
|
176
|
+
assert constraint2.function.degree() == 1
|
|
177
|
+
assert constraint2.function.constant_term == -10
|
|
178
|
+
constraint2_terms = constraint2.function.linear_terms
|
|
179
|
+
assert len(constraint2_terms) == 2
|
|
180
|
+
assert constraint2_terms[0] == 4
|
|
181
|
+
assert constraint2_terms[1] == 3
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
import pytest
|
|
2
|
-
|
|
3
|
-
from ommx.v1.constraint_pb2 import Constraint, Equality
|
|
4
|
-
from ommx.v1.decision_variables_pb2 import DecisionVariable as _DecisionVariable
|
|
5
|
-
from ommx.v1.function_pb2 import Function
|
|
6
|
-
from ommx.v1.linear_pb2 import Linear
|
|
7
|
-
from ommx.v1.quadratic_pb2 import Quadratic
|
|
8
|
-
from ommx.v1 import Instance, DecisionVariable
|
|
9
|
-
|
|
10
|
-
from ommx_python_mip_adapter import OMMXPythonMIPAdapter
|
|
11
|
-
|
|
12
|
-
from ommx_python_mip_adapter.exception import OMMXPythonMIPAdapterError
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
def test_error_not_suppoerted_decision_variable():
|
|
16
|
-
ommx_instance = Instance.from_components(
|
|
17
|
-
decision_variables=[
|
|
18
|
-
DecisionVariable(
|
|
19
|
-
_DecisionVariable(id=1, kind=_DecisionVariable.KIND_UNSPECIFIED)
|
|
20
|
-
)
|
|
21
|
-
],
|
|
22
|
-
objective=Function(constant=0),
|
|
23
|
-
constraints=[],
|
|
24
|
-
sense=Instance.MINIMIZE,
|
|
25
|
-
)
|
|
26
|
-
with pytest.raises(OMMXPythonMIPAdapterError) as e:
|
|
27
|
-
OMMXPythonMIPAdapter(ommx_instance)
|
|
28
|
-
assert "Not supported decision variable" in str(e.value)
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
def test_error_nonlinear_objective():
|
|
32
|
-
# Objective function: 2.3 * x * x
|
|
33
|
-
ommx_instance = Instance.from_components(
|
|
34
|
-
decision_variables=[DecisionVariable.continuous(0)],
|
|
35
|
-
objective=Function(
|
|
36
|
-
quadratic=Quadratic(rows=[1], columns=[1], values=[2.3]),
|
|
37
|
-
),
|
|
38
|
-
constraints=[],
|
|
39
|
-
sense=Instance.MINIMIZE,
|
|
40
|
-
)
|
|
41
|
-
|
|
42
|
-
with pytest.raises(OMMXPythonMIPAdapterError) as e:
|
|
43
|
-
OMMXPythonMIPAdapter(ommx_instance)
|
|
44
|
-
assert "The function must be either `constant` or `linear`." in str(e.value)
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
def test_error_nonlinear_constraint():
|
|
48
|
-
# Objective function: 0
|
|
49
|
-
# Constraint: 2.3 * x * x = 0
|
|
50
|
-
ommx_instance = Instance.from_components(
|
|
51
|
-
decision_variables=[DecisionVariable.continuous(1)],
|
|
52
|
-
objective=Function(
|
|
53
|
-
constant=0,
|
|
54
|
-
),
|
|
55
|
-
constraints=[
|
|
56
|
-
Constraint(
|
|
57
|
-
function=Function(
|
|
58
|
-
quadratic=Quadratic(rows=[1], columns=[1], values=[2.3]),
|
|
59
|
-
),
|
|
60
|
-
equality=Equality.EQUALITY_EQUAL_TO_ZERO,
|
|
61
|
-
),
|
|
62
|
-
],
|
|
63
|
-
sense=Instance.MINIMIZE,
|
|
64
|
-
)
|
|
65
|
-
|
|
66
|
-
with pytest.raises(OMMXPythonMIPAdapterError) as e:
|
|
67
|
-
OMMXPythonMIPAdapter(ommx_instance)
|
|
68
|
-
assert "The function must be either `constant` or `linear`." in str(e.value)
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
def test_error_not_supported_constraint_equality():
|
|
72
|
-
# Objective function: 0
|
|
73
|
-
# Constraint: 2x ?? 0 (equality: unspecified)
|
|
74
|
-
ommx_instance = Instance.from_components(
|
|
75
|
-
decision_variables=[DecisionVariable.continuous(1)],
|
|
76
|
-
objective=Function(constant=0),
|
|
77
|
-
constraints=[
|
|
78
|
-
Constraint(
|
|
79
|
-
function=Function(
|
|
80
|
-
linear=Linear(terms=[Linear.Term(id=1, coefficient=2)])
|
|
81
|
-
),
|
|
82
|
-
equality=Equality.EQUALITY_UNSPECIFIED,
|
|
83
|
-
),
|
|
84
|
-
],
|
|
85
|
-
sense=Instance.MINIMIZE,
|
|
86
|
-
)
|
|
87
|
-
|
|
88
|
-
with pytest.raises(OMMXPythonMIPAdapterError) as e:
|
|
89
|
-
OMMXPythonMIPAdapter(ommx_instance)
|
|
90
|
-
assert "Not supported constraint equality" in str(e.value)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ommx_python_mip_adapter-1.9.5 → ommx_python_mip_adapter-2.0.0b2}/tests/test_constant_constraint.py
RENAMED
|
File without changes
|