Skip to content

Commit feaeea6

Browse files
committed
Add probing reordering
1 parent c4012f5 commit feaeea6

File tree

1 file changed

+102
-14
lines changed

1 file changed

+102
-14
lines changed

highs/presolve/HPresolve.cpp

Lines changed: 102 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1524,29 +1524,117 @@ HPresolve::Result HPresolve::runProbing(HighsPostsolveStack& postsolve_stack) {
15241524
return Result::kPrimalInfeasible;
15251525
}
15261526

1527-
// store binary variables in vector with their number of implications on
1528-
// other binaries
1529-
std::vector<std::tuple<int64_t, HighsInt, HighsInt, HighsInt>> binaries;
1527+
// store binary variables in vector with their scores
1528+
std::vector<std::pair<double, HighsInt>> binaries;
1529+
binaries.reserve(model->num_col_);
15301530

15311531
if (!mipsolver->mipdata_->cliquetable.isFull()) {
1532-
binaries.reserve(model->num_col_);
1532+
double max_basic_score = kHighsInf;
1533+
std::vector<std::tuple<int64_t, HighsInt, HighsInt, HighsInt>> basic_scores;
1534+
basic_scores.reserve(model->num_col_);
15331535
HighsRandom random(options->random_seed);
15341536
for (HighsInt i = 0; i != model->num_col_; ++i) {
15351537
if (domain.isBinary(i)) {
1536-
HighsInt implicsUp = cliquetable.getNumImplications(i, 1);
1537-
HighsInt implicsDown = cliquetable.getNumImplications(i, 0);
1538-
binaries.emplace_back(
1538+
HighsInt implicsUp = cliquetable.getNumImplications(i, true);
1539+
HighsInt implicsDown = cliquetable.getNumImplications(i, false);
1540+
int64_t score =
15391541
-std::min(int64_t{5000}, int64_t(implicsUp) * implicsDown) /
1540-
(int64_t{1} + static_cast<int64_t>(numProbes[i])),
1541-
-std::min(HighsInt{100}, implicsUp + implicsDown), random.integer(),
1542-
i);
1542+
(int64_t{1} + static_cast<int64_t>(numProbes[i]));
1543+
basic_scores.emplace_back(
1544+
score, -std::min(HighsInt{100}, implicsUp + implicsDown),
1545+
random.integer(), i);
1546+
if (static_cast<double>(score) < max_basic_score)
1547+
max_basic_score = static_cast<double>(score);
1548+
}
1549+
}
1550+
HighsInt num_binaries = static_cast<HighsInt>(basic_scores.size());
1551+
if (num_binaries < 80) {
1552+
pdqsort(basic_scores.begin(), basic_scores.end());
1553+
// Use the basic scores (number of implications from the clique table)
1554+
for (std::tuple<int64_t, HighsInt, HighsInt, HighsInt> basic_score :
1555+
basic_scores) {
1556+
binaries.emplace_back(static_cast<double>(std::get<0>(basic_score)),
1557+
std::get<3>(basic_score));
1558+
}
1559+
} else {
1560+
// Use the advanced scores
1561+
// Each column x_j has a - / + score (for 0 / 1 probes).
1562+
// The score simulates how many other columns get fixed after fixing x_j,
1563+
// assuming the closed range of the row is optimally distributed
1564+
// (Some sqrt / squares are then added to walk back the dist. assumption)
1565+
// Example: Given a row \sum_{i \in k} a_i x_i <= b, and col x_j, a_j > 0
1566+
// Change score of x_j^+ by sqrt(k - 1) * (a_j / |rhs - minactivity|) ** 2
1567+
std::vector<std::pair<double, double>> scores(model->num_col_);
1568+
for (HighsInt i = 0; i != model->num_row_; ++i) {
1569+
HighsInt start = mipsolver->mipdata_->ARstart_[i];
1570+
HighsInt end = mipsolver->mipdata_->ARstart_[i + 1];
1571+
HighsInt kminusone = end - start - 1;
1572+
if (kminusone == 0) continue;
1573+
const double rhs = mipsolver->rowUpper(i);
1574+
const double lhs = mipsolver->rowLower(i);
1575+
const double minactivity = domain.getMinActivity(i);
1576+
const double maxactivity = domain.getMaxActivity(i);
1577+
if (!((minactivity != -kHighsInf && rhs != kHighsInf) ||
1578+
(maxactivity != kHighsInf && lhs != -kHighsInf)))
1579+
continue;
1580+
for (HighsInt j = start; j != end; ++j) {
1581+
const HighsInt col = mipsolver->mipdata_->ARindex_[j];
1582+
if (domain.isBinary(col)) {
1583+
double val = mipsolver->mipdata_->ARvalue_[j];
1584+
if (val == 0) continue;
1585+
if (minactivity != -kHighsInf && rhs != kHighsInf) {
1586+
double row_range = rhs - minactivity;
1587+
if (row_range <= primal_feastol) continue;
1588+
double rel_range_closed = std::abs(val) / row_range;
1589+
assert(rel_range_closed >= 0 && rel_range_closed <= 1);
1590+
if (val > 0)
1591+
scores[col].second +=
1592+
std::sqrt(kminusone) * rel_range_closed * rel_range_closed;
1593+
else
1594+
scores[col].first +=
1595+
std::sqrt(kminusone) * rel_range_closed * rel_range_closed;
1596+
}
1597+
if (maxactivity != kHighsInf && lhs != -kHighsInf) {
1598+
double row_range = maxactivity - lhs;
1599+
if (row_range <= primal_feastol) continue;
1600+
double rel_range_closed = std::abs(val) / row_range;
1601+
assert(rel_range_closed >= 0 && rel_range_closed <= 1);
1602+
if (val > 0)
1603+
scores[col].first +=
1604+
std::sqrt(kminusone) * rel_range_closed * rel_range_closed;
1605+
else
1606+
scores[col].second +=
1607+
std::sqrt(kminusone) * rel_range_closed * rel_range_closed;
1608+
}
1609+
}
1610+
}
1611+
}
1612+
std::vector<std::tuple<double, HighsInt, HighsInt>> advanced_scores(
1613+
num_binaries);
1614+
for (HighsInt i = 0; i != num_binaries; ++i) {
1615+
HighsInt col = std::get<3>(basic_scores[i]);
1616+
double basic_score =
1617+
std::get<0>(basic_scores[i]) == 0
1618+
? 1
1619+
: static_cast<double>(std::get<0>(basic_scores[i])) /
1620+
max_basic_score;
1621+
double advanced_score =
1622+
-(scores[col].first * scores[col].second +
1623+
std::max(scores[col].first, scores[col].second)) /
1624+
(1 + numProbes[col]);
1625+
advanced_scores.emplace_back(advanced_score * basic_score,
1626+
std::get<2>(basic_scores[i]), col);
1627+
}
1628+
pdqsort(advanced_scores.begin(), advanced_scores.end());
1629+
for (std::tuple<double, HighsInt, HighsInt> advanced_score :
1630+
advanced_scores) {
1631+
binaries.emplace_back(std::get<0>(advanced_score),
1632+
std::get<2>(advanced_score));
15431633
}
15441634
}
15451635
}
1546-
if (!binaries.empty()) {
1547-
// sort variables with many implications on other binaries first
1548-
pdqsort(binaries.begin(), binaries.end());
15491636

1637+
if (!binaries.empty()) {
15501638
size_t numChangedCols = 0;
15511639
while (domain.getChangedCols().size() != numChangedCols) {
15521640
if (domain.isFixed(domain.getChangedCols()[numChangedCols++]))
@@ -1606,7 +1694,7 @@ HPresolve::Result HPresolve::runProbing(HighsPostsolveStack& postsolve_stack) {
16061694
}
16071695

16081696
for (const auto& binvar : binaries) {
1609-
HighsInt i = std::get<3>(binvar);
1697+
HighsInt i = binvar.second;
16101698

16111699
if (cliquetable.getSubstitution(i) != nullptr || !domain.isBinary(i))
16121700
continue;

0 commit comments

Comments
 (0)