Skip to content

Commit b3fee7c

Browse files
authored
Merge pull request #2498 from ERGO-Code/no-col-dual
More informative logging for error checking and KKT errors in Highs::postsolve
2 parents 98d5740 + cfd986d commit b3fee7c

File tree

5 files changed

+88
-16
lines changed

5 files changed

+88
-16
lines changed

check/TestPresolve.cpp

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -735,6 +735,32 @@ TEST_CASE("presolve-issue-2446", "[highs_test_presolve]") {
735735
REQUIRE(highs.getModelPresolveStatus() == HighsPresolveStatus::kReduced);
736736
}
737737

738+
TEST_CASE("presolve-solve-postsolve-no-col-dual", "[highs_test_presolve]") {
739+
Highs highs;
740+
highs.setOptionValue("output_flag", dev_run);
741+
std::string model_file =
742+
std::string(HIGHS_DIR) + "/check/instances/afiro.mps";
743+
highs.readModel(model_file);
744+
highs.presolve();
745+
HighsLp presolved_lp = highs.getPresolvedLp();
746+
Highs highs1;
747+
highs1.setOptionValue("output_flag", dev_run);
748+
highs1.setOptionValue("presolve", kHighsOffString);
749+
highs1.passModel(presolved_lp);
750+
highs1.run();
751+
HighsSolution solution = highs1.getSolution();
752+
753+
// Perform postsolve using the optimal solution and basis for the
754+
// presolved model
755+
REQUIRE(highs.postsolve(solution) == HighsStatus::kOk);
756+
757+
// If row duals are supplied, then column duals must also be suppplied
758+
solution.col_dual.clear();
759+
REQUIRE(highs.postsolve(solution) == HighsStatus::kError);
760+
761+
highs.resetGlobalScheduler(true);
762+
}
763+
738764
TEST_CASE("presolve-egout-ac", "[highs_test_presolve]") {
739765
// Tests the case where, for this model when run_crossover is off,
740766
// sparsify is used to reduce the LP to empty. However, when
@@ -811,4 +837,4 @@ TEST_CASE("presolve-egout-ac", "[highs_test_presolve]") {
811837
lp_presolve_requires_basis_postsolve);
812838

813839
h.resetGlobalScheduler(true);
814-
}
840+
}

