@@ -1443,6 +1443,200 @@ HPresolve::Result HPresolve::dominatedColumns(
14431443 return Result::kOk ;
14441444}
14451445
1446+ void HPresolve::createPrecedenceGraph (bool generateUb) const {
1447+ HighsImplications& implications = mipsolver->mipdata_ ->implications ;
1448+ HighsSparseMatrix& precedenceLb = implications.getPrecedenceDirectedGraphLb ();
1449+
1450+ std::vector<HighsInt> precedenceArcs;
1451+ precedenceArcs.reserve (mipsolver->numRow ());
1452+ std::vector<HighsInt> numArcs (mipsolver->numCol (), 0 );
1453+
1454+ // Want to store all rows of the form x <= y + c, c \in \R
1455+ for (HighsInt row = 0 ; row != model->num_row_ ; ++row) {
1456+ if (rowDeleted[row] || rowsize[row] != 2 || isEquation (row)) continue ;
1457+ HighsInt x = -1 ;
1458+ double xCoef = 0 ;
1459+ HighsInt y = -1 ;
1460+ double yCoef = 0 ;
1461+ for (const HighsSliceNonzero& nonzero : getRowVector (row)) {
1462+ // get column index and coefficient
1463+ const HighsInt col = nonzero.index ();
1464+ const double val = nonzero.value ();
1465+ if (val > 0 ) {
1466+ x = col;
1467+ xCoef = val;
1468+ } else if (val < 0 ) {
1469+ y = col;
1470+ yCoef = val;
1471+ }
1472+ }
1473+ if (x == -1 || y == -1 || colDeleted[x] || colDeleted[y] ||
1474+ std::max (std::fabs (yCoef), std::fabs (xCoef)) <=
1475+ options->small_matrix_value ||
1476+ std::fabs (std::fabs (yCoef) - std::fabs (xCoef)) >
1477+ options->small_matrix_value ||
1478+ xCoef * yCoef >= 0 )
1479+ continue ;
1480+ if (model->row_upper_ [row] != kHighsInf ||
1481+ model->row_lower_ [row] != -kHighsInf ) {
1482+ precedenceArcs.emplace_back (row);
1483+ }
1484+ if (model->row_upper_ [row] != kHighsInf ) {
1485+ numArcs[x]++;
1486+ }
1487+ if (model->row_lower_ [row] != -kHighsInf ) {
1488+ numArcs[y]++;
1489+ }
1490+ }
1491+
1492+ if (precedenceArcs.empty ()) {
1493+ precedenceLb.clear ();
1494+ implications.getPrecedenceDirectedGraphUb ().clear ();
1495+ return ;
1496+ }
1497+
1498+ precedenceLb.num_col_ = mipsolver->numCol ();
1499+ precedenceLb.num_row_ = mipsolver->numCol ();
1500+ precedenceLb.format_ = MatrixFormat::kColwise ;
1501+ precedenceLb.p_end_ .clear ();
1502+ std::vector<HighsInt>& start = precedenceLb.start_ ;
1503+ start.assign (mipsolver->numCol () + 1 , 0 );
1504+ std::vector<HighsInt>& index = precedenceLb.index_ ;
1505+ std::vector<double >& value = precedenceLb.value_ ;
1506+ for (HighsInt col = 0 ; col != mipsolver->numCol (); col++) {
1507+ start[col + 1 ] = start[col] + numArcs[col];
1508+ }
1509+ index.resize (start.back ());
1510+ value.resize (start.back ());
1511+ std::vector<HighsInt> pos = start;
1512+ for (const HighsInt row : precedenceArcs) {
1513+ HighsInt x = -1 ;
1514+ HighsInt y = -1 ;
1515+ double scale = 0 ;
1516+ for (const HighsSliceNonzero& nonzero : getRowVector (row)) {
1517+ // get column index and coefficient
1518+ const HighsInt col = nonzero.index ();
1519+ const double val = nonzero.value ();
1520+ if (val > 0 ) {
1521+ x = col;
1522+ scale = val;
1523+ } else if (val < 0 ) {
1524+ y = col;
1525+ }
1526+ }
1527+ if (model->row_upper_ [row] != kHighsInf ) {
1528+ const HighsInt p = pos[x]++;
1529+ index[p] = y;
1530+ value[p] = model->row_upper_ [row] / scale;
1531+ }
1532+ if (model->row_lower_ [row] != -kHighsInf ) {
1533+ const HighsInt p = pos[y]++;
1534+ index[p] = x;
1535+ value[p] = -model->row_lower_ [row] / scale;
1536+ }
1537+ }
1538+
1539+ if (generateUb) {
1540+ implications.getPrecedenceDirectedGraphUb ().createRowwise (precedenceLb);
1541+ }
1542+ }
1543+
1544+ void HPresolve::strongConnect (
1545+ const std::vector<HighsInt>& start, const std::vector<HighsInt>& index,
1546+ const std::vector<double >& value, const double eps, HighsInt v,
1547+ HighsInt& time, std::vector<HighsInt>& dfsNum,
1548+ std::vector<HighsInt>& lowLink, std::vector<HighsInt>& stack,
1549+ std::vector<bool >& onStack,
1550+ std::vector<HighsInt>& stronglyConnectedComponents) {
1551+ dfsNum[v] = time;
1552+ lowLink[v] = time;
1553+ ++time;
1554+ stack.push_back (v);
1555+ onStack[v] = true ;
1556+
1557+ for (HighsInt i = start[v]; i != start[v + 1 ]; ++i) {
1558+ if (std::abs (value[i]) > eps) continue ;
1559+ const HighsInt w = index[i];
1560+ if (dfsNum[w] == -1 ) {
1561+ strongConnect (start, index, value, eps, w, time, dfsNum, lowLink, stack,
1562+ onStack, stronglyConnectedComponents);
1563+ lowLink[v] = std::min (lowLink[v], lowLink[w]);
1564+ } else if (onStack[w]) {
1565+ lowLink[v] = std::min (lowLink[v], dfsNum[w]);
1566+ }
1567+ }
1568+
1569+ if (lowLink[v] != dfsNum[v]) return ;
1570+
1571+ HighsInt u = v;
1572+ std::vector<HighsInt> stronglyConnectedComponent;
1573+ while (true ) {
1574+ HighsInt w = stack.back ();
1575+ stack.pop_back ();
1576+ onStack[w] = false ;
1577+ stronglyConnectedComponent.push_back (w);
1578+ u = std::min (u, w);
1579+ if (w == v) break ;
1580+ }
1581+
1582+ for (const HighsInt w : stronglyConnectedComponent) {
1583+ stronglyConnectedComponents[w] = u;
1584+ }
1585+ }
1586+
1587+ void HPresolve::tarjan (const std::vector<HighsInt>& start,
1588+ const std::vector<HighsInt>& index,
1589+ const std::vector<double >& value, const double eps,
1590+ std::vector<HighsInt>& stronglyConnectedComponents) {
1591+ const HighsInt n = static_cast <HighsInt>(stronglyConnectedComponents.size ());
1592+ std::vector<HighsInt> dfsNum (n, -1 );
1593+ std::vector<HighsInt> lowLink (n, -1 );
1594+ std::vector<HighsInt> stack;
1595+ stack.reserve (n);
1596+ std::vector<bool > onStack (n, false );
1597+ HighsInt time = 0 ;
1598+
1599+ for (HighsInt col = 0 ; col != n; ++col) {
1600+ if (colDeleted[col]) continue ;
1601+ if (dfsNum[col] == -1 )
1602+ strongConnect (start, index, value, eps, col, time, dfsNum, lowLink, stack,
1603+ onStack, stronglyConnectedComponents);
1604+ }
1605+ }
1606+
1607+ HPresolve::Result HPresolve::findPrecedenceCycles (
1608+ HighsPostsolveStack& postsolve_stack) {
1609+ HighsImplications& implications = mipsolver->mipdata_ ->implications ;
1610+ HighsSparseMatrix& precedenceLb = implications.getPrecedenceDirectedGraphLb ();
1611+
1612+ const HighsInt n = precedenceLb.num_col_ ;
1613+ if (n == 0 ) return Result::kOk ;
1614+
1615+ // Run Tarjan's algorithm to find any strongly connected components
1616+ // If there is a cycle, e.g. x <= y, y <= z, z <= x, then we
1617+ // can conclude that x = y = z.
1618+ std::vector<HighsInt> stronglyConnectedComponents (n, -1 );
1619+ tarjan (precedenceLb.start_ , precedenceLb.index_ , precedenceLb.value_ ,
1620+ options->small_matrix_value , stronglyConnectedComponents);
1621+
1622+ // Apply substitutions for all strongly connected components
1623+ for (HighsInt substCol = 0 ; substCol != n; ++substCol) {
1624+ if (colDeleted[substCol]) continue ;
1625+ const HighsInt stayCol = stronglyConnectedComponents[substCol];
1626+ if (stayCol == -1 || stayCol == substCol) continue ;
1627+
1628+ postsolve_stack.doubletonEquation (
1629+ -1 , substCol, stayCol, 1.0 , -1 , 0 , model->col_lower_ [substCol],
1630+ model->col_upper_ [substCol], 0.0 , false , false ,
1631+ HighsPostsolveStack::RowType::kEq , HighsEmptySlice ());
1632+
1633+ markColDeleted (substCol);
1634+ substitute (substCol, stayCol, 0.0 , 1.0 );
1635+ }
1636+
1637+ return checkLimits (postsolve_stack);
1638+ }
1639+
14461640HPresolve::Result HPresolve::prepareProbing (
14471641 HighsPostsolveStack& postsolve_stack, bool & firstCall) {
14481642 HighsDomain& domain = mipsolver->mipdata_ ->domain ;
@@ -1474,6 +1668,9 @@ HPresolve::Result HPresolve::prepareProbing(
14741668 // prepare for domain propagation
14751669 mipsolver->mipdata_ ->setupDomainPropagation ();
14761670
1671+ // Extract precedence constraints to be used for faster propagation
1672+ createPrecedenceGraph (true );
1673+
14771674 // first call?
14781675 firstCall = !mipsolver->mipdata_ ->cliquesExtracted ;
14791676
@@ -5942,6 +6139,15 @@ HPresolve::Result HPresolve::presolve(HighsPostsolveStack& postsolve_stack) {
59426139 if (problemSizeReduction () > 0.05 ) continue ;
59436140 }
59446141
6142+ // Find substitutions from the precedence graph
6143+ if (mipsolver != nullptr &&
6144+ analysis_.allow_rule_ [kPresolveRulePrecedenceCycles ]) {
6145+ storeCurrentProblemSize ();
6146+ createPrecedenceGraph (false );
6147+ HPRESOLVE_CHECKED_CALL (findPrecedenceCycles (postsolve_stack));
6148+ if (problemSizeReduction () > 0.05 ) continue ;
6149+ }
6150+
59456151 // enumerate solutions
59466152 if (mipsolver != nullptr &&
59476153 analysis_.allow_rule_ [kPresolveRuleEnumeration ]) {
@@ -6341,6 +6547,7 @@ HighsModelStatus HPresolve::run(HighsPostsolveStack& postsolve_stack) {
63416547 mipsolver->mipdata_ ->domain .addCutpool (mipsolver->mipdata_ ->cutpool );
63426548 mipsolver->mipdata_ ->domain .addConflictPool (
63436549 mipsolver->mipdata_ ->conflictPool );
6550+ createPrecedenceGraph (true );
63446551
63456552 if (mipsolver->mipdata_ ->numRestarts != 0 ) {
63466553 std::vector<HighsInt> cutinds;
0 commit comments