Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion cpp/src/branch_and_bound/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

set(BRANCH_AND_BOUND_SRC_FILES
${CMAKE_CURRENT_SOURCE_DIR}/branch_and_bound.cpp
${CMAKE_CURRENT_SOURCE_DIR}/mip_node.cpp
${CMAKE_CURRENT_SOURCE_DIR}/pseudo_costs.cpp
${CMAKE_CURRENT_SOURCE_DIR}/diving_heuristics.cpp
)
Expand Down
57 changes: 34 additions & 23 deletions cpp/src/branch_and_bound/branch_and_bound.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
/* clang-format on */

#include <branch_and_bound/branch_and_bound.hpp>
#include <branch_and_bound/diving_heuristics.hpp>
#include <branch_and_bound/mip_node.hpp>
#include <branch_and_bound/pseudo_costs.hpp>

Expand Down Expand Up @@ -35,15 +36,12 @@
#include <deque>
#include <future>
#include <limits>
#include <map>
#include <optional>
#include <string>
#include <thread>
#include <unordered_map>
#include <vector>

namespace cuopt::linear_programming::dual_simplex {

namespace {

template <typename f_t>
Expand Down Expand Up @@ -1040,7 +1038,9 @@ struct deterministic_bfs_policy_t
const std::vector<i_t>& fractional,
const std::vector<f_t>& x) override
{
i_t var = this->worker.pc_snapshot.variable_selection(fractional, x);
logger_t log;
log.log = false;
i_t var = this->worker.pc_snapshot.variable_selection(fractional, x, log);
auto dir = martin_criteria(x[var], this->bnb.root_relax_soln_.x[var]);
return {var, dir};
}
Expand All @@ -1049,8 +1049,10 @@ struct deterministic_bfs_policy_t
const std::vector<i_t>& fractional,
const std::vector<f_t>& x) override
{
logger_t log;
log.log = false;
node->objective_estimate =
this->worker.pc_snapshot.obj_estimate(fractional, x, node->lower_bound);
this->worker.pc_snapshot.obj_estimate(fractional, x, node->lower_bound, log);
}

