Skip to content

Commit 7917e73

Browse files
authored
Merge pull request #2517 from ERGO-Code/fix-2515
Logging clean-up
2 parents 13a5068 + 0263073 commit 7917e73

File tree

13 files changed

+183
-145
lines changed

13 files changed

+183
-145
lines changed

check/TestPresolve.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -837,4 +837,4 @@ TEST_CASE("presolve-egout-ac", "[highs_test_presolve]") {
837837
lp_presolve_requires_basis_postsolve);
838838

839839
h.resetGlobalScheduler(true);
840-
}
840+
}

check/TestSemiVariables.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ TEST_CASE("semi-variable-model", "[highs_test_semi_variables]") {
1717
const HighsInfo& info = highs.getInfo();
1818
HighsStatus return_status;
1919
double optimal_objective_function_value;
20-
if (!dev_run) highs.setOptionValue("output_flag", false);
20+
highs.setOptionValue("output_flag", dev_run);
2121
HighsModel model;
2222
HighsLp& lp = model.lp_;
2323
semiModel0(lp);

highs/Highs.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1700,7 +1700,7 @@ class Highs {
17001700
bool qFormatOk(const HighsInt num_nz, const HighsInt format);
17011701
void clearZeroHessian();
17021702
HighsStatus checkOptimality(const std::string& solver_type);
1703-
HighsStatus lpKktCheck(const std::string& message);
1703+
HighsStatus lpKktCheck(const HighsLp& lp, const std::string& message = "");
17041704
HighsStatus invertRequirementError(std::string method_name) const;
17051705

17061706
HighsStatus handleInfCost();

highs/lp_data/Highs.cpp

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -841,8 +841,9 @@ HighsStatus Highs::writeBasis(const std::string& filename) {
841841
}
842842

843843
HighsStatus Highs::presolve() {
844+
const HighsLogOptions& log_options = options_.log_options;
844845
if (model_.needsMods(options_.infinite_cost)) {
845-
highsLogUser(options_.log_options, HighsLogType::kError,
846+
highsLogUser(log_options, HighsLogType::kError,
846847
"Model contains infinite costs or semi-variables, so cannot "
847848
"be presolved independently\n");
848849
return HighsStatus::kError;
@@ -861,7 +862,7 @@ HighsStatus Highs::presolve() {
861862
max_threads = highs::parallel::num_threads();
862863
if (options_.threads != 0 && max_threads != options_.threads) {
863864
highsLogUser(
864-
options_.log_options, HighsLogType::kError,
865+
log_options, HighsLogType::kError,
865866
"Option 'threads' is set to %d but global scheduler has already been "
866867
"initialized to use %d threads. The previous scheduler instance can "
867868
"be destroyed by calling Highs::resetGlobalScheduler().\n",
@@ -873,6 +874,8 @@ HighsStatus Highs::presolve() {
873874
}
874875

875876
bool using_reduced_lp = false;
877+
reportPresolveReductions(log_options, model_presolve_status_, model_.lp_,
878+
presolve_.getReducedProblem());
876879
switch (model_presolve_status_) {
877880
case HighsPresolveStatus::kNotPresolved: {
878881
// Shouldn't happen
@@ -915,7 +918,7 @@ HighsStatus Highs::presolve() {
915918
default: {
916919
// case HighsPresolveStatus::kOutOfMemory
917920
assert(model_presolve_status_ == HighsPresolveStatus::kOutOfMemory);
918-
highsLogUser(options_.log_options, HighsLogType::kError,
921+
highsLogUser(log_options, HighsLogType::kError,
919922
"Presolve fails due to memory allocation error\n");
920923
setHighsModelStatusAndClearSolutionAndBasis(
921924
HighsModelStatus::kPresolveError);
@@ -927,8 +930,7 @@ HighsStatus Highs::presolve() {
927930
presolved_model_.lp_.setMatrixDimensions();
928931
}
929932

930-
highsLogUser(options_.log_options, HighsLogType::kInfo,
931-
"Presolve status: %s\n",
933+
highsLogUser(log_options, HighsLogType::kInfo, "Presolve status: %s\n",
932934
presolveStatusToString(model_presolve_status_).c_str());
933935
return returnFromHighs(return_status);
934936
}
@@ -1400,6 +1402,9 @@ HighsStatus Highs::optimizeModel() {
14001402
// presolved problem, since the iteration count is reset to zero
14011403
// if PDLP is used to clean up after postsolve
14021404
HighsInt presolved_lp_pdlp_iteration_count = 0;
1405+
// Log the presolve reductions
1406+
reportPresolveReductions(log_options, model_presolve_status_, incumbent_lp,
1407+
presolve_.getReducedProblem());
14031408
switch (model_presolve_status_) {
14041409
case HighsPresolveStatus::kNotPresolved: {
14051410
ekk_instance_.lp_name_ = "Original LP";
@@ -1414,7 +1419,6 @@ HighsStatus Highs::optimizeModel() {
14141419
case HighsPresolveStatus::kNotReduced: {
14151420
ekk_instance_.lp_name_ = "Unreduced LP";
14161421
// Log the presolve reductions
1417-
reportPresolveReductions(log_options, incumbent_lp, false);
14181422
solveLp(incumbent_lp, "Problem not reduced by presolve: solving the LP",
14191423
this_solve_original_lp_time);
14201424
return_status = interpretCallStatus(options_.log_options, call_status,
@@ -1454,8 +1458,6 @@ HighsStatus Highs::optimizeModel() {
14541458
return_status,
14551459
"cleanBounds") == HighsStatus::kError)
14561460
return HighsStatus::kError;
1457-
// Log the presolve reductions
1458-
reportPresolveReductions(log_options, incumbent_lp, reduced_lp);
14591461
// Solving the presolved LP with strictly reduced dimensions
14601462
// so ensure that the Ekk instance is cleared
14611463
ekk_instance_.clear();
@@ -1495,7 +1497,6 @@ HighsStatus Highs::optimizeModel() {
14951497
break;
14961498
}
14971499
case HighsPresolveStatus::kReducedToEmpty: {
1498-
reportPresolveReductions(log_options, incumbent_lp, true);
14991500
// Create a trivial optimal solution for postsolve to use
15001501
solution_.clear();
15011502
basis_.clear();
@@ -1625,6 +1626,9 @@ HighsStatus Highs::optimizeModel() {
16251626
presolve_.data_.recovered_solution_ = solution_;
16261627
presolve_.data_.recovered_basis_ = basis_;
16271628

1629+
if (model_presolve_status_ == HighsPresolveStatus::kReduced)
1630+
this->lpKktCheck(presolve_.getReducedProblem(), "Before postsolve");
1631+
16281632
this_postsolve_time = -timer_.read(timer_.postsolve_clock);
16291633
timer_.start(timer_.postsolve_clock);
16301634
HighsPostsolveStatus postsolve_status = runPostsolve();
@@ -1633,8 +1637,11 @@ HighsStatus Highs::optimizeModel() {
16331637
presolve_.info_.postsolve_time = this_postsolve_time;
16341638

16351639
if (postsolve_status == HighsPostsolveStatus::kSolutionRecovered) {
1636-
highsLogDev(log_options, HighsLogType::kVerbose,
1637-
"Postsolve finished\n");
1640+
// Indicate that nontrivial postsolve has been performed
1641+
if (model_presolve_status_ == HighsPresolveStatus::kReduced ||
1642+
model_presolve_status_ == HighsPresolveStatus::kReducedToEmpty)
1643+
highsLogUser(log_options, HighsLogType::kInfo,
1644+
"Performed postsolve\n");
16381645
// Set solution and its status
16391646
solution_.clear();
16401647
solution_ = presolve_.data_.recovered_solution_;
@@ -1646,7 +1653,6 @@ HighsStatus Highs::optimizeModel() {
16461653
// and IPX determined optimality
16471654
solution_.dual_valid = true;
16481655
this->invalidateBasis();
1649-
this->lpKktCheck("After postsolve");
16501656
} else {
16511657
//
16521658
// Hot-start the simplex solver for the incumbent LP
@@ -1779,7 +1785,7 @@ HighsStatus Highs::optimizeModel() {
17791785
// Unless the model status was determined using the strictly reduced LP, the
17801786
// HiGHS info is valid
17811787
if (!no_incumbent_lp_solution_or_basis) {
1782-
this->lpKktCheck("On exit from optimizeModel()");
1788+
this->lpKktCheck(this->model_.lp_);
17831789
info_.valid = true;
17841790
}
17851791

@@ -2308,7 +2314,7 @@ HighsStatus Highs::setSolution(const HighsInt num_entries,
23082314
highsLogUser(options_.log_options, HighsLogType::kWarning,
23092315
"setSolution: User set of indices has %d duplicate%s: last "
23102316
"value used\n",
2311-
int(num_duplicates), num_duplicates > 1 ? "s" : "");
2317+
int(num_duplicates), num_duplicates == 1 ? "" : "s");
23122318
return_status = HighsStatus::kWarning;
23132319
}
23142320

@@ -3522,8 +3528,8 @@ HighsPresolveStatus Highs::runPresolve(const bool force_lp_presolve,
35223528
// Start the MIP solver's timer so that timeout in presolve can be
35233529
// identified
35243530
solver.timer_.start();
3525-
// Only place that HighsMipSolver::runPresolve is called
3526-
solver.runPresolve(options_.presolve_reduction_limit);
3531+
// Only place that HighsMipSolver::runMipPresolve is called
3532+
solver.runMipPresolve(options_.presolve_reduction_limit);
35273533
presolve_return_status = solver.getPresolveStatus();
35283534
// Assign values to data members of presolve_
35293535
presolve_.data_.reduced_lp_ = solver.getPresolvedModel();
@@ -4299,9 +4305,6 @@ HighsStatus Highs::callRunPostsolve(const HighsSolution& solution,
42994305
getKktFailures(this->options_, is_qp, this->model_.lp_,
43004306
this->model_.lp_.col_cost_, this->solution_, this->info_,
43014307
get_residuals);
4302-
highsLogUser(options_.log_options, HighsLogType::kInfo, "\n");
4303-
reportLpKktFailures(this->model_.lp_, this->options_, this->info_,
4304-
"After postsolve");
43054308
if (info_.num_primal_infeasibilities == 0 &&
43064309
info_.num_dual_infeasibilities == 0) {
43074310
model_status_ = HighsModelStatus::kOptimal;

highs/lp_data/HighsInterface.cpp

Lines changed: 27 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,13 @@ void Highs::reportModelStats() const {
5252
if (non_continuous) {
5353
problem_type = "MIQP";
5454
} else {
55-
problem_type = "QP ";
55+
problem_type = "QP";
5656
}
5757
} else {
5858
if (non_continuous) {
59-
problem_type = "MIP ";
59+
problem_type = "MIP";
6060
} else {
61-
problem_type = "LP ";
61+
problem_type = "LP";
6262
}
6363
}
6464
const HighsInt a_num_nz = lp.a_matrix_.numNz();
@@ -67,17 +67,20 @@ void Highs::reportModelStats() const {
6767
highsLogDev(log_options, HighsLogType::kInfo, "%4s : %s\n",
6868
problem_type.c_str(), lp.model_name_.c_str());
6969
highsLogDev(log_options, HighsLogType::kInfo,
70-
"Rows : %" HIGHSINT_FORMAT "\n", lp.num_row_);
70+
"Row%s : %" HIGHSINT_FORMAT "\n", lp.num_row_,
71+
lp.num_row_ == 1 ? "" : "s");
7172
highsLogDev(log_options, HighsLogType::kInfo,
72-
"Cols : %" HIGHSINT_FORMAT "\n", lp.num_col_);
73+
"Col%s : %" HIGHSINT_FORMAT "\n", lp.num_col_,
74+
lp.num_col_ == 1 ? "" : "s");
7375
if (q_num_nz) {
7476
highsLogDev(log_options, HighsLogType::kInfo,
7577
"Matrix Nz : %" HIGHSINT_FORMAT "\n", a_num_nz);
7678
highsLogDev(log_options, HighsLogType::kInfo,
7779
"Hessian Nz: %" HIGHSINT_FORMAT "\n", q_num_nz);
7880
} else {
7981
highsLogDev(log_options, HighsLogType::kInfo,
80-
"Nonzeros : %" HIGHSINT_FORMAT "\n", a_num_nz);
82+
"Nonzero%s : %" HIGHSINT_FORMAT "\n", a_num_nz,
83+
a_num_nz == 1 ? "" : "s");
8184
}
8285
if (num_integer)
8386
highsLogDev(log_options, HighsLogType::kInfo,
@@ -94,15 +97,21 @@ void Highs::reportModelStats() const {
9497
std::stringstream stats_line;
9598
stats_line << problem_type;
9699
if (lp.model_name_.length()) stats_line << " " << lp.model_name_;
97-
stats_line << " has " << lp.num_row_ << " rows; " << lp.num_col_ << " cols";
100+
stats_line << " has " << lp.num_row_ << " row"
101+
<< (lp.num_row_ == 1 ? "" : "s") << "; " << lp.num_col_ << " col"
102+
<< (lp.num_col_ == 1 ? "" : "s");
98103
if (q_num_nz) {
99-
stats_line << "; " << a_num_nz << " matrix nonzeros";
100-
stats_line << "; " << q_num_nz << " Hessian nonzeros";
104+
stats_line << "; " << a_num_nz << " matrix nonzero"
105+
<< (a_num_nz == 1 ? "" : "s");
106+
stats_line << "; " << q_num_nz << " Hessian nonzero"
107+
<< (q_num_nz == 1 ? "" : "s");
101108
} else {
102-
stats_line << "; " << a_num_nz << " nonzeros";
109+
stats_line << "; " << a_num_nz << " nonzero"
110+
<< (a_num_nz == 1 ? "" : "s");
103111
}
104112
if (num_integer)
105-
stats_line << "; " << num_integer << " integer variables (" << num_binary
113+
stats_line << "; " << num_integer << " integer variable"
114+
<< (a_num_nz == 1 ? "" : "s") << " (" << num_binary
106115
<< " binary)";
107116
if (num_semi_continuous)
108117
stats_line << "; " << num_semi_continuous << " semi-continuous variables";
@@ -2637,7 +2646,7 @@ HighsStatus Highs::checkOptimality(const std::string& solver_type) {
26372646
return HighsStatus::kError;
26382647
}
26392648

2640-
HighsStatus Highs::lpKktCheck(const std::string& message) {
2649+
HighsStatus Highs::lpKktCheck(const HighsLp& lp, const std::string& message) {
26412650
if (!this->solution_.value_valid) return HighsStatus::kOk;
26422651
// Must have dual values for an LP if there are primal values
26432652
assert(this->solution_.dual_valid);
@@ -2657,16 +2666,13 @@ HighsStatus Highs::lpKktCheck(const std::string& message) {
26572666
dual_residual_tolerance = options.kkt_tolerance;
26582667
optimality_tolerance = options.kkt_tolerance;
26592668
}
2660-
info.objective_function_value =
2661-
model_.lp_.objectiveValue(solution_.col_value);
2669+
info.objective_function_value = lp.objectiveValue(solution_.col_value);
26622670
HighsPrimalDualErrors primal_dual_errors;
26632671
const bool get_residuals = !basis_.valid;
2664-
getLpKktFailures(options, model_.lp_, solution, basis_, info,
2665-
primal_dual_errors, get_residuals);
2666-
// highsLogUser(options.log_options, HighsLogType::kInfo,
2667-
// "Highs::lpKktCheck: %s\n", message.c_str());
2672+
getLpKktFailures(options, lp, solution, basis_, info, primal_dual_errors,
2673+
get_residuals);
26682674
if (this->model_status_ == HighsModelStatus::kOptimal)
2669-
reportLpKktFailures(model_.lp_, options, info);
2675+
reportLpKktFailures(lp, options, info, message);
26702676
// get_residuals is false when there is a valid basis, since
26712677
// residual errors are assumed to be small, so
26722678
// info.num_primal_residual_errors = -1, since they aren't
@@ -2728,7 +2734,7 @@ HighsStatus Highs::lpKktCheck(const std::string& message) {
27282734
if (info.primal_dual_objective_error > optimality_tolerance) {
27292735
// Ignore primal-dual objective errors if both objectives are small
27302736
const bool ok_dual_objective = computeDualObjectiveValue(
2731-
nullptr, this->model_.lp_, this->solution_, local_dual_objective);
2737+
nullptr, lp, this->solution_, local_dual_objective);
27322738
assert(ok_dual_objective);
27332739
if (info.objective_function_value * info.objective_function_value >
27342740
optimality_tolerance &&
@@ -2922,6 +2928,7 @@ HighsStatus Highs::lpKktCheck(const std::string& message) {
29222928
highsLogUser(log_options, HighsLogType::kWarning,
29232929
"Model status changed from \"Unknown\" to \"Optimal\"\n");
29242930
}
2931+
highsLogUser(log_options, HighsLogType::kInfo, "\n");
29252932
return HighsStatus::kOk;
29262933
}
29272934

0 commit comments

Comments
 (0)