Skip to content

Commit 3fb7268

Browse files
authored
Merge pull request #2 from Doing-The-Math/rs/highs_fixes
Rs/highs fixes
2 parents 0ccb811 + bcd8d91 commit 3fb7268

File tree

4 files changed

+56
-9
lines changed

4 files changed

+56
-9
lines changed

mip/highs.py

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -720,7 +720,7 @@ def __init__(self, model: mip.Model, name: str, sense: str):
720720
# Buffer string for storing names
721721
self._name_buffer = ffi.new(f"char[{self._lib.kHighsMaximumStringLength}]")
722722

723-
# type conversion maps
723+
# type conversion maps (can not distinguish binary from integer!)
724724
self._var_type_map = {
725725
mip.CONTINUOUS: self._lib.kHighsVarTypeContinuous,
726726
mip.BINARY: self._lib.kHighsVarTypeInteger,
@@ -760,8 +760,8 @@ def _get_double_option_value(self: "SolverHighs", name: str) -> float:
760760
)
761761
return value[0]
762762

763-
def _get_bool_option_value(self: "SolverHighs", name: str) -> float:
764-
value = ffi.new("bool*")
763+
def _get_bool_option_value(self: "SolverHighs", name: str) -> int:
764+
value = ffi.new("int*")
765765
check(
766766
self._lib.Highs_getBoolOptionValue(self._model, name.encode("UTF-8"), value)
767767
)
@@ -779,7 +779,7 @@ def _set_double_option_value(self: "SolverHighs", name: str, value: float):
779779
)
780780
)
781781

782-
def _set_bool_option_value(self: "SolverHighs", name: str, value: float):
782+
def _set_bool_option_value(self: "SolverHighs", name: str, value: int):
783783
check(
784784
self._lib.Highs_setBoolOptionValue(self._model, name.encode("UTF-8"), value)
785785
)
@@ -815,6 +815,8 @@ def add_var(
815815
if name:
816816
check(self._lib.Highs_passColName(self._model, col, name.encode("utf-8")))
817817
if var_type != mip.CONTINUOUS:
818+
# Note that HiGHS doesn't distinguish binary and integer variables
819+
# by type. There is only a boolean flag for "integrality".
818820
self._num_int_vars += 1
819821
check(
820822
self._lib.Highs_changeColIntegrality(
@@ -1035,6 +1037,18 @@ def set_start(self: "SolverHighs", start: List[Tuple["mip.Var", numbers.Real]]):
10351037
self._lib.Highs_setSolution(self._model, cval, ffi.NULL, ffi.NULL, ffi.NULL)
10361038

10371039
def set_objective(self: "SolverHighs", lin_expr: "mip.LinExpr", sense: str = ""):
1040+
# first reset old objective (all 0)
1041+
n = self.num_cols()
1042+
costs = ffi.new("double[]", n) # initialized to 0
1043+
check(
1044+
self._lib.Highs_changeColsCostByRange(
1045+
self._model,
1046+
0, # from_col
1047+
n - 1, # to_col
1048+
costs,
1049+
)
1050+
)
1051+
10381052
# set coefficients
10391053
for var, coef in lin_expr.expr.items():
10401054
check(self._lib.Highs_changeColCost(self._model, var.idx, coef))
@@ -1518,7 +1532,11 @@ def remove_vars(self: "SolverHighs", varsList: List[int]):
15181532

15191533
def var_get_index(self: "SolverHighs", name: str) -> int:
15201534
idx = ffi.new("int *")
1521-
self._lib.Highs_getColByName(self._model, name.encode("utf-8"), idx)
1535+
status = self._lib.Highs_getColByName(self._model, name.encode("utf-8"), idx)
1536+
if status == STATUS_ERROR:
1537+
# This means that no var with that name was found. Unfortunately,
1538+
# HiGHS::getColByName doesn't assign a value to idx in that case.
1539+
return -1
15221540
return idx[0]
15231541

15241542
def get_problem_name(self: "SolverHighs") -> str:

test/mip_files_test.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,9 @@ def test_mip_file(solver: str, instance: str):
139139

140140
max_dif = max(max(abs(ub), abs(lb)) * 0.01, TOL)
141141

142+
if solver == HIGHS and instance.endswith(".gz"):
143+
pytest.skip("HiGHS does not support .gz files.")
144+
142145
m.read(instance)
143146
if bas_file:
144147
m.verbose = True

test/mip_test.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -556,11 +556,21 @@ def test_constr_by_name_rhs(self, solver):
556556
assert model.constr_by_name("row0").rhs == val
557557

558558
@pytest.mark.parametrize("solver", SOLVERS)
559-
def test_var_by_name_rhs(self, solver):
559+
def test_var_by_name_valid(self, solver):
560560
n, model = self.build_model(solver)
561561

562-
v = model.var_by_name("x({},{})".format(0, 0))
562+
name = "x({},{})".format(0, 0)
563+
v = model.var_by_name(name)
563564
assert v is not None
565+
assert isinstance(v, mip.Var)
566+
assert v.name == name
567+
568+
@pytest.mark.parametrize("solver", SOLVERS)
569+
def test_var_by_name_invalid(self, solver):
570+
n, model = self.build_model(solver)
571+
572+
v = model.var_by_name("xyz_invalid_name")
573+
assert v is None
564574

565575
@pytest.mark.parametrize("solver", SOLVERS)
566576
def test_obj_const1(self, solver: str):

test/test_model.py

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1294,6 +1294,21 @@ def test_copy(solver):
12941294
assert id(term.expr) != id(term_copy.expr)
12951295

12961296

1297+
@skip_on(NotImplementedError)
1298+
@pytest.mark.parametrize("solver", SOLVERS)
1299+
def test_verbose(solver):
1300+
# set and get verbose flag
1301+
m = Model(solver_name=solver)
1302+
1303+
# active
1304+
m.verbose = 1
1305+
assert m.verbose == 1
1306+
1307+
# inactive
1308+
m.verbose = 0
1309+
assert m.verbose == 0
1310+
1311+
12971312
@skip_on(NotImplementedError)
12981313
@pytest.mark.parametrize("solver", SOLVERS)
12991314
def test_constraint_with_lin_expr_and_lin_expr(solver):
@@ -1356,6 +1371,7 @@ def test_objective(solver):
13561371
m = Model(solver_name=solver, sense=MAXIMIZE)
13571372
x = m.add_var(name="x", lb=0, ub=1)
13581373
y = m.add_var(name="y", lb=0, ub=1)
1374+
z = m.add_var(name="z", lb=0, ub=1)
13591375

13601376
m.objective = x - y + 0.5
13611377
assert m.objective.x is None
@@ -1374,13 +1390,13 @@ def test_objective(solver):
13741390

13751391

13761392
# Test changing the objective
1377-
m.objective = x + y + 1.5
1393+
m.objective = y + 2*z + 1.5
13781394
m.sense = MINIMIZE
13791395
# TODO: assert m.objective.sense == MINIMIZE
13801396

13811397
assert len(m.objective.expr) == 2
1382-
assert m.objective.expr[x] == 1
13831398
assert m.objective.expr[y] == 1
1399+
assert m.objective.expr[z] == 2
13841400
assert m.objective.const == 1.5
13851401

13861402
status = m.optimize()

0 commit comments

Comments
 (0)