Skip to content

Commit

Permalink
Now performing inward integer rounding of column bounds; bug fix in i…
Browse files Browse the repository at this point in the history
…nfeasibleBoundsOk(); unit tests changed where model status changes with inward integer rounding
  • Loading branch information
jajhall committed Dec 11, 2024
1 parent b71a845 commit c7e8375
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 27 deletions.
28 changes: 18 additions & 10 deletions check/TestMipSolver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -371,24 +371,19 @@ TEST_CASE("MIP-unbounded", "[highs_test_mip_solver]") {

// Now as a MIP - infeasible
lp.integrality_ = {HighsVarType::kContinuous, HighsVarType::kInteger};
// With(out) presolve, Highs::infeasibleBoundsOk() performs inwardd
// With(out) presolve, Highs::infeasibleBoundsOk() performs inward
// integer rounding of [0.25, 0.75] to [1, 0] so identifes
// infeasiblility. Hence MIP solver returns
// HighsModelStatus::kInfeasible

// Run with presolve off so that this example still propagates to FJ
highs.setOptionValue("output_flag", true);
highs.setOptionValue("presolve", kHighsOffString);

return_status = highs.passModel(lp);
REQUIRE(return_status == HighsStatus::kOk);

return_status = highs.run();
REQUIRE(return_status == HighsStatus::kOk);

model_status = highs.getModelStatus();
REQUIRE(model_status == HighsModelStatus::kUnboundedOrInfeasible);
// REQUIRE(model_status == HighsModelStatus::kInfeasible);
REQUIRE(model_status == HighsModelStatus::kInfeasible);
}

