Skip to content

Commit 72eceb6

Browse files
authored
Merge pull request #2417 from ERGO-Code/fix-2415
Fix 2415
2 parents 7ef0e50 + f8b0c5c commit 72eceb6

File tree

2 files changed

+56
-4
lines changed

2 files changed

+56
-4
lines changed

check/TestRays.cpp

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
#include "catch.hpp"
55
#include "lp_data/HConst.h"
66

7-
const bool dev_run = false;
7+
const bool dev_run = true; // false;
88
const double zero_ray_value_tolerance = 1e-14;
99

1010
void reportRay(std::string message, HighsInt dim, double* computed,
@@ -963,3 +963,48 @@ TEST_CASE("Rays-2415", "[highs_test_rays]") {
963963
REQUIRE(h.getInfo().primal_solution_status != kSolutionStatusFeasible);
964964
REQUIRE(h.getInfo().primal_solution_status == kSolutionStatusNone);
965965
}
966+
967+
TEST_CASE("Rays-2415-primal", "[highs_test_rays]") {
968+
HighsLp lp;
969+
lp.num_col_ = 1;
970+
lp.num_row_ = 1;
971+
lp.col_cost_ = {-1};
972+
lp.col_lower_ = {0};
973+
lp.col_upper_ = {kHighsInf};
974+
lp.row_lower_ = {0};
975+
lp.row_upper_ = {kHighsInf};
976+
lp.a_matrix_.start_ = {0, 1};
977+
lp.a_matrix_.index_ = {0};
978+
lp.a_matrix_.value_ = {1};
979+
Highs h;
980+
h.setOptionValue("output_flag", dev_run);
981+
if (dev_run) printf("For unbounded LP\n");
982+
for (HighsInt k = 0; k < 2; k++) {
983+
bool is_mip = k == 1;
984+
HighsInt require_dual_solution_status = kSolutionStatusInfeasible;
985+
if (is_mip) require_dual_solution_status = kSolutionStatusNone;
986+
REQUIRE(h.passModel(lp) == HighsStatus::kOk);
987+
REQUIRE(h.run() == HighsStatus::kOk);
988+
if (dev_run)
989+
printf("Solution values are col_value[0] = %g; row_value[0] = %g\n",
990+
h.getSolution().col_value[0], h.getSolution().row_value[0]);
991+
if (dev_run)
992+
printf("Solution duals are col_dual[0] = %g; row_dual[0] = %g\n",
993+
h.getSolution().col_dual[0], h.getSolution().row_dual[0]);
994+
if (dev_run)
995+
printf("Dual Solution status is %d\n",
996+
int(h.getInfo().dual_solution_status));
997+
REQUIRE(h.getInfo().dual_solution_status != kSolutionStatusFeasible);
998+
REQUIRE(h.getInfo().dual_solution_status == require_dual_solution_status);
999+
1000+
bool has_primal_ray;
1001+
std::vector<double> primal_ray_value(lp.num_col_);
1002+
REQUIRE(h.getPrimalRay(has_primal_ray, primal_ray_value.data()) ==
1003+
HighsStatus::kOk);
1004+
REQUIRE(h.getInfo().dual_solution_status != kSolutionStatusFeasible);
1005+
REQUIRE(h.getInfo().dual_solution_status == require_dual_solution_status);
1006+
if (k == 1) break;
1007+
lp.integrality_ = {HighsVarType::kInteger};
1008+
if (dev_run) printf("\nFor unbounded MIP\n");
1009+
}
1010+
}

highs/lp_data/HighsInterface.cpp

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1774,7 +1774,6 @@ HighsStatus Highs::getPrimalRayInterface(bool& has_primal_ray,
17741774
this->getOptionValue("allow_unbounded_or_infeasible",
17751775
allow_unbounded_or_infeasible);
17761776
solve_unboundedness_problem = true;
1777-
lp.integrality_.clear();
17781777
this->setOptionValue("presolve", kHighsOffString);
17791778
this->setOptionValue("solve_relaxation", true);
17801779
this->setOptionValue("allow_unbounded_or_infeasible", false);
@@ -1835,14 +1834,21 @@ HighsStatus Highs::getPrimalRayInterface(bool& has_primal_ray,
18351834
return_status = HighsStatus::kOk;
18361835
}
18371836
}
1837+
const bool is_mip = this->model_.isMip();
18381838
if (solve_unboundedness_problem) {
1839+
if (is_mip) {
1840+
// Unboundedness LP has been solved, but that will give dual
1841+
// solution status kInfeasible which, for a MIP is not correct
1842+
this->info_.dual_solution_status = SolutionStatus::kSolutionStatusNone;
1843+
this->info_.invalidateDualKkt();
1844+
}
18391845
// Restore the option values
18401846
this->setOptionValue("presolve", presolve);
18411847
this->setOptionValue("solve_relaxation", solve_relaxation);
18421848
this->setOptionValue("allow_unbounded_or_infeasible",
18431849
allow_unbounded_or_infeasible);
18441850
if (has_primal_ray) {
1845-
assert(this->info_.num_dual_infeasibilities > 0);
1851+
assert(is_mip || this->info_.num_dual_infeasibilities > 0);
18461852
assert(this->model_status_ == HighsModelStatus::kUnbounded);
18471853
}
18481854
}
@@ -2602,7 +2608,8 @@ HighsStatus Highs::lpKktCheck(const std::string& message) {
26022608
primal_dual_errors, get_residuals);
26032609
// highsLogUser(options.log_options, HighsLogType::kInfo,
26042610
// "Highs::lpKktCheck: %s\n", message.c_str());
2605-
reportLpKktFailures(model_.lp_, options, info, "LP");
2611+
if (this->model_status_ == HighsModelStatus::kOptimal)
2612+
reportLpKktFailures(model_.lp_, options, info, "LP");
26062613
// get_residuals is false when there is a valid basis, since
26072614
// residual errors are assumed to be small, so
26082615
// info.num_primal_residual_errors = -1, since they aren't

0 commit comments

Comments
 (0)