Skip to content

GPU heuristics determinism integration#9

Closed
aliceb-nv wants to merge 307 commits into
mainfrom
gpudet
Closed

GPU heuristics determinism integration#9
aliceb-nv wants to merge 307 commits into
mainfrom
gpudet

Conversation

@aliceb-nv
Copy link
Copy Markdown
Owner

Dummy PR to get CodeRabbit to execute a full review.

Description

Issue

Checklist

  • I am familiar with the Contributing Guidelines.
  • Testing
    • New or existing tests cover these changes
    • Added tests
    • Created an issue to follow-up
    • NA
  • Documentation
    • The documentation is up to date with these changes
    • Added new documentation
    • NA

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 9, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: faad4b61-2bca-4584-971b-ee56698574e2

📥 Commits

Reviewing files that changed from the base of the PR and between c34cf0b and b25b34f.

📒 Files selected for processing (11)
  • benchmarks/linear_programming/cuopt/run_mip.cpp
  • cpp/src/branch_and_bound/branch_and_bound.cpp
  • cpp/src/mip_heuristics/diversity/diversity_manager.cu
  • cpp/src/mip_heuristics/diversity/recombiners/recombiner.cuh
  • cpp/src/mip_heuristics/diversity/recombiners/sub_mip.cuh
  • cpp/src/mip_heuristics/feasibility_jump/feasibility_jump.cu
  • cpp/src/mip_heuristics/local_search/feasibility_pump/feasibility_pump.cu
  • cpp/src/mip_heuristics/presolve/third_party_presolve.cpp
  • cpp/src/mip_heuristics/solution_callbacks.cuh
  • cpp/tests/mip/diversity_test.cu
  • cpp/tests/mip/feasibility_jump_tests.cu

📝 Walkthrough

Walkthrough

Adds deterministic MIP execution and work-unit based termination, extends incumbent callbacks with origin and work-timestamp metadata, threads solution-origin through heuristics/recombiners/population/B&B, replaces many timer_t usages with a new termination_checker_t, and adds tests plus sanitizer suppressions.

Changes

