Repository navigation
Limit nodes in the RENS/RINS depth-first search - #3445
Conversation
When RENS or RINS cannot fix enough columns for a sub-MIP they run HighsSearch::solveDepthFirst(10), which dives with no node limit. A dive that tightens one general-integer bound by a single unit per node can then run until the time limit. solveDepthFirst gets an optional node limit (default unlimited, so other callers are unchanged); RENS and RINS pass 1000. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
003722e to
de1cee2
Compare
|
@DoumarCharles I've been meaning to look into budgets for this bit of the code for a while...... I think adding a limit of 1000 here is appropriate. Can you remove the comments that you've added? (I don't believe they're necessary). |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Done - I’ve removed the comments. Happy to adjust anything else, e.g. if you’d prefer the limit as a named option rather than a constant. In practice this makes a real difference for our single-threaded runs as it prevented cases from timing out. |
This is a good outcome. It's interesting that users are able to report increasingly detailed information about why HiGHS fails or is slow on their instances. We can't adopt all fixes, as they may slow HiGHS down generally, but here you've landed on something useful |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## latest #3445 +/- ##
=======================================
Coverage 75.10% 75.11%
=======================================
Files 460 460
Lines 112008 112031 +23
Branches 17757 17759 +2
=======================================
+ Hits 84125 84153 +28
+ Misses 27601 27596 -5
Partials 282 282 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Opt-Mucca
left a comment
There was a problem hiding this comment.
@DoumarCharles looks good! Thanks for making the changes.
Description
RENS/RINS depth-first search can run until the time limit
Issue
On some MIPs with general-integer columns of wide range, HiGHS spends most of its
time in a single heuristic dive at the root, although the root bound and a good
incumbent are already available. On the attached model the solve takes about 20 s
instead of about 3 s; with a shorter time limit the run ends at the limit instead.
I see it in single-threaded runs (
threads = 1, as in WebAssembly builds);whether a given model stalls depends on the search path, so on the thread count
and the random seed.
Reproduction (attached
g101_0188.lp, 2.4 KB, g101_0188.zip a synthetic batching model:orders split over production lines, line capacities, items made in whole batches;
all columns general integer):
(Linux, clang, Release; two runs each.)
Cause. When RENS or RINS cannot fix enough columns to build a sub-MIP, they run
HighsSearch::solveDepthFirst(10)on the heuristic search. That callsdive()withno node limit (the main branch-and-bound passes one). In the stalled dive the same
general-integer column is chosen again and again: each node tightens its bound by
one unit, the LP stays feasible with the same fractional part, so the dive never
reaches a leaf and the backtrack limit of
solveDepthFirstnever applies. Onlythe time limit or the column's bound range stops it (on the attached model: a
single dive of more than 130,000 nodes, depth growing by one per node).
The earlier mitigation for long dives in the main search (1adcbd0: node limit per
batch with one worker, faster reduced domain-change stacks) is already in latest;
it does not cover this depth-first search inside RENS/RINS.
Change
HighsSearch::solveDepthFirstgets an optional node limit (default: unlimited,so other callers are unchanged), passed to
dive(); the search stops once it isreached.
depth-first search in these heuristics finishes in tens of nodes.
Stopping the heuristic early only means it may return without a new solution;
the main branch-and-bound continues as before.
Testing
ctest: 169/169 passed;unit_tests: all 441 test cases passed (latest 726863d,Release, clang, Linux).
seeds 0–4, scanned for heuristic dives ≥ 2,000 nodes deep; 9 found). With a
short time limit, 3 runs that hit it on latest now finish Optimal in 3–5 s
(2 of them with a better solution), 2 others get faster (3.25 → 0.62 s,
2.96 → 1.34 s), 3 stay at the limit either way (hard proofs, unrelated to the
heuristic).
previous latest 8fd6a9a, before the rebase; none of the commits since touch the
changed files). Selection: the 41 instances latest solves to
optimality within 60 s (threads 1, seed 0). Comparison at 120 s, threads 1,
seeds 0/1/2, plus default threads seed 0: identical solved counts (41/40/38
and 41), no objective differences, and an identical search in every finished
run (node counts, bounds and LP iterations equal); the node limit never takes
effect on these instances. Shifted geometric mean of solve time (shift 1 s),
patch / latest: 0.999 (threads 1), 1.003 (default threads).
dive; patch Optimal 15055369 in under 5 s).
Checklist
latestbranch