Skip to content

Commit 1cc9137

Browse files
authored
Fix issue with incorrect lower bound on air05 (#890)
Fix an issue with incorrect lower bound on air05. In 26.02, with the new scheduler worker model, it looks like the lower bound is not computed correctly if there are nodes still remaining in the `node_queue`. This PR fathoms all the nodes and pulls the lower bound from the search tree. In 26.02 we were also incorrectly fathoming nodes with objectives less than the optimal objective, due to the `rel_gap` check. This PR removes that check. In 26.04 we were incorrectly setting the cutoff to one less than it should be when the objective was integral. This PR fixes that bug. Authors: - Chris Maes (https://github.com/chris-maes) Approvers: - Alice Boucher (https://github.com/aliceb-nv) URL: #890
1 parent ab7edbf commit 1cc9137

File tree

2 files changed

+32
-11
lines changed

2 files changed

+32
-11
lines changed

cpp/src/branch_and_bound/branch_and_bound.cpp

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1317,7 +1317,7 @@ dual::status_t branch_and_bound_t<i_t, f_t>::solve_node_lp(
13171317
simplex_solver_settings_t lp_settings = settings_;
13181318
lp_settings.set_log(false);
13191319
if (original_lp_.objective_is_integral) {
1320-
lp_settings.cut_off = std::ceil(upper_bound_ - settings_.integer_tol) - 1 + settings_.dual_tol;
1320+
lp_settings.cut_off = std::ceil(upper_bound_ - settings_.integer_tol) + settings_.dual_tol;
13211321
} else {
13221322
lp_settings.cut_off = upper_bound_ + settings_.dual_tol;
13231323
}
@@ -1426,7 +1426,7 @@ void branch_and_bound_t<i_t, f_t>::plunge_with(branch_and_bound_worker_t<i_t, f_
14261426
// - The lower bound of the parent is lower or equal to its children
14271427
worker->lower_bound = lower_bound;
14281428

1429-
if (lower_bound > upper_bound || rel_gap < settings_.relative_mip_gap_tol) {
1429+
if (lower_bound > upper_bound) {
14301430
search_tree_.graphviz_node(settings_.log, node_ptr, "cutoff", node_ptr->lower_bound);
14311431
search_tree_.update(node_ptr, node_status_t::FATHOMED);
14321432
worker->recompute_basis = true;
@@ -1536,7 +1536,7 @@ void branch_and_bound_t<i_t, f_t>::dive_with(branch_and_bound_worker_t<i_t, f_t>
15361536
f_t rel_gap = user_relative_gap(original_lp_, upper_bound, lower_bound);
15371537
worker->lower_bound = lower_bound;
15381538

1539-
if (node_ptr->lower_bound > upper_bound || rel_gap < settings_.relative_mip_gap_tol) {
1539+
if (node_ptr->lower_bound > upper_bound) {
15401540
worker->recompute_basis = true;
15411541
worker->recompute_bounds = true;
15421542
continue;
@@ -2471,8 +2471,30 @@ mip_status_t branch_and_bound_t<i_t, f_t>::solve(mip_solution_t<i_t, f_t>& solut
24712471
lower_bound = deterministic_compute_lower_bound();
24722472
solver_status_ = deterministic_global_termination_status_;
24732473
} else {
2474-
lower_bound = node_queue_.best_first_queue_size() > 0 ? node_queue_.get_lower_bound()
2475-
: search_tree_.root.lower_bound;
2474+
if (node_queue_.best_first_queue_size() > 0) {
2475+
// We need to clear the queue and use the info in the search tree for the lower bound
2476+
while (node_queue_.best_first_queue_size() > 0) {
2477+
std::optional<mip_node_t<i_t, f_t>*> start_node = node_queue_.pop_best_first();
2478+
2479+
if (!start_node.has_value()) { continue; }
2480+
if (upper_bound_ < start_node.value()->lower_bound) {
2481+
// This node was put on the heap earlier but its lower bound is now greater than the
2482+
// current upper bound
2483+
search_tree_.graphviz_node(
2484+
settings_.log, start_node.value(), "cutoff", start_node.value()->lower_bound);
2485+
search_tree_.update(start_node.value(), node_status_t::FATHOMED);
2486+
continue;
2487+
} else {
2488+
node_queue_.push(
2489+
start_node.value()); // Needed to ensure we don't lose the correct lower bound
2490+
break;
2491+
}
2492+
}
2493+
lower_bound = node_queue_.best_first_queue_size() > 0 ? node_queue_.get_lower_bound()
2494+
: search_tree_.root.lower_bound;
2495+
} else {
2496+
lower_bound = search_tree_.root.lower_bound;
2497+
}
24762498
}
24772499
set_final_solution(solution, lower_bound);
24782500
return solver_status_;
@@ -2781,7 +2803,7 @@ void branch_and_bound_t<i_t, f_t>::run_deterministic_bfs_loop(
27812803

27822804
f_t upper_bound = worker.local_upper_bound;
27832805
f_t rel_gap = user_relative_gap(original_lp_, upper_bound, node->lower_bound);
2784-
if (node->lower_bound > upper_bound || rel_gap < settings_.relative_mip_gap_tol) {
2806+
if (node->lower_bound > upper_bound) {
27852807
worker.current_node = nullptr;
27862808
worker.record_fathomed(node, node->lower_bound);
27872809
search_tree.update(node, node_status_t::FATHOMED);
@@ -3570,8 +3592,7 @@ void branch_and_bound_t<i_t, f_t>::deterministic_dive(
35703592

35713593
// Prune check using snapshot upper bound
35723594
f_t rel_gap = user_relative_gap(original_lp_, worker.local_upper_bound, node_ptr->lower_bound);
3573-
if (node_ptr->lower_bound > worker.local_upper_bound ||
3574-
rel_gap < settings_.relative_mip_gap_tol) {
3595+
if (node_ptr->lower_bound > worker.local_upper_bound) {
35753596
worker.recompute_bounds_and_basis = true;
35763597
continue;
35773598
}

cpp/src/pdlp/restart_strategy/pdlp_restart_strategy.cu

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1995,14 +1995,14 @@ void pdlp_restart_strategy_t<i_t, f_t>::solve_bound_constrained_trust_region(
19951995
f_t* end = threshold_.data() + primal_size_h_ + dual_size_h_;
19961996
auto highest_negInf_primal =
19971997
thrust::find(handle_ptr_->get_thrust_policy(),
1998-
thrust::make_reverse_iterator(thrust::device_ptr<f_t>(end)),
1999-
thrust::make_reverse_iterator(thrust::device_ptr<f_t>(start)),
1998+
cuda::std::reverse_iterator(thrust::device_ptr<f_t>(end)),
1999+
cuda::std::reverse_iterator(thrust::device_ptr<f_t>(start)),
20002000
-std::numeric_limits<f_t>::infinity());
20012001

20022002
// Set ranges accordingly
20032003
i_t index_start_primal = 0;
20042004
i_t index_end_primal = primal_size_h_ + dual_size_h_;
2005-
if (highest_negInf_primal != thrust::make_reverse_iterator(thrust::device_ptr<f_t>(start))) {
2005+
if (highest_negInf_primal != cuda::std::reverse_iterator(thrust::device_ptr<f_t>(start))) {
20062006
cuopt_assert(device_to_host_value(thrust::raw_pointer_cast(&*highest_negInf_primal)) ==
20072007
-std::numeric_limits<f_t>::infinity(),
20082008
"Incorrect primal reverse iterator");

0 commit comments

Comments
 (0)