Cohort / File(s) Summary
Public API & Constants
cpp/include/cuopt/linear_programming/constants.h, cpp/include/cuopt/linear_programming/cuopt_c.h
New determinism bitflags, solution-origin constants, cuOptMIPSolutionCallbackInfo, and extended C callback cuOptMIPGetSolutionCallbackExt plus setter.
Internals / Callbacks
cpp/include/cuopt/linear_programming/utilities/internals.hpp, cpp/src/mip_heuristics/solution_callbacks.cuh, cpp/src/pdlp/cuopt_c.cpp
Added mip_solution_origin_t, origin-to-string, get_solution_callback_ext_t interface, payload/dispatch helpers, publication/injection classes, and C wrapper registration.
Solver Settings & Context
cpp/include/.../mip/solver_settings.hpp, cpp/src/math_optimization/solver_settings.cu, cpp/src/mip_heuristics/solver_context.cuh
New work-unit scaling params (cpufj/gpu_heur/bb), gpu_heur_wait_for_exploration, determinism-mode bitmask wiring and hyperparam registration.
Termination / Work Accounting Utilities
cpp/src/utilities/termination_checker.hpp, cpp/src/utilities/work_limit_context.hpp, cpp/src/utilities/work_unit_scheduler.*
Added termination_checker_t, enhanced work_limit_context_t with producer sync and scaling, scheduler base offset; work recording and producer notification APIs.
Timer / Seed / Helpers
cpp/src/utilities/timer.hpp, cpp/src/utilities/seed_generator.*, cpp/src/utilities/copy_helpers.hpp, cpp/src/utilities/determinism_log.hpp, cpp/src/utilities/cuda_helpers.cuh
Timer check signatures extended; seed generator reworked to epoch/thread-local; copyable_atomic_t added; determinism-log macro; NVTX memory-init helpers.
Branch-and-Bound Core & Workers
cpp/src/branch_and_bound/branch_and_bound.{cpp,hpp}, cpp/src/branch_and_bound/deterministic_workers.hpp
Threaded solution origin/timestamps through B&B APIs and queues, new emit/retire queue paths, pre-exploration work handling, deterministic replay/sorting and worker snapshot updates.
Strong Branching / Pseudo-costs
cpp/src/branch_and_bound/pseudo_costs.{cpp,hpp}
Introduced reliable_variable_selection_core, trial_branching_generic, work-context threading into strong branching and snapshot update plumbing.
Relaxed LP / Dual Simplex
cpp/src/dual_simplex/*, cpp/src/mip_heuristics/relaxed_lp/*
Added iteration/work limits and work-context use; guarded work sync calls; refined work-estimate calculations and logging for determinism.
Heuristics / Diversity / Recombination
cpp/src/mip_heuristics/diversity/*, cpp/src/mip_heuristics/feasibility_jump/*, cpp/src/mip_heuristics/local_search/*
Thread origin through heuristics and population; many recombiners now return (solution,bool,work); added deterministic work estimation, frontier work, termination-checker usage, and determinism logging across FJ/FP/LS.
Population / Solution Handling
cpp/src/mip_heuristics/diversity/population.*, cpp/src/mip_heuristics/solution/solution.*
Replaced legacy origin enum with mip_solution_origin_t, introduced drained_external_solution_t, changed solution constructors, removed lower/upper_slack members, added get_hash().
Presolve / Probing / Bounds
cpp/src/mip_heuristics/presolve/*, cpp/src/mip_heuristics/problem/*
Replaced many timer_t APIs with termination_checker_t, added WORK_LIMIT criterion, deterministic-presolve gating, stream/handle overrides, and device-side deterministic initializations and reductions.
Solver Entry & Integration
cpp/src/mip_heuristics/solver.*, cpp/src/mip_heuristics/solve.cu
Switched solver/solver_context to termination checker, refactored B&B callback adapter, publish/inject flow for incumbents, bitmask determinism checks, async B&B device setup, and final reconstruction logic.
PDLP / Initial Scaling
cpp/src/pdlp/initial_scaling_strategy/*
Added optional cudaStream_t stream_override to unscale_solutions overloads and propagation to CUDA ops.
Benchmarks & CI
benchmarks/linear_programming/cuopt/run_mip.cpp, ci/compute-sanitizer-suppressions.xml
Added incumbent-tracking CSV callback in benchmark and a large compute-sanitizer suppression file.
Tests & Build
cpp/tests/mip/*, cpp/tests/CMakeLists.txt, cpp/tests/mip/CMakeLists.txt, datasets/mip/download_miplib_test_dataset.sh
Extensive new and updated determinism tests (diversity, local-search, FJ, probing, callbacks), spin-kernel test utility, enabled extra test targets, and added dataset supportcase42.

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch gpudet

@aliceb-nv
Copy link
Copy Markdown
Owner Author

@coderabbitai full review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 9, 2026

✅ Actions performed

Full review triggered.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 14

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (5)
cpp/src/mip_heuristics/local_search/rounding/bounds_repair.cuh (1)

124-150: ⚠️ Potential issue | 🔴 Critical

This code will not compile: termination_checker_t timer has no default constructor.

The bare member declaration on line 153 requires an initializer. termination_checker_t has three explicit constructors (root, non-deterministic parent, and deterministic parent) but no default constructor. This member must either be explicitly initialized in the class definition or in the class constructor, e.g., termination_checker_t timer{0.0, termination_checker_t::root_tag_t{}} or passed as a constructor parameter like other termination checkers in the codebase.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cpp/src/mip_heuristics/local_search/rounding/bounds_repair.cuh` around lines
124 - 150, The member termination_checker_t timer lacks a default constructor
and must be explicitly initialized; update the class in bounds_repair.cuh to
either (A) provide an inline initializer for the member (e.g., initialize timer
with appropriate root/deterministic constructor arguments such as a time value
and root_tag_t) or (B) add a constructor parameter (or reference) to the class
and initialize the member in the constructor initializer list using that
parameter; locate the termination_checker_t timer declaration and implement one
of these two fixes (inline initialization like termination_checker_t{...} or
propagate a termination_checker_t into the class constructor and initialize
timer there) so the code compiles.
cpp/src/pdlp/initial_scaling_strategy/initial_scaling.cu (1)

778-785: ⚠️ Potential issue | 🟠 Major

Propagate the solution's stream through this overload.

This overload still calls the two-buffer helper without stream_override, so it always unscales on stream_view_. For solution_t objects created with a handle override, that puts the kernels back on the wrong stream and can race with later work on solution.handle_ptr->get_stream().

Suggested fix
 void pdlp_initial_scaling_strategy_t<i_t, f_t>::unscale_solutions(
   solution_t<i_t, f_t>& solution) const
 {
   auto& primal_solution = solution.assignment;
   rmm::device_uvector<f_t> dummy(0, solution.handle_ptr->get_stream());
   solution.is_scaled_ = false;
-  unscale_solutions(primal_solution, dummy);
+  unscale_solutions(primal_solution,
+                    dummy,
+                    solution.handle_ptr->get_stream().value());
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cpp/src/pdlp/initial_scaling_strategy/initial_scaling.cu` around lines 778 -
785, The overload
pdlp_initial_scaling_strategy_t::unscale_solutions(solution_t&) must run on the
solution's stream; create a stream_view from solution.handle_ptr->get_stream(),
construct the dummy rmm::device_uvector with that stream, and call the
two-buffer helper with the stream override (i.e., replace
unscale_solutions(primal_solution, dummy) with
unscale_solutions(primal_solution, dummy, stream_override)); keep setting
solution.is_scaled_ as before.
cpp/src/mip_heuristics/solve.cu (1)

535-537: ⚠️ Potential issue | 🟠 Major

Use the active tolerances when validating the early-incumbent fallback.

This is the one reconstruction path that still uses the default problem_t constructor. fallback_sol.compute_feasibility() can therefore run under different tolerances than the main/postsolve paths and incorrectly accept or reject the early heuristic incumbent.

Suggested fix
-        detail::problem_t<i_t, f_t> full_problem(op_problem);
+        detail::problem_t<i_t, f_t> full_problem(
+          op_problem, settings.get_tolerances(), deterministic_run);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cpp/src/mip_heuristics/solve.cu` around lines 535 - 537, The fallback path
constructs a fresh detail::problem_t via detail::problem_t<i_t, f_t>
full_problem(op_problem) which may use default tolerances, so before calling
fallback_sol.compute_feasibility() ensure full_problem uses the currently active
tolerances: either construct full_problem with the active tolerances
API/constructor or copy/assign the active tolerance struct (the same tolerances
used by the main/postsolve flow or stored on op_problem) onto full_problem (or
its .tolerances member) and then create detail::solution_t
fallback_sol(full_problem) and call fallback_sol.compute_feasibility() so it
validates using the active tolerances instead of defaults.
cpp/src/branch_and_bound/branch_and_bound.cpp (1)

3550-3557: ⚠️ Potential issue | 🔴 Critical

Don't terminate deterministic B&B from BFS exhaustion alone.

This block ignores deterministic_diving_workers_. If the BFS pool is empty while a diving worker still has queued/current work, the coordinator can declare OPTIMAL/INFEASIBLE early and stop before those dives replay their incumbents.

Suggested fix
-  if (!deterministic_workers_->any_has_work()) {
+  const bool bfs_has_work = deterministic_workers_->any_has_work();
+  const bool diving_has_work =
+    deterministic_diving_workers_ && deterministic_diving_workers_->any_has_work();
+  if (!bfs_has_work && !diving_has_work) {
     // Tree exhausted - check if we found a solution
     if (upper_bound == std::numeric_limits<f_t>::infinity()) {
       deterministic_global_termination_status_ = mip_status_t::INFEASIBLE;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cpp/src/branch_and_bound/branch_and_bound.cpp` around lines 3550 - 3557, The
termination check only inspects deterministic_workers_->any_has_work() and can
prematurely set deterministic_global_termination_status_ while
deterministic_diving_workers_ still have queued/current dives; update the
condition to require both deterministic_workers_ and
deterministic_diving_workers_ to be empty before declaring INFEASIBLE/OPTIMAL
(e.g., replace the if (!deterministic_workers_->any_has_work()) with a combined
check that also queries deterministic_diving_workers_ via its appropriate method
such as any_has_work() or any_has_queued_or_current_work()), and only then set
deterministic_global_termination_status_ based on upper_bound.
cpp/src/mip_heuristics/diversity/diversity_manager.cu (1)

127-145: ⚠️ Potential issue | 🔴 Critical

Move the deterministic work-context initialization above the env-config early return.

Line 128 returns when CUOPT_CONFIG_ID is unset, so Lines 143-144 never run in the normal case. That leaves context.gpu_heur_loop.deterministic at its default value even when CUOPT_DETERMINISM_GPU_HEURISTICS is enabled, which breaks the deterministic termination path wired through this file.

Suggested fix
   const char* env_config_id_raw = std::getenv("CUOPT_CONFIG_ID");
+  context.gpu_heur_loop.deterministic =
+    (context.settings.determinism_mode & CUOPT_DETERMINISM_GPU_HEURISTICS);
   if (env_config_id_raw == nullptr) { return; }
@@
-  context.gpu_heur_loop.deterministic =
-    (context.settings.determinism_mode & CUOPT_DETERMINISM_GPU_HEURISTICS);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cpp/src/mip_heuristics/diversity/diversity_manager.cu` around lines 127 -
145, The deterministic flag initialization for GPU heuristics is performed after
an early return when CUOPT_CONFIG_ID is unset, so move or duplicate the line
that sets context.gpu_heur_loop.deterministic =
(context.settings.determinism_mode & CUOPT_DETERMINISM_GPU_HEURISTICS) so it
executes before the getenv/early-return logic: perform the deterministic
initialization immediately (using context.settings.determinism_mode and
CUOPT_DETERMINISM_GPU_HEURISTICS) before checking env_config_id_raw/return, then
keep the existing CUOPT_CONFIG_ID parsing, max_config check and warnings
(env_config_id_raw, env_config_id, max_config, CUOPT_LOG_WARN) as-is.
🧹 Nitpick comments (21)
ci/compute-sanitizer-suppressions.xml (1)

3-21: Consider adding documentation comments for these suppression records.

The first five records (lines 3-106) lack explanatory comments unlike the later records. Brief inline comments explaining why these cuSPARSE and CUB/Thrust initcheck warnings are safe to suppress would improve maintainability and help future developers understand the rationale.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ci/compute-sanitizer-suppressions.xml` around lines 3 - 21, The suppression
records for Initcheck (e.g., records referencing libcuda.so and
cusparseCsr2cscEx2 / libcusparse.so) lack explanatory comments; update the first
five <record> entries to add brief inline documentation per suppression
explaining why the cuSPARSE and CUB/Thrust uninitialized __global__ memory read
warnings are safe to suppress (mention the specific check kind "Initcheck" and
functions like cusparseCsr2cscEx2), include reference to the underlying root
cause or upstream bug/driver note and a ticket/PR link or test that verifies the
suppression, and follow the same comment style used in the later records so
future maintainers can understand the rationale.
cpp/src/dual_simplex/phase2.cpp (1)

3554-3556: Remove redundant nested null-check for readability.

The outer condition already guarantees work_unit_context is non-null, so the inner guard can be simplified.

♻️ Proposed cleanup
-      if (work_unit_context) {
-        work_unit_context->record_work_sync_on_horizon(phase2_work_estimate / 1e8);
-      }
+      work_unit_context->record_work_sync_on_horizon(phase2_work_estimate / 1e8);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cpp/src/dual_simplex/phase2.cpp` around lines 3554 - 3556, The nested
null-check is redundant: inside the block guarded by if (work_unit_context)
remove the inner guard and directly call
work_unit_context->record_work_sync_on_horizon(phase2_work_estimate / 1e8);;
update the code in phase2.cpp where work_unit_context and
record_work_sync_on_horizon are used so there is only the single if
(work_unit_context) check and a direct invocation of
record_work_sync_on_horizon.
cpp/src/mip_heuristics/solver_solution.cu (1)

243-255: Keep pool_size consistent with what is hashed.

pool_hash ignores empty pool entries, but pool_size currently reports total entries. Logging the non-empty count would make diagnostics less ambiguous.

♻️ Proposed cleanup
   uint32_t pool_hash = 0;
+  int non_empty_pool_size = 0;
   for (const auto& pool_sol : solution_pool_) {
     if (pool_sol.size() > 0) {
       auto host_pool_sol = cuopt::host_copy(pool_sol, rmm::cuda_stream_default);
       pool_hash ^= detail::compute_hash(host_pool_sol);
+      non_empty_pool_size++;
     }
   }
@@
-    (int)solution_pool_.size());
+    non_empty_pool_size);

Also applies to: 261-274

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cpp/src/mip_heuristics/solver_solution.cu` around lines 243 - 255, The pool
hash logic currently ignores empty entries (in the loop over solution_pool_ that
computes pool_hash via detail::compute_hash(host_pool_sol)), but pool_size is
reported as the total number of entries, causing inconsistent diagnostics;
change the code that computes/reports pool_size to count only non-empty entries
(increment a non_empty_pool_count inside the same if (pool_sol.size() > 0) block
when computing pool_hash) and use that non_empty_pool_count wherever pool_size
is reported; apply the same change to the similar block referenced around
functions/lines that also compute pool_hash (the second occurrence noted at
261-274) to keep both hash and size consistent.
cpp/src/mip_heuristics/diversity/recombiners/recombiner_stats.hpp (1)

78-84: Consider marking getter as const.

The get_last_recombiner_work() accessor doesn't modify state and should be const-qualified for consistency with get_last_recombiner_time() (line 97).

Suggested fix
-  double get_last_recombiner_work() { return last_recombiner_work; }
+  double get_last_recombiner_work() const { return last_recombiner_work; }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cpp/src/mip_heuristics/diversity/recombiners/recombiner_stats.hpp` around
lines 78 - 84, The getter get_last_recombiner_work() should be const-qualified
like get_last_recombiner_time(); update its declaration/definition in
recombiner_stats.hpp by adding a trailing const (i.e., change the signature of
get_last_recombiner_work to double get_last_recombiner_work() const) so it
doesn't modify object state and matches const-correctness across accessors.
cpp/src/utilities/timer.hpp (1)

37-51: Consider removing or conditionalizing the debug code.

The commented-out debug printf (lines 42-49) is useful for development but adds noise to the codebase. Consider either:

  1. Removing it entirely if debugging is complete
  2. Wrapping it with a compile-time flag (e.g., #ifdef TIMER_DEBUG) for selective enablement
Example with compile-time flag
   bool check_time_limit(const char* caller = __builtin_FUNCTION(),
                         const char* file   = __builtin_FILE(),
                         int line           = __builtin_LINE()) const noexcept
   {
     bool elapsed = elapsed_time() >= time_limit;
-    // if (elapsed) {
-    //   printf("************ TIME LIMIT (%.2gs) REACHED BY %s:%d: %s() ***\n",
-    //          time_limit,
-    //          file,
-    //          line,
-    //          caller);
-    //   //__builtin_trap();
-    // }
+#ifdef TIMER_DEBUG
+    if (elapsed) {
+      printf("************ TIME LIMIT (%.2gs) REACHED BY %s:%d: %s() ***\n",
+             time_limit,
+             file,
+             line,
+             caller);
+    }
+#else
+    (void)caller;
+    (void)file;
+    (void)line;
+#endif
     return elapsed;
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cpp/src/utilities/timer.hpp` around lines 37 - 51, The commented debug printf
inside check_time_limit (involving caller, file, line and time_limit) should not
remain as stray comments; either remove it entirely or wrap it with a
compile-time guard such as TIMER_DEBUG so it only compiles when debugging is
enabled. Update the function check_time_limit to conditionally print the message
(using stderr) under `#ifdef` TIMER_DEBUG (or delete the block) and keep the
existing return elapsed behavior and noexcept signature unchanged; reference the
symbols check_time_limit, elapsed_time(), time_limit, caller, file, and line
when making the change.
cpp/src/mip_heuristics/presolve/probing_cache.cu (1)

866-870: Consider documenting the determinism bounds.

The determinism-mode handling looks correct: clamping iteration_limit to 1 and truncating priority_indices to 2048 ensures bounded work. The TODO acknowledges this is temporary.

Consider extracting 2048 to a named constant for clarity (e.g., constexpr size_t MAX_DETERMINISTIC_PROBING_VARS = 2048;).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cpp/src/mip_heuristics/presolve/probing_cache.cu` around lines 866 - 870, The
magic number 2048 used when truncating priority_indices for deterministic mode
should be extracted to a named constant and documented: introduce a constexpr
size_t like MAX_DETERMINISTIC_PROBING_VARS (or similar) and replace the literal
in the block checking bound_presolve.context.settings.determinism_mode &
CUOPT_DETERMINISM_GPU_HEURISTICS, leaving the existing clamping of
bound_presolve.settings.iteration_limit = 1 intact; add a short comment
explaining that this constant bounds work per deterministic heuristics.
cpp/src/mip_heuristics/diversity/recombiners/line_segment_recombiner.cuh (1)

87-88: Acknowledge TODO for work calculation formula.

The TODO: CHANGE comment and the hardcoded 1e8 divisor suggest this work formula is preliminary. The current scaling appears reasonable as a placeholder but may need calibration based on actual workloads.

Would you like me to open an issue to track refining the work unit calculation for line segment recombination?

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cpp/src/mip_heuristics/diversity/recombiners/line_segment_recombiner.cuh`
around lines 87 - 88, The current hardcoded temporary work calculation (double
work = static_cast<double>(n_different_vars) / 1e8;) in
line_segment_recombiner.cuh is marked TODO; replace the magic divisor with a
named, configurable scale and/or expose it as a parameter so it can be tuned:
introduce a constexpr or static constant like WORK_UNIT_SCALE (or add a
constructor/parameter on the LineSegmentRecombiner class or the function that
contains this logic) and compute work = static_cast<double>(n_different_vars) /
WORK_UNIT_SCALE, remove the TODO, and ensure callers provide a sensible default
or configuration value so the scaling can be calibrated without changing source
code.
benchmarks/linear_programming/cuopt/run_mip.cpp (1)

291-292: Only register the incumbent tracker when you actually persist it.

incumbent_tracker records every incumbent for every run, but the data is only consumed inside the !out_dir.empty() block. On long solves this adds avoidable callback overhead and unbounded in-memory growth even when the trace is discarded. Gate settings.set_mip_callback(&incumbent_tracker) behind the same condition, or stream records directly to the file.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@benchmarks/linear_programming/cuopt/run_mip.cpp` around lines 291 - 292, The
incumbent_tracker_t instance (incumbent_tracker) is being registered
unconditionally via settings.set_mip_callback(&incumbent_tracker) which causes
unnecessary overhead and memory growth when traces are not persisted; change the
logic so you only construct/register the callback when you will persist results
(i.e., the same condition that checks !out_dir.empty()), e.g., create
incumbent_tracker and call settings.set_mip_callback(&incumbent_tracker) inside
that persistence branch, or alternatively modify incumbent_tracker to stream
records directly to the output file instead of buffering in memory (ensure
start_run_solver is passed to the tracker as before).
cpp/include/cuopt/linear_programming/cuopt_c.h (1)

733-797: Document the callback execution contract in the new C API.

The extended callback docs still leave two user-visible behaviors implicit: whether invocations are serialized, and whether the setter follows the same host-memory/lifetime rules already called out for the legacy callback registration. Please spell that out here so callback implementations know whether user_data needs its own locking and how long callback_info remains valid.

Based on learnings, MIP incumbent callbacks are always invoked sequentially because publish_new_best_feasible holds solution_callback_mutex_ while dispatching callbacks.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cpp/include/cuopt/linear_programming/cuopt_c.h` around lines 733 - 797,
Update the documentation for cuOptMIPGetSolutionCallbackExt and its registrar
cuOptSetMIPGetSolutionCallbackExt to explicitly state the execution and lifetime
contract: note that callbacks are invoked sequentially (serialized) — i.e.,
invocations will not run concurrently because the solver holds the same
solution_callback_mutex_ when dispatching — and that all pointer arguments
(solution, objective_value, solution_bound, callback_info) and user_data refer
to host memory and are only valid for the duration of the callback invocation
(caller must copy if retaining); also mention that user_data is not synchronized
by the solver so callers must provide their own locking if sharing mutable state
across callbacks, matching the existing semantics of cuOptMIPGetSolutionCallback
and cuOptSetMIPGetSolutionCallback.
cpp/include/cuopt/linear_programming/mip/solver_settings.hpp (1)

110-136: Add Doxygen docs for the new public solver knobs.

These fields are now part of the public C++ API, but the new work-scale and gpu_heur_wait_for_exploration knobs are only described with // comments. Please promote them to Doxygen comments and call out the API surface change in the MIP docs so generated docs and migration notes stay accurate.

As per coding guidelines, "For public header files (C++ API): Check if new public functions/classes have documentation comments (Doxygen format)" and "Flag API changes that may need corresponding docs/ updates."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cpp/include/cuopt/linear_programming/mip/solver_settings.hpp` around lines
110 - 136, Add Doxygen-style documentation for the new public solver knobs so
they appear in generated API docs and migration notes: replace the inline //
comments for cpufj_work_unit_scale, gpu_heur_work_unit_scale, bb_work_unit_scale
and gpu_heur_wait_for_exploration with /** ... */ Doxygen comments that describe
the purpose, units/scale semantics (1.0 = default baseline), valid value ranges,
and behavior when set (e.g., gpu_heur_wait_for_exploration true delays GPU
heuristics until root B&B completes). Also add a short sentence in the MIP
solver public docs/migration notes indicating these new knobs were added to the
public API (referencing the same symbols) and how they affect solver behavior.
cpp/src/mip_heuristics/diversity/recombiners/sub_mip.cuh (1)

71-72: Replace the placeholder work estimate before feeding accounting.

n_vars_from_other / 1e8 is only a rough proxy for sub-MIP cost. Hard and easy sub-MIPs can spend wildly different B&B effort while reporting almost the same work, which will skew recombiner reward/work-limit accounting. Please derive this from solver-reported effort instead of fixed variable count.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cpp/src/mip_heuristics/diversity/recombiners/sub_mip.cuh` around lines 71 -
72, Replace the placeholder static estimate (double work =
static_cast<double>(n_vars_from_other) / 1e8;) with a real solver-reported
effort metric: query the sub-MIP solver instance for its effort (e.g., node
count, processed nodes, branch-and-bound iterations or solving time) and compute
work from that value (cast to double and scale to the same units used
elsewhere). Update the assignment of work to use that solver-reported metric
(e.g., use solver->getNodeCount() or solver->getSolvingTime() / a configured
time-scale) and ensure the computed work is non-negative and uses the same
normalization as the rest of the reward/work accounting code that consumes work.
cpp/src/mip_heuristics/relaxed_lp/relaxed_lp.cu (1)

74-97: TODO: Replace hardcoded iteration estimation with an actual predictor model.

The current estimation logic uses hardcoded coefficients that may not generalize well:

double estim_ms = 313 + 200 * op_problem.n_variables - 400 * op_problem.n_constraints +
                  600 * op_problem.coefficients.size() + 7100 * estim_iters;

The negative coefficient for n_constraints seems counterintuitive (more constraints typically means more work). This is marked with TODO comments, so flagging as a reminder for follow-up.

The loop structure is safe since estim_iters increases by 100 each iteration, eventually exceeding any finite work_limit.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cpp/src/mip_heuristics/relaxed_lp/relaxed_lp.cu` around lines 74 - 97, The
inline hardcoded estimator in the determinism_mode block (using estim_iters and
the ad-hoc formula built from op_problem.n_variables, op_problem.n_constraints,
op_problem.coefficients.size()) must be replaced with a proper predictor:
implement a dedicated function (e.g., predict_iteration_ms or
estimate_iterations) that takes op_problem features and work_limit and returns a
safe iteration estimate; train or derive coefficients from profiling data,
ensure feature scaling and no illogical negative weighting for n_constraints,
and use that function to set pdlp_settings.iteration_limit (and keep
pdlp_settings.time_limit/pdlp_solver_mode/presolver assignments). Ensure the new
predictor is unit-tested and documented, and remove the inline magic formula and
TODO comment.
cpp/tests/mip/determinism_test.cu (1)

