From 9b3d53bdcbbf4c03f468b7c1ffad2d9718cc8ae4 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Thu, 23 Jul 2026 15:40:25 -0500 Subject: [PATCH 01/27] build(cmake): split libcuopt into cuopt_base / cuopt_routing / cuopt_lp + umbrella MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Signed-off-by: Ramakrishna Prabhu --- cpp/CMakeLists.txt | 288 +++++++++++++++-------- cpp/src/CMakeLists.txt | 31 ++- cpp/src/barrier/CMakeLists.txt | 1 + cpp/src/branch_and_bound/CMakeLists.txt | 1 + cpp/src/cuts/CMakeLists.txt | 1 + cpp/src/dual_simplex/CMakeLists.txt | 1 + cpp/src/io/CMakeLists.txt | 3 +- cpp/src/linear_algebra/CMakeLists.txt | 1 + cpp/src/math_optimization/CMakeLists.txt | 1 + cpp/src/mip_heuristics/CMakeLists.txt | 1 + cpp/src/pdlp/CMakeLists.txt | 1 + cpp/src/routing/CMakeLists.txt | 1 + 12 files changed, 227 insertions(+), 104 deletions(-) diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 61f6eb91df..699b727655 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -454,6 +454,9 @@ if (BUILD_TESTS) endif () set(CUOPT_SRC_FILES) +set(CUOPT_BASE_SRC_FILES) +set(CUOPT_ROUTING_SRC_FILES) +set(CUOPT_LP_SRC_FILES) set(MPS_FAST_SRC_FILES) add_subdirectory(src) @@ -464,7 +467,9 @@ set_source_files_properties( PROPERTIES COMPILE_OPTIONS "--split-compile=0") if (HOST_LINEINFO) - set_source_files_properties(${CUOPT_SRC_FILES} DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTIES COMPILE_OPTIONS "-g1") + set_source_files_properties( + ${CUOPT_BASE_SRC_FILES} ${CUOPT_ROUTING_SRC_FILES} ${CUOPT_LP_SRC_FILES} + DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTIES COMPILE_OPTIONS "-g1") endif () # Needed for the fast MPS parser, available on all x86-64-v3 compliant x86 CPUs (essentially since Haswell ~2013) @@ -477,16 +482,17 @@ endif () # TODO: figure out a set of flags for ARM that fits the range of CPUs we wish to support (neoverse?) # NEON should be universal on aarch64 and enough for our purposes (parsing) though -# Apply -UNDEBUG only to solver source files (not gRPC infrastructure). -# Must happen before gRPC files are appended to CUOPT_SRC_FILES. +# Apply -UNDEBUG to solver source files (not gRPC infrastructure). # Uses APPEND to preserve any existing per-file options (e.g. -g1 from HOST_LINEINFO). if (DEFINE_ASSERT) - set_property(SOURCE ${CUOPT_SRC_FILES} DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + set_property( + SOURCE ${CUOPT_BASE_SRC_FILES} ${CUOPT_ROUTING_SRC_FILES} ${CUOPT_LP_SRC_FILES} + DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} APPEND PROPERTY COMPILE_OPTIONS "-UNDEBUG") endif () if (NOT SKIP_GRPC_BUILD) - # Add gRPC mapper files and generated protobuf sources + # gRPC integration layer: maps proto <-> C++ LP API; lives in the umbrella, not in cuopt_lp set(GRPC_INFRA_FILES ${DATA_PROTO_SRCS} ${PROTO_SRCS} @@ -501,7 +507,6 @@ if (NOT SKIP_GRPC_BUILD) src/grpc/client/cython_grpc_client.cpp src/grpc/client/solve_remote.cpp ) - list(APPEND CUOPT_SRC_FILES ${GRPC_INFRA_FILES}) # Always keep NDEBUG defined for gRPC infrastructure files so that abseil # headers inline Mutex::Dtor() instead of emitting an external call. @@ -512,67 +517,166 @@ if (NOT SKIP_GRPC_BUILD) APPEND PROPERTY COMPILE_OPTIONS "-DNDEBUG") endif (NOT SKIP_GRPC_BUILD) -add_library(cuopt SHARED - ${CUOPT_SRC_FILES} -) +# ################################################################################################## +# - cuopt component libraries (STATIC, position-independent for use in the umbrella SHARED) ------ -set_target_properties(cuopt - PROPERTIES BUILD_RPATH "\$ORIGIN" - INSTALL_RPATH "\$ORIGIN" - INTERFACE_POSITION_INDEPENDENT_CODE ON - CXX_SCAN_FOR_MODULES OFF -) +# Helper: apply compile options and common include paths to all cuOpt component libs +function(cuopt_configure_component target) + set_target_properties(${target} PROPERTIES + POSITION_INDEPENDENT_CODE ON + CXX_SCAN_FOR_MODULES OFF + ) + target_compile_options(${target} + PRIVATE "$<$:${CUOPT_CXX_FLAGS}>" + "$<$:${CUOPT_CUDA_FLAGS}>" + ) + target_compile_definitions(${target} + PUBLIC "CUOPT_LOG_ACTIVE_LEVEL=RAPIDS_LOGGER_LOG_LEVEL_${LIBCUOPT_LOGGING_LEVEL}" + ) + target_include_directories(${target} + PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}/src" + "${CMAKE_CURRENT_BINARY_DIR}" + PUBLIC + "$" + "$" + INTERFACE + "$" + ) +endfunction() -target_compile_definitions(cuopt - PUBLIC "CUOPT_LOG_ACTIVE_LEVEL=RAPIDS_LOGGER_LOG_LEVEL_${LIBCUOPT_LOGGING_LEVEL}" - PUBLIC CUSPARSE_ENABLE_EXPERIMENTAL_API +# Compute git hash and generate build_info.hpp before any component is defined +execute_process( + COMMAND git rev-parse --short HEAD + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + OUTPUT_VARIABLE GIT_COMMIT_HASH + OUTPUT_STRIP_TRAILING_WHITESPACE ) +message("-- Building with GIT_COMMIT_HASH = '${GIT_COMMIT_HASH}'") -target_compile_options(cuopt - PRIVATE "$<$:${CUOPT_CXX_FLAGS}>" - "$<$:${CUOPT_CUDA_FLAGS}>" +configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/src/utilities/build_info.hpp.in + ${CMAKE_CURRENT_BINARY_DIR}/include/utilities/build_info.hpp + @ONLY ) -if (WRITE_FATBIN) - file(WRITE "${CUOPT_BINARY_DIR}/fatbin.ld" - [=[ - SECTIONS - { - .nvFatBinSegment : { *(.nvFatBinSegment) } - .nv_fatbin : { *(.nv_fatbin) } - } - ]=]) - target_link_options(cuopt PRIVATE "${CUOPT_BINARY_DIR}/fatbin.ld") -endif () +list(JOIN CMAKE_CUDA_ARCHITECTURES "," JOINED_CUDA_ARCHITECTURES) -add_library(cuopt::cuopt ALIAS cuopt) -# ################################################################################################## -# - include paths --------------------------------------------------------------------------------- -message(STATUS "target include directories CUDSS_INCLUDES = ${CUDSS_INCLUDE}") +set(CUOPT_PRIVATE_CUDA_LIBS + CUDA::cublasLt + CUDA::curand + CUDA::cusolver + TBB::tbb + OpenMP::OpenMP_CXX) + +get_filename_component(CUDSS_MT_LIB_FILE_NAME "${CUDSS_MT_LIB_FILE}" NAME) +# cuopt_base: utilities + linear algebra (stateful shared infra — logger, work scheduler) +add_library(cuopt_base STATIC ${CUOPT_BASE_SRC_FILES}) +cuopt_configure_component(cuopt_base) +target_include_directories(cuopt_base PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/include") +target_compile_definitions(cuopt_base PUBLIC + CUOPT_CUDA_ARCHITECTURES="${JOINED_CUDA_ARCHITECTURES}" + CUOPT_CPU_ARCHITECTURE="${CMAKE_SYSTEM_PROCESSOR}" +) +target_link_libraries(cuopt_base + PUBLIC + rmm::rmm + rapids_logger::rapids_logger + CCCL::CCCL + raft::raft + CUDA::cublas + CUDA::cusparse + PRIVATE + OpenMP::OpenMP_CXX + OpenMP::OpenMP_CUDA +) +add_library(cuopt::base ALIAS cuopt_base) + +# cuopt_routing: VRP / routing engine (omitted when SKIP_ROUTING_BUILD=ON) +if(NOT SKIP_ROUTING_BUILD) + add_library(cuopt_routing STATIC ${CUOPT_ROUTING_SRC_FILES}) + cuopt_configure_component(cuopt_routing) + target_link_libraries(cuopt_routing + PUBLIC cuopt_base + PRIVATE simde::simde OpenMP::OpenMP_CXX + ) + add_library(cuopt::routing ALIAS cuopt_routing) +endif() + +# cuopt_lp: LP / MIP / numerical optimization engine +add_library(cuopt_lp STATIC ${CUOPT_LP_SRC_FILES}) +cuopt_configure_component(cuopt_lp) +target_include_directories(cuopt_lp PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}/../thirdparty" + "${CMAKE_CURRENT_SOURCE_DIR}/src/io" + "${CUDSS_INCLUDE}" + $<$:${BZIP2_INCLUDE_DIRS}> + $<$:${ZLIB_INCLUDE_DIRS}> +) # Adding Papilo as a system include messes up clang's include resolution if papilo is already installed as a conda package -target_include_directories(cuopt PRIVATE +target_include_directories(cuopt_lp PRIVATE "${papilo_SOURCE_DIR}/src" "${papilo_BINARY_DIR}" ) - -target_include_directories(cuopt SYSTEM PRIVATE +target_include_directories(cuopt_lp SYSTEM PRIVATE "${pslp_SOURCE_DIR}/include" "${dejavu_SOURCE_DIR}" ) +target_compile_definitions(cuopt_lp + PUBLIC CUSPARSE_ENABLE_EXPERIMENTAL_API + PRIVATE CUDSS_MT_LIB_FILE_NAME="${CUDSS_MT_LIB_FILE_NAME}" +) +target_link_libraries(cuopt_lp + PUBLIC + cuopt_base + ${CUDSS_LIB_FILE} + PRIVATE + ${CUOPT_PRIVATE_CUDA_LIBS} + OpenMP::OpenMP_CUDA +) +target_link_libraries(cuopt_lp PRIVATE $) +add_dependencies(cuopt_lp PSLP) +add_library(cuopt::lp ALIAS cuopt_lp) + +# ################################################################################################## +# - cuopt: umbrella shared library ---------------------------------------------------------------- +# Re-exports all symbols from the three component libs via --whole-archive. +# Backward-compatible: GAMS and any consumer using -lcuopt / libcuopt.so is unaffected. +# gRPC integration layer (maps proto <-> C++ LP API) lives here, not in cuopt_lp. + +message(STATUS "target include directories CUDSS_INCLUDES = ${CUDSS_INCLUDE}") + +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/cuopt_umbrella.cpp" "// cuopt umbrella\n") + +if(NOT SKIP_GRPC_BUILD) + set(CUOPT_UMBRELLA_EXTRA_SRCS ${GRPC_INFRA_FILES}) +endif() + +add_library(cuopt SHARED + "${CMAKE_CURRENT_BINARY_DIR}/cuopt_umbrella.cpp" + ${CUOPT_UMBRELLA_EXTRA_SRCS} +) + +set_target_properties(cuopt PROPERTIES + BUILD_RPATH "\$ORIGIN" + INSTALL_RPATH "\$ORIGIN" + INTERFACE_POSITION_INDEPENDENT_CODE ON + CXX_SCAN_FOR_MODULES OFF +) + +target_compile_options(cuopt + PRIVATE "$<$:${CUOPT_CXX_FLAGS}>" + "$<$:${CUOPT_CUDA_FLAGS}>" +) target_include_directories(cuopt PRIVATE - "${CMAKE_CURRENT_SOURCE_DIR}/../thirdparty" "${CMAKE_CURRENT_SOURCE_DIR}/src" - "${CMAKE_CURRENT_SOURCE_DIR}/src/io" "${CMAKE_CURRENT_SOURCE_DIR}/src/grpc" "${CMAKE_CURRENT_SOURCE_DIR}/src/grpc/client" "${CMAKE_CURRENT_SOURCE_DIR}/src/grpc/codegen/generated" "${CMAKE_CURRENT_BINARY_DIR}" - "${CUDSS_INCLUDE}" - $<$:${BZIP2_INCLUDE_DIRS}> - $<$:${ZLIB_INCLUDE_DIRS}> PUBLIC "$" "$" @@ -580,67 +684,34 @@ target_include_directories(cuopt "$" ) -# Link PSLP by file to avoid export dependency tracking -target_link_libraries(cuopt PRIVATE $) -add_dependencies(cuopt PSLP) - -# ################################################################################################## -# - link libraries -------------------------------------------------------------------------------- - -set(CUOPT_PRIVATE_CUDA_LIBS - CUDA::curand - CUDA::cusolver - TBB::tbb - OpenMP::OpenMP_CXX) - -list(PREPEND CUOPT_PRIVATE_CUDA_LIBS CUDA::cublasLt) - -# Pass CUDSS_MT_LIB_FILE_NAME as a compile definition -get_filename_component(CUDSS_MT_LIB_FILE_NAME "${CUDSS_MT_LIB_FILE}" NAME) -target_compile_definitions(cuopt PRIVATE CUDSS_MT_LIB_FILE_NAME="${CUDSS_MT_LIB_FILE_NAME}") - -execute_process( - COMMAND git rev-parse --short HEAD - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} - OUTPUT_VARIABLE GIT_COMMIT_HASH - OUTPUT_STRIP_TRAILING_WHITESPACE -) -message("-- Building with GIT_COMMIT_HASH = '${GIT_COMMIT_HASH}'") - -# Generate build_info.hpp from template -# configure_file() only updates the output if content changes, avoiding unnecessary rebuilds -configure_file( - ${CMAKE_CURRENT_SOURCE_DIR}/src/utilities/build_info.hpp.in - ${CMAKE_CURRENT_BINARY_DIR}/include/utilities/build_info.hpp - @ONLY -) - -# Add the generated include directory -target_include_directories(cuopt PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/include) - -list(JOIN CMAKE_CUDA_ARCHITECTURES "," JOINED_CUDA_ARCHITECTURES) -target_compile_definitions(cuopt PUBLIC - CUOPT_CUDA_ARCHITECTURES="${JOINED_CUDA_ARCHITECTURES}" - CUOPT_CPU_ARCHITECTURE="${CMAKE_SYSTEM_PROCESSOR}") +if(NOT SKIP_ROUTING_BUILD) + target_link_libraries(cuopt PUBLIC + $) +else() + target_link_libraries(cuopt PUBLIC + $) +endif() target_link_libraries(cuopt - PUBLIC - CUDA::cublas - CUDA::cusparse - rmm::rmm - rapids_logger::rapids_logger - CCCL::CCCL - raft::raft - ${CUDSS_LIB_FILE} PRIVATE - ${CUOPT_PRIVATE_CUDA_LIBS} - simde::simde - OpenMP::OpenMP_CXX - OpenMP::OpenMP_CUDA $<$:protobuf::libprotobuf> $<$:gRPC::grpc++> ) +if (WRITE_FATBIN) + file(WRITE "${CUOPT_BINARY_DIR}/fatbin.ld" + [=[ + SECTIONS + { + .nvFatBinSegment : { *(.nvFatBinSegment) } + .nv_fatbin : { *(.nv_fatbin) } + } + ]=]) + target_link_options(cuopt PRIVATE "${CUOPT_BINARY_DIR}/fatbin.ld") +endif () + +add_library(cuopt::cuopt ALIAS cuopt) + # ################################################################################################## # - generate tests -------------------------------------------------------------------------------- if (BUILD_TESTS) @@ -669,14 +740,25 @@ else () set(_INCLUDE_DEST include/cuopt/) endif () -# adds the .so files to the runtime deb package +# Install component static libs (runtime: none — statics are build-time only; +# dev: headers are shared, statics go in the dev package for downstream cmake consumers) +set(CUOPT_COMPONENT_TARGETS cuopt_base cuopt_lp) +if(NOT SKIP_ROUTING_BUILD) + list(APPEND CUOPT_COMPONENT_TARGETS cuopt_routing) +endif() +install(TARGETS ${CUOPT_COMPONENT_TARGETS} + DESTINATION ${_LIB_DEST} + COMPONENT dev + EXPORT cuopt-exports +) + +# Install umbrella shared library (the primary runtime artifact) install(TARGETS cuopt DESTINATION ${_LIB_DEST} COMPONENT runtime EXPORT cuopt-exports ) -# adds the .so files to the development deb package install(TARGETS cuopt DESTINATION ${_LIB_DEST} COMPONENT dev @@ -700,12 +782,14 @@ set(doc_string Provide targets for cuOpt. cuOpt library is a collection of GPU accelerated combinatorial optimization algorithms. +Component targets: cuopt::base, cuopt::routing, cuopt::lp +Umbrella target: cuopt::cuopt (re-exports all symbols — backward-compatible with -lcuopt) ]=]) rapids_export(INSTALL cuopt EXPORT_SET cuopt-exports - GLOBAL_TARGETS cuopt + GLOBAL_TARGETS cuopt ${CUOPT_COMPONENT_TARGETS} NAMESPACE cuopt:: DOCUMENTATION doc_string ) @@ -714,7 +798,7 @@ rapids_export(INSTALL cuopt # - build export ------------------------------------------------------------------------------- rapids_export(BUILD cuopt EXPORT_SET cuopt-exports - GLOBAL_TARGETS cuopt + GLOBAL_TARGETS cuopt ${CUOPT_COMPONENT_TARGETS} NAMESPACE cuopt:: DOCUMENTATION doc_string ) diff --git a/cpp/src/CMakeLists.txt b/cpp/src/CMakeLists.txt index e8737cf6da..8af5cf4dfc 100644 --- a/cpp/src/CMakeLists.txt +++ b/cpp/src/CMakeLists.txt @@ -25,5 +25,34 @@ add_subdirectory(barrier) add_subdirectory(branch_and_bound) add_subdirectory(cuts) -set(CUOPT_SRC_FILES ${CUOPT_SRC_FILES} ${UTIL_SRC_FILES} PARENT_SCOPE) +# Aggregate per-domain source lists for the three component libraries +set(CUOPT_BASE_SRC_FILES + ${UTIL_SRC_FILES} + ${LINEAR_ALGEBRA_SRC_FILES} +) + +set(CUOPT_ROUTING_SRC_FILES + ${ROUTING_SRC_FILES} +) + +set(CUOPT_LP_SRC_FILES + ${LP_SRC_FILES} + ${MATH_OPT_SRC_FILES} + ${MIP_SRC_FILES} + ${PARSERS_SRC_FILES} + ${DUAL_SIMPLEX_SRC_FILES} + ${BARRIER_SRC_FILES} + ${BRANCH_AND_BOUND_SRC_FILES} + ${CUTS_SRC_FILES} +) + +set(CUOPT_BASE_SRC_FILES ${CUOPT_BASE_SRC_FILES} PARENT_SCOPE) +set(CUOPT_ROUTING_SRC_FILES ${CUOPT_ROUTING_SRC_FILES} PARENT_SCOPE) +set(CUOPT_LP_SRC_FILES ${CUOPT_LP_SRC_FILES} PARENT_SCOPE) +set(CUOPT_SRC_FILES + ${CUOPT_BASE_SRC_FILES} + ${CUOPT_ROUTING_SRC_FILES} + ${CUOPT_LP_SRC_FILES} + PARENT_SCOPE +) set(MPS_FAST_SRC_FILES ${MPS_FAST_SRC_FILES} PARENT_SCOPE) diff --git a/cpp/src/barrier/CMakeLists.txt b/cpp/src/barrier/CMakeLists.txt index 650bc733e9..83af7da102 100644 --- a/cpp/src/barrier/CMakeLists.txt +++ b/cpp/src/barrier/CMakeLists.txt @@ -10,5 +10,6 @@ set(BARRIER_SRC_FILES ${CMAKE_CURRENT_SOURCE_DIR}/pinned_host_allocator.cu ) +set(BARRIER_SRC_FILES ${BARRIER_SRC_FILES} PARENT_SCOPE) set(CUOPT_SRC_FILES ${CUOPT_SRC_FILES} ${BARRIER_SRC_FILES} PARENT_SCOPE) diff --git a/cpp/src/branch_and_bound/CMakeLists.txt b/cpp/src/branch_and_bound/CMakeLists.txt index 1e40c1bbf1..c9f47c4d55 100644 --- a/cpp/src/branch_and_bound/CMakeLists.txt +++ b/cpp/src/branch_and_bound/CMakeLists.txt @@ -10,5 +10,6 @@ set(BRANCH_AND_BOUND_SRC_FILES ) +set(BRANCH_AND_BOUND_SRC_FILES ${BRANCH_AND_BOUND_SRC_FILES} PARENT_SCOPE) set(CUOPT_SRC_FILES ${CUOPT_SRC_FILES} ${BRANCH_AND_BOUND_SRC_FILES} PARENT_SCOPE) diff --git a/cpp/src/cuts/CMakeLists.txt b/cpp/src/cuts/CMakeLists.txt index 813ac88a59..07d945135c 100644 --- a/cpp/src/cuts/CMakeLists.txt +++ b/cpp/src/cuts/CMakeLists.txt @@ -8,5 +8,6 @@ set(CUTS_SRC_FILES ${CMAKE_CURRENT_SOURCE_DIR}/objective_step.cpp ) +set(CUTS_SRC_FILES ${CUTS_SRC_FILES} PARENT_SCOPE) set(CUOPT_SRC_FILES ${CUOPT_SRC_FILES} ${CUTS_SRC_FILES} PARENT_SCOPE) diff --git a/cpp/src/dual_simplex/CMakeLists.txt b/cpp/src/dual_simplex/CMakeLists.txt index 228f2aedd7..1f424c69ab 100644 --- a/cpp/src/dual_simplex/CMakeLists.txt +++ b/cpp/src/dual_simplex/CMakeLists.txt @@ -25,5 +25,6 @@ set(DUAL_SIMPLEX_SRC_FILES # Uncomment to enable debug info #set_source_files_properties(${DUAL_SIMPLEX_SRC_FILES} DIRECTORY ${CMAKE_SOURCE_DIR} PROPERTIES COMPILE_OPTIONS "-g1") +set(DUAL_SIMPLEX_SRC_FILES ${DUAL_SIMPLEX_SRC_FILES} PARENT_SCOPE) set(CUOPT_SRC_FILES ${CUOPT_SRC_FILES} ${DUAL_SIMPLEX_SRC_FILES} PARENT_SCOPE) diff --git a/cpp/src/io/CMakeLists.txt b/cpp/src/io/CMakeLists.txt index cafcffb23f..29bd8dc1e2 100644 --- a/cpp/src/io/CMakeLists.txt +++ b/cpp/src/io/CMakeLists.txt @@ -23,5 +23,6 @@ set(PARSERS_SRC_FILES ${MPS_FAST_SRC_FILES} ) -set(CUOPT_SRC_FILES ${CUOPT_SRC_FILES} ${PARSERS_SRC_FILES} PARENT_SCOPE) +set(PARSERS_SRC_FILES ${PARSERS_SRC_FILES} PARENT_SCOPE) set(MPS_FAST_SRC_FILES ${MPS_FAST_SRC_FILES} PARENT_SCOPE) +set(CUOPT_SRC_FILES ${CUOPT_SRC_FILES} ${PARSERS_SRC_FILES} PARENT_SCOPE) diff --git a/cpp/src/linear_algebra/CMakeLists.txt b/cpp/src/linear_algebra/CMakeLists.txt index 875a016544..7a8ad1fa00 100644 --- a/cpp/src/linear_algebra/CMakeLists.txt +++ b/cpp/src/linear_algebra/CMakeLists.txt @@ -9,5 +9,6 @@ set(LINEAR_ALGEBRA_SRC_FILES ${CMAKE_CURRENT_SOURCE_DIR}/vector_math.cpp ) +set(LINEAR_ALGEBRA_SRC_FILES ${LINEAR_ALGEBRA_SRC_FILES} PARENT_SCOPE) set(CUOPT_SRC_FILES ${CUOPT_SRC_FILES} ${LINEAR_ALGEBRA_SRC_FILES} PARENT_SCOPE) diff --git a/cpp/src/math_optimization/CMakeLists.txt b/cpp/src/math_optimization/CMakeLists.txt index efa1600c54..3a118cf163 100644 --- a/cpp/src/math_optimization/CMakeLists.txt +++ b/cpp/src/math_optimization/CMakeLists.txt @@ -11,5 +11,6 @@ list(PREPEND ${CMAKE_CURRENT_SOURCE_DIR}/tic_toc.cpp ) +set(MATH_OPT_SRC_FILES ${MATH_OPT_SRC_FILES} PARENT_SCOPE) set(CUOPT_SRC_FILES ${CUOPT_SRC_FILES} ${MATH_OPT_SRC_FILES} PARENT_SCOPE) diff --git a/cpp/src/mip_heuristics/CMakeLists.txt b/cpp/src/mip_heuristics/CMakeLists.txt index 7705465512..4b8bcfd3a8 100644 --- a/cpp/src/mip_heuristics/CMakeLists.txt +++ b/cpp/src/mip_heuristics/CMakeLists.txt @@ -53,5 +53,6 @@ else() set(MIP_SRC_FILES ${MIP_LP_NECESSARY_FILES} ${MIP_NON_LP_FILES}) endif() +set(MIP_SRC_FILES ${MIP_SRC_FILES} PARENT_SCOPE) set(CUOPT_SRC_FILES ${CUOPT_SRC_FILES} ${MIP_SRC_FILES} PARENT_SCOPE) diff --git a/cpp/src/pdlp/CMakeLists.txt b/cpp/src/pdlp/CMakeLists.txt index f5f26837b6..c90332355b 100644 --- a/cpp/src/pdlp/CMakeLists.txt +++ b/cpp/src/pdlp/CMakeLists.txt @@ -44,4 +44,5 @@ else() set(LP_SRC_FILES ${LP_CORE_FILES} ${LP_ADAPTER_FILES}) endif() +set(LP_SRC_FILES ${LP_SRC_FILES} PARENT_SCOPE) set(CUOPT_SRC_FILES ${CUOPT_SRC_FILES} ${LP_SRC_FILES} PARENT_SCOPE) diff --git a/cpp/src/routing/CMakeLists.txt b/cpp/src/routing/CMakeLists.txt index 452c4806da..c92b4d0f3f 100644 --- a/cpp/src/routing/CMakeLists.txt +++ b/cpp/src/routing/CMakeLists.txt @@ -50,4 +50,5 @@ set(ROUTING_SRC_FILES ${CMAKE_CURRENT_SOURCE_DIR}/utilities/check_input.cu ${CMAKE_CURRENT_SOURCE_DIR}/utilities/cython.cu) +set(ROUTING_SRC_FILES ${ROUTING_SRC_FILES} PARENT_SCOPE) set(CUOPT_SRC_FILES ${CUOPT_SRC_FILES} ${ROUTING_SRC_FILES} PARENT_SCOPE) From 94755a05d690f1c61458d2caf81af4316f6cd63d Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Fri, 24 Jul 2026 09:52:31 -0500 Subject: [PATCH 02/27] build(cmake): fix component library linking and include propagation 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 Signed-off-by: Ramakrishna Prabhu --- cpp/CMakeLists.txt | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 699b727655..380dc294f6 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -599,7 +599,7 @@ if(NOT SKIP_ROUTING_BUILD) cuopt_configure_component(cuopt_routing) target_link_libraries(cuopt_routing PUBLIC cuopt_base - PRIVATE simde::simde OpenMP::OpenMP_CXX + PRIVATE simde::simde OpenMP::OpenMP_CXX OpenMP::OpenMP_CUDA ) add_library(cuopt::routing ALIAS cuopt_routing) endif() @@ -634,6 +634,7 @@ target_link_libraries(cuopt_lp PRIVATE ${CUOPT_PRIVATE_CUDA_LIBS} OpenMP::OpenMP_CUDA + simde::simde ) target_link_libraries(cuopt_lp PRIVATE $) add_dependencies(cuopt_lp PSLP) @@ -673,6 +674,7 @@ target_compile_options(cuopt target_include_directories(cuopt PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/src" + "${CMAKE_CURRENT_SOURCE_DIR}/src/io" "${CMAKE_CURRENT_SOURCE_DIR}/src/grpc" "${CMAKE_CURRENT_SOURCE_DIR}/src/grpc/client" "${CMAKE_CURRENT_SOURCE_DIR}/src/grpc/codegen/generated" @@ -685,18 +687,34 @@ target_include_directories(cuopt ) if(NOT SKIP_ROUTING_BUILD) - target_link_libraries(cuopt PUBLIC + target_link_libraries(cuopt PRIVATE $) else() - target_link_libraries(cuopt PUBLIC + target_link_libraries(cuopt PRIVATE $) endif() +# Re-expose the component statics' PUBLIC deps so that consumers of cuopt::cuopt +# (tests, downstream cmake, GAMS) receive the correct include dirs and link targets. target_link_libraries(cuopt + PUBLIC + rmm::rmm + rapids_logger::rapids_logger + CCCL::CCCL + raft::raft + CUDA::cublas + CUDA::cusparse + ${CUDSS_LIB_FILE} PRIVATE $<$:protobuf::libprotobuf> $<$:gRPC::grpc++> ) +target_compile_definitions(cuopt PUBLIC + "CUOPT_LOG_ACTIVE_LEVEL=RAPIDS_LOGGER_LOG_LEVEL_${LIBCUOPT_LOGGING_LEVEL}" + CUOPT_CUDA_ARCHITECTURES="${JOINED_CUDA_ARCHITECTURES}" + CUOPT_CPU_ARCHITECTURE="${CMAKE_SYSTEM_PROCESSOR}" + CUSPARSE_ENABLE_EXPERIMENTAL_API +) if (WRITE_FATBIN) file(WRITE "${CUOPT_BINARY_DIR}/fatbin.ld" From 0390d40edbdcced133ee4e8eac4ceb8e0387dfee Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Fri, 24 Jul 2026 13:48:52 -0500 Subject: [PATCH 03/27] build(conda): verify component static libs in libcuopt package_contents 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 Signed-off-by: Ramakrishna Prabhu --- conda/recipes/libcuopt/recipe.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/conda/recipes/libcuopt/recipe.yaml b/conda/recipes/libcuopt/recipe.yaml index 77c957eade..475fafba73 100644 --- a/conda/recipes/libcuopt/recipe.yaml +++ b/conda/recipes/libcuopt/recipe.yaml @@ -162,6 +162,9 @@ outputs: - package_contents: files: - lib/libcuopt.so + - lib/libcuopt_base.a + - lib/libcuopt_routing.a + - lib/libcuopt_lp.a - bin/cuopt_cli - bin/cuopt_grpc_server about: From efc5256c70cd4df058dca90962e2ab250918eafa Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Mon, 27 Jul 2026 10:08:35 -0500 Subject: [PATCH 04/27] build(cmake): switch component libs to SHARED and move gRPC bridge into cuopt_lp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Signed-off-by: Ramakrishna Prabhu --- conda/recipes/libcuopt/recipe.yaml | 6 +-- cpp/CMakeLists.txt | 86 +++++++++--------------------- 2 files changed, 28 insertions(+), 64 deletions(-) diff --git a/conda/recipes/libcuopt/recipe.yaml b/conda/recipes/libcuopt/recipe.yaml index 475fafba73..f794204c75 100644 --- a/conda/recipes/libcuopt/recipe.yaml +++ b/conda/recipes/libcuopt/recipe.yaml @@ -162,9 +162,9 @@ outputs: - package_contents: files: - lib/libcuopt.so - - lib/libcuopt_base.a - - lib/libcuopt_routing.a - - lib/libcuopt_lp.a + - lib/libcuopt_base.so + - lib/libcuopt_routing.so + - lib/libcuopt_lp.so - bin/cuopt_cli - bin/cuopt_grpc_server about: diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 380dc294f6..9ebc097d31 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -492,7 +492,7 @@ if (DEFINE_ASSERT) endif () if (NOT SKIP_GRPC_BUILD) - # gRPC integration layer: maps proto <-> C++ LP API; lives in the umbrella, not in cuopt_lp + # gRPC integration layer: maps proto <-> C++ LP API; compiled into cuopt_lp set(GRPC_INFRA_FILES ${DATA_PROTO_SRCS} ${PROTO_SRCS} @@ -523,8 +523,9 @@ endif (NOT SKIP_GRPC_BUILD) # Helper: apply compile options and common include paths to all cuOpt component libs function(cuopt_configure_component target) set_target_properties(${target} PROPERTIES - POSITION_INDEPENDENT_CODE ON CXX_SCAN_FOR_MODULES OFF + BUILD_RPATH_USE_ORIGIN TRUE + INSTALL_RPATH "\$ORIGIN" ) target_compile_options(${target} PRIVATE "$<$:${CUOPT_CXX_FLAGS}>" @@ -572,7 +573,7 @@ set(CUOPT_PRIVATE_CUDA_LIBS get_filename_component(CUDSS_MT_LIB_FILE_NAME "${CUDSS_MT_LIB_FILE}" NAME) # cuopt_base: utilities + linear algebra (stateful shared infra — logger, work scheduler) -add_library(cuopt_base STATIC ${CUOPT_BASE_SRC_FILES}) +add_library(cuopt_base SHARED ${CUOPT_BASE_SRC_FILES}) cuopt_configure_component(cuopt_base) target_include_directories(cuopt_base PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/include") target_compile_definitions(cuopt_base PUBLIC @@ -595,7 +596,7 @@ add_library(cuopt::base ALIAS cuopt_base) # cuopt_routing: VRP / routing engine (omitted when SKIP_ROUTING_BUILD=ON) if(NOT SKIP_ROUTING_BUILD) - add_library(cuopt_routing STATIC ${CUOPT_ROUTING_SRC_FILES}) + add_library(cuopt_routing SHARED ${CUOPT_ROUTING_SRC_FILES}) cuopt_configure_component(cuopt_routing) target_link_libraries(cuopt_routing PUBLIC cuopt_base @@ -605,7 +606,7 @@ if(NOT SKIP_ROUTING_BUILD) endif() # cuopt_lp: LP / MIP / numerical optimization engine -add_library(cuopt_lp STATIC ${CUOPT_LP_SRC_FILES}) +add_library(cuopt_lp SHARED ${CUOPT_LP_SRC_FILES}) cuopt_configure_component(cuopt_lp) target_include_directories(cuopt_lp PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../thirdparty" @@ -638,47 +639,36 @@ target_link_libraries(cuopt_lp ) target_link_libraries(cuopt_lp PRIVATE $) add_dependencies(cuopt_lp PSLP) +if(NOT SKIP_GRPC_BUILD) + target_sources(cuopt_lp PRIVATE ${GRPC_INFRA_FILES}) + target_include_directories(cuopt_lp PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}/src/grpc" + "${CMAKE_CURRENT_SOURCE_DIR}/src/grpc/client" + "${CMAKE_CURRENT_SOURCE_DIR}/src/grpc/codegen/generated" + ) + target_link_libraries(cuopt_lp PRIVATE protobuf::libprotobuf gRPC::grpc++) +endif() add_library(cuopt::lp ALIAS cuopt_lp) # ################################################################################################## -# - cuopt: umbrella shared library ---------------------------------------------------------------- -# Re-exports all symbols from the three component libs via --whole-archive. -# Backward-compatible: GAMS and any consumer using -lcuopt / libcuopt.so is unaffected. -# gRPC integration layer (maps proto <-> C++ LP API) lives here, not in cuopt_lp. +# - cuopt: thin umbrella shared library ----------------------------------------------------------- +# Links publicly against the three component shared libs so that -lcuopt keeps working for +# all existing consumers. No code of its own except the gRPC bridge (mapper + client) which +# sits here because it spans both the LP API and gRPC transport layers. message(STATUS "target include directories CUDSS_INCLUDES = ${CUDSS_INCLUDE}") -file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/cuopt_umbrella.cpp" "// cuopt umbrella\n") +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/cuopt_umbrella.cpp" "// thin umbrella\n") -if(NOT SKIP_GRPC_BUILD) - set(CUOPT_UMBRELLA_EXTRA_SRCS ${GRPC_INFRA_FILES}) -endif() - -add_library(cuopt SHARED - "${CMAKE_CURRENT_BINARY_DIR}/cuopt_umbrella.cpp" - ${CUOPT_UMBRELLA_EXTRA_SRCS} -) +add_library(cuopt SHARED "${CMAKE_CURRENT_BINARY_DIR}/cuopt_umbrella.cpp") set_target_properties(cuopt PROPERTIES - BUILD_RPATH "\$ORIGIN" + BUILD_RPATH_USE_ORIGIN TRUE INSTALL_RPATH "\$ORIGIN" - INTERFACE_POSITION_INDEPENDENT_CODE ON CXX_SCAN_FOR_MODULES OFF ) -target_compile_options(cuopt - PRIVATE "$<$:${CUOPT_CXX_FLAGS}>" - "$<$:${CUOPT_CUDA_FLAGS}>" -) - target_include_directories(cuopt - PRIVATE - "${CMAKE_CURRENT_SOURCE_DIR}/src" - "${CMAKE_CURRENT_SOURCE_DIR}/src/io" - "${CMAKE_CURRENT_SOURCE_DIR}/src/grpc" - "${CMAKE_CURRENT_SOURCE_DIR}/src/grpc/client" - "${CMAKE_CURRENT_SOURCE_DIR}/src/grpc/codegen/generated" - "${CMAKE_CURRENT_BINARY_DIR}" PUBLIC "$" "$" @@ -687,35 +677,11 @@ target_include_directories(cuopt ) if(NOT SKIP_ROUTING_BUILD) - target_link_libraries(cuopt PRIVATE - $) + target_link_libraries(cuopt PUBLIC cuopt_base cuopt_routing cuopt_lp) else() - target_link_libraries(cuopt PRIVATE - $) + target_link_libraries(cuopt PUBLIC cuopt_base cuopt_lp) endif() -# Re-expose the component statics' PUBLIC deps so that consumers of cuopt::cuopt -# (tests, downstream cmake, GAMS) receive the correct include dirs and link targets. -target_link_libraries(cuopt - PUBLIC - rmm::rmm - rapids_logger::rapids_logger - CCCL::CCCL - raft::raft - CUDA::cublas - CUDA::cusparse - ${CUDSS_LIB_FILE} - PRIVATE - $<$:protobuf::libprotobuf> - $<$:gRPC::grpc++> -) -target_compile_definitions(cuopt PUBLIC - "CUOPT_LOG_ACTIVE_LEVEL=RAPIDS_LOGGER_LOG_LEVEL_${LIBCUOPT_LOGGING_LEVEL}" - CUOPT_CUDA_ARCHITECTURES="${JOINED_CUDA_ARCHITECTURES}" - CUOPT_CPU_ARCHITECTURE="${CMAKE_SYSTEM_PROCESSOR}" - CUSPARSE_ENABLE_EXPERIMENTAL_API -) - if (WRITE_FATBIN) file(WRITE "${CUOPT_BINARY_DIR}/fatbin.ld" [=[ @@ -758,15 +724,13 @@ else () set(_INCLUDE_DEST include/cuopt/) endif () -# Install component static libs (runtime: none — statics are build-time only; -# dev: headers are shared, statics go in the dev package for downstream cmake consumers) set(CUOPT_COMPONENT_TARGETS cuopt_base cuopt_lp) if(NOT SKIP_ROUTING_BUILD) list(APPEND CUOPT_COMPONENT_TARGETS cuopt_routing) endif() install(TARGETS ${CUOPT_COMPONENT_TARGETS} DESTINATION ${_LIB_DEST} - COMPONENT dev + COMPONENT runtime EXPORT cuopt-exports ) From 08c8db47e01ba7e8f60e32af634fb34a3cf4e820 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Mon, 27 Jul 2026 14:33:43 -0500 Subject: [PATCH 05/27] build(cmake): extract gRPC bridge into libcuopt_grpc.so MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Signed-off-by: Ramakrishna Prabhu --- conda/recipes/libcuopt/recipe.yaml | 1 + cpp/CMakeLists.txt | 33 ++++++++++++++++++++++-------- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/conda/recipes/libcuopt/recipe.yaml b/conda/recipes/libcuopt/recipe.yaml index f794204c75..280f425734 100644 --- a/conda/recipes/libcuopt/recipe.yaml +++ b/conda/recipes/libcuopt/recipe.yaml @@ -165,6 +165,7 @@ outputs: - lib/libcuopt_base.so - lib/libcuopt_routing.so - lib/libcuopt_lp.so + - lib/libcuopt_grpc.so - bin/cuopt_cli - bin/cuopt_grpc_server about: diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 9ebc097d31..80c4c32baf 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -492,7 +492,7 @@ if (DEFINE_ASSERT) endif () if (NOT SKIP_GRPC_BUILD) - # gRPC integration layer: maps proto <-> C++ LP API; compiled into cuopt_lp + # gRPC integration layer: maps proto <-> C++ LP/routing APIs; compiled into cuopt_grpc set(GRPC_INFRA_FILES ${DATA_PROTO_SRCS} ${PROTO_SRCS} @@ -639,16 +639,27 @@ target_link_libraries(cuopt_lp ) target_link_libraries(cuopt_lp PRIVATE $) add_dependencies(cuopt_lp PSLP) +add_library(cuopt::lp ALIAS cuopt_lp) + +# cuopt_grpc: gRPC bridge — proto mappers + Cython client (LP and routing over gRPC) +# Depends on cuopt_lp and cuopt_routing so it can reference both APIs without forcing +# either component to take a gRPC dependency. if(NOT SKIP_GRPC_BUILD) - target_sources(cuopt_lp PRIVATE ${GRPC_INFRA_FILES}) - target_include_directories(cuopt_lp PRIVATE + add_library(cuopt_grpc SHARED ${GRPC_INFRA_FILES}) + cuopt_configure_component(cuopt_grpc) + target_include_directories(cuopt_grpc PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/src/grpc" "${CMAKE_CURRENT_SOURCE_DIR}/src/grpc/client" "${CMAKE_CURRENT_SOURCE_DIR}/src/grpc/codegen/generated" + "${CMAKE_CURRENT_SOURCE_DIR}/src/io" ) - target_link_libraries(cuopt_lp PRIVATE protobuf::libprotobuf gRPC::grpc++) + if(NOT SKIP_ROUTING_BUILD) + target_link_libraries(cuopt_grpc PUBLIC cuopt_lp cuopt_routing PRIVATE protobuf::libprotobuf gRPC::grpc++) + else() + target_link_libraries(cuopt_grpc PUBLIC cuopt_lp PRIVATE protobuf::libprotobuf gRPC::grpc++) + endif() + add_library(cuopt::grpc ALIAS cuopt_grpc) endif() -add_library(cuopt::lp ALIAS cuopt_lp) # ################################################################################################## # - cuopt: thin umbrella shared library ----------------------------------------------------------- @@ -681,6 +692,9 @@ if(NOT SKIP_ROUTING_BUILD) else() target_link_libraries(cuopt PUBLIC cuopt_base cuopt_lp) endif() +if(NOT SKIP_GRPC_BUILD) + target_link_libraries(cuopt PUBLIC cuopt_grpc) +endif() if (WRITE_FATBIN) file(WRITE "${CUOPT_BINARY_DIR}/fatbin.ld" @@ -728,6 +742,9 @@ set(CUOPT_COMPONENT_TARGETS cuopt_base cuopt_lp) if(NOT SKIP_ROUTING_BUILD) list(APPEND CUOPT_COMPONENT_TARGETS cuopt_routing) endif() +if(NOT SKIP_GRPC_BUILD) + list(APPEND CUOPT_COMPONENT_TARGETS cuopt_grpc) +endif() install(TARGETS ${CUOPT_COMPONENT_TARGETS} DESTINATION ${_LIB_DEST} COMPONENT runtime @@ -764,8 +781,8 @@ set(doc_string Provide targets for cuOpt. cuOpt library is a collection of GPU accelerated combinatorial optimization algorithms. -Component targets: cuopt::base, cuopt::routing, cuopt::lp -Umbrella target: cuopt::cuopt (re-exports all symbols — backward-compatible with -lcuopt) +Component targets: cuopt::base, cuopt::routing, cuopt::lp, cuopt::grpc (when gRPC is built) +Umbrella target: cuopt::cuopt (links all component libs — backward-compatible with -lcuopt) ]=]) @@ -967,7 +984,7 @@ if (NOT SKIP_GRPC_BUILD) target_link_libraries(cuopt_grpc_server PUBLIC - cuopt + cuopt_grpc OpenMP::OpenMP_CXX PRIVATE protobuf::libprotobuf From a54e62a5b5abc992bbc6ce1763f9b150df4ddfe4 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Mon, 27 Jul 2026 15:36:57 -0500 Subject: [PATCH 06/27] chore: fix copyright year in routing CMakeLists.txt Co-Authored-By: Claude Sonnet 4.6 --- cpp/src/routing/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/src/routing/CMakeLists.txt b/cpp/src/routing/CMakeLists.txt index c92b4d0f3f..fa9653076b 100644 --- a/cpp/src/routing/CMakeLists.txt +++ b/cpp/src/routing/CMakeLists.txt @@ -1,5 +1,5 @@ # cmake-format: off -# SPDX-FileCopyrightText: Copyright (c) 2024-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-FileCopyrightText: Copyright (c) 2024-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 # cmake-format: on From 6c24d1f6af468facaa91874754814a8777e0df7f Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Mon, 27 Jul 2026 16:23:25 -0500 Subject: [PATCH 07/27] fix(ci): exclude component libs from auditwheel repair in cuopt wheel MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- ci/build_wheel_cuopt.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ci/build_wheel_cuopt.sh b/ci/build_wheel_cuopt.sh index f624b27705..449bc19665 100755 --- a/ci/build_wheel_cuopt.sh +++ b/ci/build_wheel_cuopt.sh @@ -41,6 +41,10 @@ EXCLUDE_ARGS=( --exclude "libcusolver.so.*" --exclude "libcusparse.so.*" --exclude "libcuopt.so" + --exclude "libcuopt_base.so" + --exclude "libcuopt_routing.so" + --exclude "libcuopt_lp.so" + --exclude "libcuopt_grpc.so" --exclude "librapids_logger.so" --exclude "librmm.so" ) From 705167fa69e55fe7f64d15a55db026f0d336af71 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Mon, 27 Jul 2026 16:32:29 -0500 Subject: [PATCH 08/27] fix(cmake): include gRPC sources in cuopt_objs for cuopt_static test 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 --- cpp/CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 9d9d74198a..cfb05e146c 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -515,6 +515,9 @@ if (NOT SKIP_GRPC_BUILD) # at runtime with "undefined symbol: absl::…::Mutex::Dtor". set_property(SOURCE ${GRPC_INFRA_FILES} DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} APPEND PROPERTY COMPILE_OPTIONS "-DNDEBUG") + # Include gRPC sources in CUOPT_SRC_FILES so cuopt_objs (and cuopt_static for tests) + # have solve_lp_remote / solve_mip_remote defined when CUOPT_ENABLE_GRPC is set. + list(APPEND CUOPT_SRC_FILES ${GRPC_INFRA_FILES}) endif (NOT SKIP_GRPC_BUILD) # ################################################################################################## From a42419b499ce83fa8baa6aeb087a27ee97d757ac Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Tue, 28 Jul 2026 10:40:56 -0500 Subject: [PATCH 09/27] fix(cmake): link cuopt_cli against cuopt_grpc for runtime symbol resolution 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 Signed-off-by: Ramakrishna Prabhu --- cpp/CMakeLists.txt | 1 + cpp/cuopt_cli.cpp | 18 +++++++++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index cfb05e146c..5270c70ca2 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -981,6 +981,7 @@ if (NOT BUILD_LP_ONLY) target_link_libraries(cuopt_cli PUBLIC cuopt + $<$:cuopt_grpc> OpenMP::OpenMP_CXX ${CUDSS_LIBRARIES} TBB::tbb diff --git a/cpp/cuopt_cli.cpp b/cpp/cuopt_cli.cpp index feb0e8cd76..992c7f455a 100644 --- a/cpp/cuopt_cli.cpp +++ b/cpp/cuopt_cli.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -175,7 +176,22 @@ int run_single_file(const std::string& file_path, } try { - if (is_mip) { + if (memory_backend == cuopt::mathematical_optimization::memory_backend_t::CPU) { + // Remote execution: problem_interface holds a cpu_optimization_problem_t. + // Call solve_lp/mip_remote directly so libcuopt_grpc.so is a real DT_NEEDED + // dependency of this binary rather than an implicit runtime lookup. + auto* cpu_prob = static_cast*>(problem_interface.get()); + if (is_mip) { + auto& mip_settings = settings.get_mip_settings(); + auto solution = + cuopt::mathematical_optimization::solve_mip_remote(*cpu_prob, mip_settings); + } else { + auto& lp_settings = settings.get_pdlp_settings(); + auto solution = + cuopt::mathematical_optimization::solve_lp_remote(*cpu_prob, lp_settings); + } + } else if (is_mip) { auto& mip_settings = settings.get_mip_settings(); auto solution = cuopt::mathematical_optimization::solve_mip(problem_interface.get(), mip_settings); From 776bd3bae6e14fbeb290bd15765b996a259d49eb Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Tue, 28 Jul 2026 12:18:59 -0500 Subject: [PATCH 10/27] fix(cli): use is_remote_execution_enabled() not memory_backend==CPU 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 Signed-off-by: Ramakrishna Prabhu --- cpp/cuopt_cli.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/cuopt_cli.cpp b/cpp/cuopt_cli.cpp index 992c7f455a..2374fe7583 100644 --- a/cpp/cuopt_cli.cpp +++ b/cpp/cuopt_cli.cpp @@ -176,7 +176,7 @@ int run_single_file(const std::string& file_path, } try { - if (memory_backend == cuopt::mathematical_optimization::memory_backend_t::CPU) { + if (cuopt::mathematical_optimization::is_remote_execution_enabled()) { // Remote execution: problem_interface holds a cpu_optimization_problem_t. // Call solve_lp/mip_remote directly so libcuopt_grpc.so is a real DT_NEEDED // dependency of this binary rather than an implicit runtime lookup. From 8511bd039dba987c7fe40c8f4276055c30ed8ebf Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Tue, 28 Jul 2026 14:19:08 -0500 Subject: [PATCH 11/27] style: apply clang-format to cuopt_cli.cpp Co-Authored-By: Claude Sonnet 4.6 Signed-off-by: Ramakrishna Prabhu --- cpp/cuopt_cli.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/cpp/cuopt_cli.cpp b/cpp/cuopt_cli.cpp index 153ee59385..f2ee18980a 100644 --- a/cpp/cuopt_cli.cpp +++ b/cpp/cuopt_cli.cpp @@ -200,16 +200,15 @@ int run_single_file(const std::string& file_path, // Remote execution: problem_interface holds a cpu_optimization_problem_t. // Call solve_lp/mip_remote directly so libcuopt_grpc.so is a real DT_NEEDED // dependency of this binary rather than an implicit runtime lookup. - auto* cpu_prob = static_cast*>(problem_interface.get()); + auto* cpu_prob = + static_cast*>( + problem_interface.get()); if (is_mip) { auto& mip_settings = settings.get_mip_settings(); - auto solution = - cuopt::mathematical_optimization::solve_mip_remote(*cpu_prob, mip_settings); + auto solution = cuopt::mathematical_optimization::solve_mip_remote(*cpu_prob, mip_settings); } else { auto& lp_settings = settings.get_pdlp_settings(); - auto solution = - cuopt::mathematical_optimization::solve_lp_remote(*cpu_prob, lp_settings); + auto solution = cuopt::mathematical_optimization::solve_lp_remote(*cpu_prob, lp_settings); } } else if (is_mip) { auto& mip_settings = settings.get_mip_settings(); From f2b22e7d8868f031cd8b0a44cad2d774942f117a Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Tue, 28 Jul 2026 15:00:58 -0500 Subject: [PATCH 12/27] fix(build): link nccl_external into cuopt_lp 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 Signed-off-by: Ramakrishna Prabhu --- cpp/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 1ab920cce9..fbd8646f3d 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -677,6 +677,7 @@ target_link_libraries(cuopt_lp ${CUOPT_PRIVATE_CUDA_LIBS} OpenMP::OpenMP_CUDA simde::simde + nccl_external ) target_link_libraries(cuopt_lp PRIVATE $) add_dependencies(cuopt_lp PSLP) From 90ee992ebec2f7e007c4ada26692521a369497b0 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Wed, 29 Jul 2026 09:25:04 -0500 Subject: [PATCH 13/27] =?UTF-8?q?fix(build):=20break=20circular=20libcuopt?= =?UTF-8?q?=5Flp=20=E2=86=92=20libcuopt=5Fgrpc=20symbol=20dependency?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Signed-off-by: Ramakrishna Prabhu --- cpp/CMakeLists.txt | 11 ++++- .../remote_solve_registry.hpp | 40 +++++++++++++++++++ cpp/src/grpc/client/grpc_registration.cpp | 19 +++++++++ cpp/src/mip_heuristics/solve.cu | 14 +++---- cpp/src/pdlp/CMakeLists.txt | 1 + cpp/src/pdlp/remote_solve_registry.cpp | 17 ++++++++ cpp/src/pdlp/solve.cu | 13 +++--- 7 files changed, 97 insertions(+), 18 deletions(-) create mode 100644 cpp/include/cuopt/mathematical_optimization/remote_solve_registry.hpp create mode 100644 cpp/src/grpc/client/grpc_registration.cpp create mode 100644 cpp/src/pdlp/remote_solve_registry.cpp diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index fbd8646f3d..8c9b7f7f5d 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -544,6 +544,7 @@ if (NOT SKIP_GRPC_BUILD) src/grpc/client/grpc_client_env.cpp src/grpc/client/cython_grpc_client.cpp src/grpc/client/solve_remote.cpp + src/grpc/client/grpc_registration.cpp ) # Always keep NDEBUG defined for gRPC infrastructure files so that abseil @@ -861,7 +862,15 @@ endif (BUILD_TESTS) # Links publicly against the four component shared libs so that -lcuopt keeps working for # all existing consumers. -file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/cuopt_umbrella.cpp" "// thin umbrella\n") +# Reference cuOptDestroyProblem (from libcuopt_lp.so) so --as-needed keeps +# libcuopt_lp.so in DT_NEEDED, preserving the -lcuopt C API contract for users +# who compile C programs with -lcuopt. +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/cuopt_umbrella.cpp" [=[ +// Thin umbrella. References cuOptDestroyProblem so --as-needed keeps +// libcuopt_lp.so in DT_NEEDED, preserving the -lcuopt C API contract. +extern "C" void cuOptDestroyProblem(void*); +__attribute__((used)) void (* const _cuopt_lp_anchor)(void*) = &cuOptDestroyProblem; +]=]) add_library(cuopt SHARED "${CMAKE_CURRENT_BINARY_DIR}/cuopt_umbrella.cpp") add_library(cuopt::cuopt ALIAS cuopt) diff --git a/cpp/include/cuopt/mathematical_optimization/remote_solve_registry.hpp b/cpp/include/cuopt/mathematical_optimization/remote_solve_registry.hpp new file mode 100644 index 0000000000..c0723d49c4 --- /dev/null +++ b/cpp/include/cuopt/mathematical_optimization/remote_solve_registry.hpp @@ -0,0 +1,40 @@ +/* clang-format off */ +/* + * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +/* clang-format on */ + +#pragma once + +#include +#include + +// Forward declarations — full types live in libcuopt_lp / libcuopt_grpc headers. +namespace cuopt::mathematical_optimization { + +template +class cpu_optimization_problem_t; + +template +struct pdlp_solver_settings_t; + +template +struct mip_solver_settings_t; + +// Function pointer types — only the instantiation is supported. +using solve_lp_remote_fn_t = std::unique_ptr> (*)( + cpu_optimization_problem_t const&, pdlp_solver_settings_t const&); + +using solve_mip_remote_fn_t = std::unique_ptr> (*)( + cpu_optimization_problem_t const&, mip_solver_settings_t const&); + +// 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); + +} // namespace cuopt::mathematical_optimization diff --git a/cpp/src/grpc/client/grpc_registration.cpp b/cpp/src/grpc/client/grpc_registration.cpp new file mode 100644 index 0000000000..5bf4ef0c45 --- /dev/null +++ b/cpp/src/grpc/client/grpc_registration.cpp @@ -0,0 +1,19 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +// Registers the gRPC-based remote solve implementations with libcuopt_lp.so +// at dynamic-link time (before any user code runs). This breaks the circular +// dependency: libcuopt_lp.so holds nullable function pointers rather than a +// hard reference to symbols in libcuopt_grpc.so. + +#include +#include + +namespace { +__attribute__((constructor)) void register_grpc_remote_solvers() +{ + cuopt::mathematical_optimization::register_remote_solvers( + &cuopt::mathematical_optimization::solve_lp_remote, + &cuopt::mathematical_optimization::solve_mip_remote); +} +} // namespace diff --git a/cpp/src/mip_heuristics/solve.cu b/cpp/src/mip_heuristics/solve.cu index 64d78efbc0..07db7f54c7 100644 --- a/cpp/src/mip_heuristics/solve.cu +++ b/cpp/src/mip_heuristics/solve.cu @@ -6,7 +6,7 @@ /* clang-format on */ #include -#include +#include #include #include @@ -908,20 +908,16 @@ std::unique_ptr> solve_mip( try { // Check if remote execution is enabled (always uses CPU backend) -#ifdef CUOPT_ENABLE_GRPC if (is_remote_execution_enabled()) { auto* cpu_prob = dynamic_cast*>(problem_interface); cuopt_expects(cpu_prob != nullptr, error_type_t::ValidationError, "Remote execution requires CPU memory backend"); - return solve_mip_remote(*cpu_prob, settings); + cuopt_expects(g_solve_mip_remote_fn != nullptr, + error_type_t::RuntimeError, + "Remote execution requires the gRPC component (libcuopt_grpc.so) to be loaded"); + return g_solve_mip_remote_fn(*cpu_prob, settings); } -#else - cuopt_expects( - !is_remote_execution_enabled(), - error_type_t::ValidationError, - "Remote execution was requested, but this build was compiled without gRPC support"); -#endif // Local execution - dispatch to appropriate overload based on problem type auto* cpu_prob = dynamic_cast*>(problem_interface); diff --git a/cpp/src/pdlp/CMakeLists.txt b/cpp/src/pdlp/CMakeLists.txt index f9b40b34c1..6b5ac9ebf6 100644 --- a/cpp/src/pdlp/CMakeLists.txt +++ b/cpp/src/pdlp/CMakeLists.txt @@ -5,6 +5,7 @@ # Core LP files always included set(LP_CORE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/remote_solve_registry.cpp ${CMAKE_CURRENT_SOURCE_DIR}/solver_settings.cu ${CMAKE_CURRENT_SOURCE_DIR}/optimization_problem.cu ${CMAKE_CURRENT_SOURCE_DIR}/cpu_optimization_problem.cpp diff --git a/cpp/src/pdlp/remote_solve_registry.cpp b/cpp/src/pdlp/remote_solve_registry.cpp new file mode 100644 index 0000000000..511c0046d0 --- /dev/null +++ b/cpp/src/pdlp/remote_solve_registry.cpp @@ -0,0 +1,17 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +#include + +namespace cuopt::mathematical_optimization { + +solve_lp_remote_fn_t g_solve_lp_remote_fn = nullptr; +solve_mip_remote_fn_t g_solve_mip_remote_fn = nullptr; + +void register_remote_solvers(solve_lp_remote_fn_t lp_fn, solve_mip_remote_fn_t mip_fn) +{ + g_solve_lp_remote_fn = lp_fn; + g_solve_mip_remote_fn = mip_fn; +} + +} // namespace cuopt::mathematical_optimization diff --git a/cpp/src/pdlp/solve.cu b/cpp/src/pdlp/solve.cu index 2f33a7f8ae..9e643ba865 100644 --- a/cpp/src/pdlp/solve.cu +++ b/cpp/src/pdlp/solve.cu @@ -6,7 +6,7 @@ /* clang-format on */ #include -#include +#include #include #include #include @@ -2682,7 +2682,6 @@ std::unique_ptr> solve_lp( "problem_interface cannot be null"); // Check if remote execution is enabled (always uses CPU backend) -#ifdef CUOPT_ENABLE_GRPC if (is_remote_execution_enabled()) { cuopt_expects(!is_batch_mode, error_type_t::ValidationError, @@ -2692,13 +2691,11 @@ std::unique_ptr> solve_lp( cuopt_expects(cpu_prob != nullptr, error_type_t::ValidationError, "Remote execution requires CPU memory backend"); - return solve_lp_remote(*cpu_prob, settings); + cuopt_expects(g_solve_lp_remote_fn != nullptr, + error_type_t::RuntimeError, + "Remote execution requires the gRPC component (libcuopt_grpc.so) to be loaded"); + return g_solve_lp_remote_fn(*cpu_prob, settings); } -#else - cuopt_expects(!is_remote_execution_enabled(), - error_type_t::ValidationError, - "Remote execution was requested, but this build was compiled without gRPC support"); -#endif // Local execution - dispatch to appropriate overload based on problem type auto* cpu_prob = dynamic_cast*>(problem_interface); From a83e0f4cb2581462c01000dff024db73847daef9 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Wed, 29 Jul 2026 13:00:52 -0500 Subject: [PATCH 14/27] fix(build): add libcuopt_grpc.so anchor to umbrella DT_NEEDED 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 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 Signed-off-by: Ramakrishna Prabhu --- cpp/CMakeLists.txt | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 8c9b7f7f5d..97d61cf0e3 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -862,16 +862,33 @@ endif (BUILD_TESTS) # Links publicly against the four component shared libs so that -lcuopt keeps working for # all existing consumers. -# Reference cuOptDestroyProblem (from libcuopt_lp.so) so --as-needed keeps -# libcuopt_lp.so in DT_NEEDED, preserving the -lcuopt C API contract for users -# who compile C programs with -lcuopt. -file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/cuopt_umbrella.cpp" [=[ -// Thin umbrella. References cuOptDestroyProblem so --as-needed keeps -// libcuopt_lp.so in DT_NEEDED, preserving the -lcuopt C API contract. -extern "C" void cuOptDestroyProblem(void*); -__attribute__((used)) void (* const _cuopt_lp_anchor)(void*) = &cuOptDestroyProblem; +# 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 +#include +namespace cuopt::mathematical_optimization { +__attribute__((used)) solve_lp_remote_fn_t const _cuopt_grpc_anchor = + &solve_lp_remote; +} +]=]) +endif() + +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/cuopt_umbrella.cpp" "${_UMBRELLA_SRC}") + add_library(cuopt SHARED "${CMAKE_CURRENT_BINARY_DIR}/cuopt_umbrella.cpp") add_library(cuopt::cuopt ALIAS cuopt) From 0f3b7e1bf8dead5016bcc55184416a3f31d9b572 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Wed, 29 Jul 2026 17:43:55 -0500 Subject: [PATCH 15/27] fix(build): load libcuopt_grpc.so on demand via dlopen for remote execution 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 Signed-off-by: Ramakrishna Prabhu --- ci/test_skills_assets.sh | 2 +- cpp/src/mip_heuristics/solve.cu | 5 +++++ cpp/src/pdlp/solve.cu | 5 +++++ docs/cuopt/source/cuopt-c/convex/examples/Makefile | 2 +- docs/cuopt/source/cuopt-c/mip/examples/Makefile | 2 +- 5 files changed, 13 insertions(+), 3 deletions(-) diff --git a/ci/test_skills_assets.sh b/ci/test_skills_assets.sh index c75645cb93..3436d721b9 100755 --- a/ci/test_skills_assets.sh +++ b/ci/test_skills_assets.sh @@ -111,7 +111,7 @@ if [[ -n "${CONDA_PREFIX:-}" ]]; then base=$(basename "$cfile" .c) rel="${cfile#"$REPO_ROOT/"}" log "Building and running C asset: $rel" - if ! (cd "$dir" && "${CC}" -I"${INCLUDE_PATH}" -L"${LIB_PATH}" -o "$base" "$(basename "$cfile")" -lcuopt); then + if ! (cd "$dir" && "${CC}" -I"${INCLUDE_PATH}" -L"${LIB_PATH}" -o "$base" "$(basename "$cfile")" -lcuopt -lcuopt_lp); then FAILED+=("$rel (build)") log "FAIL: $rel (build)" continue diff --git a/cpp/src/mip_heuristics/solve.cu b/cpp/src/mip_heuristics/solve.cu index 07db7f54c7..370ae5ea4b 100644 --- a/cpp/src/mip_heuristics/solve.cu +++ b/cpp/src/mip_heuristics/solve.cu @@ -8,6 +8,8 @@ #include #include +#include + #include #include #include @@ -913,6 +915,9 @@ std::unique_ptr> solve_mip( cuopt_expects(cpu_prob != nullptr, error_type_t::ValidationError, "Remote execution requires CPU memory backend"); + if (g_solve_mip_remote_fn == nullptr) { + dlopen("libcuopt_grpc.so", RTLD_NOW | RTLD_GLOBAL); + } cuopt_expects(g_solve_mip_remote_fn != nullptr, error_type_t::RuntimeError, "Remote execution requires the gRPC component (libcuopt_grpc.so) to be loaded"); diff --git a/cpp/src/pdlp/solve.cu b/cpp/src/pdlp/solve.cu index 9e643ba865..d46c01e047 100644 --- a/cpp/src/pdlp/solve.cu +++ b/cpp/src/pdlp/solve.cu @@ -7,6 +7,8 @@ #include #include + +#include #include #include #include @@ -2691,6 +2693,9 @@ std::unique_ptr> solve_lp( cuopt_expects(cpu_prob != nullptr, error_type_t::ValidationError, "Remote execution requires CPU memory backend"); + if (g_solve_lp_remote_fn == nullptr) { + dlopen("libcuopt_grpc.so", RTLD_NOW | RTLD_GLOBAL); + } cuopt_expects(g_solve_lp_remote_fn != nullptr, error_type_t::RuntimeError, "Remote execution requires the gRPC component (libcuopt_grpc.so) to be loaded"); diff --git a/docs/cuopt/source/cuopt-c/convex/examples/Makefile b/docs/cuopt/source/cuopt-c/convex/examples/Makefile index fef30244f1..59bc0e0bb0 100644 --- a/docs/cuopt/source/cuopt-c/convex/examples/Makefile +++ b/docs/cuopt/source/cuopt-c/convex/examples/Makefile @@ -46,7 +46,7 @@ CC = gcc # Compiler flags CFLAGS = -I$(INCLUDE_PATH) -Wall -Wextra -LDFLAGS = -L$(LIBCUOPT_LIBRARY_PATH) -lcuopt -Wl,-rpath,$(LIBCUOPT_LIBRARY_PATH) +LDFLAGS = -L$(LIBCUOPT_LIBRARY_PATH) -lcuopt -lcuopt_lp -Wl,-rpath,$(LIBCUOPT_LIBRARY_PATH) # Automatically discover all C source files in current directory SOURCES = $(wildcard *.c) diff --git a/docs/cuopt/source/cuopt-c/mip/examples/Makefile b/docs/cuopt/source/cuopt-c/mip/examples/Makefile index bc287f0d49..f8d91d2f5f 100644 --- a/docs/cuopt/source/cuopt-c/mip/examples/Makefile +++ b/docs/cuopt/source/cuopt-c/mip/examples/Makefile @@ -22,7 +22,7 @@ CC = gcc # Compiler flags CFLAGS = -I$(INCLUDE_PATH) -Wall -Wextra -LDFLAGS = -L$(LIBCUOPT_LIBRARY_PATH) -lcuopt -Wl,-rpath,$(LIBCUOPT_LIBRARY_PATH) +LDFLAGS = -L$(LIBCUOPT_LIBRARY_PATH) -lcuopt -lcuopt_lp -Wl,-rpath,$(LIBCUOPT_LIBRARY_PATH) # Automatically discover all C source files in current directory SOURCES = $(wildcard *.c) From b872bfb3da840a3dc189c168ace2d67326a8d90f Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Wed, 29 Jul 2026 18:33:25 -0500 Subject: [PATCH 16/27] style: apply clang-format and fix copyright years Co-Authored-By: Claude Sonnet 4.6 Signed-off-by: Ramakrishna Prabhu --- cpp/src/mip_heuristics/solve.cu | 4 +--- cpp/src/pdlp/solve.cu | 4 +--- docs/cuopt/source/cuopt-c/convex/examples/Makefile | 2 +- docs/cuopt/source/cuopt-c/mip/examples/Makefile | 2 +- 4 files changed, 4 insertions(+), 8 deletions(-) diff --git a/cpp/src/mip_heuristics/solve.cu b/cpp/src/mip_heuristics/solve.cu index 370ae5ea4b..fee37530bd 100644 --- a/cpp/src/mip_heuristics/solve.cu +++ b/cpp/src/mip_heuristics/solve.cu @@ -915,9 +915,7 @@ std::unique_ptr> solve_mip( cuopt_expects(cpu_prob != nullptr, error_type_t::ValidationError, "Remote execution requires CPU memory backend"); - if (g_solve_mip_remote_fn == nullptr) { - dlopen("libcuopt_grpc.so", RTLD_NOW | RTLD_GLOBAL); - } + if (g_solve_mip_remote_fn == nullptr) { dlopen("libcuopt_grpc.so", RTLD_NOW | RTLD_GLOBAL); } cuopt_expects(g_solve_mip_remote_fn != nullptr, error_type_t::RuntimeError, "Remote execution requires the gRPC component (libcuopt_grpc.so) to be loaded"); diff --git a/cpp/src/pdlp/solve.cu b/cpp/src/pdlp/solve.cu index d46c01e047..3e0ac5a070 100644 --- a/cpp/src/pdlp/solve.cu +++ b/cpp/src/pdlp/solve.cu @@ -2693,9 +2693,7 @@ std::unique_ptr> solve_lp( cuopt_expects(cpu_prob != nullptr, error_type_t::ValidationError, "Remote execution requires CPU memory backend"); - if (g_solve_lp_remote_fn == nullptr) { - dlopen("libcuopt_grpc.so", RTLD_NOW | RTLD_GLOBAL); - } + if (g_solve_lp_remote_fn == nullptr) { dlopen("libcuopt_grpc.so", RTLD_NOW | RTLD_GLOBAL); } cuopt_expects(g_solve_lp_remote_fn != nullptr, error_type_t::RuntimeError, "Remote execution requires the gRPC component (libcuopt_grpc.so) to be loaded"); diff --git a/docs/cuopt/source/cuopt-c/convex/examples/Makefile b/docs/cuopt/source/cuopt-c/convex/examples/Makefile index 59bc0e0bb0..f54dfb1312 100644 --- a/docs/cuopt/source/cuopt-c/convex/examples/Makefile +++ b/docs/cuopt/source/cuopt-c/convex/examples/Makefile @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/docs/cuopt/source/cuopt-c/mip/examples/Makefile b/docs/cuopt/source/cuopt-c/mip/examples/Makefile index f8d91d2f5f..bc1b5983ae 100644 --- a/docs/cuopt/source/cuopt-c/mip/examples/Makefile +++ b/docs/cuopt/source/cuopt-c/mip/examples/Makefile @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); From 7b32d1ba0de63992d9850544e4575c8c12508671 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Thu, 30 Jul 2026 11:29:40 -0500 Subject: [PATCH 17/27] fix(build): allow shlib undefined for split libs, ignore pypi in linkcheck 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 Signed-off-by: Ramakrishna Prabhu --- docs/cuopt/source/conf.py | 4 +++- docs/cuopt/source/cuopt-c/convex/examples/Makefile | 2 +- docs/cuopt/source/cuopt-c/mip/examples/Makefile | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/cuopt/source/conf.py b/docs/cuopt/source/conf.py index b4749d0167..33bc4c3da1 100644 --- a/docs/cuopt/source/conf.py +++ b/docs/cuopt/source/conf.py @@ -365,7 +365,7 @@ def write_project_json(app, _builder): linkcheck_workers = 5 linkcheck_rate_limit_timeout = 60 -# GitHub and GitLab link checker exceptions +# GitHub, GitLab, and PyPI link checker exceptions linkcheck_ignore = [ # GitHub (Rate Limited) r"https://github\.com/.*", @@ -377,6 +377,8 @@ def write_project_json(app, _builder): r"https://api\.gitlab\.com/.*", r"https://gitlab\.org/.*", r"https://api\.gitlab\.org/.*", + # PyPI (Unreliable in CI networks) + r"https://pypi\.org/.*", ] diff --git a/docs/cuopt/source/cuopt-c/convex/examples/Makefile b/docs/cuopt/source/cuopt-c/convex/examples/Makefile index f54dfb1312..d663bb518d 100644 --- a/docs/cuopt/source/cuopt-c/convex/examples/Makefile +++ b/docs/cuopt/source/cuopt-c/convex/examples/Makefile @@ -46,7 +46,7 @@ CC = gcc # Compiler flags CFLAGS = -I$(INCLUDE_PATH) -Wall -Wextra -LDFLAGS = -L$(LIBCUOPT_LIBRARY_PATH) -lcuopt -lcuopt_lp -Wl,-rpath,$(LIBCUOPT_LIBRARY_PATH) +LDFLAGS = -L$(LIBCUOPT_LIBRARY_PATH) -lcuopt -lcuopt_lp -Wl,--allow-shlib-undefined -Wl,-rpath,$(LIBCUOPT_LIBRARY_PATH) # Automatically discover all C source files in current directory SOURCES = $(wildcard *.c) diff --git a/docs/cuopt/source/cuopt-c/mip/examples/Makefile b/docs/cuopt/source/cuopt-c/mip/examples/Makefile index bc1b5983ae..dace6ebd11 100644 --- a/docs/cuopt/source/cuopt-c/mip/examples/Makefile +++ b/docs/cuopt/source/cuopt-c/mip/examples/Makefile @@ -22,7 +22,7 @@ CC = gcc # Compiler flags CFLAGS = -I$(INCLUDE_PATH) -Wall -Wextra -LDFLAGS = -L$(LIBCUOPT_LIBRARY_PATH) -lcuopt -lcuopt_lp -Wl,-rpath,$(LIBCUOPT_LIBRARY_PATH) +LDFLAGS = -L$(LIBCUOPT_LIBRARY_PATH) -lcuopt -lcuopt_lp -Wl,--allow-shlib-undefined -Wl,-rpath,$(LIBCUOPT_LIBRARY_PATH) # Automatically discover all C source files in current directory SOURCES = $(wildcard *.c) From 80c1c341b39a25a556770279d2bf05e47711a813 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Thu, 30 Jul 2026 14:46:26 -0500 Subject: [PATCH 18/27] fix(build): add cuopt_lp/cuopt_base to pip RPATH in libcuopt CMakeLists 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 Signed-off-by: Ramakrishna Prabhu --- python/libcuopt/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/python/libcuopt/CMakeLists.txt b/python/libcuopt/CMakeLists.txt index 4d24169645..3c31190fe2 100644 --- a/python/libcuopt/CMakeLists.txt +++ b/python/libcuopt/CMakeLists.txt @@ -96,5 +96,7 @@ endif() message(STATUS "libcuopt: Final RPATH = ${rpaths}") 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) From 91f5b8477429b3e901c8e8493e5154f32fffe3f7 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Wed, 5 Aug 2026 14:56:13 -0500 Subject: [PATCH 19/27] =?UTF-8?q?fix(build):=20address=20review=20?= =?UTF-8?q?=E2=80=94=20DT=5FNEEDED=20anchors,=20export=20names,=20registry?= =?UTF-8?q?=20races?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add umbrella anchors for cuopt_base and cuopt_routing. libcuopt.so listed only libcuopt_lp.so and libcuopt_grpc.so in DT_NEEDED; with --as-needed the base and routing components were dropped, so routing vanished entirely under SKIP_GRPC_BUILD. - Export components as cuopt::base/routing/lp/grpc via EXPORT_NAME, matching the names advertised in the package doc string. - Make the remote-solve registry slots std::atomic. The gRPC ELF constructor publishes them during a lazy dlopen while other threads read them. - Move the lazy dlopen into ensure_remote_solvers_loaded(), shared by the LP and MIP dispatchers. - Validate the CPU-problem downcast in cuopt_cli instead of static_cast. - Add cuopt_routing and cuopt_grpc to the pip wheel RPATH list. - Fall back to "unknown" for GIT_COMMIT_HASH in non-git source trees. Co-Authored-By: Claude Opus 5 (1M context) Signed-off-by: Ramakrishna Prabhu --- cpp/CMakeLists.txt | 33 ++++++++++++- cpp/cuopt_cli.cpp | 6 ++- .../remote_solve_registry.hpp | 46 +++++++++++++++---- cpp/src/mip_heuristics/solve.cu | 9 ++-- cpp/src/pdlp/remote_solve_registry.cpp | 18 ++++++-- cpp/src/pdlp/solve.cu | 8 ++-- python/libcuopt/CMakeLists.txt | 10 ++-- 7 files changed, 101 insertions(+), 29 deletions(-) diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 97d61cf0e3..6cf78e150a 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -594,7 +594,12 @@ execute_process( 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() message("-- Building with GIT_COMMIT_HASH = '${GIT_COMMIT_HASH}'") configure_file( @@ -871,7 +876,21 @@ set(_UMBRELLA_SRC [=[ // each library in DT_NEEDED. extern "C" void cuOptDestroyProblem(void*); // libcuopt_lp.so __attribute__((used)) void (* const _cuopt_lp_anchor)(void*) = &cuOptDestroyProblem; + +namespace cuopt { void print_version_info(int); } // libcuopt_base.so +__attribute__((used)) void (* const _cuopt_base_anchor)(int) = &cuopt::print_version_info; +]=]) + +if(NOT SKIP_ROUTING_BUILD) + string(APPEND _UMBRELLA_SRC [=[ +#include +namespace cuopt::routing { +__attribute__((used)) assignment_t (* const _cuopt_routing_anchor)( + data_model_view_t const&, solver_settings_t const&) = + &solve; +} ]=]) +endif() if(NOT SKIP_GRPC_BUILD) string(APPEND _UMBRELLA_SRC [=[ @@ -960,6 +979,16 @@ endif() if(NOT SKIP_GRPC_BUILD) list(APPEND CUOPT_COMPONENT_TARGETS cuopt_grpc) endif() + +# Export as cuopt::base / cuopt::routing / cuopt::lp / cuopt::grpc, matching the +# build-tree aliases. +set(CUOPT_COMPONENT_EXPORT_NAMES "") +foreach(_component ${CUOPT_COMPONENT_TARGETS}) + string(REPLACE "cuopt_" "" _export_name "${_component}") + set_target_properties(${_component} PROPERTIES EXPORT_NAME ${_export_name}) + list(APPEND CUOPT_COMPONENT_EXPORT_NAMES ${_export_name}) +endforeach() + install(TARGETS ${CUOPT_COMPONENT_TARGETS} DESTINATION ${_LIB_DEST} COMPONENT runtime @@ -1003,7 +1032,7 @@ Umbrella target: cuopt::cuopt (links all component libs — backward-compatibl rapids_export(INSTALL cuopt EXPORT_SET cuopt-exports - GLOBAL_TARGETS cuopt ${CUOPT_COMPONENT_TARGETS} + GLOBAL_TARGETS cuopt ${CUOPT_COMPONENT_EXPORT_NAMES} NAMESPACE cuopt:: DOCUMENTATION doc_string ) @@ -1012,7 +1041,7 @@ rapids_export(INSTALL cuopt # - build export ------------------------------------------------------------------------------- rapids_export(BUILD cuopt EXPORT_SET cuopt-exports - GLOBAL_TARGETS cuopt ${CUOPT_COMPONENT_TARGETS} + GLOBAL_TARGETS cuopt ${CUOPT_COMPONENT_EXPORT_NAMES} NAMESPACE cuopt:: DOCUMENTATION doc_string ) diff --git a/cpp/cuopt_cli.cpp b/cpp/cuopt_cli.cpp index f2ee18980a..3fa3384b43 100644 --- a/cpp/cuopt_cli.cpp +++ b/cpp/cuopt_cli.cpp @@ -201,8 +201,12 @@ int run_single_file(const std::string& file_path, // Call solve_lp/mip_remote directly so libcuopt_grpc.so is a real DT_NEEDED // dependency of this binary rather than an implicit runtime lookup. auto* cpu_prob = - static_cast*>( + dynamic_cast*>( problem_interface.get()); + if (cpu_prob == nullptr) { + CUOPT_LOG_ERROR("Remote execution requires the CPU memory backend."); + return -1; + } if (is_mip) { auto& mip_settings = settings.get_mip_settings(); auto solution = cuopt::mathematical_optimization::solve_mip_remote(*cpu_prob, mip_settings); diff --git a/cpp/include/cuopt/mathematical_optimization/remote_solve_registry.hpp b/cpp/include/cuopt/mathematical_optimization/remote_solve_registry.hpp index c0723d49c4..c4d3b9feb4 100644 --- a/cpp/include/cuopt/mathematical_optimization/remote_solve_registry.hpp +++ b/cpp/include/cuopt/mathematical_optimization/remote_solve_registry.hpp @@ -8,6 +8,8 @@ #pragma once #include + +#include #include // Forward declarations — full types live in libcuopt_lp / libcuopt_grpc headers. @@ -17,24 +19,52 @@ template class cpu_optimization_problem_t; template -struct pdlp_solver_settings_t; +class pdlp_solver_settings_t; template -struct mip_solver_settings_t; +class mip_solver_settings_t; -// Function pointer types — only the instantiation is supported. +/** + * @brief Remote LP solve entry point implemented by libcuopt_grpc.so. + * + * The returned solution is owned by the caller. The callback must not propagate + * exceptions across the component boundary. Only the `` + * instantiation is supported. + */ using solve_lp_remote_fn_t = std::unique_ptr> (*)( cpu_optimization_problem_t const&, pdlp_solver_settings_t const&); +/** + * @brief Remote MIP solve entry point implemented by libcuopt_grpc.so. + * + * Same ownership and exception contract as @ref solve_lp_remote_fn_t. + */ using solve_mip_remote_fn_t = std::unique_ptr> (*)( cpu_optimization_problem_t const&, mip_solver_settings_t const&); -// 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; +/** + * @brief Registry slots defined in libcuopt_lp.so (remote_solve_registry.cpp). + * + * Null until libcuopt_grpc.so is loaded and calls register_remote_solvers(). Atomic + * because the registering ELF constructor runs on whichever thread triggers the lazy + * dlopen while other threads may be reading the slots. + */ +extern std::atomic g_solve_lp_remote_fn; +extern std::atomic g_solve_mip_remote_fn; -// Called by libcuopt_grpc.so's constructor to wire up the real implementations. +/** + * @brief Wire up the real remote-solve implementations. + * + * Called by libcuopt_grpc.so's ELF constructor. Thread-safe. + */ void register_remote_solvers(solve_lp_remote_fn_t lp_fn, solve_mip_remote_fn_t mip_fn); +/** + * @brief Load libcuopt_grpc.so on demand so its constructor populates the registry. + * + * Idempotent and thread-safe; a failed load leaves the registry slots null so callers + * can report the failure themselves. + */ +void ensure_remote_solvers_loaded(); + } // namespace cuopt::mathematical_optimization diff --git a/cpp/src/mip_heuristics/solve.cu b/cpp/src/mip_heuristics/solve.cu index fee37530bd..474dadee28 100644 --- a/cpp/src/mip_heuristics/solve.cu +++ b/cpp/src/mip_heuristics/solve.cu @@ -8,8 +8,6 @@ #include #include -#include - #include #include #include @@ -915,11 +913,12 @@ std::unique_ptr> solve_mip( cuopt_expects(cpu_prob != nullptr, error_type_t::ValidationError, "Remote execution requires CPU memory backend"); - if (g_solve_mip_remote_fn == nullptr) { dlopen("libcuopt_grpc.so", RTLD_NOW | RTLD_GLOBAL); } - cuopt_expects(g_solve_mip_remote_fn != nullptr, + ensure_remote_solvers_loaded(); + auto* remote_fn = g_solve_mip_remote_fn.load(std::memory_order_acquire); + cuopt_expects(remote_fn != nullptr, error_type_t::RuntimeError, "Remote execution requires the gRPC component (libcuopt_grpc.so) to be loaded"); - return g_solve_mip_remote_fn(*cpu_prob, settings); + return remote_fn(*cpu_prob, settings); } // Local execution - dispatch to appropriate overload based on problem type diff --git a/cpp/src/pdlp/remote_solve_registry.cpp b/cpp/src/pdlp/remote_solve_registry.cpp index 511c0046d0..900c2abde6 100644 --- a/cpp/src/pdlp/remote_solve_registry.cpp +++ b/cpp/src/pdlp/remote_solve_registry.cpp @@ -3,15 +3,25 @@ #include +#include + namespace cuopt::mathematical_optimization { -solve_lp_remote_fn_t g_solve_lp_remote_fn = nullptr; -solve_mip_remote_fn_t g_solve_mip_remote_fn = nullptr; +std::atomic g_solve_lp_remote_fn{nullptr}; +std::atomic g_solve_mip_remote_fn{nullptr}; void register_remote_solvers(solve_lp_remote_fn_t lp_fn, solve_mip_remote_fn_t mip_fn) { - g_solve_lp_remote_fn = lp_fn; - g_solve_mip_remote_fn = mip_fn; + g_solve_lp_remote_fn.store(lp_fn, std::memory_order_release); + g_solve_mip_remote_fn.store(mip_fn, std::memory_order_release); +} + +void ensure_remote_solvers_loaded() +{ + if (g_solve_lp_remote_fn.load(std::memory_order_acquire) != nullptr) { return; } + // The constructor in libcuopt_grpc.so calls register_remote_solvers(). dlopen is + // itself thread-safe and refcounted, so a concurrent second call is harmless. + dlopen("libcuopt_grpc.so", RTLD_NOW | RTLD_GLOBAL); } } // namespace cuopt::mathematical_optimization diff --git a/cpp/src/pdlp/solve.cu b/cpp/src/pdlp/solve.cu index 3e0ac5a070..cdbbc6f19f 100644 --- a/cpp/src/pdlp/solve.cu +++ b/cpp/src/pdlp/solve.cu @@ -8,7 +8,6 @@ #include #include -#include #include #include #include @@ -2693,11 +2692,12 @@ std::unique_ptr> solve_lp( cuopt_expects(cpu_prob != nullptr, error_type_t::ValidationError, "Remote execution requires CPU memory backend"); - if (g_solve_lp_remote_fn == nullptr) { dlopen("libcuopt_grpc.so", RTLD_NOW | RTLD_GLOBAL); } - cuopt_expects(g_solve_lp_remote_fn != nullptr, + ensure_remote_solvers_loaded(); + auto* remote_fn = g_solve_lp_remote_fn.load(std::memory_order_acquire); + cuopt_expects(remote_fn != nullptr, error_type_t::RuntimeError, "Remote execution requires the gRPC component (libcuopt_grpc.so) to be loaded"); - return g_solve_lp_remote_fn(*cpu_prob, settings); + return remote_fn(*cpu_prob, settings); } // Local execution - dispatch to appropriate overload based on problem type diff --git a/python/libcuopt/CMakeLists.txt b/python/libcuopt/CMakeLists.txt index 3c31190fe2..499eca9b67 100644 --- a/python/libcuopt/CMakeLists.txt +++ b/python/libcuopt/CMakeLists.txt @@ -95,8 +95,8 @@ else() endif() message(STATUS "libcuopt: Final RPATH = ${rpaths}") -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) +foreach(_target cuopt cuopt_base cuopt_routing cuopt_lp cuopt_grpc cuopt_cli cuopt_grpc_server) + if(TARGET ${_target}) + set_property(TARGET ${_target} PROPERTY INSTALL_RPATH ${rpaths} APPEND) + endif() +endforeach() From fe01c17ca0a32754b421f69630f7ba4e8328cbd5 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Thu, 6 Aug 2026 10:30:23 -0500 Subject: [PATCH 20/27] fix(build): gate remote-solver registry on an explicit ready flag register_remote_solvers() stored the LP callback before the MIP callback while ensure_remote_solvers_loaded() used the LP slot as its readiness sentinel. A concurrent MIP solve could observe the LP slot set, skip the lazy dlopen, then read a still-null MIP slot and fail with a spurious "gRPC component not loaded" error. Publish a separate ready flag after both callbacks instead, so readiness does not depend on callback count or store order. Also report dlopen failures via dlerror() rather than discarding them, and link cuopt_lp against ${CMAKE_DL_LIBS} since it calls dlopen directly. Use the documented APPEND-before-PROPERTY form in the pip RPATH loop. Co-Authored-By: Claude Opus 5 (1M context) Signed-off-by: Ramakrishna Prabhu --- cpp/CMakeLists.txt | 1 + .../remote_solve_registry.hpp | 7 +++++++ cpp/src/pdlp/remote_solve_registry.cpp | 17 +++++++++++++---- python/libcuopt/CMakeLists.txt | 2 +- 4 files changed, 22 insertions(+), 5 deletions(-) diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 6cf78e150a..60c00a861f 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -684,6 +684,7 @@ target_link_libraries(cuopt_lp OpenMP::OpenMP_CUDA simde::simde nccl_external + ${CMAKE_DL_LIBS} ) target_link_libraries(cuopt_lp PRIVATE $) add_dependencies(cuopt_lp PSLP) diff --git a/cpp/include/cuopt/mathematical_optimization/remote_solve_registry.hpp b/cpp/include/cuopt/mathematical_optimization/remote_solve_registry.hpp index c4d3b9feb4..c8c301ceb2 100644 --- a/cpp/include/cuopt/mathematical_optimization/remote_solve_registry.hpp +++ b/cpp/include/cuopt/mathematical_optimization/remote_solve_registry.hpp @@ -52,6 +52,13 @@ using solve_mip_remote_fn_t = std::unique_ptr g_solve_lp_remote_fn; extern std::atomic g_solve_mip_remote_fn; +/** + * @brief Readiness flag, published after both callbacks are stored. + * + * Readers must observe this as true before trusting either slot. + */ +extern std::atomic g_remote_solvers_ready; + /** * @brief Wire up the real remote-solve implementations. * diff --git a/cpp/src/pdlp/remote_solve_registry.cpp b/cpp/src/pdlp/remote_solve_registry.cpp index 900c2abde6..12f3e13997 100644 --- a/cpp/src/pdlp/remote_solve_registry.cpp +++ b/cpp/src/pdlp/remote_solve_registry.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 #include +#include #include @@ -9,19 +10,27 @@ namespace cuopt::mathematical_optimization { std::atomic g_solve_lp_remote_fn{nullptr}; std::atomic g_solve_mip_remote_fn{nullptr}; +std::atomic g_remote_solvers_ready{false}; void register_remote_solvers(solve_lp_remote_fn_t lp_fn, solve_mip_remote_fn_t mip_fn) { - g_solve_lp_remote_fn.store(lp_fn, std::memory_order_release); - g_solve_mip_remote_fn.store(mip_fn, std::memory_order_release); + g_solve_lp_remote_fn.store(lp_fn, std::memory_order_relaxed); + g_solve_mip_remote_fn.store(mip_fn, std::memory_order_relaxed); + // Published last with release ordering: a reader that observes the ready flag is + // guaranteed to observe both callbacks. Using a separate flag rather than one of the + // slots keeps the readiness condition independent of how many callbacks there are. + g_remote_solvers_ready.store(true, std::memory_order_release); } void ensure_remote_solvers_loaded() { - if (g_solve_lp_remote_fn.load(std::memory_order_acquire) != nullptr) { return; } + if (g_remote_solvers_ready.load(std::memory_order_acquire)) { return; } // The constructor in libcuopt_grpc.so calls register_remote_solvers(). dlopen is // itself thread-safe and refcounted, so a concurrent second call is harmless. - dlopen("libcuopt_grpc.so", RTLD_NOW | RTLD_GLOBAL); + if (dlopen("libcuopt_grpc.so", RTLD_NOW | RTLD_GLOBAL) == nullptr) { + const char* err = dlerror(); + CUOPT_LOG_DEBUG("Could not load libcuopt_grpc.so: %s", err != nullptr ? err : "unknown error"); + } } } // namespace cuopt::mathematical_optimization diff --git a/python/libcuopt/CMakeLists.txt b/python/libcuopt/CMakeLists.txt index 499eca9b67..b9806d194a 100644 --- a/python/libcuopt/CMakeLists.txt +++ b/python/libcuopt/CMakeLists.txt @@ -97,6 +97,6 @@ message(STATUS "libcuopt: Final RPATH = ${rpaths}") foreach(_target cuopt cuopt_base cuopt_routing cuopt_lp cuopt_grpc cuopt_cli cuopt_grpc_server) if(TARGET ${_target}) - set_property(TARGET ${_target} PROPERTY INSTALL_RPATH ${rpaths} APPEND) + set_property(TARGET ${_target} APPEND PROPERTY INSTALL_RPATH ${rpaths}) endif() endforeach() From 1c4cdbda723dc35387216eae1543ed6ea5f86eb8 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Thu, 6 Aug 2026 16:07:54 -0500 Subject: [PATCH 21/27] build(cmake): rename cuopt_lp to cuopt_mathematical_optimization The component covers LP, QP, SOCP and MIP, so name it for what it is. Renames the target, the shipped libcuopt_mathematical_optimization.so, the cuopt::mathematical_optimization export, and the source-list variable. Also moves the linear algebra sources out of cuopt_base and into the mathematical optimization component: routing resolves only default_logger() and seed_generator::seed_ from base and references no linear algebra symbols, so they do not belong in the shared base. cuopt_base drops to 1.17 MB. Extracts the umbrella translation unit out of CMakeLists.txt into cmake/umbrella.cpp.in, configured with #cmakedefine rather than assembled with string(APPEND). The C++ now lives in a real .cpp file that can be formatted and read on its own. Co-Authored-By: Claude Opus 5 (1M context) Signed-off-by: Ramakrishna Prabhu --- ci/build_wheel_cuopt.sh | 2 +- ci/test_skills_assets.sh | 2 +- conda/recipes/libcuopt/recipe.yaml | 2 +- cpp/CMakeLists.txt | 103 +++++++----------- cpp/cmake/umbrella.cpp.in | 47 ++++++++ .../remote_solve_registry.hpp | 4 +- .../solve_remote.hpp | 4 +- cpp/src/CMakeLists.txt | 8 +- cpp/src/grpc/client/grpc_registration.cpp | 4 +- .../source/cuopt-c/convex/examples/Makefile | 2 +- .../source/cuopt-c/mip/examples/Makefile | 2 +- python/libcuopt/CMakeLists.txt | 2 +- 12 files changed, 101 insertions(+), 81 deletions(-) create mode 100644 cpp/cmake/umbrella.cpp.in diff --git a/ci/build_wheel_cuopt.sh b/ci/build_wheel_cuopt.sh index 449bc19665..79ed9503c2 100755 --- a/ci/build_wheel_cuopt.sh +++ b/ci/build_wheel_cuopt.sh @@ -43,7 +43,7 @@ EXCLUDE_ARGS=( --exclude "libcuopt.so" --exclude "libcuopt_base.so" --exclude "libcuopt_routing.so" - --exclude "libcuopt_lp.so" + --exclude "libcuopt_mathematical_optimization.so" --exclude "libcuopt_grpc.so" --exclude "librapids_logger.so" --exclude "librmm.so" diff --git a/ci/test_skills_assets.sh b/ci/test_skills_assets.sh index 3436d721b9..27f54a2a71 100755 --- a/ci/test_skills_assets.sh +++ b/ci/test_skills_assets.sh @@ -111,7 +111,7 @@ if [[ -n "${CONDA_PREFIX:-}" ]]; then base=$(basename "$cfile" .c) rel="${cfile#"$REPO_ROOT/"}" log "Building and running C asset: $rel" - if ! (cd "$dir" && "${CC}" -I"${INCLUDE_PATH}" -L"${LIB_PATH}" -o "$base" "$(basename "$cfile")" -lcuopt -lcuopt_lp); then + if ! (cd "$dir" && "${CC}" -I"${INCLUDE_PATH}" -L"${LIB_PATH}" -o "$base" "$(basename "$cfile")" -lcuopt -lcuopt_mathematical_optimization); then FAILED+=("$rel (build)") log "FAIL: $rel (build)" continue diff --git a/conda/recipes/libcuopt/recipe.yaml b/conda/recipes/libcuopt/recipe.yaml index 2e1346cfdd..4896c08da5 100644 --- a/conda/recipes/libcuopt/recipe.yaml +++ b/conda/recipes/libcuopt/recipe.yaml @@ -167,7 +167,7 @@ outputs: - lib/libcuopt.so - lib/libcuopt_base.so - lib/libcuopt_routing.so - - lib/libcuopt_lp.so + - lib/libcuopt_mathematical_optimization.so - lib/libcuopt_grpc.so - bin/cuopt_cli - bin/cuopt_grpc_server diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 19c0773145..15fe0302bf 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -494,7 +494,7 @@ endif () set(CUOPT_SRC_FILES) set(CUOPT_BASE_SRC_FILES) set(CUOPT_ROUTING_SRC_FILES) -set(CUOPT_LP_SRC_FILES) +set(CUOPT_MATHEMATICAL_OPTIMIZATION_SRC_FILES) set(MPS_FAST_SRC_FILES) add_subdirectory(src) @@ -506,7 +506,7 @@ set_source_files_properties( if (HOST_LINEINFO) set_source_files_properties( - ${CUOPT_BASE_SRC_FILES} ${CUOPT_ROUTING_SRC_FILES} ${CUOPT_LP_SRC_FILES} + ${CUOPT_BASE_SRC_FILES} ${CUOPT_ROUTING_SRC_FILES} ${CUOPT_MATHEMATICAL_OPTIMIZATION_SRC_FILES} DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTIES COMPILE_OPTIONS "-g1") endif () @@ -524,7 +524,7 @@ endif () # Uses APPEND to preserve any existing per-file options (e.g. -g1 from HOST_LINEINFO). if (DEFINE_ASSERT) set_property( - SOURCE ${CUOPT_BASE_SRC_FILES} ${CUOPT_ROUTING_SRC_FILES} ${CUOPT_LP_SRC_FILES} + SOURCE ${CUOPT_BASE_SRC_FILES} ${CUOPT_ROUTING_SRC_FILES} ${CUOPT_MATHEMATICAL_OPTIMIZATION_SRC_FILES} DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} APPEND PROPERTY COMPILE_OPTIONS "-UNDEBUG") endif () @@ -652,10 +652,10 @@ if(NOT SKIP_ROUTING_BUILD) add_library(cuopt::routing ALIAS cuopt_routing) endif() -# cuopt_lp: LP / MIP / numerical optimization engine -add_library(cuopt_lp SHARED ${CUOPT_LP_SRC_FILES}) -cuopt_configure_component(cuopt_lp) -target_include_directories(cuopt_lp PRIVATE +# cuopt_mathematical_optimization: LP / MIP / numerical optimization engine +add_library(cuopt_mathematical_optimization SHARED ${CUOPT_MATHEMATICAL_OPTIMIZATION_SRC_FILES}) +cuopt_configure_component(cuopt_mathematical_optimization) +target_include_directories(cuopt_mathematical_optimization PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../thirdparty" "${CMAKE_CURRENT_SOURCE_DIR}/src/io" "${CUDSS_INCLUDE}" @@ -663,19 +663,19 @@ target_include_directories(cuopt_lp PRIVATE $<$:${ZLIB_INCLUDE_DIRS}> ) # Adding Papilo as a system include messes up clang's include resolution if papilo is already installed as a conda package -target_include_directories(cuopt_lp PRIVATE +target_include_directories(cuopt_mathematical_optimization PRIVATE "${papilo_SOURCE_DIR}/src" "${papilo_BINARY_DIR}" ) -target_include_directories(cuopt_lp SYSTEM PRIVATE +target_include_directories(cuopt_mathematical_optimization SYSTEM PRIVATE "${pslp_SOURCE_DIR}/include" "${dejavu_SOURCE_DIR}" ) -target_compile_definitions(cuopt_lp +target_compile_definitions(cuopt_mathematical_optimization PUBLIC CUSPARSE_ENABLE_EXPERIMENTAL_API PRIVATE CUDSS_MT_LIB_FILE_NAME="${CUDSS_MT_LIB_FILE_NAME}" ) -target_link_libraries(cuopt_lp +target_link_libraries(cuopt_mathematical_optimization PUBLIC cuopt_base ${CUDSS_LIB_FILE} @@ -686,19 +686,19 @@ target_link_libraries(cuopt_lp nccl_external ${CMAKE_DL_LIBS} ) -target_link_libraries(cuopt_lp PRIVATE $) -add_dependencies(cuopt_lp PSLP) -target_include_directories(cuopt_lp SYSTEM PRIVATE +target_link_libraries(cuopt_mathematical_optimization PRIVATE $) +add_dependencies(cuopt_mathematical_optimization PSLP) +target_include_directories(cuopt_mathematical_optimization SYSTEM PRIVATE $) -target_compile_definitions(cuopt_lp PRIVATE TBB_PREVIEW_GLOBAL_CONTROL KAMINPAR_64BIT_EDGE_IDS) -target_link_libraries(cuopt_lp PRIVATE $) +target_compile_definitions(cuopt_mathematical_optimization PRIVATE TBB_PREVIEW_GLOBAL_CONTROL KAMINPAR_64BIT_EDGE_IDS) +target_link_libraries(cuopt_mathematical_optimization PRIVATE $) if (TARGET KaMinPar) - add_dependencies(cuopt_lp KaMinPar) + add_dependencies(cuopt_mathematical_optimization KaMinPar) endif () -add_library(cuopt::lp ALIAS cuopt_lp) +add_library(cuopt::mathematical_optimization ALIAS cuopt_mathematical_optimization) # cuopt_grpc: gRPC bridge — proto mappers + Cython client (LP and routing over gRPC) -# Depends on cuopt_lp and cuopt_routing so it can reference both APIs without forcing +# Depends on cuopt_mathematical_optimization and cuopt_routing so it can reference both APIs without forcing # either component to take a gRPC dependency. if(NOT SKIP_GRPC_BUILD) add_library(cuopt_grpc SHARED ${GRPC_INFRA_FILES}) @@ -710,9 +710,9 @@ if(NOT SKIP_GRPC_BUILD) "${CMAKE_CURRENT_SOURCE_DIR}/src/io" ) if(NOT SKIP_ROUTING_BUILD) - target_link_libraries(cuopt_grpc PUBLIC cuopt_lp cuopt_routing PRIVATE protobuf::libprotobuf gRPC::grpc++) + target_link_libraries(cuopt_grpc PUBLIC cuopt_mathematical_optimization cuopt_routing PRIVATE protobuf::libprotobuf gRPC::grpc++) else() - target_link_libraries(cuopt_grpc PUBLIC cuopt_lp PRIVATE protobuf::libprotobuf gRPC::grpc++) + target_link_libraries(cuopt_grpc PUBLIC cuopt_mathematical_optimization PRIVATE protobuf::libprotobuf gRPC::grpc++) endif() add_library(cuopt::grpc ALIAS cuopt_grpc) endif() @@ -865,51 +865,24 @@ endif (BUILD_TESTS) # ################################################################################################## # - cuopt: thin umbrella shared library ----------------------------------------------------------- -# Links publicly against the four component shared libs so that -lcuopt keeps working for -# all existing consumers. - -# 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; - -namespace cuopt { void print_version_info(int); } // libcuopt_base.so -__attribute__((used)) void (* const _cuopt_base_anchor)(int) = &cuopt::print_version_info; -]=]) +# Links publicly against the component shared libs so that -lcuopt keeps working for +# all existing consumers. Its only translation unit is generated from +# cmake/umbrella.cpp.in -- see that file for why the anchors are needed. if(NOT SKIP_ROUTING_BUILD) - string(APPEND _UMBRELLA_SRC [=[ -#include -namespace cuopt::routing { -__attribute__((used)) assignment_t (* const _cuopt_routing_anchor)( - data_model_view_t const&, solver_settings_t const&) = - &solve; -} -]=]) + set(CUOPT_ANCHOR_ROUTING TRUE) endif() - 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 -#include -namespace cuopt::mathematical_optimization { -__attribute__((used)) solve_lp_remote_fn_t const _cuopt_grpc_anchor = - &solve_lp_remote; -} -]=]) + set(CUOPT_ANCHOR_GRPC TRUE) endif() -file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/cuopt_umbrella.cpp" "${_UMBRELLA_SRC}") +configure_file( + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/umbrella.cpp.in" + "${CMAKE_CURRENT_BINARY_DIR}/umbrella.cpp" + @ONLY +) -add_library(cuopt SHARED "${CMAKE_CURRENT_BINARY_DIR}/cuopt_umbrella.cpp") +add_library(cuopt SHARED "${CMAKE_CURRENT_BINARY_DIR}/umbrella.cpp") add_library(cuopt::cuopt ALIAS cuopt) set_target_properties(cuopt PROPERTIES @@ -932,9 +905,9 @@ target_compile_definitions(cuopt ) if(NOT SKIP_ROUTING_BUILD) - target_link_libraries(cuopt PUBLIC cuopt_base cuopt_routing cuopt_lp) + target_link_libraries(cuopt PUBLIC cuopt_base cuopt_routing cuopt_mathematical_optimization) else() - target_link_libraries(cuopt PUBLIC cuopt_base cuopt_lp) + target_link_libraries(cuopt PUBLIC cuopt_base cuopt_mathematical_optimization) endif() if(NOT SKIP_GRPC_BUILD) target_link_libraries(cuopt PUBLIC cuopt_grpc) @@ -973,7 +946,7 @@ else () set(_INCLUDE_DEST include/cuopt/) endif () -set(CUOPT_COMPONENT_TARGETS cuopt_base cuopt_lp) +set(CUOPT_COMPONENT_TARGETS cuopt_base cuopt_mathematical_optimization) if(NOT SKIP_ROUTING_BUILD) list(APPEND CUOPT_COMPONENT_TARGETS cuopt_routing) endif() @@ -981,7 +954,7 @@ if(NOT SKIP_GRPC_BUILD) list(APPEND CUOPT_COMPONENT_TARGETS cuopt_grpc) endif() -# Export as cuopt::base / cuopt::routing / cuopt::lp / cuopt::grpc, matching the +# Export as cuopt::base / cuopt::routing / cuopt::mathematical_optimization / cuopt::grpc, matching the # build-tree aliases. set(CUOPT_COMPONENT_EXPORT_NAMES "") foreach(_component ${CUOPT_COMPONENT_TARGETS}) @@ -996,7 +969,7 @@ install(TARGETS ${CUOPT_COMPONENT_TARGETS} EXPORT cuopt-exports ) -# Install umbrella shared library (the primary runtime artifact) +# Install the umbrella shared library (the primary runtime artifact) install(TARGETS cuopt DESTINATION ${_LIB_DEST} COMPONENT runtime @@ -1026,7 +999,7 @@ set(doc_string Provide targets for cuOpt. cuOpt library is a collection of GPU accelerated combinatorial optimization algorithms. -Component targets: cuopt::base, cuopt::routing, cuopt::lp, cuopt::grpc (when gRPC is built) +Component targets: cuopt::base, cuopt::routing, cuopt::mathematical_optimization, cuopt::grpc (when gRPC is built) Umbrella target: cuopt::cuopt (links all component libs — backward-compatible with -lcuopt) ]=]) diff --git a/cpp/cmake/umbrella.cpp.in b/cpp/cmake/umbrella.cpp.in new file mode 100644 index 0000000000..a3b7c904ee --- /dev/null +++ b/cpp/cmake/umbrella.cpp.in @@ -0,0 +1,47 @@ +/* clang-format off */ +/* + * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +/* clang-format on */ + +// Generated from cmake/umbrella.cpp.in -- do not edit the generated copy. +// +// The umbrella libcuopt.so has no code of its own; it exists so that -lcuopt keeps +// resolving for consumers that linked the pre-split library. Because it defines +// nothing, --as-needed would drop every component from DT_NEEDED. Referencing one +// exported symbol per component keeps them all linked in. + +#cmakedefine CUOPT_ANCHOR_ROUTING +#cmakedefine CUOPT_ANCHOR_GRPC + +// libcuopt_mathematical_optimization.so +extern "C" void cuOptDestroyProblem(void*); +__attribute__((used)) void (*const _cuopt_mathematical_optimization_anchor)(void*) = + &cuOptDestroyProblem; + +// libcuopt_base.so +namespace cuopt { +void print_version_info(int); +} +__attribute__((used)) void (*const _cuopt_base_anchor)(int) = &cuopt::print_version_info; + +#ifdef CUOPT_ANCHOR_ROUTING +// libcuopt_routing.so +#include +namespace cuopt::routing { +__attribute__((used)) assignment_t (*const _cuopt_routing_anchor)( + data_model_view_t const&, solver_settings_t const&) = &solve; +} +#endif + +#ifdef CUOPT_ANCHOR_GRPC +// libcuopt_grpc.so. Anchoring solve_lp_remote also guarantees the ELF constructor in +// grpc_registration.cpp runs, which registers the remote-solve callbacks in +// libcuopt_mathematical_optimization.so. +#include +#include +namespace cuopt::mathematical_optimization { +__attribute__((used)) solve_lp_remote_fn_t const _cuopt_grpc_anchor = &solve_lp_remote; +} +#endif diff --git a/cpp/include/cuopt/mathematical_optimization/remote_solve_registry.hpp b/cpp/include/cuopt/mathematical_optimization/remote_solve_registry.hpp index c8c301ceb2..2e008a0825 100644 --- a/cpp/include/cuopt/mathematical_optimization/remote_solve_registry.hpp +++ b/cpp/include/cuopt/mathematical_optimization/remote_solve_registry.hpp @@ -12,7 +12,7 @@ #include #include -// Forward declarations — full types live in libcuopt_lp / libcuopt_grpc headers. +// Forward declarations — full types live in libcuopt_mathematical_optimization / libcuopt_grpc headers. namespace cuopt::mathematical_optimization { template @@ -43,7 +43,7 @@ using solve_mip_remote_fn_t = std::unique_ptr const&, mip_solver_settings_t const&); /** - * @brief Registry slots defined in libcuopt_lp.so (remote_solve_registry.cpp). + * @brief Registry slots defined in libcuopt_mathematical_optimization.so (remote_solve_registry.cpp). * * Null until libcuopt_grpc.so is loaded and calls register_remote_solvers(). Atomic * because the registering ELF constructor runs on whichever thread triggers the lazy diff --git a/cpp/include/cuopt/mathematical_optimization/solve_remote.hpp b/cpp/include/cuopt/mathematical_optimization/solve_remote.hpp index 3636195660..159e18f868 100644 --- a/cpp/include/cuopt/mathematical_optimization/solve_remote.hpp +++ b/cpp/include/cuopt/mathematical_optimization/solve_remote.hpp @@ -19,10 +19,10 @@ template class cpu_optimization_problem_t; template -struct pdlp_solver_settings_t; +class pdlp_solver_settings_t; template -struct mip_solver_settings_t; +class mip_solver_settings_t; // ============================================================================ // Remote Execution Functions diff --git a/cpp/src/CMakeLists.txt b/cpp/src/CMakeLists.txt index 8af5cf4dfc..94c54d330d 100644 --- a/cpp/src/CMakeLists.txt +++ b/cpp/src/CMakeLists.txt @@ -28,14 +28,14 @@ add_subdirectory(cuts) # Aggregate per-domain source lists for the three component libraries set(CUOPT_BASE_SRC_FILES ${UTIL_SRC_FILES} - ${LINEAR_ALGEBRA_SRC_FILES} ) set(CUOPT_ROUTING_SRC_FILES ${ROUTING_SRC_FILES} ) -set(CUOPT_LP_SRC_FILES +set(CUOPT_MATHEMATICAL_OPTIMIZATION_SRC_FILES + ${LINEAR_ALGEBRA_SRC_FILES} ${LP_SRC_FILES} ${MATH_OPT_SRC_FILES} ${MIP_SRC_FILES} @@ -48,11 +48,11 @@ set(CUOPT_LP_SRC_FILES set(CUOPT_BASE_SRC_FILES ${CUOPT_BASE_SRC_FILES} PARENT_SCOPE) set(CUOPT_ROUTING_SRC_FILES ${CUOPT_ROUTING_SRC_FILES} PARENT_SCOPE) -set(CUOPT_LP_SRC_FILES ${CUOPT_LP_SRC_FILES} PARENT_SCOPE) +set(CUOPT_MATHEMATICAL_OPTIMIZATION_SRC_FILES ${CUOPT_MATHEMATICAL_OPTIMIZATION_SRC_FILES} PARENT_SCOPE) set(CUOPT_SRC_FILES ${CUOPT_BASE_SRC_FILES} ${CUOPT_ROUTING_SRC_FILES} - ${CUOPT_LP_SRC_FILES} + ${CUOPT_MATHEMATICAL_OPTIMIZATION_SRC_FILES} PARENT_SCOPE ) set(MPS_FAST_SRC_FILES ${MPS_FAST_SRC_FILES} PARENT_SCOPE) diff --git a/cpp/src/grpc/client/grpc_registration.cpp b/cpp/src/grpc/client/grpc_registration.cpp index 5bf4ef0c45..9ed54d4b1c 100644 --- a/cpp/src/grpc/client/grpc_registration.cpp +++ b/cpp/src/grpc/client/grpc_registration.cpp @@ -1,9 +1,9 @@ // SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -// Registers the gRPC-based remote solve implementations with libcuopt_lp.so +// Registers the gRPC-based remote solve implementations with libcuopt_mathematical_optimization.so // at dynamic-link time (before any user code runs). This breaks the circular -// dependency: libcuopt_lp.so holds nullable function pointers rather than a +// dependency: libcuopt_mathematical_optimization.so holds nullable function pointers rather than a // hard reference to symbols in libcuopt_grpc.so. #include diff --git a/docs/cuopt/source/cuopt-c/convex/examples/Makefile b/docs/cuopt/source/cuopt-c/convex/examples/Makefile index 4195376ca5..43585f9247 100644 --- a/docs/cuopt/source/cuopt-c/convex/examples/Makefile +++ b/docs/cuopt/source/cuopt-c/convex/examples/Makefile @@ -46,7 +46,7 @@ CC = gcc # Compiler flags CFLAGS = -I$(INCLUDE_PATH) -Wall -Wextra -LDFLAGS = -L$(LIBCUOPT_LIBRARY_PATH) -lcuopt -lcuopt_lp -Wl,--allow-shlib-undefined -Wl,-rpath,$(LIBCUOPT_LIBRARY_PATH) +LDFLAGS = -L$(LIBCUOPT_LIBRARY_PATH) -lcuopt -lcuopt_mathematical_optimization -Wl,--allow-shlib-undefined -Wl,-rpath,$(LIBCUOPT_LIBRARY_PATH) # Automatically discover all C source files in current directory SOURCES = $(wildcard *.c) diff --git a/docs/cuopt/source/cuopt-c/mip/examples/Makefile b/docs/cuopt/source/cuopt-c/mip/examples/Makefile index dace6ebd11..51f9b409f2 100644 --- a/docs/cuopt/source/cuopt-c/mip/examples/Makefile +++ b/docs/cuopt/source/cuopt-c/mip/examples/Makefile @@ -22,7 +22,7 @@ CC = gcc # Compiler flags CFLAGS = -I$(INCLUDE_PATH) -Wall -Wextra -LDFLAGS = -L$(LIBCUOPT_LIBRARY_PATH) -lcuopt -lcuopt_lp -Wl,--allow-shlib-undefined -Wl,-rpath,$(LIBCUOPT_LIBRARY_PATH) +LDFLAGS = -L$(LIBCUOPT_LIBRARY_PATH) -lcuopt -lcuopt_mathematical_optimization -Wl,--allow-shlib-undefined -Wl,-rpath,$(LIBCUOPT_LIBRARY_PATH) # Automatically discover all C source files in current directory SOURCES = $(wildcard *.c) diff --git a/python/libcuopt/CMakeLists.txt b/python/libcuopt/CMakeLists.txt index b9806d194a..1e417a2e7c 100644 --- a/python/libcuopt/CMakeLists.txt +++ b/python/libcuopt/CMakeLists.txt @@ -95,7 +95,7 @@ else() endif() message(STATUS "libcuopt: Final RPATH = ${rpaths}") -foreach(_target cuopt cuopt_base cuopt_routing cuopt_lp cuopt_grpc cuopt_cli cuopt_grpc_server) +foreach(_target cuopt cuopt_base cuopt_routing cuopt_mathematical_optimization cuopt_grpc cuopt_cli cuopt_grpc_server) if(TARGET ${_target}) set_property(TARGET ${_target} APPEND PROPERTY INSTALL_RPATH ${rpaths}) endif() From b3b870245bd13e31e5675a5df9923d36c5b4ed43 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Thu, 6 Aug 2026 22:15:11 -0500 Subject: [PATCH 22/27] build(cmake): ship libcuopt.so as a linker script Bare -lcuopt stopped resolving once libcuopt.so became a thin umbrella: the linker does not satisfy a consumer's undefined symbols through a dependency's DT_NEEDED (--no-copy-dt-needed-entries, the default since binutils 2.22), so C consumers had to name each component and the example Makefiles had to paper over it with --allow-shlib-undefined. Ship libcuopt.so as a GNU ld script naming the components instead. This is how glibc ships libc.so. -lcuopt resolves to the whole set again and consumers get a direct DT_NEEDED on each component, so the umbrella library and its --as-needed anchor symbols are no longer needed and are removed. cuopt::cuopt becomes an INTERFACE target, leaving CMake and Cython consumers unchanged. libcuopt.so is no longer an ELF object, so it cannot be dlopen()ed: load.py loads the components directly, and the wheel omits the script since nothing in it links. Anything previously linked against libcuopt.so needs a rebuild. Also fixes WRITE_FATBIN, which applied fatbin.ld to the umbrella. That target held no device code after the split, so the section grouping had silently become a no-op; it now applies to the components carrying the fatbins. Co-Authored-By: Claude Opus 5 (1M context) Signed-off-by: Ramakrishna Prabhu --- conda/recipes/libcuopt/recipe.yaml | 5 ++- cpp/CMakeLists.txt | 60 +++++++++++++++--------------- cpp/cmake/libcuopt.so.in | 8 ++++ cpp/cmake/umbrella.cpp.in | 47 ----------------------- python/libcuopt/CMakeLists.txt | 3 +- python/libcuopt/libcuopt/load.py | 35 +++++++++++++++-- python/libcuopt/pyproject.toml | 5 +++ 7 files changed, 80 insertions(+), 83 deletions(-) create mode 100644 cpp/cmake/libcuopt.so.in delete mode 100644 cpp/cmake/umbrella.cpp.in diff --git a/conda/recipes/libcuopt/recipe.yaml b/conda/recipes/libcuopt/recipe.yaml index 4896c08da5..e57dc069fb 100644 --- a/conda/recipes/libcuopt/recipe.yaml +++ b/conda/recipes/libcuopt/recipe.yaml @@ -113,7 +113,10 @@ outputs: prefix_detection: ignore: # See https://github.com/rapidsai/build-planning/issues/160 - - lib/libcuopt.so + - lib/libcuopt_base.so + - lib/libcuopt_routing.so + - lib/libcuopt_mathematical_optimization.so + - lib/libcuopt_grpc.so string: cuda${{ cuda_major }}_${{ date_string }}_${{ head_rev }} requirements: build: diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 15fe0302bf..44a55ea45e 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -864,53 +864,44 @@ if (BUILD_TESTS) endif (BUILD_TESTS) # ################################################################################################## -# - cuopt: thin umbrella shared library ----------------------------------------------------------- -# Links publicly against the component shared libs so that -lcuopt keeps working for -# all existing consumers. Its only translation unit is generated from -# cmake/umbrella.cpp.in -- see that file for why the anchors are needed. +# - cuopt: linker script + INTERFACE target ------------------------------------------------------- +# libcuopt.so is a GNU ld script naming the component libraries, so -lcuopt keeps resolving +# for consumers that linked the pre-split library. cuopt::cuopt is the CMake equivalent. +# See cmake/libcuopt.so.in for why a real ELF cannot serve this purpose. +set(CUOPT_LINKER_SCRIPT_INPUTS "libcuopt_base.so") if(NOT SKIP_ROUTING_BUILD) - set(CUOPT_ANCHOR_ROUTING TRUE) + string(APPEND CUOPT_LINKER_SCRIPT_INPUTS " libcuopt_routing.so") endif() +string(APPEND CUOPT_LINKER_SCRIPT_INPUTS " libcuopt_mathematical_optimization.so") if(NOT SKIP_GRPC_BUILD) - set(CUOPT_ANCHOR_GRPC TRUE) + string(APPEND CUOPT_LINKER_SCRIPT_INPUTS " libcuopt_grpc.so") endif() +set(CUOPT_LINKER_SCRIPT "${CMAKE_CURRENT_BINARY_DIR}/libcuopt.so") configure_file( - "${CMAKE_CURRENT_SOURCE_DIR}/cmake/umbrella.cpp.in" - "${CMAKE_CURRENT_BINARY_DIR}/umbrella.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/libcuopt.so.in" + "${CUOPT_LINKER_SCRIPT}" @ONLY ) -add_library(cuopt SHARED "${CMAKE_CURRENT_BINARY_DIR}/umbrella.cpp") +add_library(cuopt INTERFACE) add_library(cuopt::cuopt ALIAS cuopt) -set_target_properties(cuopt PROPERTIES - BUILD_RPATH_USE_ORIGIN TRUE - INSTALL_RPATH "\$ORIGIN" - CXX_SCAN_FOR_MODULES OFF -) - target_include_directories(cuopt - PUBLIC + INTERFACE "$" "$" - INTERFACE "$" ) -target_compile_definitions(cuopt - PUBLIC - "CUOPT_LOG_ACTIVE_LEVEL=RAPIDS_LOGGER_LOG_LEVEL_${LIBCUOPT_LOGGING_LEVEL}" - CUSPARSE_ENABLE_EXPERIMENTAL_API -) if(NOT SKIP_ROUTING_BUILD) - target_link_libraries(cuopt PUBLIC cuopt_base cuopt_routing cuopt_mathematical_optimization) + target_link_libraries(cuopt INTERFACE cuopt_base cuopt_routing cuopt_mathematical_optimization) else() - target_link_libraries(cuopt PUBLIC cuopt_base cuopt_mathematical_optimization) + target_link_libraries(cuopt INTERFACE cuopt_base cuopt_mathematical_optimization) endif() if(NOT SKIP_GRPC_BUILD) - target_link_libraries(cuopt PUBLIC cuopt_grpc) + target_link_libraries(cuopt INTERFACE cuopt_grpc) endif() if (WRITE_FATBIN) @@ -922,7 +913,15 @@ if (WRITE_FATBIN) .nv_fatbin : { *(.nv_fatbin) } } ]=]) - target_link_options(cuopt PRIVATE "${CUOPT_BINARY_DIR}/fatbin.ld") + # Applies to the components that actually carry CUDA fatbins. Before the split this + # was set on libcuopt.so, which held all the device code; it now holds none. + set(_CUOPT_FATBIN_TARGETS cuopt_base cuopt_mathematical_optimization) + if(NOT SKIP_ROUTING_BUILD) + list(APPEND _CUOPT_FATBIN_TARGETS cuopt_routing) + endif() + foreach(_target ${_CUOPT_FATBIN_TARGETS}) + target_link_options(${_target} PRIVATE "${CUOPT_BINARY_DIR}/fatbin.ld") + endforeach() endif () # ################################################################################################## @@ -969,14 +968,15 @@ install(TARGETS ${CUOPT_COMPONENT_TARGETS} EXPORT cuopt-exports ) -# Install the umbrella shared library (the primary runtime artifact) +# cuopt::cuopt carries no artifact of its own; it is exported so that +# target_link_libraries(app cuopt::cuopt) keeps pulling in every component. install(TARGETS cuopt - DESTINATION ${_LIB_DEST} - COMPONENT runtime EXPORT cuopt-exports ) -install(TARGETS cuopt +# libcuopt.so is the ld script that keeps -lcuopt working. It is a link-time artifact, +# so it belongs to the dev component; nothing loads it at runtime. +install(FILES "${CUOPT_LINKER_SCRIPT}" DESTINATION ${_LIB_DEST} COMPONENT dev ) diff --git a/cpp/cmake/libcuopt.so.in b/cpp/cmake/libcuopt.so.in new file mode 100644 index 0000000000..0e80a0fba3 --- /dev/null +++ b/cpp/cmake/libcuopt.so.in @@ -0,0 +1,8 @@ +/* GNU ld script -- generated from cmake/libcuopt.so.in, do not edit the generated copy. + libcuopt.so is not an ELF object. cuOpt ships as component libraries, and the linker + does not resolve a consumer's undefined symbols through a dependency's DT_NEEDED + (--no-copy-dt-needed-entries, the default since binutils 2.22). Naming this script + libcuopt.so keeps -lcuopt resolving to the whole set, exactly as it did before the + library was split. Consumers end up with a direct DT_NEEDED on each component. + Anything that loads cuOpt at runtime must dlopen the components, not this file. */ +INPUT(@CUOPT_LINKER_SCRIPT_INPUTS@) diff --git a/cpp/cmake/umbrella.cpp.in b/cpp/cmake/umbrella.cpp.in deleted file mode 100644 index a3b7c904ee..0000000000 --- a/cpp/cmake/umbrella.cpp.in +++ /dev/null @@ -1,47 +0,0 @@ -/* clang-format off */ -/* - * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 - */ -/* clang-format on */ - -// Generated from cmake/umbrella.cpp.in -- do not edit the generated copy. -// -// The umbrella libcuopt.so has no code of its own; it exists so that -lcuopt keeps -// resolving for consumers that linked the pre-split library. Because it defines -// nothing, --as-needed would drop every component from DT_NEEDED. Referencing one -// exported symbol per component keeps them all linked in. - -#cmakedefine CUOPT_ANCHOR_ROUTING -#cmakedefine CUOPT_ANCHOR_GRPC - -// libcuopt_mathematical_optimization.so -extern "C" void cuOptDestroyProblem(void*); -__attribute__((used)) void (*const _cuopt_mathematical_optimization_anchor)(void*) = - &cuOptDestroyProblem; - -// libcuopt_base.so -namespace cuopt { -void print_version_info(int); -} -__attribute__((used)) void (*const _cuopt_base_anchor)(int) = &cuopt::print_version_info; - -#ifdef CUOPT_ANCHOR_ROUTING -// libcuopt_routing.so -#include -namespace cuopt::routing { -__attribute__((used)) assignment_t (*const _cuopt_routing_anchor)( - data_model_view_t const&, solver_settings_t const&) = &solve; -} -#endif - -#ifdef CUOPT_ANCHOR_GRPC -// libcuopt_grpc.so. Anchoring solve_lp_remote also guarantees the ELF constructor in -// grpc_registration.cpp runs, which registers the remote-solve callbacks in -// libcuopt_mathematical_optimization.so. -#include -#include -namespace cuopt::mathematical_optimization { -__attribute__((used)) solve_lp_remote_fn_t const _cuopt_grpc_anchor = &solve_lp_remote; -} -#endif diff --git a/python/libcuopt/CMakeLists.txt b/python/libcuopt/CMakeLists.txt index 1e417a2e7c..d288ea3999 100644 --- a/python/libcuopt/CMakeLists.txt +++ b/python/libcuopt/CMakeLists.txt @@ -95,7 +95,8 @@ else() endif() message(STATUS "libcuopt: Final RPATH = ${rpaths}") -foreach(_target cuopt cuopt_base cuopt_routing cuopt_mathematical_optimization cuopt_grpc cuopt_cli cuopt_grpc_server) +# cuopt is an INTERFACE target (libcuopt.so is a linker script), so it has no RPATH. +foreach(_target cuopt_base cuopt_routing cuopt_mathematical_optimization cuopt_grpc cuopt_cli cuopt_grpc_server) if(TARGET ${_target}) set_property(TARGET ${_target} APPEND PROPERTY INSTALL_RPATH ${rpaths}) endif() diff --git a/python/libcuopt/libcuopt/load.py b/python/libcuopt/libcuopt/load.py index 77dd6ed161..43f7eb1366 100644 --- a/python/libcuopt/libcuopt/load.py +++ b/python/libcuopt/libcuopt/load.py @@ -53,7 +53,33 @@ def load_library(): != "false" ) - soname = "libcuopt.so" + # cuOpt ships as component libraries. libcuopt.so is a linker script, + # not an ELF object, so it cannot be dlopen()ed -- load the components + # instead. Each pulls its own dependencies in through DT_NEEDED, so this + # order only needs to be valid, not exhaustive. routing and grpc are + # optional (SKIP_ROUTING_BUILD, SKIP_GRPC_BUILD) and may be absent. + components = [ + ("libcuopt_base.so", True), + ("libcuopt_routing.so", False), + ("libcuopt_mathematical_optimization.so", True), + ("libcuopt_grpc.so", False), + ] + loaded = [] + for soname, required in components: + lib = _load_component(soname, prefer_system_installation, required) + if lib is not None: + loaded.append(lib) + return loaded + + +def _load_component( + soname: str, prefer_system_installation: bool, required: bool +): + """Load one cuOpt component. + + Returns the handle, or ``None`` if it could not be loaded. Failing to + load an optional component is silent; failing a required one warns. + """ libcuopt_lib = None if prefer_system_installation: # Prefer a system library if one is present to @@ -76,6 +102,8 @@ def load_library(): # If none of the searches above succeed, just silently return None # and rely on other mechanisms (like RPATHs on other DSOs) to # help the loader find the library. + if not required: + return None import warnings @@ -84,11 +112,10 @@ def load_library(): f"Error: {str(e)}. " "Falling back to relying on system loader. " "cuOpt functionality may be unavailable. " - "This might lead to a generic error such as " - "'libcuopt.so missing' if the library cannot be found.", + f"This might lead to a generic error such as " + f"'{soname} missing' if the library cannot be found.", RuntimeWarning, ) - pass # The caller almost never needs to do anything with this library, but no # harm in offering the option since this object at least provides a handle # to inspect where libcuopt was loaded from. diff --git a/python/libcuopt/pyproject.toml b/python/libcuopt/pyproject.toml index 14d73cc5ee..416285dd06 100644 --- a/python/libcuopt/pyproject.toml +++ b/python/libcuopt/pyproject.toml @@ -65,6 +65,11 @@ sdist.reproducible = true wheel.packages = ["libcuopt"] wheel.install-dir = "libcuopt" wheel.py-api = "py3" +# libcuopt.so is a GNU ld script, not an ELF object. It exists so that -lcuopt keeps +# working for C consumers, who link against the conda or deb packages. Shipping a +# non-ELF file with a .so name would trip auditwheel, and nothing in the wheel loads +# it -- load.py dlopen()s the components directly. +wheel.exclude = ["**/libcuopt.so"] [tool.scikit-build.metadata.version] provider = "scikit_build_core.metadata.regex" From 6647ae493fcbab3a2603ec7b9be1c3c9465a4068 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Thu, 6 Aug 2026 22:17:30 -0500 Subject: [PATCH 23/27] style: apply clang-format and refresh copyright year Rewraps comments that exceeded the column limit after the cuopt_mathematical_optimization rename. Co-Authored-By: Claude Opus 5 (1M context) Signed-off-by: Ramakrishna Prabhu --- .../mathematical_optimization/remote_solve_registry.hpp | 6 ++++-- python/libcuopt/libcuopt/load.py | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/cpp/include/cuopt/mathematical_optimization/remote_solve_registry.hpp b/cpp/include/cuopt/mathematical_optimization/remote_solve_registry.hpp index 2e008a0825..d0968cd0b6 100644 --- a/cpp/include/cuopt/mathematical_optimization/remote_solve_registry.hpp +++ b/cpp/include/cuopt/mathematical_optimization/remote_solve_registry.hpp @@ -12,7 +12,8 @@ #include #include -// Forward declarations — full types live in libcuopt_mathematical_optimization / libcuopt_grpc headers. +// Forward declarations — full types live in libcuopt_mathematical_optimization / libcuopt_grpc +// headers. namespace cuopt::mathematical_optimization { template @@ -43,7 +44,8 @@ using solve_mip_remote_fn_t = std::unique_ptr const&, mip_solver_settings_t const&); /** - * @brief Registry slots defined in libcuopt_mathematical_optimization.so (remote_solve_registry.cpp). + * @brief Registry slots defined in libcuopt_mathematical_optimization.so + * (remote_solve_registry.cpp). * * Null until libcuopt_grpc.so is loaded and calls register_remote_solvers(). Atomic * because the registering ELF constructor runs on whichever thread triggers the lazy diff --git a/python/libcuopt/libcuopt/load.py b/python/libcuopt/libcuopt/load.py index 43f7eb1366..fbc092973a 100644 --- a/python/libcuopt/libcuopt/load.py +++ b/python/libcuopt/libcuopt/load.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 From 34070b16118f7791393f63aecbcd5578841d9d62 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Fri, 7 Aug 2026 14:17:24 -0500 Subject: [PATCH 24/27] chore: drop stray files committed by mistake '1' is shell-redirection output and small_mip.mps is a local test problem; neither belongs in the repository. Co-Authored-By: Claude Opus 5 (1M context) Signed-off-by: Ramakrishna Prabhu --- 1 | 424 -------------------------------------------------- small_mip.mps | 44 ------ 2 files changed, 468 deletions(-) delete mode 100644 1 delete mode 100644 small_mip.mps diff --git a/1 b/1 deleted file mode 100644 index 9ac6ba7ab0..0000000000 --- a/1 +++ /dev/null @@ -1,424 +0,0 @@ -Setting parameter absolute_dual_tolerance to 1.000000e-05 -Setting parameter relative_dual_tolerance to 1.000000e-05 -Setting parameter absolute_primal_tolerance to 1.000000e-05 -Setting parameter relative_primal_tolerance to 1.000000e-05 -Setting parameter absolute_gap_tolerance to 1.000000e-05 -Setting parameter relative_gap_tolerance to 1.000000e-05 -Setting parameter primal_infeasible_tolerance to 1.000000e-05 -Setting parameter dual_infeasible_tolerance to 1.000000e-05 -Setting parameter time_limit to 1.020000e+01 -Setting parameter iteration_limit to 10 -Setting parameter infeasibility_detection to false -Setting parameter mip_heuristics_only to true -Setting parameter mip_clique_cuts to 0 -Setting parameter method to 2 -Setting parameter user_problem_file to -Setting parameter mip_symmetry to 0 -Setting parameter mip_cut_min_orthogonality to 0.000000e+00 -Setting parameter mip_hyper_heuristic_relaxed_lp_time_limit to 1.000000e+00 -Setting parameter mip_hyper_heuristic_cycle_detection_length to 30 -Setting parameter mip_batch_pdlp_strong_branching to 1 -Setting parameter mip_hyper_diving_min_node_depth to 10 -Setting parameter mip_hyper_diving_farkas to -1 -Setting parameter mip_scaling to 1 -Setting parameter mip_hyper_diving_line_search to -1 -Setting parameter mip_hyper_heuristic_root_lp_time_ratio to 1.000000e-01 -Setting parameter strict_infeasibility to true -Setting parameter work_limit to 7.200000e+03 -Setting parameter mip_hyper_diving_iteration_limit_factor to 5.000000e-02 -Setting parameter mip_hyper_diving_node_limit to 500 -Setting parameter mip_hyper_heuristic_rins_fix_rate to 5.000000e-01 -Setting parameter dual_infeasible_tolerance to 1.000000e-02 -Setting parameter mip_hyper_heuristic_max_iterations_without_improvement to 8 -Setting parameter mip_reduced_cost_strengthening to 0 -Setting parameter absolute_dual_tolerance to 0.000000e+00 -Setting parameter mip_hyper_heuristic_rins_max_time_limit to 2.000000e+01 -Setting parameter mip_batch_pdlp_reliability_branching to 1 -Setting parameter mip_hyper_diving_backtrack_limit to 5 -Setting parameter mip_hyper_diving_pseudocost to -1 -Setting parameter mip_hyper_heuristic_presolve_max_time to 6.000000e+01 -Setting parameter mip_hyper_heuristic_num_cpufj_threads to 8 -Setting parameter pdlp_precision to 0 -Setting parameter folding to 0 -Setting parameter dual_postsolve to false -Setting parameter save_best_primal_so_far to true -Setting parameter log_file to 1 -Setting parameter cudss_deterministic to true -Setting parameter mip_mixed_integer_rounding_cuts to 0 -Setting parameter mip_cut_change_threshold to -5.000000e-01 -Setting parameter mip_knapsack_cuts to 0 -Setting parameter mip_reliability_branching to 0 -Setting parameter barrier_step_scale to 5.000000e-01 -Setting parameter per_constraint_residual to true -Setting parameter mip_probing to false -Setting parameter mip_hyper_heuristic_presolve_time_ratio to 1.000000e-01 -Setting parameter mip_implied_bound_cuts to 0 -Setting parameter mip_relative_tolerance to 1.000000e-02 -Setting parameter absolute_gap_tolerance to 0.000000e+00 -Setting parameter iteration_limit to 9999999 -Setting parameter mip_zero_half_cuts to 0 -Setting parameter presolve to 1 -Setting parameter node_limit to 2147483646 -Setting parameter mip_absolute_tolerance to 0.000000e+00 -Setting parameter augmented to 0 -Setting parameter solution_file to -Setting parameter relative_primal_tolerance to 0.000000e+00 -Setting parameter dualize to 0 -Setting parameter mip_hyper_heuristic_stagnation_trigger to 3 -Setting parameter pdlp_solver_mode to 3 -Setting parameter mip_hyper_heuristic_n_of_minimums_for_exit to 7000 -Setting parameter absolute_primal_tolerance to 0.000000e+00 -Setting parameter mip_hyper_heuristic_root_lp_max_time to 1.500000e+01 -Setting parameter mip_determinism_mode to 1 -Setting parameter relative_dual_tolerance to 0.000000e+00 -Setting parameter mip_relative_gap to 2.000000e-04 -Setting parameter crossover to true -Setting parameter mip_integrality_tolerance to 0.000000e+00 -Setting parameter mip_hyper_diving_vector_length to -1 -Setting parameter eliminate_dense_columns to false -Setting parameter mip_objective_step to 0 -Setting parameter mip_semi_continuous_big_m to 5.000000e+09 -Setting parameter mip_mixed_integer_gomory_cuts to 0 -Setting parameter mip_strong_chvatal_gomory_cuts to 0 -Setting parameter primal_infeasible_tolerance to 1.000000e-02 -Setting parameter relative_gap_tolerance to 0.000000e+00 -Setting parameter presolve_file to 1 -Setting parameter mip_hyper_heuristic_related_vars_time_limit to 3.000000e+01 -Setting parameter ordering to 0 -Setting parameter mip_strong_branching_simplex_iteration_limit to 0 -Setting parameter first_primal_feasible to true -Setting parameter mip_hyper_heuristic_rins_time_limit to 3.000000e+00 -Setting parameter barrier_dual_initial_point to 0 -Setting parameter mip_hyper_heuristic_initial_infeasibility_weight to 1.000000e+03 -Setting parameter mip_cut_passes to 9 -Setting parameter mip_absolute_gap to 1.000000e-02 -Setting parameter random_seed to 0 -Setting parameter num_cpu_threads to 0 -Setting parameter infeasibility_detection to true -Setting parameter barrier_iterative_refinement to false -Setting parameter time_limit to 3.600000e+03 -Setting parameter mip_hyper_diving_show_type to false -Setting parameter mip_flow_cover_cuts to 0 -Setting parameter mip_hyper_diving_coefficient to -1 -Setting parameter log_to_console to false -Setting parameter num_gpus to 2 -Setting parameter mip_hyper_diving_guided to -1 -Setting parameter mip_hyper_heuristic_enabled_recombiners to 15 -Setting parameter mip_hyper_heuristic_population_size to 32 -Setting parameter iteration_limit to 9999999 -Setting parameter node_limit to 2147483646 -Setting parameter pdlp_solver_mode to 3 -Setting parameter method to 2 -Setting parameter num_cpu_threads to 0 -Setting parameter augmented to 0 -Setting parameter folding to 0 -Setting parameter dualize to 0 -Setting parameter ordering to 0 -Setting parameter barrier_dual_initial_point to 0 -Setting parameter mip_cut_passes to 9 -Setting parameter mip_mixed_integer_rounding_cuts to 0 -Setting parameter mip_mixed_integer_gomory_cuts to 0 -Setting parameter mip_knapsack_cuts to 0 -Setting parameter mip_flow_cover_cuts to 0 -Setting parameter mip_clique_cuts to 0 -Setting parameter mip_zero_half_cuts to 0 -Setting parameter mip_implied_bound_cuts to 0 -Setting parameter mip_strong_chvatal_gomory_cuts to 0 -Setting parameter mip_reduced_cost_strengthening to 0 -Setting parameter mip_objective_step to 0 -Setting parameter num_gpus to 2 -Setting parameter num_gpus to 2 -Setting parameter mip_batch_pdlp_strong_branching to 1 -Setting parameter mip_batch_pdlp_reliability_branching to 1 -Setting parameter mip_strong_branching_simplex_iteration_limit to 0 -Setting parameter presolve to 1 -Setting parameter presolve to 1 -Setting parameter mip_determinism_mode to 1 -Setting parameter random_seed to 0 -Setting parameter mip_reliability_branching to 0 -Setting parameter pdlp_precision to 0 -Setting parameter mip_symmetry to 0 -Setting parameter mip_scaling to 1 -Setting parameter mip_hyper_heuristic_population_size to 32 -Setting parameter mip_hyper_heuristic_num_cpufj_threads to 8 -Setting parameter mip_hyper_heuristic_stagnation_trigger to 3 -Setting parameter mip_hyper_heuristic_max_iterations_without_improvement to 8 -Setting parameter mip_hyper_heuristic_n_of_minimums_for_exit to 7000 -Setting parameter mip_hyper_heuristic_enabled_recombiners to 15 -Setting parameter mip_hyper_heuristic_cycle_detection_length to 30 -Setting parameter mip_hyper_diving_line_search to -1 -Setting parameter mip_hyper_diving_pseudocost to -1 -Setting parameter mip_hyper_diving_guided to -1 -Setting parameter mip_hyper_diving_coefficient to -1 -Setting parameter mip_hyper_diving_farkas to -1 -Setting parameter mip_hyper_diving_vector_length to -1 -Setting parameter mip_hyper_diving_min_node_depth to 10 -Setting parameter mip_hyper_diving_node_limit to 500 -Setting parameter mip_hyper_diving_backtrack_limit to 5 -Setting parameter time_limit to 3.600000e+03 -Setting parameter time_limit to 3.600000e+03 -Setting parameter work_limit to 7.200000e+03 -Setting parameter absolute_dual_tolerance to 0.000000e+00 -Setting parameter relative_dual_tolerance to 0.000000e+00 -Setting parameter absolute_primal_tolerance to 0.000000e+00 -Setting parameter relative_primal_tolerance to 0.000000e+00 -Setting parameter absolute_gap_tolerance to 0.000000e+00 -Setting parameter relative_gap_tolerance to 0.000000e+00 -Setting parameter mip_absolute_tolerance to 0.000000e+00 -Setting parameter mip_relative_tolerance to 1.000000e-02 -Setting parameter mip_integrality_tolerance to 0.000000e+00 -Setting parameter mip_absolute_gap to 1.000000e-02 -Setting parameter mip_relative_gap to 2.000000e-04 -Setting parameter primal_infeasible_tolerance to 1.000000e-02 -Setting parameter dual_infeasible_tolerance to 1.000000e-02 -Setting parameter mip_cut_change_threshold to -5.000000e-01 -Setting parameter mip_cut_min_orthogonality to 0.000000e+00 -Setting parameter barrier_step_scale to 5.000000e-01 -Setting parameter mip_hyper_heuristic_presolve_time_ratio to 1.000000e-01 -Setting parameter mip_hyper_heuristic_presolve_max_time to 6.000000e+01 -Setting parameter mip_hyper_heuristic_root_lp_time_ratio to 1.000000e-01 -Setting parameter mip_hyper_heuristic_root_lp_max_time to 1.500000e+01 -Setting parameter mip_hyper_heuristic_rins_time_limit to 3.000000e+00 -Setting parameter mip_hyper_heuristic_rins_max_time_limit to 2.000000e+01 -Setting parameter mip_hyper_heuristic_rins_fix_rate to 5.000000e-01 -Setting parameter mip_hyper_heuristic_initial_infeasibility_weight to 1.000000e+03 -Setting parameter mip_hyper_heuristic_relaxed_lp_time_limit to 1.000000e+00 -Setting parameter mip_hyper_heuristic_related_vars_time_limit to 3.000000e+01 -Setting parameter mip_semi_continuous_big_m to 5.000000e+09 -Setting parameter mip_hyper_diving_iteration_limit_factor to 5.000000e-02 -Setting parameter infeasibility_detection to true -Setting parameter strict_infeasibility to true -Setting parameter per_constraint_residual to true -Setting parameter save_best_primal_so_far to true -Setting parameter first_primal_feasible to true -Setting parameter mip_heuristics_only to true -Setting parameter log_to_console to false -Setting parameter log_to_console to false -Setting parameter crossover to true -Setting parameter eliminate_dense_columns to false -Setting parameter cudss_deterministic to true -Setting parameter dual_postsolve to false -Setting parameter barrier_iterative_refinement to false -Setting parameter mip_probing to false -Setting parameter mip_hyper_diving_show_type to false -Setting parameter log_file to 1 -Setting parameter log_file to 1 -Setting parameter solution_file to -Setting parameter solution_file to -Setting parameter user_problem_file to -Setting parameter user_problem_file to -Setting parameter presolve_file to 1 -Setting parameter presolve_file to 1 -Parameters loaded from: /tmp/pytest-of-luffy/pytest-2/test_solver_settings0/solver_settings_load.config -Setting parameter iteration_limit to 9999999 -Setting parameter node_limit to 2147483646 -Setting parameter pdlp_solver_mode to 3 -Setting parameter method to 2 -Setting parameter num_cpu_threads to 0 -Setting parameter augmented to 0 -Setting parameter folding to 0 -Setting parameter dualize to 0 -Setting parameter ordering to 0 -Setting parameter barrier_dual_initial_point to 0 -Setting parameter mip_cut_passes to 9 -Setting parameter mip_mixed_integer_rounding_cuts to 0 -Setting parameter mip_mixed_integer_gomory_cuts to 0 -Setting parameter mip_knapsack_cuts to 0 -Setting parameter mip_flow_cover_cuts to 0 -Setting parameter mip_clique_cuts to 0 -Setting parameter mip_zero_half_cuts to 0 -Setting parameter mip_implied_bound_cuts to 0 -Setting parameter mip_strong_chvatal_gomory_cuts to 0 -Setting parameter mip_reduced_cost_strengthening to 0 -Setting parameter mip_objective_step to 0 -Setting parameter num_gpus to 2 -Setting parameter mip_batch_pdlp_strong_branching to 1 -Setting parameter mip_batch_pdlp_reliability_branching to 1 -Setting parameter mip_strong_branching_simplex_iteration_limit to 0 -Setting parameter presolve to 1 -Setting parameter mip_determinism_mode to 1 -Setting parameter random_seed to 0 -Setting parameter mip_reliability_branching to 0 -Setting parameter pdlp_precision to 0 -Setting parameter mip_symmetry to 0 -Setting parameter mip_scaling to 1 -Setting parameter mip_hyper_heuristic_population_size to 32 -Setting parameter mip_hyper_heuristic_num_cpufj_threads to 8 -Setting parameter mip_hyper_heuristic_stagnation_trigger to 3 -Setting parameter mip_hyper_heuristic_max_iterations_without_improvement to 8 -Setting parameter mip_hyper_heuristic_n_of_minimums_for_exit to 7000 -Setting parameter mip_hyper_heuristic_enabled_recombiners to 15 -Setting parameter mip_hyper_heuristic_cycle_detection_length to 30 -Setting parameter mip_hyper_diving_line_search to -1 -Setting parameter mip_hyper_diving_pseudocost to -1 -Setting parameter mip_hyper_diving_guided to -1 -Setting parameter mip_hyper_diving_coefficient to -1 -Setting parameter mip_hyper_diving_farkas to -1 -Setting parameter mip_hyper_diving_vector_length to -1 -Setting parameter mip_hyper_diving_min_node_depth to 10 -Setting parameter mip_hyper_diving_node_limit to 500 -Setting parameter mip_hyper_diving_backtrack_limit to 5 -Setting parameter time_limit to 3.600000e+03 -Setting parameter work_limit to 7.200000e+03 -Setting parameter absolute_dual_tolerance to 0.000000e+00 -Setting parameter relative_dual_tolerance to 0.000000e+00 -Setting parameter absolute_primal_tolerance to 0.000000e+00 -Setting parameter relative_primal_tolerance to 0.000000e+00 -Setting parameter absolute_gap_tolerance to 0.000000e+00 -Setting parameter relative_gap_tolerance to 0.000000e+00 -Setting parameter mip_absolute_tolerance to 0.000000e+00 -Setting parameter mip_relative_tolerance to 1.000000e-02 -Setting parameter mip_integrality_tolerance to 0.000000e+00 -Setting parameter mip_absolute_gap to 1.000000e-02 -Setting parameter mip_relative_gap to 2.000000e-04 -Setting parameter primal_infeasible_tolerance to 1.000000e-02 -Setting parameter dual_infeasible_tolerance to 1.000000e-02 -Setting parameter mip_cut_change_threshold to -5.000000e-01 -Setting parameter mip_cut_min_orthogonality to 0.000000e+00 -Setting parameter barrier_step_scale to 5.000000e-01 -Setting parameter mip_hyper_heuristic_presolve_time_ratio to 1.000000e-01 -Setting parameter mip_hyper_heuristic_presolve_max_time to 6.000000e+01 -Setting parameter mip_hyper_heuristic_root_lp_time_ratio to 1.000000e-01 -Setting parameter mip_hyper_heuristic_root_lp_max_time to 1.500000e+01 -Setting parameter mip_hyper_heuristic_rins_time_limit to 3.000000e+00 -Setting parameter mip_hyper_heuristic_rins_max_time_limit to 2.000000e+01 -Setting parameter mip_hyper_heuristic_rins_fix_rate to 5.000000e-01 -Setting parameter mip_hyper_heuristic_initial_infeasibility_weight to 1.000000e+03 -Setting parameter mip_hyper_heuristic_relaxed_lp_time_limit to 1.000000e+00 -Setting parameter mip_hyper_heuristic_related_vars_time_limit to 3.000000e+01 -Setting parameter mip_semi_continuous_big_m to 5.000000e+09 -Setting parameter mip_hyper_diving_iteration_limit_factor to 5.000000e-02 -Setting parameter infeasibility_detection to true -Setting parameter strict_infeasibility to true -Setting parameter per_constraint_residual to true -Setting parameter save_best_primal_so_far to true -Setting parameter first_primal_feasible to true -Setting parameter mip_heuristics_only to true -Setting parameter log_to_console to false -Setting parameter crossover to true -Setting parameter eliminate_dense_columns to false -Setting parameter cudss_deterministic to true -Setting parameter dual_postsolve to false -Setting parameter barrier_iterative_refinement to false -Setting parameter mip_probing to false -Setting parameter mip_hyper_diving_show_type to false -Setting parameter log_file to 1 -Setting parameter solution_file to -Setting parameter user_problem_file to -Setting parameter presolve_file to 1 -Setting parameter iteration_limit to 9999999 -Setting parameter node_limit to 2147483646 -Setting parameter pdlp_solver_mode to 3 -Setting parameter method to 2 -Setting parameter num_cpu_threads to 0 -Setting parameter augmented to 0 -Setting parameter folding to 0 -Setting parameter dualize to 0 -Setting parameter ordering to 0 -Setting parameter barrier_dual_initial_point to 0 -Setting parameter mip_cut_passes to 9 -Setting parameter mip_mixed_integer_rounding_cuts to 0 -Setting parameter mip_mixed_integer_gomory_cuts to 0 -Setting parameter mip_knapsack_cuts to 0 -Setting parameter mip_flow_cover_cuts to 0 -Setting parameter mip_clique_cuts to 0 -Setting parameter mip_zero_half_cuts to 0 -Setting parameter mip_implied_bound_cuts to 0 -Setting parameter mip_strong_chvatal_gomory_cuts to 0 -Setting parameter mip_reduced_cost_strengthening to 0 -Setting parameter mip_objective_step to 0 -Setting parameter num_gpus to 2 -Setting parameter mip_batch_pdlp_strong_branching to 1 -Setting parameter mip_batch_pdlp_reliability_branching to 1 -Setting parameter mip_strong_branching_simplex_iteration_limit to 0 -Setting parameter presolve to 1 -Setting parameter mip_determinism_mode to 1 -Setting parameter random_seed to 0 -Setting parameter mip_reliability_branching to 0 -Setting parameter pdlp_precision to 0 -Setting parameter mip_symmetry to 0 -Setting parameter mip_scaling to 1 -Setting parameter mip_hyper_heuristic_population_size to 32 -Setting parameter mip_hyper_heuristic_num_cpufj_threads to 8 -Setting parameter mip_hyper_heuristic_stagnation_trigger to 3 -Setting parameter mip_hyper_heuristic_max_iterations_without_improvement to 8 -Setting parameter mip_hyper_heuristic_n_of_minimums_for_exit to 7000 -Setting parameter mip_hyper_heuristic_enabled_recombiners to 15 -Setting parameter mip_hyper_heuristic_cycle_detection_length to 30 -Setting parameter mip_hyper_diving_line_search to -1 -Setting parameter mip_hyper_diving_pseudocost to -1 -Setting parameter mip_hyper_diving_guided to -1 -Setting parameter mip_hyper_diving_coefficient to -1 -Setting parameter mip_hyper_diving_farkas to -1 -Setting parameter mip_hyper_diving_vector_length to -1 -Setting parameter mip_hyper_diving_min_node_depth to 10 -Setting parameter mip_hyper_diving_node_limit to 500 -Setting parameter mip_hyper_diving_backtrack_limit to 5 -Setting parameter time_limit to 3.600000e+03 -Setting parameter work_limit to 7.200000e+03 -Setting parameter absolute_dual_tolerance to 0.000000e+00 -Setting parameter relative_dual_tolerance to 0.000000e+00 -Setting parameter absolute_primal_tolerance to 0.000000e+00 -Setting parameter relative_primal_tolerance to 0.000000e+00 -Setting parameter absolute_gap_tolerance to 0.000000e+00 -Setting parameter relative_gap_tolerance to 0.000000e+00 -Setting parameter mip_absolute_tolerance to 0.000000e+00 -Setting parameter mip_relative_tolerance to 1.000000e-02 -Setting parameter mip_integrality_tolerance to 0.000000e+00 -Setting parameter mip_absolute_gap to 1.000000e-02 -Setting parameter mip_relative_gap to 2.000000e-04 -Setting parameter primal_infeasible_tolerance to 1.000000e-02 -Setting parameter dual_infeasible_tolerance to 1.000000e-02 -Setting parameter mip_cut_change_threshold to -5.000000e-01 -Setting parameter mip_cut_min_orthogonality to 0.000000e+00 -Setting parameter barrier_step_scale to 5.000000e-01 -Setting parameter mip_hyper_heuristic_presolve_time_ratio to 1.000000e-01 -Setting parameter mip_hyper_heuristic_presolve_max_time to 6.000000e+01 -Setting parameter mip_hyper_heuristic_root_lp_time_ratio to 1.000000e-01 -Setting parameter mip_hyper_heuristic_root_lp_max_time to 1.500000e+01 -Setting parameter mip_hyper_heuristic_rins_time_limit to 3.000000e+00 -Setting parameter mip_hyper_heuristic_rins_max_time_limit to 2.000000e+01 -Setting parameter mip_hyper_heuristic_rins_fix_rate to 5.000000e-01 -Setting parameter mip_hyper_heuristic_initial_infeasibility_weight to 1.000000e+03 -Setting parameter mip_hyper_heuristic_relaxed_lp_time_limit to 1.000000e+00 -Setting parameter mip_hyper_heuristic_related_vars_time_limit to 3.000000e+01 -Setting parameter mip_semi_continuous_big_m to 5.000000e+09 -Setting parameter mip_hyper_diving_iteration_limit_factor to 5.000000e-02 -Setting parameter infeasibility_detection to true -Setting parameter strict_infeasibility to true -Setting parameter per_constraint_residual to true -Setting parameter save_best_primal_so_far to true -Setting parameter first_primal_feasible to true -Setting parameter mip_heuristics_only to true -Setting parameter log_to_console to false -Setting parameter crossover to true -Setting parameter eliminate_dense_columns to false -Setting parameter cudss_deterministic to true -Setting parameter dual_postsolve to false -Setting parameter barrier_iterative_refinement to false -Setting parameter mip_probing to false -Setting parameter mip_hyper_diving_show_type to false -Setting parameter log_file to 1 -Setting parameter solution_file to -Setting parameter user_problem_file to -Setting parameter presolve_file to 1 -cuOpt version: 26.8.0, git hash: 9b12a98d, host arch: x86_64, device archs: 75-real -CPU: AMD Ryzen Threadripper PRO 3975WX 32-Cores, threads (physical/logical): 32/64, RAM: 33.14 GiB -CUDA 13.3, device: Quadro RTX 8000 (ID 0), VRAM: 47.24 GiB -CUDA device UUID: b7f2b679-057e-ca93-f5bd-22907d9b1b3c - -Solving a problem with 2 constraints, 1 variables (0 integers), and 2 nonzeros -Problem scaling: -Objective coefficents range: [1e+00, 1e+00] -Constraint matrix coefficients range: [1e+00, 1e+00] -Constraint rhs / bounds range: [0e+00, 1e+00] -Variable bounds range: [0e+00, 0e+00] - - -Running Papilo presolve (git hash 741a2b9c) -Presolve status: reduced the problem -Presolve removed: 2 constraints, 1 variables, 2 nonzeros -Presolved problem: 0 constraints, 0 variables (0 integer), 0 nonzeros -Presolve completely solved the problem -Papilo presolve time: 0.00s -Status: Optimal Objective: 0.000000 diff --git a/small_mip.mps b/small_mip.mps deleted file mode 100644 index 5b312835f6..0000000000 --- a/small_mip.mps +++ /dev/null @@ -1,44 +0,0 @@ -NAME SMALLMIP -ROWS - N OBJ - L c1 - G c2 - E R2 - L R3 - G c5 -COLUMNS - MARK0001 'MARKER' 'INTORG' - x1 c1 1 - x1 c2 2 - x1 c5 1 - x1 OBJ 2 - x2 c1 1 - x2 R2 1 - x2 c5 1 - x2 OBJ 3 - x3 c1 1 - x3 c2 1 - x3 c5 1 - x3 OBJ 1 - x4 c2 -1 - x4 R3 1 - x4 c5 1 - x4 OBJ 1 - x5 R2 3 - x5 R3 1 - x5 c5 1 - x5 OBJ 4 - MARK0001 'MARKER' 'INTEND' -RHS - RHS1 c1 10 - RHS1 c2 3 - RHS1 R2 7 - RHS1 R3 8 - RHS1 c5 5 -BOUNDS - UI BOUND1 x1 inf - UI BOUND1 x2 4 - UI BOUND1 x3 6 - UI BOUND1 x4 inf - UI BOUND1 x5 inf -ENDATA From 38b15e132dd62ad80b19ab4184104198799061a8 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Mon, 10 Aug 2026 13:54:43 -0500 Subject: [PATCH 25/27] fix(build): drop argparse from the cuopt INTERFACE target cuopt became an INTERFACE target when libcuopt.so turned into a linker script, and PRIVATE/PUBLIC are not valid on a target that compiles nothing, so the libcuopt wheel build failed to configure: CMake Error at CMakeLists.txt:65 (target_link_libraries): INTERFACE library can only be used with the INTERFACE keyword of target_link_libraries The call was redundant anyway. argparse is used only by cuopt_cli.cpp and grpc_server_main.cpp, and cpp/CMakeLists.txt already links argparse::argparse into both cuopt_cli and cuopt_grpc_server. Verified by running ci/build_wheel_libcuopt.sh in the CI wheel image: the wheel builds, auditwheel repair succeeds, pydistcheck reports no errors and twine passes. The wheel contains the four component libraries and not the linker script. python/libcuopt is only configured by the wheel build, which is why a local cpp/build could not catch this. Co-Authored-By: Claude Opus 5 (1M context) Signed-off-by: Ramakrishna Prabhu --- python/libcuopt/CMakeLists.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/python/libcuopt/CMakeLists.txt b/python/libcuopt/CMakeLists.txt index d288ea3999..8aaf765a74 100644 --- a/python/libcuopt/CMakeLists.txt +++ b/python/libcuopt/CMakeLists.txt @@ -62,9 +62,9 @@ set(CUOPT_BUILD_TESTUTIL OFF) add_subdirectory(../../cpp cuopt-cpp) -target_link_libraries(cuopt PRIVATE - argparse -) +# cuopt is an INTERFACE target (libcuopt.so is a linker script) and compiles nothing, +# so it has no use for argparse. cuopt_cli and cuopt_grpc_server, which do, already link +# argparse::argparse in cpp/CMakeLists.txt. target_link_libraries(cuopt_cli PRIVATE argparse ) From c7b70da1ec4cb606db98a29b190a892da08a565e Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Mon, 10 Aug 2026 15:06:52 -0500 Subject: [PATCH 26/27] fix(build): ship libcuopt.so linker script in the wheel The wheel installs 2020 C/C++ headers including cuopt_c.h, and the C API docs tell users to pip install libcuopt-cuXX then locate libcuopt.so and link -lcuopt. Excluding the linker script left that path with headers but nothing to link against. The exclusion was guarding against auditwheel choking on a non-ELF .so, which it does not do: auditwheel's elf_file_filter parses each candidate and skips anything raising ELFError, so the script is copied through untouched. Its --exclude option skips SONAMEs from being grafted, not files from being processed, so no exception was needed in the first place. Verified with ci/build_wheel_libcuopt.sh in the CI wheel image: auditwheel repair, pydistcheck and twine all pass, and the repaired wheel contains the linker script alongside the four component libraries. Co-Authored-By: Claude Opus 5 (1M context) Signed-off-by: Ramakrishna Prabhu --- python/libcuopt/pyproject.toml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/python/libcuopt/pyproject.toml b/python/libcuopt/pyproject.toml index 416285dd06..14d73cc5ee 100644 --- a/python/libcuopt/pyproject.toml +++ b/python/libcuopt/pyproject.toml @@ -65,11 +65,6 @@ sdist.reproducible = true wheel.packages = ["libcuopt"] wheel.install-dir = "libcuopt" wheel.py-api = "py3" -# libcuopt.so is a GNU ld script, not an ELF object. It exists so that -lcuopt keeps -# working for C consumers, who link against the conda or deb packages. Shipping a -# non-ELF file with a .so name would trip auditwheel, and nothing in the wheel loads -# it -- load.py dlopen()s the components directly. -wheel.exclude = ["**/libcuopt.so"] [tool.scikit-build.metadata.version] provider = "scikit_build_core.metadata.regex" From 503c1fbb9b4f6e05cd8431262b7e8a4608912e3d Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Wed, 12 Aug 2026 11:42:24 -0500 Subject: [PATCH 27/27] build(cmake): shorten cuopt_mathematical_optimization to cuopt_mathopt 37 characters is a long name for a shared library next to librmm.so and libraft.so, and it would propagate into the per-solver package names in #1635 as libcuopt-mathematical-optimization-cuXX. mathopt still satisfies the intent of moving away from lp, and renaming before those packages exist is far cheaper than after. Applied consistently: the target, libcuopt_mathopt.so, the cuopt::mathopt export, and CUOPT_MATHOPT_SRC_FILES. The C++ namespace cuopt::mathematical_optimization and the cuopt/mathematical_optimization header directory are unchanged -- those are API surface, not the library name. Co-Authored-By: Claude Opus 5 (1M context) Signed-off-by: Ramakrishna Prabhu --- ci/build_wheel_cuopt.sh | 2 +- ci/test_skills_assets.sh | 2 +- conda/recipes/libcuopt/recipe.yaml | 8 +-- cpp/CMakeLists.txt | 56 +++++++++---------- .../remote_solve_registry.hpp | 4 +- cpp/src/CMakeLists.txt | 6 +- cpp/src/grpc/client/grpc_registration.cpp | 4 +- .../source/cuopt-c/convex/examples/Makefile | 2 +- .../source/cuopt-c/mip/examples/Makefile | 2 +- python/libcuopt/CMakeLists.txt | 2 +- python/libcuopt/libcuopt/load.py | 2 +- 11 files changed, 45 insertions(+), 45 deletions(-) diff --git a/ci/build_wheel_cuopt.sh b/ci/build_wheel_cuopt.sh index 79ed9503c2..ac63fdce65 100755 --- a/ci/build_wheel_cuopt.sh +++ b/ci/build_wheel_cuopt.sh @@ -43,7 +43,7 @@ EXCLUDE_ARGS=( --exclude "libcuopt.so" --exclude "libcuopt_base.so" --exclude "libcuopt_routing.so" - --exclude "libcuopt_mathematical_optimization.so" + --exclude "libcuopt_mathopt.so" --exclude "libcuopt_grpc.so" --exclude "librapids_logger.so" --exclude "librmm.so" diff --git a/ci/test_skills_assets.sh b/ci/test_skills_assets.sh index 27f54a2a71..6358ea405f 100755 --- a/ci/test_skills_assets.sh +++ b/ci/test_skills_assets.sh @@ -111,7 +111,7 @@ if [[ -n "${CONDA_PREFIX:-}" ]]; then base=$(basename "$cfile" .c) rel="${cfile#"$REPO_ROOT/"}" log "Building and running C asset: $rel" - if ! (cd "$dir" && "${CC}" -I"${INCLUDE_PATH}" -L"${LIB_PATH}" -o "$base" "$(basename "$cfile")" -lcuopt -lcuopt_mathematical_optimization); then + if ! (cd "$dir" && "${CC}" -I"${INCLUDE_PATH}" -L"${LIB_PATH}" -o "$base" "$(basename "$cfile")" -lcuopt -lcuopt_mathopt); then FAILED+=("$rel (build)") log "FAIL: $rel (build)" continue diff --git a/conda/recipes/libcuopt/recipe.yaml b/conda/recipes/libcuopt/recipe.yaml index 0e2713d036..1017bf9108 100644 --- a/conda/recipes/libcuopt/recipe.yaml +++ b/conda/recipes/libcuopt/recipe.yaml @@ -109,8 +109,8 @@ outputs: content: | cmake --install cpp/build # libcuopt.so is a linker script, so the components are what carry symbols. - # Only mathematical_optimization provides the C API. - ./ci/check_symbols.sh cpp/build/libcuopt_mathematical_optimization.so + # Only cuopt_mathopt provides the C API. + ./ci/check_symbols.sh cpp/build/libcuopt_mathopt.so ./ci/check_symbols.sh cpp/build/libcuopt_base.so --no-public-api-check ./ci/check_symbols.sh cpp/build/libcuopt_routing.so --no-public-api-check ./ci/check_symbols.sh cpp/build/libcuopt_grpc.so --no-public-api-check @@ -121,7 +121,7 @@ outputs: # See https://github.com/rapidsai/build-planning/issues/160 - lib/libcuopt_base.so - lib/libcuopt_routing.so - - lib/libcuopt_mathematical_optimization.so + - lib/libcuopt_mathopt.so - lib/libcuopt_grpc.so string: cuda${{ cuda_major }}_${{ datetime_string }}_${{ head_rev }} requirements: @@ -177,7 +177,7 @@ outputs: - lib/libcuopt.so - lib/libcuopt_base.so - lib/libcuopt_routing.so - - lib/libcuopt_mathematical_optimization.so + - lib/libcuopt_mathopt.so - lib/libcuopt_grpc.so - bin/cuopt_cli - bin/cuopt_grpc_server diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 09d591d976..8d4b729b71 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -494,7 +494,7 @@ endif () set(CUOPT_SRC_FILES) set(CUOPT_BASE_SRC_FILES) set(CUOPT_ROUTING_SRC_FILES) -set(CUOPT_MATHEMATICAL_OPTIMIZATION_SRC_FILES) +set(CUOPT_MATHOPT_SRC_FILES) set(MPS_FAST_SRC_FILES) add_subdirectory(src) @@ -506,7 +506,7 @@ set_source_files_properties( if (HOST_LINEINFO) set_source_files_properties( - ${CUOPT_BASE_SRC_FILES} ${CUOPT_ROUTING_SRC_FILES} ${CUOPT_MATHEMATICAL_OPTIMIZATION_SRC_FILES} + ${CUOPT_BASE_SRC_FILES} ${CUOPT_ROUTING_SRC_FILES} ${CUOPT_MATHOPT_SRC_FILES} DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTIES COMPILE_OPTIONS "-g1") endif () @@ -524,7 +524,7 @@ endif () # Uses APPEND to preserve any existing per-file options (e.g. -g1 from HOST_LINEINFO). if (DEFINE_ASSERT) set_property( - SOURCE ${CUOPT_BASE_SRC_FILES} ${CUOPT_ROUTING_SRC_FILES} ${CUOPT_MATHEMATICAL_OPTIMIZATION_SRC_FILES} + SOURCE ${CUOPT_BASE_SRC_FILES} ${CUOPT_ROUTING_SRC_FILES} ${CUOPT_MATHOPT_SRC_FILES} DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} APPEND PROPERTY COMPILE_OPTIONS "-UNDEBUG") endif () @@ -660,10 +660,10 @@ if(NOT SKIP_ROUTING_BUILD) add_library(cuopt::routing ALIAS cuopt_routing) endif() -# cuopt_mathematical_optimization: LP / MIP / numerical optimization engine -add_library(cuopt_mathematical_optimization SHARED ${CUOPT_MATHEMATICAL_OPTIMIZATION_SRC_FILES}) -cuopt_configure_component(cuopt_mathematical_optimization) -target_include_directories(cuopt_mathematical_optimization PRIVATE +# cuopt_mathopt: LP / MIP / numerical optimization engine +add_library(cuopt_mathopt SHARED ${CUOPT_MATHOPT_SRC_FILES}) +cuopt_configure_component(cuopt_mathopt) +target_include_directories(cuopt_mathopt PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../thirdparty" "${CMAKE_CURRENT_SOURCE_DIR}/src/io" "${CUDSS_INCLUDE}" @@ -671,19 +671,19 @@ target_include_directories(cuopt_mathematical_optimization PRIVATE $<$:${ZLIB_INCLUDE_DIRS}> ) # Adding Papilo as a system include messes up clang's include resolution if papilo is already installed as a conda package -target_include_directories(cuopt_mathematical_optimization PRIVATE +target_include_directories(cuopt_mathopt PRIVATE "${papilo_SOURCE_DIR}/src" "${papilo_BINARY_DIR}" ) -target_include_directories(cuopt_mathematical_optimization SYSTEM PRIVATE +target_include_directories(cuopt_mathopt SYSTEM PRIVATE "${pslp_SOURCE_DIR}/include" "${dejavu_SOURCE_DIR}" ) -target_compile_definitions(cuopt_mathematical_optimization +target_compile_definitions(cuopt_mathopt PUBLIC CUSPARSE_ENABLE_EXPERIMENTAL_API PRIVATE CUDSS_MT_LIB_FILE_NAME="${CUDSS_MT_LIB_FILE_NAME}" ) -target_link_libraries(cuopt_mathematical_optimization +target_link_libraries(cuopt_mathopt PUBLIC cuopt_base ${CUDSS_LIB_FILE} @@ -694,19 +694,19 @@ target_link_libraries(cuopt_mathematical_optimization nccl_external ${CMAKE_DL_LIBS} ) -target_link_libraries(cuopt_mathematical_optimization PRIVATE $) -add_dependencies(cuopt_mathematical_optimization PSLP) -target_include_directories(cuopt_mathematical_optimization SYSTEM PRIVATE +target_link_libraries(cuopt_mathopt PRIVATE $) +add_dependencies(cuopt_mathopt PSLP) +target_include_directories(cuopt_mathopt SYSTEM PRIVATE $) -target_compile_definitions(cuopt_mathematical_optimization PRIVATE TBB_PREVIEW_GLOBAL_CONTROL KAMINPAR_64BIT_EDGE_IDS) -target_link_libraries(cuopt_mathematical_optimization PRIVATE $) +target_compile_definitions(cuopt_mathopt PRIVATE TBB_PREVIEW_GLOBAL_CONTROL KAMINPAR_64BIT_EDGE_IDS) +target_link_libraries(cuopt_mathopt PRIVATE $) if (TARGET KaMinPar) - add_dependencies(cuopt_mathematical_optimization KaMinPar) + add_dependencies(cuopt_mathopt KaMinPar) endif () -add_library(cuopt::mathematical_optimization ALIAS cuopt_mathematical_optimization) +add_library(cuopt::mathopt ALIAS cuopt_mathopt) # cuopt_grpc: gRPC bridge — proto mappers + Cython client (LP and routing over gRPC) -# Depends on cuopt_mathematical_optimization and cuopt_routing so it can reference both APIs without forcing +# Depends on cuopt_mathopt and cuopt_routing so it can reference both APIs without forcing # either component to take a gRPC dependency. if(NOT SKIP_GRPC_BUILD) add_library(cuopt_grpc SHARED ${GRPC_INFRA_FILES}) @@ -718,9 +718,9 @@ if(NOT SKIP_GRPC_BUILD) "${CMAKE_CURRENT_SOURCE_DIR}/src/io" ) if(NOT SKIP_ROUTING_BUILD) - target_link_libraries(cuopt_grpc PUBLIC cuopt_mathematical_optimization cuopt_routing PRIVATE protobuf::libprotobuf gRPC::grpc++) + target_link_libraries(cuopt_grpc PUBLIC cuopt_mathopt cuopt_routing PRIVATE protobuf::libprotobuf gRPC::grpc++) else() - target_link_libraries(cuopt_grpc PUBLIC cuopt_mathematical_optimization PRIVATE protobuf::libprotobuf gRPC::grpc++) + target_link_libraries(cuopt_grpc PUBLIC cuopt_mathopt PRIVATE protobuf::libprotobuf gRPC::grpc++) endif() add_library(cuopt::grpc ALIAS cuopt_grpc) endif() @@ -885,7 +885,7 @@ set(CUOPT_LINKER_SCRIPT_INPUTS "libcuopt_base.so") if(NOT SKIP_ROUTING_BUILD) string(APPEND CUOPT_LINKER_SCRIPT_INPUTS " libcuopt_routing.so") endif() -string(APPEND CUOPT_LINKER_SCRIPT_INPUTS " libcuopt_mathematical_optimization.so") +string(APPEND CUOPT_LINKER_SCRIPT_INPUTS " libcuopt_mathopt.so") if(NOT SKIP_GRPC_BUILD) string(APPEND CUOPT_LINKER_SCRIPT_INPUTS " libcuopt_grpc.so") endif() @@ -908,9 +908,9 @@ target_include_directories(cuopt ) if(NOT SKIP_ROUTING_BUILD) - target_link_libraries(cuopt INTERFACE cuopt_base cuopt_routing cuopt_mathematical_optimization) + target_link_libraries(cuopt INTERFACE cuopt_base cuopt_routing cuopt_mathopt) else() - target_link_libraries(cuopt INTERFACE cuopt_base cuopt_mathematical_optimization) + target_link_libraries(cuopt INTERFACE cuopt_base cuopt_mathopt) endif() if(NOT SKIP_GRPC_BUILD) target_link_libraries(cuopt INTERFACE cuopt_grpc) @@ -927,7 +927,7 @@ if (WRITE_FATBIN) ]=]) # Applies to the components that actually carry CUDA fatbins. Before the split this # was set on libcuopt.so, which held all the device code; it now holds none. - set(_CUOPT_FATBIN_TARGETS cuopt_base cuopt_mathematical_optimization) + set(_CUOPT_FATBIN_TARGETS cuopt_base cuopt_mathopt) if(NOT SKIP_ROUTING_BUILD) list(APPEND _CUOPT_FATBIN_TARGETS cuopt_routing) endif() @@ -957,7 +957,7 @@ else () set(_INCLUDE_DEST include/cuopt/) endif () -set(CUOPT_COMPONENT_TARGETS cuopt_base cuopt_mathematical_optimization) +set(CUOPT_COMPONENT_TARGETS cuopt_base cuopt_mathopt) if(NOT SKIP_ROUTING_BUILD) list(APPEND CUOPT_COMPONENT_TARGETS cuopt_routing) endif() @@ -965,7 +965,7 @@ if(NOT SKIP_GRPC_BUILD) list(APPEND CUOPT_COMPONENT_TARGETS cuopt_grpc) endif() -# Export as cuopt::base / cuopt::routing / cuopt::mathematical_optimization / cuopt::grpc, matching the +# Export as cuopt::base / cuopt::routing / cuopt::mathopt / cuopt::grpc, matching the # build-tree aliases. set(CUOPT_COMPONENT_EXPORT_NAMES "") foreach(_component ${CUOPT_COMPONENT_TARGETS}) @@ -1011,7 +1011,7 @@ set(doc_string Provide targets for cuOpt. cuOpt library is a collection of GPU accelerated combinatorial optimization algorithms. -Component targets: cuopt::base, cuopt::routing, cuopt::mathematical_optimization, cuopt::grpc (when gRPC is built) +Component targets: cuopt::base, cuopt::routing, cuopt::mathopt, cuopt::grpc (when gRPC is built) Umbrella target: cuopt::cuopt (links all component libs — backward-compatible with -lcuopt) ]=]) diff --git a/cpp/include/cuopt/mathematical_optimization/remote_solve_registry.hpp b/cpp/include/cuopt/mathematical_optimization/remote_solve_registry.hpp index cd2559b05f..b2b0da55ab 100644 --- a/cpp/include/cuopt/mathematical_optimization/remote_solve_registry.hpp +++ b/cpp/include/cuopt/mathematical_optimization/remote_solve_registry.hpp @@ -14,7 +14,7 @@ #include #include -// Forward declarations — full types live in libcuopt_mathematical_optimization / libcuopt_grpc +// Forward declarations — full types live in libcuopt_mathopt / libcuopt_grpc // headers. namespace cuopt::mathematical_optimization { @@ -46,7 +46,7 @@ using solve_mip_remote_fn_t = std::unique_ptr const&, mip_solver_settings_t const&); /** - * @brief Registry slots defined in libcuopt_mathematical_optimization.so + * @brief Registry slots defined in libcuopt_mathopt.so * (remote_solve_registry.cpp). * * Null until libcuopt_grpc.so is loaded and calls register_remote_solvers(). Atomic diff --git a/cpp/src/CMakeLists.txt b/cpp/src/CMakeLists.txt index 94c54d330d..264d315b19 100644 --- a/cpp/src/CMakeLists.txt +++ b/cpp/src/CMakeLists.txt @@ -34,7 +34,7 @@ set(CUOPT_ROUTING_SRC_FILES ${ROUTING_SRC_FILES} ) -set(CUOPT_MATHEMATICAL_OPTIMIZATION_SRC_FILES +set(CUOPT_MATHOPT_SRC_FILES ${LINEAR_ALGEBRA_SRC_FILES} ${LP_SRC_FILES} ${MATH_OPT_SRC_FILES} @@ -48,11 +48,11 @@ set(CUOPT_MATHEMATICAL_OPTIMIZATION_SRC_FILES set(CUOPT_BASE_SRC_FILES ${CUOPT_BASE_SRC_FILES} PARENT_SCOPE) set(CUOPT_ROUTING_SRC_FILES ${CUOPT_ROUTING_SRC_FILES} PARENT_SCOPE) -set(CUOPT_MATHEMATICAL_OPTIMIZATION_SRC_FILES ${CUOPT_MATHEMATICAL_OPTIMIZATION_SRC_FILES} PARENT_SCOPE) +set(CUOPT_MATHOPT_SRC_FILES ${CUOPT_MATHOPT_SRC_FILES} PARENT_SCOPE) set(CUOPT_SRC_FILES ${CUOPT_BASE_SRC_FILES} ${CUOPT_ROUTING_SRC_FILES} - ${CUOPT_MATHEMATICAL_OPTIMIZATION_SRC_FILES} + ${CUOPT_MATHOPT_SRC_FILES} PARENT_SCOPE ) set(MPS_FAST_SRC_FILES ${MPS_FAST_SRC_FILES} PARENT_SCOPE) diff --git a/cpp/src/grpc/client/grpc_registration.cpp b/cpp/src/grpc/client/grpc_registration.cpp index 9ed54d4b1c..a7ec47bda6 100644 --- a/cpp/src/grpc/client/grpc_registration.cpp +++ b/cpp/src/grpc/client/grpc_registration.cpp @@ -1,9 +1,9 @@ // SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -// Registers the gRPC-based remote solve implementations with libcuopt_mathematical_optimization.so +// Registers the gRPC-based remote solve implementations with libcuopt_mathopt.so // at dynamic-link time (before any user code runs). This breaks the circular -// dependency: libcuopt_mathematical_optimization.so holds nullable function pointers rather than a +// dependency: libcuopt_mathopt.so holds nullable function pointers rather than a // hard reference to symbols in libcuopt_grpc.so. #include diff --git a/docs/cuopt/source/cuopt-c/convex/examples/Makefile b/docs/cuopt/source/cuopt-c/convex/examples/Makefile index 43585f9247..327f2bff23 100644 --- a/docs/cuopt/source/cuopt-c/convex/examples/Makefile +++ b/docs/cuopt/source/cuopt-c/convex/examples/Makefile @@ -46,7 +46,7 @@ CC = gcc # Compiler flags CFLAGS = -I$(INCLUDE_PATH) -Wall -Wextra -LDFLAGS = -L$(LIBCUOPT_LIBRARY_PATH) -lcuopt -lcuopt_mathematical_optimization -Wl,--allow-shlib-undefined -Wl,-rpath,$(LIBCUOPT_LIBRARY_PATH) +LDFLAGS = -L$(LIBCUOPT_LIBRARY_PATH) -lcuopt -lcuopt_mathopt -Wl,--allow-shlib-undefined -Wl,-rpath,$(LIBCUOPT_LIBRARY_PATH) # Automatically discover all C source files in current directory SOURCES = $(wildcard *.c) diff --git a/docs/cuopt/source/cuopt-c/mip/examples/Makefile b/docs/cuopt/source/cuopt-c/mip/examples/Makefile index 51f9b409f2..a6c3a82ffc 100644 --- a/docs/cuopt/source/cuopt-c/mip/examples/Makefile +++ b/docs/cuopt/source/cuopt-c/mip/examples/Makefile @@ -22,7 +22,7 @@ CC = gcc # Compiler flags CFLAGS = -I$(INCLUDE_PATH) -Wall -Wextra -LDFLAGS = -L$(LIBCUOPT_LIBRARY_PATH) -lcuopt -lcuopt_mathematical_optimization -Wl,--allow-shlib-undefined -Wl,-rpath,$(LIBCUOPT_LIBRARY_PATH) +LDFLAGS = -L$(LIBCUOPT_LIBRARY_PATH) -lcuopt -lcuopt_mathopt -Wl,--allow-shlib-undefined -Wl,-rpath,$(LIBCUOPT_LIBRARY_PATH) # Automatically discover all C source files in current directory SOURCES = $(wildcard *.c) diff --git a/python/libcuopt/CMakeLists.txt b/python/libcuopt/CMakeLists.txt index 8aaf765a74..b3bdb5b1ec 100644 --- a/python/libcuopt/CMakeLists.txt +++ b/python/libcuopt/CMakeLists.txt @@ -96,7 +96,7 @@ endif() message(STATUS "libcuopt: Final RPATH = ${rpaths}") # cuopt is an INTERFACE target (libcuopt.so is a linker script), so it has no RPATH. -foreach(_target cuopt_base cuopt_routing cuopt_mathematical_optimization cuopt_grpc cuopt_cli cuopt_grpc_server) +foreach(_target cuopt_base cuopt_routing cuopt_mathopt cuopt_grpc cuopt_cli cuopt_grpc_server) if(TARGET ${_target}) set_property(TARGET ${_target} APPEND PROPERTY INSTALL_RPATH ${rpaths}) endif() diff --git a/python/libcuopt/libcuopt/load.py b/python/libcuopt/libcuopt/load.py index fbc092973a..3fa7fc2f96 100644 --- a/python/libcuopt/libcuopt/load.py +++ b/python/libcuopt/libcuopt/load.py @@ -61,7 +61,7 @@ def load_library(): components = [ ("libcuopt_base.so", True), ("libcuopt_routing.so", False), - ("libcuopt_mathematical_optimization.so", True), + ("libcuopt_mathopt.so", True), ("libcuopt_grpc.so", False), ] loaded = []