Skip to content

Commit 3ebc3b1

Browse files
committed
wip
1 parent b176767 commit 3ebc3b1

4 files changed

Lines changed: 470 additions & 42 deletions

File tree

cpp/src/mip_heuristics/diversity/diversity_manager.cu

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ bool diversity_manager_t<i_t, f_t>::run_presolve(f_t time_limit, timer_t global_
209209
if (!problem_ptr->empty && !check_bounds_sanity(*problem_ptr)) { return false; }
210210
if (!presolve_timer.check_time_limit() && !context.settings.heuristics_only &&
211211
!problem_ptr->empty) {
212-
timer_t clique_timer(15.);
212+
timer_t clique_timer(std::numeric_limits<double>::infinity());
213213
dual_simplex::user_problem_t<i_t, f_t> host_problem(problem_ptr->handle_ptr);
214214
problem_ptr->get_host_user_problem(host_problem);
215215
std::shared_ptr<clique_table_t<i_t, f_t>> clique_table;

cpp/src/mip_heuristics/presolve/conflict_graph/clique_table.cu

Lines changed: 99 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include <utilities/logger.hpp>
2929
#include <utilities/macros.cuh>
3030
#include <utilities/timer.hpp>
31+
#include <utility>
3132

3233
namespace 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
398399
template <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

432435
template <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();

cpp/src/mip_heuristics/solve.cu

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ mip_solution_t<i_t, f_t> solve_mip(optimization_problem_t<i_t, f_t>& op_problem,
259259
if (!run_presolve) { CUOPT_LOG_INFO("Presolve is disabled, skipping"); }
260260

261261
auto constexpr const dual_postsolve = false;
262-
if (run_presolve) {
262+
if (run_presolve && false) {
263263
detail::sort_csr(op_problem);
264264
// allocate not more than 10% of the time limit to presolve.
265265
// Note that this is not the presolve time, but the time limit for presolve.
@@ -302,7 +302,7 @@ mip_solution_t<i_t, f_t> solve_mip(optimization_problem_t<i_t, f_t>& op_problem,
302302

303303
auto sol = run_mip(problem, settings, timer);
304304

305-
if (run_presolve) {
305+
if (run_presolve && false) {
306306
auto status_to_skip = sol.get_termination_status() == mip_termination_status_t::TimeLimit ||
307307
sol.get_termination_status() == mip_termination_status_t::WorkLimit ||
308308
sol.get_termination_status() == mip_termination_status_t::Infeasible;

0 commit comments

Comments
 (0)