292-345: Consider making callback count thresholds configurable or documenting their basis.

The test uses hardcoded constants:

constexpr size_t callback_compare_count = 5;
constexpr size_t callback_capture_limit = 32;
constexpr size_t min_gpu_callback_count = 3;

These may become fragile if the solver's behavior changes (e.g., fewer GPU solutions found within the work limit). Consider:

  1. Documenting why these specific values were chosen
  2. Making them relative to actual callback counts (e.g., compare min of both counts)

Otherwise, the test structure is solid for verifying deterministic callback sequences.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cpp/tests/mip/determinism_test.cu` around lines 292 - 345, The test hardcodes
callback thresholds (callback_compare_count, callback_capture_limit,
min_gpu_callback_count) which can be fragile; change the assertions to compute
thresholds from the observed runs instead of fixed constants: use the minimum of
callback_run1.n_calls and callback_run2.n_calls (or min(callbacks_run1.size(),
callbacks_run2.size())) for the compare count used in
expect_callback_prefixes_bitwise_equal, and derive a GPU-callback threshold
based on count_gpu_callbacks(callbacks_run1/2) (e.g., require at least
min(count_gpu_callbacks(run1), count_gpu_callbacks(run2)) and/or a relative
fraction) and document the chosen logic in a brief comment near the constants so
future changes to solve_mip/seed behavior won’t break the test; update
references in this function
(DeterministicBBTest::deterministic_callback_sequence_reproducible_with_gpu_pipeline,
callback_run1/callback_run2, callbacks_run1/callbacks_run2,
expect_callback_prefixes_bitwise_equal, count_gpu_callbacks) accordingly.
cpp/src/mip_heuristics/local_search/line_segment_search/line_segment_search.cu (1)

164-173: Dead code: unreachable else branch.

The condition if (true) makes the else branch unreachable. This appears to be debug/development code that should either be cleaned up or converted to a proper configuration flag.

Remove dead code or use a proper flag
     bool is_feasible = false;
-    // if (!settings.recombiner_mode) {
-    if (true) {
-      is_feasible = solution.round_nearest();
-    } else {
-      fj.settings.mode            = fj_mode_t::ROUNDING;
-      fj.settings.update_weights  = false;
-      fj.settings.feasibility_run = is_feasibility_run;
-      fj.settings.time_limit      = std::min(0.1, timer.remaining_time());
-      is_feasible                 = fj.solve(solution);
-    }
+    is_feasible = solution.round_nearest();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@cpp/src/mip_heuristics/local_search/line_segment_search/line_segment_search.cu`
around lines 164 - 173, The current if (true) makes the else branch unreachable;
either remove the dead branch or restore/replace the debug flag so the code uses
the intended configuration (e.g. settings.recombiner_mode) instead of a
hardcoded true. Update the conditional around solution.round_nearest() so it
checks the actual configuration (restore use of settings.recombiner_mode or
another proper flag) and keep the fallback that sets fj.settings.mode,
fj.settings.update_weights, fj.settings.feasibility_run, fj.settings.time_limit
(using timer.remaining_time()) and calls fj.solve(solution) when the flag
indicates a feasibility run; remove any leftover debug-only constructs after
making the switch.
cpp/tests/mip/mip_utils.cuh (1)

