Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions ci/build_wheel_cuopt.sh
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ EXCLUDE_ARGS=(
--exclude "libcusolver.so.*"
--exclude "libcusparse.so.*"
--exclude "libcuopt.so"
--exclude "libcuopt_client.so"
--exclude "libnvJitLink.so*"
--exclude "librapids_logger.so"
--exclude "librmm.so"
Expand Down
1 change: 1 addition & 0 deletions conda/recipes/libcuopt/recipe.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ outputs:
- package_contents:
files:
- lib/libcuopt.so
- lib/libcuopt_client.so
- bin/cuopt_cli
- bin/cuopt_grpc_server
about:
Expand Down
165 changes: 153 additions & 12 deletions cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -550,11 +550,12 @@ if (BUILD_TESTS)
endif ()

set(CUOPT_SRC_FILES)
set(CUOPT_CLIENT_SRC_FILES)
set(MPS_FAST_SRC_FILES)
add_subdirectory(src)

if (HOST_LINEINFO)
set_source_files_properties(${CUOPT_SRC_FILES} DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTIES COMPILE_OPTIONS "-g1")
set_source_files_properties(${CUOPT_SRC_FILES} ${CUOPT_CLIENT_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)
Expand All @@ -567,11 +568,11 @@ 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 only to solver and parser source files (not gRPC infrastructure).
# Must happen before gRPC files are appended to CUOPT_CLIENT_SRC_FILES.
# 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_SRC_FILES} ${CUOPT_CLIENT_SRC_FILES} DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
APPEND PROPERTY COMPILE_OPTIONS "-UNDEBUG")
endif ()

Expand All @@ -598,9 +599,7 @@ if (NOT SKIP_GRPC_BUILD)
src/grpc/client/grpc_client.cpp
src/grpc/client/grpc_client_env.cpp
src/grpc/client/cython_grpc_client.cpp
src/grpc/client/solve_remote.cpp
)

# Routing (VRP) arm: everything that depends on the routing engine. Kept as
# its own list so a routing-only gRPC client can be split out of the
# cuopt_grpc component without moving code around again.
Expand All @@ -616,7 +615,24 @@ if (NOT SKIP_GRPC_BUILD)
if (CUOPT_ENABLE_GRPC_ROUTING)
list(APPEND GRPC_INFRA_FILES ${GRPC_ROUTING_FILES})
endif ()
list(APPEND CUOPT_SRC_FILES ${GRPC_INFRA_FILES})

# The protocol and the LP/MIP arm build into cuopt_client. The routing arm does NOT,
# despite also being free of raft/rmm/thrust: its mappers call routing::solver_settings_t
# and routing::assignment_t accessors that live in CUDA translation units inside libcuopt.
# Putting them in cuopt_client creates a cycle (libcuopt -> cuopt_client -> libcuopt) that
# ldd -r does not flag, because both libraries are loaded together -- it surfaces only when
# the call happens, as "undefined symbol: routing::solver_settings_t::get_time_limit".
# Moving the routing arm down needs those host-only accessors split out first, exactly as
# was done for the LP/MIP settings.
list(APPEND CUOPT_CLIENT_SRC_FILES ${GRPC_PROTO_GENERATED_FILES} ${GRPC_MATHOPT_FILES})
if (CUOPT_ENABLE_GRPC_ROUTING)
list(APPEND CUOPT_SRC_FILES ${GRPC_ROUTING_FILES})
endif ()

# solve_remote.cpp is the local-vs-remote dispatcher: it calls into the GPU
# solver, so it stays in cuopt_objs rather than moving down to cuopt_client.
list(APPEND CUOPT_SRC_FILES src/grpc/client/solve_remote.cpp)
list(APPEND GRPC_INFRA_FILES src/grpc/client/solve_remote.cpp)

# Always keep NDEBUG defined for gRPC infrastructure files so that abseil
# headers inline Mutex::Dtor() instead of emitting an external call.
Expand All @@ -631,6 +647,126 @@ if (NOT SKIP_GRPC_BUILD)
APPEND PROPERTY COMPILE_OPTIONS "$<$<COMPILE_LANGUAGE:CXX>:-fvisibility=default>")
endif (NOT SKIP_GRPC_BUILD)

