Skip to content

build(cmake): split libcuopt into cuopt_base / cuopt_routing / cuopt_lp component libs - #1622

Draft
ramakrishnap-nv wants to merge 20 commits into
mainfrom
feat/split-routing-lp-libs
Draft

build(cmake): split libcuopt into cuopt_base / cuopt_routing / cuopt_lp component libs#1622
ramakrishnap-nv wants to merge 20 commits into
mainfrom
feat/split-routing-lp-libs

Conversation

@ramakrishnap-nv

@ramakrishnap-nv ramakrishnap-nv commented Jul 24, 2026

Copy link
Copy Markdown
Collaborator

Splits libcuopt.so into four component SHARED libraries (cuopt_base, cuopt_routing, cuopt_lp, cuopt_grpc) plus a thin umbrella libcuopt.so that links them all — existing -lcuopt consumers are unaffected.

🤖 Generated with Claude Code

ramakrishnap-nv and others added 3 commits July 24, 2026 13:48
…lp + umbrella

Introduce three STATIC component libraries that logically partition the
cuOpt sources by domain, then fold them into the existing libcuopt.so
umbrella via --whole-archive (LINK_LIBRARY:WHOLE_ARCHIVE).

Component libraries:
- cuopt_base   — utilities + linear algebra (logger, work scheduler)
- cuopt_routing — VRP / routing engine; links cuopt_base
- cuopt_lp      — LP / MIP / numerical optimization; links cuopt_base

Umbrella:
- cuopt SHARED  — re-exports all symbols from the three statics via
  --whole-archive; backward-compatible for GAMS (-lcuopt / libcuopt.so)

CMake aliases exposed: cuopt::base, cuopt::routing, cuopt::lp, cuopt::cuopt

No source files moved. External build output (libcuopt.so, headers,
install layout) is unchanged. SKIP_ROUTING_BUILD=ON continues to work
by omitting cuopt_routing from the build and umbrella link.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Ramakrishna Prabhu <ramakrishnap@nvidia.com>
Four issues found while validating the library split locally:

- cuopt_routing was missing OpenMP::OpenMP_CUDA, causing routing CUDA
  files to reject #pragma omp directives as unknown in CUDA compiler mode
- cuopt_lp was missing simde::simde, required by the fast MPS parser
  (io/experimental_mps_fast/) which uses SIMD intrinsics via simde headers
- The umbrella cuopt target was missing src/io in its private include
  dirs, causing gRPC mapper files (grpc_problem_mapper.cpp) that include
  mps_parser_internal.hpp to fail to compile
- WHOLE_ARCHIVE linkage on the umbrella was PUBLIC, propagating the static
  sub-libs as link dependencies to all consumers (test binaries). This
  caused double-definition errors when tests linked both libcuopt.so and
  the statics. Changed to PRIVATE and re-exposed the statics' transitive
  PUBLIC deps (rmm, raft, CCCL, CUDA libs) directly on the umbrella so
  that consumers receive the correct source-fetched include dirs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Ramakrishna Prabhu <ramakrishnap@nvidia.com>
Add libcuopt_base.a, libcuopt_routing.a, and libcuopt_lp.a to the
package_contents file check so CI fails fast if any of the three
component static libraries are missing from the installed package.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Ramakrishna Prabhu <ramakrishnap@nvidia.com>
@copy-pr-bot

copy-pr-bot Bot commented Jul 24, 2026

Copy link
Copy Markdown

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.

ramakrishnap-nv and others added 2 commits July 27, 2026 10:08
…to cuopt_lp

Component libs (cuopt_base, cuopt_routing, cuopt_lp) are now SHARED
instead of STATIC. The umbrella libcuopt.so becomes a thin stub (~15 KB)
carrying only DT_NEEDED entries for the three component libs; no code or
WHOLE_ARCHIVE baking.

The gRPC bridge (mapper + Cython client) moves from the umbrella into
cuopt_lp where it semantically belongs — LP/MIP remote solve is an LP
concern. The umbrella drops all gRPC sources, include dirs, and
protobuf/gRPC link deps.

Both RPATH settings use $ORIGIN so component libs find each other when
co-installed. Conda package_contents check updated from .a to .so.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Ramakrishna Prabhu <ramakrishnap@nvidia.com>
Moves all gRPC infrastructure (proto mappers, Cython client, solve_remote)
from cuopt_lp into a new cuopt_grpc SHARED component. cuopt_grpc links
cuopt_lp + cuopt_routing (when built), keeping both core solver libs free
of any gRPC/protobuf dependency.

The grpc_server binary now links cuopt_grpc directly. The umbrella links
cuopt_grpc when gRPC is built so -lcuopt continues to expose remote-solve
symbols to existing consumers.

When PR #1597 (VRP gRPC) lands, routing gRPC sources go into cuopt_grpc
alongside the LP ones — no cross-dependency between cuopt_lp and cuopt_routing
is needed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Ramakrishna Prabhu <ramakrishnap@nvidia.com>
@ramakrishnap-nv ramakrishnap-nv self-assigned this Jul 27, 2026
@ramakrishnap-nv ramakrishnap-nv added non-breaking Introduces a non-breaking change improvement Improves an existing functionality labels Jul 27, 2026
@ramakrishnap-nv ramakrishnap-nv added this to the 26.10 milestone Jul 27, 2026
Resolve conflicts between:
- Our component library split (cuopt_base/routing/lp/grpc as SHARED + thin umbrella)
- main's cuopt_objs OBJECT library approach added in #1581

Both coexist: cuopt_objs + cuopt_static serve internal tests; the SHARED
component libs + umbrella serve all other consumers.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@ramakrishnap-nv

Copy link
Copy Markdown
Collaborator Author

/ok to test ec81ebc

@ramakrishnap-nv

Copy link
Copy Markdown
Collaborator Author

