Skip to content

GPU state vector backend based on cuQuantum (cuStateVec) - #4

Closed
GrovyleX wants to merge 5 commits into
matulni:mainfrom
GrovyleX:gpu-backend
Closed

GPU state vector backend based on cuQuantum (cuStateVec)#4
GrovyleX wants to merge 5 commits into
matulni:mainfrom
GrovyleX:gpu-backend

Conversation

@GrovyleX

Copy link
Copy Markdown

Implements a GPU-accelerated state vector backend for Graphix pattern simulation, built on NVIDIA cuQuantum (cuStateVec) and CuPy, following the template in this repository. Addresses TeamGraphix/graphix#496 (unitaryHACK 2026).

Design

  • The state is a flat CuPy array of constant size 2**max_space (complex128); only the first 2**nqubit entries are meaningful, so the active register can grow (N) and shrink (M) without reallocating GPU memory. flatten() copies the active slice back to the host.
  • Graphix uses the most-significant-bit convention, cuStateVec the least-significant-bit one; indices are converted with _msb_to_lsb` before every cuStateVec call.
  • Single-/multi-qubit gates and CZ entanglement use custatevec.apply_matrix; single-qubit expectation values use custatevec.compute_expectation (divided by the squared norm to match the reference backend); qubit removal, swap and tensor use CuPy.
  • The cuStateVec handle and device gate constants are created lazily, so the module imports without a GPU (CI runners can import and type-check it).
  • add_nodes has a device-only fast path for the common single-|+> case (an N command), and StatevectorBackend.with_capacity(max_qubits) preallocates the buffer for pattern simulation.
  • evolve (multi-qubit, circuit-only) is intentionally left unimplemented, as in the template.

Tests

Every operation and full pattern simulation are compared against the reference graphix.sim.statevec backend. The suite is skipped automatically when no CUDA device is available (@pytest.mark.skipif(not _gpu_available())), as suggested in the issue. Passes ruff, ruff format, mypy (strict), pyright, and pytest (49 tests on an NVIDIA GTX 1650).

Benchmark results

End-to-end QFT pattern simulation (with the min_space pass), CPU vs GPU, on a GTX 1650 (4 GB). GPU timings use explicit cupy.cuda.Device().synchronize(); each point is the fastest of three runs (benchmarks/compare_cpu_gpu.py).

qubits max space CPU (ms) GPU (ms) speed-up
2 3 4.2 18.1 0.2x
4 5 38.6 81.2 0.5x
6 7 61.0 345.4 0.2x
8 9 190.1 496.3 0.4x
10 11 553.4 1039.0 0.5x
12 13 1132.1 1930.5 0.6x
14 15 3382.5 2460.2 1.4x
16 17 1562.9
18 19 3614.3
20 21 13722.7

CPU vs GPU benchmark

For small patterns the CPU backend wins — each MBQC command is a tiny operation and the GPU's fixed per-call overhead dominates. The crossover is around 14 qubits; beyond it the CPU cost grows steeply (at 16 qubits the CPU backend did not finish a single QFT simulation within 15 minutes), whereas the GPU backend reaches 20 qubits in ~14 s. The value of the GPU backend is therefor reaching pattern sizes that are impractical on the CPU. Timings are noisy and
hardware-dependent; the GTX 1650 is an entry-level card.

AI disclosure (unitaryHACK policy)

I used an LLM (Claude) to help work through the cuStateVec API and to speed up boilerplate. The work is mine: I set up the GPU environment, ran and debugged the backend on my own hardware, reviewed every line against the reference implementation, wrote and ran the tests, and produced the benchmarks. I can explain and defend the whole change.

GrovyleX added 5 commits June 10, 2026 11:14
Add the graphix_statevec_cuquantum package: a Statevec (DenseState) backed by a flat CuPy buffer of size 2**max_space, and a StatevectorBackend. Gates and CZ entanglement use cuStateVec apply_matrix, single-qubit expectation values use compute_expectation, and qubit removal/swap/tensor use CuPy. Graphix MSB indices are converted to cuStateVec LSB index bits. The cuStateVec handle and device gate constants are created lazily so the module imports without a GPU. Replaces the template package.
Add the cuquantum optional dependency (cupy-cuda12x, cuquantum-python-cu12), treat cupy and cuquantum as untyped for mypy, drop the unused console script, and regenerate the lock file.
Compare every operation and full pattern simulation against graphix.sim.statevec. The suite is skipped automatically when no CUDA device is available.
Time end-to-end QFT pattern simulation on the CPU and GPU backends with explicit CPU-GPU synchronization, and update the template micro-benchmarks for the new backend.
@matulni

matulni commented Jun 11, 2026

Copy link
Copy Markdown
Owner

Thank you for your contribution and the interest in the project!

As you may have noticed, PR #1 already address the same issue.

The overall approach of your PR is very similar to theirs, and most differences consist of applying the feedback given in that PR.

Could you clarify how your contribution differs from or improves upon the other PR and the discussion therein?

@GrovyleX

Copy link
Copy Markdown
Author

Thanks - yeah, fair, #1 is clearly ahead and has had the deeper review. No need for two near-identical PRs; happy to defer to it. I'll close this. Thanks for looking. Or just leave it open and quiet.

@GrovyleX GrovyleX closed this Jun 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants