Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
2e9de8d
add vrp support to the grpc server
tmckayus Jul 10, 2026
818bf71
Remove to_host_problem; pivot VRP gRPC to a single C++/Cython client
ramakrishnap-nv Jul 21, 2026
ce75942
Add compiled C++/Cython VRP gRPC client (single architecture)
ramakrishnap-nv Jul 21, 2026
bca7e01
Add VRP client serialization coverage tests + probe
ramakrishnap-nv Jul 21, 2026
a329c6c
Simplify VRP client: drop dead solve_vrp + unused decls
ramakrishnap-nv Jul 21, 2026
002c971
Address review: honor time_limit=0; result_vrp not_ready pre-check
ramakrishnap-nv Jul 21, 2026
7a61e65
Address review: sanitize VRP error; fix to_device host-buffer lifetim…
ramakrishnap-nv Jul 21, 2026
3dbe430
Merge branch 'main' into routing-grpc-vrp-e2e
ramakrishnap-nv Jul 22, 2026
ff54bba
Drive VRP proto/enum from field_registry (fix codegen-verify)
ramakrishnap-nv Jul 22, 2026
08a8bbc
Rename routing grpc tests to unique basenames
ramakrishnap-nv Jul 22, 2026
5db33e2
grpc: keep problem-representation logic out of the codegen for VRP
ramakrishnap-nv Jul 23, 2026
3db6b3a
Merge remote-tracking branch 'origin/main' into routing-grpc-vrp-e2e
ramakrishnap-nv Jul 24, 2026
ae8d5d8
grpc: state problem_category enum values explicitly (LP=0, MIP=1)
ramakrishnap-nv Jul 27, 2026
1f2efb4
grpc/vrp: fix result size accounting, time_limit=0, and client error …
ramakrishnap-nv Jul 28, 2026
e4ccc75
grpc/vrp: carry RoutingSolution as a structured message, not a bytes …
ramakrishnap-nv Jul 28, 2026
7d76dea
Merge remote-tracking branch 'origin/main' into routing-grpc-vrp-e2e
ramakrishnap-nv Jul 29, 2026
23f611b
Merge branch 'main' into routing-grpc-vrp-e2e
ramakrishnap-nv Aug 12, 2026
f74a057
grpc: isolate the routing arm so the component split stays mechanical
ramakrishnap-nv Aug 13, 2026
fd605da
grpc: export the routing symbols cuopt_grpc_server links
ramakrishnap-nv Aug 13, 2026
e0702dd
python: build the gRPC clients as one extension module
ramakrishnap-nv Aug 14, 2026
4183bd5
build(cmake): scope the routing gRPC define and fetch nlohmann_json v…
ramakrishnap-nv Aug 14, 2026
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
105 changes: 98 additions & 7 deletions cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,21 @@ if (NOT SKIP_GRPC_BUILD)
add_compile_definitions(CUOPT_ENABLE_GRPC)
message(STATUS "gRPC enabled (target gRPC::grpc++ is available)")

# The routing arm of the gRPC layer needs the routing engine. The wire
# protocol (including the routing protos) is always generated so LP-only
# builds still speak the same protocol; only the mappers and the VRP
# client/worker paths are dropped.
#
# This only gates which sources are compiled (see GRPC_ROUTING_FILES below).
# The matching -D is attached to cuopt_grpc_server alone, the one target
# whose sources test the macro, rather than to every target in the tree.
if (NOT SKIP_ROUTING_BUILD)
set(CUOPT_ENABLE_GRPC_ROUTING ON)
message(STATUS "gRPC routing (VRP) support enabled")
else ()
message(STATUS "gRPC routing (VRP) support disabled (SKIP_ROUTING_BUILD)")
endif ()

# Find protoc compiler (provided by config package or target)
if (TARGET protobuf::protoc)
get_target_property(_PROTOBUF_PROTOC protobuf::protoc IMPORTED_LOCATION_RELEASE)
Expand Down Expand Up @@ -423,9 +438,30 @@ if (NOT SKIP_GRPC_BUILD)
endif ()
endif ()

# Proto search paths: manual protos in src/grpc, generated data proto in src/grpc/codegen/generated
# Proto search paths: LP/protocol protos in src/grpc, routing protos in
# src/grpc/routing, generated data proto in src/grpc/codegen/generated.
# Routing protos live under their own search root so the routing gRPC layer
# stays a self-contained directory; their import names are unchanged.
set(PROTO_PATH_MANUAL "${CMAKE_CURRENT_SOURCE_DIR}/src/grpc")
set(PROTO_PATH_GEN "${CMAKE_CURRENT_SOURCE_DIR}/src/grpc/codegen/generated")
set(PROTO_PATH_ROUTING "${CMAKE_CURRENT_SOURCE_DIR}/src/grpc/routing")

# Routing solution proto (leaf; no imports). Embedded structurally by the
# generated data proto and by cuopt_remote.proto, so it is generated first.
set(ROUTING_SOLUTION_PROTO_FILE "${PROTO_PATH_ROUTING}/cuopt_routing_solution.proto")
set(ROUTING_SOLUTION_PROTO_SRCS "${CMAKE_CURRENT_BINARY_DIR}/cuopt_routing_solution.pb.cc")
set(ROUTING_SOLUTION_PROTO_HDRS "${CMAKE_CURRENT_BINARY_DIR}/cuopt_routing_solution.pb.h")

add_custom_command(
OUTPUT "${ROUTING_SOLUTION_PROTO_SRCS}" "${ROUTING_SOLUTION_PROTO_HDRS}"
COMMAND ${_PROTOBUF_PROTOC}
ARGS --cpp_out ${CMAKE_CURRENT_BINARY_DIR}
--proto_path ${PROTO_PATH_ROUTING}
${ROUTING_SOLUTION_PROTO_FILE}
DEPENDS ${ROUTING_SOLUTION_PROTO_FILE}
COMMENT "Generating C++ code from cuopt_routing_solution.proto"
VERBATIM
)

# Generate C++ code from cuopt_remote_data.proto (auto-generated data definitions)
set(DATA_PROTO_FILE "${PROTO_PATH_GEN}/cuopt_remote_data.proto")
Expand All @@ -437,8 +473,10 @@ if (NOT SKIP_GRPC_BUILD)
COMMAND ${_PROTOBUF_PROTOC}
ARGS --cpp_out ${CMAKE_CURRENT_BINARY_DIR}
--proto_path ${PROTO_PATH_GEN}
--proto_path ${PROTO_PATH_MANUAL}
--proto_path ${PROTO_PATH_ROUTING}
${DATA_PROTO_FILE}
DEPENDS ${DATA_PROTO_FILE}
DEPENDS ${DATA_PROTO_FILE} ${ROUTING_SOLUTION_PROTO_FILE}
COMMENT "Generating C++ code from cuopt_remote_data.proto"
VERBATIM
)
Expand All @@ -454,8 +492,9 @@ if (NOT SKIP_GRPC_BUILD)
ARGS --cpp_out ${CMAKE_CURRENT_BINARY_DIR}
--proto_path ${PROTO_PATH_MANUAL}
--proto_path ${PROTO_PATH_GEN}
--proto_path ${PROTO_PATH_ROUTING}
${PROTO_FILE}
DEPENDS ${PROTO_FILE} ${DATA_PROTO_FILE}
DEPENDS ${PROTO_FILE} ${DATA_PROTO_FILE} ${ROUTING_SOLUTION_PROTO_FILE}
COMMENT "Generating C++ code from cuopt_remote.proto"
VERBATIM
)
Expand All @@ -467,6 +506,24 @@ if (NOT SKIP_GRPC_BUILD)
set(GRPC_SERVICE_SRCS "${CMAKE_CURRENT_BINARY_DIR}/cuopt_remote_service.grpc.pb.cc")
set(GRPC_SERVICE_HDRS "${CMAKE_CURRENT_BINARY_DIR}/cuopt_remote_service.grpc.pb.h")

# Routing proto (standalone VRP messages; imported by service proto)
set(ROUTING_PROTO_FILE "${PROTO_PATH_ROUTING}/cuopt_routing.proto")
set(ROUTING_PROTO_SRCS "${CMAKE_CURRENT_BINARY_DIR}/cuopt_routing.pb.cc")
set(ROUTING_PROTO_HDRS "${CMAKE_CURRENT_BINARY_DIR}/cuopt_routing.pb.h")

