Skip to content

Commit b40b32f

Browse files
committed
Merge remote-tracking branch 'cuopt-nvidia/main' into mir_cuts_improve
2 parents 4347577 + d862480 commit b40b32f

File tree

22 files changed

+2644
-352
lines changed

22 files changed

+2644
-352
lines changed

benchmarks/linear_programming/cuopt/run_mip.cpp

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,10 @@ int run_single_file(std::string file_path,
183183
CUOPT_LOG_ERROR("Parsing MPS failed exiting!");
184184
return -1;
185185
}
186+
// Use the benchmark filename for downstream instance-level reporting.
187+
// This keeps per-instance metrics aligned with the run list even if the MPS NAME card differs.
188+
mps_data_model.set_problem_name(base_filename);
189+
186190
if (initial_solution_dir.has_value()) {
187191
auto initial_solutions = read_solution_from_dir(
188192
initial_solution_dir.value(), base_filename, mps_data_model.get_variable_names());
@@ -209,6 +213,7 @@ int run_single_file(std::string file_path,
209213
settings.tolerances.absolute_tolerance = 1e-6;
210214
settings.presolver = cuopt::linear_programming::presolver_t::Default;
211215
settings.reliability_branching = reliability_branching;
216+
settings.clique_cuts = -1;
212217
settings.seed = 42;
213218
cuopt::linear_programming::benchmark_info_t benchmark_info;
214219
settings.benchmark_info_ptr = &benchmark_info;
@@ -413,7 +418,16 @@ int main(int argc, char* argv[])
413418
int reliability_branching = program.get<int>("--reliability-branching");
414419
bool deterministic = program.get<bool>("--determinism");
415420

416-
if (num_cpu_threads < 0) { num_cpu_threads = omp_get_max_threads() / n_gpus; }
421+
if (num_cpu_threads < 0) {
422+
num_cpu_threads = omp_get_max_threads() / n_gpus;
423+
// std::ifstream smt_file("/sys/devices/system/cpu/smt/active");
424+
// if (smt_file.is_open()) {
425+
// int smt_active = 0;
426+
// smt_file >> smt_active;
427+
// if (smt_active) { num_cpu_threads /= 2; }
428+
// }
429+
num_cpu_threads = std::max(num_cpu_threads, 1);
430+
}
417431

418432
if (program.is_used("--out-dir")) {
419433
out_dir = program.get<std::string>("--out-dir");

cpp/include/cuopt/linear_programming/constants.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
#define CUOPT_MIP_MIXED_INTEGER_ROUNDING_CUTS "mip_mixed_integer_rounding_cuts"
6565
#define CUOPT_MIP_MIXED_INTEGER_GOMORY_CUTS "mip_mixed_integer_gomory_cuts"
6666
#define CUOPT_MIP_KNAPSACK_CUTS "mip_knapsack_cuts"
67+
#define CUOPT_MIP_CLIQUE_CUTS "mip_clique_cuts"
6768
#define CUOPT_MIP_STRONG_CHVATAL_GOMORY_CUTS "mip_strong_chvatal_gomory_cuts"
6869
#define CUOPT_MIP_REDUCED_COST_STRENGTHENING "mip_reduced_cost_strengthening"
6970
#define CUOPT_MIP_CUT_CHANGE_THRESHOLD "mip_cut_change_threshold"

cpp/include/cuopt/linear_programming/mip/solver_settings.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ class mip_solver_settings_t {
9393
i_t mir_cuts = -1;
9494
i_t mixed_integer_gomory_cuts = -1;
9595
i_t knapsack_cuts = -1;
96+
i_t clique_cuts = -1;
9697
i_t strong_chvatal_gomory_cuts = -1;
9798
i_t reduced_cost_strengthening = -1;
9899
f_t cut_change_threshold = -1.0;

cpp/src/branch_and_bound/branch_and_bound.cpp

Lines changed: 67 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <branch_and_bound/pseudo_costs.hpp>
1111

1212
#include <cuts/cuts.hpp>
13+
#include <mip_heuristics/presolve/conflict_graph/clique_table.cuh>
1314

1415
#include <dual_simplex/basis_solves.hpp>
1516
#include <dual_simplex/bounds_strengthening.hpp>
@@ -241,9 +242,11 @@ template <typename i_t, typename f_t>
241242
branch_and_bound_t<i_t, f_t>::branch_and_bound_t(
242243
const user_problem_t<i_t, f_t>& user_problem,
243244
const simplex_solver_settings_t<i_t, f_t>& solver_settings,
244-
f_t start_time)
245+
f_t start_time,
246+
std::shared_ptr<detail::clique_table_t<i_t, f_t>> clique_table)
245247
: original_problem_(user_problem),
246248
settings_(solver_settings),
249+
clique_table_(std::move(clique_table)),
247250
original_lp_(user_problem.handle_ptr, 1, 1, 1),
248251
Arow_(1, 1, 0),
249252
incumbent_(1),
@@ -1973,6 +1976,31 @@ mip_status_t branch_and_bound_t<i_t, f_t>::solve(mip_solution_t<i_t, f_t>& solut
19731976

19741977
root_relax_soln_.resize(original_lp_.num_rows, original_lp_.num_cols);
19751978

1979+
if (settings_.clique_cuts != 0 && clique_table_ == nullptr) {
1980+
signal_extend_cliques_.store(false, std::memory_order_release);
1981+
typename ::cuopt::linear_programming::mip_solver_settings_t<i_t, f_t>::tolerances_t
1982+
tolerances_for_clique{};
1983+
tolerances_for_clique.presolve_absolute_tolerance = settings_.primal_tol;
1984+
tolerances_for_clique.absolute_tolerance = settings_.primal_tol;
1985+
tolerances_for_clique.relative_tolerance = settings_.zero_tol;
1986+
tolerances_for_clique.integrality_tolerance = settings_.integer_tol;
1987+
tolerances_for_clique.absolute_mip_gap = settings_.absolute_mip_gap_tol;
1988+
tolerances_for_clique.relative_mip_gap = settings_.relative_mip_gap_tol;
1989+
auto* signal_ptr = &signal_extend_cliques_;
1990+
clique_table_future_ =
1991+
std::async(std::launch::async,
1992+
[this,
1993+
tolerances_for_clique,
1994+
signal_ptr]() -> std::shared_ptr<detail::clique_table_t<i_t, f_t>> {
1995+
user_problem_t<i_t, f_t> problem_copy = original_problem_;
1996+
cuopt::timer_t timer(std::numeric_limits<double>::infinity());
1997+
std::shared_ptr<detail::clique_table_t<i_t, f_t>> table;
1998+
detail::find_initial_cliques(
1999+
problem_copy, tolerances_for_clique, &table, timer, false, signal_ptr);
2000+
return table;
2001+
});
2002+
}
2003+
19762004
i_t original_rows = original_lp_.num_rows;
19772005
simplex_solver_settings_t lp_settings = settings_;
19782006
lp_settings.inside_mip = 1;
@@ -2008,38 +2036,44 @@ mip_status_t branch_and_bound_t<i_t, f_t>::solve(mip_solution_t<i_t, f_t>& solut
20082036
exploration_stats_.total_lp_iters = root_relax_soln_.iterations;
20092037
exploration_stats_.total_lp_solve_time = toc(exploration_stats_.start_time);
20102038

2039+
auto finish_clique_thread = [this]() {
2040+
if (clique_table_future_.valid()) {
2041+
signal_extend_cliques_.store(true, std::memory_order_release);
2042+
clique_table_ = clique_table_future_.get();
2043+
}
2044+
};
2045+
20112046
if (root_status == lp_status_t::INFEASIBLE) {
20122047
settings_.log.printf("MIP Infeasible\n");
2013-
// FIXME: rarely dual simplex detects infeasible whereas it is feasible.
2014-
// to add a small safety net, check if there is a primal solution already.
2015-
// Uncomment this if the issue with cost266-UUE is resolved
2016-
// if (settings.heuristic_preemption_callback != nullptr) {
2017-
// settings.heuristic_preemption_callback();
2018-
// }
2048+
finish_clique_thread();
20192049
return mip_status_t::INFEASIBLE;
20202050
}
20212051
if (root_status == lp_status_t::UNBOUNDED) {
20222052
settings_.log.printf("MIP Unbounded\n");
20232053
if (settings_.heuristic_preemption_callback != nullptr) {
20242054
settings_.heuristic_preemption_callback();
20252055
}
2056+
finish_clique_thread();
20262057
return mip_status_t::UNBOUNDED;
20272058
}
20282059
if (root_status == lp_status_t::TIME_LIMIT) {
20292060
solver_status_ = mip_status_t::TIME_LIMIT;
20302061
set_final_solution(solution, -inf);
2062+
finish_clique_thread();
20312063
return solver_status_;
20322064
}
20332065

20342066
if (root_status == lp_status_t::WORK_LIMIT) {
20352067
solver_status_ = mip_status_t::WORK_LIMIT;
20362068
set_final_solution(solution, -inf);
2069+
finish_clique_thread();
20372070
return solver_status_;
20382071
}
20392072

20402073
if (root_status == lp_status_t::NUMERICAL_ISSUES) {
20412074
solver_status_ = mip_status_t::NUMERICAL;
20422075
set_final_solution(solution, -inf);
2076+
finish_clique_thread();
20432077
return solver_status_;
20442078
}
20452079

@@ -2070,6 +2104,7 @@ mip_status_t branch_and_bound_t<i_t, f_t>::solve(mip_solution_t<i_t, f_t>& solut
20702104

20712105
if (num_fractional == 0) {
20722106
set_solution_at_root(solution, cut_info);
2107+
finish_clique_thread();
20732108
return mip_status_t::OPTIMAL;
20742109
}
20752110

@@ -2084,8 +2119,16 @@ mip_status_t branch_and_bound_t<i_t, f_t>::solve(mip_solution_t<i_t, f_t>& solut
20842119
}
20852120

20862121
cut_pool_t<i_t, f_t> cut_pool(original_lp_.num_cols, settings_);
2087-
cut_generation_t<i_t, f_t> cut_generation(
2088-
cut_pool, original_lp_, settings_, Arow_, new_slacks_, var_types_);
2122+
cut_generation_t<i_t, f_t> cut_generation(cut_pool,
2123+
original_lp_,
2124+
settings_,
2125+
Arow_,
2126+
new_slacks_,
2127+
var_types_,
2128+
original_problem_,
2129+
clique_table_,
2130+
&clique_table_future_,
2131+
&signal_extend_cliques_);
20892132

20902133
std::vector<f_t> saved_solution;
20912134
#ifdef CHECK_CUTS_AGAINST_SAVED_SOLUTION
@@ -2096,7 +2139,8 @@ mip_status_t branch_and_bound_t<i_t, f_t>::solve(mip_solution_t<i_t, f_t>& solut
20962139
f_t last_objective = root_objective_;
20972140
f_t root_relax_objective = root_objective_;
20982141

2099-
i_t cut_pool_size = 0;
2142+
f_t cut_generation_start_time = tic();
2143+
i_t cut_pool_size = 0;
21002144
for (i_t cut_pass = 0; cut_pass < settings_.max_cut_passes; cut_pass++) {
21012145
if (num_fractional == 0) {
21022146
set_solution_at_root(solution, cut_info);
@@ -2116,17 +2160,26 @@ mip_status_t branch_and_bound_t<i_t, f_t>::solve(mip_solution_t<i_t, f_t>& solut
21162160

21172161
// Generate cuts and add them to the cut pool
21182162
f_t cut_start_time = tic();
2119-
cut_generation.generate_cuts(original_lp_,
2163+
bool problem_feasible = cut_generation.generate_cuts(original_lp_,
21202164
settings_,
21212165
Arow_,
21222166
new_slacks_,
21232167
var_types_,
21242168
basis_update,
21252169
root_relax_soln_.x,
21262170
root_relax_soln_.y,
2171+
root_relax_soln_.z,
21272172
basic_list,
21282173
nonbasic_list,
2129-
variable_bounds);
2174+
variable_bounds,
2175+
exploration_stats_.start_time);
2176+
if (!problem_feasible) {
2177+
if (settings_.heuristic_preemption_callback != nullptr) {
2178+
settings_.heuristic_preemption_callback();
2179+
}
2180+
finish_clique_thread();
2181+
return mip_status_t::INFEASIBLE;
2182+
}
21302183
f_t cut_generation_time = toc(cut_start_time);
21312184
if (cut_generation_time > 1.0) {
21322185
settings_.log.debug("Cut generation time %.2f seconds\n", cut_generation_time);
@@ -2356,8 +2409,9 @@ mip_status_t branch_and_bound_t<i_t, f_t>::solve(mip_solution_t<i_t, f_t>& solut
23562409
}
23572410

23582411
print_cut_info(settings_, cut_info);
2359-
2412+
f_t cut_generation_time = toc(cut_generation_start_time);
23602413
if (cut_info.has_cuts()) {
2414+
settings_.log.printf("Cut generation time: %.2f seconds\n", cut_generation_time);
23612415
settings_.log.printf("Cut pool size : %d\n", cut_pool_size);
23622416
settings_.log.printf("Size with cuts : %d constraints, %d variables, %d nonzeros\n",
23632417
original_lp_.num_rows,

cpp/src/branch_and_bound/branch_and_bound.hpp

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,17 @@
3232

3333
#include <omp.h>
3434

35+
#include <atomic>
3536
#include <functional>
37+
#include <future>
38+
#include <memory>
3639
#include <vector>
3740

41+
namespace cuopt::linear_programming::detail {
42+
template <typename i_t, typename f_t>
43+
struct clique_table_t;
44+
}
45+
3846
namespace cuopt::linear_programming::dual_simplex {
3947

4048
enum class mip_status_t {
@@ -68,7 +76,8 @@ class branch_and_bound_t {
6876
public:
6977
branch_and_bound_t(const user_problem_t<i_t, f_t>& user_problem,
7078
const simplex_solver_settings_t<i_t, f_t>& solver_settings,
71-
f_t start_time);
79+
f_t start_time,
80+
std::shared_ptr<detail::clique_table_t<i_t, f_t>> clique_table = nullptr);
7281

7382
// Set an initial guess based on the user_problem. This should be called before solve.
7483
void set_initial_guess(const std::vector<f_t>& user_guess) { guess_ = user_guess; }
@@ -106,8 +115,6 @@ class branch_and_bound_t {
106115

107116
void set_concurrent_lp_root_solve(bool enable) { enable_concurrent_lp_root_solve_ = enable; }
108117

109-
bool stop_for_time_limit(mip_solution_t<i_t, f_t>& solution);
110-
111118
// Repair a low-quality solution from the heuristics.
112119
bool repair_solution(const std::vector<f_t>& leaf_edge_norms,
113120
const std::vector<f_t>& potential_solution,
@@ -141,6 +148,9 @@ class branch_and_bound_t {
141148
private:
142149
const user_problem_t<i_t, f_t>& original_problem_;
143150
const simplex_solver_settings_t<i_t, f_t> settings_;
151+
std::shared_ptr<detail::clique_table_t<i_t, f_t>> clique_table_;
152+
std::future<std::shared_ptr<detail::clique_table_t<i_t, f_t>>> clique_table_future_;
153+
std::atomic<bool> signal_extend_cliques_{false};
144154

145155
work_limit_context_t work_unit_context_{"B&B"};
146156

0 commit comments

Comments
 (0)