Skip to content
Open
Show file tree
Hide file tree
Changes from 83 commits
Commits
Show all changes
92 commits
Select commit Hold shift + click to select a range
90dc7d1
initial
aliceb-nv Oct 6, 2025
73189ca
tweaks
aliceb-nv Oct 7, 2025
8a2b914
added rins class and callback
aliceb-nv Oct 8, 2025
22764d8
initial working
aliceb-nv Oct 8, 2025
a6e3381
objective cut
aliceb-nv Oct 9, 2025
eed48a5
take from external queue
aliceb-nv Oct 16, 2025
b91c057
assert moved
aliceb-nv Oct 16, 2025
0cea762
tuning tweaks
aliceb-nv Oct 17, 2025
af84d2f
fix incorrect node counting in RINS
aliceb-nv Oct 17, 2025
0a6e7d9
rins fjcpu thread and crash fix
aliceb-nv Oct 17, 2025
deebc2c
looser optimality gap tolerance to solve submips more quickly
aliceb-nv Oct 17, 2025
fd3aa5f
protect population vector access w/ mutex
aliceb-nv Oct 17, 2025
721f22b
fix empty pop crash
aliceb-nv Oct 17, 2025
141d6f2
add missing cudaSetDevice call and further guards
aliceb-nv Oct 17, 2025
f6e73f3
streamline population locks
aliceb-nv Oct 17, 2025
2215477
bump 1
aliceb-nv Oct 18, 2025
5069666
bump 2
aliceb-nv Oct 18, 2025
80dd8bd
contention tweaks
aliceb-nv Oct 18, 2025
e4f2eab
bump
aliceb-nv Oct 18, 2025
4ad54bf
remove excessive locks, fix bnb bug
aliceb-nv Oct 20, 2025
87c6b2f
more RINS threads
aliceb-nv Oct 20, 2025
b016849
node limit
aliceb-nv Oct 20, 2025
2028250
random shuffle
aliceb-nv Oct 20, 2025
427cc84
tweaks
aliceb-nv Oct 20, 2025
f2ec2d7
more threads
aliceb-nv Oct 20, 2025
4c1ba90
no objectve cut
aliceb-nv Oct 20, 2025
2734212
multireference rins
aliceb-nv Oct 20, 2025
b612ef8
reduced fixing rates
aliceb-nv Oct 20, 2025
390ca44
scratch fj restart
aliceb-nv Oct 20, 2025
e2059b5
restoring tuning parameters
aliceb-nv Oct 21, 2025
a9250d4
restoring
aliceb-nv Oct 21, 2025
2504de8
restoring
aliceb-nv Oct 21, 2025
15e8217
no restart
aliceb-nv Oct 21, 2025
686ad39
full restore
aliceb-nv Oct 21, 2025
1704631
bump 1
aliceb-nv Oct 21, 2025
3cf1c09
bump 2
aliceb-nv Oct 21, 2025
b956793
bump 3
aliceb-nv Oct 21, 2025
72e0605
deadlock fix
aliceb-nv Oct 21, 2025
3c90ce6
bump 1
aliceb-nv Oct 21, 2025
44a8549
bump2
aliceb-nv Oct 21, 2025
7ba4e0b
bump3
aliceb-nv Oct 21, 2025
bbb0d79
Revert "restoring"
aliceb-nv Oct 21, 2025
0934775
Revert "restoring"
aliceb-nv Oct 21, 2025
7b19151
tmp
aliceb-nv Oct 21, 2025
d104899
Merge branch 'main' into RINS
aliceb-nv Oct 22, 2025
c9ff269
fix cpufj sol queue bug
aliceb-nv Oct 22, 2025
badbdd2
cleanup
aliceb-nv Oct 22, 2025
43a71d5
bump
aliceb-nv Oct 22, 2025
5831e6d
bump 2
aliceb-nv Oct 22, 2025
e6d9bab
bump 3
aliceb-nv Oct 22, 2025
ab210a0
minor cleanup
aliceb-nv Oct 22, 2025
8198b44
address review comments
aliceb-nv Oct 22, 2025
4ffa79a
fix typo
aliceb-nv Oct 22, 2025
07ee172
node limit
aliceb-nv Oct 22, 2025
c4c89d3
more threads
aliceb-nv Oct 22, 2025
3c03846
restore
aliceb-nv Oct 22, 2025
d1862fe
Merge branch 'main' into RINS
rgsl888prabhu Oct 22, 2025
a7d18cf
Update pyproject.toml
rgsl888prabhu Oct 22, 2025
50ab8dc
fix b&b assert
aliceb-nv Oct 23, 2025
e438989
fix unneeded mutex call
aliceb-nv Oct 23, 2025
55ec54e
Merge branch 'main' into RINS
aliceb-nv Oct 24, 2025
1e8043d
w/ stable3
aliceb-nv Oct 23, 2025
2d1571a
bump 1
aliceb-nv Oct 23, 2025
8df41b4
bump 2
aliceb-nv Oct 23, 2025
bb74f5a
bump 3
aliceb-nv Oct 23, 2025
95b0eb9
RINS on separate stream
aliceb-nv Oct 24, 2025
da9a2c0
bump 1
aliceb-nv Oct 24, 2025
dab3534
bump 2
aliceb-nv Oct 24, 2025
7971a8a
bump 3
aliceb-nv Oct 24, 2025
5630d85
baseline 1
aliceb-nv Oct 30, 2025
678fb7c
Merge branch 'main' into RINS
aliceb-nv Oct 30, 2025
93e1ec9
bump 2
aliceb-nv Oct 30, 2025
34df2af
bump 3
aliceb-nv Oct 30, 2025
9036ed7
review comments
aliceb-nv Oct 30, 2025
994621d
fix problem copy
aliceb-nv Oct 30, 2025
122fb22
bump 1
aliceb-nv Oct 30, 2025
1e53e2b
restore
aliceb-nv Oct 30, 2025
63a19c2
fix cpu worker destructor crash
aliceb-nv Oct 31, 2025
0a17d9d
fix leftovers and typo
aliceb-nv Oct 31, 2025
fd22484
tentative fix
aliceb-nv Oct 31, 2025
a683ad4
bump
aliceb-nv Oct 31, 2025
f4862b8
Fix crashed on var flags w/ free vars
aliceb-nv Nov 4, 2025
9c89559
bump
aliceb-nv Nov 4, 2025
97afc8b
remove log_prefix from args
aliceb-nv Nov 4, 2025
9ddca08
fix context BnB pointer
aliceb-nv Nov 4, 2025
285b4c7
Update pyproject.toml
aliceb-nv Nov 4, 2025
5c71aab
fix crash
aliceb-nv Nov 4, 2025
78e6479
Merge branch 'main' into RINS
aliceb-nv Nov 5, 2025
6e05ca1
fix crash due to solution_t allocation during graph capture, added mo…
aliceb-nv Nov 5, 2025
e60338b
bump
aliceb-nv Nov 5, 2025
54136ae
bump 2
aliceb-nv Nov 5, 2025
0c76574
fix crash on early external solution add
aliceb-nv Nov 5, 2025
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
49 changes: 37 additions & 12 deletions cpp/src/dual_simplex/branch_and_bound.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,7 @@ void branch_and_bound_t<i_t, f_t>::set_new_solution(const std::vector<f_t>& solu
original_lp_, settings_, var_types_, crushed_solution, primal_err, bound_err, num_fractional);
if (is_feasible) {
upper_bound_ = obj;
incumbent_.set_incumbent_solution(obj, crushed_solution);
} else {
attempt_repair = true;
constexpr bool verbose = false;
Expand Down Expand Up @@ -448,16 +449,25 @@ mip_status_t branch_and_bound_t<i_t, f_t>::set_final_solution(mip_solution_t<i_t
settings_.log.printf("Time limit reached. Stopping the solver...\n");
mip_status = mip_status_t::TIME_LIMIT;
}
if (status_ == mip_exploration_status_t::NODE_LIMIT) {
settings_.log.printf("Node limit reached. Stopping the solver...\n");
mip_status = mip_status_t::NODE_LIMIT;
}

f_t upper_bound = get_upper_bound();
f_t gap = upper_bound - lower_bound;
f_t obj = compute_user_objective(original_lp_, upper_bound);
f_t user_lower = compute_user_objective(original_lp_, lower_bound);
f_t gap_rel = user_relative_gap(original_lp_, upper_bound, lower_bound);
f_t upper_bound = get_upper_bound();
f_t gap = upper_bound - lower_bound;
f_t obj = compute_user_objective(original_lp_, upper_bound);
f_t user_bound = compute_user_objective(original_lp_, lower_bound);
f_t gap_rel = user_relative_gap(original_lp_, upper_bound, lower_bound);
bool is_maximization = original_lp_.obj_scale < 0.0;

settings_.log.printf(
"Explored %d nodes in %.2fs.\n", stats_.nodes_explored, toc(stats_.start_time));
settings_.log.printf("Absolute Gap %e Objective %.16e Lower Bound %.16e\n", gap, obj, user_lower);
settings_.log.printf("Absolute Gap %e Objective %.16e %s Bound %.16e\n",
gap,
obj,
is_maximization ? "Upper" : "Lower",
user_bound);

if (gap <= settings_.absolute_mip_gap_tol || gap_rel <= settings_.relative_mip_gap_tol) {
mip_status = mip_status_t::OPTIMAL;
Expand All @@ -483,7 +493,10 @@ mip_status_t branch_and_bound_t<i_t, f_t>::set_final_solution(mip_solution_t<i_t
}
}

uncrush_primal_solution(original_problem_, original_lp_, incumbent_.x, solution.x);
if (upper_bound != inf) {
assert(incumbent_.has_incumbent);
uncrush_primal_solution(original_problem_, original_lp_, incumbent_.x, solution.x);
}
solution.objective = incumbent_.objective;
solution.lower_bound = lower_bound;
solution.nodes_explored = stats_.nodes_explored;
Expand Down Expand Up @@ -637,6 +650,12 @@ node_status_t branch_and_bound_t<i_t, f_t>::solve_node(search_tree_t<i_t, f_t>&
search_tree.graphviz_node(log, node_ptr, "lower bound", leaf_objective);
pc_.update_pseudo_costs(node_ptr, leaf_objective);

if (settings_.node_processed_callback != nullptr) {
std::vector<f_t> original_x;
uncrush_primal_solution(original_problem_, original_lp_, leaf_solution.x, original_x);
settings_.node_processed_callback(original_x, leaf_objective);
}

if (leaf_num_fractional == 0) {
// Found a integer feasible solution
add_feasible_solution(leaf_objective, leaf_solution.x, node_ptr->depth, thread_type);
Expand Down Expand Up @@ -845,6 +864,10 @@ void branch_and_bound_t<i_t, f_t>::explore_subtree(i_t task_id,
status_ = mip_exploration_status_t::TIME_LIMIT;
return;
}
if (stats_.nodes_explored >= settings_.node_limit) {
status_ = mip_exploration_status_t::NODE_LIMIT;
return;
}

// Set the correct bounds for the leaf problem
leaf_problem.lower = original_lp_.lower;
Expand Down Expand Up @@ -1018,13 +1041,15 @@ void branch_and_bound_t<i_t, f_t>::diving_thread(lp_problem_t<i_t, f_t>& leaf_pr
}

template <typename i_t, typename f_t>
mip_status_t branch_and_bound_t<i_t, f_t>::solve(mip_solution_t<i_t, f_t>& solution)
mip_status_t branch_and_bound_t<i_t, f_t>::solve(mip_solution_t<i_t, f_t>& solution,
std::string log_prefix)
{
logger_t log;
log.log = false;
status_ = mip_exploration_status_t::UNSET;
stats_.nodes_unexplored = 0;
stats_.nodes_explored = 0;
log.log = false;
log.log_prefix = settings_.log.log_prefix = log_prefix;
status_ = mip_exploration_status_t::UNSET;
stats_.nodes_unexplored = 0;
stats_.nodes_explored = 0;

if (guess_.size() != 0) {
std::vector<f_t> crushed_guess;
Expand Down
2 changes: 1 addition & 1 deletion cpp/src/dual_simplex/branch_and_bound.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ class branch_and_bound_t {
i_t get_heap_size();

// The main entry routine. Returns the solver status and populates solution with the incumbent.
mip_status_t solve(mip_solution_t<i_t, f_t>& solution);
mip_status_t solve(mip_solution_t<i_t, f_t>& solution, std::string log_prefix = "");

private:
const user_problem_t<i_t, f_t>& original_problem_;
Expand Down
5 changes: 3 additions & 2 deletions cpp/src/dual_simplex/logger.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ class logger_t {

size_t len = strlen(buffer);
if (len > 0 && buffer[len - 1] == '\n') { buffer[len - 1] = '\0'; }
CUOPT_LOG_INFO(buffer);
CUOPT_LOG_INFO("%s%s", log_prefix.c_str(), buffer);
}
#else
if (log_to_console) {
Expand Down Expand Up @@ -105,7 +105,7 @@ class logger_t {

size_t len = strlen(buffer);
if (len > 0 && buffer[len - 1] == '\n') { buffer[len - 1] = '\0'; }
CUOPT_LOG_TRACE(buffer);
CUOPT_LOG_TRACE("%s%s", log_prefix.c_str(), buffer);
}
#else
if (log_to_console) {
Expand All @@ -128,6 +128,7 @@ class logger_t {

bool log;
bool log_to_console;
std::string log_prefix;

private:
bool log_to_file;
Expand Down
5 changes: 4 additions & 1 deletion cpp/src/dual_simplex/presolve.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1381,7 +1381,10 @@ void uncrush_primal_solution(const user_problem_t<i_t, f_t>& user_problem,
{
user_solution.resize(user_problem.num_cols);
assert(problem.num_cols >= user_problem.num_cols);
std::copy(solution.begin(), solution.begin() + user_problem.num_cols, user_solution.data());
assert(solution.size() >= user_problem.num_cols);
std::copy(solution.begin(),
solution.begin() + std::min((i_t)solution.size(), user_problem.num_cols),
user_solution.data());
}

template <typename i_t, typename f_t>
Expand Down
3 changes: 3 additions & 0 deletions cpp/src/dual_simplex/simplex_solver_settings.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ struct simplex_solver_settings_t {
public:
simplex_solver_settings_t()
: iteration_limit(std::numeric_limits<i_t>::max()),
node_limit(std::numeric_limits<i_t>::max()),
time_limit(std::numeric_limits<f_t>::infinity()),
absolute_mip_gap_tol(0.0),
relative_mip_gap_tol(1e-3),
Expand Down Expand Up @@ -91,6 +92,7 @@ struct simplex_solver_settings_t {
void set_log_filename(const std::string& log_filename) { log.set_log_file(log_filename); }
void close_log_file() { log.close_log_file(); }
i_t iteration_limit;
i_t node_limit;
f_t time_limit;
f_t absolute_mip_gap_tol; // Tolerance on mip gap to declare optimal
f_t relative_mip_gap_tol; // Tolerance on mip gap to declare optimal
Expand Down Expand Up @@ -144,6 +146,7 @@ struct simplex_solver_settings_t {
i_t num_diving_threads; // number of threads dedicated to diving
i_t inside_mip; // 0 if outside MIP, 1 if inside MIP at root node, 2 if inside MIP at leaf node
std::function<void(std::vector<f_t>&, f_t)> solution_callback;
std::function<void(const std::vector<f_t>&, f_t)> node_processed_callback;
std::function<void()> heuristic_preemption_callback;
std::function<void(std::vector<f_t>&, std::vector<f_t>&, f_t)> set_simplex_solution_callback;
mutable logger_t log;
Expand Down
12 changes: 9 additions & 3 deletions cpp/src/dual_simplex/solution.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,16 +61,21 @@ class lp_solution_t {
template <typename i_t, typename f_t>
class mip_solution_t {
public:
mip_solution_t(i_t n) : x(n), objective(std::numeric_limits<f_t>::quiet_NaN()), lower_bound(-inf)
mip_solution_t(i_t n)
: x(n),
objective(std::numeric_limits<f_t>::quiet_NaN()),
lower_bound(-inf),
has_incumbent(false)
{
}

void resize(i_t n) { x.resize(n); }

void set_incumbent_solution(f_t primal_objective, const std::vector<f_t>& primal_solution)
{
x = primal_solution;
objective = primal_objective;
x = primal_solution;
objective = primal_objective;
has_incumbent = true;
}

// Primal solution vector
Expand All @@ -79,6 +84,7 @@ class mip_solution_t {
f_t lower_bound;
i_t nodes_explored;
i_t simplex_iterations;
bool has_incumbent;
};

} // namespace cuopt::linear_programming::dual_simplex
1 change: 1 addition & 0 deletions cpp/src/mip/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ set(MIP_NON_LP_FILES
${CMAKE_CURRENT_SOURCE_DIR}/diversity/diversity_manager.cu
${CMAKE_CURRENT_SOURCE_DIR}/diversity/multi_armed_bandit.cu
${CMAKE_CURRENT_SOURCE_DIR}/diversity/population.cu
${CMAKE_CURRENT_SOURCE_DIR}/diversity/lns/rins.cu
${CMAKE_CURRENT_SOURCE_DIR}/relaxed_lp/relaxed_lp.cu
${CMAKE_CURRENT_SOURCE_DIR}/local_search/local_search.cu
${CMAKE_CURRENT_SOURCE_DIR}/local_search/rounding/bounds_repair.cu
Expand Down
5 changes: 4 additions & 1 deletion cpp/src/mip/diversity/diversity_manager.cu
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ diversity_manager_t<i_t, f_t>::diversity_manager_t(mip_solver_context_t<i_t, f_t
lp_dual_optimal_solution(context.problem_ptr->n_constraints,
context.problem_ptr->handle_ptr->get_stream()),
ls(context, lp_optimal_solution),
rins(context, *this),
timer(diversity_config.default_time_limit),
bound_prop_recombiner(context,
context.problem_ptr->n_variables,
Expand Down Expand Up @@ -417,6 +418,8 @@ solution_t<i_t, f_t> diversity_manager_t<i_t, f_t>::run_solver()
run_fj_alone(sol);
return sol;
}
rins.enable();

generate_solution(timer.remaining_time(), false);
if (timer.check_time_limit()) {
population.add_external_solutions_to_population();
Expand Down Expand Up @@ -731,7 +734,7 @@ void diversity_manager_t<i_t, f_t>::set_simplex_solution(const std::vector<f_t>&
{
CUOPT_LOG_DEBUG("Setting simplex solution with objective %f", objective);
using sol_t = solution_t<i_t, f_t>;
cudaSetDevice(context.handle_ptr->get_device());
RAFT_CUDA_TRY(cudaSetDevice(context.handle_ptr->get_device()));
context.handle_ptr->sync_stream();
cuopt_func_call(sol_t new_sol(*problem_ptr));
cuopt_assert(new_sol.assignment.size() == solution.size(), "Assignment size mismatch");
Expand Down
3 changes: 3 additions & 0 deletions cpp/src/mip/diversity/diversity_manager.cuh
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#include <cuopt/linear_programming/mip/solver_settings.hpp>
#include <cuopt/linear_programming/mip/solver_stats.hpp>

#include <mip/diversity/lns/rins.cuh>
#include <mip/local_search/local_search.cuh>
#include <mip/solution/solution.cuh>
#include <mip/solver.cuh>
Expand Down Expand Up @@ -105,6 +106,8 @@ class diversity_manager_t {
// atomic for signalling pdlp to stop
volatile int global_concurrent_halt{0};

rins_t<i_t, f_t> rins;

bool run_only_ls_recombiner{false};
bool run_only_bp_recombiner{false};
bool run_only_fp_recombiner{false};
Expand Down
Loading
Loading