void on_node_completed(mip_node_t<i_t, f_t>* node,
Expand Down Expand Up @@ -1115,33 +1117,36 @@ struct deterministic_diving_policy_t
const std::vector<i_t>& fractional,
const std::vector<f_t>& x) override
{
logger_t log;
log.log = false;

switch (this->worker.diving_type) {
case search_strategy_t::PSEUDOCOST_DIVING:
return this->worker.variable_selection_from_snapshot(fractional, x);
return pseudocost_diving(
this->worker.pc_snapshot, fractional, x, *this->worker.root_solution, log);

case search_strategy_t::LINE_SEARCH_DIVING:
if (this->worker.root_solution) {
logger_t log;
log.log = false;
return line_search_diving<i_t, f_t>(fractional, x, *this->worker.root_solution, log);
}
return this->worker.variable_selection_from_snapshot(fractional, x);
return line_search_diving<i_t, f_t>(fractional, x, *this->worker.root_solution, log);

case search_strategy_t::GUIDED_DIVING:
return this->worker.guided_variable_selection(fractional, x);
if (this->worker.incumbent_snapshot.empty()) {
return pseudocost_diving(
this->worker.pc_snapshot, fractional, x, *this->worker.root_solution, log);
} else {
return guided_diving(
this->worker.pc_snapshot, fractional, x, this->worker.incumbent_snapshot, log);
}

case search_strategy_t::COEFFICIENT_DIVING: {
logger_t log;
log.log = false;
return coefficient_diving<i_t, f_t>(this->bnb.original_lp_,
return coefficient_diving<i_t, f_t>(this->worker.leaf_problem,
fractional,
x,
this->bnb.var_up_locks_,
this->bnb.var_down_locks_,
log);
}

default: return this->worker.variable_selection_from_snapshot(fractional, x);
default: CUOPT_LOG_ERROR("Invalid diving method!"); return {-1, rounding_direction_t::NONE};
Comment thread
nguidotti marked this conversation as resolved.
}
}

Expand Down Expand Up @@ -1189,6 +1194,9 @@ std::pair<node_status_t, rounding_direction_t> branch_and_bound_t<i_t, f_t>::upd
node_status_t status = node_status_t::PENDING;
rounding_direction_t round_dir = rounding_direction_t::NONE;

worker->recompute_basis = true;
worker->recompute_bounds = true;
Comment on lines +1197 to +1198
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this could be turned into a member function of worker (or a policy function)? something like invalidate_basis or something of the sort (probably not an ideal suggestion, but I feel like we could be higher-level here)


if (lp_status == dual::status_t::DUAL_UNBOUNDED) {
node_ptr->lower_bound = inf;
policy.graphviz(search_tree, node_ptr, "infeasible", 0.0);
Expand Down Expand Up @@ -1248,6 +1256,8 @@ std::pair<node_status_t, rounding_direction_t> branch_and_bound_t<i_t, f_t>::upd
assert(dir != rounding_direction_t::NONE);

policy.update_objective_estimate(node_ptr, leaf_fractional, leaf_solution.x);
worker->recompute_basis = false;
worker->recompute_bounds = false;

logger_t log;
log.log = false;
Expand Down Expand Up @@ -2507,7 +2517,7 @@ mip_status_t branch_and_bound_t<i_t, f_t>::solve(mip_solution_t<i_t, f_t>& solut
set_uninitialized_steepest_edge_norms(original_lp_, basic_list, edge_norms_);

pc_.resize(original_lp_.num_cols);
original_lp_.A.transpose(pc_.AT);
original_lp_.A.transpose(*pc_.AT);
{
raft::common::nvtx::range scope_sb("BB::strong_branching");
strong_branching<i_t, f_t>(original_lp_,
Expand Down Expand Up @@ -3322,11 +3332,12 @@ template <typename PoolT>
void branch_and_bound_t<i_t, f_t>::deterministic_broadcast_snapshots(
PoolT& pool, const std::vector<f_t>& incumbent_snapshot)
{
deterministic_snapshot_t<i_t, f_t> snap;
snap.upper_bound = upper_bound_.load();
snap.total_lp_iters = exploration_stats_.total_lp_iters.load();
snap.incumbent = incumbent_snapshot;
snap.pc_snapshot = pc_.create_snapshot();
deterministic_snapshot_t<i_t, f_t> snap{
.upper_bound = upper_bound_,
.pc_snapshot = pc_,
.incumbent = incumbent_snapshot,
.total_lp_iters = exploration_stats_.total_lp_iters,
};

for (auto& worker : pool) {
worker.set_snapshots(snap);
Expand Down
6 changes: 3 additions & 3 deletions cpp/src/branch_and_bound/branch_and_bound.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@
#pragma once

#include <branch_and_bound/bb_event.hpp>
#include <branch_and_bound/branch_and_bound_worker.hpp>
#include <branch_and_bound/deterministic_workers.hpp>
#include <branch_and_bound/diving_heuristics.hpp>
#include <branch_and_bound/mip_node.hpp>
#include <branch_and_bound/node_queue.hpp>
#include <branch_and_bound/pseudo_costs.hpp>
#include <branch_and_bound/worker.hpp>
#include <branch_and_bound/worker_pool.hpp>

#include <cuts/cuts.hpp>

Expand Down Expand Up @@ -107,7 +107,7 @@ class branch_and_bound_t {
}
}

// Set a solution based on the user problem during the course of the solve
// Set a solution based on the user problem during solve time
void set_new_solution(const std::vector<f_t>& solution);

// This queues the solution to be processed at the correct work unit timestamp
Expand Down
31 changes: 31 additions & 0 deletions cpp/src/branch_and_bound/constants.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/* clang-format off */
/*
* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
/* clang-format on */

#pragma once

namespace cuopt::linear_programming::dual_simplex {

constexpr int num_search_strategies = 5;

// Indicate the search and variable selection algorithms used by each thread
// in B&B (See [1]).
//
// [1] T. Achterberg, “Constraint Integer Programming,” PhD, Technischen Universität Berlin,
// Berlin, 2007. doi: 10.14279/depositonce-1634.
enum search_strategy_t : int {
BEST_FIRST = 0, // Best-First + Plunging.
PSEUDOCOST_DIVING = 1, // Pseudocost diving (9.2.5)
LINE_SEARCH_DIVING = 2, // Line search diving (9.2.4)
GUIDED_DIVING = 3, // Guided diving (9.2.3).
COEFFICIENT_DIVING = 4 // Coefficient diving (9.2.1)
};

enum class rounding_direction_t { NONE = -1, DOWN = 0, UP = 1 };

enum class branch_and_bound_mode_t { PARALLEL = 0, DETERMINISTIC = 1 };

} // namespace cuopt::linear_programming::dual_simplex
24 changes: 4 additions & 20 deletions cpp/src/branch_and_bound/deterministic_workers.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@
#pragma once

#include <branch_and_bound/bb_event.hpp>
#include <branch_and_bound/branch_and_bound_worker.hpp>
#include <branch_and_bound/diving_heuristics.hpp>
#include <branch_and_bound/node_queue.hpp>
#include <branch_and_bound/worker.hpp>

#include <utilities/work_limit_context.hpp>

Expand Down Expand Up @@ -58,7 +58,7 @@ struct deterministic_snapshot_t {
f_t upper_bound;
pseudo_cost_snapshot_t<i_t, f_t> pc_snapshot;
std::vector<f_t> incumbent;
i_t total_lp_iters;
int64_t total_lp_iters;
};
Comment thread
nguidotti marked this conversation as resolved.

template <typename i_t, typename f_t, typename Derived>
Expand All @@ -74,7 +74,7 @@ class deterministic_worker_base_t : public branch_and_bound_worker_t<i_t, f_t> {

// Diving-specific snapshots (ignored by BFS workers)
std::vector<f_t> incumbent_snapshot;
i_t total_lp_iters_snapshot{0};
int64_t total_lp_iters_snapshot{0};

std::vector<queued_integer_solution_t<i_t, f_t>> integer_solutions;
int next_solution_seq{0};
Expand All @@ -90,7 +90,7 @@ class deterministic_worker_base_t : public branch_and_bound_worker_t<i_t, f_t> {
const std::vector<variable_type_t>& var_types,
const simplex_solver_settings_t<i_t, f_t>& settings,
const std::string& context_name)
: base_t(id, original_lp, Arow, var_types, settings), work_context(context_name)
: base_t(id, original_lp, Arow, var_types, settings), work_context(context_name), pc_snapshot(1)
{
work_context.deterministic = true;
}
Expand Down Expand Up @@ -342,22 +342,6 @@ class deterministic_diving_worker_t
{objective, solution, depth, this->worker_id, this->next_solution_seq++});
++this->total_integer_solutions;
}

branch_variable_t<i_t> variable_selection_from_snapshot(const std::vector<i_t>& fractional,
const std::vector<f_t>& solution) const
{
assert(root_solution != nullptr);
return this->pc_snapshot.pseudocost_diving(fractional, solution, *root_solution);
}

branch_variable_t<i_t> guided_variable_selection(const std::vector<i_t>& fractional,
const std::vector<f_t>& solution) const
{
if (this->incumbent_snapshot.empty()) {
return variable_selection_from_snapshot(fractional, solution);
}
return this->pc_snapshot.guided_diving(fractional, solution, this->incumbent_snapshot);
}
};

template <typename i_t, typename f_t, typename WorkerT, typename Derived>
Expand Down
Loading