highs/lp_data/Highs.cpp

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4116,8 +4116,10 @@ HighsStatus Highs::callRunPostsolve(const HighsSolution& solution,
41164116

41174117
// Must at least have a primal column solution of the right size
41184118
if (HighsInt(solution.col_value.size()) != presolved_lp.num_col_) {
4119-
highsLogUser(options_.log_options, HighsLogType::kError,
4120-
"Primal solution provided to postsolve is incorrect size\n");
4119+
highsLogUser(
4120+
options_.log_options, HighsLogType::kError,
4121+
"Primal solution provided to postsolve is of size %d rather than %d\n",
4122+
int(solution.col_value.size()), int(presolved_lp.num_col_));
41214123
return HighsStatus::kError;
41224124
}
41234125
// Check any basis that is supplied
@@ -4179,7 +4181,7 @@ HighsStatus Highs::callRunPostsolve(const HighsSolution& solution,
41794181
"Postsolve performed for MIP, but model status cannot be known\n");
41804182
} else {
41814183
highsLogUser(options_.log_options, HighsLogType::kError,
4182-
"Postsolve return status is %d\n", (int)postsolve_status);
4184+
"Postsolve return status is %d\n", int(postsolve_status));
41834185
setHighsModelStatusAndClearSolutionAndBasis(
41844186
HighsModelStatus::kPostsolveError);
41854187
}
@@ -4194,10 +4196,20 @@ HighsStatus Highs::callRunPostsolve(const HighsSolution& solution,
41944196
presolve_.data_.recovered_solution_.row_dual.size() > 0 ||
41954197
presolve_.data_.recovered_solution_.dual_valid;
41964198
if (dual_supplied) {
4197-
if (!isDualSolutionRightSize(presolved_lp,
4198-
presolve_.data_.recovered_solution_)) {
4199+
if (!isRowDualSolutionRightSize(presolved_lp,
4200+
presolve_.data_.recovered_solution_)) {
4201+
highsLogUser(options_.log_options, HighsLogType::kError,
4202+
"Row dual solution provided to postsolve is of size %d "
4203+
"rather than %d\n",
4204+
int(solution.row_dual.size()), int(presolved_lp.num_row_));
4205+
return HighsStatus::kError;
4206+
}
4207+
if (!isColDualSolutionRightSize(presolved_lp,
4208+
presolve_.data_.recovered_solution_)) {
41994209
highsLogUser(options_.log_options, HighsLogType::kError,
4200-
"Dual solution provided to postsolve is incorrect size\n");
4210+
"Column dual solution provided to postsolve is of size %d "
4211+
"rather than %d\n",
4212+
int(solution.col_dual.size()), int(presolved_lp.num_col_));
42014213
return HighsStatus::kError;
42024214
}
42034215
presolve_.data_.recovered_solution_.dual_valid = true;
@@ -4288,6 +4300,9 @@ HighsStatus Highs::callRunPostsolve(const HighsSolution& solution,
42884300
getKktFailures(this->options_, is_qp, this->model_.lp_,
42894301
this->model_.lp_.col_cost_, this->solution_, this->info_,
42904302
get_residuals);
4303+
highsLogUser(options_.log_options, HighsLogType::kInfo, "\n");
4304+
reportLpKktFailures(this->model_.lp_, this->options_, this->info_,
4305+
"After postsolve");
42914306
if (info_.num_primal_infeasibilities == 0 &&
42924307
info_.num_dual_infeasibilities == 0) {
42934308
model_status_ = HighsModelStatus::kOptimal;
@@ -4296,7 +4311,7 @@ HighsStatus Highs::callRunPostsolve(const HighsSolution& solution,
42964311
}
42974312
highsLogUser(
42984313
options_.log_options, HighsLogType::kInfo,
4299-
"Pure postsolve yields primal %ssolution, but no basis: model "
4314+
"\nPure postsolve yields primal %ssolution, but no basis: model "
43004315
"status is %s\n",
43014316
solution_.dual_valid ? "and dual " : "",
43024317
modelStatusToString(model_status_).c_str());

highs/lp_data/HighsInterface.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2665,7 +2665,7 @@ HighsStatus Highs::lpKktCheck(const std::string& message) {
26652665
// highsLogUser(options.log_options, HighsLogType::kInfo,
26662666
// "Highs::lpKktCheck: %s\n", message.c_str());
26672667
if (this->model_status_ == HighsModelStatus::kOptimal)
2668-
reportLpKktFailures(model_.lp_, options, info, "LP");
2668+
reportLpKktFailures(model_.lp_, options, info);
26692669
// get_residuals is false when there is a valid basis, since
26702670
// residual errors are assumed to be small, so
26712671
// info.num_primal_residual_errors = -1, since they aren't

highs/lp_data/HighsSolution.cpp

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1665,15 +1665,35 @@ bool isBasisConsistent(const HighsLp& lp, const HighsBasis& basis) {
16651665
return num_basic_variables == lp.num_row_;
16661666
}
16671667

1668+
bool isColPrimalSolutionRightSize(const HighsLp& lp,
1669+
const HighsSolution& solution) {
1670+
return solution.col_value.size() == static_cast<size_t>(lp.num_col_);
1671+
}
1672+
1673+
bool isRowPrimalSolutionRightSize(const HighsLp& lp,
1674+
const HighsSolution& solution) {
1675+
return solution.row_value.size() == static_cast<size_t>(lp.num_row_);
1676+
}
1677+
16681678
bool isPrimalSolutionRightSize(const HighsLp& lp,
16691679
const HighsSolution& solution) {
1670-
return solution.col_value.size() == static_cast<size_t>(lp.num_col_) &&
1671-
solution.row_value.size() == static_cast<size_t>(lp.num_row_);
1680+
return isColPrimalSolutionRightSize(lp, solution) &&
1681+
isRowPrimalSolutionRightSize(lp, solution);
1682+
}
1683+
1684+
bool isColDualSolutionRightSize(const HighsLp& lp,
1685+
const HighsSolution& solution) {
1686+
return solution.col_dual.size() == static_cast<size_t>(lp.num_col_);
1687+
}
1688+
1689+
bool isRowDualSolutionRightSize(const HighsLp& lp,
1690+
const HighsSolution& solution) {
1691+
return solution.row_dual.size() == static_cast<size_t>(lp.num_row_);
16721692
}
16731693

16741694
bool isDualSolutionRightSize(const HighsLp& lp, const HighsSolution& solution) {
1675-
return solution.col_dual.size() == static_cast<size_t>(lp.num_col_) &&
1676-
solution.row_dual.size() == static_cast<size_t>(lp.num_row_);
1695+
return isColDualSolutionRightSize(lp, solution) &&
1696+
isRowDualSolutionRightSize(lp, solution);
16771697
}
16781698

16791699
bool isSolutionRightSize(const HighsLp& lp, const HighsSolution& solution) {
@@ -1687,7 +1707,7 @@ bool isBasisRightSize(const HighsLp& lp, const HighsBasis& basis) {
16871707
}
16881708

16891709
void reportLpKktFailures(const HighsLp& lp, const HighsOptions& options,
1690-
const HighsInfo& info, const std::string& solver) {
1710+
const HighsInfo& info, const std::string& message) {
16911711
const HighsLogOptions& log_options = options.log_options;
16921712
double primal_feasibility_tolerance = options.primal_feasibility_tolerance;
16931713
double dual_feasibility_tolerance = options.dual_feasibility_tolerance;
@@ -1714,7 +1734,8 @@ void reportLpKktFailures(const HighsLp& lp, const HighsOptions& options,
17141734
HighsLogType log_type =
17151735
has_kkt_failures ? HighsLogType::kWarning : HighsLogType::kInfo;
17161736

1717-
highsLogUser(log_options, log_type, "LP solution KKT conditions\n");
1737+
highsLogUser(log_options, log_type, "LP solution KKT conditions%s%s\n",
1738+
message == "" ? "" : ": ", message == "" ? "" : message.c_str());
17181739

17191740
highsLogUser(
17201741
log_options, HighsLogType::kInfo,

highs/lp_data/HighsSolution.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,14 +148,24 @@ void resetModelStatusAndHighsInfo(HighsModelStatus& model_status,
148148
HighsInfo& highs_info);
149149
bool isBasisConsistent(const HighsLp& lp, const HighsBasis& basis);
150150

151+
bool isColPrimalSolutionRightSize(const HighsLp& lp,
152+
const HighsSolution& solution);
153+
bool isRowPrimalSolutionRightSize(const HighsLp& lp,
154+
const HighsSolution& solution);
151155
bool isPrimalSolutionRightSize(const HighsLp& lp,
152156
const HighsSolution& solution);
157+
158+
bool isColDualSolutionRightSize(const HighsLp& lp,
159+
const HighsSolution& solution);
160+
bool isRowDualSolutionRightSize(const HighsLp& lp,
161+
const HighsSolution& solution);
153162
bool isDualSolutionRightSize(const HighsLp& lp, const HighsSolution& solution);
163+
154164
bool isSolutionRightSize(const HighsLp& lp, const HighsSolution& solution);
155165
bool isBasisRightSize(const HighsLp& lp, const HighsBasis& basis);
156166

157167
void reportLpKktFailures(const HighsLp& lp, const HighsOptions& options,
158168
const HighsInfo& highs_info,
159-
const std::string& solver = "");
169+
const std::string& message = "");
160170

161171
#endif // LP_DATA_HIGHSSOLUTION_H_

0 commit comments

Comments
 (0)