diff --git a/pulp/apis/highs_api.py b/pulp/apis/highs_api.py index cbc0ea09..8d5c0f1e 100644 --- a/pulp/apis/highs_api.py +++ b/pulp/apis/highs_api.py @@ -379,7 +379,7 @@ def buildSolverModel(self, lp): var.index, highspy.HighsVarType.kInteger ) - for constraint in lp.constraints.values(): + for i, constraint in enumerate(lp.constraints.values()): non_zero_constraint_items = [ (var.index, coefficient) for var, coefficient in constraint.items() @@ -391,6 +391,8 @@ def buildSolverModel(self, lp): else: indices, coefficients = zip(*non_zero_constraint_items) + constraint.index = i + lb = constraint.getLb() ub = constraint.getUb() lp.solverModel.addRow( @@ -476,10 +478,24 @@ def findSolutionValues(self, lp): } col_values = list(solution.col_value) + col_duals = list(solution.col_dual) # Assign values to the variables as with lp.assignVarsVals() for var in lp.variables(): var.varValue = col_values[var.index] + var.dj = col_duals[var.index] + + for constraint in lp.constraints.values(): + # PuLP returns LpConstraint.constant as if it were on the + # left-hand side, which means the signs on the following line + # are correct + constraint.slack = ( + constraint.constant + solution.row_value[constraint.index] + ) + # We need to flip the sign for slacks for LE constraints + if constraint.sense == constants.LpConstraintLE: + constraint.slack *= -1.0 + constraint.pi = solution.row_dual[constraint.index] if obj_value == float(inf) and status in ( HighsModelStatus.kTimeLimit, diff --git a/pulp/tests/test_pulp.py b/pulp/tests/test_pulp.py index 331d78b0..77666b5c 100644 --- a/pulp/tests/test_pulp.py +++ b/pulp/tests/test_pulp.py @@ -621,6 +621,7 @@ def test_dual_variables_reduced_costs(self): PULP_CBC_CMD, YAPOSIB, PYGLPK, + HiGHS, SAS94, SASCAS, ]: