Skip to content

Commit fe88b22

Browse files
committed
Added skeleton SlackColSubstitution as presolve/postsolve action
1 parent 6e5297e commit fe88b22

File tree

5 files changed

+143
-4
lines changed

5 files changed

+143
-4
lines changed

check/TestPresolve.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -616,7 +616,7 @@ TEST_CASE("presolve-slacks", "[highs_test_presolve]") {
616616
lp.a_matrix_.index_ = {0, 0};
617617
lp.a_matrix_.value_ = {1, 1};
618618
Highs h;
619-
h.setOptionValue("output_flag", dev_run);
619+
// h.setOptionValue("output_flag", dev_run);
620620
REQUIRE(h.passModel(lp) == HighsStatus::kOk);
621621
REQUIRE(h.presolve() == HighsStatus::kOk);
622622
REQUIRE(h.getPresolvedLp().num_col_ == 0);
@@ -636,6 +636,6 @@ TEST_CASE("presolve-slacks", "[highs_test_presolve]") {
636636
REQUIRE(h.passModel(lp) == HighsStatus::kOk);
637637
REQUIRE(h.run() == HighsStatus::kOk);
638638
REQUIRE(h.presolve() == HighsStatus::kOk);
639-
// REQUIRE(h.getPresolvedLp().num_col_ == 0);
640-
// REQUIRE(h.getPresolvedLp().num_row_ == 0);
639+
REQUIRE(h.getPresolvedLp().num_col_ == 2);
640+
REQUIRE(h.getPresolvedLp().num_row_ == 2);
641641
}

src/lp_data/Highs.cpp

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1251,6 +1251,40 @@ HighsStatus Highs::run() {
12511251
// Run solver.
12521252
bool have_optimal_solution = false;
12531253
// ToDo Put solution of presolved problem in a separate method
1254+
1255+
if (!this->options_.presolve_remove_slacks) {
1256+
HighsLp& reduced_lp = presolve_.getReducedProblem();
1257+
HighsInt num_double_slack = 0;
1258+
HighsInt num_slack = 0;
1259+
HighsInt num_zero_cost_slack = 0;
1260+
HighsInt num_unit_coeff_slack = 0;
1261+
double min_slack_coeff = kHighsInf;
1262+
double max_slack_coeff = -kHighsInf;
1263+
std::vector<bool> found_slack;
1264+
found_slack.assign(reduced_lp.num_row_, false);
1265+
for (HighsInt iCol = 0; iCol < reduced_lp.num_col_; iCol++) {
1266+
HighsInt nnz = reduced_lp.a_matrix_.start_[iCol+1] - reduced_lp.a_matrix_.start_[iCol];
1267+
if (nnz != 1) continue;
1268+
HighsInt iRow = reduced_lp.a_matrix_.index_[reduced_lp.a_matrix_.start_[iCol]];
1269+
if (found_slack[iRow]) {
1270+
num_double_slack++;
1271+
continue;
1272+
}
1273+
if (reduced_lp.row_lower_[iRow] != reduced_lp.row_upper_[iRow]) continue;
1274+
double coeff = std::fabs(reduced_lp.a_matrix_.value_[reduced_lp.a_matrix_.start_[iCol]]);
1275+
if (coeff == 1.0) num_unit_coeff_slack++;
1276+
min_slack_coeff = std::min(coeff,min_slack_coeff);
1277+
max_slack_coeff = std::max(coeff,max_slack_coeff);
1278+
found_slack[iRow] = true;
1279+
num_slack++;
1280+
if (reduced_lp.col_cost_[iCol] == 0) num_zero_cost_slack++;
1281+
}
1282+
printf("grepSlack,model,col,slack,unit coeff,zero_cost,double,min coeff, max_coeff\n");
1283+
printf("grepSlack,%s,%d, %d, %d, %d, %d, %g, %g\n", this->model_.lp_.model_name_.c_str(),
1284+
int(reduced_lp.num_col_), int(num_slack), int(num_unit_coeff_slack), int(num_zero_cost_slack), int(num_double_slack),
1285+
min_slack_coeff, max_slack_coeff);
1286+
}
1287+
12541288
switch (model_presolve_status_) {
12551289
case HighsPresolveStatus::kNotPresolved: {
12561290
ekk_instance_.lp_name_ = "Original LP";
@@ -1275,7 +1309,7 @@ HighsStatus Highs::run() {
12751309
break;
12761310
}
12771311
case HighsPresolveStatus::kReduced: {
1278-
HighsLp& reduced_lp = presolve_.getReducedProblem();
1312+
HighsLp& reduced_lp = presolve_.getReducedProblem();
12791313
reduced_lp.setMatrixDimensions();
12801314
if (kAllowDeveloperAssert) {
12811315
// Validate the reduced LP

src/lp_data/HighsOptions.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,7 @@ struct HighsOptionsStruct {
372372
HighsInt presolve_substitution_maxfillin;
373373
HighsInt presolve_rule_off;
374374
bool presolve_rule_logging;
375+
bool presolve_remove_slacks;
375376
bool simplex_initial_condition_check;
376377
bool no_unnecessary_rebuild_refactor;
377378
double simplex_initial_condition_tolerance;
@@ -507,6 +508,7 @@ struct HighsOptionsStruct {
507508
presolve_substitution_maxfillin(0),
508509
presolve_rule_off(0),
509510
presolve_rule_logging(false),
511+
presolve_remove_slacks(false),
510512
simplex_initial_condition_check(false),
511513
no_unnecessary_rebuild_refactor(false),
512514
simplex_initial_condition_tolerance(0.0),
@@ -1324,6 +1326,11 @@ class HighsOptions : public HighsOptionsStruct {
13241326
advanced, &presolve_rule_logging, false);
13251327
records.push_back(record_bool);
13261328

1329+
record_bool = new OptionRecordBool(
1330+
"presolve_remove_slacks", "Remove slacks after presolve",
1331+
advanced, &presolve_remove_slacks, true);//false);
1332+
records.push_back(record_bool);
1333+
13271334
record_int = new OptionRecordInt(
13281335
"presolve_substitution_maxfillin",
13291336
"Maximal fillin allowed for substitutions in presolve", advanced,

src/presolve/HighsPostsolveStack.cpp

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1351,4 +1351,52 @@ void HighsPostsolveStack::DuplicateColumn::transformToPresolvedSpace(
13511351
primalSol[col] = primalSol[col] + colScale * primalSol[duplicateCol];
13521352
}
13531353

1354+
void HighsPostsolveStack::SlackColSubstitution::undo(
1355+
const HighsOptions& options, const std::vector<Nonzero>& rowValues,
1356+
const std::vector<Nonzero>& colValues, HighsSolution& solution,
1357+
HighsBasis& basis) {
1358+
// a (removed) cut may have been used in this reduction.
1359+
bool isModelRow = static_cast<size_t>(row) < solution.row_value.size();
1360+
1361+
// compute primal values
1362+
double colCoef = 0;
1363+
HighsCDouble rowValue = 0;
1364+
for (const auto& rowVal : rowValues) {
1365+
if (rowVal.index == col)
1366+
colCoef = rowVal.value;
1367+
else
1368+
rowValue += rowVal.value * solution.col_value[rowVal.index];
1369+
}
1370+
1371+
assert(colCoef != 0);
1372+
// Row values aren't fully postsolved, so why do this?
1373+
if (isModelRow)
1374+
solution.row_value[row] =
1375+
double(rowValue + colCoef * solution.col_value[col]);
1376+
solution.col_value[col] = double((rhs - rowValue) / colCoef);
1377+
1378+
// if no dual values requested, return here
1379+
if (!solution.dual_valid) return;
1380+
1381+
// compute the row dual value such that reduced cost of basic column is 0
1382+
if (isModelRow) {
1383+
solution.row_dual[row] = 0;
1384+
HighsCDouble dualval = colCost;
1385+
for (const auto& colVal : colValues) {
1386+
if (static_cast<size_t>(colVal.index) < solution.row_dual.size())
1387+
dualval -= colVal.value * solution.row_dual[colVal.index];
1388+
}
1389+
solution.row_dual[row] = double(dualval / colCoef);
1390+
}
1391+
1392+
solution.col_dual[col] = 0;
1393+
1394+
// set basis status if necessary
1395+
if (!basis.valid) return;
1396+
1397+
basis.col_status[col] = HighsBasisStatus::kBasic;
1398+
if (isModelRow)
1399+
basis.row_status[row] = computeRowStatus(solution.row_dual[row], rowType);
1400+
}
1401+
13541402
} // namespace presolve

src/presolve/HighsPostsolveStack.h

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,19 @@ class HighsPostsolveStack {
219219
void transformToPresolvedSpace(std::vector<double>& primalSol) const;
220220
};
221221

222+
struct SlackColSubstitution {
223+
double rhs;
224+
double colCost;
225+
HighsInt row;
226+
HighsInt col;
227+
RowType rowType;
228+
229+
void undo(const HighsOptions& options,
230+
const std::vector<Nonzero>& rowValues,
231+
const std::vector<Nonzero>& colValues, HighsSolution& solution,
232+
HighsBasis& basis);
233+
};
234+
222235
/// tags for reduction
223236
enum class ReductionType : uint8_t {
224237
kLinearTransform,
@@ -234,6 +247,7 @@ class HighsPostsolveStack {
234247
kForcingColumnRemovedRow,
235248
kDuplicateRow,
236249
kDuplicateColumn,
250+
kSlackColSubstitution,
237251
};
238252

239253
HighsDataStack reductionValues;
@@ -323,6 +337,26 @@ class HighsPostsolveStack {
323337
reductionAdded(ReductionType::kFreeColSubstitution);
324338
}
325339

340+
template <typename RowStorageFormat, typename ColStorageFormat>
341+
void slackColSubstitution(HighsInt row, HighsInt col, double rhs,
342+
double colCost, RowType rowType,
343+
const HighsMatrixSlice<RowStorageFormat>& rowVec,
344+
const HighsMatrixSlice<ColStorageFormat>& colVec) {
345+
rowValues.clear();
346+
for (const HighsSliceNonzero& rowVal : rowVec)
347+
rowValues.emplace_back(origColIndex[rowVal.index()], rowVal.value());
348+
349+
colValues.clear();
350+
for (const HighsSliceNonzero& colVal : colVec)
351+
colValues.emplace_back(origRowIndex[colVal.index()], colVal.value());
352+
353+
reductionValues.push(SlackColSubstitution{rhs, colCost, origRowIndex[row],
354+
origColIndex[col], rowType});
355+
reductionValues.push(rowValues);
356+
reductionValues.push(colValues);
357+
reductionAdded(ReductionType::kSlackColSubstitution);
358+
}
359+
326360
template <typename ColStorageFormat>
327361
void doubletonEquation(HighsInt row, HighsInt colSubst, HighsInt col,
328362
double coefSubst, double coef, double rhs,
@@ -710,6 +744,14 @@ class HighsPostsolveStack {
710744
reduction.undo(options, solution, basis);
711745
break;
712746
}
747+
case ReductionType::kSlackColSubstitution: {
748+
SlackColSubstitution reduction;
749+
reductionValues.pop(colValues);
750+
reductionValues.pop(rowValues);
751+
reductionValues.pop(reduction);
752+
reduction.undo(options, rowValues, colValues, solution, basis);
753+
break;
754+
}
713755
default:
714756
printf("Reduction case %d not handled\n",
715757
int(reductions[i - 1].first));
@@ -887,6 +929,14 @@ class HighsPostsolveStack {
887929
reductionValues.pop(reduction);
888930
reduction.undo(options, solution, basis);
889931
}
932+
case ReductionType::kSlackColSubstitution: {
933+
SlackColSubstitution reduction;
934+
reductionValues.pop(colValues);
935+
reductionValues.pop(rowValues);
936+
reductionValues.pop(reduction);
937+
reduction.undo(options, rowValues, colValues, solution, basis);
938+
break;
939+
}
890940
}
891941
}
892942
#ifdef DEBUG_EXTRA

0 commit comments

Comments
 (0)