Fix PDLP capture cublas error and hang on infeasible solutions - #1416
Conversation
📝 WalkthroughWalkthroughThe PR hardens concurrent root LP solving in opportunistic MIP mode and improves CUDA graph robustness. Root LP results are now validated for usability (non-NumericalError status and matching solution sizes), with unusable results signaling B&B to halt its wait-release loop instead of causing deadlock or crash. Root-relaxation handoff and variable clamping are guarded by this usability flag. CUDA graph capture exception handling distinguishes and recovers from capture invalidation separately from other failures. ChangesConcurrent Root LP Safety and CUDA Graph Robustness
🎯 4 (Complex) | ⏱️ ~45 minutes 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
|
/merge |
Summary
Fixes two concurrency bugs in the MIP concurrent root-LP path that could crash or
hang the solver: (1) an intermittent
CUBLAS_STATUS_INTERNAL_ERRORduring PDLPgraph capture, and (2) a deadlock when the concurrent root LP fails to produce a
usable solution (e.g. on an infeasible model). Both are exercised by the same
flow — the heuristics' concurrent root LP (PDLP + cuDSS barrier) running alongside
B&B — and both reproduce independently of each other.
Problem
1. cuBLAS error during graph capture
PDLP captures its adaptive step into a CUDA graph (
manual_cuda_graph_t,cudaStreamCaptureModeThreadLocal) while the cuDSS barrier runs concurrently on aseparate handle. cuDSS's device-synchronizing, library-global work (handle
create/first factorization/destroy, allocations) invalidates PDLP's in-flight
capture. When that invalidation is observed inside a cuBLAS call (cuBLAS can't
return a CUDA error code), it surfaces as
CUBLAS_STATUS_INTERNAL_ERRORthrownfrom
cublasdotinadaptive_step_size_strategy.cu.manual_cuda_graph_t::runalready recovered from this collision, but only whencudaStreamEndCapturereturnedcudaErrorStreamCaptureInvalidated— not when alibrary call threw mid-capture before
EndCapturewas reached. So that surfaceescaped and aborted the solve.
2. Deadlock when the root LP fails
On an infeasible model the barrier's cuDSS factorization fails (
NumericalError,empty primal/dual). The heuristic then unconditionally
raft::copy'dnelementsfrom the empty result, which threw. That exception unwound out of
dm.run_solver()and got parked at the MIPomp taskgroupjoin (it must wait forchild tasks before propagating). Meanwhile B&B's
solve_root_relaxationspins in await loop whose only exits are
root_crossover_solution_set_/root_concurrent_halt— neither of which the (now-thrown) heuristic ever signals. Result: a hang that
ignores the time limit, with no Python traceback (exception stuck mid-unwind).
Solution
1.
manual_cuda_graph.cuh— recover from thrown errors mid-captureWrap
work()intry/catch. On a throw, end the capture and let its statusdisambiguate: if the capture was invalidated, the recorded work was never issued —
drain the sticky error and re-run
workeagerly (no capture, so the concurrent opcan't break it). Otherwise the error is genuine and is rethrown. This routes the
thrown-
CUBLAS_STATUS_INTERNAL_ERRORsurface into the same recovery the cleancudaErrorStreamCaptureInvalidatedpath already used, without keying off cuBLAS'sambiguous status code, and without masking real failures.
2.
diversity_manager.cu— handle an unusable root LP and release B&BAfter the concurrent root LP returns, check whether the result is usable
(
status != NumericalErrorand primal/dual sizes match). When it isn't, skip thecopy/hand-off that previously threw and release B&B's root-relaxation wait via
branch_and_bound_ptr->set_root_concurrent_halt(1), so B&B falls back to its owndual-simplex root instead of deadlocking. When the result is usable, behavior is
unchanged (copy + hand off via the root-relaxation callback).
Testing
main(identical with/without the capture fix).solves, 0 crashes, 0 hangs; every solve now terminates cleanly as
Infeasible(B&B reportsRoot relaxation returned: INFEASIBLE).closes #1396