Skip to content

Conversation

@nguidotti
Copy link
Contributor

@nguidotti nguidotti commented Dec 12, 2025

The goal of this PR is to improve the diving framework in the MIP solver based on [1, Section 9.2]. It introduces the following changes:

MIPLIB results (GH200):

main (#718): 226 feasible solutions with 12.8% average primal gap
diving-heuristics: 226 feasible solutions with 11.4% average primal gap

Reference:

[1] T. Achterberg, “Constraint Integer Programming,” PhD, Technischen Universität Berlin,
Berlin, 2007. doi: 10.14279/depositonce-1634.

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

Summary by CodeRabbit

  • New Features

    • Multiple diving heuristics added (line-search, pseudocost, guided, coefficient) to improve branching behavior.
    • New queue-based node scheduler for better task distribution and parallel exploration.
  • Improvements

    • Basis repair and refactor routines now use variable bound information for more robust repairs.
    • Exposed diving configuration settings to control heuristics, task counts, and limits.
    • Enhanced variable-selection to return objective estimates and richer solver statistics.

✏️ Tip: You can customize this high-level summary in your review settings.

@nguidotti nguidotti added this to the 26.02 milestone Dec 12, 2025
@nguidotti nguidotti self-assigned this Dec 12, 2025
@nguidotti nguidotti requested review from a team as code owners December 12, 2025 15:56
@nguidotti nguidotti added non-breaking Introduces a non-breaking change improvement Improves an existing functionality labels Dec 12, 2025
@nguidotti nguidotti added the mip label Dec 12, 2025
@coderabbitai
Copy link

coderabbitai bot commented Dec 12, 2025

📝 Walkthrough

Walkthrough

Introduces new diving heuristic implementations and a node_queue scheduler, replaces the old diving_queue, extends basis repair and refactor interfaces with LP bounds, changes pseudo-costs and phase2 APIs, expands branching thread types to multi-strategy bnb_thread_type_t, and updates related settings, logging, and call sites.

Changes

Cohort / File(s) Summary
Build Configuration
cpp/src/dual_simplex/CMakeLists.txt
Adds diving_heuristics.cpp to DUAL_SIMPLEX_SRC_FILES.
Diving Heuristics (New)
cpp/src/dual_simplex/diving_heuristics.hpp, cpp/src/dual_simplex/diving_heuristics.cpp
Adds new diving strategies and helpers: line_search_diving, pseudocost_diving, guided_diving, coefficient_diving, calculate_variable_locks, and branch_variable_t type; includes explicit double instantiations.
Node Scheduling Infrastructure (New)
cpp/src/dual_simplex/node_queue.hpp
Introduces heap_t and node_queue_t with synchronized best-first and diving heaps, dual-queue scheduling, and APIs to push/pop nodes and query lower bounds.
Removed Queue Implementation
cpp/src/dual_simplex/diving_queue.hpp
Removes diving_queue_t and diving_root_t min-heap based diving queue.
Branch and Bound Core Refactor
cpp/src/dual_simplex/branch_and_bound.hpp, cpp/src/dual_simplex/branch_and_bound.cpp
Replaces thread_type_t with bnb_thread_type_t (adds multiple diving strategies), adds bnb_stats_t, variable_selection API, new plunge/dive/thread signatures, moves from heap to node_queue, introduces martin_criteria and initializes Arow_.
Basis Repair Signature & Logic
cpp/src/dual_simplex/basis_solves.hpp, cpp/src/dual_simplex/basis_solves.cpp
Extends basis_repair declaration and template instantiations to accept lower and upper vectors; selection of nonbasic status now considers bounds (NONBASIC_LOWER/UPPER/FREE).
Basis Updates
cpp/src/dual_simplex/basis_updates.hpp, cpp/src/dual_simplex/basis_updates.cpp
refactor_basis now takes lower and upper vectors and forwards them to basis_repair on factorization failure.
Basis Repair Call Sites
cpp/src/dual_simplex/crossover.cpp, cpp/src/dual_simplex/primal.cpp
Updated basis_repair calls to pass lp.lower and lp.upper.
Phase2 API and Callers
cpp/src/dual_simplex/phase2.cpp
compute_initial_primal_infeasibilities now has an extra f_t& primal_inf out parameter and returns squared infeasibility; updated callers and uses refactor_basis(..., lp.lower, lp.upper).
Pseudo-Costs API & Concurrency
cpp/src/dual_simplex/pseudo_costs.hpp, cpp/src/dual_simplex/pseudo_costs.cpp
Replaces variable_selection with variable_selection_and_obj_estimate returning std::pair<i_t,f_t> and accepting lower_bound; replaces manual mutex with std::lock_guard.
Settings Configuration
cpp/src/dual_simplex/simplex_solver_settings.hpp
Adds diving_heuristics_settings_t and replaces num_diving_threads with nested diving_settings (num_diving_tasks, disable flags, limits, backtrack).
Node Metadata
cpp/src/dual_simplex/mip_node.hpp
Adds objective_estimate member to mip_node_t (initialized to +inf), copies it in constructors/detach; removes node_compare_t.
Logging Utilities
cpp/src/dual_simplex/logger.hpp
Changes enable_log_to_file mode parameter to const char* and adds optional mode to set_log_file.
Bounds Strengthening Logging
cpp/src/dual_simplex/bounds_strengthening.cpp
Replaces printf infeasibility messages with debug-level logger calls.
Diving Queue Removal / Replacement
cpp/src/dual_simplex/diving_queue.hpp, cpp/src/dual_simplex/node_queue.hpp
Removes old diving_queue implementation; functionality replaced by node_queue_t.
MIP Solver Threading & Config Usage
cpp/src/mip/solver.cu
Replaces assignment to num_diving_threads with diving_settings.num_diving_tasks and removes a CPU thread announcement log.
MIP Diversity Components
cpp/src/mip/diversity/lns/rins.cu, cpp/src/mip/diversity/recombiners/sub_mip.cuh
Replace flat num_diving_threads config with diving_settings block, explicitly disabling certain diving strategies and setting num_diving_tasks.
New Source File Added
cpp/src/dual_simplex/diving_heuristics.cpp
New implementation file compiled via CMake (see Build Configuration).

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Areas needing focused review:

  • branch_and_bound.hpp/cpp: threading model, new enum, stats, and node_queue integration.
  • diving_heuristics.cpp: correctness of selection scores, locks, and template instantiations.
  • node_queue.hpp: synchronization, heap correctness, and interactions with garbage/ownership of node pointers.
  • Basis signature propagation: basis_repair and refactor_basis changes across crossover, primal, phase2, and basis_updates.
  • Pseudo-costs API change: ensure all call sites updated to new return type and lower_bound parameter.
  • Phase2 API change: callers updated for new out parameter and squared return value handling.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 1.49% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title check ❓ Inconclusive The PR title 'Diving Improvements' is vague and does not clearly convey the specific nature of the changes, which involve implementing multiple diving heuristics, creating a node queue infrastructure, and refactoring basis repair signatures. Consider using a more descriptive title like 'Implement multiple diving heuristics and node queue infrastructure' to better communicate the primary changes to reviewers.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

Copy link

@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: 4

Caution

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

⚠️ Outside diff range comments (1)
cpp/src/dual_simplex/phase2.cpp (1)

2837-2858: Pass primal_infeasibility_squared instead of primal_infeasibility to update_single_primal_infeasibility at line 2858.

The previous two calls to update_primal_infeasibilities (lines 2837 and 2849) correctly accumulate squared infeasibility changes into primal_infeasibility_squared. However, update_single_primal_infeasibility also operates on squared infeasibilities internally (computing infeas * infeas and accumulating via primal_inf + (new_val - old_val)), so it should update the same accumulator. Passing primal_infeasibility instead breaks infeasibility tracking—the entering variable's squared infeasibility delta is accumulated into the wrong variable, leaving primal_infeasibility_squared incomplete.

🧹 Nitpick comments (8)
cpp/src/mip/diversity/recombiners/sub_mip.cuh (1)

108-114: Diving heuristics temporarily restricted with clear intent.

The explicit configuration disables most diving heuristics (guided, coefficient, pseudocost) for SubMIP, restricting to line search diving only. The comment clearly indicates this is a temporary restriction pending future enablement of all heuristics.

Consider extracting this diving configuration pattern into a helper function since the same configuration appears in multiple files (sub_mip.cuh and rins.cu). This would reduce duplication and make future updates easier.

// Example helper
static auto create_line_search_only_diving_settings() {
  diving_heuristics_settings_t settings;
  settings.num_diving_tasks = 1;
  settings.disable_guided_diving = true;
  settings.disable_coefficient_diving = true;
  settings.disable_pseudocost_diving = true;
  return settings;
}

Based on coding guidelines: refactor code duplication in solver components.

cpp/src/mip/diversity/lns/rins.cu (1)

258-270: Branch-and-bound settings correctly updated for new diving API.

The configuration properly integrates the new diving_settings structure while preserving existing behavior for threading, tolerances, logging, and callbacks. The temporary restriction to line search diving only is clearly documented.

This file contains the same diving configuration pattern as sub_mip.cuh. Consider consolidating this repeated configuration into a shared helper function to reduce duplication.

Based on coding guidelines: refactor code duplication in solver components (3+ occurrences).

cpp/src/dual_simplex/diving_heuristics.hpp (1)

10-13: Consider adding explicit logger include.

logger_t& is used in all function signatures, but the header relies on transitive inclusion through pseudo_costs.hpp. For better header self-containment and faster compile times, consider adding an explicit include or forward declaration:

 #include <dual_simplex/basis_updates.hpp>
 #include <dual_simplex/bounds_strengthening.hpp>
 #include <dual_simplex/pseudo_costs.hpp>
+#include <dual_simplex/logger.hpp>
 #include <vector>
cpp/src/dual_simplex/basis_solves.cpp (1)

663-671: Logic flow issue: condition at line 667 may be unreachable in practice.

The current logic:

  1. Line 663: If lower == -inf && upper == inf → FREE
  2. Line 665: Else if lower > -inf → LOWER
  3. Line 667: Else if upper < inf → UPPER
  4. Line 669: Else → assert

At line 667, we know lower[bad_j] <= -inf (from failing line 665). Combined with failing line 663, we must have upper[bad_j] < inf, so the condition is always true when reached. The else at line 669 is dead code.

Consider simplifying:

     if (lower[bad_j] == -inf && upper[bad_j] == inf) {
       vstatus[bad_j] = variable_status_t::NONBASIC_FREE;
     } else if (lower[bad_j] > -inf) {
       vstatus[bad_j] = variable_status_t::NONBASIC_LOWER;
-    } else if (upper[bad_j] < inf) {
-      vstatus[bad_j] = variable_status_t::NONBASIC_UPPER;
     } else {
-      assert(1 == 0);
+      vstatus[bad_j] = variable_status_t::NONBASIC_UPPER;
     }

The assert is unnecessary since the logic is exhaustive.

cpp/src/dual_simplex/diving_heuristics.cpp (1)

49-55: Edge case: empty fractional vector causes undefined behavior.

If fractional is empty, the fallback at line 50 accesses fractional[0] which is undefined, and the assertions at lines 54-55 will fail. While callers should ensure fractional is non-empty, defensive handling would improve robustness:

   if (round_dir == rounding_direction_t::NONE) {
+    assert(!fractional.empty() && "line_search_diving called with empty fractional list");
     branch_var = fractional[0];
     round_dir  = rounding_direction_t::DOWN;
   }
-
-  assert(round_dir != rounding_direction_t::NONE);
-  assert(branch_var >= 0);
cpp/src/dual_simplex/node_queue.hpp (3)

33-38: Incorrect use of std::forward with doubly-referenced type.

The Args&& in the template parameter already declares forwarding references. Using std::forward<Args&&> adds an extra &&, which is redundant and could cause issues with some compilers.

   template <typename... Args>
   void emplace(Args&&... args)
   {
-    buffer.emplace_back(std::forward<Args&&>(args)...);
+    buffer.emplace_back(std::forward<Args>(args)...);
     std::push_heap(buffer.begin(), buffer.end(), comp);
   }

136-146: Potential integer type narrowing in size accessors.

heap_t::size() returns size_t, but diving_queue_size() and best_first_queue_size() return i_t (typically int). For very large heaps exceeding INT_MAX, this could cause truncation.

Consider returning size_t or using static_cast with appropriate bounds checking:

-  i_t diving_queue_size()
+  size_t diving_queue_size()
   {
     std::lock_guard<omp_mutex_t> lock(mutex);
     return diving_heap.size();
   }

-  i_t best_first_queue_size()
+  size_t best_first_queue_size()
   {
     std::lock_guard<omp_mutex_t> lock(mutex);
     return best_first_heap.size();
   }

116-134: Stale entries in diving_heap cause O(n) iterations in pop_diving().

Both best_first_heap and diving_heap reference the same heap_entry_t objects via shared_ptr. When pop_best_first() executes std::exchange(entry.value()->node, nullptr) at line 110, it nullifies the entry's node pointer. However, the entry itself remains in diving_heap. Subsequent calls to pop_diving() must iterate through these stale entries (lines 122–130) until finding a valid one, resulting in O(n) complexity where n is the number of stale entries accumulated.

Consider maintaining a count of valid entries or implementing atomic removal from both heaps to avoid worst-case iteration when heavy best-first activity precedes diving operations.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 89ebcbb and 501c20d.

📒 Files selected for processing (23)
  • cpp/src/dual_simplex/CMakeLists.txt (1 hunks)
  • cpp/src/dual_simplex/basis_solves.cpp (3 hunks)
  • cpp/src/dual_simplex/basis_solves.hpp (1 hunks)
  • cpp/src/dual_simplex/basis_updates.cpp (2 hunks)
  • cpp/src/dual_simplex/basis_updates.hpp (1 hunks)
  • cpp/src/dual_simplex/bounds_strengthening.cpp (2 hunks)
  • cpp/src/dual_simplex/branch_and_bound.cpp (30 hunks)
  • cpp/src/dual_simplex/branch_and_bound.hpp (8 hunks)
  • cpp/src/dual_simplex/crossover.cpp (3 hunks)
  • cpp/src/dual_simplex/diving_heuristics.cpp (1 hunks)
  • cpp/src/dual_simplex/diving_heuristics.hpp (1 hunks)
  • cpp/src/dual_simplex/diving_queue.hpp (0 hunks)
  • cpp/src/dual_simplex/logger.hpp (1 hunks)
  • cpp/src/dual_simplex/mip_node.hpp (4 hunks)
  • cpp/src/dual_simplex/node_queue.hpp (1 hunks)
  • cpp/src/dual_simplex/phase2.cpp (12 hunks)
  • cpp/src/dual_simplex/primal.cpp (1 hunks)
  • cpp/src/dual_simplex/pseudo_costs.cpp (4 hunks)
  • cpp/src/dual_simplex/pseudo_costs.hpp (1 hunks)
  • cpp/src/dual_simplex/simplex_solver_settings.hpp (3 hunks)
  • cpp/src/mip/diversity/lns/rins.cu (1 hunks)
  • cpp/src/mip/diversity/recombiners/sub_mip.cuh (1 hunks)
  • cpp/src/mip/solver.cu (1 hunks)
💤 Files with no reviewable changes (1)
  • cpp/src/dual_simplex/diving_queue.hpp
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{cu,cuh,cpp,hpp,h}

📄 CodeRabbit inference engine (.github/.coderabbit_review_guide.md)

**/*.{cu,cuh,cpp,hpp,h}: Track GPU device memory allocations and deallocations to prevent memory leaks; ensure cudaMalloc/cudaFree balance and cleanup of streams/events
Validate algorithm correctness in optimization logic: simplex pivots, branch-and-bound decisions, routing heuristics, and constraint/objective handling must produce correct results
Check numerical stability: prevent overflow/underflow, precision loss, division by zero/near-zero, and use epsilon comparisons for floating-point equality checks
Validate correct initialization of variable bounds, constraint coefficients, and algorithm state before solving; ensure reset when transitioning between algorithm phases (presolve, simplex, diving, crossover)
Ensure variables and constraints are accessed from the correct problem context (original vs presolve vs folded vs postsolve); verify index mapping consistency across problem transformations
For concurrent CUDA operations (barriers, async operations), explicitly create and manage dedicated streams instead of reusing the default stream; document stream lifecycle
Eliminate unnecessary host-device synchronization (cudaDeviceSynchronize) in hot paths that blocks GPU pipeline; use streams and events for async execution
Assess algorithmic complexity for large-scale problems (millions of variables/constraints); ensure O(n log n) or better complexity, not O(n²) or worse
Verify correct problem size checks before expensive GPU/CPU operations; prevent resource exhaustion on oversized problems
Identify assertions with overly strict numerical tolerances that fail on legitimate degenerate/edge cases (near-zero pivots, singular matrices, empty problems)
Ensure race conditions are absent in multi-GPU code and multi-threaded server implementations; verify proper synchronization of shared state
Refactor code duplication in solver components (3+ occurrences) into shared utilities; for GPU kernels, use templated device functions to avoid duplication
Check that hard-coded GPU de...

Files:

  • cpp/src/dual_simplex/basis_updates.hpp
  • cpp/src/dual_simplex/bounds_strengthening.cpp
  • cpp/src/dual_simplex/basis_solves.hpp
  • cpp/src/dual_simplex/logger.hpp
  • cpp/src/mip/diversity/recombiners/sub_mip.cuh
  • cpp/src/mip/diversity/lns/rins.cu
  • cpp/src/mip/solver.cu
  • cpp/src/dual_simplex/primal.cpp
  • cpp/src/dual_simplex/mip_node.hpp
  • cpp/src/dual_simplex/crossover.cpp
  • cpp/src/dual_simplex/basis_updates.cpp
  • cpp/src/dual_simplex/simplex_solver_settings.hpp
  • cpp/src/dual_simplex/pseudo_costs.hpp
  • cpp/src/dual_simplex/basis_solves.cpp
  • cpp/src/dual_simplex/diving_heuristics.hpp
  • cpp/src/dual_simplex/pseudo_costs.cpp
  • cpp/src/dual_simplex/phase2.cpp
  • cpp/src/dual_simplex/diving_heuristics.cpp
  • cpp/src/dual_simplex/node_queue.hpp
  • cpp/src/dual_simplex/branch_and_bound.hpp
  • cpp/src/dual_simplex/branch_and_bound.cpp
**/*.{h,hpp,py}

📄 CodeRabbit inference engine (.github/.coderabbit_review_guide.md)

Verify C API does not break ABI stability (no struct layout changes, field reordering); maintain backward compatibility in Python and server APIs with deprecation warnings

Files:

  • cpp/src/dual_simplex/basis_updates.hpp
  • cpp/src/dual_simplex/basis_solves.hpp
  • cpp/src/dual_simplex/logger.hpp
  • cpp/src/dual_simplex/mip_node.hpp
  • cpp/src/dual_simplex/simplex_solver_settings.hpp
  • cpp/src/dual_simplex/pseudo_costs.hpp
  • cpp/src/dual_simplex/diving_heuristics.hpp
  • cpp/src/dual_simplex/node_queue.hpp
  • cpp/src/dual_simplex/branch_and_bound.hpp
**/*.{cpp,hpp,h}

📄 CodeRabbit inference engine (.github/.coderabbit_review_guide.md)

**/*.{cpp,hpp,h}: Check for unclosed file handles when reading MPS/QPS problem files; ensure RAII patterns or proper cleanup in exception paths
Validate input sanitization to prevent buffer overflows and resource exhaustion attacks; avoid unsafe deserialization of problem files
Prevent thread-unsafe use of global and static variables; use proper mutex/synchronization in server code accessing shared solver state

Files:

  • cpp/src/dual_simplex/basis_updates.hpp
  • cpp/src/dual_simplex/bounds_strengthening.cpp
  • cpp/src/dual_simplex/basis_solves.hpp
  • cpp/src/dual_simplex/logger.hpp
  • cpp/src/dual_simplex/primal.cpp
  • cpp/src/dual_simplex/mip_node.hpp
  • cpp/src/dual_simplex/crossover.cpp
  • cpp/src/dual_simplex/basis_updates.cpp
  • cpp/src/dual_simplex/simplex_solver_settings.hpp
  • cpp/src/dual_simplex/pseudo_costs.hpp
  • cpp/src/dual_simplex/basis_solves.cpp
  • cpp/src/dual_simplex/diving_heuristics.hpp
  • cpp/src/dual_simplex/pseudo_costs.cpp
  • cpp/src/dual_simplex/phase2.cpp
  • cpp/src/dual_simplex/diving_heuristics.cpp
  • cpp/src/dual_simplex/node_queue.hpp
  • cpp/src/dual_simplex/branch_and_bound.hpp
  • cpp/src/dual_simplex/branch_and_bound.cpp
**/*.{cu,cpp,hpp,h}

📄 CodeRabbit inference engine (.github/.coderabbit_review_guide.md)

Avoid inappropriate use of exceptions in performance-critical GPU operation paths; prefer error codes or CUDA error checking for latency-sensitive code

Files:

  • cpp/src/dual_simplex/basis_updates.hpp
  • cpp/src/dual_simplex/bounds_strengthening.cpp
  • cpp/src/dual_simplex/basis_solves.hpp
  • cpp/src/dual_simplex/logger.hpp
  • cpp/src/mip/diversity/lns/rins.cu
  • cpp/src/mip/solver.cu
  • cpp/src/dual_simplex/primal.cpp
  • cpp/src/dual_simplex/mip_node.hpp
  • cpp/src/dual_simplex/crossover.cpp
  • cpp/src/dual_simplex/basis_updates.cpp
  • cpp/src/dual_simplex/simplex_solver_settings.hpp
  • cpp/src/dual_simplex/pseudo_costs.hpp
  • cpp/src/dual_simplex/basis_solves.cpp
  • cpp/src/dual_simplex/diving_heuristics.hpp
  • cpp/src/dual_simplex/pseudo_costs.cpp
  • cpp/src/dual_simplex/phase2.cpp
  • cpp/src/dual_simplex/diving_heuristics.cpp
  • cpp/src/dual_simplex/node_queue.hpp
  • cpp/src/dual_simplex/branch_and_bound.hpp
  • cpp/src/dual_simplex/branch_and_bound.cpp
**/*.{cu,cuh}

📄 CodeRabbit inference engine (.github/.coderabbit_review_guide.md)

**/*.{cu,cuh}: Every CUDA kernel launch and memory operation must have error checking with CUDA_CHECK or equivalent verification
Avoid reinventing functionality already available in Thrust, CCCL, or RMM libraries; prefer standard library utilities over custom implementations

Files:

  • cpp/src/mip/diversity/recombiners/sub_mip.cuh
  • cpp/src/mip/diversity/lns/rins.cu
  • cpp/src/mip/solver.cu
**/*.cu

📄 CodeRabbit inference engine (.github/.coderabbit_review_guide.md)

**/*.cu: Verify race conditions and correctness of GPU kernel shared memory, atomics, and warp-level operations
Detect inefficient GPU kernel launches with low occupancy or poor memory access patterns; optimize for coalesced memory access and minimize warp divergence in hot paths

Files:

  • cpp/src/mip/diversity/lns/rins.cu
  • cpp/src/mip/solver.cu
🧠 Learnings (18)
📓 Common learnings
Learnt from: CR
Repo: NVIDIA/cuopt PR: 0
File: .github/.coderabbit_review_guide.md:0-0
Timestamp: 2025-11-25T10:20:49.822Z
Learning: Reduce tight coupling between solver components (presolve, simplex, basis, barrier); increase modularity and reusability of optimization algorithms
Learnt from: CR
Repo: NVIDIA/cuopt PR: 0
File: .github/.coderabbit_review_guide.md:0-0
Timestamp: 2025-11-25T10:20:49.822Z
Learning: Applies to **/*.{cu,cuh,cpp,hpp,h} : Validate correct initialization of variable bounds, constraint coefficients, and algorithm state before solving; ensure reset when transitioning between algorithm phases (presolve, simplex, diving, crossover)
Learnt from: CR
Repo: NVIDIA/cuopt PR: 0
File: .github/.coderabbit_review_guide.md:0-0
Timestamp: 2025-11-25T10:20:49.822Z
Learning: Applies to **/*test*.{cpp,cu,py} : Add tests for algorithm phase transitions: verify correct initialization of bounds and state when transitioning from presolve to simplex to diving to crossover
Learnt from: CR
Repo: NVIDIA/cuopt PR: 0
File: .github/.coderabbit_review_guide.md:0-0
Timestamp: 2025-11-25T10:20:49.822Z
Learning: Applies to **/*.{cu,cuh,cpp,hpp,h} : Validate algorithm correctness in optimization logic: simplex pivots, branch-and-bound decisions, routing heuristics, and constraint/objective handling must produce correct results
📚 Learning: 2025-11-25T10:20:49.822Z
Learnt from: CR
Repo: NVIDIA/cuopt PR: 0
File: .github/.coderabbit_review_guide.md:0-0
Timestamp: 2025-11-25T10:20:49.822Z
Learning: Reduce tight coupling between solver components (presolve, simplex, basis, barrier); increase modularity and reusability of optimization algorithms

Applied to files:

  • cpp/src/dual_simplex/basis_updates.hpp
  • cpp/src/dual_simplex/bounds_strengthening.cpp
  • cpp/src/dual_simplex/basis_solves.hpp
  • cpp/src/mip/solver.cu
  • cpp/src/dual_simplex/primal.cpp
  • cpp/src/dual_simplex/crossover.cpp
  • cpp/src/dual_simplex/basis_updates.cpp
  • cpp/src/dual_simplex/simplex_solver_settings.hpp
  • cpp/src/dual_simplex/basis_solves.cpp
  • cpp/src/dual_simplex/phase2.cpp
  • cpp/src/dual_simplex/branch_and_bound.hpp
  • cpp/src/dual_simplex/branch_and_bound.cpp
📚 Learning: 2025-11-25T10:20:49.822Z
Learnt from: CR
Repo: NVIDIA/cuopt PR: 0
File: .github/.coderabbit_review_guide.md:0-0
Timestamp: 2025-11-25T10:20:49.822Z
Learning: Applies to **/*.{cu,cuh,cpp,hpp,h} : Validate correct initialization of variable bounds, constraint coefficients, and algorithm state before solving; ensure reset when transitioning between algorithm phases (presolve, simplex, diving, crossover)

Applied to files:

  • cpp/src/dual_simplex/basis_updates.hpp
  • cpp/src/dual_simplex/bounds_strengthening.cpp
  • cpp/src/dual_simplex/basis_solves.hpp
  • cpp/src/mip/diversity/recombiners/sub_mip.cuh
  • cpp/src/dual_simplex/CMakeLists.txt
  • cpp/src/mip/diversity/lns/rins.cu
  • cpp/src/mip/solver.cu
  • cpp/src/dual_simplex/primal.cpp
  • cpp/src/dual_simplex/mip_node.hpp
  • cpp/src/dual_simplex/crossover.cpp
  • cpp/src/dual_simplex/basis_updates.cpp
  • cpp/src/dual_simplex/simplex_solver_settings.hpp
  • cpp/src/dual_simplex/basis_solves.cpp
  • cpp/src/dual_simplex/diving_heuristics.hpp
  • cpp/src/dual_simplex/pseudo_costs.cpp
  • cpp/src/dual_simplex/phase2.cpp
  • cpp/src/dual_simplex/diving_heuristics.cpp
  • cpp/src/dual_simplex/branch_and_bound.hpp
  • cpp/src/dual_simplex/branch_and_bound.cpp
📚 Learning: 2025-11-25T10:20:49.822Z
Learnt from: CR
Repo: NVIDIA/cuopt PR: 0
File: .github/.coderabbit_review_guide.md:0-0
Timestamp: 2025-11-25T10:20:49.822Z
Learning: Applies to **/*.{cu,cuh,cpp,hpp,h} : Validate algorithm correctness in optimization logic: simplex pivots, branch-and-bound decisions, routing heuristics, and constraint/objective handling must produce correct results

Applied to files:

  • cpp/src/dual_simplex/basis_updates.hpp
  • cpp/src/dual_simplex/bounds_strengthening.cpp
  • cpp/src/dual_simplex/basis_solves.hpp
  • cpp/src/dual_simplex/CMakeLists.txt
  • cpp/src/mip/diversity/lns/rins.cu
  • cpp/src/mip/solver.cu
  • cpp/src/dual_simplex/primal.cpp
  • cpp/src/dual_simplex/crossover.cpp
  • cpp/src/dual_simplex/basis_updates.cpp
  • cpp/src/dual_simplex/simplex_solver_settings.hpp
  • cpp/src/dual_simplex/pseudo_costs.hpp
  • cpp/src/dual_simplex/basis_solves.cpp
  • cpp/src/dual_simplex/diving_heuristics.hpp
  • cpp/src/dual_simplex/pseudo_costs.cpp
  • cpp/src/dual_simplex/phase2.cpp
  • cpp/src/dual_simplex/diving_heuristics.cpp
  • cpp/src/dual_simplex/node_queue.hpp
  • cpp/src/dual_simplex/branch_and_bound.hpp
  • cpp/src/dual_simplex/branch_and_bound.cpp
📚 Learning: 2025-11-25T10:20:49.822Z
Learnt from: CR
Repo: NVIDIA/cuopt PR: 0
File: .github/.coderabbit_review_guide.md:0-0
Timestamp: 2025-11-25T10:20:49.822Z
Learning: Applies to **/*.{cu,cuh,cpp,hpp,h} : Ensure variables and constraints are accessed from the correct problem context (original vs presolve vs folded vs postsolve); verify index mapping consistency across problem transformations

Applied to files:

  • cpp/src/dual_simplex/bounds_strengthening.cpp
  • cpp/src/mip/solver.cu
  • cpp/src/dual_simplex/crossover.cpp
  • cpp/src/dual_simplex/basis_solves.cpp
  • cpp/src/dual_simplex/diving_heuristics.hpp
  • cpp/src/dual_simplex/pseudo_costs.cpp
  • cpp/src/dual_simplex/phase2.cpp
  • cpp/src/dual_simplex/diving_heuristics.cpp
  • cpp/src/dual_simplex/branch_and_bound.hpp
  • cpp/src/dual_simplex/branch_and_bound.cpp
📚 Learning: 2025-11-25T10:20:49.822Z
Learnt from: CR
Repo: NVIDIA/cuopt PR: 0
File: .github/.coderabbit_review_guide.md:0-0
Timestamp: 2025-11-25T10:20:49.822Z
Learning: Applies to **/*test*.{cpp,cu,py} : Add tests for algorithm phase transitions: verify correct initialization of bounds and state when transitioning from presolve to simplex to diving to crossover

Applied to files:

  • cpp/src/dual_simplex/bounds_strengthening.cpp
  • cpp/src/dual_simplex/CMakeLists.txt
  • cpp/src/mip/diversity/lns/rins.cu
  • cpp/src/mip/solver.cu
  • cpp/src/dual_simplex/primal.cpp
  • cpp/src/dual_simplex/crossover.cpp
  • cpp/src/dual_simplex/basis_updates.cpp
  • cpp/src/dual_simplex/simplex_solver_settings.hpp
  • cpp/src/dual_simplex/basis_solves.cpp
  • cpp/src/dual_simplex/diving_heuristics.hpp
  • cpp/src/dual_simplex/phase2.cpp
  • cpp/src/dual_simplex/diving_heuristics.cpp
  • cpp/src/dual_simplex/branch_and_bound.hpp
  • cpp/src/dual_simplex/branch_and_bound.cpp
📚 Learning: 2025-11-25T10:20:49.822Z
Learnt from: CR
Repo: NVIDIA/cuopt PR: 0
File: .github/.coderabbit_review_guide.md:0-0
Timestamp: 2025-11-25T10:20:49.822Z
Learning: Applies to **/*.{cu,cuh,cpp,hpp,h} : Identify assertions with overly strict numerical tolerances that fail on legitimate degenerate/edge cases (near-zero pivots, singular matrices, empty problems)

Applied to files:

  • cpp/src/dual_simplex/bounds_strengthening.cpp
  • cpp/src/dual_simplex/basis_solves.cpp
📚 Learning: 2025-11-25T10:20:49.822Z
Learnt from: CR
Repo: NVIDIA/cuopt PR: 0
File: .github/.coderabbit_review_guide.md:0-0
Timestamp: 2025-11-25T10:20:49.822Z
Learning: Applies to **/*test*.{cpp,cu,py} : Test with free variables, singleton problems, and extreme problem dimensions near resource limits to validate edge case handling

Applied to files:

  • cpp/src/dual_simplex/bounds_strengthening.cpp
  • cpp/src/dual_simplex/CMakeLists.txt
📚 Learning: 2025-11-25T10:20:49.822Z
Learnt from: CR
Repo: NVIDIA/cuopt PR: 0
File: .github/.coderabbit_review_guide.md:0-0
Timestamp: 2025-11-25T10:20:49.822Z
Learning: Applies to **/*.{cu,cuh,cpp,hpp,h} : Assess algorithmic complexity for large-scale problems (millions of variables/constraints); ensure O(n log n) or better complexity, not O(n²) or worse

Applied to files:

  • cpp/src/dual_simplex/bounds_strengthening.cpp
  • cpp/src/dual_simplex/branch_and_bound.cpp
📚 Learning: 2025-11-25T10:20:49.822Z
Learnt from: CR
Repo: NVIDIA/cuopt PR: 0
File: .github/.coderabbit_review_guide.md:0-0
Timestamp: 2025-11-25T10:20:49.822Z
Learning: Applies to **/*.{cu,cuh,cpp,hpp,h} : Refactor code duplication in solver components (3+ occurrences) into shared utilities; for GPU kernels, use templated device functions to avoid duplication

Applied to files:

  • cpp/src/dual_simplex/CMakeLists.txt
  • cpp/src/mip/diversity/lns/rins.cu
  • cpp/src/mip/solver.cu
  • cpp/src/dual_simplex/simplex_solver_settings.hpp
  • cpp/src/dual_simplex/diving_heuristics.hpp
  • cpp/src/dual_simplex/diving_heuristics.cpp
  • cpp/src/dual_simplex/node_queue.hpp
  • cpp/src/dual_simplex/branch_and_bound.hpp
  • cpp/src/dual_simplex/branch_and_bound.cpp
📚 Learning: 2025-11-25T10:20:49.822Z
Learnt from: CR
Repo: NVIDIA/cuopt PR: 0
File: .github/.coderabbit_review_guide.md:0-0
Timestamp: 2025-11-25T10:20:49.822Z
Learning: Applies to **/*.{cu,cuh} : Avoid reinventing functionality already available in Thrust, CCCL, or RMM libraries; prefer standard library utilities over custom implementations

Applied to files:

  • cpp/src/dual_simplex/CMakeLists.txt
📚 Learning: 2025-11-25T10:20:49.822Z
Learnt from: CR
Repo: NVIDIA/cuopt PR: 0
File: .github/.coderabbit_review_guide.md:0-0
Timestamp: 2025-11-25T10:20:49.822Z
Learning: Applies to **/*.{cu,cuh,cpp,hpp,h} : Check that hard-coded GPU device IDs and resource limits are made configurable; abstract multi-backend support for different CUDA versions

Applied to files:

  • cpp/src/dual_simplex/CMakeLists.txt
  • cpp/src/mip/diversity/lns/rins.cu
  • cpp/src/mip/solver.cu
📚 Learning: 2025-11-25T10:20:49.822Z
Learnt from: CR
Repo: NVIDIA/cuopt PR: 0
File: .github/.coderabbit_review_guide.md:0-0
Timestamp: 2025-11-25T10:20:49.822Z
Learning: Applies to **/*.{cu,cuh,cpp,hpp,h} : Verify correct problem size checks before expensive GPU/CPU operations; prevent resource exhaustion on oversized problems

Applied to files:

  • cpp/src/dual_simplex/CMakeLists.txt
📚 Learning: 2025-11-25T10:20:49.822Z
Learnt from: CR
Repo: NVIDIA/cuopt PR: 0
File: .github/.coderabbit_review_guide.md:0-0
Timestamp: 2025-11-25T10:20:49.822Z
Learning: Applies to **/*.{cu,cuh,cpp,hpp,h} : Track GPU device memory allocations and deallocations to prevent memory leaks; ensure cudaMalloc/cudaFree balance and cleanup of streams/events

Applied to files:

  • cpp/src/dual_simplex/CMakeLists.txt
📚 Learning: 2025-11-25T10:20:49.822Z
Learnt from: CR
Repo: NVIDIA/cuopt PR: 0
File: .github/.coderabbit_review_guide.md:0-0
Timestamp: 2025-11-25T10:20:49.822Z
Learning: Applies to **/*benchmark*.{cpp,cu,py} : Include performance benchmarks and regression detection for GPU operations; verify near real-time performance on million-variable problems

Applied to files:

  • cpp/src/dual_simplex/CMakeLists.txt
📚 Learning: 2025-12-04T20:09:09.264Z
Learnt from: chris-maes
Repo: NVIDIA/cuopt PR: 602
File: cpp/src/linear_programming/solve.cu:732-742
Timestamp: 2025-12-04T20:09:09.264Z
Learning: In cpp/src/linear_programming/solve.cu, the barrier solver does not currently return INFEASIBLE or UNBOUNDED status. It only returns OPTIMAL, TIME_LIMIT, NUMERICAL_ISSUES, or CONCURRENT_LIMIT.

Applied to files:

  • cpp/src/mip/solver.cu
  • cpp/src/dual_simplex/diving_heuristics.hpp
  • cpp/src/dual_simplex/phase2.cpp
  • cpp/src/dual_simplex/diving_heuristics.cpp
  • cpp/src/dual_simplex/branch_and_bound.hpp
  • cpp/src/dual_simplex/branch_and_bound.cpp
📚 Learning: 2025-11-25T10:20:49.822Z
Learnt from: CR
Repo: NVIDIA/cuopt PR: 0
File: .github/.coderabbit_review_guide.md:0-0
Timestamp: 2025-11-25T10:20:49.822Z
Learning: Applies to **/*.{cpp,hpp,h} : Prevent thread-unsafe use of global and static variables; use proper mutex/synchronization in server code accessing shared solver state

Applied to files:

  • cpp/src/mip/solver.cu
  • cpp/src/dual_simplex/pseudo_costs.cpp
📚 Learning: 2025-12-04T04:11:12.640Z
Learnt from: chris-maes
Repo: NVIDIA/cuopt PR: 500
File: cpp/src/dual_simplex/scaling.cpp:68-76
Timestamp: 2025-12-04T04:11:12.640Z
Learning: In the cuOPT dual simplex solver, CSR/CSC matrices (including the quadratic objective matrix Q) are required to have valid dimensions and indices by construction. Runtime bounds checking in performance-critical paths like matrix scaling is avoided to prevent slowdowns. Validation is performed via debug-only check_matrix() calls wrapped in #ifdef CHECK_MATRIX.

Applied to files:

  • cpp/src/dual_simplex/basis_solves.cpp
  • cpp/src/dual_simplex/branch_and_bound.hpp
🧬 Code graph analysis (11)
cpp/src/dual_simplex/basis_updates.hpp (1)
cpp/src/dual_simplex/presolve.cpp (3)
  • lower (240-240)
  • upper (85-85)
  • upper (241-241)
cpp/src/dual_simplex/logger.hpp (1)
cpp/src/dual_simplex/simplex_solver_settings.hpp (2)
  • log_filename (99-99)
  • log_filename (99-99)
cpp/src/dual_simplex/primal.cpp (2)
cpp/src/dual_simplex/basis_solves.cpp (3)
  • basis_repair (614-675)
  • basis_repair (614-622)
  • basis_repair (860-868)
cpp/src/dual_simplex/basis_solves.hpp (1)
  • basis_repair (43-51)
cpp/src/dual_simplex/crossover.cpp (2)
cpp/src/dual_simplex/basis_solves.cpp (3)
  • basis_repair (614-675)
  • basis_repair (614-622)
  • basis_repair (860-868)
cpp/src/dual_simplex/basis_solves.hpp (1)
  • basis_repair (43-51)
cpp/src/dual_simplex/basis_updates.cpp (3)
cpp/src/dual_simplex/basis_solves.cpp (3)
  • basis_repair (614-675)
  • basis_repair (614-622)
  • basis_repair (860-868)
cpp/src/dual_simplex/basis_solves.hpp (1)
  • basis_repair (43-51)
cpp/src/dual_simplex/basis_updates.hpp (1)
  • A (374-380)
cpp/src/dual_simplex/basis_solves.cpp (1)
cpp/src/dual_simplex/presolve.cpp (3)
  • lower (240-240)
  • upper (85-85)
  • upper (241-241)
cpp/src/dual_simplex/diving_heuristics.hpp (2)
cpp/src/dual_simplex/pseudo_costs.hpp (2)
  • fractional (46-49)
  • fractional (51-52)
cpp/src/dual_simplex/simplex_solver_settings.hpp (2)
  • log (98-98)
  • log (100-100)
cpp/src/dual_simplex/pseudo_costs.cpp (1)
cpp/src/dual_simplex/pseudo_costs.hpp (2)
  • fractional (46-49)
  • fractional (51-52)
cpp/src/dual_simplex/phase2.cpp (1)
cpp/src/dual_simplex/primal.cpp (2)
  • primal_infeasibility (202-240)
  • primal_infeasibility (202-205)
cpp/src/dual_simplex/node_queue.hpp (1)
cpp/src/dual_simplex/mip_node.hpp (4)
  • lower (97-108)
  • lower (97-99)
  • lower (112-130)
  • lower (112-114)
cpp/src/dual_simplex/branch_and_bound.hpp (1)
cpp/src/dual_simplex/node_queue.hpp (4)
  • node (21-25)
  • node (21-21)
  • node (27-31)
  • node (27-27)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: wheel-build-cuopt-mps-parser / 13.0.2, 3.11, arm64, rockylinux8
  • GitHub Check: wheel-build-cuopt-mps-parser / 13.0.2, 3.13, arm64, rockylinux8
  • GitHub Check: wheel-build-cuopt-mps-parser / 13.0.2, 3.10, amd64, rockylinux8
  • GitHub Check: wheel-build-cuopt-mps-parser / 13.0.2, 3.13, amd64, rockylinux8
  • GitHub Check: wheel-build-cuopt-mps-parser / 13.0.2, 3.11, amd64, rockylinux8
  • GitHub Check: wheel-build-cuopt-sh-client / 13.0.2, 3.10, amd64, rockylinux8
  • GitHub Check: checks / check-style
🔇 Additional comments (39)
cpp/src/dual_simplex/CMakeLists.txt (1)

34-34: LGTM!

The addition of diving_heuristics.cpp to the build aligns with the PR objectives to introduce new diving heuristic strategies.

cpp/src/dual_simplex/bounds_strengthening.cpp (2)

157-164: Logging level correctly adjusted to debug.

The change from printf to settings.log.debug() appropriately reduces stdout noise while preserving diagnostic information for debugging. This is proper logging hygiene for infeasibility detection during bounds strengthening iterations.


214-216: Logging level correctly adjusted to debug.

Consistent with the previous logging change, this infeasibility message is appropriately moved to debug level logging.

cpp/src/dual_simplex/primal.cpp (1)

301-309: All basis_repair call sites have been consistently updated with bound parameters.

The call in primal.cpp at lines 301-309 correctly passes lp.lower and lp.upper as the 3rd and 4th parameters. Verification confirms this pattern is consistent across all five basis_repair call sites in the codebase:

  • primal.cpp:301 - passes lp.lower, lp.upper
  • crossover.cpp:788, 1142, 1341 - all pass lp.lower, lp.upper
  • basis_updates.cpp:2071 - passes lower, upper (local variables in templated context)

This ensures bounds-aware basis repair during algorithm phase transitions (simplex, crossover phases).

cpp/src/mip/solver.cu (1)

176-180: Threading configuration correctly refactored for new diving_settings structure.

The code properly updates the thread distribution logic to use the new diving_settings.num_diving_tasks field instead of the previous num_diving_threads. The thread allocation strategy remains unchanged (BFS threads = num_threads/4, diving tasks = remaining threads).

cpp/src/dual_simplex/pseudo_costs.hpp (1)

46-49: API change correctly expands pseudo-cost functionality.

The function signature update from variable_selection to variable_selection_and_obj_estimate properly reflects the expanded responsibility: returning both the selected variable index and an objective estimate. The addition of the lower_bound parameter enables more sophisticated objective estimation for diving heuristics.

All call sites have been correctly updated. The old variable_selection function is preserved as a backward-compatibility wrapper that properly unpacks the returned pair via std::tie, stores the objective estimate in node state for search tree ordering, and returns the branch decision. New call sites directly use variable_selection_and_obj_estimate with proper structured binding to capture both returned elements.

cpp/src/dual_simplex/logger.hpp (1)

33-44: LGTM! Backward-compatible API refinement.

The change from std::string to const char* for the mode parameter avoids unnecessary string construction when passing literals. The default value "w" ensures backward compatibility with existing callers like set_log_filename that don't specify a mode.

cpp/src/dual_simplex/basis_solves.hpp (1)

42-51: LGTM! Bound-aware basis repair signature.

The addition of lower and upper bound vectors enables basis_repair to correctly assign variable status (NONBASIC_FREE, NONBASIC_LOWER, or NONBASIC_UPPER) for variables being removed from the basis. This is essential for maintaining dual feasibility during repair operations. Based on coding guidelines, this correctly validates variable bounds during algorithm state transitions.

cpp/src/dual_simplex/crossover.cpp (3)

788-796: LGTM! Correct bound propagation to basis repair.

The lp.lower and lp.upper bounds are correctly passed to basis_repair, enabling proper variable status assignment during the primal push refactoring phase.


1142-1150: LGTM! Consistent bound handling in crossover initialization.

Bounds are correctly propagated during the initial basis repair in the crossover function, consistent with the other call sites.


1341-1349: LGTM! Bounds passed after phase 1 completion.

The third call site correctly passes bounds after phase 1 completes, maintaining consistency across all basis_repair invocations in the crossover workflow.

cpp/src/dual_simplex/basis_updates.cpp (2)

2046-2054: LGTM! Signature update matches declaration.

The refactor_basis implementation correctly accepts the new lower and upper bound parameters, consistent with the header declaration in basis_updates.hpp.


2071-2072: LGTM! Bounds correctly forwarded to basis_repair.

When initial factorization fails, the bounds are properly passed to basis_repair to enable correct variable status assignment during the repair process. Based on coding guidelines, this ensures correct initialization of variable bounds when transitioning between algorithm phases.

cpp/src/dual_simplex/basis_updates.hpp (1)

374-380: Verify all callers of refactor_basis have been updated to pass the new bound parameters.

The refactor_basis declaration correctly adds lower and upper bound vectors as const references. All callers in phase2.cpp (lines 2248, 2910, 2917) have been properly updated to pass lp.lower and lp.upper. This is a breaking API change requiring all callers to be updated.

cpp/src/dual_simplex/simplex_solver_settings.hpp (1)

22-34: Well-structured diving configuration.

The new diving_heuristics_settings_t struct cleanly encapsulates diving-related parameters with sensible defaults. The separation of heuristic enable/disable flags and resource limits (node_limit, iteration_limit_factor, backtrack) follows good configuration design practices.

cpp/src/dual_simplex/pseudo_costs.cpp (2)

202-202: Good improvement: RAII-based locking.

Using std::lock_guard<omp_mutex_t> ensures the mutex is properly released even if an exception occurs or the function returns early. This is a safer pattern than manual lock/unlock.


256-321: Correct implementation of pseudocost-based objective estimate.

The function correctly:

  1. Uses RAII locking for thread safety
  2. Computes per-variable pseudocosts with fallback to averages for uninitialized values
  3. Applies the reliability branching score formula (product of down/up estimates)
  4. Accumulates a conservative objective estimate using the minimum of up/down contributions

The algorithm aligns with standard MIP solver literature (Achterberg Sect. 6.4).

cpp/src/dual_simplex/diving_heuristics.hpp (1)

17-47: Clean diving heuristics API design.

The header provides a well-organized interface for the four diving strategies from Achterberg's thesis:

  • Line search diving (Sect. 9.2.4)
  • Pseudocost diving (Sect. 9.2.5)
  • Guided diving (Sect. 9.2.3)
  • Coefficient diving (Sect. 9.2.1)

The branch_variable_t return type cleanly bundles the selected variable with its rounding direction.

cpp/src/dual_simplex/diving_heuristics.cpp (3)

66-139: Correct pseudocost diving implementation with thread safety.

The implementation correctly:

  1. Acquires mutex lock before accessing shared pseudocost data
  2. Uses fallback averages for uninitialized pseudocosts
  3. Applies the scoring formula from Achterberg Sect. 9.2.5
  4. Uses threshold-based direction selection (0.4 for root distance, 0.3/0.7 for fractionality)

The magic constants are standard heuristic values from MIP literature.


141-197: Correct guided diving implementation.

The function properly implements Achterberg's guided diving (Sect. 9.2.3):

  • Direction determined by proximity to incumbent solution
  • Score weighted 5:1 in favor of the preferred direction
  • Thread-safe pseudocost access

Note the same empty fractional edge case applies here.


229-273: Correct coefficient diving implementation.

The function implements Achterberg's coefficient diving (Sect. 9.2.1):

  • Selects variable with minimum constraint locks
  • Prefers rounding direction that creates fewer constraint violations
  • Uses fractionality as tie-breaker when locks are equal

The same empty fractional edge case consideration applies.

cpp/src/dual_simplex/basis_solves.cpp (1)

613-622: Signature extension for bound-aware basis repair is complete and correctly integrated.

The addition of lower and upper bound vectors enables correct variable status assignment during basis repair. All call sites have been updated to pass the bound vectors:

  • primal.cpp (1 call)
  • crossover.cpp (3 calls)
  • basis_updates.cpp (1 call)

All invocations properly pass lp.lower and lp.upper (or local lower/upper from the calling context). The signature change aligns with the PR's goal of propagating bound information through solver phases.

cpp/src/dual_simplex/node_queue.hpp (1)

60-101: Well-structured dual-heap design for unified node scheduling.

The node_queue_t class effectively unifies best-first and diving heaps with shared entries, aligning with the PR objective to reduce memory consumption. The use of shared_ptr<heap_entry_t> for cross-heap entry sharing is a clean approach.

cpp/src/dual_simplex/phase2.cpp (3)

620-651: Signature change correctly separates squared and linear infeasibility.

The updated compute_initial_primal_infeasibilities now properly computes both forms:

  • Returns primal_inf_squared (sum of squared infeasibilities)
  • Outputs primal_inf (sum of linear infeasibilities) via reference

This distinction is important for different algorithmic needs (squared for steepest-edge pricing weights, linear for feasibility checks). The initialization of squared_infeasibilities vector at lines 631-632 ensures clean state.


2365-2373: Proper initialization of infeasibility tracking variables.

The new code correctly initializes primal_infeasibility and primal_infeasibility_squared separately, with compute_initial_primal_infeasibilities populating both values. This ensures clean state before the main iteration loop.


2248-2251: All refactor_basis call sites are consistent with the function signature.

Verification confirms that all three call sites in phase2.cpp (lines 2248, 2910–2911, and 2917–2918) correctly include the lp.lower and lp.upper bound parameters matching the function signature in basis_updates.hpp and basis_updates.cpp.

cpp/src/dual_simplex/branch_and_bound.hpp (4)

56-67: Clear documentation and well-structured enum for diving strategies.

The bnb_thread_type_t enum provides clear categorization with references to the Achterberg dissertation sections. This aligns well with the PR objective of implementing multiple diving heuristics.


75-86: Thread-safe statistics structure with atomic counters.

The bnb_stats_t struct appropriately uses omp_atomic_t for counters that may be updated from multiple threads. The last_log and nodes_since_last_log members with the comment about main-thread-only usage help clarify the intended access pattern.


182-184: Unified node queue replaces separate heap structures.

The replacement of the previous heap-based structures with node_queue_t aligns with the PR objective to "unify diving and best-first heaps into a single node_queue object." This should help reduce memory consumption as mentioned in the PR description.


261-266: variable_selection return type and parameters are well-designed.

Returning branch_variable_t<i_t> which includes both the variable index and rounding direction is cleaner than having multiple output parameters. The additional logger_t& log parameter enables per-call logging control.

cpp/src/dual_simplex/branch_and_bound.cpp (9)

558-577: Martin's criteria implementation for rounding direction.

The martin_criteria function correctly implements the rounding heuristic based on distance from the root relaxation value. The epsilon tolerance (1e-6) for tie-breaking is reasonable.


579-619: Clean dispatch pattern for variable selection strategies.

The variable_selection method uses a clear switch statement to dispatch to different diving heuristics. The objective estimate calculation is correctly scoped to only the EXPLORATION case where it's needed for heap ordering.


640-643: Appropriate fallback from GUIDED_DIVING to PSEUDOCOST_DIVING.

When there's no incumbent solution (upper_bound == inf), guided diving cannot function since it relies on the incumbent for guidance. Falling back to pseudocost diving is a sensible default.


656-661: Iteration budget enforcement for diving threads.

The iteration limit for diving threads is computed as a factor of total B&B LP iterations minus iterations already consumed by this dive. This prevents diving from consuming too many iterations.

However, if stats.total_lp_iters is close to max_iter, this could return ITERATION_LIMIT very early. Verify that the iteration_limit_factor setting provides sufficient budget for meaningful dives.


1143-1219: Deep diving implementation with backtrack limit.

The dive_from method implements the diving heuristics with proper backtracking control. Key observations:

  1. Lines 1163-1166: Local dive_stats properly tracks per-dive statistics
  2. Lines 1179-1180: Time and node limits checked within the dive loop
  3. Lines 1215-1218: Backtrack limit enforces shallow backtracking by removing nodes from the back of the stack when depth difference exceeds the limit

The algorithm correctly prioritizes depth-first exploration while limiting the breadth of backtracking.


1248-1255: Bounds propagation for diving thread efficiency.

The code correctly retrieves variable bounds from the node and applies bounds strengthening before diving. The reset_starting_bounds flag ensures bounds are properly reset when starting from a new node.


1518-1535: Diving strategy configuration is flexible and well-structured.

The diving strategies are populated based on settings, allowing users to disable specific strategies. The reserve(4) call is appropriate given the four possible strategies.


251-260: Improved lower bound computation uses node_queue.

The get_lower_bound method correctly combines:

  1. lower_bound_ceiling_ (for numerical issue tracking)
  2. node_queue.get_lower_bound() (heap minimum)
  3. local_lower_bounds_ (per-thread tracking)

This provides an accurate global lower bound across all active exploration paths.


1354-1354: Row-major matrix initialization for bounds strengthening.

Converting the constraint matrix to row-major format (Arow_) enables efficient row-based access during bounds strengthening. This is initialized once at the start of solve(), avoiding repeated conversions.

Copy link

@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: 1

♻️ Duplicate comments (1)
cpp/src/dual_simplex/diving_heuristics.cpp (1)

244-244: Type mismatch: locks should be i_t, not f_t.

The variable locks stores the minimum of two i_t values but is declared as f_t. This causes an unnecessary type conversion.

-    f_t locks                 = std::min(up_lock, down_lock);
+    i_t locks                 = std::min(up_lock, down_lock);
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 501c20d and 0ee757a.

📒 Files selected for processing (4)
  • cpp/src/dual_simplex/branch_and_bound.cpp (30 hunks)
  • cpp/src/dual_simplex/diving_heuristics.cpp (1 hunks)
  • cpp/src/dual_simplex/mip_node.hpp (4 hunks)
  • cpp/src/dual_simplex/simplex_solver_settings.hpp (3 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{cu,cuh,cpp,hpp,h}

📄 CodeRabbit inference engine (.github/.coderabbit_review_guide.md)

**/*.{cu,cuh,cpp,hpp,h}: Track GPU device memory allocations and deallocations to prevent memory leaks; ensure cudaMalloc/cudaFree balance and cleanup of streams/events
Validate algorithm correctness in optimization logic: simplex pivots, branch-and-bound decisions, routing heuristics, and constraint/objective handling must produce correct results
Check numerical stability: prevent overflow/underflow, precision loss, division by zero/near-zero, and use epsilon comparisons for floating-point equality checks
Validate correct initialization of variable bounds, constraint coefficients, and algorithm state before solving; ensure reset when transitioning between algorithm phases (presolve, simplex, diving, crossover)
Ensure variables and constraints are accessed from the correct problem context (original vs presolve vs folded vs postsolve); verify index mapping consistency across problem transformations
For concurrent CUDA operations (barriers, async operations), explicitly create and manage dedicated streams instead of reusing the default stream; document stream lifecycle
Eliminate unnecessary host-device synchronization (cudaDeviceSynchronize) in hot paths that blocks GPU pipeline; use streams and events for async execution
Assess algorithmic complexity for large-scale problems (millions of variables/constraints); ensure O(n log n) or better complexity, not O(n²) or worse
Verify correct problem size checks before expensive GPU/CPU operations; prevent resource exhaustion on oversized problems
Identify assertions with overly strict numerical tolerances that fail on legitimate degenerate/edge cases (near-zero pivots, singular matrices, empty problems)
Ensure race conditions are absent in multi-GPU code and multi-threaded server implementations; verify proper synchronization of shared state
Refactor code duplication in solver components (3+ occurrences) into shared utilities; for GPU kernels, use templated device functions to avoid duplication
Check that hard-coded GPU de...

Files:

  • cpp/src/dual_simplex/mip_node.hpp
  • cpp/src/dual_simplex/simplex_solver_settings.hpp
  • cpp/src/dual_simplex/diving_heuristics.cpp
  • cpp/src/dual_simplex/branch_and_bound.cpp
**/*.{h,hpp,py}

📄 CodeRabbit inference engine (.github/.coderabbit_review_guide.md)

Verify C API does not break ABI stability (no struct layout changes, field reordering); maintain backward compatibility in Python and server APIs with deprecation warnings

Files:

  • cpp/src/dual_simplex/mip_node.hpp
  • cpp/src/dual_simplex/simplex_solver_settings.hpp
**/*.{cpp,hpp,h}

📄 CodeRabbit inference engine (.github/.coderabbit_review_guide.md)

**/*.{cpp,hpp,h}: Check for unclosed file handles when reading MPS/QPS problem files; ensure RAII patterns or proper cleanup in exception paths
Validate input sanitization to prevent buffer overflows and resource exhaustion attacks; avoid unsafe deserialization of problem files
Prevent thread-unsafe use of global and static variables; use proper mutex/synchronization in server code accessing shared solver state

Files:

  • cpp/src/dual_simplex/mip_node.hpp
  • cpp/src/dual_simplex/simplex_solver_settings.hpp
  • cpp/src/dual_simplex/diving_heuristics.cpp
  • cpp/src/dual_simplex/branch_and_bound.cpp
**/*.{cu,cpp,hpp,h}

📄 CodeRabbit inference engine (.github/.coderabbit_review_guide.md)

Avoid inappropriate use of exceptions in performance-critical GPU operation paths; prefer error codes or CUDA error checking for latency-sensitive code

Files:

  • cpp/src/dual_simplex/mip_node.hpp
  • cpp/src/dual_simplex/simplex_solver_settings.hpp
  • cpp/src/dual_simplex/diving_heuristics.cpp
  • cpp/src/dual_simplex/branch_and_bound.cpp
🧠 Learnings (12)
📓 Common learnings
Learnt from: CR
Repo: NVIDIA/cuopt PR: 0
File: .github/.coderabbit_review_guide.md:0-0
Timestamp: 2025-11-25T10:20:49.822Z
Learning: Applies to **/*.{cu,cuh,cpp,hpp,h} : Validate algorithm correctness in optimization logic: simplex pivots, branch-and-bound decisions, routing heuristics, and constraint/objective handling must produce correct results
📚 Learning: 2025-11-25T10:20:49.822Z
Learnt from: CR
Repo: NVIDIA/cuopt PR: 0
File: .github/.coderabbit_review_guide.md:0-0
Timestamp: 2025-11-25T10:20:49.822Z
Learning: Applies to **/*.{cu,cuh,cpp,hpp,h} : Validate algorithm correctness in optimization logic: simplex pivots, branch-and-bound decisions, routing heuristics, and constraint/objective handling must produce correct results

Applied to files:

  • cpp/src/dual_simplex/mip_node.hpp
  • cpp/src/dual_simplex/simplex_solver_settings.hpp
  • cpp/src/dual_simplex/diving_heuristics.cpp
  • cpp/src/dual_simplex/branch_and_bound.cpp
📚 Learning: 2025-11-25T10:20:49.822Z
Learnt from: CR
Repo: NVIDIA/cuopt PR: 0
File: .github/.coderabbit_review_guide.md:0-0
Timestamp: 2025-11-25T10:20:49.822Z
Learning: Applies to **/*.{cu,cuh,cpp,hpp,h} : Validate correct initialization of variable bounds, constraint coefficients, and algorithm state before solving; ensure reset when transitioning between algorithm phases (presolve, simplex, diving, crossover)

Applied to files:

  • cpp/src/dual_simplex/mip_node.hpp
  • cpp/src/dual_simplex/simplex_solver_settings.hpp
  • cpp/src/dual_simplex/diving_heuristics.cpp
  • cpp/src/dual_simplex/branch_and_bound.cpp
📚 Learning: 2025-11-25T10:20:49.822Z
Learnt from: CR
Repo: NVIDIA/cuopt PR: 0
File: .github/.coderabbit_review_guide.md:0-0
Timestamp: 2025-11-25T10:20:49.822Z
Learning: Applies to **/*.{cu,cuh,cpp,hpp,h} : Identify assertions with overly strict numerical tolerances that fail on legitimate degenerate/edge cases (near-zero pivots, singular matrices, empty problems)

Applied to files:

  • cpp/src/dual_simplex/mip_node.hpp
📚 Learning: 2025-11-25T10:20:49.822Z
Learnt from: CR
Repo: NVIDIA/cuopt PR: 0
File: .github/.coderabbit_review_guide.md:0-0
Timestamp: 2025-11-25T10:20:49.822Z
Learning: Applies to **/*.{cu,cuh,cpp,hpp,h} : Check numerical stability: prevent overflow/underflow, precision loss, division by zero/near-zero, and use epsilon comparisons for floating-point equality checks

Applied to files:

  • cpp/src/dual_simplex/mip_node.hpp
📚 Learning: 2025-11-25T10:20:49.822Z
Learnt from: CR
Repo: NVIDIA/cuopt PR: 0
File: .github/.coderabbit_review_guide.md:0-0
Timestamp: 2025-11-25T10:20:49.822Z
Learning: Applies to **/*.{cu,cuh,cpp,hpp,h} : Ensure variables and constraints are accessed from the correct problem context (original vs presolve vs folded vs postsolve); verify index mapping consistency across problem transformations

Applied to files:

  • cpp/src/dual_simplex/mip_node.hpp
  • cpp/src/dual_simplex/diving_heuristics.cpp
  • cpp/src/dual_simplex/branch_and_bound.cpp
📚 Learning: 2025-11-25T10:20:49.822Z
Learnt from: CR
Repo: NVIDIA/cuopt PR: 0
File: .github/.coderabbit_review_guide.md:0-0
Timestamp: 2025-11-25T10:20:49.822Z
Learning: Applies to **/*.{cu,cuh,cpp,hpp,h} : Refactor code duplication in solver components (3+ occurrences) into shared utilities; for GPU kernels, use templated device functions to avoid duplication

Applied to files:

  • cpp/src/dual_simplex/simplex_solver_settings.hpp
  • cpp/src/dual_simplex/diving_heuristics.cpp
  • cpp/src/dual_simplex/branch_and_bound.cpp
📚 Learning: 2025-11-25T10:20:49.822Z
Learnt from: CR
Repo: NVIDIA/cuopt PR: 0
File: .github/.coderabbit_review_guide.md:0-0
Timestamp: 2025-11-25T10:20:49.822Z
Learning: Applies to **/*test*.{cpp,cu,py} : Add tests for algorithm phase transitions: verify correct initialization of bounds and state when transitioning from presolve to simplex to diving to crossover

Applied to files:

  • cpp/src/dual_simplex/simplex_solver_settings.hpp
  • cpp/src/dual_simplex/diving_heuristics.cpp
  • cpp/src/dual_simplex/branch_and_bound.cpp
📚 Learning: 2025-11-25T10:20:49.822Z
Learnt from: CR
Repo: NVIDIA/cuopt PR: 0
File: .github/.coderabbit_review_guide.md:0-0
Timestamp: 2025-11-25T10:20:49.822Z
Learning: Reduce tight coupling between solver components (presolve, simplex, basis, barrier); increase modularity and reusability of optimization algorithms

Applied to files:

  • cpp/src/dual_simplex/simplex_solver_settings.hpp
  • cpp/src/dual_simplex/branch_and_bound.cpp
📚 Learning: 2025-11-25T10:20:49.822Z
Learnt from: CR
Repo: NVIDIA/cuopt PR: 0
File: .github/.coderabbit_review_guide.md:0-0
Timestamp: 2025-11-25T10:20:49.822Z
Learning: Applies to **/*.{cu,cuh,cpp,hpp,h} : Verify correct problem size checks before expensive GPU/CPU operations; prevent resource exhaustion on oversized problems

Applied to files:

  • cpp/src/dual_simplex/simplex_solver_settings.hpp
📚 Learning: 2025-11-25T10:20:49.822Z
Learnt from: CR
Repo: NVIDIA/cuopt PR: 0
File: .github/.coderabbit_review_guide.md:0-0
Timestamp: 2025-11-25T10:20:49.822Z
Learning: Applies to **/*.{cu,cuh,cpp,hpp,h} : Assess algorithmic complexity for large-scale problems (millions of variables/constraints); ensure O(n log n) or better complexity, not O(n²) or worse

Applied to files:

  • cpp/src/dual_simplex/diving_heuristics.cpp
  • cpp/src/dual_simplex/branch_and_bound.cpp
📚 Learning: 2025-12-04T20:09:09.264Z
Learnt from: chris-maes
Repo: NVIDIA/cuopt PR: 602
File: cpp/src/linear_programming/solve.cu:732-742
Timestamp: 2025-12-04T20:09:09.264Z
Learning: In cpp/src/linear_programming/solve.cu, the barrier solver does not currently return INFEASIBLE or UNBOUNDED status. It only returns OPTIMAL, TIME_LIMIT, NUMERICAL_ISSUES, or CONCURRENT_LIMIT.

Applied to files:

  • cpp/src/dual_simplex/diving_heuristics.cpp
  • cpp/src/dual_simplex/branch_and_bound.cpp
🧬 Code graph analysis (1)
cpp/src/dual_simplex/branch_and_bound.cpp (3)
cpp/src/dual_simplex/node_queue.hpp (11)
  • buffer (40-48)
  • buffer (50-50)
  • buffer (51-51)
  • buffer (52-52)
  • buffer (53-53)
  • lower (116-134)
  • lower (116-118)
  • node (21-25)
  • node (21-21)
  • node (27-31)
  • node (27-27)
cpp/src/dual_simplex/branch_and_bound.hpp (7)
  • leaf_objective (205-208)
  • node_ptr (247-259)
  • node_ptr (262-266)
  • solution (116-116)
  • solution (134-134)
  • solution (201-201)
  • node (215-215)
cpp/src/dual_simplex/diving_heuristics.hpp (3)
  • line_search_diving (24-27)
  • pseudocost_diving (30-34)
  • guided_diving (37-41)
🔇 Additional comments (24)
cpp/src/dual_simplex/mip_node.hpp (5)

48-48: LGTM: Consistent initialization of objective_estimate.

The field is properly initialized to infinity using std::numeric_limits<f_t>::infinity(), which is type-safe for the template parameter f_t.


63-63: LGTM: Type-safe initialization in root constructor.

This initialization now correctly uses std::numeric_limits<f_t>::infinity() matching the default constructor, ensuring type safety when the template is instantiated with different floating-point types.


85-85: LGTM: Proper propagation from parent node.

The objective estimate is correctly propagated from the parent node to child nodes, which is essential for the diving heuristics' node scoring mechanism.


233-239: LGTM: Complete field copying in detach_copy().

All relevant fields including objective_estimate are now copied when detaching a node, ensuring the detached copy retains all necessary state for independent processing.


245-245: LGTM: Public member declaration.

The objective_estimate field is appropriately declared as public alongside other node attributes like lower_bound, enabling direct access for node queue sorting operations.

cpp/src/dual_simplex/simplex_solver_settings.hpp (3)

22-34: Well-structured diving settings configuration.

The new diving_heuristics_settings_t struct cleanly encapsulates all diving-related parameters with sensible defaults:

  • node_limit = 500 bounds exploration depth
  • iteration_limit_factor = 0.05 prevents runaway simplex iterations during dives
  • backtrack = 5 limits backtracking depth in the dive stack

This aligns well with standard MIP solver diving configurations.


94-94: LGTM: Correct use of std::max for diving task count.

The initialization now correctly ensures at least 1 diving task while allowing more threads when available: std::max(num_threads - num_bfs_threads, 1).


155-156: LGTM: Clean integration of diving settings.

The diving settings are properly integrated as a member of the solver settings struct, maintaining a clean separation of concerns.

cpp/src/dual_simplex/diving_heuristics.cpp (5)

12-64: LGTM: Line search diving implementation.

The implementation correctly follows Achterberg's line search diving heuristic (§9.2.4):

  • Scores variables based on fractional part divided by distance from root solution
  • Prefers rounding toward the root solution value
  • Has a sensible fallback when the solution equals the root

The mutex-free design is appropriate since this function only reads shared state.


66-139: LGTM: Pseudocost diving with proper synchronization.

The implementation correctly:

  • Acquires a lock before accessing pseudo-cost data (line 73)
  • Falls back to averages for uninitialized pseudo-costs (lines 91-96)
  • Uses (1 + pc) in denominators to prevent division by zero (lines 98-99)
  • Applies the multi-criteria decision logic from Achterberg §9.2.5

141-197: LGTM: Guided diving implementation.

The guided diving correctly uses the incumbent solution to determine rounding direction (line 166-167), preferring the direction that moves closer to the known feasible solution. The weighted scoring (5:1 ratio) appropriately prioritizes the chosen direction.


199-227: LGTM: Variable locks calculation.

The function correctly counts up/down locks based on constraint structure:

  • Handles ranged constraints (both bounds finite) by counting both directions
  • Uses coefficient sign to determine lock direction for one-sided constraints

The return type is now correctly std::tuple<i_t, i_t> matching the integer lock counts.


275-297: LGTM: Template instantiations.

Explicit instantiations for int/double are provided under the DUAL_SIMPLEX_INSTANTIATE_DOUBLE guard, matching the project's pattern.

cpp/src/dual_simplex/branch_and_bound.cpp (11)

195-205: LGTM: Extended thread type symbols for logging.

The 2-character symbols (B, CD, LD, PD, GD) provide clear identification of the source thread type in logs while maintaining column alignment.


249-260: LGTM: Unified lower bound computation.

The lower bound now correctly aggregates from multiple sources:

  1. The ceiling from numerical issues
  2. The node queue's best-first lower bound
  3. All local thread bounds

The std::isfinite check at line 259 correctly handles uninitialized or infinite bounds.


558-619: Well-structured variable selection dispatch.

The variable_selection function cleanly dispatches to the appropriate diving heuristic based on thread type. Key observations:

  • EXPLORATION uses pseudo-cost selection with Martin's criteria for direction
  • The objective_estimate is only set for EXPLORATION (line 600), which is correct since only exploration threads push nodes to the queue
  • The default case logs and returns an invalid result, which will trigger assertions downstream - consider returning a more defensive fallback.

640-643: Good defensive fallback for guided diving.

When no incumbent exists (upper_bound == inf), guided diving cannot function since it requires an incumbent solution to guide the rounding direction. Falling back to pseudocost diving is the correct behavior.


656-661: Verify iteration limit calculation for edge cases.

The iteration limit for diving threads is calculated as max_iter - stats.total_lp_iters. This could underflow if stats.total_lp_iters exceeds max_iter due to floating-point precision when computing max_iter = iteration_limit_factor * bnb_lp_iters.

Consider using explicit clamping:

   if (thread_type != bnb_thread_type_t::EXPLORATION) {
     i_t bnb_lp_iters            = exploration_stats_.total_lp_iters;
     f_t max_iter                = settings_.diving_settings.iteration_limit_factor * bnb_lp_iters;
-    lp_settings.iteration_limit = max_iter - stats.total_lp_iters;
+    lp_settings.iteration_limit = std::max(static_cast<i_t>(max_iter) - stats.total_lp_iters, static_cast<i_t>(0));
     if (lp_settings.iteration_limit <= 0) { return node_solve_info_t::ITERATION_LIMIT; }
   }

This ensures the limit is never negative even with floating-point truncation.


814-815: LGTM: Iteration limit handling in solve_node.

The new ITERATION_LIMIT case correctly propagates the status when diving threads exhaust their iteration budget, allowing clean exit from the dive.


1143-1220: Well-implemented diving exploration with backtracking.

The dive_from function correctly implements bounded diving with:

  • Local subtree for isolation from the main search tree
  • Per-dive statistics tracking (dive_stats)
  • Node and iteration limits (lines 1180-1181)
  • Backtrack depth limiting (lines 1215-1218)

The backtrack logic at lines 1215-1218 trims the stack when depth difference exceeds the configured limit, preventing excessive memory usage during deep dives.


1222-1268: LGTM: Diving thread implementation.

The diving thread correctly:

  • Maintains local problem state and bounds
  • Resets starting bounds when beginning a new dive (lines 1241-1246)
  • Applies bounds strengthening before diving (line 1254)
  • Checks for cutoff before committing to a dive (line 1255)

1354-1377: Good defensive handling of disabled diving strategies.

The code properly builds the diving strategies vector based on settings and emits a warning when all are disabled (line 1376). This is correctly paired with the guard at line 1564 that skips task spawning when the vector is empty.


1564-1570: LGTM: Safe diving task dispatch.

The guard at line 1564 (if (!diving_strategies.empty())) correctly prevents the modulo operation when no strategies are available, addressing the potential division-by-zero concern from the past review.


1574-1576: LGTM: Final lower bound computation.

The final lower bound correctly uses the node queue's lower bound when nodes remain, or falls back to the root's lower bound when the queue is empty.

@nguidotti nguidotti marked this pull request as draft December 12, 2025 18:41
@copy-pr-bot
Copy link

copy-pr-bot bot commented Dec 12, 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.

@nguidotti
Copy link
Contributor Author

/ok to test 0aa344e

@nguidotti
Copy link
Contributor Author

/ok to test 7a5284e

@nguidotti
Copy link
Contributor Author

/ok to test 9f07cbb

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

Labels

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants