Skip to content

Commit f14bd68

Browse files
Sebastian Hegersebheger
authored andcommitted
Add some inital variable tests for model
1 parent bd90ec1 commit f14bd68

File tree

1 file changed

+211
-0
lines changed

1 file changed

+211
-0
lines changed

test/test_model.py

Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
import os
2+
3+
import pytest
4+
5+
from mip import (
6+
CBC,
7+
GUROBI,
8+
Model,
9+
MAXIMIZE,
10+
MINIMIZE,
11+
OptimizationStatus,
12+
INTEGER,
13+
CONTINUOUS,
14+
BINARY,
15+
)
16+
17+
TOL = 1e-4
18+
SOLVERS = [CBC]
19+
if "GUROBI_HOME" in os.environ:
20+
SOLVERS += [GUROBI]
21+
22+
# Overall Optimization Tests
23+
24+
25+
@pytest.mark.parametrize("solver", SOLVERS)
26+
@pytest.mark.parametrize("var_type", (CONTINUOUS, INTEGER))
27+
def test_minimize_single_continuous_or_integer_variable_with_default_bounds(
28+
solver, var_type
29+
):
30+
m = Model(solver_name=solver, sense=MINIMIZE)
31+
x = m.add_var(name="x", var_type=var_type, obj=1)
32+
m.optimize()
33+
# check result
34+
assert m.status == OptimizationStatus.OPTIMAL
35+
assert abs(x.x) < TOL
36+
assert abs(m.objective_value) < TOL
37+
38+
39+
@pytest.mark.parametrize("solver", SOLVERS)
40+
@pytest.mark.parametrize("var_type", (CONTINUOUS, INTEGER))
41+
def test_maximize_single_continuous_or_integer_variable_with_default_bounds(
42+
solver, var_type
43+
):
44+
m = Model(solver_name=solver, sense=MAXIMIZE)
45+
x = m.add_var(name="x", var_type=var_type, obj=1)
46+
m.optimize()
47+
# check result
48+
assert m.status == OptimizationStatus.UNBOUNDED
49+
assert x.x is None
50+
assert m.objective_value is None
51+
52+
53+
@pytest.mark.parametrize("solver", SOLVERS)
54+
@pytest.mark.parametrize(
55+
"sense,status,xvalue,objvalue",
56+
[
57+
(MAXIMIZE, OptimizationStatus.OPTIMAL, 1, 1), # implicit upper bound 1
58+
(MINIMIZE, OptimizationStatus.OPTIMAL, 0, 0), # implicit lower bound 0
59+
],
60+
)
61+
def test_single_binary_variable_with_default_bounds(
62+
solver, sense: str, status, xvalue, objvalue
63+
):
64+
m = Model(solver_name=solver, sense=sense)
65+
x = m.add_var(name="x", var_type=BINARY, obj=1)
66+
m.optimize()
67+
# check result
68+
assert m.status == status
69+
assert abs(x.x - xvalue) < TOL
70+
assert abs(m.objective_value - objvalue) < TOL
71+
72+
73+
@pytest.mark.parametrize("solver", SOLVERS)
74+
@pytest.mark.parametrize("var_type", (CONTINUOUS, INTEGER))
75+
@pytest.mark.parametrize(
76+
"lb,ub,min_obj,max_obj",
77+
(
78+
(0, 0, 0, 0), # fixed to 0
79+
(2, 2, 2, 2), # fixed to positive
80+
(-2, -2, -2, -2), # fixed to negative
81+
(1, 2, 1, 2), # positive range
82+
(-3, 2, -3, 2), # negative range
83+
(-4, 5, -4, 5), # range from positive to negative
84+
),
85+
)
86+
def test_single_continuous_or_integer_variable_with_different_bounds(
87+
solver, var_type, lb, ub, min_obj, max_obj
88+
):
89+
# Minimum Case
90+
m = Model(solver_name=solver, sense=MINIMIZE)
91+
m.add_var(name="x", var_type=var_type, lb=lb, ub=ub, obj=1)
92+
m.optimize()
93+
# check result
94+
assert m.status == OptimizationStatus.OPTIMAL
95+
assert abs(m.objective_value - min_obj) < TOL
96+
97+
# Maximum Case
98+
m = Model(solver_name=solver, sense=MAXIMIZE)
99+
m.add_var(name="x", var_type=var_type, lb=lb, ub=ub, obj=1)
100+
m.optimize()
101+
# check result
102+
assert m.status == OptimizationStatus.OPTIMAL
103+
assert abs(m.objective_value - max_obj) < TOL
104+
105+
106+
@pytest.mark.parametrize("solver", SOLVERS)
107+
@pytest.mark.parametrize(
108+
"lb,ub,min_obj,max_obj",
109+
(
110+
(0, 1, 0, 1), # regular case
111+
(0, 0, 0, 0), # fixed to 0
112+
(1, 1, 1, 1), # fixed to 1
113+
),
114+
)
115+
def test_binary_variable_with_different_bounds(solver, lb, ub, min_obj, max_obj):
116+
# Minimum Case
117+
m = Model(solver_name=solver, sense=MINIMIZE)
118+
m.add_var(name="x", var_type=BINARY, lb=lb, ub=ub, obj=1)
119+
m.optimize()
120+
# check result
121+
assert m.status == OptimizationStatus.OPTIMAL
122+
assert abs(m.objective_value - min_obj) < TOL
123+
124+
# Maximum Case
125+
m = Model(solver_name=solver, sense=MAXIMIZE)
126+
m.add_var(name="x", var_type=BINARY, lb=lb, ub=ub, obj=1)
127+
m.optimize()
128+
# check result
129+
assert m.status == OptimizationStatus.OPTIMAL
130+
assert abs(m.objective_value - max_obj) < TOL
131+
132+
133+
@pytest.mark.parametrize("solver", SOLVERS)
134+
def test_binary_variable_illegal_bounds(solver):
135+
m = Model(solver_name=solver)
136+
# Illegal lower bound
137+
with pytest.raises(ValueError):
138+
m.add_var("x", lb=-1, var_type=BINARY)
139+
# Illegal upper bound
140+
with pytest.raises(ValueError):
141+
m.add_var("x", ub=2, var_type=BINARY)
142+
143+
144+
@pytest.mark.parametrize("solver", SOLVERS)
145+
@pytest.mark.parametrize("sense", (MINIMIZE, MAXIMIZE))
146+
@pytest.mark.parametrize(
147+
"var_type,lb,ub",
148+
(
149+
(CONTINUOUS, 3.5, 2),
150+
(INTEGER, 5, 4),
151+
(BINARY, 1, 0),
152+
),
153+
)
154+
def test_contradictory_variable_bounds(solver, sense: str, var_type: str, lb, ub):
155+
m = Model(solver_name=solver, sense=sense)
156+
m.add_var(name="x", var_type=var_type, lb=lb, ub=ub, obj=1)
157+
m.optimize()
158+
# check result
159+
assert m.status == OptimizationStatus.INFEASIBLE
160+
161+
162+
@pytest.mark.parametrize("solver", SOLVERS)
163+
def test_float_bounds_for_integer_variable(solver):
164+
# Minimum Case
165+
m = Model(solver_name=solver, sense=MINIMIZE)
166+
m.add_var(name="x", var_type=INTEGER, lb=-1.5, ub=3.5, obj=1)
167+
m.optimize()
168+
# check result
169+
assert m.status == OptimizationStatus.OPTIMAL
170+
assert abs(m.objective_value - (-1)) < TOL
171+
172+
# Maximum Case
173+
m = Model(solver_name=solver, sense=MAXIMIZE)
174+
m.add_var(name="x", var_type=INTEGER, lb=-1.5, ub=3.5, obj=1)
175+
m.optimize()
176+
# check result
177+
assert m.status == OptimizationStatus.OPTIMAL
178+
assert abs(m.objective_value - 3) < TOL
179+
180+
181+
@pytest.mark.parametrize("solver", SOLVERS)
182+
@pytest.mark.parametrize("sense", (MINIMIZE, MAXIMIZE))
183+
def test_single_default_variable_with_nothing_to_do(solver, sense):
184+
m = Model(solver_name=solver, sense=sense)
185+
m.add_var(name="x")
186+
m.optimize()
187+
# check result
188+
assert m.status == OptimizationStatus.OPTIMAL
189+
assert abs(m.objective_value) < TOL
190+
191+
192+
@pytest.mark.parametrize("solver", SOLVERS)
193+
@pytest.mark.parametrize("var_type", (CONTINUOUS, INTEGER, BINARY))
194+
@pytest.mark.parametrize("obj", (1.2, 2))
195+
def test_single_variable_with_different_non_zero_objectives(solver, var_type, obj):
196+
# Maximize
197+
m = Model(solver_name=solver, sense=MAXIMIZE)
198+
x = m.add_var(name="x", var_type=var_type, lb=0, ub=1, obj=obj)
199+
m.optimize()
200+
# check result
201+
assert m.status == OptimizationStatus.OPTIMAL
202+
assert abs(m.objective_value - obj) < TOL
203+
assert abs(x.x - 1.0) < TOL
204+
# Minimize with negative
205+
m = Model(solver_name=solver, sense=MINIMIZE)
206+
x = m.add_var(name="x", var_type=var_type, lb=0, ub=1, obj=-obj)
207+
m.optimize()
208+
# check result
209+
assert m.status == OptimizationStatus.OPTIMAL
210+
assert abs(m.objective_value - (-obj)) < TOL
211+
assert abs(x.x - 1.0) < TOL

0 commit comments

Comments
 (0)