Skip to content

Commit bb0ec81

Browse files
authored
Merge pull request #3248 from ERGO-Code/fix-dual-flip-after-postsolve
Fix dual flip after postsolve
2 parents bb5f653 + cb317c0 commit bb0ec81

5 files changed

Lines changed: 70 additions & 15 deletions

File tree

check/TestPresolve.cpp

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1154,6 +1154,65 @@ TEST_CASE("add-to-matrix", "[highs_test_presolve]") {
11541154
h.resetGlobalScheduler(true);
11551155
}
11561156

1157+
TEST_CASE("max-lp-dual-postsolve", "[highs_test_presolve]") {
1158+
Highs h;
1159+
h.setOptionValue("output_flag", dev_run);
1160+
1161+
// Start with blending problem
1162+
HighsLp lp;
1163+
lp.model_name_ = "blending";
1164+
lp.num_col_ = 2;
1165+
lp.num_row_ = 2;
1166+
lp.col_cost_ = {8, 10};
1167+
lp.col_lower_ = {0, 0};
1168+
lp.col_upper_ = {inf, inf};
1169+
lp.row_lower_ = {-inf, -inf};
1170+
lp.row_upper_ = {120, 210};
1171+
lp.a_matrix_.format_ = MatrixFormat::kColwise;
1172+
lp.a_matrix_.start_ = {0, 2, 4};
1173+
lp.a_matrix_.index_ = {0, 1, 0, 1};
1174+
lp.a_matrix_.value_ = {0.3, 0.7, 0.5, 0.5};
1175+
lp.sense_ = ObjSense::kMaximize;
1176+
lp.offset_ = 0;
1177+
h.passModel(lp);
1178+
1179+
REQUIRE(h.setOptionValue("solver", kIpxString) == HighsStatus::kOk);
1180+
REQUIRE(h.setOptionValue("run_crossover", kHighsOffString) ==
1181+
HighsStatus::kOk);
1182+
1183+
h.run();
1184+
if (dev_run) h.writeSolution("", kSolutionStylePretty);
1185+
REQUIRE(h.getModelStatus() == HighsModelStatus::kOptimal);
1186+
1187+
double optimal_objective = h.getObjectiveValue();
1188+
HighsSolution solution = h.getSolution();
1189+
1190+
// Add a redundant constraint that's removed in presolve so that
1191+
// postsolve must be performed
1192+
std::vector<HighsInt> index = {0, 1};
1193+
std::vector<double> value = {1, 1};
1194+
REQUIRE(h.addRow(-inf, 1000, 2, index.data(), value.data()) ==
1195+
HighsStatus::kOk);
1196+
h.clearSolver();
1197+
1198+
h.run();
1199+
if (dev_run) h.writeSolution("", kSolutionStylePretty);
1200+
REQUIRE(h.getModelStatus() == HighsModelStatus::kOptimal);
1201+
1202+
REQUIRE(doubleEqual(optimal_objective, h.getObjectiveValue()));
1203+
// Check that the dual values are unchanged for the original problem
1204+
for (HighsInt iCol = 0; iCol < lp.num_col_; iCol++)
1205+
REQUIRE(
1206+
doubleEqual(solution.col_dual[iCol], h.getSolution().col_dual[iCol]));
1207+
for (HighsInt iRow = 0; iRow < lp.num_row_; iRow++)
1208+
REQUIRE(
1209+
doubleEqual(solution.row_dual[iRow], h.getSolution().row_dual[iRow]));
1210+
1211+
REQUIRE(h.getModelStatus() == HighsModelStatus::kOptimal);
1212+
1213+
h.resetGlobalScheduler(true);
1214+
}
1215+
11571216
TEST_CASE("issue-3140", "[highs_test_presolve]") {
11581217
Highs highs;
11591218
highs.setOptionValue("output_flag", dev_run);

highs/ipm/hipo/ipm/Solver.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1213,7 +1213,7 @@ isSuccess Solver::checkTerminationKkt() {
12131213
highs_solution, Hoptions_, "During HiPO solve");
12141214

12151215
if (model_status == HighsModelStatus::kOptimal) {
1216-
logger_.printInfo("Check successfull\n");
1216+
logger_.printInfo("Check successful\n");
12171217
return true;
12181218
} else
12191219
logger_.printInfo("Check failed\n");
@@ -1282,7 +1282,7 @@ isFailure Solver::initialiseLinearSolver() {
12821282
}
12831283

12841284
isSuccess Solver::switchToMultifrontal() {
1285-
bool switch_successfull = false;
1285+
bool switch_successful = false;
12861286

12871287
if (LS_->type() == kUpLookingType && options_.factor == kHighsChooseString) {
12881288
LS_.reset(new FactorHighsSolver(*kkt_, options_, model_, regul_, info_,
@@ -1297,11 +1297,11 @@ isSuccess Solver::switchToMultifrontal() {
12971297
it_->largest_dx_x_ = 0;
12981298
it_->largest_dy_y_ = 0;
12991299
info_.error = kOk;
1300-
switch_successfull = true;
1300+
switch_successful = true;
13011301
}
13021302
}
13031303

1304-
return switch_successfull;
1304+
return switch_successful;
13051305
}
13061306

13071307
void Solver::printHeader() const {

highs/lp_data/Highs.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4009,8 +4009,13 @@ HighsPostsolveStatus Highs::runPostsolve() {
40094009
assert(model_.lp_.a_matrix_.isColwise());
40104010
calculateRowValuesQuad(model_.lp_, presolve_.data_.recovered_solution_);
40114011

4012-
if (have_dual_solution && model_.lp_.sense_ == ObjSense::kMaximize)
4013-
presolve_.negateReducedLpColDuals();
4012+
if (have_dual_solution && model_.lp_.sense_ == ObjSense::kMaximize) {
4013+
// Negate the dual values since the incumbent LP is a maximization
4014+
for (HighsInt iCol = 0; iCol < model_.lp_.num_col_; iCol++)
4015+
presolve_.data_.recovered_solution_.col_dual[iCol] *= -1;
4016+
for (HighsInt iRow = 0; iRow < model_.lp_.num_row_; iRow++)
4017+
presolve_.data_.recovered_solution_.row_dual[iRow] *= -1;
4018+
}
40144019

40154020
// Ensure that the postsolve status is used to set
40164021
// presolve_.postsolve_status_, as well as being returned

highs/presolve/PresolveComponent.cpp

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,6 @@ HighsStatus PresolveComponent::init(const HighsLp& lp, HighsTimer& timer,
2121
return HighsStatus::kOk;
2222
}
2323

24-
void PresolveComponent::negateReducedLpColDuals() {
25-
for (HighsInt col = 0; col < data_.reduced_lp_.num_col_; col++)
26-
data_.recovered_solution_.col_dual[col] =
27-
-data_.recovered_solution_.col_dual[col];
28-
return;
29-
}
30-
3124
HighsPresolveStatus PresolveComponent::run() {
3225
presolve::HPresolve presolve;
3326
presolve.setInput(data_.reduced_lp_, *options_,

highs/presolve/PresolveComponent.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,6 @@ class PresolveComponent : public HighsComponent {
7575
HighsLp& getReducedProblem() { return data_.reduced_lp_; }
7676
HighsPresolveLog& getPresolveLog() { return data_.presolve_log_; }
7777

78-
void negateReducedLpColDuals();
79-
8078
PresolveComponentInfo info_;
8179
PresolveComponentData data_;
8280
const HighsOptions* options_;

0 commit comments

Comments
 (0)