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>
241242branch_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 ,
0 commit comments