231-235: Unchecked access to fj.climbers[0].

The code accesses fj.climbers[0] without verifying that climbers is non-empty. While this may be guaranteed by the FJ solver construction, adding a guard or assertion would make the test more robust.

Add assertion for safety
   fj.solve(solution);
   auto solution_vector = host_copy(solution.assignment, solution.handle_ptr->get_stream());

+  cuopt_assert(!fj.climbers.empty(), "FJ solver must have at least one climber");
   return {solution,
           solution_vector,
           fj.climbers[0]->local_minimums_reached.value(solution.handle_ptr->get_stream()),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cpp/tests/mip/mip_utils.cuh` around lines 231 - 235, Unchecked access to
fj.climbers[0] can crash if climbers is empty; before returning the tuple that
reads fj.climbers[0]->local_minimums_reached, incumbent_objective, and
violation_score, add a safety check (e.g. assert(!fj.climbers.empty()) or an if
that throws/logs) to guarantee climbers has at least one element, and reference
fj.climbers in the check so the subsequent uses in this return expression are
safe.
cpp/include/cuopt/linear_programming/utilities/internals.hpp (2)

26-62: Add Doxygen documentation for new public API elements.

This is a public header and the new mip_solution_origin_t enum and mip_solution_origin_to_string function are part of the public API. Consider adding Doxygen documentation describing:

  • The purpose and use cases for mip_solution_origin_t
  • When each origin value is used
  • Thread-safety guarantees (if any)

As per coding guidelines: "For public header files (C++ API): Check if new public functions/classes have documentation comments (Doxygen format)."

Example Doxygen documentation
+/**
+ * `@brief` Identifies the algorithmic origin of a MIP solution.
+ *
+ * This enum indicates which solver component produced a given incumbent solution,
+ * useful for analysis, logging, and callback-based solution tracking.
+ */
 enum class mip_solution_origin_t : uint32_t {
   UNKNOWN                 = CUOPT_MIP_SOLUTION_ORIGIN_UNKNOWN,
   // ... etc
 };

+/**
+ * `@brief` Convert a solution origin to a human-readable string.
+ * `@param` origin The solution origin enum value
+ * `@return` A null-terminated string literal describing the origin
+ */
 constexpr const char* mip_solution_origin_to_string(mip_solution_origin_t origin)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cpp/include/cuopt/linear_programming/utilities/internals.hpp` around lines 26
- 62, Add Doxygen comments for the new public API: document the enum
mip_solution_origin_t and the helper function mip_solution_origin_to_string. For
mip_solution_origin_t provide a brief description of its purpose (identifies how
a MIP solution was produced), list and describe each enumerator (UNKNOWN,
BRANCH_AND_BOUND_NODE, BRANCH_AND_BOUND_DIVING, FEASIBILITY_JUMP,
CPU_FEASIBILITY_JUMP, LOCAL_SEARCH, LP_ROUNDING, RECOMBINATION, SUB_MIP,
USER_INITIAL, USER_INJECTED, RINS, PRESOLVE) and typical use cases for each; for
mip_solution_origin_to_string document its behavior, return value and that it
maps enum values to human-readable strings; and add a sentence about
thread-safety/constness (e.g., function is constexpr/const and safe for
concurrent reads) as per public-header documentation conventions.

102-113: Add Doxygen documentation for get_solution_callback_ext_t.

The new extended callback class is a public API addition. Document:

  • The difference from get_solution_callback_t
  • The purpose of the callback_info parameter
  • Usage example if appropriate

As per coding guidelines: "Flag API changes that may need corresponding docs/ updates."

Example Doxygen documentation
+/**
+ * `@brief` Extended solution callback providing additional metadata about incumbents.
+ *
+ * Unlike get_solution_callback_t, this callback receives a mip_solution_callback_info_t
+ * structure containing solution origin, work timestamp, and other metadata.
+ *
+ * `@note` Callbacks are invoked sequentially under a mutex; implementations do not
+ * need their own synchronization.
+ */
 class get_solution_callback_ext_t : public base_solution_callback_t {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cpp/include/cuopt/linear_programming/utilities/internals.hpp` around lines
102 - 113, Add Doxygen comments to the public class get_solution_callback_ext_t
explaining it is an extended variant of get_solution_callback_t (describe how it
differs: accepts extra callback context info via the callback_info parameter and
returns solution/metrics through the existing void* outputs), document the
purpose and contents of the mip_solution_callback_info_t callback_info parameter
(what fields callers can expect and when it is non-null), and annotate the
get_solution virtual method with `@param` tags for data, objective_value,
solution_bound, callback_info, and user_data plus a short usage example showing
how to cast the void* outputs and read callback_info; also add a brief `@note`
that this is a public API addition so corresponding external docs should be
updated.
cpp/tests/mip/determinism_utils.cuh (1)

57-75: Member initialization order mismatch causes undefined behavior.

The class declares members in order: stream, flag, spin_thread (lines 72-74), but the member initializer list initializes flag before stream is fully constructed. C++ initializes members in declaration order, not initializer list order, so stream will actually be initialized first. However, the initializer list syntax flag(0, stream) appears before stream is listed, which can cause confusion and tools to warn.

The current declaration order (stream then flag) is actually correct, so the code will work, but the initializer list order should match declaration order for clarity and to avoid compiler warnings.

Reorder initializer list to match declaration order
   spin_stream_raii_t()
-    : flag(0, stream), spin_thread(launch_spin_kernel_stream_thread, stream.view(), flag.data())
+    : stream(), flag(0, stream), spin_thread(launch_spin_kernel_stream_thread, stream.view(), flag.data())
   {
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cpp/tests/mip/determinism_utils.cuh` around lines 57 - 75, The constructor
initializer list in class spin_stream_raii_t should match the member declaration
order (stream, flag, spin_thread) to avoid warnings: update spin_stream_raii_t()
so it initializes stream first, then flag (using flag(0, stream)), then
spin_thread (launch_spin_kernel_stream_thread, stream.view(), flag.data()); keep
the same arguments (stream.view(), flag.data()) but reorder the initializer list
to stream, flag, spin_thread.
cpp/src/mip_heuristics/diversity/recombiners/bound_prop_recombiner.cuh (1)

192-193: TODO: Placeholder work calculation needs implementation.

The comment indicates this work metric calculation is temporary. The current formula n_vars_from_other / 1e8 may not accurately reflect actual computational work.

Would you like me to help design a more representative work metric based on the actual operations performed (probing, constraint propagation iterations, etc.)?

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cpp/src/mip_heuristics/diversity/recombiners/bound_prop_recombiner.cuh`
around lines 192 - 193, The temporary work metric using double work =
static_cast<double>(n_vars_from_other) / 1e8 must be replaced with a
representative cost model: compute work as a weighted sum of the actual
operations performed (e.g., number of variables touched n_vars_from_other,
number of propagation iterations n_prop_iters, number of constraints/clauses
inspected n_constraints_seen) using named constants like COST_PER_VAR and
COST_PER_PROP_ITER (all as double) and ensure the result is non-zero (clamp with
a small epsilon). Update the calculation in bound_prop_recombiner.cuh where work
is assigned, remove the TODO, and use these symbolic weights so the metric can
be tuned later (e.g., work = n_vars_from_other*COST_PER_VAR +
n_prop_iters*COST_PER_PROP_ITER + n_constraints_seen*COST_PER_CONSTRAINT).
cpp/include/cuopt/linear_programming/constants.h (1)

114-139: Document the new public determinism/origin constants in the C API docs.

These macros are part of the public surface, and external callers can rely on the raw integer values directly. Please add/update the user-facing docs or migration notes so integrators know mip_determinism_mode is now a bitset and callbacks may report the new CUOPT_MIP_SOLUTION_ORIGIN_* values.

As per coding guidelines, cpp/include/cuopt/**/*: "Flag API changes that may need corresponding docs/ updates" and "For breaking changes, recommend updating docs and migration guides."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cpp/include/cuopt/linear_programming/constants.h` around lines 114 - 139,
Public-facing macros for determinism and MIP solution origin
(CUOPT_DETERMINISM_*, CUOPT_MODE_*, and CUOPT_MIP_SOLUTION_ORIGIN_*) were
extended and are part of the C API public surface; update the C API
documentation and migration notes to describe that mip_determinism_mode is now a
bitset (document CUOPT_DETERMINISM_NONE, CUOPT_DETERMINISM_BB,
CUOPT_DETERMINISM_GPU_HEURISTICS, CUOPT_DETERMINISM_FULL and the alias modes
CUOPT_MODE_*) and add entries for each new CUOPT_MIP_SOLUTION_ORIGIN_* value
(including meanings for BRANCH_AND_BOUND_DIVING, FEASIBILITY_JUMP,
CPU_FEASIBILITY_JUMP, LOCAL_SEARCH, LP_ROUNDING, RECOMBINATION, SUB_MIP,
USER_INITIAL, USER_INJECTED, RINS, PRESOLVE); mention potential ABI/behavior
implications for callbacks that inspect raw integer values and include a short
migration note guiding integrators how to update checks (bitwise checks for
determinism) and how callbacks should handle the new origin enum values.
cpp/src/mip_heuristics/local_search/local_search.cu (1)

567-568: Consider using a nested timer for consistency.

This timer is created with *context.termination as the base, making it a top-level timer rather than nested under timer. Compare with line 373 where local_timer is nested under its parent timer:

termination_checker_t local_timer(context.gpu_heur_loop, fj_settings.time_limit, timer);

While the explicit timer.remaining_time() / 3 budget and outer timer checks at lines 580/607 provide correctness, nesting would ensure consistent termination propagation:

♻️ Suggested change for consistency
-      termination_checker_t binary_timer(
-        context.gpu_heur_loop, timer.remaining_time() / 3, *context.termination);
+      termination_checker_t binary_timer(
+        context.gpu_heur_loop, timer.remaining_time() / 3, timer);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cpp/src/mip_heuristics/local_search/local_search.cu` around lines 567 - 568,
The binary_timer is constructed as a top-level termination source using
*context.termination instead of being nested under the parent timer; change the
construction of termination_checker_t binary_timer to pass the parent timer
(timer) as the third argument while keeping the same budget
(timer.remaining_time() / 3) so that termination propagation is consistent with
local_timer (see local_timer usage) and the outer timer checks still apply.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 8bff2b1a-0a96-43e8-b4b3-b846688dbbd2

📥 Commits

Reviewing files that changed from the base of the PR and between 378add0 and c34cf0b.

📒 Files selected for processing (116)
  • benchmarks/linear_programming/cuopt/run_mip.cpp
  • ci/compute-sanitizer-suppressions.xml
  • cpp/include/cuopt/linear_programming/constants.h
  • cpp/include/cuopt/linear_programming/cuopt_c.h
  • cpp/include/cuopt/linear_programming/mip/solver_settings.hpp
  • cpp/include/cuopt/linear_programming/utilities/internals.hpp
  • cpp/src/branch_and_bound/branch_and_bound.cpp
  • cpp/src/branch_and_bound/branch_and_bound.hpp
  • cpp/src/branch_and_bound/deterministic_workers.hpp
  • cpp/src/branch_and_bound/pseudo_costs.cpp
  • cpp/src/branch_and_bound/pseudo_costs.hpp
  • cpp/src/dual_simplex/basis_updates.cpp
  • cpp/src/dual_simplex/bound_flipping_ratio_test.cpp
  • cpp/src/dual_simplex/bound_flipping_ratio_test.hpp
  • cpp/src/dual_simplex/phase2.cpp
  • cpp/src/dual_simplex/simplex_solver_settings.hpp
  • cpp/src/math_optimization/solver_settings.cu
  • cpp/src/mip_heuristics/diversity/diversity_config.hpp
  • cpp/src/mip_heuristics/diversity/diversity_manager.cu
  • cpp/src/mip_heuristics/diversity/diversity_manager.cuh
  • cpp/src/mip_heuristics/diversity/lns/rins.cu
  • cpp/src/mip_heuristics/diversity/multi_armed_bandit.cuh
  • cpp/src/mip_heuristics/diversity/population.cu
  • cpp/src/mip_heuristics/diversity/population.cuh
  • cpp/src/mip_heuristics/diversity/recombiners/bound_prop_recombiner.cuh
  • cpp/src/mip_heuristics/diversity/recombiners/fp_recombiner.cuh
  • cpp/src/mip_heuristics/diversity/recombiners/line_segment_recombiner.cuh
  • cpp/src/mip_heuristics/diversity/recombiners/recombiner.cuh
  • cpp/src/mip_heuristics/diversity/recombiners/recombiner_stats.hpp
  • cpp/src/mip_heuristics/diversity/recombiners/sub_mip.cuh
  • cpp/src/mip_heuristics/diversity/weights.cuh
  • cpp/src/mip_heuristics/early_heuristic.cuh
  • cpp/src/mip_heuristics/feasibility_jump/early_cpufj.cuh
  • cpp/src/mip_heuristics/feasibility_jump/early_gpufj.cu
  • cpp/src/mip_heuristics/feasibility_jump/early_gpufj.cuh
  • cpp/src/mip_heuristics/feasibility_jump/feasibility_jump.cu
  • cpp/src/mip_heuristics/feasibility_jump/feasibility_jump.cuh
  • cpp/src/mip_heuristics/feasibility_jump/feasibility_jump_impl_common.cuh
  • cpp/src/mip_heuristics/feasibility_jump/feasibility_jump_kernels.cu
  • cpp/src/mip_heuristics/feasibility_jump/feasibility_jump_kernels.cuh
  • cpp/src/mip_heuristics/feasibility_jump/fj_cpu.cu
  • cpp/src/mip_heuristics/feasibility_jump/fj_cpu.cuh
  • cpp/src/mip_heuristics/feasibility_jump/load_balancing.cuh
  • cpp/src/mip_heuristics/feasibility_jump/utils.cuh
  • cpp/src/mip_heuristics/local_search/feasibility_pump/feasibility_pump.cu
  • cpp/src/mip_heuristics/local_search/feasibility_pump/feasibility_pump.cuh
  • cpp/src/mip_heuristics/local_search/line_segment_search/line_segment_search.cu
  • cpp/src/mip_heuristics/local_search/line_segment_search/line_segment_search.cuh
  • cpp/src/mip_heuristics/local_search/local_search.cu
  • cpp/src/mip_heuristics/local_search/local_search.cuh
  • cpp/src/mip_heuristics/local_search/rounding/bounds_repair.cu
  • cpp/src/mip_heuristics/local_search/rounding/bounds_repair.cuh
  • cpp/src/mip_heuristics/local_search/rounding/constraint_prop.cu
  • cpp/src/mip_heuristics/local_search/rounding/constraint_prop.cuh
  • cpp/src/mip_heuristics/local_search/rounding/lb_bounds_repair.cu
  • cpp/src/mip_heuristics/local_search/rounding/lb_bounds_repair.cuh
  • cpp/src/mip_heuristics/local_search/rounding/lb_constraint_prop.cu
  • cpp/src/mip_heuristics/local_search/rounding/lb_constraint_prop.cuh
  • cpp/src/mip_heuristics/local_search/rounding/simple_rounding.cu
  • cpp/src/mip_heuristics/local_search/rounding/simple_rounding_kernels.cuh
  • cpp/src/mip_heuristics/mip_constants.hpp
  • cpp/src/mip_heuristics/presolve/bounds_presolve.cu
  • cpp/src/mip_heuristics/presolve/bounds_presolve.cuh
  • cpp/src/mip_heuristics/presolve/bounds_update_data.cu
  • cpp/src/mip_heuristics/presolve/lb_probing_cache.cu
  • cpp/src/mip_heuristics/presolve/load_balanced_bounds_presolve.cu
  • cpp/src/mip_heuristics/presolve/load_balanced_bounds_presolve.cuh
  • cpp/src/mip_heuristics/presolve/multi_probe.cu
  • cpp/src/mip_heuristics/presolve/multi_probe.cuh
  • cpp/src/mip_heuristics/presolve/probing_cache.cu
  • cpp/src/mip_heuristics/presolve/probing_cache.cuh
  • cpp/src/mip_heuristics/presolve/third_party_presolve.cpp
  • cpp/src/mip_heuristics/presolve/third_party_presolve.hpp
  • cpp/src/mip_heuristics/presolve/utils.cuh
  • cpp/src/mip_heuristics/problem/presolve_data.cu
  • cpp/src/mip_heuristics/problem/presolve_data.cuh
  • cpp/src/mip_heuristics/problem/problem.cu
  • cpp/src/mip_heuristics/problem/problem.cuh
  • cpp/src/mip_heuristics/problem/problem_fixing.cuh
  • cpp/src/mip_heuristics/relaxed_lp/relaxed_lp.cu
  • cpp/src/mip_heuristics/relaxed_lp/relaxed_lp.cuh
  • cpp/src/mip_heuristics/solution/solution.cu
  • cpp/src/mip_heuristics/solution/solution.cuh
  • cpp/src/mip_heuristics/solution_callbacks.cuh
  • cpp/src/mip_heuristics/solve.cu
  • cpp/src/mip_heuristics/solver.cu
  • cpp/src/mip_heuristics/solver.cuh
  • cpp/src/mip_heuristics/solver_context.cuh
  • cpp/src/mip_heuristics/solver_solution.cu
  • cpp/src/pdlp/cuopt_c.cpp
  • cpp/src/pdlp/initial_scaling_strategy/initial_scaling.cu
  • cpp/src/pdlp/initial_scaling_strategy/initial_scaling.cuh
  • cpp/src/utilities/copy_helpers.hpp
  • cpp/src/utilities/cuda_helpers.cuh
  • cpp/src/utilities/determinism_log.hpp
  • cpp/src/utilities/seed_generator.cu
  • cpp/src/utilities/seed_generator.cuh
  • cpp/src/utilities/termination_checker.hpp
  • cpp/src/utilities/timer.hpp
  • cpp/src/utilities/work_limit_context.hpp
  • cpp/src/utilities/work_unit_scheduler.cpp
  • cpp/src/utilities/work_unit_scheduler.hpp
  • cpp/tests/CMakeLists.txt
  • cpp/tests/mip/CMakeLists.txt
  • cpp/tests/mip/determinism_test.cu
  • cpp/tests/mip/determinism_utils.cuh
  • cpp/tests/mip/diversity_test.cu
  • cpp/tests/mip/feasibility_jump_tests.cu
  • cpp/tests/mip/incumbent_callback_test.cu
  • cpp/tests/mip/load_balancing_test.cu
  • cpp/tests/mip/local_search_test.cu
  • cpp/tests/mip/mip_utils.cuh
  • cpp/tests/mip/multi_probe_test.cu
  • cpp/tests/mip/presolve_test.cu
  • datasets/mip/download_miplib_test_dataset.sh
  • skills/cuopt-developer/SKILL.md

Comment thread ci/compute-sanitizer-suppressions.xml
Comment thread cpp/src/branch_and_bound/branch_and_bound.cpp
Comment thread cpp/src/mip_heuristics/diversity/diversity_manager.cu
Comment thread cpp/src/mip_heuristics/diversity/recombiners/fp_recombiner.cuh
Comment thread cpp/src/mip_heuristics/diversity/weights.cuh
Comment thread cpp/src/mip_heuristics/solution_callbacks.cuh Outdated
Comment thread cpp/tests/mip/diversity_test.cu
Comment thread cpp/tests/mip/diversity_test.cu Outdated
Comment thread cpp/tests/mip/feasibility_jump_tests.cu
Comment thread cpp/tests/mip/load_balancing_test.cu
@aliceb-nv aliceb-nv closed this Apr 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant