Skip to content

Commit

Permalink
Cleaned up HPresolve::removeSlacks but left off by default since not …
Browse files Browse the repository at this point in the history
…advantageous on average
  • Loading branch information
jajhall committed Nov 10, 2024
1 parent d0744ed commit f769209
Show file tree
Hide file tree
Showing 5 changed files with 29 additions and 95 deletions.
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
44 changes: 4 additions & 40 deletions src/lp_data/Highs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1252,46 +1252,6 @@ HighsStatus Highs::run() {
bool have_optimal_solution = false;
// ToDo Put solution of presolved problem in a separate method

// if (!this->options_.presolve_remove_slacks) {
HighsLp& reduced_lp = presolve_.getReducedProblem();
HighsInt num_double_slack = 0;
HighsInt num_slack = 0;
HighsInt num_zero_cost_slack = 0;
HighsInt num_unit_coeff_slack = 0;
double min_slack_coeff = kHighsInf;
double max_slack_coeff = -kHighsInf;
std::vector<bool> found_slack;
found_slack.assign(reduced_lp.num_row_, false);
for (HighsInt iCol = 0; iCol < reduced_lp.num_col_; iCol++) {
HighsInt nnz = reduced_lp.a_matrix_.start_[iCol + 1] -
reduced_lp.a_matrix_.start_[iCol];
if (nnz != 1) continue;
HighsInt iRow =
reduced_lp.a_matrix_.index_[reduced_lp.a_matrix_.start_[iCol]];
if (found_slack[iRow]) {
num_double_slack++;
continue;
}
if (reduced_lp.row_lower_[iRow] != reduced_lp.row_upper_[iRow]) continue;
num_slack++;
printf("Column %d is slack\n", int(iCol));
double coeff = std::fabs(
reduced_lp.a_matrix_.value_[reduced_lp.a_matrix_.start_[iCol]]);
if (coeff == 1.0) num_unit_coeff_slack++;
min_slack_coeff = std::min(coeff, min_slack_coeff);
max_slack_coeff = std::max(coeff, max_slack_coeff);
found_slack[iRow] = true;
if (reduced_lp.col_cost_[iCol] == 0) num_zero_cost_slack++;
}
printf(
"grepSlack,model,col,slack,unit coeff,zero_cost,double,min coeff, "
"max_coeff\n");
printf("grepSlack,%s,%d, %d, %d, %d, %d, %g, %g\n",
this->model_.lp_.model_name_.c_str(), int(reduced_lp.num_col_),
int(num_slack), int(num_unit_coeff_slack), int(num_zero_cost_slack),
int(num_double_slack), min_slack_coeff, max_slack_coeff);
// }

switch (model_presolve_status_) {
case HighsPresolveStatus::kNotPresolved: {
ekk_instance_.lp_name_ = "Original LP";
Expand Down Expand Up @@ -1565,6 +1525,10 @@ HighsStatus Highs::run() {
options_ = save_options;
if (return_status == HighsStatus::kError)
return returnFromRun(return_status, undo_mods);
if (postsolve_iteration_count > 0)
highsLogUser(options_.log_options, HighsLogType::kInfo,
"Required %d simplex iterations after postsolve\n",
int(postsolve_iteration_count));
}
} else {
highsLogUser(log_options, HighsLogType::kError,
Expand Down
17 changes: 3 additions & 14 deletions src/presolve/HPresolve.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4361,9 +4361,8 @@ HPresolve::Result HPresolve::presolve(HighsPostsolveStack& postsolve_stack) {
}

// Now consider removing slacks
if (options->presolve_remove_slacks) {
if (options->presolve_remove_slacks)
HPRESOLVE_CHECKED_CALL(removeSlacks(postsolve_stack));
}

report();
} else {
Expand All @@ -4382,10 +4381,8 @@ HPresolve::Result HPresolve::presolve(HighsPostsolveStack& postsolve_stack) {

HPresolve::Result HPresolve::removeSlacks(
HighsPostsolveStack& postsolve_stack) {
// singletonColumns data structure appears not to be retained
// SingletonColumns data structure appears not to be retained
// throughout presolve
//
bool unit_coeff_only = true;
for (HighsInt iCol = 0; iCol != model->num_col_; ++iCol) {
if (colDeleted[iCol]) continue;
if (colsize[iCol] != 1) continue;
Expand All @@ -4400,11 +4397,6 @@ HPresolve::Result HPresolve::removeSlacks(
double cost = model->col_cost_[iCol];
double rhs = model->row_lower_[iRow];
double coeff = Avalue[coliter];
printf(
"Col %d is continuous and is singleton in equality row %d with cost "
"%g, bounds [%g, %g], coeff %g and RHS = %g\n",
int(iCol), int(iRow), cost, lower, upper, coeff, rhs);
if (unit_coeff_only && std::fabs(coeff) != 1.0) continue;
assert(coeff);
// Slack is s = (rhs - a^Tx)/coeff
//
Expand All @@ -4428,10 +4420,7 @@ HPresolve::Result HPresolve::removeSlacks(
model->offset_ += multiplier * rhs;
}
//
postsolve_stack.slackColSubstitution(iRow, iCol, rhs, cost,
lower,
upper,
getRowVector(iRow));
postsolve_stack.slackColSubstitution(iRow, iCol, rhs, getRowVector(iRow));

markColDeleted(iCol);

Expand Down
50 changes: 19 additions & 31 deletions src/presolve/HighsPostsolveStack.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
#include <numeric>

#include "lp_data/HConst.h"
#include "lp_data/HighsModelUtils.h" // For debugging
#include "lp_data/HighsModelUtils.h" // For debugging #2001
#include "lp_data/HighsOptions.h"
#include "util/HighsCDouble.h"
#include "util/HighsUtils.h"
Expand Down Expand Up @@ -1355,10 +1355,7 @@ void HighsPostsolveStack::DuplicateColumn::transformToPresolvedSpace(
void HighsPostsolveStack::SlackColSubstitution::undo(
const HighsOptions& options, const std::vector<Nonzero>& rowValues,
HighsSolution& solution, HighsBasis& basis) {
// Taken from HighsPostsolveStack::FreeColSubstitution::undo(
//
// a (removed) cut may have been used in this reduction.
//
bool debug_print = false;
// May have to determine row dual and basis status unless doing
// primal-only transformation in MIP solver, in which case row may
// no longer exist if it corresponds to a removed cut, so have to
Expand All @@ -1382,50 +1379,41 @@ void HighsPostsolveStack::SlackColSubstitution::undo(
double(rowValue + colCoef * solution.col_value[col]);

solution.col_value[col] = double((rhs - rowValue) / colCoef);
double rowLower = colCoef > 0 ? rhs - colCoef * slackUpper : rhs - colCoef * slackLower;
double rowUpper = colCoef > 0 ? rhs - colCoef * slackLower : rhs - colCoef * slackUpper;

printf(
"\nHighsPostsolveStack::SlackColSubstitution::undo rowValue = %11.5g; "
"bounds [%11.5g, %11.5g] colCoef = %11.6g\n",
double(rowValue), rowLower, rowUpper, colCoef);
printf(
"HighsPostsolveStack::SlackColSubstitution::undo colValue = %11.5g, bounds [%11.5g, %11.5g]\n",
solution.col_value[col], slackLower, slackUpper);

// If no dual values requested, return here
if (!solution.dual_valid) return;

// Row retains its dual value, and column has this dual value scaled by coeff
if (isModelRow) {
solution.col_dual[col] = - solution.row_dual[row] / colCoef;
printf(
"HighsPostsolveStack::SlackColSubstitution::undo rowDual = %11.5g; colDual = %11.5g\n",
solution.row_dual[row], solution.col_dual[col]);
}
if (isModelRow) solution.col_dual[col] = -solution.row_dual[row] / colCoef;

// Set basis status if necessary
if (!basis.valid) return;

// If row is basic, then slack is basic, otherwise row retains its status
// If row is basic, then slack is basic, otherwise row retains its status
if (isModelRow) {
HighsBasisStatus save_row_basis_status = basis.row_status[row];
if (basis.row_status[row] == HighsBasisStatus::kBasic) {
basis.col_status[col] = HighsBasisStatus::kBasic;
basis.row_status[row] = computeRowStatus(solution.row_dual[row], RowType::kEq);
basis.row_status[row] =
computeRowStatus(solution.row_dual[row], RowType::kEq);
} else if (basis.row_status[row] == HighsBasisStatus::kLower) {
basis.col_status[col] = colCoef > 0 ? HighsBasisStatus::kUpper : HighsBasisStatus::kLower;
basis.col_status[col] =
colCoef > 0 ? HighsBasisStatus::kUpper : HighsBasisStatus::kLower;
} else {
basis.col_status[col] = colCoef > 0 ? HighsBasisStatus::kLower : HighsBasisStatus::kUpper;
basis.col_status[col] =
colCoef > 0 ? HighsBasisStatus::kLower : HighsBasisStatus::kUpper;
}
printf("HighsPostsolveStack::SlackColSubstitution::undo OgRowStatus = %s; "
"RowStatus = %s; ColStatus = %s\n",
utilBasisStatusToString(save_row_basis_status).c_str(), utilBasisStatusToString(basis.row_status[row]).c_str(),
utilBasisStatusToString(basis.col_status[col]).c_str());
if (debug_print)
printf(
"HighsPostsolveStack::SlackColSubstitution::undo OgRowStatus = %s; "
"RowStatus = %s; ColStatus = %s\n",
utilBasisStatusToString(save_row_basis_status).c_str(),
utilBasisStatusToString(basis.row_status[row]).c_str(),
utilBasisStatusToString(basis.col_status[col]).c_str());
if (basis.col_status[col] == HighsBasisStatus::kLower) {
assert(solution.col_dual[col] > 0);
assert(solution.col_dual[col] > -options.dual_feasibility_tolerance);
} else if (basis.col_status[col] == HighsBasisStatus::kUpper) {
assert(solution.col_dual[col] < 0);
assert(solution.col_dual[col] < options.dual_feasibility_tolerance);
}
} else {
basis.col_status[col] = HighsBasisStatus::kNonbasic;
Expand Down
11 changes: 2 additions & 9 deletions src/presolve/HighsPostsolveStack.h
Original file line number Diff line number Diff line change
Expand Up @@ -221,9 +221,6 @@ class HighsPostsolveStack {

struct SlackColSubstitution {
double rhs;
double colCost;
double slackLower;
double slackUpper;
HighsInt row;
HighsInt col;

Expand Down Expand Up @@ -339,17 +336,13 @@ class HighsPostsolveStack {

template <typename RowStorageFormat>
void slackColSubstitution(HighsInt row, HighsInt col, double rhs,
double colCost,
double slackLower, double slackUpper,
const HighsMatrixSlice<RowStorageFormat>& rowVec) {
rowValues.clear();
for (const HighsSliceNonzero& rowVal : rowVec)
rowValues.emplace_back(origColIndex[rowVal.index()], rowVal.value());

reductionValues.push(SlackColSubstitution{rhs, colCost,
slackLower, slackUpper,
origRowIndex[row],
origColIndex[col]});
reductionValues.push(
SlackColSubstitution{rhs, origRowIndex[row], origColIndex[col]});
reductionValues.push(rowValues);
reductionAdded(ReductionType::kSlackColSubstitution);
}
Expand Down

0 comments on commit f769209

Please sign in to comment.