From 3373f3c0e6daecff4100811cca66f9c6cf0f4266 Mon Sep 17 00:00:00 2001 From: "Nicolas L. Guidotti" Date: Thu, 15 Oct 2026 15:29:38 +0200 Subject: [PATCH 1/2] treat infeasible and cutoff nodes in the same way and set the node lower bound to the global upper bound. Signed-off-by: Nicolas L. Guidotti --- cpp/src/branch_and_bound/branch_and_bound.cpp | 18 ++++++++---------- .../branch_and_bound/deterministic_workers.hpp | 9 --------- cpp/src/branch_and_bound/mip_node.hpp | 9 ++++----- 3 files changed, 12 insertions(+), 24 deletions(-) diff --git a/cpp/src/branch_and_bound/branch_and_bound.cpp b/cpp/src/branch_and_bound/branch_and_bound.cpp index 5880b3f924..c1ce4d8e15 100644 --- a/cpp/src/branch_and_bound/branch_and_bound.cpp +++ b/cpp/src/branch_and_bound/branch_and_bound.cpp @@ -1212,7 +1212,6 @@ struct deterministic_bfs_policy_t branch_direction_t dir) override { switch (status) { - case node_status_t::INFEASIBLE: this->worker.record_infeasible(node); break; case node_status_t::FATHOMED: this->worker.record_fathomed(node, node->lower_bound); break; case node_status_t::INTEGER_FEASIBLE: this->worker.record_integer_solution(node, node->lower_bound); @@ -1384,15 +1383,14 @@ std::pair branch_and_bound_t::updat worker->recompute_bounds = true; if (lp_status == dual_status_t::DUAL_UNBOUNDED) { - node_ptr->lower_bound = inf; - policy.graphviz(search_tree, node_ptr, "infeasible", 0.0); - search_tree.update(node_ptr, node_status_t::INFEASIBLE); - status = node_status_t::INFEASIBLE; + node_ptr->lower_bound = upper_bound; + policy.graphviz(search_tree, node_ptr, "infeasible", upper_bound); + search_tree.update(node_ptr, node_status_t::FATHOMED); + status = node_status_t::FATHOMED; } else if (lp_status == dual_status_t::CUTOFF) { - f_t leaf_obj = compute_objective(leaf_problem, leaf_solution.x); node_ptr->lower_bound = upper_bound; - policy.graphviz(search_tree, node_ptr, "cut off", leaf_obj); + policy.graphviz(search_tree, node_ptr, "cut off", upper_bound); search_tree.update(node_ptr, node_status_t::FATHOMED); status = node_status_t::FATHOMED; @@ -5275,12 +5273,12 @@ node_status_t branch_and_bound_t::solve_node_deterministic( if (!feasible) { node_ptr->lower_bound = std::numeric_limits::infinity(); - search_tree.update(node_ptr, node_status_t::INFEASIBLE); - worker.record_infeasible(node_ptr); + search_tree.update(node_ptr, node_status_t::FATHOMED); + worker.record_fathomed(node_ptr, worker.local_upper_bound); --exploration_stats_.nodes_unexplored; ++exploration_stats_.nodes_explored; worker.recompute_bounds_and_basis = true; - return node_status_t::INFEASIBLE; + return node_status_t::FATHOMED; } // Solve LP relaxation diff --git a/cpp/src/branch_and_bound/deterministic_workers.hpp b/cpp/src/branch_and_bound/deterministic_workers.hpp index 200e64b472..2dbc1bf14a 100644 --- a/cpp/src/branch_and_bound/deterministic_workers.hpp +++ b/cpp/src/branch_and_bound/deterministic_workers.hpp @@ -262,15 +262,6 @@ class deterministic_bfs_worker_t ++total_nodes_pruned; } - void record_infeasible(mip_node_t* node) - { - record_event( - bb_event_t::make_infeasible(this->clock, this->worker_id, node->creation_seq)); - ++nodes_processed_this_horizon; - ++this->total_nodes_processed; - ++total_nodes_infeasible; - } - void record_numerical(mip_node_t* node) { record_event( diff --git a/cpp/src/branch_and_bound/mip_node.hpp b/cpp/src/branch_and_bound/mip_node.hpp index 21d74c0e1d..57fda8c8ba 100644 --- a/cpp/src/branch_and_bound/mip_node.hpp +++ b/cpp/src/branch_and_bound/mip_node.hpp @@ -26,16 +26,15 @@ namespace cuopt::mathematical_optimization::mip { enum class node_status_t : int { PENDING = 0, // Node is still in the tree, waiting to be solved INTEGER_FEASIBLE = 1, // Node has an integer feasible solution - INFEASIBLE = 2, // Node is infeasible - FATHOMED = 3, // Node objective is greater than the upper bound - HAS_CHILDREN = 4, // Node has children to explore - NUMERICAL = 5 // Encountered numerical issue when solving the LP relaxation + FATHOMED = 2, // Node is infeasible or has objective is greater than the upper bound + HAS_CHILDREN = 3, // Node has children to explore + NUMERICAL = 4 // Encountered numerical issue when solving the LP relaxation }; inline bool inactive_status(node_status_t status) { return (status == node_status_t::FATHOMED || status == node_status_t::INTEGER_FEASIBLE || - status == node_status_t::INFEASIBLE || status == node_status_t::NUMERICAL); + status == node_status_t::NUMERICAL); } template From 6bc53eec712c7cbdac59c7c47b82e2b8604134a5 Mon Sep 17 00:00:00 2001 From: "Nicolas L. Guidotti" Date: Thu, 15 Oct 2026 15:57:54 +0200 Subject: [PATCH 2/2] correctly set the node lower bound in determinism mode Signed-off-by: Nicolas L. Guidotti --- cpp/src/branch_and_bound/branch_and_bound.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/src/branch_and_bound/branch_and_bound.cpp b/cpp/src/branch_and_bound/branch_and_bound.cpp index c1ce4d8e15..4fe410c281 100644 --- a/cpp/src/branch_and_bound/branch_and_bound.cpp +++ b/cpp/src/branch_and_bound/branch_and_bound.cpp @@ -5272,7 +5272,7 @@ node_status_t branch_and_bound_t::solve_node_deterministic( #endif if (!feasible) { - node_ptr->lower_bound = std::numeric_limits::infinity(); + node_ptr->lower_bound = worker.local_upper_bound; search_tree.update(node_ptr, node_status_t::FATHOMED); worker.record_fathomed(node_ptr, worker.local_upper_bound); --exploration_stats_.nodes_unexplored;