diff --git a/src/interfaces/highs_c_api.h b/src/interfaces/highs_c_api.h index a53518e12e..3a0c9c05c9 100644 --- a/src/interfaces/highs_c_api.h +++ b/src/interfaces/highs_c_api.h @@ -543,9 +543,10 @@ HighsInt Highs_passModel(void* highs, const HighsInt num_col, * in compressed sparse row form), using `q_start`, `q_index`, * and `q_value`.The Hessian matrix is provided to HiGHS as the * lower triangular component in compressed sparse column form. - * The sparse matrix consists of three arrays, `start`, `index`, - * and `value`. `start` is an array of length [num_col] - * containing the starting index of each column in `index`. + * The sparse matrix consists of three arrays, `start`, + * `index`, and `value`. `start` is an array of length + * [num_col] containing the starting index of each column in + * `index`. * @param index An array of length [num_nz] with indices of matrix entries. * @param value An array of length [num_nz] with values of matrix entries. * diff --git a/src/presolve/HPresolve.cpp b/src/presolve/HPresolve.cpp index f2d549e865..9229c8c264 100644 --- a/src/presolve/HPresolve.cpp +++ b/src/presolve/HPresolve.cpp @@ -1183,7 +1183,11 @@ HPresolve::Result HPresolve::dominatedColumns( checkDomination(-1, j, -1, k)) { // case (iii) lb(x_j) = -inf, -x_j > -x_k: set x_k = ub(x_k) ++numFixedCols; - fixColToLower(postsolve_stack, j); + if (fixColToLowerOrUnbounded(postsolve_stack, j)) { + // Handle unboundedness + presolve_status_ = HighsPresolveStatus::kUnboundedOrInfeasible; + return Result::kDualInfeasible; + } HPRESOLVE_CHECKED_CALL(removeRowSingletons(postsolve_stack)); break; } else if (-ajBestRowMinus <= ak + options->small_matrix_value && @@ -1192,7 +1196,11 @@ HPresolve::Result HPresolve::dominatedColumns( checkDomination(-1, j, 1, k)) { // case (iv) lb(x_j) = -inf, -x_j > x_k: set x_k = lb(x_k) ++numFixedCols; - fixColToLower(postsolve_stack, j); + if (fixColToLowerOrUnbounded(postsolve_stack, j)) { + // Handle unboundedness + presolve_status_ = HighsPresolveStatus::kUnboundedOrInfeasible; + return Result::kDualInfeasible; + } HPRESOLVE_CHECKED_CALL(removeRowSingletons(postsolve_stack)); break; } @@ -1223,7 +1231,11 @@ HPresolve::Result HPresolve::dominatedColumns( checkDomination(1, j, 1, k)) { // case (i) ub(x_j) = inf, x_j > x_k: set x_k = lb(x_k) ++numFixedCols; - fixColToUpper(postsolve_stack, j); + if (fixColToUpperOrUnbounded(postsolve_stack, j)) { + // Handle unboundedness + presolve_status_ = HighsPresolveStatus::kUnboundedOrInfeasible; + return Result::kDualInfeasible; + } HPRESOLVE_CHECKED_CALL(removeRowSingletons(postsolve_stack)); break; } else if (ajBestRowPlus <= -ak + options->small_matrix_value && @@ -1232,7 +1244,11 @@ HPresolve::Result HPresolve::dominatedColumns( checkDomination(1, j, -1, k)) { // case (ii) ub(x_j) = inf, x_j > -x_k: set x_k = ub(x_k) ++numFixedCols; - fixColToUpper(postsolve_stack, j); + if (fixColToUpperOrUnbounded(postsolve_stack, j)) { + // Handle unboundedness + presolve_status_ = HighsPresolveStatus::kUnboundedOrInfeasible; + return Result::kDualInfeasible; + } HPRESOLVE_CHECKED_CALL(removeRowSingletons(postsolve_stack)); break; } @@ -1271,7 +1287,11 @@ HPresolve::Result HPresolve::dominatedColumns( checkDomination(1, j, 1, k)) { // case (i) ub(x_j) = inf, x_j > x_k: set x_k = lb(x_k) ++numFixedCols; - fixColToLower(postsolve_stack, k); + if (fixColToLowerOrUnbounded(postsolve_stack, k)) { + // Handle unboundedness + presolve_status_ = HighsPresolveStatus::kUnboundedOrInfeasible; + return Result::kDualInfeasible; + } HPRESOLVE_CHECKED_CALL(removeRowSingletons(postsolve_stack)); } else if (model->col_upper_[k] != kHighsInf && (upperImplied || @@ -1284,7 +1304,11 @@ HPresolve::Result HPresolve::dominatedColumns( checkDomination(1, j, -1, k)) { // case (ii) ub(x_j) = inf, x_j > -x_k: set x_k = ub(x_k) ++numFixedCols; - fixColToUpper(postsolve_stack, k); + if (fixColToUpperOrUnbounded(postsolve_stack, k)) { + // Handle unboundedness + presolve_status_ = HighsPresolveStatus::kUnboundedOrInfeasible; + return Result::kDualInfeasible; + } HPRESOLVE_CHECKED_CALL(removeRowSingletons(postsolve_stack)); } } @@ -1313,7 +1337,11 @@ HPresolve::Result HPresolve::dominatedColumns( checkDomination(-1, j, -1, k)) { // case (iii) lb(x_j) = -inf, -x_j > -x_k: set x_k = ub(x_k) ++numFixedCols; - fixColToUpper(postsolve_stack, k); + if (fixColToUpperOrUnbounded(postsolve_stack, k)) { + // Handle unboundedness + presolve_status_ = HighsPresolveStatus::kUnboundedOrInfeasible; + return Result::kDualInfeasible; + } HPRESOLVE_CHECKED_CALL(removeRowSingletons(postsolve_stack)); } else if (model->col_lower_[k] != -kHighsInf && (lowerImplied || @@ -1326,7 +1354,11 @@ HPresolve::Result HPresolve::dominatedColumns( checkDomination(-1, j, 1, k)) { // case (iv) lb(x_j) = -inf, -x_j > x_k: set x_k = lb(x_k) ++numFixedCols; - fixColToLower(postsolve_stack, k); + if (fixColToLowerOrUnbounded(postsolve_stack, k)) { + // Handle unboundedness + presolve_status_ = HighsPresolveStatus::kUnboundedOrInfeasible; + return Result::kDualInfeasible; + } HPRESOLVE_CHECKED_CALL(removeRowSingletons(postsolve_stack)); } } @@ -2837,7 +2869,11 @@ HPresolve::Result HPresolve::singletonCol(HighsPostsolveStack& postsolve_stack, if (colDualLower > options->dual_feasibility_tolerance) { if (model->col_lower_[col] == -kHighsInf) return Result::kDualInfeasible; if (logging_on) analysis_.startPresolveRuleLog(kPresolveRuleDominatedCol); - fixColToLower(postsolve_stack, col); + if (fixColToLowerOrUnbounded(postsolve_stack, col)) { + // Handle unboundedness + presolve_status_ = HighsPresolveStatus::kUnboundedOrInfeasible; + return Result::kDualInfeasible; + } analysis_.logging_on_ = logging_on; if (logging_on) analysis_.stopPresolveRuleLog(kPresolveRuleDominatedCol); return checkLimits(postsolve_stack); @@ -2846,7 +2882,11 @@ HPresolve::Result HPresolve::singletonCol(HighsPostsolveStack& postsolve_stack, if (colDualUpper < -options->dual_feasibility_tolerance) { if (model->col_upper_[col] == kHighsInf) return Result::kDualInfeasible; if (logging_on) analysis_.startPresolveRuleLog(kPresolveRuleDominatedCol); - fixColToUpper(postsolve_stack, col); + if (fixColToUpperOrUnbounded(postsolve_stack, col)) { + // Handle unboundedness + presolve_status_ = HighsPresolveStatus::kUnboundedOrInfeasible; + return Result::kDualInfeasible; + } analysis_.logging_on_ = logging_on; if (logging_on) analysis_.stopPresolveRuleLog(kPresolveRuleDominatedCol); return checkLimits(postsolve_stack); @@ -2856,7 +2896,11 @@ HPresolve::Result HPresolve::singletonCol(HighsPostsolveStack& postsolve_stack, if (colDualUpper <= options->dual_feasibility_tolerance) { if (model->col_upper_[col] != kHighsInf) { if (logging_on) analysis_.startPresolveRuleLog(kPresolveRuleDominatedCol); - fixColToUpper(postsolve_stack, col); + if (fixColToUpperOrUnbounded(postsolve_stack, col)) { + // Handle unboundedness + presolve_status_ = HighsPresolveStatus::kUnboundedOrInfeasible; + return Result::kDualInfeasible; + } analysis_.logging_on_ = logging_on; if (logging_on) analysis_.stopPresolveRuleLog(kPresolveRuleDominatedCol); } else if (impliedDualRowBounds.getSumLowerOrig(col) == 0.0 && @@ -2895,7 +2939,11 @@ HPresolve::Result HPresolve::singletonCol(HighsPostsolveStack& postsolve_stack, if (colDualLower >= -options->dual_feasibility_tolerance) { if (model->col_lower_[col] != -kHighsInf) { if (logging_on) analysis_.startPresolveRuleLog(kPresolveRuleDominatedCol); - fixColToLower(postsolve_stack, col); + if (fixColToLowerOrUnbounded(postsolve_stack, col)) { + // Handle unboundedness + presolve_status_ = HighsPresolveStatus::kUnboundedOrInfeasible; + return Result::kDualInfeasible; + } analysis_.logging_on_ = logging_on; if (logging_on) analysis_.stopPresolveRuleLog(kPresolveRuleDominatedCol); } else if (impliedDualRowBounds.getSumUpperOrig(col) == 0.0 && @@ -3344,11 +3392,21 @@ HPresolve::Result HPresolve::rowPresolve(HighsPostsolveStack& postsolve_stack, // Fix variable if (std::abs(model->col_lower_[x1] - fixVal) <= primal_feastol) { - fixColToLower(postsolve_stack, x1); + if (fixColToLowerOrUnbounded(postsolve_stack, x1)) { + // Handle unboundedness + presolve_status_ = + HighsPresolveStatus::kUnboundedOrInfeasible; + return Result::kDualInfeasible; + } } else { assert(std::abs(model->col_upper_[x1] - fixVal) <= primal_feastol); - fixColToUpper(postsolve_stack, x1); + if (fixColToUpperOrUnbounded(postsolve_stack, x1)) { + // Handle unboundedness + presolve_status_ = + HighsPresolveStatus::kUnboundedOrInfeasible; + return Result::kDualInfeasible; + } } rowpositions.erase(rowpositions.begin() + x1Cand); } else { @@ -3862,15 +3920,29 @@ HPresolve::Result HPresolve::emptyCol(HighsPostsolveStack& postsolve_stack, return Result::kDualInfeasible; } - if (model->col_cost_[col] > 0) - fixColToLower(postsolve_stack, col); - else if (model->col_cost_[col] < 0 || - std::abs(model->col_upper_[col]) < std::abs(model->col_lower_[col])) - fixColToUpper(postsolve_stack, col); - else if (model->col_lower_[col] != -kHighsInf) - fixColToLower(postsolve_stack, col); - else + if (model->col_cost_[col] > 0) { + if (fixColToLowerOrUnbounded(postsolve_stack, col)) { + // Handle unboundedness + presolve_status_ = HighsPresolveStatus::kUnboundedOrInfeasible; + return Result::kDualInfeasible; + } + } else if (model->col_cost_[col] < 0 || + std::abs(model->col_upper_[col]) < + std::abs(model->col_lower_[col])) { + if (fixColToUpperOrUnbounded(postsolve_stack, col)) { + // Handle unboundedness + presolve_status_ = HighsPresolveStatus::kUnboundedOrInfeasible; + return Result::kDualInfeasible; + } + } else if (model->col_lower_[col] != -kHighsInf) { + if (fixColToLowerOrUnbounded(postsolve_stack, col)) { + // Handle unboundedness + presolve_status_ = HighsPresolveStatus::kUnboundedOrInfeasible; + return Result::kDualInfeasible; + } + } else { fixColToZero(postsolve_stack, col); + } analysis_.logging_on_ = logging_on; if (logging_on) analysis_.stopPresolveRuleLog(kPresolveRuleEmptyCol); @@ -3913,7 +3985,11 @@ HPresolve::Result HPresolve::colPresolve(HighsPostsolveStack& postsolve_stack, if (model->col_lower_[col] == -kHighsInf) return Result::kDualInfeasible; else { - fixColToLower(postsolve_stack, col); + if (fixColToLowerOrUnbounded(postsolve_stack, col)) { + // Handle unboundedness + presolve_status_ = HighsPresolveStatus::kUnboundedOrInfeasible; + return Result::kDualInfeasible; + } HPRESOLVE_CHECKED_CALL(removeRowSingletons(postsolve_stack)); } return checkLimits(postsolve_stack); @@ -3923,7 +3999,11 @@ HPresolve::Result HPresolve::colPresolve(HighsPostsolveStack& postsolve_stack, if (model->col_upper_[col] == kHighsInf) return Result::kDualInfeasible; else { - fixColToUpper(postsolve_stack, col); + if (fixColToUpperOrUnbounded(postsolve_stack, col)) { + // Handle unboundedness + presolve_status_ = HighsPresolveStatus::kUnboundedOrInfeasible; + return Result::kDualInfeasible; + } HPRESOLVE_CHECKED_CALL(removeRowSingletons(postsolve_stack)); } return checkLimits(postsolve_stack); @@ -3932,7 +4012,11 @@ HPresolve::Result HPresolve::colPresolve(HighsPostsolveStack& postsolve_stack, // check for weakly dominated column if (colDualUpper <= options->dual_feasibility_tolerance) { if (model->col_upper_[col] != kHighsInf) { - fixColToUpper(postsolve_stack, col); + if (fixColToUpperOrUnbounded(postsolve_stack, col)) { + // Handle unboundedness + presolve_status_ = HighsPresolveStatus::kUnboundedOrInfeasible; + return Result::kDualInfeasible; + } HPRESOLVE_CHECKED_CALL(removeRowSingletons(postsolve_stack)); return checkLimits(postsolve_stack); } else if (impliedDualRowBounds.getSumLowerOrig(col) == 0.0) { @@ -3958,7 +4042,11 @@ HPresolve::Result HPresolve::colPresolve(HighsPostsolveStack& postsolve_stack, } else if (colDualLower >= -options->dual_feasibility_tolerance) { // symmetric case for fixing to the lower bound if (model->col_lower_[col] != -kHighsInf) { - fixColToLower(postsolve_stack, col); + if (fixColToLowerOrUnbounded(postsolve_stack, col)) { + // Handle unboundedness + presolve_status_ = HighsPresolveStatus::kUnboundedOrInfeasible; + return Result::kDualInfeasible; + } HPRESOLVE_CHECKED_CALL(removeRowSingletons(postsolve_stack)); return checkLimits(postsolve_stack); } else if (impliedDualRowBounds.getSumUpperOrig(col) == 0.0) { @@ -4941,12 +5029,14 @@ void HPresolve::substitute(HighsInt substcol, HighsInt staycol, double offset, } } -void HPresolve::fixColToLower(HighsPostsolveStack& postsolve_stack, - HighsInt col) { +bool HPresolve::fixColToLowerOrUnbounded(HighsPostsolveStack& postsolve_stack, + HighsInt col) { + double fixval = model->col_lower_[col]; + bool unbounded = fixval == -kHighsInf; + if (unbounded) return true; + const bool logging_on = analysis_.logging_on_; if (logging_on) analysis_.startPresolveRuleLog(kPresolveRuleFixedCol); - double fixval = model->col_lower_[col]; - assert(fixval != -kHighsInf); // printf("fixing column %" HIGHSINT_FORMAT " to %.15g\n", col, fixval); @@ -4980,14 +5070,18 @@ void HPresolve::fixColToLower(HighsPostsolveStack& postsolve_stack, model->col_cost_[col] = 0; analysis_.logging_on_ = logging_on; if (logging_on) analysis_.stopPresolveRuleLog(kPresolveRuleFixedCol); + return false; } -void HPresolve::fixColToUpper(HighsPostsolveStack& postsolve_stack, - HighsInt col) { +bool HPresolve::fixColToUpperOrUnbounded(HighsPostsolveStack& postsolve_stack, + HighsInt col) { + double fixval = model->col_upper_[col]; + bool unbounded = fixval == kHighsInf; + if (unbounded) return true; + const bool logging_on = analysis_.logging_on_; if (logging_on) analysis_.startPresolveRuleLog(kPresolveRuleFixedCol); - double fixval = model->col_upper_[col]; - assert(fixval != kHighsInf); + // printf("fixing column %" HIGHSINT_FORMAT " to %.15g\n", col, fixval); // mark the column as deleted first so that it is not registered as singleton @@ -5020,6 +5114,7 @@ void HPresolve::fixColToUpper(HighsPostsolveStack& postsolve_stack, model->col_cost_[col] = 0; analysis_.logging_on_ = logging_on; if (logging_on) analysis_.stopPresolveRuleLog(kPresolveRuleFixedCol); + return false; } void HPresolve::fixColToZero(HighsPostsolveStack& postsolve_stack, @@ -5780,7 +5875,11 @@ HPresolve::Result HPresolve::detectParallelRowsAndCols( HighsInt row = Arow[colhead[duplicateCol]]; numRowSingletons[row] -= 1; } - fixColToLower(postsolve_stack, duplicateCol); + if (fixColToLowerOrUnbounded(postsolve_stack, duplicateCol)) { + // Handle unboundedness + presolve_status_ = HighsPresolveStatus::kUnboundedOrInfeasible; + return Result::kDualInfeasible; + } break; case kDominanceDuplicateColToUpper: delCol = duplicateCol; @@ -5788,7 +5887,11 @@ HPresolve::Result HPresolve::detectParallelRowsAndCols( HighsInt row = Arow[colhead[duplicateCol]]; numRowSingletons[row] -= 1; } - fixColToUpper(postsolve_stack, duplicateCol); + if (fixColToUpperOrUnbounded(postsolve_stack, duplicateCol)) { + // Handle unboundedness + presolve_status_ = HighsPresolveStatus::kUnboundedOrInfeasible; + return Result::kDualInfeasible; + } break; case kDominanceColToLower: delCol = col; @@ -5796,7 +5899,11 @@ HPresolve::Result HPresolve::detectParallelRowsAndCols( HighsInt row = Arow[colhead[col]]; numRowSingletons[row] -= 1; } - fixColToLower(postsolve_stack, col); + if (fixColToLowerOrUnbounded(postsolve_stack, col)) { + // Handle unboundedness + presolve_status_ = HighsPresolveStatus::kUnboundedOrInfeasible; + return Result::kDualInfeasible; + } break; case kDominanceColToUpper: delCol = col; @@ -5804,7 +5911,11 @@ HPresolve::Result HPresolve::detectParallelRowsAndCols( HighsInt row = Arow[colhead[col]]; numRowSingletons[row] -= 1; } - fixColToUpper(postsolve_stack, col); + if (fixColToUpperOrUnbounded(postsolve_stack, col)) { + // Handle unboundedness + presolve_status_ = HighsPresolveStatus::kUnboundedOrInfeasible; + return Result::kDualInfeasible; + } break; case kMergeParallelCols: const bool ok_merge = postsolve_stack.duplicateColumn( diff --git a/src/presolve/HPresolve.h b/src/presolve/HPresolve.h index fe21860ff0..9c0511b3e6 100644 --- a/src/presolve/HPresolve.h +++ b/src/presolve/HPresolve.h @@ -223,9 +223,11 @@ class HPresolve { void markColDeleted(HighsInt col); - void fixColToLower(HighsPostsolveStack& postsolve_stack, HighsInt col); + bool fixColToLowerOrUnbounded(HighsPostsolveStack& postsolve_stack, + HighsInt col); - void fixColToUpper(HighsPostsolveStack& postsolve_stack, HighsInt col); + bool fixColToUpperOrUnbounded(HighsPostsolveStack& postsolve_stack, + HighsInt col); void fixColToZero(HighsPostsolveStack& postsolve_stack, HighsInt col);