Skip to content

Comments

Clique Table and Preprocessing#627

Open
akifcorduk wants to merge 64 commits intoNVIDIA:mainfrom
akifcorduk:cliques
Open

Clique Table and Preprocessing#627
akifcorduk wants to merge 64 commits intoNVIDIA:mainfrom
akifcorduk:cliques

Conversation

@akifcorduk
Copy link
Contributor

@akifcorduk akifcorduk commented Nov 25, 2025

This PR implements the first part of the paper from the paper: Preprocessing and Cutting Planes with Conflict Graphs.
This part contains only the preprocessing parts and clique cuts will follow in a separate PR:

  • Clique detection by converting constraints into sorted knapsack constraints. This allows fast clique detection.
  • Additional cliques in the same constraint by utilizing sorting structures.
  • Clique extension/merging across the conflict graph.
  • Clique covering and problem modification.

The data structures and query functions are implemented and will be used as a basis for clique cuts and usage in heuristics.

Benchmark results: there is little to no change in benchmarks. This PR is a basis for the clique cuts PR.

Summary by CodeRabbit

  • New Features

    • Automatic clique detection and injection into MIP presolve to improve preprocessing.
    • Host-to-solver constraint sync allowing external problem updates to be applied before solving.
  • Improvements

    • Presolve now respects a time cap to bound preprocessing work.
    • Improved constraint handling and propagation during solution setup.
    • Local search initialization refined for more reliable first-run behavior.

@akifcorduk akifcorduk added this to the 26.02 milestone Nov 25, 2025
@akifcorduk akifcorduk added non-breaking Introduces a non-breaking change improvement Improves an existing functionality labels Nov 25, 2025
@copy-pr-bot
Copy link

copy-pr-bot bot commented Nov 25, 2025

Auto-sync is disabled for draft pull requests in this repository. Workflows must be run manually.

Contributors can view more details about this message here.

@coderabbitai
Copy link

coderabbitai bot commented Nov 25, 2025

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a clique-based presolve subsystem and integrates it into the MIP pipeline, extends CSR sparse-matrix API, exposes a new problem setter for host-derived constraints, adjusts build files, and updates presolve/solver/local-search flows and comments.

Changes

Cohort / File(s) Summary
Build Configuration
cpp/CMakeLists.txt, cpp/src/mip/CMakeLists.txt
Updated solve_MIP include directories (PRIVATE src, PUBLIC BUILD_INTERFACE include) and added clique_table.cu to non-LP build list.
Clique Infrastructure
cpp/src/mip/presolve/conflict_graph/clique_table.cuh, cpp/src/mip/presolve/conflict_graph/clique_table.cu
New templated clique table/types and extensive algorithms for extracting, extending, pruning, and inserting cliques from knapsack-like constraints; explicit instantiations for float/double.
Sparse Matrix API
cpp/src/dual_simplex/sparse_matrix.hpp, cpp/src/dual_simplex/sparse_matrix.cpp
Added csr_matrix_t::get_constraint_range() and insert_row() overloads; added CSC→CSR scatter helper; fixed comment typo and updated copyright year.
Presolve / Diversity Integration
cpp/src/mip/diversity/diversity_manager.cu
Hooked initial-clique finding into run_presolve: construct host_problem, compute initial cliques, update host constraints via set_constraints_from_host_user_problem, reorder/gate bounds updates and trivial presolve.
Probing Cache / Comments
cpp/src/mip/presolve/probing_cache.cu, cpp/src/mip/presolve/probing_cache.cuh
Added unordered_set include; added explanatory comments about probing cache behavior (no API/behavioral changes).
Problem Representation
cpp/src/mip/problem/problem.cuh, cpp/src/mip/problem/problem.cu
Added problem_t::set_constraints_from_host_user_problem(...) to populate device CSR, rhs, bounds, range handling, names, and variable types from a host user_problem; resize_constraints now preserves/initializes prev_dual elements. (Note: duplicate definition observed in cu file.)
Solver Integration
cpp/src/mip/solver.cu
Introduced presolve time cap (min(10% remaining, 60s)) and call to context.problem_ptr->set_constraints_from_host_user_problem(branch_and_bound_problem) before branch-and-bound setup.
Local Search Initialization
cpp/src/mip/local_search/local_search.cu
On first run, initialize in_fj.cstr_weights to constraint count and fill with 1.0.

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main changes: it introduces clique table data structures and preprocessing functionality for clique detection and constraint modification.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@rg20
Copy link
Contributor

rg20 commented Jan 22, 2026

@akifcorduk is this still relevant?

