GPU

Solving on an NVIDIA device takes the backend and the solver’s device stack — CUDA, MadNLP, MadNLPGPU and CUDSS — and install_backend("cuda") installs them together, into one environment. Run it once and restart Python afterwards — the environment cannot change under a running Julia:

import examodels as exa

exa.install_backend("cuda")      # CUDA + MadNLP + MadNLPGPU + CUDSS, together

A device model is then ordinary code — MadNLP and a device linear solver are chosen automatically. Measured on a Quadro GV100, the Lukšan–Vlček problem at 100 000 variables solves in 0.75 s on the device against 18.7 s with the same solver on the CPU and 5.6 s with Ipopt (all in a warm process):

import examodels as exa

N = 100_000
core = exa.Core(backend="cuda")
x = core.add_var(N, start=[-1.2 if i % 2 == 0 else 1.0 for i in range(N)])
core.add_obj(lambda i: 100 * (x[i-1]**2 - x[i])**2 + (x[i-1] - 1)**2,
             over=range(1, N))
core.add_con(lambda i: 3 * x[i+1]**3 + 2 * x[i+2] - 5
             + exa.sin(x[i+1] - x[i+2]) * exa.sin(x[i+1] + x[i+2])
             + 4 * x[i+1] - x[i] * exa.exp(x[i] - x[i+1]) - 3,
             over=range(0, N - 2))

sol = exa.Model(core).solve()          # MadNLP + cuDSS, chosen automatically
print(sol.status, sol.objective)

exa.backends() lists what can be constructed: serial, cpu (threads), cuda, rocm, oneapi, metal. Each is loaded only if asked for, so a host model never starts a GPU runtime.

Older GPUs need a pinned CUDA backend

A fresh resolve takes the newest CUDA.jl, whose bundled CUDA 12.9 runtime does not fully support compute-capability-7.0 (Volta) devices — the symptom is a warning at load and EXIT: Internal Error from MadNLP. On such a card, pin the backend before resolving: juliapkg.add("CUDA", "052768ef-5323-5732-b1bb-66c8b64840ba", version="=6.2.0").

What runs on a device

Ordinary models, multi-dimensional blocks, product index sets, tables, constraint augmentation, parameters and two-stage models all work on a device. Evaluation buffers and setter values are placed where the model’s arrays live, so nothing about the API changes.

The one thing that cannot is a nonlinear oracle with Python callbacks: a Python function cannot run inside a device kernel. Such an oracle runs with adapt=True, which copies to the host for each call — correct, but a round trip.

Sharing memory with CuPy

See CuPy interchange: device arrays can be passed in either direction with no host round-trip.

A known upstream limitation

CompressedNLPModel divides by the constraint count, so it raises on a model with no constraints. Nothing can be done about that from this side; it is recorded as a test so a fix upstream shows up.