# ##################################################################################################
# - cuopt_client - CPU-only support library ----------------------------------------------------------
#
# Holds the host-side problem representation (parsers, data_model_view, mps_data_model,
# writers), the gRPC wire protocol (generated protos + mappers), and the gRPC client.
# None of it touches CUDA, so this library links no CUDA runtime.
#
# It exists so the Python extension modules that never call into the GPU -- data_model,
# solver_settings, io, and the gRPC client -- can link something other than libcuopt.so,
# which is what makes a GPU-free client install possible. libcuopt and cuopt_grpc_server
# both link it, so there is exactly one implementation of the mappers, not a client fork.
#
# LANGUAGES is deliberately not CUDA here: adding a .cu file to CUOPT_CLIENT_SRC_FILES
# should fail loudly rather than quietly reintroduce a CUDA dependency.
# Built as an OBJECT library first, mirroring cuopt_objs/cuopt, so that cuopt_static (for
# internal tests) can link the objects directly. The tests reach parser internals such as
# the fast MPS parser's mps_phase_registry_t, which linking the shared library would not
# give them. See the NOTE below for why this library's visibility differs from cuopt_objs.
add_library(cuopt_client_objs OBJECT ${CUOPT_CLIENT_SRC_FILES})
# NOTE: default visibility, deliberately unlike cuopt_objs.
#
# cuopt_objs can hide everything not marked CUOPT_EXPORT because libcuopt has a curated
# public C++ API. cuopt_client is different: it was carved out of the *internals*, so
# libcuopt itself depends on ~214 of its symbols (the whole cpu_optimization_problem_t /
# data_model_view_t / mps_data_model_t / grpc_client_t surface). Those are internal
# cross-library references, not a public API, and hiding them makes libcuopt.so fail to
# load with e.g. "undefined symbol: grpc_client_t::solve_mip".
#
# Curating them behind CUOPT_EXPORT would mean annotating essentially every host-side
# method, so default visibility is the right trade here.
set_target_properties(cuopt_client_objs
PROPERTIES POSITION_INDEPENDENT_CODE ON
CXX_SCAN_FOR_MODULES OFF
)

add_library(cuopt_client SHARED $<TARGET_OBJECTS:cuopt_client_objs>)
add_library(cuopt::cuopt_client ALIAS cuopt_client)

target_include_directories(cuopt_client
PUBLIC
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
"$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/include>"
INTERFACE
"$<INSTALL_INTERFACE:include>"
)

target_compile_definitions(cuopt_client
PUBLIC "CUOPT_LOG_ACTIVE_LEVEL=RAPIDS_LOGGER_LOG_LEVEL_${LIBCUOPT_LOGGING_LEVEL}"
)

set_target_properties(cuopt_client
PROPERTIES POSITION_INDEPENDENT_CODE ON
CXX_SCAN_FOR_MODULES OFF
BUILD_RPATH "\$ORIGIN"
INSTALL_RPATH "\$ORIGIN"
LINKER_LANGUAGE CXX
)

target_compile_definitions(cuopt_client_objs
PUBLIC "CUOPT_LOG_ACTIVE_LEVEL=RAPIDS_LOGGER_LOG_LEVEL_${LIBCUOPT_LOGGING_LEVEL}"
)

target_compile_options(cuopt_client_objs
PRIVATE "$<$<COMPILE_LANGUAGE:CXX>:${CUOPT_CXX_FLAGS}>"
)

target_include_directories(cuopt_client_objs
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}"
"${CMAKE_CURRENT_BINARY_DIR}/include"
$<$<BOOL:${CUOPT_PARSER_WITH_BZIP2}>:${BZIP2_INCLUDE_DIRS}>
$<$<BOOL:${CUOPT_PARSER_WITH_ZLIB}>:${ZLIB_INCLUDE_DIRS}>
PUBLIC
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
"$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/include>"
INTERFACE
"$<INSTALL_INTERFACE:include>"
)

