diff --git a/build.sh b/build.sh index 218505ed46..0a17d2567f 100755 --- a/build.sh +++ b/build.sh @@ -15,7 +15,7 @@ REPODIR=$(cd "$(dirname "$0")"; pwd) LIBCUOPT_BUILD_DIR=${LIBCUOPT_BUILD_DIR:=${REPODIR}/cpp/build} LIBMPS_PARSER_BUILD_DIR=${LIBMPS_PARSER_BUILD_DIR:=${REPODIR}/cpp/libmps_parser/build} -VALIDARGS="clean libcuopt cuopt_grpc_server libmps_parser cuopt_mps_parser cuopt cuopt_server cuopt_sh_client docs deb -a -b -g -fsanitize -tsan -msan -v -l= --verbose-pdlp --build-lp-only --no-fetch-rapids --skip-c-python-adapters --skip-tests-build --skip-routing-build --skip-grpc-build --skip-fatbin-write --host-lineinfo [--cmake-args=\\\"\\\"] [--cache-tool=] -n --allgpuarch --ci-only-arch --show_depr_warn -h --help" +VALIDARGS="clean libcuopt cuopt_grpc_server libmps_parser cuopt_mps_parser cuopt cuopt_server cuopt_sh_client docs deb -a -b -g -fsanitize -tsan -msan -v -l= --verbose-pdlp --build-lp-only --math --routing --no-fetch-rapids --skip-c-python-adapters --skip-tests-build --skip-routing-build --skip-grpc-build --skip-fatbin-write --host-lineinfo [--cmake-args=\\\"\\\"] [--cache-tool=] -n --allgpuarch --ci-only-arch --show_depr_warn -h --help" HELP="$0 [ ...] [ ...] where is: clean - remove all existing build artifacts and configuration (start over) @@ -41,9 +41,11 @@ HELP="$0 [ ...] [ ...] -l= - log level. Options are: TRACE | DEBUG | INFO | WARN | ERROR | CRITICAL | OFF. Default=INFO --verbose-pdlp - verbose mode for pdlp solver --build-lp-only - build only linear programming components, excluding routing package and MIP-specific files + --math - math-only slice: C++ LP/MILP/QP stack without routing; Python LP slice; cuopt.routing raises ImportError + --routing - routing-only slice: C++ routing without math stack; Python routing slice; cuopt.linear_programming raises ImportError --skip-c-python-adapters - skip building C and Python adapter files (cython_solve.cu and cuopt_c.cpp) --skip-tests-build - disable building of all tests - --skip-routing-build - skip building routing components + --skip-routing-build - legacy CMake flag: native routing C++ off only (same routing half as --math). Does not set the Python slice; prefer --math for a coherent math-only install. --skip-grpc-build - skip building gRPC and protobuf components (auto-enabled with -tsan) --skip-fatbin-write - skip the fatbin write --host-lineinfo - build with debug line information for host code @@ -86,6 +88,9 @@ SKIP_C_PYTHON_ADAPTERS=0 SKIP_TESTS_BUILD=0 SKIP_ROUTING_BUILD=0 SKIP_GRPC_BUILD=0 +CUOPT_BUILD_MATH_CMAKE=ON +CUOPT_BUILD_ROUTING_CMAKE=ON +CUOPT_PYTHON_COMPONENT=FULL WRITE_FATBIN=1 HOST_LINEINFO=0 CACHE_ARGS=() @@ -316,6 +321,16 @@ if [ ${BUILD_LP_ONLY} -eq 1 ] && [ ${SKIP_C_PYTHON_ADAPTERS} -eq 0 ]; then exit 1 fi +if hasArg --math && hasArg --routing; then + echo "ERROR: --math and --routing cannot be used together." + exit 1 +fi + +if hasArg --routing && [ ${SKIP_ROUTING_BUILD} -eq 1 ]; then + echo "ERROR: --routing conflicts with --skip-routing-build (or --build-lp-only, which implies skip routing)." + exit 1 +fi + if [ ${BUILD_SANITIZER} -eq 1 ] && [ ${BUILD_TSAN} -eq 1 ]; then echo "ERROR: -fsanitize and -tsan cannot be used together" echo "AddressSanitizer and ThreadSanitizer are mutually exclusive" @@ -347,9 +362,20 @@ else fi fi +# Product slices (--math / --routing): native CUOPT_BUILD_* + Python CUOPT_PYTHON_COMPONENT +if hasArg --math && ! hasArg --routing; then + CUOPT_BUILD_MATH_CMAKE=ON + CUOPT_BUILD_ROUTING_CMAKE=OFF + CUOPT_PYTHON_COMPONENT=LP +elif hasArg --routing && ! hasArg --math; then + CUOPT_BUILD_MATH_CMAKE=OFF + CUOPT_BUILD_ROUTING_CMAKE=ON + CUOPT_PYTHON_COMPONENT=ROUTING +fi + ################################################################################ # Configure, build, and install libmps_parser -if buildAll || hasArg libmps_parser; then +if { buildAll || hasArg libmps_parser; } && [ "${CUOPT_PYTHON_COMPONENT}" != "ROUTING" ]; then mkdir -p "${LIBMPS_PARSER_BUILD_DIR}" cd "${LIBMPS_PARSER_BUILD_DIR}" cmake -DDEFINE_ASSERT=${DEFINE_ASSERT} \ @@ -367,6 +393,10 @@ fi ################################################################################ # Configure and build libcuopt (and optionally just the gRPC server) if buildAll || hasArg libcuopt || hasArg cuopt_grpc_server; then + if hasArg cuopt_grpc_server && [ "${CUOPT_BUILD_MATH_CMAKE}" != "ON" ]; then + echo "ERROR: cuopt_grpc_server requires the math stack (CUOPT_BUILD_MATH=ON). Omit --routing or use --math / full build." + exit 1 + fi mkdir -p "${LIBCUOPT_BUILD_DIR}" cd "${LIBCUOPT_BUILD_DIR}" cmake -DDEFINE_ASSERT=${DEFINE_ASSERT} \ @@ -385,6 +415,8 @@ if buildAll || hasArg libcuopt || hasArg cuopt_grpc_server; then -DSKIP_C_PYTHON_ADAPTERS=${SKIP_C_PYTHON_ADAPTERS} \ -DBUILD_TESTS=$((1 - ${SKIP_TESTS_BUILD})) \ -DSKIP_ROUTING_BUILD=${SKIP_ROUTING_BUILD} \ + -DCUOPT_BUILD_MATH=${CUOPT_BUILD_MATH_CMAKE} \ + -DCUOPT_BUILD_ROUTING=${CUOPT_BUILD_ROUTING_CMAKE} \ -DSKIP_GRPC_BUILD=${SKIP_GRPC_BUILD} \ -DWRITE_FATBIN=${WRITE_FATBIN} \ -DHOST_LINEINFO=${HOST_LINEINFO} \ @@ -426,12 +458,12 @@ if buildAll || hasArg cuopt; then cd "${REPODIR}"/python/cuopt # $EXTRA_CMAKE_ARGS gets concatenated into a string with [*] and then we find/replace spaces with semi-colons - SKBUILD_CMAKE_ARGS="-DCMAKE_PREFIX_PATH=${INSTALL_PREFIX};-DCMAKE_LIBRARY_PATH=${LIBCUOPT_BUILD_DIR};-DCMAKE_CUDA_ARCHITECTURES=${CUOPT_CMAKE_CUDA_ARCHITECTURES};${EXTRA_CMAKE_ARGS[*]// /;}" \ + SKBUILD_CMAKE_ARGS="-DCMAKE_PREFIX_PATH=${INSTALL_PREFIX};-DCMAKE_LIBRARY_PATH=${LIBCUOPT_BUILD_DIR};-DCMAKE_CUDA_ARCHITECTURES=${CUOPT_CMAKE_CUDA_ARCHITECTURES};-DCUOPT_PYTHON_COMPONENT=${CUOPT_PYTHON_COMPONENT};${EXTRA_CMAKE_ARGS[*]// /;}" \ python "${PYTHON_ARGS_FOR_INSTALL[@]}" . fi # Build and install the cuopt MPS parser Python package -if buildAll || hasArg cuopt_mps_parser; then +if { buildAll || hasArg cuopt_mps_parser; } && [ "${CUOPT_PYTHON_COMPONENT}" != "ROUTING" ]; then cd "${REPODIR}"/python/cuopt/cuopt/linear_programming SKBUILD_CMAKE_ARGS="-DCMAKE_PREFIX_PATH=${INSTALL_PREFIX};-DCMAKE_LIBRARY_PATH=${LIBCUOPT_BUILD_DIR};-DCMAKE_CUDA_ARCHITECTURES=${CUOPT_CMAKE_CUDA_ARCHITECTURES};${EXTRA_CMAKE_ARGS[*]// /;}" \ @@ -439,13 +471,13 @@ if buildAll || hasArg cuopt_mps_parser; then fi # Build and install the cuopt_server Python package -if buildAll || hasArg cuopt_server; then +if { buildAll || hasArg cuopt_server; } && [ "${CUOPT_PYTHON_COMPONENT}" != "ROUTING" ]; then cd "${REPODIR}"/python/cuopt_server python "${PYTHON_ARGS_FOR_INSTALL[@]}" . fi # Build and install the cuopt_sh_client Python package -if buildAll || hasArg cuopt_sh_client; then +if { buildAll || hasArg cuopt_sh_client; } && [ "${CUOPT_PYTHON_COMPONENT}" != "ROUTING" ]; then cd "${REPODIR}"/python/cuopt_self_hosted/ python "${PYTHON_ARGS_FOR_INSTALL[@]}" . fi diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index e7b4693547..c7eed06253 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -50,11 +50,36 @@ option(CMAKE_CUDA_LINEINFO "Enable the -lineinfo option for nvcc useful for cuda option(BUILD_TESTS "Configure CMake to build tests" ON) option(BUILD_LP_ONLY "Build only linear programming components, exclude routing and MIP-specific files" OFF) option(SKIP_C_PYTHON_ADAPTERS "Skip building C and Python adapter files (cython_solve.cu and cuopt_c.cpp)" OFF) -option(SKIP_ROUTING_BUILD "Skip building routing components" OFF) +option(SKIP_ROUTING_BUILD "Skip building routing components (same effect as CUOPT_BUILD_ROUTING=OFF)" OFF) option(SKIP_GRPC_BUILD "Skip building gRPC and protobuf components" OFF) option(WRITE_FATBIN "Enable fatbin writing" ON) option(HOST_LINEINFO "Build with debug line information for host code" OFF) +# Product slices: both ON = full libcuopt. Use build.sh --math / --routing or -DCUOPT_BUILD_*=OFF. +option(CUOPT_BUILD_MATH "Build LP/MILP/QP C++ stack (pdlp, mip, simplex, …)" ON) +option(CUOPT_BUILD_ROUTING "Build vehicle routing C++ stack" ON) + +if(SKIP_ROUTING_BUILD) + set(CUOPT_BUILD_ROUTING OFF CACHE BOOL "Build vehicle routing C++ stack" FORCE) +endif() + +if(NOT CUOPT_BUILD_MATH AND NOT CUOPT_BUILD_ROUTING) + message(FATAL_ERROR "At least one of CUOPT_BUILD_MATH or CUOPT_BUILD_ROUTING must be ON.") +endif() + +# LP remote/gRPC mappers depend on the math headers; routing-only lib cannot compile them. +set(CUOPT_ENABLE_GRPC OFF) + +if(NOT CUOPT_BUILD_MATH AND NOT SKIP_GRPC_BUILD) + message(STATUS "CUOPT_BUILD_MATH=OFF: disabling gRPC (requires math stack).") + set(SKIP_GRPC_BUILD ON CACHE BOOL "" FORCE) +endif() + +if(NOT CUOPT_BUILD_MATH AND BUILD_TESTS) + message(STATUS "CUOPT_BUILD_MATH=OFF: disabling C++ unit tests (require math stack).") + set(BUILD_TESTS OFF CACHE BOOL "" FORCE) +endif() + message(VERBOSE "cuOpt: Enable nvcc -lineinfo: ${CMAKE_CUDA_LINEINFO}") message(VERBOSE "cuOpt: Build cuOpt unit-tests: ${BUILD_TESTS}") message(VERBOSE "cuOpt: Build cuOpt multigpu tests: ${BUILD_TESTS}") @@ -62,6 +87,7 @@ message(VERBOSE "cuOpt: Disable OpenMP: ${DISABLE_OPENMP}") message(VERBOSE "cuOpt: Build LP-only mode: ${BUILD_LP_ONLY}") message(VERBOSE "cuOpt: Skip C/Python adapters: ${SKIP_C_PYTHON_ADAPTERS}") message(VERBOSE "cuOpt: Skip routing build: ${SKIP_ROUTING_BUILD}") +message(VERBOSE "cuOpt: CUOPT_BUILD_MATH=${CUOPT_BUILD_MATH} CUOPT_BUILD_ROUTING=${CUOPT_BUILD_ROUTING}") message(VERBOSE "cuOpt: Build with debug line information for host code: ${HOST_LINEINFO}") message(VERBOSE "cuOpt: fatbin: ${WRITE_FATBIN}") @@ -241,53 +267,54 @@ else () find_package(RAFT REQUIRED) endif () -FetchContent_Declare( - papilo - GIT_REPOSITORY "https://github.com/scipopt/papilo.git" - # We would want to get the main branch. However, the main branch - # does not have some of the presolvers and settings that we need - # Mainly, probing and clique merging. - # This is the reason we are using the development branch - # from Oct 12, 2025. Once these changes are merged into the main branch, - #we can switch to the main branch. - GIT_TAG "741a2b9c8155b249d6df574d758b4d97d4417520" - GIT_PROGRESS TRUE - EXCLUDE_FROM_ALL - SYSTEM -) - -find_package(TBB REQUIRED) -set(BUILD_TESTING OFF CACHE BOOL "Disable test build for papilo") -set(PAPILO_NO_BINARIES ON) -option(LUSOL "Disable LUSOL" OFF) - -FetchContent_MakeAvailable(papilo) - -# PSLP - Lightweight C presolver for linear programs -# https://github.com/dance858/PSLP -FetchContent_Declare( - pslp - GIT_REPOSITORY "https://github.com/dance858/PSLP.git" - GIT_TAG "v0.0.8" - GIT_PROGRESS TRUE - EXCLUDE_FROM_ALL - SYSTEM -) - -# Build PSLP as static to embed in cuopt (avoids runtime library path issues) -set(BUILD_SHARED_LIBS_SAVED ${BUILD_SHARED_LIBS}) -set(BUILD_SHARED_LIBS OFF) -FetchContent_MakeAvailable(pslp) -set(BUILD_SHARED_LIBS ${BUILD_SHARED_LIBS_SAVED}) - +if(CUOPT_BUILD_MATH) + FetchContent_Declare( + papilo + GIT_REPOSITORY "https://github.com/scipopt/papilo.git" + # We would want to get the main branch. However, the main branch + # does not have some of the presolvers and settings that we need + # Mainly, probing and clique merging. + # This is the reason we are using the development branch + # from Oct 12, 2025. Once these changes are merged into the main branch, + #we can switch to the main branch. + GIT_TAG "741a2b9c8155b249d6df574d758b4d97d4417520" + GIT_PROGRESS TRUE + EXCLUDE_FROM_ALL + SYSTEM + ) + + find_package(TBB REQUIRED) + set(BUILD_TESTING OFF CACHE BOOL "Disable test build for papilo") + set(PAPILO_NO_BINARIES ON) + option(LUSOL "Disable LUSOL" OFF) + + FetchContent_MakeAvailable(papilo) + + # PSLP - Lightweight C presolver for linear programs + # https://github.com/dance858/PSLP + FetchContent_Declare( + pslp + GIT_REPOSITORY "https://github.com/dance858/PSLP.git" + GIT_TAG "v0.0.8" + GIT_PROGRESS TRUE + EXCLUDE_FROM_ALL + SYSTEM + ) + + # Build PSLP as static to embed in cuopt (avoids runtime library path issues) + set(BUILD_SHARED_LIBS_SAVED ${BUILD_SHARED_LIBS}) + set(BUILD_SHARED_LIBS OFF) + FetchContent_MakeAvailable(pslp) + set(BUILD_SHARED_LIBS ${BUILD_SHARED_LIBS_SAVED}) + + find_package(CUDSS REQUIRED) +endif() include(${rapids-cmake-dir}/cpm/rapids_logger.cmake) # generate logging macros rapids_cpm_rapids_logger(BUILD_EXPORT_SET cuopt-exports INSTALL_EXPORT_SET cuopt-exports) create_logger_macros(CUOPT "cuopt::default_logger()" include/cuopt) -find_package(CUDSS REQUIRED) - # ################################################################################################## # - gRPC and Protobuf setup ----------------------------------------------------------------------- @@ -461,17 +488,40 @@ endif () add_library(cuopt::cuopt ALIAS cuopt) # ################################################################################################## # - include paths --------------------------------------------------------------------------------- -message(STATUS "target include directories CUDSS_INCLUDES = ${CUDSS_INCLUDE}") - -# 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 - "${papilo_SOURCE_DIR}/src" - "${papilo_BINARY_DIR}" -) - -target_include_directories(cuopt SYSTEM PRIVATE - "${pslp_SOURCE_DIR}/include" -) +if(CUOPT_BUILD_MATH) + message(STATUS "target include directories CUDSS_INCLUDES = ${CUDSS_INCLUDE}") + + # 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 + "${papilo_SOURCE_DIR}/src" + "${papilo_BINARY_DIR}" + ) + + target_include_directories(cuopt SYSTEM PRIVATE + "${pslp_SOURCE_DIR}/include" + ) + + target_include_directories(cuopt + PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}" + "${CUDSS_INCLUDE}" + PUBLIC + "$" + INTERFACE + ${CUDSS_INCLUDE} + ) + + # Link PSLP by file to avoid export dependency tracking + target_link_libraries(cuopt PRIVATE $) + add_dependencies(cuopt PSLP) + + add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/libmps_parser) + set(CMAKE_LIBRARY_PATH ${CMAKE_CURRENT_BINARY_DIR}/libmps_parser/) + + # 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}") +endif() target_include_directories(cuopt PRIVATE @@ -480,38 +530,24 @@ target_include_directories(cuopt "${CMAKE_CURRENT_SOURCE_DIR}/src/grpc" "${CMAKE_CURRENT_SOURCE_DIR}/src/grpc/client" "${CMAKE_CURRENT_BINARY_DIR}" - "${CUDSS_INCLUDE}" PUBLIC "$" "$" - "$" INTERFACE "$" - ${CUDSS_INCLUDE} ) -# 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) - -add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/libmps_parser) -set(CMAKE_LIBRARY_PATH ${CMAKE_CURRENT_BINARY_DIR}/libmps_parser/) - - -# 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}") +if(CUOPT_BUILD_MATH) + list(APPEND CUOPT_PRIVATE_CUDA_LIBS CUDA::curand CUDA::cusolver) + list(APPEND CUOPT_PRIVATE_CUDA_LIBS TBB::tbb) + list(PREPEND CUOPT_PRIVATE_CUDA_LIBS CUDA::cublasLt) +endif() execute_process( COMMAND git rev-parse --short HEAD @@ -537,16 +573,19 @@ target_compile_definitions(cuopt PUBLIC CUOPT_CUDA_ARCHITECTURES="${JOINED_CUDA_ARCHITECTURES}" CUOPT_CPU_ARCHITECTURE="${CMAKE_SYSTEM_PROCESSOR}") -target_link_libraries(cuopt - PUBLIC +set(_cuopt_link_public CUDA::cublas - CUDA::cusparse rmm::rmm rapids_logger::rapids_logger CCCL::CCCL raft::raft - cuopt::mps_parser - ${CUDSS_LIB_FILE} +) +if(CUOPT_BUILD_MATH) + list(PREPEND _cuopt_link_public CUDA::cusparse) + list(APPEND _cuopt_link_public cuopt::mps_parser ${CUDSS_LIB_FILE}) +endif() +target_link_libraries(cuopt + PUBLIC ${_cuopt_link_public} PRIVATE ${CUOPT_PRIVATE_CUDA_LIBS} $<$:protobuf::libprotobuf> @@ -583,14 +622,18 @@ else () endif () # adds the .so files to the runtime deb package -install(TARGETS cuopt mps_parser +set(_cuopt_install_libs cuopt) +if(TARGET mps_parser) + list(APPEND _cuopt_install_libs mps_parser) +endif() +install(TARGETS ${_cuopt_install_libs} DESTINATION ${_LIB_DEST} COMPONENT runtime EXPORT cuopt-exports ) # adds the .so files to the development deb package -install(TARGETS cuopt mps_parser +install(TARGETS ${_cuopt_install_libs} DESTINATION ${_LIB_DEST} COMPONENT dev ) @@ -659,7 +702,7 @@ rapids_cpm_find( GIT_SHALLOW TRUE ) -if (NOT BUILD_LP_ONLY) +if (NOT BUILD_LP_ONLY AND CUOPT_BUILD_MATH) add_executable(cuopt_cli cuopt_cli.cpp) # PIE executable: auditwheel/patchelf expands .dynstr/RPATH when repairing wheels; non-PIE @@ -711,7 +754,7 @@ endif () option(BUILD_MIP_BENCHMARKS "Build MIP benchmarks" OFF) -if (BUILD_MIP_BENCHMARKS AND NOT BUILD_LP_ONLY) +if (BUILD_MIP_BENCHMARKS AND NOT BUILD_LP_ONLY AND CUOPT_BUILD_MATH) add_executable(solve_MIP ../benchmarks/linear_programming/cuopt/run_mip.cpp) target_include_directories(solve_MIP PRIVATE @@ -747,7 +790,7 @@ if (BUILD_MIP_BENCHMARKS AND NOT BUILD_LP_ONLY) endif () option(BUILD_LP_BENCHMARKS "Build LP benchmarks" OFF) -if (BUILD_LP_BENCHMARKS) +if (BUILD_LP_BENCHMARKS AND CUOPT_BUILD_MATH) add_executable(solve_LP ../benchmarks/linear_programming/cuopt/run_pdlp.cu) set_target_properties(solve_LP @@ -773,7 +816,7 @@ endif () # ################################################################################################## # - cuopt_grpc_server - gRPC-based remote server -------------------------------------------------- -if (NOT SKIP_GRPC_BUILD) +if (NOT SKIP_GRPC_BUILD AND CUOPT_BUILD_MATH) add_executable(cuopt_grpc_server src/grpc/server/grpc_server_main.cpp src/grpc/server/grpc_server_logger.cpp @@ -834,7 +877,7 @@ if (NOT SKIP_GRPC_BUILD) ) message(STATUS "Building cuopt_grpc_server (gRPC-based remote solve server)") -endif (NOT SKIP_GRPC_BUILD) +endif () # ################################################################################################## # - CPack has to be the last item in the cmake file------------------------------------------------- diff --git a/cpp/src/CMakeLists.txt b/cpp/src/CMakeLists.txt index c99210bf34..0ce144669b 100644 --- a/cpp/src/CMakeLists.txt +++ b/cpp/src/CMakeLists.txt @@ -9,18 +9,18 @@ set(UTIL_SRC_FILES ${CMAKE_CURRENT_SOURCE_DIR}/utilities/seed_generator.cu ${CMAKE_CURRENT_SOURCE_DIR}/utilities/timestamp_utils.cpp ${CMAKE_CURRENT_SOURCE_DIR}/utilities/work_unit_scheduler.cpp) -add_subdirectory(pdlp) -add_subdirectory(math_optimization) -add_subdirectory(mip_heuristics) +if(CUOPT_BUILD_MATH) + add_subdirectory(pdlp) + add_subdirectory(math_optimization) + add_subdirectory(mip_heuristics) + add_subdirectory(dual_simplex) + add_subdirectory(barrier) + add_subdirectory(branch_and_bound) + add_subdirectory(cuts) +endif() -# Only build routing for full builds, not LP-only builds -if(NOT SKIP_ROUTING_BUILD) +if(CUOPT_BUILD_ROUTING) add_subdirectory(routing) endif() -add_subdirectory(dual_simplex) -add_subdirectory(barrier) -add_subdirectory(branch_and_bound) -add_subdirectory(cuts) - set(CUOPT_SRC_FILES ${CUOPT_SRC_FILES} ${UTIL_SRC_FILES} PARENT_SCOPE) diff --git a/cpp/tests/CMakeLists.txt b/cpp/tests/CMakeLists.txt index 2c1aa5be73..72d0953f29 100644 --- a/cpp/tests/CMakeLists.txt +++ b/cpp/tests/CMakeLists.txt @@ -91,18 +91,20 @@ endif(RAPIDS_DATASET_ROOT_DIR) # ## test sources if(BUILD_TESTS) - if(NOT SKIP_ROUTING_BUILD) + if(CUOPT_BUILD_ROUTING) add_subdirectory(routing) add_subdirectory(distance_engine) add_subdirectory(examples) endif() - if(NOT BUILD_LP_ONLY) - add_subdirectory(mip) + if(CUOPT_BUILD_MATH) + if(NOT BUILD_LP_ONLY) + add_subdirectory(mip) + endif() + add_subdirectory(linear_programming) + add_subdirectory(dual_simplex) + add_subdirectory(qp) + add_subdirectory(utilities) endif() - add_subdirectory(linear_programming) - add_subdirectory(dual_simplex) - add_subdirectory(qp) - add_subdirectory(utilities) enable_testing() endif() diff --git a/python/cuopt/CMakeLists.txt b/python/cuopt/CMakeLists.txt index 66bfbd3e16..34a4ede92b 100644 --- a/python/cuopt/CMakeLists.txt +++ b/python/cuopt/CMakeLists.txt @@ -23,11 +23,24 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CUDA_STANDARD 20) set(CMAKE_CUDA_STANDARD_REQUIRED ON) +set(CUOPT_PYTHON_COMPONENT "FULL" CACHE STRING "Python package slice: FULL, LP (math), or ROUTING") +set_property(CACHE CUOPT_PYTHON_COMPONENT PROPERTY STRINGS FULL LP ROUTING) + +configure_file( + "${CMAKE_CURRENT_SOURCE_DIR}/cuopt/_build_variant.py.in" + "${CMAKE_CURRENT_SOURCE_DIR}/cuopt/_build_variant.py" + @ONLY +) + find_package(cuopt "${RAPIDS_VERSION}") -find_package(mps_parser "${RAPIDS_VERSION}") +if(NOT CUOPT_PYTHON_COMPONENT STREQUAL "ROUTING") + find_package(mps_parser "${RAPIDS_VERSION}") +endif() include(rapids-cython-core) rapids_cython_init() add_subdirectory(cuopt) -add_subdirectory(cuopt/linear_programming/internals/) +if(NOT CUOPT_PYTHON_COMPONENT STREQUAL "ROUTING") + add_subdirectory(cuopt/linear_programming/internals/) +endif() diff --git a/python/cuopt/cuopt/CMakeLists.txt b/python/cuopt/cuopt/CMakeLists.txt index d996471797..c33a0d671d 100644 --- a/python/cuopt/cuopt/CMakeLists.txt +++ b/python/cuopt/cuopt/CMakeLists.txt @@ -3,16 +3,25 @@ # SPDX-License-Identifier: Apache-2.0 # cmake-format: on -add_subdirectory(distance_engine) -add_subdirectory(linear_programming/data_model) -add_subdirectory(linear_programming/solver) +if(NOT CUOPT_PYTHON_COMPONENT STREQUAL "LP") + add_subdirectory(distance_engine) +endif() +if(NOT CUOPT_PYTHON_COMPONENT STREQUAL "ROUTING") + add_subdirectory(linear_programming/data_model) + add_subdirectory(linear_programming/solver) +endif() # We don't need to have mps_parser within cuOpt # Remove subdirectory addition in future -add_subdirectory(routing) +if(NOT CUOPT_PYTHON_COMPONENT STREQUAL "LP") + add_subdirectory(routing) +endif() set(cython_sources) -set(linked_libraries cuopt::cuopt cuopt::mps_parser) +set(linked_libraries cuopt::cuopt) +if(NOT CUOPT_PYTHON_COMPONENT STREQUAL "ROUTING") + list(APPEND linked_libraries cuopt::mps_parser) +endif() rapids_cython_create_modules(SOURCE_FILES "${cython_sources}" LINKED_LIBRARIES "${linked_libraries}" ASSOCIATED_TARGETS cuopt CXX) diff --git a/python/cuopt/cuopt/__init__.py b/python/cuopt/cuopt/__init__.py index 08f36edcb9..5b12de7147 100644 --- a/python/cuopt/cuopt/__init__.py +++ b/python/cuopt/cuopt/__init__.py @@ -2,18 +2,50 @@ # SPDX-License-Identifier: Apache-2.0 try: - import libcuopt + from cuopt._build_variant import CUOPT_PYTHON_BUILD_COMPONENT as _CUOPT_SLICE except ModuleNotFoundError: - pass + _CUOPT_SLICE = "FULL" + +# Native loader: try split wheels first, then full libcuopt +if _CUOPT_SLICE == "LP": + for _native in ("libcuopt_lp", "libcuopt"): + try: + _m = __import__(_native, fromlist=["load_library"]) + except ModuleNotFoundError: + continue + _m.load_library() + del _m + break + del _native +elif _CUOPT_SLICE == "ROUTING": + for _native in ("libcuopt_routing", "libcuopt"): + try: + _m = __import__(_native, fromlist=["load_library"]) + except ModuleNotFoundError: + continue + _m.load_library() + del _m + break + del _native else: - libcuopt.load_library() - del libcuopt + for _native_pkg in ("libcuopt", "libcuopt_lp", "libcuopt_routing"): + try: + _m = __import__(_native_pkg, fromlist=["load_library"]) + except ModuleNotFoundError: + continue + _m.load_library() + del _m + break + del _native_pkg from cuopt._version import __git_commit__, __version__, __version_major_minor__ -# Lazy imports for linear_programming, routing, and distance_engine modules -# This allows cuopt to be imported on CPU-only hosts when remote solve is configured -_submodules = ["linear_programming", "routing", "distance_engine"] +if _CUOPT_SLICE == "ROUTING": + _submodules = ["routing", "distance_engine"] +elif _CUOPT_SLICE == "LP": + _submodules = ["linear_programming"] +else: + _submodules = ["linear_programming", "routing", "distance_engine"] def __getattr__(name): diff --git a/python/cuopt/cuopt/_build_variant.py.in b/python/cuopt/cuopt/_build_variant.py.in new file mode 100644 index 0000000000..85b44e65f3 --- /dev/null +++ b/python/cuopt/cuopt/_build_variant.py.in @@ -0,0 +1,8 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 +# +# Generated by CMake from _build_variant.py.in — do not edit the generated file. + +"""Records which native/Python slice this wheel was built for (FULL, LP, ROUTING).""" + +CUOPT_PYTHON_BUILD_COMPONENT = "@CUOPT_PYTHON_COMPONENT@" diff --git a/python/cuopt/cuopt/distance_engine/__init__.py b/python/cuopt/cuopt/distance_engine/__init__.py index c2452bb330..693f98ed33 100644 --- a/python/cuopt/cuopt/distance_engine/__init__.py +++ b/python/cuopt/cuopt/distance_engine/__init__.py @@ -1,4 +1,15 @@ # SPDX-FileCopyrightText: Copyright (c) 2023-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 +try: + from cuopt._build_variant import CUOPT_PYTHON_BUILD_COMPONENT as _CUOPT_SLICE +except ModuleNotFoundError: + _CUOPT_SLICE = "FULL" + +if _CUOPT_SLICE == "LP": + raise ImportError( + "cuopt.distance_engine is not available: this installation was built for LP/math only " + "(e.g. build.sh --math). Use a routing or full build for distance_engine." + ) + from cuopt.distance_engine.waypoint_matrix import WaypointMatrix diff --git a/python/cuopt/cuopt/linear_programming/__init__.py b/python/cuopt/cuopt/linear_programming/__init__.py index d267c21718..743354b698 100644 --- a/python/cuopt/cuopt/linear_programming/__init__.py +++ b/python/cuopt/cuopt/linear_programming/__init__.py @@ -1,6 +1,17 @@ # SPDX-FileCopyrightText: Copyright (c) 2023-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 +try: + from cuopt._build_variant import CUOPT_PYTHON_BUILD_COMPONENT as _CUOPT_SLICE +except ModuleNotFoundError: + _CUOPT_SLICE = "FULL" + +if _CUOPT_SLICE == "ROUTING": + raise ImportError( + "cuopt.linear_programming is not available: this installation was built for ROUTING only " + "(e.g. build.sh --routing). Use a math/full build for LP/MILP/QP." + ) + from cuopt.linear_programming import internals from cuopt.linear_programming.data_model import DataModel from cuopt.linear_programming.problem import Problem diff --git a/python/cuopt/cuopt/routing/__init__.py b/python/cuopt/cuopt/routing/__init__.py index 081d58f998..2f93b6d391 100644 --- a/python/cuopt/cuopt/routing/__init__.py +++ b/python/cuopt/cuopt/routing/__init__.py @@ -1,6 +1,17 @@ # SPDX-FileCopyrightText: Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 +try: + from cuopt._build_variant import CUOPT_PYTHON_BUILD_COMPONENT as _CUOPT_SLICE +except ModuleNotFoundError: + _CUOPT_SLICE = "FULL" + +if _CUOPT_SLICE == "LP": + raise ImportError( + "cuopt.routing is not available: this installation was built for LP/math only " + "(e.g. build.sh --math). Use a routing or full build for vehicle routing." + ) + from cuopt.routing.assignment import Assignment, SolutionStatus from cuopt.routing.utils import ( add_vehicle_constraints, diff --git a/python/cuopt_self_hosted/pyproject.toml b/python/cuopt_self_hosted/pyproject.toml index f4a3b75a60..128972d435 100644 --- a/python/cuopt_self_hosted/pyproject.toml +++ b/python/cuopt_self_hosted/pyproject.toml @@ -16,8 +16,7 @@ readme = { file = "README.md", content-type = "text/markdown" } authors = [ { name = "NVIDIA Corporation" }, ] -license = "Apache-2.0" -license-files = ["LICENSE"] +license = { text = "Apache-2.0" } requires-python = ">=3.11" dependencies = [ "cuopt-mps-parser==26.6.*,>=0.0.0a0", diff --git a/python/cuopt_server/pyproject.toml b/python/cuopt_server/pyproject.toml index 4f9f141011..f006f60568 100644 --- a/python/cuopt_server/pyproject.toml +++ b/python/cuopt_server/pyproject.toml @@ -17,8 +17,7 @@ readme = { file = "README.md", content-type = "text/markdown" } authors = [ { name = "NVIDIA Corporation" }, ] -license = "Apache-2.0" -license-files = ["LICENSE"] +license = { text = "Apache-2.0" } requires-python = ">=3.11" dependencies = [ "cuopt==26.6.*,>=0.0.0a0",