CuPy interchange

Device memory is shared with CuPy in both directions, with no host round-trip.

import cupy

model.set_start(x, cupy.full(n, 2.5))     # given straight to the model
model.objective(cupy_point)               # evaluated where the data already is
view = exa.as_cupy(backend_array)         # a CuPy view of the model's own memory

as_cupy publishes the backend array through CUDA’s array interface — the backend does not expose that interface itself, so it is built from the array’s pointer, length and element size. The result aliases the same memory: writing through the view writes into the model. The test for this asserts pointer identity rather than equal values, because equal values would pass on a copy.

from_cupy is the other direction, and is what the setters and the evaluation helpers use when handed anything exposing __cuda_array_interface__.

Ownership

The backend keeps ownership of its memory; a view holds a reference to the owner so it cannot be freed underneath. Going the other way, the CuPy array must outlive the wrapped view — the backend will not keep it alive for you.

Coexisting in one process

CuPy and the backend’s CUDA runtime work side by side; an allocation made by one survives the other using the device. There is one caveat that is a library-path problem rather than an interop one: CuPy’s pip-installed CUDA libraries can shadow the backend’s own, which the backend warns about. Keeping site-packages/nvidia/*/lib off LD_LIBRARY_PATH avoids it.

Install with pip install examodels[cuda].