diff --git a/check/TestPresolve.cpp b/check/TestPresolve.cpp index 7d2e5d9604..e4263c010e 100644 --- a/check/TestPresolve.cpp +++ b/check/TestPresolve.cpp @@ -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); diff --git a/src/lp_data/Highs.cpp b/src/lp_data/Highs.cpp index 894b703aac..f408807367 100644 --- a/src/lp_data/Highs.cpp +++ b/src/lp_data/Highs.cpp @@ -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 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"; @@ -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, diff --git a/src/presolve/HPresolve.cpp b/src/presolve/HPresolve.cpp index 8e99f91023..61d18979f0 100644 --- a/src/presolve/HPresolve.cpp +++ b/src/presolve/HPresolve.cpp @@ -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 { @@ -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; @@ -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 // @@ -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); diff --git a/src/presolve/HighsPostsolveStack.cpp b/src/presolve/HighsPostsolveStack.cpp index 94f7cd0e0c..2e78d9a1a7 100644 --- a/src/presolve/HighsPostsolveStack.cpp +++ b/src/presolve/HighsPostsolveStack.cpp @@ -13,7 +13,7 @@ #include #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" @@ -1355,10 +1355,7 @@ void HighsPostsolveStack::DuplicateColumn::transformToPresolvedSpace( void HighsPostsolveStack::SlackColSubstitution::undo( const HighsOptions& options, const std::vector& 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 @@ -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; diff --git a/src/presolve/HighsPostsolveStack.h b/src/presolve/HighsPostsolveStack.h index 8b3f97200e..f3409d074c 100644 --- a/src/presolve/HighsPostsolveStack.h +++ b/src/presolve/HighsPostsolveStack.h @@ -221,9 +221,6 @@ class HighsPostsolveStack { struct SlackColSubstitution { double rhs; - double colCost; - double slackLower; - double slackUpper; HighsInt row; HighsInt col; @@ -339,17 +336,13 @@ class HighsPostsolveStack { template void slackColSubstitution(HighsInt row, HighsInt col, double rhs, - double colCost, - double slackLower, double slackUpper, const HighsMatrixSlice& 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); }