| 667 | } |
| 668 | |
| 669 | HPresolve::Result HPresolve::updateColImpliedBounds(HighsInt row, HighsInt col, |
| 670 | double val) { |
| 671 | // propagate implied column bound upper bound if row has an upper bound |
| 672 | double rowLower, rowUpper; |
| 673 | if (!checkUpdateColImpliedBounds(row, &rowLower, &rowUpper)) |
| 674 | return Result::kOk; |
| 675 | |
| 676 | const double threshold = 1000 * primal_feastol; |
| 677 | |
| 678 | auto checkImpliedBound = [&](HighsInt row, HighsInt col, double val, |
| 679 | double rowBnd, double residualAct, |
| 680 | HighsInt direction) { |
| 681 | if (direction * residualAct <= -kHighsInf) return Result::kOk; |
| 682 | double impliedBound = static_cast<double>( |
| 683 | (static_cast<HighsCDouble>(rowBnd) - residualAct) / val); |
| 684 | |
| 685 | if (std::abs(impliedBound) * kHighsTiny > primal_feastol) |
| 686 | return Result::kOk; |
| 687 | |
| 688 | // do not use the implied bound if this a not a model row, since the |
| 689 | // row can be removed and should not be used, e.g., to identify a |
| 690 | // column as implied free |
| 691 | bool useImplBound = mipsolver == nullptr || |
| 692 | mipsolver->mipdata_->postSolveStack.getOrigRowIndex( |
| 693 | row) < mipsolver->orig_model_->num_row_; |
| 694 | |
| 695 | if (direction * val > 0) { |
| 696 | // upper bound |
| 697 | // when solving a MIP, keep (1) tighter bounds on integer variables or (2) |
| 698 | // implied bound (as column bound) |
| 699 | bool updateColBound = |
| 700 | mipsolver != nullptr && |
| 701 | ((model->integrality_[col] != HighsVarType::kContinuous && |
| 702 | impliedBound < model->col_upper_[col] - primal_feastol) || |
| 703 | (!useImplBound && |
| 704 | impliedBound < model->col_upper_[col] - threshold)); |
| 705 | |
| 706 | // only tighten bound if it is tighter by a wide enough margin |
| 707 | if (useImplBound && impliedBound < implColUpper[col] - threshold) |
| 708 | changeImplColUpper(col, impliedBound, row); |
| 709 | if (updateColBound) |
| 710 | HPRESOLVE_CHECKED_CALL(changeColUpper(col, impliedBound)); |
| 711 | } else { |
| 712 | // lower bound |
| 713 | // when solving a MIP, keep (1) tighter bounds on integer variables or (2) |
| 714 | // implied bound (as column bound) |
| 715 | bool updateColBound = |
| 716 | mipsolver != nullptr && |
| 717 | ((model->integrality_[col] != HighsVarType::kContinuous && |
| 718 | impliedBound > model->col_lower_[col] + primal_feastol) || |
| 719 | (!useImplBound && |
| 720 | impliedBound > model->col_lower_[col] + threshold)); |
| 721 | |
| 722 | // only tighten bound if it is tighter by a wide enough margin |
| 723 | if (useImplBound && impliedBound > implColLower[col] + threshold) |
| 724 | changeImplColLower(col, impliedBound, row); |
| 725 | if (updateColBound) |
| 726 | HPRESOLVE_CHECKED_CALL(changeColLower(col, impliedBound)); |
nothing calls this directly
no test coverage detected