Skip to content

Commit c83d8ca

Browse files
committed
highs: support changing columns
1 parent b0c5066 commit c83d8ca

File tree

2 files changed

+44
-10
lines changed

2 files changed

+44
-10
lines changed

mip/highs.py

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,9 @@
109109
HighsInt Highs_changeColBounds(
110110
void* highs, const HighsInt col, const double lower, const double upper
111111
);
112+
HighsInt Highs_changeCoeff(
113+
void* highs, const HighsInt row, const HighsInt col, const double value
114+
);
112115
HighsInt Highs_getRowsByRange(
113116
const void* highs, const HighsInt from_row, const HighsInt to_row,
114117
HighsInt* num_row, double* lower, double* upper, HighsInt* num_nz,
@@ -260,6 +263,23 @@ def _set_bool_option_value(self: "SolverHighs", name: str, value: float):
260263
self._lib.Highs_setBoolOptionValue(self._model, name.encode("UTF-8"), value)
261264
)
262265

266+
def _change_coef(self: "SolverHighs", row: int, col: int, value: float):
267+
"Overwrite a single coefficient in the matrix."
268+
check(self._lib.Highs_changeCoeff(self._model, row, col, value))
269+
270+
def _set_column(self: "SolverHighs", col: int, column: "mip.Column"):
271+
"Overwrite coefficients of one column."
272+
# We also have to set to 0 all coefficients of the old column, so we
273+
# fetch that first.
274+
var = self.model.vars[col]
275+
old_column = self.var_get_column(var)
276+
coeffs = {cons.idx: 0.0 for cons in old_column.constrs}
277+
coeffs.update(
278+
{cons.idx: coef for cons, coef in zip(column.constrs, column.coeffs)}
279+
)
280+
for row, coef in coeffs.items():
281+
self._change_coef(row, col, coef)
282+
263283
def add_var(
264284
self: "SolverHighs",
265285
obj: numbers.Real = 0,
@@ -269,7 +289,6 @@ def add_var(
269289
column: "mip.Column" = None,
270290
name: str = "",
271291
):
272-
# TODO: handle column data
273292
col: int = self.num_cols()
274293
check(self._lib.Highs_addVar(self._model, lb, ub))
275294
check(self._lib.Highs_changeColCost(self._model, col, obj))
@@ -280,6 +299,9 @@ def add_var(
280299
)
281300
)
282301

302+
if column:
303+
self._set_column(col, column)
304+
283305
# store name & type
284306
self._var_name.append(name)
285307
self._var_col[name] = col
@@ -852,8 +874,7 @@ def var_get_column(self: "SolverHighs", var: "mip.Var") -> "mip.Column":
852874
)
853875

854876
def var_set_column(self: "SolverHighs", var: "mip.Var", value: "mip.Column"):
855-
# TODO
856-
raise NotImplementedError()
877+
self._set_column(var.idx, value)
857878

858879
def var_get_rc(self: "SolverHighs", var: "mip.Var") -> numbers.Real:
859880
if self._rc:

test/test_model.py

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -679,8 +679,12 @@ def test_setting_variable_attributes(solver):
679679
y = m.add_var("y", obj=1)
680680
c = m.add_constr(y <= x, "some_constraint")
681681
# TODO: Remove Not implemented error when implemented
682-
with pytest.raises(NotImplementedError):
683-
x.column = Column([c], [-2]) # new column based on constraint (y <= 2*x)
682+
column = Column([c], [-2]) # new column based on constraint (y <= 2*x)
683+
if solver == HIGHS:
684+
x.column = column
685+
else:
686+
with pytest.raises(NotImplementedError):
687+
x.column = column
684688

685689
# Check before optimization
686690
assert x.lb == -1.0
@@ -693,15 +697,24 @@ def test_setting_variable_attributes(solver):
693697
if solver == CBC:
694698
assert x.branch_priority == 0
695699
# TODO: Check when implemented
696-
# column = x.column
697-
# assert column.coeffs == [-2]
698-
# assert column.constrs == [c]
700+
if solver == HIGHS:
701+
column = x.column
702+
assert column.coeffs == [-2]
703+
assert column.constrs == [c]
699704

700705
m.optimize()
701706

702707
# Check that optimization result considered changes correctly
703-
assert abs(m.objective_value - 10.0) <= TOL
704-
assert abs(x.x - 5) < TOL
708+
if solver == HIGHS:
709+
# column was changed, so y == 2*x
710+
assert abs(m.objective_value - 15.0) <= TOL
711+
assert abs(x.x - 5) < TOL
712+
assert abs(y.x - 10) < TOL
713+
else:
714+
# column was not changed, so y == x
715+
assert abs(m.objective_value - 10.0) <= TOL
716+
assert abs(x.x - 5) < TOL
717+
assert abs(y.x - 5) < TOL
705718

706719

707720
@pytest.mark.parametrize("solver", SOLVERS)

0 commit comments

Comments
 (0)