Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Local Solvers

Three of the five backends run entirely on the machine calling them, using only local hardware and no account. dwave-cpu runs on the CPU; cuda-gpu and metal-gpu run custom simulated-annealing kernels on an NVIDIA or Apple Silicon GPU. The metal-gpu examples below ran for real, on an Apple Silicon Mac; no NVIDIA GPU was available while writing this chapter, so the cuda-gpu material comes from reading xqsa/cuda_gpu.py rather than from execution.

dwave-cpu: The Baseline

SolverDWaveCPU wraps dwave-samplersSimulatedAnnealingSampler. It needs only a CPU and no credentials, and that is exactly what makes it xqsa.DEFAULT_SOLVER: every other backend in this chapter is worth comparing against it before you trust a faster or more exotic answer. It ships in the base package – pip install xqsa is enough.

from xqsa import SolverDWaveCPU
from xqvm_py.xqmx import XQMX

model = XQMX.binary_model(size=4)
model.set_linear(0, -1)
model.set_quadratic(0, 1, 2)

solver = SolverDWaveCPU(seed=42)
result = solver.solve(model)
print(result.energy, result.metadata)

Constructor parameters, all overridable per call via solve(**kwargs):

ParameterDefaultMeaning
num_reads100Independent annealing runs; the best is kept
num_sweeps1000Sweeps per run
num_sweeps_per_beta1Sweeps held at each temperature level before cooling; num_sweeps must divide evenly
beta_rangeNoneInverse-temperature (start, end); None lets dwave-samplers pick
seedNoneRNG seed; None means non-deterministic

GPU Backends: cuda-gpu and metal-gpu

SolverCudaGPU and SolverMetalGPU bypass the D-Wave SDK entirely. Both convert the model to a dense array and run parallel-replica simulated annealing directly on the GPU, in custom CUDA or Metal kernels. Picking between the CPU backend and either GPU backend is a performance question, not a correctness one – see Backends; all three run the identical annealing algorithm family, just on different processors.

Install the extra that matches your hardware:

pip install xqsa[cuda]     # NVIDIA GPU, CUDA 12.x driver
pip install xqsa[metal]    # Apple Silicon Mac with a Metal device
from xqsa import SolverMetalGPU
from xqvm_py.xqmx import XQMX

model = XQMX.binary_model(size=4)
model.set_linear(0, -1)
model.set_quadratic(0, 1, 2)

solver = SolverMetalGPU(strategy="sa", num_reads=200, num_sweeps=2000, seed=42)
result = solver.solve(model)
print(result.energy)
# -1

Wall-clock result.timing is not shown here: it varies by roughly 2x between runs on identical hardware, so a single pasted number would read as a performance claim it cannot support. metal-gpu also supports strategy="gibbs" (block Gibbs sampling over a greedy graph colouring of the coupling graph). Both strategies produce correct output on real Metal hardware: xqsa/tests/test_gpu_validation.py checks each against models with a known exact ground state, plus a spin-glass model matched within 2% of the CPU reference energy, all six of which pass.

cuda-gpu supports only strategy="sa" – CUDA has no Gibbs kernel. See xqsa/cuda_gpu.py for the implementation the rest of this section describes.

Parametercuda-gpu defaultmetal-gpu defaultMeaning
strategy"sa" (only option)"sa" or "gibbs"Kernel to dispatch
num_reads100100Parallel replicas, one per GPU threadgroup/block
num_sweeps10001000Sweeps per replica
num_sweeps_per_beta11Sweeps held per temperature level before cooling
beta_schedule_type"linear""geometric"Shape of the cooling schedule – note the two backends default to different shapes
beta_rangeNoneNoneInverse-temperature (start, end); None auto-computes it from the model’s coefficients
seedNoneNoneBase seed for the on-device RNG

These are the constructor’s own defaults. xqsa.build_solver, the backend-agnostic constructor covered in Solving Overview, raises num_reads to 200 and num_sweeps to 2000 for both GPU backends instead of using these defaults.

metal-gpu computes coefficients in float32; cuda-gpu computes them in float64, end to end in its kernels. result.energy is always recomputed afterward in exact integer arithmetic on both, so either GPU run and a CPU run report the same authoritative energy regardless of which precision the search itself used. See Energy and Precision for what the float32 downcast costs metal-gpu and when it matters.

Driver Prerequisites

ExtraHardwarePrerequisiteVerify
[cuda]NVIDIA GPUCUDA 12.x drivernvidia-smi
[metal]Apple SiliconA Metal devicebuilt in on every Apple Silicon Mac

After installing, confirm the solver class constructs before building a model around it:

python -c "from xqsa import SolverCudaGPU; SolverCudaGPU(); print('ok')"
python -c "from xqsa import SolverMetalGPU; SolverMetalGPU(); print('ok')"

Both raise ImportError with a pip install xqsa[...] hint if the extra is missing, and RuntimeError if the extra is installed but no matching GPU is detected – import xqsa itself never fails on a missing extra, only constructing the solver that needs it does.