@@ -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