@rgsl888prabhu rgsl888prabhu changed the base branch from main to release/26.02 January 22, 2026 16:48
@akifcorduk akifcorduk marked this pull request as ready for review January 29, 2026 11:59
@akifcorduk akifcorduk requested review from a team as code owners January 29, 2026 11:59
Copy link
Contributor

@aliceb-nv aliceb-nv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot for your awesome work Akif!! This is going to be super helpful in closing the optimality gap.
Just a few minor comments, mostly relevant for possible follow ups, otherwise LGTM

Comment on lines 292 to 311
std::unordered_set<i_t> clique_table_t<i_t, f_t>::get_adj_set_of_var(i_t var_idx)
{
std::unordered_set<i_t> adj_set;
for (const auto& clique_idx : var_clique_map_first[var_idx]) {
adj_set.insert(first[clique_idx].begin(), first[clique_idx].end());
}

for (const auto& addtl_clique_idx : var_clique_map_addtl[var_idx]) {
adj_set.insert(first[addtl_cliques[addtl_clique_idx].clique_idx].begin(),
first[addtl_cliques[addtl_clique_idx].clique_idx].end());
}

for (const auto& adj_vertex : adj_list_small_cliques[var_idx]) {
adj_set.insert(adj_vertex);
}
// Add the complement of var_idx to the adjacency set
i_t complement_idx = (var_idx >= n_variables) ? (var_idx - n_variables) : (var_idx + n_variables);
adj_set.insert(complement_idx);
adj_set.erase(var_idx);
return adj_set;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If i understand correctly, all adjacency sets are of size O(n_vars), which should be small enough in most cases to be a non-factor. Do you think we could use bitsets here instead? It seems to me a lot of clique logic can be translated to bitwise ops

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes definitely. In cuts PR, computing clique lifting with dynamic programming is done with bitsets. I think we can also convert them. But let's do it in a follow up PR as this PR is quite long.

std::vector<addtl_clique_t<i_t, f_t>> addtl_cliques;
// TODO figure out the performance of lookup for the following: unordered_set vs vector
// keeps the indices of original(first) cliques that contain variable x
std::vector<std::unordered_set<i_t>> var_clique_map_first;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

side note: If we ever find that we're bottlenecked on many-cliques instances, I found this paper on sparse bitmaps which looks pretty cool :) https://roaringbitmap.org/

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you! There are some heuristic limits to sizes for memory and runtime but we can explore the compressing options :)

return true;
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be done in the var_idx2 -> var_idx1 direction as well? The additional clique structures aren't symmetrical as I understand

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great catch! Yes additional cliques are not symmetrical. Fixed it.

Comment on lines 300 to 301
adj_set.insert(first[addtl_cliques[addtl_clique_idx].clique_idx].begin(),
first[addtl_cliques[addtl_clique_idx].clique_idx].end());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This adds the entire base clique. Should this take "start_pos_on_clique" instead to do as "check_adjacency" does? (addtl clique is: vertex_idx + first[clique_idx][start_pos_on_clique:])

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great catch! You are right, we are inserting the whole first clique, we should only insert a representative vertex and part of first clique.

clique_table_t<i_t, f_t>& clique_table,
dual_simplex::user_problem_t<i_t, f_t>& problem,
dual_simplex::csr_matrix_t<i_t, f_t>& A,
cuopt::timer_t& timer)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My intuition is that this could be significantly sped up with some bitwise ops (AND intersections, popcounts). But let's just keep this in mind for possible follow ups if this proves to be a bottleneck :)

Comment on lines +2049 to +2061
void problem_t<i_t, f_t>::set_constraints_from_host_user_problem(
const cuopt::linear_programming::dual_simplex::user_problem_t<i_t, f_t>& user_problem)
{
raft::common::nvtx::range fun_scope("set_constraints_from_host_user_problem");
cuopt_assert(user_problem.handle_ptr == handle_ptr, "handle mismatch");
cuopt_assert(user_problem.num_cols == n_variables, "num cols mismatch");
n_constraints = user_problem.num_rows;
cuopt_assert(user_problem.rhs.size() == static_cast<size_t>(n_constraints), "rhs size mismatch");
cuopt_assert(user_problem.row_sense.size() == static_cast<size_t>(n_constraints),
"row sense size mismatch");
cuopt_assert(user_problem.range_rows.size() == user_problem.range_value.size(),
"range rows/value size mismatch");

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clique_table.cu can perform variable fixings as well (in remove_dominated_cliques), should the variable bounds be copied as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that's correct. Initially variable fixings were not implemented, now we need to update the variable bounds too. Thanks for catching that!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

improvement Improves an existing functionality non-breaking Introduces a non-breaking change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants