Skip to content

Commit

Permalink
Merge pull request #1885 from ERGO-Code/fix-1883
Browse files Browse the repository at this point in the history
Fix 1883
  • Loading branch information
jajhall committed Aug 19, 2024
2 parents e253cb0 + cf82e09 commit 61f72a3
Show file tree
Hide file tree
Showing 3 changed files with 157 additions and 43 deletions.
7 changes: 4 additions & 3 deletions src/interfaces/highs_c_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand Down
187 changes: 149 additions & 38 deletions src/presolve/HPresolve.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 &&
Expand All @@ -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;
}
Expand Down Expand Up @@ -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 &&
Expand All @@ -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;
}
Expand Down Expand Up @@ -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 ||
Expand All @@ -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));
}
}
Expand Down Expand Up @@ -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 ||
Expand All @@ -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));
}
}
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand All @@ -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 &&
Expand Down Expand Up @@ -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 &&
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand All @@ -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) {
Expand All @@ -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) {
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -5780,31 +5875,47 @@ 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;
if (colsize[duplicateCol] == 1) {
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;
if (colsize[col] == 1) {
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;
if (colsize[col] == 1) {
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(
Expand Down
6 changes: 4 additions & 2 deletions src/presolve/HPresolve.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down

0 comments on commit 61f72a3

Please sign in to comment.