Skip to content

Check seeded upper bounds before accepting the first incumbent #2142

Closed

Description

@coderabbitai

Summary

A finite upper bound can be seeded through branch_and_bound_t::set_initial_upper_bound() before a solver-space incumbent exists. accept_new_incumbent() currently accepts the first candidate unconditionally.

Some solution-update paths then retain the seeded upper_bound_ through std::min or fetch_min, while storing the worse candidate in incumbent_. For example, a seeded upper bound of 100 and a first candidate objective of 110 can leave upper_bound_ at 100 and incumbent_.objective at 110.

This behavior predates PR #2131. Address it in a separate follow-up PR.

Required changes

  • Define how a first solver-space incumbent must satisfy a finite seeded upper bound, including the appropriate numerical tolerance.
  • Update accept_new_incumbent() and audit its callers so a worse candidate cannot create an inconsistent incumbent and upper bound.
  • Preserve first-candidate acceptance when no finite upper bound exists.
  • Preserve the objective-lattice, integral-objective, and general-objective comparison rules after an incumbent exists.
  • Check the sub-MIP path, which seeds an upper bound from the main solve before it necessarily has a valid local incumbent.
  • Add regression tests for the first-incumbent behavior.

Affected areas

  • cpp/src/branch_and_bound/branch_and_bound.cpp
  • branch_and_bound_t::accept_new_incumbent
  • branch_and_bound_t::set_initial_upper_bound
  • Heuristic, repaired, tree-search, deterministic, and sub-MIP incumbent handling

Acceptance criteria

  • With a finite seeded upper bound, a first candidate that exceeds the allowed bound is rejected without modifying the incumbent.
  • A first candidate that satisfies the seeded upper bound is handled consistently with the defined tolerance.
  • Without a finite seeded upper bound, the first feasible candidate is accepted.
  • Later incumbent updates retain the existing comparison rules.
  • Regression tests cover candidates above, equal to, and below a seeded upper bound, plus the unseeded case.
  • Tests verify that accepted incumbent objectives and stored upper bounds remain consistent under the defined contract.

Context

Requested by @nguidotti.

Activity

  1. self-assigned this
    on Oct 14, 2026
  2. added theissue type on Oct 14, 2026
  3. added this to the 26.12 milestone on Oct 14, 2026
  4. aliceb-nv commented on Oct 14, 2026

    @aliceb-nv
    Contributor

    Intended design. An upper bound can exist without a corresponding solver-space solution, e.g. in the case of symmetry breaking presolve cutting parts of the polytope, but a user-space solution corresponding to this upper bound will always exist and be returned. In practice no cases where an upper bound had no solver-space solution was observed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingmipnon-breakingIntroduces a non-breaking change

Type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions