Capability and Support Matrix
This page records which CPU and GPU architectures each PRISM-Q backend supports, and where distributed execution stands. CPU backends are written in portable Rust and run on every supported architecture; SIMD acceleration (AVX2/FMA/BMI2 on x86-64, NEON on ARM64) is selected at runtime where a kernel exists, otherwise a scalar path is used.
Legend
| Mark | Meaning |
|---|---|
| Yes | Supported |
| SIMD | Supported with a dedicated SIMD-accelerated kernel on this architecture |
| Scalar | Runs, but without a dedicated SIMD kernel (portable fallback) |
| No | Not available for this backend |
| Planned | Not implemented yet; on the roadmap |
Backend support by architecture
The nine CPU backends implement the Backend trait; the distributed statevector
backend is a tenth, feature-gated implementation covered by the Distributed
column. Planned marks only work the roadmap carries: a ROCm port of the
existing CUDA kernels. Backends without a CUDA kernel have nothing to port, so
their ROCm cell is No, and the roadmap carries no distributed execution for
any backend other than the statevector.
| Backend | x86-64 | AVX2/FMA/BMI2 | ARM64 | NEON | CUDA (NVIDIA) | ROCm (AMD) | Distributed |
|---|---|---|---|---|---|---|---|
| Statevector | Yes | SIMD | Yes | SIMD | Yes | Planned | Yes |
| Stabilizer | Yes | SIMD | Yes | SIMD | Yes | Planned | No |
| Factored Stabilizer | Yes | SIMD | Yes | SIMD | No | No | No |
| Sparse | Yes | Scalar | Yes | Scalar | No | No | No |
| MPS | Yes | SIMD | Yes | SIMD | No | No | No |
| Product State | Yes | Scalar | Yes | Scalar | No | No | No |
| Tensor Network | Yes | Scalar | Yes | Scalar | No | No | No |
| Factored | Yes | SIMD | Yes | SIMD | No | No | No |
| Density Matrix | Yes | SIMD | Yes | SIMD | No | No | No |
The Clifford+T engines below are not Backend implementations; they serve
probability, shot, and observable queries through their own routes (see
Clifford+T Simulation).
| Engine | x86-64 | AVX2/FMA/BMI2 | ARM64 | NEON | CUDA (NVIDIA) | ROCm (AMD) | Distributed |
|---|---|---|---|---|---|---|---|
| Stabilizer Rank | Yes | SIMD | Yes | SIMD | No | No | No |
| Stochastic Pauli | Yes | Scalar | Yes | Scalar | No | No | No |
| Deterministic Pauli | Yes | Scalar | Yes | Scalar | No | No | No |
Notes:
- AVX2/FMA/BMI2 is the x86-64 SIMD tier. The active tier is chosen at runtime (AVX2+FMA, then FMA, then SSE2 baseline). See Threading, SIMD, and Memory Layout.
- NEON is the ARM64 SIMD tier. Backends marked
SIMDcarry a NEON kernel that mirrors the x86-64 path; the rest fall back to scalar code on ARM64. - CUDA covers the optional
gpufeature. Only the statevector and stabilizer paths have device kernels; every other backend runs on CPU. See the GPU Backend guide. - Distributed covers the optional
distributedanddistributed-mpifeatures. The statevector backend splits the state across MPI ranks with exact results, including gates, measurement, reset, and multi-shot sampling without gathering the dense state. Usesimulate(&circuit).distributed(context). A run can start from an injected amplitude vector through.initial_state(...): every rank receives the full2^nvector and keeps only its own slice.
Shot and observable queries above the dense cap
simulate(...).shots(n), .sample_counts(n), and .expectation_values(...)
answer from a dense 2^n vector unless the backend carries its own path. The
dense route is capped by system memory (roughly 29 qubits on a 16 GiB host; see
PRISM_MAX_SV_QUBITS). Backends marked Native below answer without it and are
bounded only by their own representation.
| Backend | Shots and counts | Expectation values |
|---|---|---|
| Sparse | Native, CDF over the stored amplitudes | Native, O(k) over the amplitude map |
| MPS | Native, sequential conditional sampling | Native, one chain contraction per observable |
| Factored | Native, one draw per sub-state | Native, product over the blocks |
| Product State | Native, one Bernoulli draw per qubit | Native, one closed-form factor per qubit |
| Distributed Statevector | Native, rank-local CDF plus one scalar per rank | Native, rank-local sandwich plus one Allreduce |
| Statevector | Dense (streams from amplitudes, no probability vector) | Dense |
| Stabilizer, Factored Stabilizer | Compiled Clifford sampler | Sparse Pauli Dynamics, exact |
| Stochastic / Deterministic Pauli | Not applicable | Native Pauli propagation |
| Tensor Network | Dense | Native, one doubled-network contraction per observable |
| Density Matrix | Dense | Rejected, naming the backend |
Native sampling is deterministic from the seed alone: the same seed and shot count reproduce the same bitstrings. It is not shot-for-shot identical to the dense route, which consumes its randomness on a different schedule; the distributions agree.
Backends without an observable path return BackendUnsupported naming
themselves, so a rejected request says which engine could not serve it rather
than blaming the route that selected it.
simulate(...).marginals() reads per-qubit Z expectations rather than a
distribution when the resolved backend has an observable path and the circuit
routes straight to it. Sparse, MPS, product state, factored, tensor network,
distributed, the density matrix, and both stabilizer backends have one, and the
dense output cap does not apply on that route; a Clifford circuit with
measurements reads its marginals off the tableau at any width. It falls back to
the dense distribution on backends without an observable path, on a circuit
that splits into independent blocks unless those blocks run as product states,
and under a noise model, where the mixture is read densely and the
density-matrix memory limit applies instead.
Under a noise model run() and marginals() answer from the exact mixture, so
both reject a model carrying readout error instead of serving one: readout acts
on the measurement record rather than the state, and it is indexed by classical
bit where a marginal is indexed by qubit. shots and sample_counts are the
terminals that apply it.
simulate(...).run() is the one terminal that needs the whole distribution, so
on the distributed backend it rejects a register past the dense cap up front
rather than running first and answering with no distribution.
What kind of answer a result is
Every result type carries a metadata field describing how it was produced:
the engine automatic dispatch resolved to, whether that engine can discard state
weight, where the state lived, and the shot count for a sampled result.
| Field | Reads |
|---|---|
backend | The engine that answered, after Auto routing |
exactness | Exact, or Approximate with a fidelity lower bound when the engine reports one |
placement | Host or Device |
shots | Shots drawn, None for an analytic result |
Approximate marks the route rather than the run. An MPS at bond 256 on a
circuit that never fills a bond truncates nothing and still reports
Approximate, with a bound of 1.0: the variant answers whether the answer could
have been approximated, the bound answers whether it was.
The bound describes the normalized state, which is what every read returns: a truncating MPS does not renormalize its chain, but expectation values, shot sampling, the probability vector and the exported statevector all rescale on read, so the probabilities sum to 1 at any bond cap. The discarded weight the bound reports is error in that state, not weight missing from it.
Auto sends a circuit past the statevector cap to an MPS at a bounded bond
dimension, which is the only route those circuits have. It is taken by default
and the result says so. simulate(...).require_exact() rejects that route
instead, with an error naming the engine it would have used.
Simulate::expectation_values_reported returns the values with a standard error
per value on a route that estimates rather than evaluates. An evaluated route
reports Exact and no interval, so a caller distinguishes "converged" from "not
estimated" without comparing a float against zero.
Not yet supported
| Target | Status | Notes |
|---|---|---|
| ROCm (AMD GPU) | Planned | No AMD device kernels; the GPU path is CUDA-only |
| Distributed GPU | Planned | No multi-node GPU execution |
| Multi-GPU | Planned | A GPU context binds a single device; sharding one statevector across devices also needs peer access between them to stay ahead of the host path |
| Distributed noisy shots | Planned | Noise models are rejected on the distributed backend; trajectory execution is not lockstep across ranks |
These targets are listed so the matrix reflects the roadmap rather than hiding the gaps.