diff --git a/check/TestMipSolver.cpp b/check/TestMipSolver.cpp index eeba016a62..0249725857 100644 --- a/check/TestMipSolver.cpp +++ b/check/TestMipSolver.cpp @@ -677,8 +677,17 @@ TEST_CASE("IP-infeasible-unbounded", "[highs_test_mip_solver]") { // Solve highs.passModel(lp); highs.run(); - REQUIRE(highs.getModelStatus() == - HighsModelStatus::kUnboundedOrInfeasible); + HighsModelStatus required_model_status; + if (k == 0) { + if (l == 0) { + required_model_status = HighsModelStatus::kInfeasible; + } else { + required_model_status = HighsModelStatus::kUnbounded; + } + } else { + required_model_status = HighsModelStatus::kUnboundedOrInfeasible; + } + REQUIRE(highs.getModelStatus() == required_model_status); } highs.setOptionValue("presolve", kHighsOnString); } diff --git a/src/mip/HighsMipSolver.cpp b/src/mip/HighsMipSolver.cpp index e339ee0863..8482442e35 100644 --- a/src/mip/HighsMipSolver.cpp +++ b/src/mip/HighsMipSolver.cpp @@ -177,7 +177,14 @@ void HighsMipSolver::run() { } // Apply the trivial heuristics analysis_.mipTimerStart(kMipClockTrivialHeuristics); - mipdata_->trivialHeuristics(); + HighsModelStatus model_status = mipdata_->trivialHeuristics(); + if (modelstatus_ == HighsModelStatus::kNotset && + model_status == HighsModelStatus::kInfeasible) { + // trivialHeuristics can spot trivial infeasibility, so act on it + modelstatus_ = model_status; + cleanupSolve(); + return; + } analysis_.mipTimerStop(kMipClockTrivialHeuristics); if (analysis_.analyse_mip_time & !submip) highsLogUser(options_mip_->log_options, HighsLogType::kInfo, diff --git a/src/mip/HighsMipSolverData.cpp b/src/mip/HighsMipSolverData.cpp index 224ea05aa2..aadcf4ab5f 100644 --- a/src/mip/HighsMipSolverData.cpp +++ b/src/mip/HighsMipSolverData.cpp @@ -166,7 +166,7 @@ bool HighsMipSolverData::solutionRowFeasible( return true; } -void HighsMipSolverData::trivialHeuristics() { +HighsModelStatus HighsMipSolverData::trivialHeuristics() { // printf("\nHighsMipSolverData::trivialHeuristics() Number of continuous // columns is %d\n", // int(continuous_cols.size())); @@ -176,8 +176,8 @@ void HighsMipSolverData::trivialHeuristics() { kSolutionSourceTrivialZ, kSolutionSourceTrivialL, kSolutionSourceTrivialU, kSolutionSourceTrivialP}; - const std::vector& col_lower = mipsolver.model_->col_lower_; - const std::vector& col_upper = mipsolver.model_->col_upper_; + std::vector col_lower = mipsolver.model_->col_lower_; + std::vector col_upper = mipsolver.model_->col_upper_; const std::vector& row_lower = mipsolver.model_->row_lower_; const std::vector& row_upper = mipsolver.model_->row_upper_; const HighsSparseMatrix& matrix = mipsolver.model_->a_matrix_; @@ -189,6 +189,12 @@ void HighsMipSolverData::trivialHeuristics() { bool all_integer_upper_finite = true; for (HighsInt integer_col = 0; integer_col < numintegercols; integer_col++) { HighsInt iCol = integer_cols[integer_col]; + // Round bounds in to nearest integer + col_lower[iCol] = std::ceil(col_lower[iCol]); + col_upper[iCol] = std::floor(col_upper[iCol]); + // If bounds are inconsistent then MIP is infeasible + if (col_lower[iCol] > col_upper[iCol]) return HighsModelStatus::kInfeasible; + if (col_lower[iCol] > 0) all_integer_lower_non_positive = false; if (col_lower[iCol]) all_integer_lower_zero = false; if (col_lower[iCol] <= -kHighsInf) all_integer_lower_finite = false; @@ -288,6 +294,7 @@ void HighsMipSolverData::trivialHeuristics() { } } } + return HighsModelStatus::kNotset; } void HighsMipSolverData::startAnalyticCenterComputation( diff --git a/src/mip/HighsMipSolverData.h b/src/mip/HighsMipSolverData.h index 6a9665f996..bf7522b086 100644 --- a/src/mip/HighsMipSolverData.h +++ b/src/mip/HighsMipSolverData.h @@ -204,7 +204,7 @@ struct HighsMipSolverData { } bool solutionRowFeasible(const std::vector& solution) const; - void trivialHeuristics(); + HighsModelStatus trivialHeuristics(); void startAnalyticCenterComputation( const highs::parallel::TaskGroup& taskGroup);