All skills
nvidia avatar

/cuopt-developer

@e37c0a0
by NVIDIA Corporationnvidia/skills3.5k stars
424

Modify, build, test, debug, and contribute to NVIDIA cuOpt (C++/CUDA, Python, server, CI). Use for solver internals, PRs, DCO, and code conventions.

Use this Skill: https://skilld.dev/gh/nvidia/skills/cuopt-developer

This session only. Nothing lands on disk.

referencespython_bindings.md

≈2k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Python Bindings Guide

How Python bindings work in cuOpt and how to extend them.

Architecture: Three Layers

Python API Layer (.py)        ← User-facing, docstrings, convenience methods
        ↓
Cython Wrapper Layer (.pyx)   ← Memory management, GIL handling, type conversion
        ↓
C++ Implementation (.hpp/.cu) ← Solver logic, CUDA kernels

Key Directories

Layer Path Purpose
Library loader python/libcuopt/libcuopt/load.py Dynamically loads libcuopt.so via ctypes
Python API python/cuopt/cuopt/linear_programming/ User-facing classes (Problem, SolverSettings)
Python API python/cuopt/cuopt/routing/ Routing API
Cython bindings python/cuopt/cuopt/linear_programming/solver/solver_wrapper.pyx Solver bridge
Cython bindings python/cuopt/cuopt/linear_programming/data_model/data_model_wrapper.pyx Data model bridge
Cython declarations python/cuopt/cuopt/linear_programming/solver/solver.pxd C++ interface declarations
Cython declarations python/cuopt/cuopt/linear_programming/data_model/data_model.pxd C++ interface declarations
C++ headers cpp/include/cuopt/mathematical_optimization/ Public API
C++ implementation cpp/src/ Solver internals

File Types

Extension Purpose Example
.pxd Cython declaration — declares C++ classes, functions, enums for Cython solver.pxd
.pyx Cython implementation — wraps C++ in Python-callable code solver_wrapper.pyx
.py Pure Python — user-facing API, no direct C++ calls solver.py, data_model.py

How a Parameter Flows: End-to-End Example

Tracing optimality_tolerance from Python to C++:

Step 1: User Python code

settings = SolverSettings()
settings.set_optimality_tolerance(1e-2)
solution = linear_programming.Solve(data_model, settings)

Step 2: Python API stores the setting

python/cuopt/cuopt/linear_programming/solver_settings/solver_settings.py:

def set_optimality_tolerance(self, eps_optimal):
    for param in solver_params:
        if param.endswith("tolerance"):
            self.settings_dict[param] = eps_optimal

Parameters are discovered at import time from C++ via reflection (see step 3).

Step 3: Cython discovers parameter names from C++

python/cuopt/cuopt/linear_programming/solver/solver_parameters.pyx:

cpdef get_solver_parameter_names():
    cdef unique_ptr[solver_settings_t[int, double]] unique_solver_settings
    unique_solver_settings.reset(new solver_settings_t[int, double]())
    cdef vector[string] parameter_names = unique_solver_settings.get().get_parameter_names()

    cdef list py_parameter_names = []
    for i in range(parameter_names.size()):
        py_parameter_names.append(parameter_names[i].decode("utf-8"))
    return py_parameter_names

solver_params = get_solver_parameter_names()  # Called at import time

Step 4: Cython passes settings to C++

python/cuopt/cuopt/linear_programming/solver/solver_wrapper.pyx:

cdef set_solver_setting(
        unique_ptr[solver_settings_t[int, double]]& unique_solver_settings,
        settings, ...):
    cdef solver_settings_t[int, double]* c_solver_settings = unique_solver_settings.get()
    for name, value in settings.settings_dict.items():
        c_solver_settings.set_parameter_from_string(
            name.encode('utf-8'),
            str(value).encode('utf-8')
        )

Step 5: Cython calls C++ solver with GIL released

def Solve(py_data_model_obj, settings, mip=False):
    # ... setup ...
    with nogil:  # Release Python GIL for GPU computation
        sol_ret_ptr = move(call_solve(
            data_model_obj.c_data_model_view.get(),
            unique_solver_settings.get(),
        ))
    return create_solution(move(sol_ret_ptr), data_model_obj)

Always release the GIL around C++ calls that do GPU work — this allows other Python threads to run during solve.

Step 6: C++ implementation receives the call

cpp/src/math_optimization/solver_settings.cu:

void solver_settings_t<i_t, f_t>::set_parameter_from_string(
    const std::string& name, const std::string& value)
{
    // Routes to appropriate setter
    pdlp_settings_.set_optimality_tolerance(std::stof(value));
}

