Skip to content

Commit 3f3e4c1

Browse files
Fix concurrent root halt on terminal LP status
1 parent dcb97b7 commit 3f3e4c1

2 files changed

Lines changed: 45 additions & 0 deletions

File tree

cpp/src/branch_and_bound/branch_and_bound.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1981,6 +1981,15 @@ lp_status_t branch_and_bound_t<i_t, f_t>::solve_root_relaxation(
19811981
std::string solver_name = "";
19821982

19831983
lp_status_t root_status;
1984+
auto request_root_concurrent_halt = [this](lp_status_t status) {
1985+
if (status == lp_status_t::OPTIMAL || status == lp_status_t::INFEASIBLE ||
1986+
status == lp_status_t::UNBOUNDED || status == lp_status_t::UNBOUNDED_OR_INFEASIBLE ||
1987+
status == lp_status_t::ITERATION_LIMIT || status == lp_status_t::TIME_LIMIT ||
1988+
status == lp_status_t::NUMERICAL_ISSUES || status == lp_status_t::CUTOFF ||
1989+
status == lp_status_t::WORK_LIMIT) {
1990+
set_root_concurrent_halt(1);
1991+
}
1992+
};
19841993

19851994
// Launch a task for solving the root LP relaxation via dual simplex.
19861995
#pragma omp task default(shared) depend(out : root_status) priority(CUOPT_CRITICAL_TASK_PRIORITY)
@@ -1995,6 +2004,7 @@ lp_status_t branch_and_bound_t<i_t, f_t>::solve_root_relaxation(
19952004
root_vstatus_,
19962005
edge_norms_,
19972006
nullptr);
2007+
request_root_concurrent_halt(root_status);
19982008
}
19992009

20002010
// Wait for the root relaxation solution to be sent by the diversity manager or dual simplex

python/libcuopt/libcuopt/tests/test_cli.sh

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,38 @@ cuopt_cli "${RAPIDS_DATASET_ROOT_DIR}"/linear_programming/good-mps-1.lp.bz2 | gr
3131
# Add a for mixed integer programming test with options
3232

3333
cuopt_cli "${RAPIDS_DATASET_ROOT_DIR}"/mip/sample.mps --mip-absolute-gap 0.01 --time-limit 10 | grep -q "Solution objective" || (echo "Expected solution objective not found" && exit 1)
34+
35+
# Regression for opportunistic concurrent root LP solve on an infeasible MILP.
36+
# Default opportunistic mode should return Infeasible instead of aborting.
37+
tmp_mps="$(mktemp "${TMPDIR:-/tmp}/cuopt-infeasible-milp.XXXXXX.mps")"
38+
trap 'rm -f "${tmp_mps}"' EXIT
39+
python3 - "${tmp_mps}" <<'PY'
40+
import sys
41+
42+
path = sys.argv[1]
43+
n = 120
44+
a_vals = [1.0 + ((i * 37) % 90) / 10.0 for i in range(n)]
45+
b_vals = [a + (((i * 17) % 11) - 5.0) / 20.0 for i, a in enumerate(a_vals)]
46+
need_a = 0.8 * sum(a_vals)
47+
cap_b = 0.3 * sum(b_vals)
48+
49+
with open(path, "w", encoding="utf-8") as f:
50+
f.write("NAME INFEAS_MILP\n")
51+
f.write("ROWS\n")
52+
f.write(" N COST\n")
53+
f.write(" G NEED_A\n")
54+
f.write(" L CAP_B\n")
55+
f.write("COLUMNS\n")
56+
f.write(" M1 'MARKER' 'INTORG'\n")
57+
for i, (a, b) in enumerate(zip(a_vals, b_vals)):
58+
f.write(f" x{i:<8} COST 1 NEED_A {a:.17g}\n")
59+
f.write(f" x{i:<8} CAP_B {b:.17g}\n")
60+
f.write(" M2 'MARKER' 'INTEND'\n")
61+
f.write("RHS\n")
62+
f.write(f" RHS NEED_A {need_a:.17g} CAP_B {cap_b:.17g}\n")
63+
f.write("BOUNDS\n")
64+
for i in range(n):
65+
f.write(f" UP BND x{i:<8} 1\n")
66+
f.write("ENDATA\n")
67+
PY
68+
cuopt_cli "${tmp_mps}" --time-limit 30 | grep -q "Infeasible" || (echo "Expected infeasible status for opportunistic MILP" && exit 1)

0 commit comments

Comments
 (0)