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 ),
@@ -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 ,
0 commit comments