CPU and GPU backends
Every method that runs a circuit takes a backend argument. You rarely need it: the default, "auto", picks the faster backend for the circuit’s size and falls back to the CPU when there is no GPU.
qc.run(shots=1000) # auto
qc.run(shots=1000, backend="cpu") # always the CPU
qc.run(shots=1000, backend="gpu") # the GPU, or RuntimeErrorThe three choices compared
| Property | CPU | GPU | auto |
|---|---|---|---|
| How you ask for it | backend="cpu" | backend="gpu" | backend="auto" (the default) |
| Precision | 64-bit floats | 32-bit floats, within about 1e-5 of the CPU for a few hundred gates | 64-bit below 14 qubits, 32-bit from 14 |
| Runs on | Every CPU core, through Rayon | Any NVIDIA, AMD, Intel or Apple GPU, through wgpu | Whichever is faster for the size |
| Fastest for | Fewer than 14 qubits | 14 qubits and more | Both, by switching at 14 |
| Largest register | 30 qubits (16 GiB) | What the GPU memory holds | That of the backend it picks |
| With no GPU | Works as usual | Raises RuntimeError | Falls back to the CPU |
Why auto switches at 14 qubits
Launching work on a GPU and reading the result back costs about a millisecond, whatever the circuit. Below 14 qubits the CPU finishes before that. From 14 qubits up the GPU is faster, and the gap grows with every qubit: at 22 qubits it is about 15 times faster than the CPU backend. The crossover was measured on one desktop (Ryzen 5 5500 and Radeon RX 6600) and may sit a qubit higher or lower on yours.
Circuits that measure part-way switch later, at 20 qubits. Each mid-circuit measurement waits for one number to come back from the GPU, about a millisecond, and every shot is simulated on its own, so those waits add up. Below 20 qubits the CPU finishes a whole shot first.
Precision
The CPU computes in 64-bit floats. GPUs generally lack fast 64-bit arithmetic, so the GPU backend uses 32-bit floats. On the circuits in the test suite (up to 18 qubits and a few hundred gates) it agrees with the CPU to about 1e-5. Rounding error grows with the number of gates, so deep circuits drift further. Counts from run() are unaffected in practice; pass backend="cpu" when you compare amplitudes to many decimal places.
Because "auto" switches at 14 qubits, statevector(), probabilities() and expectation() give 64-bit results below 14 qubits and 32-bit results from 14 on a machine with a GPU. If you compare results across register sizes, pass backend="cpu" so every size has the same precision.
On the GPU, each mid-circuit measurement renormalises the state using a 32-bit sum. The error is small but adds up over many measurements in one shot, such as repeated syndrome rounds. Use backend="cpu" for those circuits when precision matters.
Supported GPUs
qsengine reaches the GPU through wgpu, which speaks Vulkan, DirectX 12 and Metal. Recent NVIDIA, AMD, Intel and Apple GPUs all work with their normal drivers, and there is no CUDA or vendor toolkit to install.