Key Cython Patterns

Declaring C++ classes in .pxd

cdef extern from "cuopt/mathematical_optimization/solver_settings.hpp" namespace "cuopt::mathematical_optimization":
    ctypedef enum pdlp_solver_mode_t "cuopt::mathematical_optimization::pdlp_solver_mode_t":
        Stable1 "cuopt::mathematical_optimization::pdlp_solver_mode_t::Stable1"
        Stable2 "cuopt::mathematical_optimization::pdlp_solver_mode_t::Stable2"

    cdef cppclass solver_settings_t[i_t, f_t]:
        solver_settings_t() except +
        vector[string] get_parameter_names()
        void set_parameter_from_string(const string& name, const string& value) except +

C++ object lifecycle with unique_ptr

from libcpp.memory cimport unique_ptr, move

cdef unique_ptr[solver_settings_t[int, double]] settings
settings.reset(new solver_settings_t[int, double]())
# Auto-destroyed when scope exits

Bridging C++ enums to Python IntEnum

class PDLPSolverMode(IntEnum):
    Stable1 = pdlp_solver_mode_t.Stable1
    Stable2 = pdlp_solver_mode_t.Stable2

Type conversions

Direction Pattern
Python str → C++ string name.encode('utf-8')
C++ string → Python str cstring.decode('utf-8')
C++ vector<double> → numpy np.asarray(<double[:size]> vec.data()).copy()
numpy → C++ pointer Pass .data pointer via Cython typed memoryview

Device memory handling

from rmm.pylibrmm.device_buffer import DeviceBuffer

if result_ptr.is_gpu():
    solution_buf = DeviceBuffer.c_from_unique_ptr(
        move(get_gpu_solution(result_ptr[0]))
    )
    solution = series_from_buf(solution_buf, pa.float64()).to_numpy()

Build System

Cython modules are built via CMake + rapids-cython-core.

CMakeLists.txt pattern

python/cuopt/cuopt/linear_programming/solver/CMakeLists.txt:

set(cython_sources solver_wrapper.pyx solver_parameters.pyx)
set(linked_libraries cuopt::cuopt)
rapids_cython_create_modules(...)

Build command

./build.sh cuopt    # Builds Cython extensions + Python package

After modifying .pyx or .pxd files, you must rebuild: Cython changes are not reflected until recompiled.

Adding a New Parameter: Checklist

  1. C++ header — Add parameter to settings struct in cpp/include/cuopt/
  2. C++ implementation — Add setter/getter and wire into set_parameter_from_string() in cpp/src/
  3. Cython declaration (.pxd) — If the parameter requires a new C++ method signature, declare it
  4. Cython wrapper (.pyx) — If using the string-based parameter interface (set_parameter_from_string), no .pyx change is needed — the parameter is auto-discovered via reflection
  5. Python API (.py) — Add a convenience method in SolverSettings if warranted
  6. Server schema — Update docs/cuopt/source/cuopt_spec.yaml if the parameter should be server-accessible
  7. Tests — Add tests at both C++ (cpp/tests/) and Python (python/cuopt/cuopt/tests/) levels
  8. Rebuild — ./build.sh libcuopt && ./build.sh cuopt

Lazy Loading Pattern

python/cuopt/cuopt/__init__.py uses lazy imports for CPU-only environments:

_submodules = ["linear_programming", "routing", "distance_engine"]

def __getattr__(name):
    if name in _submodules:
        import importlib
        return importlib.import_module(f"cuopt.{name}")
    raise AttributeError(...)

This allows importing cuopt on hosts without a GPU (e.g., for remote solve via server).

Source: SKILL.md on GitHub

1 alert3mo3 checks · Risk SAFE
  • Gen Agent Trust Hub3mo

    The cuopt-developer skill is a comprehensive and secure development environment guide for the NVIDIA cuOpt project. It incorporates robust safety guardrails, including explicit prohibitions against privileged operations (sudo), system-level modifications, and insecure coding practices such as the use of 'eval()' on user input or remote script execution via 'curl | bash'. The skill emphasizes reproducible, user-space environment management using conda and standard project tools.

  • Socket3mo

    No alerts

  • Snyk3mo

    Risk: CRITICAL · 1 issue

Signed by skilld at e37c0a0. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub yesterday.

Activeupdated 3 months ago
version
26.08.00
Other metadata
metadata
{
  "author": "NVIDIA cuOpt Team",
  "tags": [
    "cuopt",
    "development",
    "contributing",
    "cpp-cuda",
    "python-bindings"
  ]
}

README badge

README badge for nvidia/skills/cuopt-developer