2828#include < utilities/logger.hpp>
2929#include < utilities/macros.cuh>
3030#include < utilities/timer.hpp>
31+ #include < utility>
3132
3233namespace cuopt ::linear_programming::detail {
3334
@@ -396,10 +397,10 @@ bool clique_table_t<i_t, f_t>::check_adjacency(i_t var_idx1, i_t var_idx2)
396397// if this is called outside extend clique, csr matrix should be converted into csc and copied into
397398// problem because the problem is partly modified
398399template <typename i_t , typename f_t >
399- void insert_clique_into_problem (const std::vector<i_t >& clique,
400- dual_simplex::user_problem_t <i_t , f_t >& problem,
401- dual_simplex::csr_matrix_t <i_t , f_t >& A,
402- f_t coeff_scale)
400+ i_t insert_clique_into_problem (const std::vector<i_t >& clique,
401+ dual_simplex::user_problem_t <i_t , f_t >& problem,
402+ dual_simplex::csr_matrix_t <i_t , f_t >& A,
403+ f_t coeff_scale)
403404{
404405 // convert vertices into original vars
405406 f_t rhs_offset = 0 .;
@@ -421,12 +422,14 @@ void insert_clique_into_problem(const std::vector<i_t>& clique,
421422 f_t rhs = coeff_scale - rhs_offset;
422423 // insert the new clique into the problem as a new constraint
423424 dual_simplex::sparse_vector_t <i_t , f_t > new_row (A.n , new_vars.size ());
424- new_row.i = std::move (new_vars);
425- new_row.x = std::move (new_coeffs);
425+ new_row.i = std::move (new_vars);
426+ new_row.x = std::move (new_coeffs);
427+ i_t inserted_row_idx = A.m ;
426428 A.append_row (new_row);
427429 problem.row_sense .push_back (' L' );
428430 problem.rhs .push_back (rhs);
429431 problem.row_names .push_back (" Clique" + std::to_string (problem.row_names .size ()));
432+ return inserted_row_idx;
430433}
431434
432435template <typename i_t , typename f_t >
@@ -438,9 +441,13 @@ bool extend_clique(const std::vector<i_t>& clique,
438441 i_t min_extension_gain,
439442 i_t remaining_rows_budget,
440443 i_t remaining_nnz_budget,
441- i_t & inserted_row_nnz)
444+ i_t & inserted_row_nnz,
445+ i_t & inserted_row_idx,
446+ i_t & extended_clique_idx)
442447{
443448 inserted_row_nnz = 0 ;
449+ inserted_row_idx = -1 ;
450+ extended_clique_idx = -1 ;
444451 i_t smallest_degree = std::numeric_limits<i_t >::max ();
445452 i_t smallest_degree_var = -1 ;
446453 // find smallest degree vertex in the current set packing constraint
@@ -498,6 +505,7 @@ bool extend_clique(const std::vector<i_t>& clique,
498505 complement_conflict_var);
499506 cuopt_assert (n_of_complement_conflicts == 1 , " There can only be one complement conflict" );
500507 // Keep the discovered extension in the clique table for downstream dominance checks.
508+ extended_clique_idx = static_cast <i_t >(clique_table.first .size ());
501509 clique_table.first .push_back (new_clique);
502510 for (const auto & var_idx : new_clique) {
503511 clique_table.var_degrees [var_idx] = -1 ;
@@ -524,6 +532,7 @@ bool extend_clique(const std::vector<i_t>& clique,
524532 } else {
525533 // Keep the discovered extension in the clique table even when row insertion is skipped by
526534 // row/nnz budgets.
535+ extended_clique_idx = static_cast <i_t >(clique_table.first .size ());
527536 clique_table.first .push_back (new_clique);
528537 for (const auto & var_idx : new_clique) {
529538 clique_table.var_degrees [var_idx] = -1 ;
@@ -538,7 +547,7 @@ bool extend_clique(const std::vector<i_t>& clique,
538547 return true ;
539548 }
540549 // insert the new clique into the problem as a new constraint
541- insert_clique_into_problem (new_clique, problem, A, coeff_scale);
550+ inserted_row_idx = insert_clique_into_problem (new_clique, problem, A, coeff_scale);
542551 inserted_row_nnz = static_cast <i_t >(new_clique.size ());
543552 }
544553 }
@@ -553,21 +562,25 @@ i_t extend_cliques(const std::vector<knapsack_constraint_t<i_t, f_t>>& knapsack_
553562 clique_table_t <i_t , f_t >& clique_table,
554563 dual_simplex::user_problem_t <i_t , f_t >& problem,
555564 dual_simplex::csr_matrix_t <i_t , f_t >& A,
565+ std::vector<std::pair<i_t , i_t >>& inserted_extended_rows,
556566 cuopt::timer_t & timer)
557567{
558- constexpr i_t min_extension_gain = 2 ;
559- constexpr i_t extension_yield_window = 64 ;
568+ constexpr i_t min_extension_gain = 0 ;
569+ constexpr i_t extension_yield_window = 100000000 ;
560570 constexpr i_t min_successes_per_window = 1 ;
561571
562- i_t base_rows = A.m ;
563- i_t base_nnz = A.row_start [A.m ];
564- i_t max_added_rows = std::max<i_t >(8 , base_rows / 50 );
565- i_t max_added_nnz = std::max<i_t >(8 * clique_table.max_clique_size_for_extension , base_nnz / 50 );
566-
572+ i_t base_rows = A.m ;
573+ i_t base_nnz = A.row_start [A.m ];
574+ // i_t max_added_rows = std::max<i_t>(8, base_rows / 50);
575+ // i_t max_added_nnz = std::max<i_t>(8 * clique_table.max_clique_size_for_extension, base_nnz /
576+ // 50);
577+ i_t max_added_rows = std::numeric_limits<i_t >::max ();
578+ i_t max_added_nnz = std::numeric_limits<i_t >::max ();
567579 i_t added_rows = 0 ;
568580 i_t added_nnz = 0 ;
569581 i_t window_attempts = 0 ;
570582 i_t window_successes = 0 ;
583+ inserted_extended_rows.clear ();
571584
572585 CUOPT_LOG_DEBUG (" Clique extension heuristics: min_gain=%d row_budget=%d nnz_budget=%d" ,
573586 min_extension_gain,
@@ -622,20 +635,27 @@ i_t extend_cliques(const std::vector<knapsack_constraint_t<i_t, f_t>>& knapsack_
622635 for (const auto & entry : knapsack_constraint.entries ) {
623636 clique.push_back (entry.col );
624637 }
625- i_t inserted_row_nnz = 0 ;
626- f_t coeff_scale = knapsack_constraint.entries [0 ].val ;
627- bool extended_clique = extend_clique (clique,
638+ i_t inserted_row_nnz = 0 ;
639+ i_t inserted_row_idx = -1 ;
640+ i_t extended_clique_idx = -1 ;
641+ f_t coeff_scale = knapsack_constraint.entries [0 ].val ;
642+ bool extended_clique = extend_clique (clique,
628643 clique_table,
629644 problem,
630645 A,
631646 coeff_scale,
632647 min_extension_gain,
633648 max_added_rows - added_rows,
634649 max_added_nnz - added_nnz,
635- inserted_row_nnz);
650+ inserted_row_nnz,
651+ inserted_row_idx,
652+ extended_clique_idx);
636653 if (extended_clique) {
637654 n_extended_cliques++;
638655 window_successes++;
656+ if (inserted_row_idx >= 0 && extended_clique_idx >= 0 ) {
657+ inserted_extended_rows.emplace_back (inserted_row_idx, extended_clique_idx);
658+ }
639659 if (inserted_row_nnz > 0 ) {
640660 added_rows++;
641661 added_nnz += inserted_row_nnz;
@@ -690,6 +710,7 @@ void remove_dominated_cliques(
690710 std::unordered_set<i_t >& set_packing_constraints,
691711 const std::vector<knapsack_constraint_t <i_t , f_t >>& knapsack_constraints,
692712 i_t n_extended_cliques,
713+ const std::vector<std::pair<i_t , i_t >>& inserted_extended_rows,
693714 cuopt::timer_t & timer)
694715{
695716 // TODO check if we need to add the dominance for the table itself
@@ -715,14 +736,18 @@ void remove_dominated_cliques(
715736 if (!time_limit_reached) {
716737 CUOPT_LOG_DEBUG (" Constraint variable lists built: %zu" , set_packing_constraints.size ());
717738 constexpr size_t dominance_window = 100 ;
718- struct clique_sig_t {
719- i_t knapsack_idx;
739+ struct dominance_sig_t {
740+ bool is_extended;
741+ i_t source_idx;
720742 i_t row_idx;
721743 i_t size;
722744 long long signature;
723745 };
724- std::vector<clique_sig_t > sp_sigs;
725- sp_sigs.reserve (set_packing_constraints.size ());
746+ std::vector<std::vector<i_t >> extended_cstr_vars;
747+ extended_cstr_vars.reserve (inserted_extended_rows.size ());
748+ std::unordered_map<i_t , i_t > clique_idx_to_row_idx;
749+ std::vector<dominance_sig_t > dominance_sigs;
750+ dominance_sigs.reserve (set_packing_constraints.size () + inserted_extended_rows.size ());
726751 CUOPT_LOG_DEBUG (" Building set packing signatures" );
727752 for (const auto knapsack_idx : set_packing_constraints) {
728753 if (timer.check_time_limit ()) {
@@ -735,13 +760,38 @@ void remove_dominated_cliques(
735760 for (auto v : vars) {
736761 signature += static_cast <long long >(v);
737762 }
738- sp_sigs.push_back ({knapsack_idx,
739- knapsack_constraints[knapsack_idx].cstr_idx ,
740- static_cast <i_t >(vars.size ()),
741- signature});
763+ dominance_sigs.push_back ({false ,
764+ knapsack_idx,
765+ knapsack_constraints[knapsack_idx].cstr_idx ,
766+ static_cast <i_t >(vars.size ()),
767+ signature});
742768 }
743- CUOPT_LOG_DEBUG (" Sorting signatures: %zu" , sp_sigs.size ());
744- std::sort (sp_sigs.begin (), sp_sigs.end (), [](const auto & a, const auto & b) {
769+ for (const auto & inserted : inserted_extended_rows) {
770+ if (timer.check_time_limit ()) {
771+ time_limit_reached = true ;
772+ break ;
773+ }
774+ const i_t row_idx = inserted.first ;
775+ const i_t clique_idx = inserted.second ;
776+ if (clique_idx < 0 || clique_idx >= static_cast <i_t >(clique_table.first .size ())) { continue ; }
777+ if (row_idx < 0 || row_idx >= static_cast <i_t >(problem.row_sense .size ())) { continue ; }
778+ std::vector<i_t > vars (clique_table.first [clique_idx].begin (),
779+ clique_table.first [clique_idx].end ());
780+ std::sort (vars.begin (), vars.end ());
781+ vars.erase (std::unique (vars.begin (), vars.end ()), vars.end ());
782+ if (vars.empty ()) { continue ; }
783+ long long signature = 0 ;
784+ for (auto v : vars) {
785+ signature += static_cast <long long >(v);
786+ }
787+ i_t source_idx = static_cast <i_t >(extended_cstr_vars.size ());
788+ extended_cstr_vars.push_back (std::move (vars));
789+ clique_idx_to_row_idx[clique_idx] = row_idx;
790+ dominance_sigs.push_back (
791+ {true , source_idx, row_idx, static_cast <i_t >(extended_cstr_vars.back ().size ()), signature});
792+ }
793+ CUOPT_LOG_DEBUG (" Sorting signatures: %zu" , dominance_sigs.size ());
794+ std::sort (dominance_sigs.begin (), dominance_sigs.end (), [](const auto & a, const auto & b) {
745795 if (a.signature != b.signature ) { return a.signature < b.signature ; }
746796 return a.size < b.size ;
747797 });
@@ -782,11 +832,12 @@ void remove_dominated_cliques(
782832 }
783833 };
784834 auto find_window_end = [&](long long signature) {
785- auto it = std::upper_bound (
786- sp_sigs.begin (), sp_sigs.end (), signature, [](long long value, const auto & a) {
787- return value < a.signature ;
788- });
789- return static_cast <size_t >(std::distance (sp_sigs.begin (), it));
835+ auto it =
836+ std::upper_bound (dominance_sigs.begin (),
837+ dominance_sigs.end (),
838+ signature,
839+ [](long long value, const auto & a) { return value < a.signature ; });
840+ return static_cast <size_t >(std::distance (dominance_sigs.begin (), it));
790841 };
791842 CUOPT_LOG_DEBUG (" Scanning extended cliques for dominance" );
792843 for (i_t i = 0 ; i < n_extended_cliques; i++) {
@@ -803,6 +854,10 @@ void remove_dominated_cliques(
803854 cuopt_assert (
804855 std::unique (curr_clique_vars.begin (), curr_clique_vars.end ()) == curr_clique_vars.end (),
805856 " Clique variables are not unique" );
857+ i_t current_row_idx = -1 ;
858+ if (auto it = clique_idx_to_row_idx.find (clique_idx); it != clique_idx_to_row_idx.end ()) {
859+ current_row_idx = it->second ;
860+ }
806861 long long signature = 0 ;
807862 for (auto v : curr_clique_vars) {
808863 signature += static_cast <long long >(v);
@@ -811,9 +866,11 @@ void remove_dominated_cliques(
811866 size_t end = find_window_end (signature);
812867 size_t start = (end > dominance_window) ? (end - dominance_window) : 0 ;
813868 for (size_t idx = end; idx > start; idx--) {
814- size_t cand_idx = idx - 1 ;
815- const auto & sp = sp_sigs[cand_idx];
816- const auto & vars_sp = cstr_vars[sp.knapsack_idx ];
869+ size_t cand_idx = idx - 1 ;
870+ const auto & sp = dominance_sigs[cand_idx];
871+ if (sp.row_idx == current_row_idx) { continue ; }
872+ const auto & vars_sp =
873+ sp.is_extended ? extended_cstr_vars[sp.source_idx ] : cstr_vars[sp.source_idx ];
817874 if (vars_sp.size () > curr_clique_vars.size ()) { continue ; }
818875 cuopt_assert (std::is_sorted (vars_sp.begin (), vars_sp.end ()),
819876 " vars_sp vector passed to is_subset is not sorted" );
@@ -824,7 +881,7 @@ void remove_dominated_cliques(
824881 removal_marker[sp.row_idx ]) {
825882 continue ;
826883 }
827- if (knapsack_constraints[sp.knapsack_idx ].is_set_partitioning ) {
884+ if (!sp. is_extended && knapsack_constraints[sp.source_idx ].is_set_partitioning ) {
828885 // note that we never deleter set partitioning constraints but it fixes some other
829886 // variables
830887 if (vars_sp.size () != curr_clique_vars.size ()) {
@@ -1015,7 +1072,9 @@ void find_initial_cliques(dual_simplex::user_problem_t<i_t, f_t>& problem,
10151072#ifdef DEBUG_CLIQUE_TABLE
10161073 t_maps = stage_timer.elapsed_time ();
10171074#endif
1018- i_t n_extended_cliques = extend_cliques (knapsack_constraints, clique_table, problem, A, timer);
1075+ std::vector<std::pair<i_t , i_t >> inserted_extended_rows;
1076+ i_t n_extended_cliques =
1077+ extend_cliques (knapsack_constraints, clique_table, problem, A, inserted_extended_rows, timer);
10191078#ifdef DEBUG_CLIQUE_TABLE
10201079 t_extend = stage_timer.elapsed_time ();
10211080#endif
@@ -1025,6 +1084,7 @@ void find_initial_cliques(dual_simplex::user_problem_t<i_t, f_t>& problem,
10251084 set_packing_constraints,
10261085 knapsack_constraints,
10271086 n_extended_cliques,
1087+ inserted_extended_rows,
10281088 timer);
10291089#ifdef DEBUG_CLIQUE_TABLE
10301090 t_remove = stage_timer.elapsed_time ();
0 commit comments