Skip to content

Commit cd1e016

Browse files
committed
Verified that changeColsBounds clears solution, and suppressing presolve reduction statement when presolve is off
1 parent 2a59e07 commit cd1e016

File tree

3 files changed

+69
-10
lines changed

3 files changed

+69
-10
lines changed

check/TestMipSolver.cpp

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1055,17 +1055,57 @@ TEST_CASE("get-fixed-lp", "[highs_test_mip_solver]") {
10551055
double mip_optimal_objective = h.getInfo().objective_function_value;
10561056
HighsSolution solution = h.getSolution();
10571057

1058+
// Transform the incumbent MIP into the fixed LP
1059+
HighsLp mip = h.getLp();
1060+
std::vector<HighsInt> col_set;
1061+
std::vector<double> fixed_value;
1062+
std::vector<HighsVarType> integrality = mip.integrality_;
1063+
for (HighsInt iCol = 0; iCol < mip.num_col_; iCol++) {
1064+
if (mip.integrality_[iCol] == HighsVarType::kInteger) {
1065+
col_set.push_back(iCol);
1066+
fixed_value.push_back(solution.col_value[iCol]);
1067+
integrality[iCol] = HighsVarType::kContinuous;
1068+
}
1069+
}
1070+
h.clearIntegrality();
1071+
HighsInt num_set_entries = col_set.size();
1072+
h.changeColsBounds(num_set_entries, col_set.data(), fixed_value.data(),
1073+
fixed_value.data());
1074+
h.setOptionValue("presolve", kHighsOffString);
1075+
REQUIRE(h.run() == HighsStatus::kOk);
1076+
1077+
REQUIRE(h.getInfo().objective_function_value == mip_optimal_objective);
1078+
// In calling changeColsBounds, the incumbent solution is cleared,
1079+
// so there is no information from which to construct an advanced
1080+
// basis. Hence simplex starts from a logical basis and requires a
1081+
// positive number of iterations (#2556)
1082+
REQUIRE(h.getInfo().simplex_iteration_count > 0);
1083+
1084+
// Now, passing the MIP solution, there is information from which to
1085+
// construct an advanced basis. In the case of flugpl, this is
1086+
// optimal, so no simplex iterations are required
1087+
h.clearSolver();
1088+
h.setSolution(solution);
1089+
REQUIRE(h.run() == HighsStatus::kOk);
1090+
1091+
REQUIRE(h.getInfo().objective_function_value == mip_optimal_objective);
1092+
REQUIRE(h.getInfo().simplex_iteration_count == 0);
1093+
1094+
// Now re-load the MIP, re-solve, and get the fixed LP
1095+
REQUIRE(h.passModel(mip) == HighsStatus::kOk);
1096+
REQUIRE(h.run() == HighsStatus::kOk);
1097+
REQUIRE(h.getInfo().objective_function_value == mip_optimal_objective);
1098+
10581099
REQUIRE(h.getFixedLp(fixed_lp) == HighsStatus::kOk);
10591100

10601101
REQUIRE(h.passModel(fixed_lp) == HighsStatus::kOk);
10611102
REQUIRE(h.run() == HighsStatus::kOk);
10621103

10631104
REQUIRE(h.getInfo().objective_function_value == mip_optimal_objective);
10641105

1065-
// Now run from saved solution without presolve
1106+
// Now run from saved solution (without presolve)
10661107
h.clearSolver();
10671108
h.setSolution(solution);
1068-
h.setOptionValue("presolve", kHighsOffString);
10691109
REQUIRE(h.run() == HighsStatus::kOk);
10701110

10711111
REQUIRE(h.getInfo().objective_function_value == mip_optimal_objective);
@@ -1074,7 +1114,7 @@ TEST_CASE("get-fixed-lp", "[highs_test_mip_solver]") {
10741114
REQUIRE(h.readModel(model_file) == HighsStatus::kOk);
10751115
// Perturb one of the integer variables for code coverage of
10761116
// warning: makes fixed LP of flugpl infeasible
1077-
std::vector<HighsVarType> integrality = h.getLp().integrality_;
1117+
integrality = h.getLp().integrality_;
10781118
for (HighsInt iCol = 0; iCol < fixed_lp.num_col_; iCol++) {
10791119
if (integrality[iCol] != HighsVarType::kContinuous) {
10801120
solution.col_value[iCol] -= 0.01;

highs/lp_data/Highs.cpp

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1350,18 +1350,36 @@ HighsStatus Highs::optimizeModel() {
13501350
}
13511351
if (basis_.valid) assert(basis_.useful);
13521352

1353-
if ((has_basis || options_.presolve == kHighsOffString || unconstrained_lp) &&
1353+
const bool without_presolve = options_.presolve == kHighsOffString;
1354+
if ((unconstrained_lp || has_basis || without_presolve) &&
13541355
solver_will_use_basis) {
13551356
// There is a valid basis for the problem, presolve is off, or LP
13561357
// has no constraint matrix, and the solver will use the basis
1357-
ekk_instance_.lp_name_ =
1358-
"LP without presolve, or with basis, or unconstrained";
1358+
// (otherwise it's better to use presolve, if it's not switched
1359+
// off)
1360+
//
1361+
// Determine a coherent message about how the LP is being solved
1362+
std::stringstream lp_solve_ss;
1363+
if (unconstrained_lp) {
1364+
lp_solve_ss << "Solving unconstrained LP";
1365+
} else if (has_basis) {
1366+
if (without_presolve) {
1367+
lp_solve_ss << "Solving LP with useful basis";
1368+
} else {
1369+
lp_solve_ss << "Solving LP with useful basis so presolve not used";
1370+
}
1371+
} else {
1372+
// One of unconstrained_lp, has_basis and without_presolve must
1373+
// be true, and the first two anren't
1374+
assert(without_presolve);
1375+
lp_solve_ss << "Solving LP without presolve or useful basis";
1376+
}
1377+
std::string lp_solve = lp_solve_ss.str();
1378+
ekk_instance_.lp_name_ = lp_solve;
13591379
// If there is a valid HiGHS basis, refine any status values that
13601380
// are simply HighsBasisStatus::kNonbasic
13611381
if (basis_.useful) refineBasis(incumbent_lp, solution_, basis_);
1362-
solveLp(incumbent_lp,
1363-
"Solving LP without presolve, or with basis, or unconstrained",
1364-
this_solve_original_lp_time);
1382+
solveLp(incumbent_lp, lp_solve, this_solve_original_lp_time);
13651383
return_status = interpretCallStatus(options_.log_options, call_status,
13661384
return_status, "callSolveLp");
13671385
if (return_status == HighsStatus::kError)

highs/mip/HighsMipSolverData.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -709,7 +709,8 @@ void HighsMipSolverData::runMipPresolve(
709709
}
710710
mipsolver.timer_.stop(mipsolver.timer_.presolve_clock);
711711

712-
if (numRestarts == 0)
712+
// Report the final presolve reductions unless this is a restart
713+
if (mipsolver.options_mip_->presolve != kHighsOffString && numRestarts == 0)
713714
reportPresolveReductions(mipsolver.options_mip_->log_options,
714715
presolve_status, *mipsolver.orig_model_,
715716
*mipsolver.model_);

0 commit comments

Comments
 (0)