Skip to content

Commit 4ad65b5

Browse files
authored
Merge branch 'main' into improveLogs
2 parents f10d720 + d862480 commit 4ad65b5

25 files changed

Lines changed: 2716 additions & 393 deletions

File tree

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 = 1e-3;

cpp/src/branch_and_bound/branch_and_bound.cpp

Lines changed: 81 additions & 26 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),
@@ -1980,11 +1983,37 @@ mip_status_t branch_and_bound_t<i_t, f_t>::solve(mip_solution_t<i_t, f_t>& solut
19801983

19811984
root_relax_soln_.resize(original_lp_.num_rows, original_lp_.num_cols);
19821985

1983-
i_t original_rows = original_lp_.num_rows;
1984-
simplex_solver_settings_t lp_settings = settings_;
1985-
lp_settings.inside_mip = 1;
1986-
lp_settings.scale_columns = false;
1987-
lp_settings.concurrent_halt = get_root_concurrent_halt();
1986+
1987+
if (settings_.clique_cuts != 0 && clique_table_ == nullptr) {
1988+
signal_extend_cliques_.store(false, std::memory_order_release);
1989+
typename ::cuopt::linear_programming::mip_solver_settings_t<i_t, f_t>::tolerances_t
1990+
tolerances_for_clique{};
1991+
tolerances_for_clique.presolve_absolute_tolerance = settings_.primal_tol;
1992+
tolerances_for_clique.absolute_tolerance = settings_.primal_tol;
1993+
tolerances_for_clique.relative_tolerance = settings_.zero_tol;
1994+
tolerances_for_clique.integrality_tolerance = settings_.integer_tol;
1995+
tolerances_for_clique.absolute_mip_gap = settings_.absolute_mip_gap_tol;
1996+
tolerances_for_clique.relative_mip_gap = settings_.relative_mip_gap_tol;
1997+
auto* signal_ptr = &signal_extend_cliques_;
1998+
clique_table_future_ =
1999+
std::async(std::launch::async,
2000+
[this,
2001+
tolerances_for_clique,
2002+
signal_ptr]() -> std::shared_ptr<detail::clique_table_t<i_t, f_t>> {
2003+
user_problem_t<i_t, f_t> problem_copy = original_problem_;
2004+
cuopt::timer_t timer(std::numeric_limits<double>::infinity());
2005+
std::shared_ptr<detail::clique_table_t<i_t, f_t>> table;
2006+
detail::find_initial_cliques(
2007+
problem_copy, tolerances_for_clique, &table, timer, false, signal_ptr);
2008+
return table;
2009+
});
2010+
}
2011+
2012+
i_t original_rows = original_lp_.num_rows;
2013+
simplex_solver_settings_t lp_settings = settings_;
2014+
lp_settings.inside_mip = 1;
2015+
lp_settings.scale_columns = false;
2016+
lp_settings.concurrent_halt = get_root_concurrent_halt();
19882017
lp_settings.dual_simplex_objective_callback = [this](f_t user_obj) {
19892018
root_lp_current_lower_bound_.store(user_obj);
19902019
};
@@ -2020,38 +2049,44 @@ mip_status_t branch_and_bound_t<i_t, f_t>::solve(mip_solution_t<i_t, f_t>& solut
20202049
exploration_stats_.total_lp_iters = root_relax_soln_.iterations;
20212050
exploration_stats_.total_lp_solve_time = toc(exploration_stats_.start_time);
20222051

2052+
auto finish_clique_thread = [this]() {
2053+
if (clique_table_future_.valid()) {
2054+
signal_extend_cliques_.store(true, std::memory_order_release);
2055+
clique_table_ = clique_table_future_.get();
2056+
}
2057+
};
2058+
20232059
if (root_status == lp_status_t::INFEASIBLE) {
20242060
settings_.log.printf("MIP Infeasible\n");
2025-
// FIXME: rarely dual simplex detects infeasible whereas it is feasible.
2026-
// to add a small safety net, check if there is a primal solution already.
2027-
// Uncomment this if the issue with cost266-UUE is resolved
2028-
// if (settings.heuristic_preemption_callback != nullptr) {
2029-
// settings.heuristic_preemption_callback();
2030-
// }
2061+
finish_clique_thread();
20312062
return mip_status_t::INFEASIBLE;
20322063
}
20332064
if (root_status == lp_status_t::UNBOUNDED) {
20342065
settings_.log.printf("MIP Unbounded\n");
20352066
if (settings_.heuristic_preemption_callback != nullptr) {
20362067
settings_.heuristic_preemption_callback();
20372068
}
2069+
finish_clique_thread();
20382070
return mip_status_t::UNBOUNDED;
20392071
}
20402072
if (root_status == lp_status_t::TIME_LIMIT) {
20412073
solver_status_ = mip_status_t::TIME_LIMIT;
20422074
set_final_solution(solution, -inf);
2075+
finish_clique_thread();
20432076
return solver_status_;
20442077
}
20452078

20462079
if (root_status == lp_status_t::WORK_LIMIT) {
20472080
solver_status_ = mip_status_t::WORK_LIMIT;
20482081
set_final_solution(solution, -inf);
2082+
finish_clique_thread();
20492083
return solver_status_;
20502084
}
20512085

20522086
if (root_status == lp_status_t::NUMERICAL_ISSUES) {
20532087
solver_status_ = mip_status_t::NUMERICAL;
20542088
set_final_solution(solution, -inf);
2089+
finish_clique_thread();
20552090
return solver_status_;
20562091
}
20572092

@@ -2082,6 +2117,7 @@ mip_status_t branch_and_bound_t<i_t, f_t>::solve(mip_solution_t<i_t, f_t>& solut
20822117

20832118
if (num_fractional == 0) {
20842119
set_solution_at_root(solution, cut_info);
2120+
finish_clique_thread();
20852121
return mip_status_t::OPTIMAL;
20862122
}
20872123

@@ -2096,8 +2132,16 @@ mip_status_t branch_and_bound_t<i_t, f_t>::solve(mip_solution_t<i_t, f_t>& solut
20962132
}
20972133

20982134
cut_pool_t<i_t, f_t> cut_pool(original_lp_.num_cols, settings_);
2099-
cut_generation_t<i_t, f_t> cut_generation(
2100-
cut_pool, original_lp_, settings_, Arow_, new_slacks_, var_types_);
2135+
cut_generation_t<i_t, f_t> cut_generation(cut_pool,
2136+
original_lp_,
2137+
settings_,
2138+
Arow_,
2139+
new_slacks_,
2140+
var_types_,
2141+
original_problem_,
2142+
clique_table_,
2143+
&clique_table_future_,
2144+
&signal_extend_cliques_);
21012145

21022146
std::vector<f_t> saved_solution;
21032147
#ifdef CHECK_CUTS_AGAINST_SAVED_SOLUTION
@@ -2108,7 +2152,8 @@ mip_status_t branch_and_bound_t<i_t, f_t>::solve(mip_solution_t<i_t, f_t>& solut
21082152
f_t last_objective = root_objective_;
21092153
f_t root_relax_objective = root_objective_;
21102154

2111-
i_t cut_pool_size = 0;
2155+
f_t cut_generation_start_time = tic();
2156+
i_t cut_pool_size = 0;
21122157
for (i_t cut_pass = 0; cut_pass < settings_.max_cut_passes; cut_pass++) {
21132158
if (num_fractional == 0) {
21142159
set_solution_at_root(solution, cut_info);
@@ -2127,16 +2172,25 @@ mip_status_t branch_and_bound_t<i_t, f_t>::solve(mip_solution_t<i_t, f_t>& solut
21272172
#endif
21282173

21292174
// Generate cuts and add them to the cut pool
2130-
f_t cut_start_time = tic();
2131-
cut_generation.generate_cuts(original_lp_,
2132-
settings_,
2133-
Arow_,
2134-
new_slacks_,
2135-
var_types_,
2136-
basis_update,
2137-
root_relax_soln_.x,
2138-
basic_list,
2139-
nonbasic_list);
2175+
f_t cut_start_time = tic();
2176+
bool problem_feasible = cut_generation.generate_cuts(original_lp_,
2177+
settings_,
2178+
Arow_,
2179+
new_slacks_,
2180+
var_types_,
2181+
basis_update,
2182+
root_relax_soln_.x,
2183+
root_relax_soln_.z,
2184+
basic_list,
2185+
nonbasic_list,
2186+
exploration_stats_.start_time);
2187+
if (!problem_feasible) {
2188+
if (settings_.heuristic_preemption_callback != nullptr) {
2189+
settings_.heuristic_preemption_callback();
2190+
}
2191+
finish_clique_thread();
2192+
return mip_status_t::INFEASIBLE;
2193+
}
21402194
f_t cut_generation_time = toc(cut_start_time);
21412195
if (cut_generation_time > 1.0) {
21422196
settings_.log.debug("Cut generation time %.2f seconds\n", cut_generation_time);
@@ -2357,8 +2411,9 @@ mip_status_t branch_and_bound_t<i_t, f_t>::solve(mip_solution_t<i_t, f_t>& solut
23572411
}
23582412

23592413
print_cut_info(settings_, cut_info);
2360-
2414+
f_t cut_generation_time = toc(cut_generation_start_time);
23612415
if (cut_info.has_cuts()) {
2416+
settings_.log.printf("Cut generation time: %.2f seconds\n", cut_generation_time);
23622417
settings_.log.printf("Cut pool size : %d\n", cut_pool_size);
23632418
settings_.log.printf("Size with cuts : %d constraints, %d variables, %d nonzeros\n",
23642419
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)