# CCCL is a compile-time (header-only) dependency here: the fast MPS parser uses
# host helpers from <cuda/cmath> (ceil_div, round_up). It pulls in no CUDA runtime.
# bzip2 / zlib / lz4 are dlopen'd at runtime by file_to_string.cpp, so they are
# header-only here too and deliberately absent from the link line.
# The OBJECT library needs these for their INTERFACE include dirs / defines at compile time.
target_link_libraries(cuopt_client_objs
PUBLIC
rapids_logger::rapids_logger
CCCL::CCCL
# Header-only here, exactly like CCCL: the client sources transitively include
# <rmm/device_uvector.hpp> and <raft/core/device_span.hpp> via pdlp/solver_settings.hpp.
# No CUDA runtime is linked -- the device getters those headers declare only ever
# throw -- but without these the build relies on conda happening to put the headers on
# the default include path, and a CPM/fetched-rmm build fails to find them.
rmm::rmm
raft::raft
PRIVATE
simde::simde
OpenMP::OpenMP_CXX
$<$<BOOL:${CUOPT_ENABLE_GRPC}>:protobuf::libprotobuf>
$<$<BOOL:${CUOPT_ENABLE_GRPC}>:gRPC::grpc++>
)

target_link_libraries(cuopt_client
PUBLIC
rapids_logger::rapids_logger
CCCL::CCCL
PRIVATE
simde::simde
OpenMP::OpenMP_CXX
${CMAKE_DL_LIBS}
$<$<BOOL:${CUOPT_ENABLE_GRPC}>:protobuf::libprotobuf>
$<$<BOOL:${CUOPT_ENABLE_GRPC}>:gRPC::grpc++>
)

