Skip to content

Commit c9bc15b

Browse files
committed
Now warning about KKT errors after removing user scaling
1 parent a8a840d commit c9bc15b

File tree

5 files changed

+71
-32
lines changed

5 files changed

+71
-32
lines changed

check/TestUserScale.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,5 +97,14 @@ TEST_CASE("user-small-cost-scale", "[highs_user_scale]") {
9797
REQUIRE(solution.col_value[0] == 40);
9898
REQUIRE(solution.col_value[1] == 20);
9999

100+
std::string model = "flugpl";
101+
std::string filename =
102+
std::string(HIGHS_DIR) + "/check/instances/" + model + ".mps";
103+
highs.readModel(filename);
104+
105+
REQUIRE(highs.setOptionValue("user_cost_scale", -30) == HighsStatus::kOk);
106+
107+
highs.run();
108+
100109
highs.resetGlobalScheduler(true);
101110
}

highs/lp_data/Highs.cpp

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -988,15 +988,25 @@ HighsStatus Highs::run() {
988988
assert(unscale_status != HighsStatus::kError);
989989
}
990990
const bool update_kkt = true;
991-
this->userScaleSolution(user_scale_data, update_kkt);
991+
unscale_status = this->userScaleSolution(user_scale_data, update_kkt);
992992
// Restore the user scale values, remembering that they've been
993993
// negated to undo user scaling
994994
this->options_.user_cost_scale = -user_scale_data.user_cost_scale;
995995
this->options_.user_bound_scale = -user_scale_data.user_bound_scale;
996996
highsLogUser(this->options_.log_options, HighsLogType::kInfo,
997-
"After solving the user-scaled model the unscaled solution "
997+
"After solving the user-scaled model, the unscaled solution "
998998
"has objective value %.12g\n",
999999
this->info_.objective_function_value);
1000+
if (model_status_ == HighsModelStatus::kOptimal &&
1001+
unscale_status != HighsStatus::kOk) {
1002+
// KKT errors in the unscaled optimal solution, so log a warning and
1003+
// return
1004+
highsLogUser(
1005+
this->options_.log_options, HighsLogType::kWarning,
1006+
"User scaled problem solved to optimality, but unscaled solution "
1007+
"does not satisfy feasibilty and optimality tolerances\n");
1008+
status = HighsStatus::kWarning;
1009+
}
10001010
}
10011011
return status;
10021012
}

highs/lp_data/HighsInterface.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2589,7 +2589,7 @@ HighsStatus Highs::lpKktCheck(const HighsLp& lp, const std::string& message) {
25892589
getLpKktFailures(options, lp, solution, basis_, info, primal_dual_errors,
25902590
get_residuals);
25912591
if (this->model_status_ == HighsModelStatus::kOptimal)
2592-
reportLpKktFailures(lp, options, info, message);
2592+
reportKktFailures(lp, options, info, message);
25932593
// get_residuals is false when there is a valid basis, since
25942594
// residual errors are assumed to be small, so
25952595
// info.num_primal_residual_errors = -1, since they aren't
@@ -3025,7 +3025,10 @@ HighsStatus Highs::userScaleSolution(HighsUserScaleData& data,
30253025
if (!update_kkt) return return_status;
30263026
info_.objective_function_value *= (bound_scale_value * cost_scale_value);
30273027
getKktFailures(options_, model_, solution_, basis_, info_);
3028-
return return_status;
3028+
return reportKktFailures(model_.lp_, options_, info_,
3029+
"After removing user scaling")
3030+
? HighsStatus::kWarning
3031+
: return_status;
30293032
}
30303033

30313034
void HighsIllConditioning::clear() { this->record.clear(); }

highs/lp_data/HighsSolution.cpp

Lines changed: 42 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1706,15 +1706,20 @@ bool isBasisRightSize(const HighsLp& lp, const HighsBasis& basis) {
17061706
basis.row_status.size() == static_cast<size_t>(lp.num_row_);
17071707
}
17081708

1709-
void reportLpKktFailures(const HighsLp& lp, const HighsOptions& options,
1710-
const HighsInfo& info, const std::string& message) {
1709+
bool reportKktFailures(const HighsLp& lp, const HighsOptions& options,
1710+
const HighsInfo& info, const std::string& message) {
17111711
const HighsLogOptions& log_options = options.log_options;
1712+
double mip_feasibility_tolerance = options.mip_feasibility_tolerance;
17121713
double primal_feasibility_tolerance = options.primal_feasibility_tolerance;
17131714
double dual_feasibility_tolerance = options.dual_feasibility_tolerance;
17141715
double primal_residual_tolerance = options.primal_residual_tolerance;
17151716
double dual_residual_tolerance = options.dual_residual_tolerance;
17161717
double optimality_tolerance = options.optimality_tolerance;
1717-
if (options.kkt_tolerance != kDefaultKktTolerance) {
1718+
const bool is_mip = lp.isMip();
1719+
if (is_mip) {
1720+
primal_feasibility_tolerance = mip_feasibility_tolerance;
1721+
} else if (options.kkt_tolerance != kDefaultKktTolerance) {
1722+
mip_feasibility_tolerance = options.kkt_tolerance;
17181723
primal_feasibility_tolerance = options.kkt_tolerance;
17191724
dual_feasibility_tolerance = options.kkt_tolerance;
17201725
primal_residual_tolerance = options.kkt_tolerance;
@@ -1723,34 +1728,44 @@ void reportLpKktFailures(const HighsLp& lp, const HighsOptions& options,
17231728
}
17241729

17251730
const bool force_report = false;
1731+
const bool complementarity_error =
1732+
!is_mip && info.primal_dual_objective_error > optimality_tolerance;
1733+
const bool integrality_error =
1734+
is_mip && info.max_integrality_violation >= mip_feasibility_tolerance;
17261735
const bool has_kkt_failures =
1727-
info.num_primal_infeasibilities > 0 ||
1736+
integrality_error || info.num_primal_infeasibilities > 0 ||
17281737
info.num_dual_infeasibilities > 0 ||
17291738
info.num_primal_residual_errors > 0 ||
1730-
info.num_dual_residual_errors > 0 ||
1731-
info.primal_dual_objective_error > optimality_tolerance;
1732-
if (!has_kkt_failures && !force_report) return;
1739+
info.num_dual_residual_errors > 0 || complementarity_error;
1740+
if (!has_kkt_failures && !force_report) return has_kkt_failures;
17331741

17341742
HighsLogType log_type =
17351743
has_kkt_failures ? HighsLogType::kWarning : HighsLogType::kInfo;
17361744

1737-
highsLogUser(log_options, log_type, "LP solution KKT conditions%s%s\n",
1745+
highsLogUser(log_options, log_type, "Solution optimality conditions%s%s\n",
17381746
message == "" ? "" : ": ", message == "" ? "" : message.c_str());
1739-
1740-
highsLogUser(
1741-
log_options, HighsLogType::kInfo,
1742-
"num/max %6d / %8.3g (relative %6d / %8.3g) primal "
1743-
"infeasibilities (tolerance = %4.0e)\n",
1744-
int(info.num_primal_infeasibilities), info.max_primal_infeasibility,
1745-
int(info.num_relative_primal_infeasibilities),
1746-
info.max_relative_primal_infeasibility, primal_feasibility_tolerance);
1747-
highsLogUser(log_options, HighsLogType::kInfo,
1748-
"num/max %6d / %8.3g (relative %6d / %8.3g) dual "
1749-
"infeasibilities (tolerance = %4.0e)\n",
1750-
int(info.num_dual_infeasibilities), info.max_dual_infeasibility,
1751-
int(info.num_relative_dual_infeasibilities),
1752-
info.max_relative_dual_infeasibility,
1753-
dual_feasibility_tolerance);
1747+
if (is_mip && info.max_integrality_violation >= 0)
1748+
highsLogUser(log_options, HighsLogType::kInfo,
1749+
" max %8.3g "
1750+
"integrality violations"
1751+
" (tolerance = %4.0e)\n",
1752+
info.max_integrality_violation, mip_feasibility_tolerance);
1753+
if (info.num_primal_infeasibilities >= 0)
1754+
highsLogUser(
1755+
log_options, HighsLogType::kInfo,
1756+
"num/max %6d / %8.3g (relative %6d / %8.3g) primal "
1757+
"infeasibilities (tolerance = %4.0e)\n",
1758+
int(info.num_primal_infeasibilities), info.max_primal_infeasibility,
1759+
int(info.num_relative_primal_infeasibilities),
1760+
info.max_relative_primal_infeasibility, primal_feasibility_tolerance);
1761+
if (info.num_dual_infeasibilities >= 0)
1762+
highsLogUser(
1763+
log_options, HighsLogType::kInfo,
1764+
"num/max %6d / %8.3g (relative %6d / %8.3g) dual "
1765+
"infeasibilities (tolerance = %4.0e)\n",
1766+
int(info.num_dual_infeasibilities), info.max_dual_infeasibility,
1767+
int(info.num_relative_dual_infeasibilities),
1768+
info.max_relative_dual_infeasibility, dual_feasibility_tolerance);
17541769
if (info.num_primal_residual_errors >= 0)
17551770
highsLogUser(
17561771
log_options, HighsLogType::kInfo,
@@ -1778,9 +1793,10 @@ void reportLpKktFailures(const HighsLp& lp, const HighsOptions& options,
17781793
info.primal_dual_objective_error, optimality_tolerance);
17791794
}
17801795
if (printf_kkt) {
1781-
printf("grepLpKktFailures,%s,%s,%s,%d,%d,%d,%d,%d,%d,%d,%d,%g\n",
1796+
printf("grepKktFailures,%s,%s,%s,%g,%d,%d,%d,%d,%d,%d,%d,%d,%g\n",
17821797
options.solver.c_str(), lp.model_name_.c_str(),
1783-
lp.origin_name_.c_str(), int(info.num_primal_infeasibilities),
1798+
lp.origin_name_.c_str(), info.max_integrality_violation,
1799+
int(info.num_primal_infeasibilities),
17841800
int(info.num_dual_infeasibilities),
17851801
int(info.num_primal_residual_errors),
17861802
int(info.num_dual_residual_errors),
@@ -1790,6 +1806,7 @@ void reportLpKktFailures(const HighsLp& lp, const HighsOptions& options,
17901806
int(info.num_relative_dual_residual_errors),
17911807
info.primal_dual_objective_error);
17921808
}
1809+
return has_kkt_failures;
17931810
}
17941811

17951812
bool HighsSolution::hasUndefined() const {

highs/lp_data/HighsSolution.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -164,8 +164,8 @@ bool isDualSolutionRightSize(const HighsLp& lp, const HighsSolution& solution);
164164
bool isSolutionRightSize(const HighsLp& lp, const HighsSolution& solution);
165165
bool isBasisRightSize(const HighsLp& lp, const HighsBasis& basis);
166166

167-
void reportLpKktFailures(const HighsLp& lp, const HighsOptions& options,
168-
const HighsInfo& highs_info,
169-
const std::string& message = "");
167+
bool reportKktFailures(const HighsLp& lp, const HighsOptions& options,
168+
const HighsInfo& highs_info,
169+
const std::string& message = "");
170170

171171
#endif // LP_DATA_HIGHSSOLUTION_H_

0 commit comments

Comments
 (0)