add_custom_command(
OUTPUT "${ROUTING_PROTO_SRCS}" "${ROUTING_PROTO_HDRS}"
COMMAND ${_PROTOBUF_PROTOC}
ARGS --cpp_out ${CMAKE_CURRENT_BINARY_DIR}
--proto_path ${PROTO_PATH_ROUTING}
--proto_path ${PROTO_PATH_MANUAL}
--proto_path ${PROTO_PATH_GEN}
${ROUTING_PROTO_FILE}
DEPENDS ${ROUTING_PROTO_FILE} ${PROTO_FILE} ${DATA_PROTO_FILE} ${ROUTING_SOLUTION_PROTO_FILE}
COMMENT "Generating C++ code from cuopt_routing.proto"
VERBATIM
)

add_custom_command(
OUTPUT "${GRPC_PROTO_SRCS}" "${GRPC_PROTO_HDRS}" "${GRPC_SERVICE_SRCS}" "${GRPC_SERVICE_HDRS}"
COMMAND ${_PROTOBUF_PROTOC}
Expand All @@ -475,8 +532,9 @@ if (NOT SKIP_GRPC_BUILD)
--plugin=protoc-gen-grpc=${_GRPC_CPP_PLUGIN_EXECUTABLE}
--proto_path ${PROTO_PATH_MANUAL}
--proto_path ${PROTO_PATH_GEN}
--proto_path ${PROTO_PATH_ROUTING}
${GRPC_PROTO_FILE}
DEPENDS ${GRPC_PROTO_FILE} ${PROTO_FILE} ${DATA_PROTO_FILE}
DEPENDS ${GRPC_PROTO_FILE} ${PROTO_FILE} ${DATA_PROTO_FILE} ${ROUTING_PROTO_FILE} ${ROUTING_SOLUTION_PROTO_FILE}
COMMENT "Generating gRPC C++ code from cuopt_remote_service.proto"
VERBATIM
)
Expand Down Expand Up @@ -524,12 +582,21 @@ if (DEFINE_ASSERT)
endif ()

if (NOT SKIP_GRPC_BUILD)
# Add gRPC mapper files and generated protobuf sources
set(GRPC_INFRA_FILES
# Generated protobuf sources. The whole wire protocol is generated in every
# gRPC build, routing messages included: cuopt_remote.proto embeds
# RoutingSolution, so these carry no dependency on the routing engine.
set(GRPC_PROTO_GENERATED_FILES
${ROUTING_SOLUTION_PROTO_SRCS}
${DATA_PROTO_SRCS}
${PROTO_SRCS}
${ROUTING_PROTO_SRCS}
${GRPC_PROTO_SRCS}
${GRPC_SERVICE_SRCS}
)

# LP/MIP arm: proto <-> mathematical_optimization, plus the shared client
# infrastructure (connection, chunking, polling, Cython shim).
set(GRPC_MATHOPT_FILES
src/grpc/grpc_problem_mapper.cpp
src/grpc/grpc_solution_mapper.cpp
src/grpc/grpc_settings_mapper.cpp
Expand All @@ -539,6 +606,22 @@ if (NOT SKIP_GRPC_BUILD)
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.
set(GRPC_ROUTING_FILES
src/grpc/routing/grpc_routing_problem_mapper.cpp
src/grpc/routing/grpc_routing_settings_mapper.cpp
src/grpc/routing/grpc_routing_solution_mapper.cpp
src/grpc/routing/grpc_client_vrp.cpp
src/grpc/routing/cython_grpc_client_vrp.cpp
)

set(GRPC_INFRA_FILES ${GRPC_PROTO_GENERATED_FILES} ${GRPC_MATHOPT_FILES})
if (CUOPT_ENABLE_GRPC_ROUTING)
list(APPEND GRPC_INFRA_FILES ${GRPC_ROUTING_FILES})
endif ()
list(APPEND CUOPT_SRC_FILES ${GRPC_INFRA_FILES})

# Always keep NDEBUG defined for gRPC infrastructure files so that abseil
Expand All @@ -548,7 +631,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")
set_property(SOURCE ${PROTO_SRCS} ${GRPC_PROTO_SRCS} ${GRPC_SERVICE_SRCS} ${DATA_PROTO_SRCS} DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
# Generated protobuf symbols must stay visible in libcuopt.so: cuopt_grpc_server
# links them from the library rather than compiling the protos itself.
set_property(SOURCE ${GRPC_PROTO_GENERATED_FILES} DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
APPEND PROPERTY COMPILE_OPTIONS "$<$<COMPILE_LANGUAGE:CXX>:-fvisibility=default>")
endif (NOT SKIP_GRPC_BUILD)

Expand Down Expand Up @@ -1033,6 +1118,12 @@ if (NOT SKIP_GRPC_BUILD)
POSITION_INDEPENDENT_CODE ON
)

# grpc_worker.cpp is the only source that tests this macro, so scope it to
# this target instead of leaking a -D into every target in the directory.
if (CUOPT_ENABLE_GRPC_ROUTING)
target_compile_definitions(cuopt_grpc_server PRIVATE CUOPT_ENABLE_GRPC_ROUTING)
endif ()

target_compile_options(cuopt_grpc_server
PRIVATE "$<$<COMPILE_LANGUAGE:CXX>:${CUOPT_CXX_FLAGS}>"
)
Expand Down
35 changes: 35 additions & 0 deletions cpp/cmake/thirdparty/get_nlohmann_json.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# cmake-format: off
# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
# cmake-format: on

# Header-only JSON parser, used by the routing gRPC test driver to read problem
# files. Uses rapids_cpm_find so an existing install that ships a CMake config
# package (conda, system) is reused, and the pinned source is fetched via CPM
# otherwise. Going through CPM also populates the CPM cache, so any later use of
# nlohmann_json in the project resolves without repeating the search/download.
#
# NOTE: nlohmann_json is not part of rapids-cmake's version file, so the version
# is pinned here rather than by rapids_cpm_<pkg>. The pin matches cuVS
# (rapidsai/cuvs cpp/cmake/thirdparty/get_nlohmann_json.cmake) and the
# nlohmann_json conda package, so the conda copy is reused rather than fetched.
#
# Test-only: not added to cuopt's BUILD/INSTALL export sets, since nothing that
# is installed links it.
function(find_and_configure_nlohmann_json)
set(oneValueArgs VERSION PINNED_TAG)
cmake_parse_arguments(PKG "" "${oneValueArgs}" "" ${ARGN})

rapids_cpm_find(nlohmann_json ${PKG_VERSION}
GLOBAL_TARGETS nlohmann_json::nlohmann_json
CPM_ARGS
GIT_REPOSITORY https://github.com/nlohmann/json.git
GIT_TAG ${PKG_PINNED_TAG}
EXCLUDE_FROM_ALL
OPTIONS
"JSON_BuildTests OFF"
"JSON_Install OFF"
)
endfunction()

find_and_configure_nlohmann_json(VERSION 3.12.0 PINNED_TAG v3.12.0)
24 changes: 24 additions & 0 deletions cpp/include/cuopt/grpc/cython_grpc_client.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

#include <cuopt/export.hpp>
#include <cuopt/mathematical_optimization/utilities/cython_solve.hpp>
#include <cuopt/routing/cpu_routing_problem.hpp>

#include <cstddef>
#include <cstdint>
Expand All @@ -23,6 +24,11 @@ class data_model_view_t;
} // namespace io
} // namespace cuopt::mathematical_optimization

namespace cuopt::routing {
template <typename i_t, typename f_t>
class solver_settings_t;
} // namespace cuopt::routing

namespace cuopt {
namespace CUOPT_EXPORT cython {

Expand Down Expand Up @@ -58,6 +64,13 @@ struct grpc_result_outcome_t {
std::unique_ptr<solver_ret_t> solution;
};

struct grpc_vrp_result_outcome_t {
bool not_ready = false;
bool success = false;
std::string error_message;
cuopt::routing::cpu_routing_solution_t solution;
};

struct grpc_logs_result_t {
bool success = false;
std::string error_message;
Expand Down Expand Up @@ -139,6 +152,17 @@ class grpc_python_client_t {
*/
grpc_result_outcome_t result(const std::string& job_id);

/**
* @brief Submit a VRP problem (unary only). Reuse status/wait/delete/result_vrp.
*/
grpc_submit_result_t submit_vrp(cuopt::routing::cpu_routing_problem_t* problem,
cuopt::routing::solver_settings_t<int, float>* settings);

/**
* @brief Fetch and parse a completed VRP job's routing solution.
*/
grpc_vrp_result_outcome_t result_vrp(const std::string& job_id);

/**
* @brief Block until the job completes, collecting all solver log lines.
*/
Expand Down
Loading
Loading