Skip to content
Closed
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
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ struct mip_submip_hyper_params_t {
// number of simplex iteration from the parent B&B.
f_t iteration_limit_ratio = 0.8;

// If there is not enough variables fixed or we already found an improving solution,
// perform a short DFS to quickly find a feasible solution. This setting controls
// the maximum number of nodes allow for backtracking.
i_t dfs_max_backtrack = 5;

// Run CPU FJ over the sub-MIP
bool enable_cpufj = true;
};
339 changes: 196 additions & 143 deletions cpp/src/branch_and_bound/branch_and_bound.cpp

Large diffs are not rendered by default.

20 changes: 13 additions & 7 deletions cpp/src/branch_and_bound/branch_and_bound.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,14 @@
#include <cuopt/mathematical_optimization/pdlp/solver_settings.hpp>

#include <mip_heuristics/presolve/third_party_presolve.hpp>
#include <mip_heuristics/root_heuristics.hpp>

#include <omp.h>

#include <atomic>
#include <functional>
#include <future>
#include <list>
#include <memory>
#include <vector>

Expand Down Expand Up @@ -370,22 +372,26 @@ class branch_and_bound_t {
bool launch_rins_worker(const std::vector<f_t>& sol);
void set_solution_from_submip(const std::vector<f_t>& solution,
const third_party_presolve_t<i_t, f_t>& presolver,
f_t fixrate,
f_t obj);
f_t fixrate);

// Solve the RINS sub-MIP
void solve_submip(diving_worker_t<i_t, f_t>* worker,
const std::vector<f_t>& current_incumbent,
i_t num_var_fixed,
i_t num_integers,
i_t submip_level,
std::string_view log_prefix);
std::string_view log_prefix,
bool is_root_heuristic);

// Creates and solves the RINS sub-MIP
void rins(diving_worker_t<i_t, f_t>* rins_worker, const std::vector<f_t>& node_solution);

// Get the simplex settings for solving the LP of a single node
simplex::simplex_solver_settings_t<i_t, f_t> get_node_lp_settings();
void rins(diving_worker_t<i_t, f_t>* worker,
const std::vector<f_t>& current_incumbent,
bool is_root_heuristic);

void launch_root_heuristics(const simplex::lp_problem_t<i_t, f_t>& lp,
const std::vector<f_t>& sol,
std::list<root_heuristics_t<i_t, f_t>>& heuristics,
omp_atomic_t<i_t>* worker_count);

// Solve the LP relaxation of a leaf node
simplex::dual_status_t solve_node_lp(mip_node_t<i_t, f_t>* node_ptr,
Expand Down
2 changes: 2 additions & 0 deletions cpp/src/branch_and_bound/worker.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,8 @@ class diving_worker_t : public branch_and_bound_worker_t<i_t, f_t> {
// The best-first worker that is associated with this diving worker. Used for controlling the
// number of active diving workers.
bfs_worker_t<i_t, f_t>* bfs_worker{nullptr};

std::atomic<int> halt = false;
};

struct submip_stats_t {
Expand Down
8 changes: 8 additions & 0 deletions cpp/src/dual_simplex/solve.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,12 @@ f_t compute_user_objective(const lp_problem_t<i_t, f_t>& lp, f_t obj)
return user_obj;
}

template <typename i_t, typename f_t>
f_t compute_solver_objective(const lp_problem_t<i_t, f_t>& lp, f_t user_obj)

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.

Let's not use the term solver_objective

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Maybe "internal" objective?

{
return user_obj / lp.obj_scale - lp.obj_constant;
}

template <typename i_t, typename f_t>
lp_status_t solve_linear_program_advanced(const lp_problem_t<i_t, f_t>& original_lp,
const f_t start_time,
Expand Down Expand Up @@ -813,6 +819,8 @@ template double compute_user_objective<int, double>(const lp_problem_t<int, doub

template double compute_user_objective(const lp_problem_t<int, double>& lp, double obj);

template double compute_solver_objective(const lp_problem_t<int, double>& lp, double user_obj);

template lp_status_t solve_linear_program_advanced(
const lp_problem_t<int, double>& original_lp,
const double start_time,
Expand Down
3 changes: 3 additions & 0 deletions cpp/src/dual_simplex/solve.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ f_t compute_user_objective(const lp_problem_t<i_t, f_t>& lp, const std::vector<f
template <typename i_t, typename f_t>
f_t compute_user_objective(const lp_problem_t<i_t, f_t>& lp, f_t obj);

template <typename i_t, typename f_t>
f_t compute_solver_objective(const lp_problem_t<i_t, f_t>& lp, f_t user_obj);

template <typename i_t, typename f_t>
lp_status_t solve_linear_program_advanced(const lp_problem_t<i_t, f_t>& original_lp,
const f_t start_time,
Expand Down
12 changes: 9 additions & 3 deletions cpp/src/mip_heuristics/feasibility_jump/fj_cpu.cu
Original file line number Diff line number Diff line change
Expand Up @@ -1814,13 +1814,19 @@ void fj_cpu_worker_t<i_t, f_t>::create_worker(
}

template <typename i_t, typename f_t>
void fj_cpu_worker_t<i_t, f_t>::run_async(f_t time_limit, double work_unit_limit)
void fj_cpu_worker_t<i_t, f_t>::run_async(f_t time_limit,
double work_unit_limit,
omp_atomic_t<i_t>* worker_count)
{
if (!fj_cpu) return;

#pragma omp task shared(fj_cpu) firstprivate(time_limit, work_unit_limit) \
if (worker_count) ++(*worker_count);
#pragma omp task shared(fj_cpu) firstprivate(time_limit, work_unit_limit, worker_count) \
priority(CUOPT_DEFAULT_TASK_PRIORITY) default(none) depend(out : *fj_cpu)
cpufj_solve(fj_cpu.get(), time_limit, work_unit_limit);
{
cpufj_solve(fj_cpu.get(), time_limit, work_unit_limit);
if (worker_count) --(*worker_count);
}
}

template <typename i_t, typename f_t>
Expand Down
5 changes: 3 additions & 2 deletions cpp/src/mip_heuristics/feasibility_jump/fj_cpu_worker.cuh
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,9 @@ struct fj_cpu_worker_t {

// Run the worker asynchronously (i.e., launch an openmp task and then continue the
// execution). Call `stop()` for stopping the worker
void run_async(f_t time_limit = std::numeric_limits<f_t>::infinity(),
double work_unit_limit = std::numeric_limits<double>::infinity());
void run_async(f_t time_limit = std::numeric_limits<f_t>::infinity(),
double work_unit_limit = std::numeric_limits<double>::infinity(),
omp_atomic_t<i_t>* worker_count = nullptr);

// Run the CPU FJ synchronously (i.e., wait for it to finish before proceeding)
void run_sync(f_t time_limit = std::numeric_limits<f_t>::infinity(),
Expand Down
57 changes: 57 additions & 0 deletions cpp/src/mip_heuristics/root_heuristics.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/* 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

#include <branch_and_bound/worker.hpp>
#include <dual_simplex/user_problem.hpp>
#include "feasibility_jump/fj_cpu_worker.cuh"

namespace cuopt::mathematical_optimization::mip {

template <typename i_t, typename f_t>
struct root_heuristics_t {
std::unique_ptr<diving_worker_t<i_t, f_t>> submip_worker_;
fj_cpu_worker_t<i_t, f_t> fj_cpu_worker_;
std::vector<simplex::variable_type_t> var_types_;
csr_matrix_t<i_t, f_t> Arow_;

root_heuristics_t(const csr_matrix_t<i_t, f_t>& Arow,
const std::vector<simplex::variable_type_t>& var_types)
: submip_worker_(nullptr), var_types_(var_types), Arow_(Arow) {};

~root_heuristics_t() { stop(); }

void stop()
{
fj_cpu_worker_.stop();
if (submip_worker_) {
submip_worker_->halt = true;
diving_worker_t<i_t, f_t>* worker = submip_worker_.get();
#pragma omp taskwait depend(in : *worker)
}
}

void create_submip_worker(i_t id,
const simplex::lp_problem_t<i_t, f_t>& lp,
const simplex::simplex_solver_settings_t<i_t, f_t>& settings,
f_t root_obj,
const std::vector<simplex::variable_status_t>& root_vstatus,
const std::vector<f_t>& sol)
{
submip_worker_ =
std::make_unique<diving_worker_t<i_t, f_t>>(id, lp, Arow_, var_types_, settings);
submip_worker_->start_node = mip_node_t<i_t, f_t>(root_obj, root_vstatus);
submip_worker_->leaf_vstatus = root_vstatus;
submip_worker_->leaf_solution.x = sol;
submip_worker_->recompute_bounds = false;
submip_worker_->recompute_basis = true;
submip_worker_->search_strategy = search_strategy_t::SUBMIP;
submip_worker_->set_active();
}
};
} // namespace cuopt::mathematical_optimization::mip