@@ -4568,12 +4568,21 @@ HPresolve::Result HPresolve::dualFixing(HighsPostsolveStack& postsolve_stack,
45684568 downLockRow = -1 ;
45694569 upLockRow = -1 ;
45704570
4571+ // consider objective function
4572+ if (model->col_cost_ [col] > 0 )
4573+ numUpLocks++;
4574+ else if (model->col_cost_ [col] < 0 )
4575+ numDownLocks++;
4576+
45714577 // check coefficients
45724578 for (const auto & nz : getColumnVector (col)) {
45734579 // get row index and coefficient
45744580 HighsInt row = nz.index ();
45754581 double val = nz.value ();
45764582
4583+ // skip redundant rows
4584+ if (isRedundant (row)) continue ;
4585+
45774586 // check lhs and rhs for finiteness
45784587 bool lhsFinite = model->row_lower_ [row] != -kHighsInf ;
45794588 bool rhsFinite = model->row_upper_ [row] != kHighsInf ;
@@ -4614,9 +4623,10 @@ HPresolve::Result HPresolve::dualFixing(HighsPostsolveStack& postsolve_stack,
46144623 // lambda for variable substitution
46154624 auto substituteCol = [&](HighsInt col, HighsInt row, HighsInt direction,
46164625 double colBound, double otherColBound) {
4617- // some bookkeeping
4618- HighsInt colNonZerosChecked = 0 ;
4619- HighsInt binVarsTried = 0 ;
4626+ // check lhs and rhs for finiteness
4627+ bool lhsFinite = model->row_lower_ [row] != -kHighsInf ;
4628+ bool rhsFinite = model->row_upper_ [row] != kHighsInf ;
4629+
46204630 // use storeRow and getStoredRow since getRowVector's rowroot[row] would be
46214631 // overwritten by subsequent findNonZero calls, which would produce
46224632 // undefined behavior
@@ -4631,21 +4641,21 @@ HPresolve::Result HPresolve::dualFixing(HighsPostsolveStack& postsolve_stack,
46314641 model->col_upper_ [rowNz.index ()] != 1.0 )
46324642 continue ;
46334643
4634- // skip binary variable if setting it to its lower bound bound does not
4635- // make the row redundant
4636- if (model->row_upper_ [row] != kHighsInf &&
4637- impliedRowBounds.getResidualSumUpperOrig (row, rowNz.index (),
4638- rowNz.value ()) >
4639- model->row_upper_ [row] + primal_feastol)
4644+ // skip binary variable if setting it to its lower bound does not make the
4645+ // row redundant
4646+ if (rhsFinite && impliedRowBounds.getResidualSumUpperOrig (
4647+ row, rowNz.index (), rowNz.value ()) >
4648+ model->row_upper_ [row] + primal_feastol)
46404649 continue ;
4641- if (model->row_lower_ [row] != -kHighsInf &&
4642- impliedRowBounds.getResidualSumLowerOrig (row, rowNz.index (),
4643- rowNz.value ()) <
4644- model->row_lower_ [row] - primal_feastol)
4650+ if (lhsFinite && impliedRowBounds.getResidualSumLowerOrig (
4651+ row, rowNz.index (), rowNz.value ()) <
4652+ model->row_lower_ [row] - primal_feastol)
46454653 continue ;
46464654
4647- // store triplets (row, nonzero, nonzero) in a vector to speed up search
4648- binVarsTried++;
4655+ // now compute the implied lower bound (direction = 1) or implied upper
4656+ // bound (direction = -1) provided that the binary variable is set to its
4657+ // upper bound. store triplets (row, nonzero, nonzero) in a vector to
4658+ // speed up search
46494659 nzs.clear ();
46504660 if (colsize[col] < colsize[rowNz.index ()]) {
46514661 for (const auto & colNz : getColumnVector (col)) {
@@ -4667,12 +4677,11 @@ HPresolve::Result HPresolve::dualFixing(HighsPostsolveStack& postsolve_stack,
46674677 }
46684678 }
46694679
4670- // store best bound
4680+ // find best bound
46714681 double bestBound = -kHighsInf ;
46724682 for (const auto & triplet : nzs) {
46734683 // compute implied bound from row given that the binary variable is at
46744684 // its upper bound
4675- colNonZerosChecked++;
46764685 double rhs = 0.0 ;
46774686 double residual = 0.0 ;
46784687 if (direction * triplet.jval < 0 ) {
@@ -4686,12 +4695,19 @@ HPresolve::Result HPresolve::dualFixing(HighsPostsolveStack& postsolve_stack,
46864695 triplet.jval ) +
46874696 std::min (triplet.kval , 0.0 );
46884697 }
4698+ // direction = 1: compute implied lower bound
4699+ // direction = -1: compute implied upper bound
46894700 double candidateBound = direction * (rhs - residual) / triplet.jval ;
4701+ // remember best bound (note the sign switch for direction < 0 above)
46904702 bestBound = std::max (bestBound, candidateBound);
46914703 }
46924704
4705+ // round bound
4706+ if (model->integrality_ [col] != HighsVarType::kContinuous )
4707+ bestBound = std::ceil (bestBound - primal_feastol);
4708+
46934709 // check if lower / upper bound is implied
4694- if (direction * bestBound >= direction * colBound - primal_feastol) {
4710+ if (bestBound >= direction * colBound - primal_feastol) {
46954711 // substitute variable
46964712 double offset = otherColBound;
46974713 double scale = colBound - otherColBound;
@@ -4785,8 +4801,7 @@ HPresolve::Result HPresolve::dualFixing(HighsPostsolveStack& postsolve_stack,
47854801 computeLocks (col, numDownLocks, numUpLocks, downLockRow, upLockRow);
47864802
47874803 // check if variable can be fixed
4788- if (numDownLocks + (model->col_cost_ [col] < 0 ? 1 : 0 ) == 0 ||
4789- numUpLocks + (model->col_cost_ [col] > 0 ? 1 : 0 ) == 0 ) {
4804+ if (numDownLocks == 0 || numUpLocks == 0 ) {
47904805 // fix variable
47914806 if (numDownLocks == 0 ? fixColToLowerOrUnbounded (postsolve_stack, col)
47924807 : fixColToUpperOrUnbounded (postsolve_stack, col)) {
@@ -4795,22 +4810,19 @@ HPresolve::Result HPresolve::dualFixing(HighsPostsolveStack& postsolve_stack,
47954810 return Result::kDualInfeasible ;
47964811 }
47974812 } else {
4798- if (mipsolver != nullptr && (numDownLocks == 1 || numUpLocks == 1 ) &&
4799- model->col_lower_ [col] != -kHighsInf &&
4813+ if (mipsolver != nullptr && model->col_lower_ [col] != -kHighsInf &&
48004814 model->col_upper_ [col] != kHighsInf ) {
48014815 // try substitution
4802- if (numDownLocks == 1 ) {
4816+ if (numDownLocks == 1 && downLockRow != - 1 ) {
48034817 HPRESOLVE_CHECKED_CALL (substituteCol (col, downLockRow, HighsInt{1 },
48044818 model->col_upper_ [col],
48054819 model->col_lower_ [col]));
4806- if (colDeleted[col]) return Result::kOk ;
4807- }
4808- if (numUpLocks == 1 ) {
4820+ } else if (numUpLocks == 1 && upLockRow != -1 ) {
48094821 HPRESOLVE_CHECKED_CALL (substituteCol (col, upLockRow, HighsInt{-1 },
48104822 model->col_lower_ [col],
48114823 model->col_upper_ [col]));
4812- if (colDeleted[col]) return Result::kOk ;
48134824 }
4825+ if (colDeleted[col]) return Result::kOk ;
48144826 }
48154827 // try to strengthen bounds
48164828 double newBound = 0.0 ;
0 commit comments