@CodeRabbit review

@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Changes

cuOpt componentized build and remote execution

Layer / File(s) Summary
Component source aggregation
cpp/src/CMakeLists.txt, cpp/src/.../CMakeLists.txt
CMake now propagates nested source lists and aggregates base, routing, and LP sources for downstream targets.
Component libraries and dependencies
cpp/CMakeLists.txt
Shared component targets, NCCL and KaMinPar wiring, umbrella symbol anchors, installation exports, and conditional gRPC dependencies are updated.
Dynamic remote-solver registration
cpp/include/.../remote_solve_registry.hpp, cpp/src/grpc/..., cpp/src/pdlp/..., cpp/src/mip_heuristics/solve.cu, cpp/cuopt_cli.cpp
Remote LP and MIP execution uses registered callbacks and loads the gRPC component on demand.
Distribution and examples
ci/*, conda/recipes/libcuopt/recipe.yaml, python/libcuopt/CMakeLists.txt, docs/cuopt/...
Packaging expectations, wheel repair, install RPATHs, C example linking, and documentation link checking are updated.

Estimated code review effort: 4 (Complex) | ~60 minutes

Suggested reviewers: chris-maes, iroy30

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the primary change: splitting libcuopt into component libraries using CMake.
Description check ✅ Passed The description accurately explains the component-library split, umbrella library, and compatibility goal.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 2
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🛠️ Fix failing CI checks 💡
  • Fix failing CI checks
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/split-routing-lp-libs

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

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

⚠️ Outside diff range comments (1)
cpp/CMakeLists.txt (1)

902-918: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Exported component names should match the documented API

cuopt::base / cuopt::lp are build-tree aliases only. The install/export set will expose the real targets (cuopt::cuopt_base, cuopt::cuopt_lp, etc.) unless those targets set EXPORT_NAME, so a consumer using find_package(cuopt) won’t be able to link against cuopt::base as documented.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cpp/CMakeLists.txt` around lines 902 - 918, Update the component target
export configuration used by the rapids_export INSTALL and BUILD calls so
installed targets retain the documented names cuopt::base, cuopt::routing,
cuopt::lp, and cuopt::grpc. Set the appropriate EXPORT_NAME values on the
underlying cuopt_component targets, while preserving cuopt::cuopt as the
umbrella target and keeping build-tree aliases consistent.
🧹 Nitpick comments (2)
cpp/CMakeLists.txt (2)

549-556: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Give the git hash lookup a fallback for non-git source trees.

Without RESULT_VARIABLE/ERROR_QUIET, tarball builds leak git's error to the configure log and bake an empty hash into build_info.hpp.

♻️ Proposed fallback
 execute_process(
         COMMAND git rev-parse --short HEAD
         WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
         OUTPUT_VARIABLE GIT_COMMIT_HASH
         OUTPUT_STRIP_TRAILING_WHITESPACE
+        RESULT_VARIABLE _git_hash_result
+        ERROR_QUIET
 )
+if(NOT _git_hash_result EQUAL 0 OR GIT_COMMIT_HASH STREQUAL "")
+    set(GIT_COMMIT_HASH "unknown")
+endif()
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cpp/CMakeLists.txt` around lines 549 - 556, Update the git hash lookup in the
top-level CMake configuration to capture the execute_process result and suppress
stderr for source trees without Git metadata. When the lookup fails, assign a
stable non-empty fallback hash before the existing GIT_COMMIT_HASH message and
build_info.hpp generation; preserve the real short HEAD value for Git checkouts.

664-675: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoff

cuopt_objs duplicates the component include/definition setup.

The object library re-declares papilo/pslp/dejavu includes, CUDSS defines, and architecture defines that cuopt_configure_component (plus cuopt_lp) already establish. Since cuopt_objs backs cuopt_static for the test builds, drift here means tests compile under a different configuration than shipped libraries. Consider factoring the shared include/definition block into a helper both paths call.

Also applies to: 693-694, 720-724

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cpp/CMakeLists.txt` around lines 664 - 675, Refactor the shared include
directories and compile definitions currently duplicated by cuopt_objs and the
cuopt_configure_component/cuopt_lp setup into a reusable CMake helper. Invoke
that helper for both cuopt_objs and the shipped-library path, including the
papilo/pslp/dejavu includes, CUDSS definitions, and architecture definitions,
while preserving target-specific settings such as POSITION_INDEPENDENT_CODE and
logging definitions.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@cpp/CMakeLists.txt`:
- Around line 902-918: Update the component target export configuration used by
the rapids_export INSTALL and BUILD calls so installed targets retain the
documented names cuopt::base, cuopt::routing, cuopt::lp, and cuopt::grpc. Set
the appropriate EXPORT_NAME values on the underlying cuopt_component targets,
while preserving cuopt::cuopt as the umbrella target and keeping build-tree
aliases consistent.

---

Nitpick comments:
In `@cpp/CMakeLists.txt`:
- Around line 549-556: Update the git hash lookup in the top-level CMake
configuration to capture the execute_process result and suppress stderr for
source trees without Git metadata. When the lookup fails, assign a stable
non-empty fallback hash before the existing GIT_COMMIT_HASH message and
build_info.hpp generation; preserve the real short HEAD value for Git checkouts.
- Around line 664-675: Refactor the shared include directories and compile
definitions currently duplicated by cuopt_objs and the
cuopt_configure_component/cuopt_lp setup into a reusable CMake helper. Invoke
that helper for both cuopt_objs and the shipped-library path, including the
papilo/pslp/dejavu includes, CUDSS definitions, and architecture definitions,
while preserving target-specific settings such as POSITION_INDEPENDENT_CODE and
logging definitions.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 2d788dd1-855a-4038-900e-9a3eab8f07be

📥 Commits

Reviewing files that changed from the base of the PR and between 91134a2 and ec81ebc.

📒 Files selected for processing (13)
  • conda/recipes/libcuopt/recipe.yaml
  • cpp/CMakeLists.txt
  • cpp/src/CMakeLists.txt
  • cpp/src/barrier/CMakeLists.txt
  • cpp/src/branch_and_bound/CMakeLists.txt
  • cpp/src/cuts/CMakeLists.txt
  • cpp/src/dual_simplex/CMakeLists.txt
  • cpp/src/io/CMakeLists.txt
  • cpp/src/linear_algebra/CMakeLists.txt
  • cpp/src/math_optimization/CMakeLists.txt
  • cpp/src/mip_heuristics/CMakeLists.txt
  • cpp/src/pdlp/CMakeLists.txt
  • cpp/src/routing/CMakeLists.txt

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@ramakrishnap-nv

Copy link
Copy Markdown
Collaborator Author

/ok to test a54e62a

@github-actions

github-actions Bot commented Jul 27, 2026

Copy link
Copy Markdown

CI Test Summary

✅ All 31 test job(s) passed.

ramakrishnap-nv and others added 2 commits July 27, 2026 16:23
libcuopt.so is now a thin umbrella with DT_NEEDED on libcuopt_base.so,
libcuopt_routing.so, libcuopt_lp.so, and libcuopt_grpc.so. auditwheel
traverses DT_NEEDED transitively and failed when it couldn't locate
the component libs. Exclude them the same way libcuopt.so is excluded —
they ship with the libcuopt wheel and are available at runtime.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…builds

main appends GRPC_INFRA_FILES to CUOPT_SRC_FILES before creating cuopt_objs
so cuopt_static (used by NUMOPT_INTERNAL_TEST) gets solve_lp_remote /
solve_mip_remote. We dropped that line when we moved those files into
cuopt_grpc, causing undefined-reference link failures in tests.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@ramakrishnap-nv

Copy link
Copy Markdown
Collaborator Author

/ok to test 705167f

ramakrishnap-nv and others added 3 commits July 28, 2026 10:40
…lution

cuopt_lp.so calls solve_lp/mip_remote (under CUOPT_ENABLE_GRPC) which are
defined in cuopt_grpc.so. With --as-needed the linker was dropping
libcuopt_grpc.so from executables that never directly referenced a grpc
symbol, leaving solve_lp_remote unresolved at runtime.

Route remote solves in cuopt_cli directly through solve_lp/mip_remote so
libcuopt_grpc.so is a genuine DT_NEEDED of the binary; --as-needed then
keeps it in the link and the symbol is in scope when libcuopt_lp.so needs it.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Ramakrishna Prabhu <ramakrishnap@nvidia.com>
memory_backend_t::CPU fires on any CPU-only host even when
CUOPT_REMOTE_HOST is not set, incorrectly routing local solves
through the gRPC client path. is_remote_execution_enabled() checks
CUOPT_REMOTE_HOST + CUOPT_REMOTE_PORT and is the correct guard.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Ramakrishna Prabhu <ramakrishnap@nvidia.com>
Conflicts resolved in cpp/CMakeLists.txt:

- cuopt_objs: take KaMinPar include-dirs, compile-defs, and dependency
  from main; drop duplicate variable definitions already hoisted to the
  top of the file by the split PR (CUOPT_PRIVATE_CUDA_LIBS, git hash,
  build_info.hpp, JOINED_CUDA_ARCHITECTURES, CUDSS_MT_LIB_FILE_NAME).

- cuopt (umbrella): keep HEAD (empty) — the thin umbrella does not need
  direct CUDA/rmm/PSLP/KaMinPar links; those live in the component libs.
  KaMinPar is added to cuopt_lp (shared lib that compiles partitioner.cpp)
  separately.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Ramakrishna Prabhu <ramakrishnap@nvidia.com>
@ramakrishnap-nv ramakrishnap-nv added the do not merge Do not merge if this flag is set label Jul 28, 2026
@ramakrishnap-nv

Copy link
Copy Markdown
Collaborator Author

/ok to test 1bc38f7

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Ramakrishna Prabhu <ramakrishnap@nvidia.com>
@ramakrishnap-nv

Copy link
Copy Markdown
Collaborator Author

/ok to test 8511bd0

distributed_pdlp files (multi_gpu_engine.cu, distributed_algorithms.cu)
use NCCL APIs and are compiled into cuopt_lp. After the library split,
cuopt_lp must declare its own NCCL dependency rather than inheriting it
from the monolithic cuopt_static target.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Ramakrishna Prabhu <ramakrishnap@nvidia.com>
@ramakrishnap-nv

Copy link
Copy Markdown
Collaborator Author

/ok to test f2b22e7

solve.cu (LP) called solve_lp_remote/solve_mip_remote directly under
CUOPT_ENABLE_GRPC. Those symbols live in libcuopt_grpc.so, which itself
depends on libcuopt_lp.so — a circular dependency that caused
PDLP_MG_TEST, docs, and server tests to fail when loading libcuopt_lp.so
without libcuopt_grpc.so present.

Fix: introduce a function-pointer registry in libcuopt_lp.so.
- remote_solve_registry.cpp defines g_solve_lp_remote_fn /
  g_solve_mip_remote_fn (nullptr until gRPC is loaded) and
  register_remote_solvers().
- grpc_registration.cpp registers the real implementations via a
  __attribute__((constructor)) that fires when libcuopt_grpc.so is
  dlopen'd.
- solve.cu (LP and MIP) call through the function pointers; a clear
  RuntimeError is raised if remote execution is requested but the gRPC
  component is not loaded.

Also fix the -lcuopt C API contract: cuopt_umbrella.cpp now references
cuOptDestroyProblem so --as-needed keeps libcuopt_lp.so in libcuopt.so's
DT_NEEDED, allowing C programs to link with -lcuopt as before.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Ramakrishna Prabhu <ramakrishnap@nvidia.com>
@ramakrishnap-nv

Copy link
Copy Markdown
Collaborator Author

/ok to test 90ee992

The grpc_registration constructor (which wires up g_solve_lp_remote_fn)
only fires when libcuopt_grpc.so is loaded. Programs that link with
-lcuopt must therefore have libcuopt_grpc.so in the load chain.

Reference solve_lp_remote<int,double> in cuopt_umbrella.cpp so
--as-needed keeps libcuopt_grpc.so in libcuopt.so's DT_NEEDED alongside
libcuopt_lp.so. Any binary that links with -lcuopt now gets both
component libs loaded automatically, and the remote-solve function
pointers are registered before user code runs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Ramakrishna Prabhu <ramakrishnap@nvidia.com>
@ramakrishnap-nv

Copy link
Copy Markdown
Collaborator Author

/ok to test a83e0f4

…cution

With the library split, --as-needed strips libcuopt.so and libcuopt_grpc.so
from test binary DT_NEEDED since all C API symbols come from libcuopt_lp.so
directly.  This prevents the __attribute__((constructor)) in grpc_registration.cpp
from firing, leaving g_solve_lp_remote_fn/g_solve_mip_remote_fn null.

Fix: when remote execution is enabled and the function pointer is still null,
call dlopen("libcuopt_grpc.so", RTLD_NOW | RTLD_GLOBAL) to load the gRPC
component on demand.  Its constructor registers the function pointers before
we dereference them.  If gRPC is not installed the error message is unchanged.

Also add -lcuopt_lp to C example Makefiles and ci/test_skills_assets.sh to
satisfy newer linkers that reject "DSO missing from command line" errors when
symbols are transitively provided through libcuopt.so's DT_NEEDED.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Ramakrishna Prabhu <ramakrishnap@nvidia.com>
@ramakrishnap-nv

Copy link
Copy Markdown
Collaborator Author

/ok to test 0f3b7e1

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Ramakrishna Prabhu <ramakrishnap@nvidia.com>
@ramakrishnap-nv

Copy link
Copy Markdown
Collaborator Author

/ok to test b872bfb

…check

C example Makefiles now pass -Wl,--allow-shlib-undefined so the linker
accepts libcuopt_lp.so's transitive deps (librmm, libcudss, etc.) being
resolved at runtime via RPATH rather than at link time. Required in wheel
installs where auditwheel bundles those deps with mangled names outside
the standard search path.

Add pypi.org to sphinx linkcheck_ignore; pypi.org consistently times out
in CI networks and the link was already present in introduction.rst.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Ramakrishna Prabhu <ramakrishnap@nvidia.com>
@ramakrishnap-nv

Copy link
Copy Markdown
Collaborator Author

/ok totest 7b32d1b

@ramakrishnap-nv

Copy link
Copy Markdown
Collaborator Author

/ok to test 7b32d1b

After the library split, libcuopt_lp.so and libcuopt_base.so are the
libs that directly reference libcudss, librmm, etc. They only had
INSTALL_RPATH="$ORIGIN" (set in cpp/CMakeLists.txt). The nvidia
pip-package paths ($ORIGIN/../../nvidia/cudss/lib, etc.) were only
applied to the umbrella cuopt target, cuopt_cli, and cuopt_grpc_server.

At runtime the dynamic linker uses each shared library's own RPATH to
find its deps, so libcuopt_lp.so needed the nvidia paths too. Without
them, libcudss.so.0 (from nvidia-cudss-cu12) could not be found when C
examples were run as standalone binaries from wheel installs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Ramakrishna Prabhu <ramakrishnap@nvidia.com>
@ramakrishnap-nv

Copy link
Copy Markdown
Collaborator Author

/ok to test 80c1c34

@ramakrishnap-nv

Copy link
Copy Markdown
Collaborator Author

@CodeRabbit review

@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (2)
cpp/src/mip_heuristics/solve.cu (1)

918-922: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicated lazy-load-and-validate sequence.

This is a line-for-line copy of the LP path in cpp/src/pdlp/solve.cu (Lines 2696-2700), including the discarded dlopen result and the hardcoded unversioned soname. A single helper in the registry (e.g. ensure_remote_solvers_loaded()) would keep the loader name, the dlerror() reporting, and any future synchronization in one place instead of two components.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cpp/src/mip_heuristics/solve.cu` around lines 918 - 922, Replace the inline
lazy-load and null-check sequence in the MIP remote execution path with a shared
registry helper such as ensure_remote_solvers_loaded(). Move loader-name
handling, dlopen result validation, dlerror reporting, and synchronization into
that helper, then reuse it from both this path and the corresponding LP path
while preserving the existing runtime-error behavior when the remote solver
remains unavailable.
cpp/include/cuopt/mathematical_optimization/remote_solve_registry.hpp (1)

25-30: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Add Doxygen comments for the new registry surface.

This lives under cpp/include/cuopt/, where new declarations are expected to carry Doxygen-style documentation. Line comments cover the intent, but the callback contract worth stating explicitly is: who owns the returned solution, whether the callbacks may throw across the component boundary, and that only <int, double> is instantiated.

As per path instructions, "New public functions/classes need Doxygen-style documentation" and "Suggest documenting thread-safety, GPU requirements, numerical behavior".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cpp/include/cuopt/mathematical_optimization/remote_solve_registry.hpp` around
lines 25 - 30, Add Doxygen-style documentation immediately before
solve_lp_remote_fn_t and solve_mip_remote_fn_t, describing that callbacks return
ownership via unique_ptr, must not throw across the component boundary, and are
instantiated only for <int, double>. Retain the existing intent comment while
documenting the callback contract for both typedefs.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@cpp/CMakeLists.txt`:
- Around line 865-890: Extend _UMBRELLA_SRC with used symbol-reference anchors
for exported symbols from cuopt_base and cuopt_routing so their libraries remain
in DT_NEEDED under --as-needed; add the routing anchor only under the
appropriate routing build guard, while preserving the existing LP anchor and
SKIP_GRPC_BUILD gRPC guard.

In `@cpp/cuopt_cli.cpp`:
- Around line 203-205: Replace the unchecked static_cast in the remote execution
path with a validated dynamic_cast to cpu_optimization_problem_t. Before passing
cpu_prob to solve_*_remote, enforce that the cast succeeded and fail cleanly
using the existing cuopt_expects-style validation, preserving the GPU backend
path without reinterpreting its problem type.

In `@cpp/include/cuopt/mathematical_optimization/remote_solve_registry.hpp`:
- Around line 32-38: Synchronize remote-solver registry access by declaring
g_solve_lp_remote_fn and g_solve_mip_remote_fn as atomic function-pointer slots
in cpp/include/cuopt/mathematical_optimization/remote_solve_registry.hpp, and
store registrations with release ordering in
cpp/src/pdlp/remote_solve_registry.cpp. In cpp/src/pdlp/solve.cu lines
2696-2700, load g_solve_lp_remote_fn once into a local before the null check and
reuse it for dispatch; apply the same pattern to g_solve_mip_remote_fn in
cpp/src/mip_heuristics/solve.cu lines 918-922.
- Around line 19-23: Change the forward declarations of pdlp_solver_settings_t
and mip_solver_settings_t to use class instead of struct, matching their class
definitions and keeping both template parameter lists unchanged.

In `@python/libcuopt/CMakeLists.txt`:
- Around line 98-102: Update the RPATH property list in the CMake configuration
to include the cuopt_routing target with the same ${rpaths} append used by
cuopt_base and cuopt_lp, ensuring its installed shared library receives the
bundled CUDA library runtime paths.

---

Nitpick comments:
In `@cpp/include/cuopt/mathematical_optimization/remote_solve_registry.hpp`:
- Around line 25-30: Add Doxygen-style documentation immediately before
solve_lp_remote_fn_t and solve_mip_remote_fn_t, describing that callbacks return
ownership via unique_ptr, must not throw across the component boundary, and are
instantiated only for <int, double>. Retain the existing intent comment while
documenting the callback contract for both typedefs.

In `@cpp/src/mip_heuristics/solve.cu`:
- Around line 918-922: Replace the inline lazy-load and null-check sequence in
the MIP remote execution path with a shared registry helper such as
ensure_remote_solvers_loaded(). Move loader-name handling, dlopen result
validation, dlerror reporting, and synchronization into that helper, then reuse
it from both this path and the corresponding LP path while preserving the
existing runtime-error behavior when the remote solver remains unavailable.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: ea594003-fad8-4a06-acda-c89669a7e541

📥 Commits

Reviewing files that changed from the base of the PR and between ec81ebc and 80c1c34.

📒 Files selected for processing (16)
  • ci/build_wheel_cuopt.sh
  • ci/test_skills_assets.sh
  • conda/recipes/libcuopt/recipe.yaml
  • cpp/CMakeLists.txt
  • cpp/cuopt_cli.cpp
  • cpp/include/cuopt/mathematical_optimization/remote_solve_registry.hpp
  • cpp/src/grpc/client/grpc_registration.cpp
  • cpp/src/mip_heuristics/solve.cu
  • cpp/src/pdlp/CMakeLists.txt
  • cpp/src/pdlp/remote_solve_registry.cpp
  • cpp/src/pdlp/solve.cu
  • cpp/src/routing/CMakeLists.txt
  • docs/cuopt/source/conf.py
  • docs/cuopt/source/cuopt-c/convex/examples/Makefile
  • docs/cuopt/source/cuopt-c/mip/examples/Makefile
  • python/libcuopt/CMakeLists.txt
🚧 Files skipped from review as they are similar to previous changes (1)
  • cpp/src/routing/CMakeLists.txt

Comment thread cpp/CMakeLists.txt
Comment on lines +865 to +890
# Reference one symbol from each component lib so --as-needed keeps them in
# DT_NEEDED. The constructor in grpc_registration.cpp fires when
# libcuopt_grpc.so is loaded and wires up the remote-solve function pointers
# in libcuopt_lp.so, so that must happen whenever -lcuopt is used.
set(_UMBRELLA_SRC [=[
// Thin umbrella: one symbol reference per component so --as-needed keeps
// each library in DT_NEEDED.
extern "C" void cuOptDestroyProblem(void*); // libcuopt_lp.so
__attribute__((used)) void (* const _cuopt_lp_anchor)(void*) = &cuOptDestroyProblem;
]=])

if(NOT SKIP_GRPC_BUILD)
string(APPEND _UMBRELLA_SRC [=[
// solve_lp_remote is in libcuopt_grpc.so; pulling it in ensures the
// grpc_registration constructor runs and registers the remote-solve
// function pointers in libcuopt_lp.so.
#include <cuopt/mathematical_optimization/solve_remote.hpp>
#include <cuopt/mathematical_optimization/remote_solve_registry.hpp>
namespace cuopt::mathematical_optimization {
__attribute__((used)) solve_lp_remote_fn_t const _cuopt_grpc_anchor =
&solve_lp_remote<int, double>;
}
]=])
endif()

file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/cuopt_umbrella.cpp" "${_UMBRELLA_SRC}")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🔴 Critical | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -n 'SKIP_GRPC_BUILD|CUOPT_ENABLE_GRPC' cpp/CMakeLists.txt
rg -n '_cuopt_base_anchor|_cuopt_routing_anchor|_UMBRELLA_SRC' cpp/CMakeLists.txt

Repository: NVIDIA/cuopt

Length of output: 1129


🏁 Script executed:

#!/bin/bash
set -eu
printf '%s\n' '--- CMake target/link context ---'
sed -n '520,575p;780,850p;850,935p;1050,1100p' cpp/CMakeLists.txt
printf '%s\n' '--- component target and exported-symbol references ---'
rg -n 'add_library|target_sources|target_link_libraries|install|cuopt_base|cuopt_routing|cuOptDestroyProblem|solve_lp_remote' cpp/CMakeLists.txt cpp/src cpp/include -g '*.{cmake,txt,cpp,cu,c,h,hpp}' | head -300
printf '%s\n' '--- candidate component files ---'
git ls-files | rg '(^|/)(base|routing|.*base.*|.*routing.*)|solve_remote|remote_solve_registry'

Repository: NVIDIA/cuopt

Length of output: 32659


🏁 Script executed:

#!/bin/bash
set -eu
printf '%s\n' '--- option resolution and gRPC target conditions ---'
sed -n '35,65p;350,410p;610,715p;930,1010p;1150,1230p' cpp/CMakeLists.txt
printf '%s\n' '--- linker/as-needed configuration ---'
rg -n --hidden -i 'as-needed|no-as-needed|copy-dt-needed|link_options|CMAKE_SHARED_LINKER_FLAGS|CMAKE_EXE_LINKER_FLAGS' . -g '!**/build/**' -g '!**/node_modules/**'
printf '%s\n' '--- public base/routing declarations and definitions ---'
rg -n '^(export )?(namespace|class|struct)|(^|[^A-Za-z0-9_])(solve|cuOpt[A-Z]|cuopt_[A-Za-z0-9_]+)\s*\\(' cpp/include/cuopt/routing cpp/include/cuopt/mathematical_optimization cpp/src/base cpp/src/routing cpp/src/pdlp -g '*.{h,hpp,cpp,cu}' | head -300

Repository: NVIDIA/cuopt

Length of output: 16472


🏁 Script executed:

#!/bin/bash
set -eu
printf '%s\n' '--- component source lists ---'
sed -n '410,525p' cpp/CMakeLists.txt
printf '%s\n' '--- installed/public routing API declarations ---'
rg -n 'namespace cuopt|class |struct |cuOpt|solve\\s*\\(' cpp/include/cuopt/routing cpp/include/cuopt/mathematical_optimization -g '*.{h,hpp}' | head -240
printf '%s\n' '--- routing/base definitions and exported C API definitions ---'
rg -n 'namespace cuopt|cuOpt[A-Z][A-Za-z0-9_]*\\s*\\(|^[A-Za-z_][A-Za-z0-9_:<>*& ]*::[A-Za-z_][A-Za-z0-9_]*\\s*\\(' cpp/src/routing cpp/src/base cpp/src/pdlp -g '*.{cpp,cu}' | head -240
printf '%s\n' '--- all CUOPT_ENABLE_GRPC references ---'
rg -n --hidden 'CUOPT_ENABLE_GRPC|SKIP_GRPC_BUILD' . -g '!**/build/**' -g '!**/node_modules/**'

Repository: NVIDIA/cuopt

Length of output: 9218


🏁 Script executed:

#!/bin/bash
python3 - <<'PY'
from pathlib import Path
import re

text = Path("cpp/CMakeLists.txt").read_text()

umbrella = re.search(
    r"set\(_UMBRELLA_SRC \[=\[(.*?)\]=\]\)",
    text,
    re.S,
).group(1)
anchors = {
    name: name in umbrella
    for name in ("_cuopt_lp_anchor", "_cuopt_grpc_anchor",
                 "_cuopt_base_anchor", "_cuopt_routing_anchor")
}

cuopt_links = re.search(
    r"if\(NOT SKIP_ROUTING_BUILD\)\s*"
    r"target_link_libraries\(cuopt PUBLIC (.*?)\)\s*"
    r"else\(\)\s*"
    r"target_link_libraries\(cuopt PUBLIC (.*?)\)",
    text,
    re.S,
)
grpc_link = re.search(
    r"if\(NOT SKIP_GRPC_BUILD\)\s*"
    r"target_link_libraries\(cuopt PUBLIC cuopt_grpc\)",
    text,
)

print("umbrella anchors:", anchors)
print("routing-enabled umbrella dependencies:",
      " ".join(cuopt_links.group(1).split()) if cuopt_links else "<not found>")
print("routing-disabled umbrella dependencies:",
      " ".join(cuopt_links.group(2).split()) if cuopt_links else "<not found>")
print("gRPC umbrella dependency guarded by SKIP_GRPC_BUILD:",
      bool(grpc_link))
print("gRPC anchor guarded by SKIP_GRPC_BUILD:",
      "if(NOT SKIP_GRPC_BUILD)" in text[text.index("if(NOT SKIP_GRPC_BUILD)", text.index("set(_UMBRELLA_SRC")):])

assert anchors["_cuopt_lp_anchor"]
assert not anchors["_cuopt_base_anchor"]
assert not anchors["_cuopt_routing_anchor"]
assert cuopt_links and "cuopt_base" in cuopt_links.group(1)
assert cuopt_links and "cuopt_routing" in cuopt_links.group(1)
assert grpc_link
PY

Repository: NVIDIA/cuopt

Length of output: 521


Add real DT_NEEDED anchors for cuopt_base and cuopt_routing.

cuopt links these components publicly, but the umbrella TU references only LP and gRPC. With --as-needed, base and routing can be omitted, breaking consumers that use -lcuopt. Add exported-symbol anchors, conditionally for routing. The gRPC guards are already aligned with SKIP_GRPC_BUILD.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cpp/CMakeLists.txt` around lines 865 - 890, Extend _UMBRELLA_SRC with used
symbol-reference anchors for exported symbols from cuopt_base and cuopt_routing
so their libraries remain in DT_NEEDED under --as-needed; add the routing anchor
only under the appropriate routing build guard, while preserving the existing LP
anchor and SKIP_GRPC_BUILD gRPC guard.

Source: Path instructions

Comment thread cpp/cuopt_cli.cpp
Comment on lines +203 to +205
auto* cpu_prob =
static_cast<cuopt::mathematical_optimization::cpu_optimization_problem_t<int, double>*>(
problem_interface.get());

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

Unchecked static_cast to cpu_optimization_problem_t — wrong downcast when remote execution is enabled with the GPU backend.

is_remote_execution_enabled() and get_memory_backend_type() are independent conditions. If the backend resolves to GPU (Lines 136-140 construct an optimization_problem_t) while CUOPT_REMOTE_HOST/CUOPT_REMOTE_PORT are set, this static_cast reinterprets a GPU problem as a CPU problem and *cpu_prob is then handed to solve_*_remote — undefined behavior, most likely a corrupt read rather than a clean error.

The library-side dispatchers guard this exact conversion (cpp/src/pdlp/solve.cu Line 2692 and cpp/src/mip_heuristics/solve.cu Line 914 use dynamic_cast plus cuopt_expects); the CLI should not be the one place that skips the check.

🛡️ Proposed fix: validate the downcast
       auto* cpu_prob =
-        static_cast<cuopt::mathematical_optimization::cpu_optimization_problem_t<int, double>*>(
+        dynamic_cast<cuopt::mathematical_optimization::cpu_optimization_problem_t<int, double>*>(
           problem_interface.get());
+      if (cpu_prob == nullptr) {
+        CUOPT_LOG_ERROR(
+          "Remote execution requires the CPU memory backend, but a GPU-backed problem was "
+          "created. Unset CUOPT_REMOTE_HOST/CUOPT_REMOTE_PORT or select the CPU backend.");
+        return -1;
+      }
       if (is_mip) {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
auto* cpu_prob =
static_cast<cuopt::mathematical_optimization::cpu_optimization_problem_t<int, double>*>(
problem_interface.get());
auto* cpu_prob =
dynamic_cast<cuopt::mathematical_optimization::cpu_optimization_problem_t<int, double>*>(
problem_interface.get());
if (cpu_prob == nullptr) {
CUOPT_LOG_ERROR(
"Remote execution requires the CPU memory backend, but a GPU-backed problem was "
"created. Unset CUOPT_REMOTE_HOST/CUOPT_REMOTE_PORT or select the CPU backend.");
return -1;
}
if (is_mip) {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cpp/cuopt_cli.cpp` around lines 203 - 205, Replace the unchecked static_cast
in the remote execution path with a validated dynamic_cast to
cpu_optimization_problem_t. Before passing cpu_prob to solve_*_remote, enforce
that the cast succeeded and fail cleanly using the existing cuopt_expects-style
validation, preserving the GPU backend path without reinterpreting its problem
type.

Comment on lines +19 to +23
template <typename i_t, typename f_t>
struct pdlp_solver_settings_t;

template <typename i_t, typename f_t>
struct mip_solver_settings_t;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -nP --type=cpp --type=cuda -g '!**/tests/**' '\b(class|struct)\s+(pdlp_solver_settings_t|mip_solver_settings_t|cpu_optimization_problem_t)\b' | head -50

Repository: NVIDIA/cuopt

Length of output: 150


🏁 Script executed:

#!/bin/bash
set -eu
printf '%s\n' 'Definitions and declarations across tracked source files:'
rg -n --glob '!**/tests/**' '\b(class|struct)\s+(pdlp_solver_settings_t|mip_solver_settings_t|cpu_optimization_problem_t)\b' .
printf '%s\n' 'References to the settings types:'
rg -n --glob '!**/tests/**' '\b(pdlp_solver_settings_t|mip_solver_settings_t)\b' cpp python 2>/dev/null | head -100

Repository: NVIDIA/cuopt

Length of output: 14060


🏁 Script executed:

#!/bin/bash
set -eu
printf '%s\n' 'remote_solve_registry.hpp:'
sed -n '1,80p' cpp/include/cuopt/mathematical_optimization/remote_solve_registry.hpp
printf '%s\n' 'Definitions:'
sed -n '100,130p' cpp/include/cuopt/mathematical_optimization/pdlp/solver_settings.hpp
sed -n '50,75p' cpp/include/cuopt/mathematical_optimization/mip/solver_settings.hpp
printf '%s\n' 'Include/use context:'
rg -n -C 3 'remote_solve_registry|pdlp_solver_settings_t|mip_solver_settings_t' cpp/include/cuopt/mathematical_optimization/remote_solve_registry.hpp cpp/include/cuopt/mathematical_optimization/solve_remote.hpp cpp/src -g '*.cpp' -g '*.cu' -g '*.hpp' -g '*.cuh' | head -180

Repository: NVIDIA/cuopt

Length of output: 18505


🏁 Script executed:

#!/bin/bash
set -eu
if command -v g++ >/dev/null 2>&1; then
  printf '%s\n' 'Compiler behavior for mismatched class-keys:'
  printf '%s\n' 'template<class T> class X; template<class T> struct X {}; int main() {}' |
    g++ -std=c++17 -x c++ -fsyntax-only -Wall -Wextra -
else
  printf '%s\n' 'g++ unavailable; source evidence is sufficient to identify the class-key mismatch.'
fi

Repository: NVIDIA/cuopt

Length of output: 233


Use class for both forward declarations.

These templates are defined as classes; the mismatched class-keys are ill-formed when both declarations are visible.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cpp/include/cuopt/mathematical_optimization/remote_solve_registry.hpp` around
lines 19 - 23, Change the forward declarations of pdlp_solver_settings_t and
mip_solver_settings_t to use class instead of struct, matching their class
definitions and keeping both template parameter lists unchanged.

Comment on lines +32 to +38
// Defined in libcuopt_lp.so (remote_solve_registry.cpp).
// Set to nullptr until libcuopt_grpc.so is loaded and calls register_remote_solvers().
extern solve_lp_remote_fn_t g_solve_lp_remote_fn;
extern solve_mip_remote_fn_t g_solve_mip_remote_fn;

// Called by libcuopt_grpc.so's constructor to wire up the real implementations.
void register_remote_solvers(solve_lp_remote_fn_t lp_fn, solve_mip_remote_fn_t mip_fn);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Unsynchronized access to the remote-solver registry across three sites. The shared root cause is that the registry slots are declared as plain function pointers: they are published from libcuopt_grpc.so's ELF constructor during a lazy dlopen while other threads read them with no synchronization, so concurrent solve_lp/solve_mip calls race on a non-atomic object.

  • cpp/include/cuopt/mathematical_optimization/remote_solve_registry.hpp#L32-L38: declare both slots as std::atomic<...> (and store with release ordering in cpp/src/pdlp/solve.cu's sibling remote_solve_registry.cpp); this is the only edit that fixes the race.
  • cpp/src/pdlp/solve.cu#L2696-L2700: once the slot is atomic, load it once into a local before the null test and reuse that local for the dispatch, rather than reading the global three times.
  • cpp/src/mip_heuristics/solve.cu#L918-L922: apply the same single-load-into-a-local pattern for g_solve_mip_remote_fn.
📍 Affects 3 files
  • cpp/include/cuopt/mathematical_optimization/remote_solve_registry.hpp#L32-L38 (this comment)
  • cpp/src/pdlp/solve.cu#L2696-L2700
  • cpp/src/mip_heuristics/solve.cu#L918-L922
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cpp/include/cuopt/mathematical_optimization/remote_solve_registry.hpp` around
lines 32 - 38, Synchronize remote-solver registry access by declaring
g_solve_lp_remote_fn and g_solve_mip_remote_fn as atomic function-pointer slots
in cpp/include/cuopt/mathematical_optimization/remote_solve_registry.hpp, and
store registrations with release ordering in
cpp/src/pdlp/remote_solve_registry.cpp. In cpp/src/pdlp/solve.cu lines
2696-2700, load g_solve_lp_remote_fn once into a local before the null check and
reuse it for dispatch; apply the same pattern to g_solve_mip_remote_fn in
cpp/src/mip_heuristics/solve.cu lines 918-922.

Comment on lines 98 to 102
set_property(TARGET cuopt PROPERTY INSTALL_RPATH ${rpaths} APPEND)
set_property(TARGET cuopt_base PROPERTY INSTALL_RPATH ${rpaths} APPEND)
set_property(TARGET cuopt_lp PROPERTY INSTALL_RPATH ${rpaths} APPEND)
set_property(TARGET cuopt_cli PROPERTY INSTALL_RPATH ${rpaths} APPEND)
set_property(TARGET cuopt_grpc_server PROPERTY INSTALL_RPATH ${rpaths} APPEND)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

cuopt_routing is missing from the RPATH list.

cuopt_base and cuopt_lp were given the same ${rpaths} append as cuopt, cuopt_cli, and cuopt_grpc_server, but cuopt_routing (introduced as its own component library in this PR) isn't included. If it links any of the bundled CUDA libs the RPATH set targets (cublas/cusparse/cusolver/nccl/nvjitlink), the wheel-installed libcuopt_routing.so could fail to locate them at runtime.

🔧 Suggested fix
 set_property(TARGET cuopt PROPERTY INSTALL_RPATH ${rpaths} APPEND)
 set_property(TARGET cuopt_base PROPERTY INSTALL_RPATH ${rpaths} APPEND)
+set_property(TARGET cuopt_routing PROPERTY INSTALL_RPATH ${rpaths} APPEND)
 set_property(TARGET cuopt_lp PROPERTY INSTALL_RPATH ${rpaths} APPEND)
 set_property(TARGET cuopt_cli PROPERTY INSTALL_RPATH ${rpaths} APPEND)
 set_property(TARGET cuopt_grpc_server PROPERTY INSTALL_RPATH ${rpaths} APPEND)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
set_property(TARGET cuopt PROPERTY INSTALL_RPATH ${rpaths} APPEND)
set_property(TARGET cuopt_base PROPERTY INSTALL_RPATH ${rpaths} APPEND)
set_property(TARGET cuopt_lp PROPERTY INSTALL_RPATH ${rpaths} APPEND)
set_property(TARGET cuopt_cli PROPERTY INSTALL_RPATH ${rpaths} APPEND)
set_property(TARGET cuopt_grpc_server PROPERTY INSTALL_RPATH ${rpaths} APPEND)
set_property(TARGET cuopt PROPERTY INSTALL_RPATH ${rpaths} APPEND)
set_property(TARGET cuopt_base PROPERTY INSTALL_RPATH ${rpaths} APPEND)
set_property(TARGET cuopt_routing PROPERTY INSTALL_RPATH ${rpaths} APPEND)
set_property(TARGET cuopt_lp PROPERTY INSTALL_RPATH ${rpaths} APPEND)
set_property(TARGET cuopt_cli PROPERTY INSTALL_RPATH ${rpaths} APPEND)
set_property(TARGET cuopt_grpc_server PROPERTY INSTALL_RPATH ${rpaths} APPEND)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@python/libcuopt/CMakeLists.txt` around lines 98 - 102, Update the RPATH
property list in the CMake configuration to include the cuopt_routing target
with the same ${rpaths} append used by cuopt_base and cuopt_lp, ensuring its
installed shared library receives the bundled CUDA library runtime paths.

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

Labels

do not merge Do not merge if this flag is set improvement Improves an existing functionality non-breaking Introduces a non-breaking change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant