NVIDIA / NVIDIA/cuopt

Converge LP/MIP remote execution on the explicit-client pattern (remove remote-solve registry)

Open
#1,692 2 comments 0 reactions 1 assignee View on GitHub

@rg20 is already working on this.

Since Aug 7, 2026.

awaiting response
Dominant language
Cuda
Stars
1k
Forks
233
Avg merge
4d 4h
Merged PRs (30d)
95

Description

Background

LP/MIP remote execution is triggered implicitly: solve_lp / solve_mip check is_remote_execution_enabled() (set by CUOPT_REMOTE_HOST / CUOPT_REMOTE_PORT) and reroute. Because that check lives inside cuopt_mathematical_optimization, the engine needs to call into cuopt_grpc — the reverse of the dependency direction, since cuopt_grpc already links the engine.

The workaround is a function-pointer registry: cuopt_mathematical_optimization holds nullable slots that libcuopt_grpc.so's ELF constructor fills via register_remote_solvers(), reached through a lazy dlopen. That lazy load is also why the slots have to be atomic — the constructor publishes them on whichever thread triggers the load while others may be reading.

Raised by @hlinsen in #1622:

The umbrella/CLI/Python layer should select local versus remote, with cuopt_grpc depending one-way on mathematical-optimization types. Can we remove the dlopen/atomic registration mechanism entirely?

The pattern already exists

#1597 (routing over gRPC) does this the clean way. Zero uses of CUOPT_REMOTE_HOST, is_remote_execution_enabled or get_memory_backend_type; no registry, no dlopen, no constructor registration. The user asks for remote explicitly:

client = RoutingClient("gpu-host:50051")
client.solve(data_model, settings)

cpu_routing_problem_t is a plain data type in cuopt_routing; the mapper, client and server live in cuopt_grpc. The dependency stays one-way and the routing engine stays local-only.

LP/MIP is the outlier, not the pattern.

Proposal

Add LPClient / MIPClient mirroring RoutingClient, then delete the registry:

  • cpp/include/cuopt/mathematical_optimization/remote_solve_registry.hpp
  • cpp/src/pdlp/remote_solve_registry.cpp
  • cpp/src/grpc/client/grpc_registration.cpp
  • the remote branches in cpp/src/pdlp/solve.cu, cpp/src/mip_heuristics/solve.cu, cpp/src/pdlp/utilities/cython_solve.cu and cpp/cuopt_cli.cpp

cuopt_mathematical_optimization becomes local-only and every component dependency is one-way. The dlopen, the atomics and the ELF constructor all go with it.

Open question

What happens to CUOPT_REMOTE_HOST / CUOPT_REMOTE_PORT. Keeping them as a deprecated compatibility path preserves existing users' setups; removing them is cleaner but is a user-visible break. This is a product decision, not a build one.

Sequencing

Depends on #1622 (component split) and #1597 (routing over gRPC). Best done in or right after #1597, which already owns the gRPC client layer and establishes the pattern — rather than in #1622, which would mean duplicating that design work and adding a user-visible API change to a build-focused PR.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.