Skip to content

Commit d93e175

Browse files
committed
Improve repairSolution handling of numerical issues
Fixed highspy unit test that had assumed unique solution
1 parent bc037c3 commit d93e175

File tree

2 files changed

+29
-4
lines changed

2 files changed

+29
-4
lines changed

highs/lp_data/HighsCallback.cpp

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
* @brief
1010
*/
1111
#include "lp_data/HighsCallback.h"
12+
#include "util/HighsIntegers.h"
1213

1314
#include <algorithm>
1415
#include <cassert>
@@ -254,10 +255,30 @@ HighsStatus HighsCallbackInput::repairSolution() {
254255
clone.setOptionValue("output_flag", false);
255256
clone.passModel(highs->getModel());
256257

258+
HighsVarType vtype = HighsVarType::kContinuous;
259+
double tolerance = highs->getOptions().mip_feasibility_tolerance;
260+
257261
// fix the variables
258262
for (HighsInt i = 0; i < user_solution.size(); i++) {
259263
if (user_solution[i] != kHighsUndefined) {
260-
clone.changeColBounds(i, user_solution[i], user_solution[i]);
264+
double value = user_solution[i];
265+
highs->getColIntegrality(i, vtype);
266+
267+
if (vtype == HighsVarType::kInteger) {
268+
// check if the provided solution value is integerfeasible
269+
if (!HighsIntegers::isIntegral(value, tolerance)) {
270+
highsLogUser(highs->getOptions().log_options, HighsLogType::kError,
271+
"repairSolution: User solution (index %d) is outside "
272+
"integrality tolerance (value %f)\n",
273+
i, value);
274+
return HighsStatus::kError;
275+
} else {
276+
// since the variable is integral, round it to avoid numerical issues
277+
value = std::round(value);
278+
}
279+
}
280+
281+
clone.changeColBounds(i, value, value);
261282
}
262283
}
263284

tests/test_highspy.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1454,13 +1454,13 @@ def partial_solution(e):
14541454

14551455
# get every 2nd element from 2d numpy sol array
14561456
self.assertEqual(e.data_in.setSolution(x[:, ::2], sol[:, ::2]), highspy.HighsStatus.kOk)
1457-
self.assertTrue((e.data_in.user_solution[0:4] == [0,highspy.kHighsUndefined,0,highspy.kHighsUndefined]).all())
1457+
self.assertEqual(list(e.data_in.user_solution[0:4]), [sol[0,0],highspy.kHighsUndefined,sol[0,2],highspy.kHighsUndefined])
14581458
self.assertEqual(e.data_in.repairSolution(), highspy.HighsStatus.kOk)
1459-
self.assertEqual(list(e.data_in.user_solution[0:8]), [0., 0., 0., 0., 0., 1., 0., 0.])
1459+
self.assertEqual(list(e.data_in.user_solution[0:8]), list(sol[0,0:8]))
14601460

14611461
def try_change_ptr(e):
14621462
e.data_in.user_solution = [0] * (N*N)
1463-
1463+
14641464
self.assertRaises(Exception, lambda: try_change_ptr(e))
14651465

14661466
# modify directly
@@ -1475,6 +1475,10 @@ def try_change_ptr(e):
14751475
self.assertEqual(list(e.data_in.user_solution[0:8]), [0., 0., 0., 0., 0., 0., 1., 0.])
14761476
self.assertEqual(e.data_in.user_has_solution, True)
14771477

1478+
# set partial solution with fractional value
1479+
self.assertEqual(e.data_in.setSolution([0., 0.5]), highspy.HighsStatus.kOk)
1480+
self.assertEqual(e.data_in.repairSolution(), highspy.HighsStatus.kError)
1481+
14781482
# verify partial solution
14791483
h.cbMipUserSolution += partial_solution
14801484

0 commit comments

Comments
 (0)