add_library(cuopt_objs OBJECT
${CUOPT_SRC_FILES}
)
Expand Down Expand Up @@ -757,6 +893,7 @@ target_compile_definitions(cuopt_objs PUBLIC

target_link_libraries(cuopt_objs
PUBLIC
cuopt::cuopt_client
CUDA::cublas
CUDA::cusparse
rmm::rmm
Expand All @@ -777,7 +914,10 @@ target_link_libraries(cuopt_objs
# - generate tests --------------------------------------------------------------------------------
if (BUILD_TESTS)
include(CTest)
add_library(cuopt_static STATIC $<TARGET_OBJECTS:cuopt_objs>)
# Embeds cuopt_client_objs directly rather than linking libcuopt_client.so: the internal
# test binaries reach parser internals that the shared library deliberately does not
# export. Do not also link cuopt::cuopt_client here -- that would duplicate every symbol.
add_library(cuopt_static STATIC $<TARGET_OBJECTS:cuopt_objs> $<TARGET_OBJECTS:cuopt_client_objs>)
target_link_libraries(cuopt_static
PUBLIC
CUDA::cublas
Expand Down Expand Up @@ -839,6 +979,7 @@ target_include_directories(cuopt
)
target_link_libraries(cuopt
PUBLIC
cuopt::cuopt_client
CUDA::cublas
CUDA::cusparse
rmm::rmm
Expand Down Expand Up @@ -904,14 +1045,14 @@ else ()
endif ()

# adds the .so files to the runtime deb package
install(TARGETS cuopt
install(TARGETS cuopt cuopt_client
DESTINATION ${_LIB_DEST}
COMPONENT runtime
EXPORT cuopt-exports
)

# adds the .so files to the development deb package
install(TARGETS cuopt
install(TARGETS cuopt cuopt_client
DESTINATION ${_LIB_DEST}
COMPONENT dev
)
Expand Down Expand Up @@ -939,7 +1080,7 @@ cuOpt library is a collection of GPU accelerated combinatorial optimization algo

rapids_export(INSTALL cuopt
EXPORT_SET cuopt-exports
GLOBAL_TARGETS cuopt
GLOBAL_TARGETS cuopt cuopt_client
NAMESPACE cuopt::
DOCUMENTATION doc_string
)
Expand All @@ -948,7 +1089,7 @@ rapids_export(INSTALL cuopt
# - build export -------------------------------------------------------------------------------
rapids_export(BUILD cuopt
EXPORT_SET cuopt-exports
GLOBAL_TARGETS cuopt
GLOBAL_TARGETS cuopt cuopt_client
NAMESPACE cuopt::
DOCUMENTATION doc_string
)
Expand Down
7 changes: 6 additions & 1 deletion cpp/src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,15 @@
# cmake-format: on

set(UTIL_SRC_FILES ${CMAKE_CURRENT_SOURCE_DIR}/utilities/seed_generator.cu
${CMAKE_CURRENT_SOURCE_DIR}/utilities/logger.cpp
${CMAKE_CURRENT_SOURCE_DIR}/utilities/version_info.cpp
${CMAKE_CURRENT_SOURCE_DIR}/utilities/timestamp_utils.cpp
${CMAKE_CURRENT_SOURCE_DIR}/utilities/work_unit_scheduler.cpp)

# logger.cpp backs <utilities/logger.hpp>, which both the parsers and the gRPC
# sources include, so it belongs to the CPU library. Without this, cuopt_client would
# have an undefined reference to the logger.
set(UTIL_CLIENT_SRC_FILES ${CMAKE_CURRENT_SOURCE_DIR}/utilities/logger.cpp)

add_subdirectory(linear_algebra)
add_subdirectory(pdlp)
add_subdirectory(math_optimization)
Expand All @@ -26,4 +30,5 @@ add_subdirectory(branch_and_bound)
add_subdirectory(cuts)

set(CUOPT_SRC_FILES ${CUOPT_SRC_FILES} ${UTIL_SRC_FILES} PARENT_SCOPE)
set(CUOPT_CLIENT_SRC_FILES ${CUOPT_CLIENT_SRC_FILES} ${UTIL_CLIENT_SRC_FILES} PARENT_SCOPE)
set(MPS_FAST_SRC_FILES ${MPS_FAST_SRC_FILES} PARENT_SCOPE)
7 changes: 6 additions & 1 deletion cpp/src/io/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,10 @@ set(PARSERS_SRC_FILES
${MPS_FAST_SRC_FILES}
)

set(CUOPT_SRC_FILES ${CUOPT_SRC_FILES} ${PARSERS_SRC_FILES} PARENT_SCOPE)
# The parsers and the host-side problem representation are CUDA-free (the only
# `cuda::` uses are host integer helpers from header-only libcu++), so they build
# into the CPU-only cuopt_client library rather than into cuopt_objs. That is what
# lets the Python data_model / solver_settings / io extension modules link a
# library with no CUDA runtime dependency.
set(CUOPT_CLIENT_SRC_FILES ${CUOPT_CLIENT_SRC_FILES} ${PARSERS_SRC_FILES} PARENT_SCOPE)
set(MPS_FAST_SRC_FILES ${MPS_FAST_SRC_FILES} PARENT_SCOPE)
10 changes: 9 additions & 1 deletion cpp/src/math_optimization/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,20 @@

list(PREPEND
MATH_OPT_SRC_FILES
${CMAKE_CURRENT_SOURCE_DIR}/solver_settings.cpp
${CMAKE_CURRENT_SOURCE_DIR}/solver_settings_gpu.cu
${CMAKE_CURRENT_SOURCE_DIR}/solution_reader.cu
${CMAKE_CURRENT_SOURCE_DIR}/solution_writer.cu
${CMAKE_CURRENT_SOURCE_DIR}/tic_toc.cpp
)

# solver_settings_t is host-only apart from the device members in solver_settings_gpu.cu,
# so the bulk of it builds into cuopt_client. The gRPC client takes a solver_settings_t
# in its public API, so this is required for the client library to resolve standalone.
set(MATH_OPT_CLIENT_SRC_FILES
${CMAKE_CURRENT_SOURCE_DIR}/solver_settings.cpp
)

set(CUOPT_SRC_FILES ${CUOPT_SRC_FILES}
${MATH_OPT_SRC_FILES} PARENT_SCOPE)
set(CUOPT_CLIENT_SRC_FILES ${CUOPT_CLIENT_SRC_FILES}
${MATH_OPT_CLIENT_SRC_FILES} PARENT_SCOPE)
Loading