TEST_CASE("MIP-od", "[highs_test_mip_solver]") {
Expand Down Expand Up @@ -683,9 +678,22 @@ TEST_CASE("IP-infeasible-unbounded", "[highs_test_mip_solver]") {
required_model_status = HighsModelStatus::kUnbounded;
}
} else {
// Presolve on, and identifies primal infeasible or unbounded
required_model_status = HighsModelStatus::kUnboundedOrInfeasible;
// Presolve on
if (l == 0) {
// Inward integer rounding proves infeasiblilty
required_model_status = HighsModelStatus::kInfeasible;
} else {
// Presolve identifies primal infeasible or unbounded
required_model_status = HighsModelStatus::kUnboundedOrInfeasible;
}
}
if (dev_run)
printf(
"For k = %d and l = %d, original bounds on col 1 are [%g, %g]: "
"model status is \"%s\" and required status is \"%s\"\n",
int(k), int(l), lp.col_lower_[1], lp.col_upper_[1],
highs.modelStatusToString(highs.getModelStatus()).c_str(),
highs.modelStatusToString(required_model_status).c_str());
REQUIRE(highs.getModelStatus() == required_model_status);
}
highs.setOptionValue("presolve", kHighsOnString);
Expand All @@ -694,8 +702,8 @@ TEST_CASE("IP-infeasible-unbounded", "[highs_test_mip_solver]") {

TEST_CASE("IP-with-fract-bounds-no-presolve", "[highs_test_mip_solver]") {
Highs highs;
// No presolve
highs.setOptionValue("output_flag", dev_run);
// No presolve
highs.setOptionValue("presolve", "off");

// IP without constraints and fractional bounds on variables
Expand Down
2 changes: 1 addition & 1 deletion check/TestPresolve.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -616,7 +616,7 @@ TEST_CASE("presolve-slacks", "[highs_test_presolve]") {
lp.a_matrix_.index_ = {0, 0};
lp.a_matrix_.value_ = {1, 1};
Highs h;
// h.setOptionValue("output_flag", dev_run);
h.setOptionValue("output_flag", dev_run);
REQUIRE(h.passModel(lp) == HighsStatus::kOk);
REQUIRE(h.presolve() == HighsStatus::kOk);
REQUIRE(h.getPresolvedLp().num_col_ == 0);
Expand Down
10 changes: 5 additions & 5 deletions src/lp_data/HStruct.h
Original file line number Diff line number Diff line change
Expand Up @@ -145,12 +145,12 @@ struct HighsIllConditioning {
};

struct HighsLinearObjective {
double weight;
double offset;
double weight = 0;
double offset = 0;
std::vector<double> coefficients;
double abs_tolerance;
double rel_tolerance;
HighsInt priority;
double abs_tolerance = -1;
double rel_tolerance = -1;
HighsInt priority = 0;
void clear();
};

Expand Down
30 changes: 19 additions & 11 deletions src/lp_data/HighsInterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3512,12 +3512,17 @@ bool Highs::infeasibleBoundsOk() {
HighsInt num_true_infeasible_bound = 0;
HighsInt num_ok_infeasible_bound = 0;
const bool has_integrality = lp.integrality_.size() > 0;
bool performed_inward_integer_rounding = false;
// Lambda for assessing infeasible bounds
auto assessInfeasibleBound = [&](const std::string type, const HighsInt iX,
double& lower, double& upper) {
double range = upper - lower;
if (range >= 0) return true;
if (range > -this->options_.primal_feasibility_tolerance) {
// Infeasibility is less than feasibility tolerance, so fix
// bounds at lower (upper) if lower (upper) is an integer - and
// both can't be integer, otherwise the range <= -1 - otherwise
// fix at 0.5 * (lower + upper)
num_ok_infeasible_bound++;
bool report = num_ok_infeasible_bound <= 10;
bool integer_lower = lower == std::floor(lower + 0.5);
Expand Down Expand Up @@ -3549,21 +3554,22 @@ bool Highs::infeasibleBoundsOk() {
}
return true;
}
// Infeasibility is greater than feasibility tolerance, so report
// this (up to 10 times)
num_true_infeasible_bound++;
if (num_true_infeasible_bound <= 10)
highsLogUser(log_options, HighsLogType::kInfo,
"%s %d bounds [%g, %g] have excessive infeasibility = %g\n",
type.c_str(), int(iX), lower, upper, range);
highsLogUser(
log_options, HighsLogType::kInfo,
"%s %d bounds [%g, %g] have excessive infeasibility = %g%s\n",
type.c_str(), int(iX), lower, upper, range,
performed_inward_integer_rounding ? " due to inward integer rounding"
: "");
return false;
};

const bool perform_inward_integer_rounding =
!this->options_.solve_relaxation && false;
if (!perform_inward_integer_rounding)
printf(
"Highs::infeasibleBoundsOk() Not performing inward integer rounding of "
"bounds\n");
const bool perform_inward_integer_rounding = !this->options_.solve_relaxation;
for (HighsInt iCol = 0; iCol < lp.num_col_; iCol++) {
performed_inward_integer_rounding = false;
double lower = lp.col_lower_[iCol];
double upper = lp.col_upper_[iCol];
if (has_integrality) {
Expand All @@ -3575,9 +3581,11 @@ bool Highs::infeasibleBoundsOk() {
lp.integrality_[iCol] == HighsVarType::kInteger) {
// Assess bounds after inward integer rounding
double integer_lower = std::ceil(lower);
double integer_upper = std::floor(lower);
double integer_upper = std::floor(upper);
assert(integer_lower >= lower);
assert(integer_upper <= upper);
performed_inward_integer_rounding =
integer_lower > lower || integer_upper < upper;
lower = integer_lower;
upper = integer_upper;
}
Expand Down Expand Up @@ -3718,7 +3726,7 @@ HighsStatus Highs::multiobjectiveSolve() {
"%2d %11.6g %11.6g %11.6g %11.6g %11d ", int(iObj),
linear_objective.weight, linear_objective.offset,
linear_objective.abs_tolerance, linear_objective.rel_tolerance,
linear_objective.priority);
int(linear_objective.priority));
if (lp.num_col_ < coeff_logging_size_limit) {
for (HighsInt iCol = 0; iCol < lp.num_col_; iCol++)
*multi_objective_log << highsFormatToString(
Expand Down

0 comments on commit c7e8375

Please sign in to comment.