Skip to content

Commit 153bd40

Browse files
committed
Added unit test for not clearing solution if row bound changes retain feasibility
1 parent a1ced62 commit 153bd40

File tree

4 files changed

+36
-8
lines changed

4 files changed

+36
-8
lines changed

check/TestBasis.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,13 +335,15 @@ TEST_CASE("Basis-read", "[highs_basis_data]") {
335335
HighsBasisStatus status_before = HighsBasisStatus::kNonbasic;
336336
HighsBasisStatus status_after = HighsBasisStatus::kBasic;
337337
Highs h1;
338+
h1.setOptionValue("output_flag", dev_run);
338339
const HighsBasis& basis1 = h1.getBasis();
339340
h1.passModel(lp);
340341
REQUIRE(basis1.col_status[0] == status_before);
341342
h1.run();
342343
REQUIRE(basis1.col_status[0] == status_after);
343344

344345
Highs h2;
346+
h2.setOptionValue("output_flag", dev_run);
345347
const HighsBasis& basis2 = h2.getBasis();
346348
h2.passModel(lp);
347349
REQUIRE(basis2.col_status[0] == status_before);

check/TestIpm.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ TEST_CASE("test-2527", "[highs_ipm]") {
205205
std::string filename =
206206
std::string(HIGHS_DIR) + "/check/instances/primal1.mps";
207207
Highs h;
208-
// h.setOptionValue("output_flag", dev_run);
208+
h.setOptionValue("output_flag", dev_run);
209209
REQUIRE(h.readModel(filename) == HighsStatus::kOk);
210210
HighsLp lp = h.getLp();
211211
lp.col_cost_.assign(lp.num_col_, 0);

check/TestMipSolver.cpp

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1059,12 +1059,10 @@ TEST_CASE("get-fixed-lp", "[highs_test_mip_solver]") {
10591059
HighsLp mip = h.getLp();
10601060
std::vector<HighsInt> col_set;
10611061
std::vector<double> fixed_value;
1062-
std::vector<HighsVarType> integrality = mip.integrality_;
10631062
for (HighsInt iCol = 0; iCol < mip.num_col_; iCol++) {
10641063
if (mip.integrality_[iCol] == HighsVarType::kInteger) {
10651064
col_set.push_back(iCol);
10661065
fixed_value.push_back(solution.col_value[iCol]);
1067-
integrality[iCol] = HighsVarType::kContinuous;
10681066
}
10691067
}
10701068
h.clearIntegrality();
@@ -1119,7 +1117,7 @@ TEST_CASE("get-fixed-lp", "[highs_test_mip_solver]") {
11191117
REQUIRE(h.readModel(model_file) == HighsStatus::kOk);
11201118
// Perturb one of the integer variables for code coverage of
11211119
// warning: makes fixed LP of flugpl infeasible
1122-
integrality = h.getLp().integrality_;
1120+
std::vector<HighsVarType> integrality = h.getLp().integrality_;
11231121
for (HighsInt iCol = 0; iCol < fixed_lp.num_col_; iCol++) {
11241122
if (integrality[iCol] != HighsVarType::kContinuous) {
11251123
solution.col_value[iCol] -= 0.01;
@@ -1169,3 +1167,25 @@ TEST_CASE("get-fixed-lp-semi", "[highs_test_mip_solver]") {
11691167

11701168
REQUIRE(h.getInfo().objective_function_value == mip_optimal_objective);
11711169
}
1170+
1171+
TEST_CASE("row-fixed-lp", "[highs_test_mip_solver]") {
1172+
std::string model = "flugpl";
1173+
std::string model_file =
1174+
std::string(HIGHS_DIR) + "/check/instances/" + model + ".mps";
1175+
Highs h;
1176+
// h.setOptionValue("output_flag", dev_run);
1177+
REQUIRE(h.readModel(model_file) == HighsStatus::kOk);
1178+
REQUIRE(h.run() == HighsStatus::kOk);
1179+
double mip_optimal_objective = h.getInfo().objective_function_value;
1180+
HighsSolution solution = h.getSolution();
1181+
1182+
HighsLp lp = h.getLp();
1183+
h.clearIntegrality();
1184+
h.changeRowsBounds(0, lp.num_row_ - 1, solution.row_value.data(),
1185+
solution.row_value.data());
1186+
h.setOptionValue("presolve", kHighsOffString);
1187+
REQUIRE(h.run() == HighsStatus::kOk);
1188+
REQUIRE(h.getInfo().objective_function_value <= mip_optimal_objective);
1189+
1190+
h.resetGlobalScheduler(true);
1191+
}

highs/lp_data/HighsInterface.cpp

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -991,9 +991,8 @@ bool Highs::feasibleWrtBounds(const bool columns) const {
991991
const HighsLp& lp = model_.lp_;
992992
const double primal_feasibility_tolerance =
993993
this->options_.primal_feasibility_tolerance;
994-
std::vector<double> value = columns ?
995-
this->solution_.col_value :
996-
this->solution_.row_value;
994+
std::vector<double> value =
995+
columns ? this->solution_.col_value : this->solution_.row_value;
997996
std::vector<double> lower = columns ? lp.col_lower_ : lp.row_lower_;
998997
std::vector<double> upper = columns ? lp.col_upper_ : lp.row_upper_;
999998
HighsInt dim = columns ? lp.num_col_ : lp.num_row_;
@@ -1121,7 +1120,14 @@ HighsStatus Highs::changeRowBoundsInterface(
11211120
// nonbasic variables whose bounds have changed
11221121
setNonbasicStatusInterface(index_collection, false);
11231122
// Deduce the consequences of new row bounds
1124-
invalidateModelStatusSolutionAndInfo();
1123+
if (!this->basis_.useful && feasibleWrtBounds(false)) {
1124+
// Retain the solution if there's no basis, and the solution is
1125+
// feasible
1126+
invalidateModelStatusAndInfo();
1127+
} else {
1128+
// Invalidate the solution
1129+
invalidateModelStatusSolutionAndInfo();
1130+
}
11251131
// Determine any implications for simplex data
11261132
ekk_instance_.updateStatus(LpAction::kNewBounds);
11271133
return HighsStatus::kOk;

0 commit comments

Comments
 (0)