Converge LP/MIP remote execution on the explicit-client pattern (remove remote-solve registry)
@rg20 is already working on this.
Since Aug 7, 2026.
- 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_grpcdepending 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.hppcpp/src/pdlp/remote_solve_registry.cppcpp/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.cuandcpp/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
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.