Why Your Quantum Circuit Was Rejected, and What to Change
· 11 min read · ZKSF team
A quantum circuit that is mathematically valid will still be refused by a large fraction of the machines that could in principle run it. The refusal is rarely a bug. It is a statement that some finite resource, memory, gate set, shot budget or accuracy, has been exhausted, and the useful information is which one.
This article enumerates the refusal categories that exist across statevector simulators, tensor-network engines, stabilizer simulators and real hardware, and states what changes in each case. The specific messages quoted are the ones our own router emits, because they were written to name the constraint and the remedy rather than to report that something went wrong.
1. The circuit does not fit in memory
An exact statevector simulation stores one complex amplitude per basis state. In double precision that is 16 bytes each, so an n-qubit register costs 16 x 2^n bytes and nothing can be done about it. The consequence is a hard wall rather than a gradual slowdown:
Qubits Statevector memory Fits on
20 16 MiB anything
30 16 GiB a large workstation
32 64 GiB an 80 GB A100/H100
34 256 GiB a dedicated memory node
40 16 TiB nothing you will rent by the hour
50 16 PiB nothing that existsEach additional qubit doubles the requirement, so the distance between a circuit that runs comfortably and one that is impossible is about four qubits. A refusal in this category reads:
36 qubits needs 1,024 GiB RAM for exact statevector; local limit is
32 qubits. Structured circuits may still run via mps/clifford backends.The final sentence is the actionable part. The memory ceiling applies to the *method*, not to the circuit. A 36-qubit circuit is out of reach for exact statevector simulation and may be entirely tractable for a tensor-network or stabilizer method, because those do not store the full amplitude vector at all. The remedy is not a bigger machine; it is a different representation.
Noisy simulation hits the same wall earlier still. A density matrix carries 2^n x 2^n entries rather than 2^n, so the memory cost is the square of the statevector cost and a circuit that fits comfortably as a pure state may not fit as a mixed one.
2. The gate is outside the engine's supported set
Every simulation method faster than brute force earns its speed by restricting what it can represent. The restriction is the method, so an unsupported gate is not an unimplemented feature but a statement that the circuit has left the regime the method covers.
A stabilizer simulator accepts Clifford gates only: H, S, CNOT, CZ, the Paulis and measurement. The Gottesman-Knill theorem guarantees polynomial-time simulation for exactly that set, and a single T gate or arbitrary-angle rotation removes the guarantee. Five thousand qubits are fine; one T gate is not.
A Pauli-propagation engine accepts a broader but still bounded set. Ours refuses with:
gate 'ccx' is not supported by the Pauli engine; supported: h, cx, cz,
swap, rx, ry, rz, rzz, rxx, ryy, x, y, z, s, t and their inversesListing the supported set in the refusal matters more than it appears. The usual remedy is decomposition rather than a change of engine: a Toffoli decomposes into six CNOTs and a handful of single-qubit gates, all of which are in the list above, and `transpile(circuit, basis_gates=[...])` performs the rewrite mechanically. A circuit rejected for one gate is frequently accepted verbatim after transpilation to the engine's basis.
Tensor-network engines refuse on a narrower ground, that a gate has no mapping into the contraction library's own operator set, which is a coverage gap rather than a theoretical limit. The same decomposition strategy applies.
3. OpenQASM cannot express the circuit
This category surprises people because the circuit is valid, the engine supports every gate in it, and the export still fails. OpenQASM 2 has no representation for a free parameter. A circuit built with symbolic parameters, which is precisely what every variational ansatz is before binding, has no legal OpenQASM 2 form:
from qiskit.circuit.library import real_amplitudes
from qiskit.qasm2 import dumps
ansatz = real_amplitudes(4, reps=2) # 12 free parameters
dumps(ansatz)
# QASM2ExportError: circuit has unbound parametersThe fix is to bind before exporting. `ansatz.assign_parameters(values)` produces a concrete circuit that serialises cleanly, and any workflow that submits an ansatz to a remote service must bind at each evaluation regardless, so this is a sequencing error rather than a limitation.
OpenQASM 2 also has no native syntax for classical control flow beyond a single conditional on a whole register, no mid-circuit reset in all dialects, and no standard spelling for many multi-qubit gates. OpenQASM 3 addresses most of this, but support across vendors is uneven, so a circuit that round-trips through QASM 2 is still the most portable artifact available. When portability matters, restricting the circuit to what QASM 2 can express is a deliberate design choice, not a concession.
4. Real hardware imposes bounds a simulator does not
Hardware refusals come from the device registry rather than from physics, and they are worth knowing before a submission rather than after a queue wait. Two representative devices:
Device Qubits Shots per task Price
Rigetti Cepheus-1-108Q 108 1 - 10,000 $0.30 + $0.000425/shot
IonQ Forte-1 (trapped ion) 36 100 - 5,000 $0.30 + $0.08/shotTwo constraints here catch people out. The first is that shot counts have a lower bound as well as an upper one: IonQ rejects a task below 100 shots outright, so a quick single-shot smoke test that works against every simulator fails against the device. The second is that qubit ceilings differ by nearly a factor of three between devices at the same price per task, so a circuit that is routine on one platform is unsubmittable on the other.
Connectivity is a third constraint that does not produce a refusal at all. Neither device is fully connected, so a two-qubit gate between physically distant qubits is compiled into a chain of SWAP operations. The circuit is accepted, runs, and returns a result of markedly lower fidelity than the same circuit on a simulator, with nothing in the response to indicate that the depth grew during routing. Checking the transpiled depth against the submitted depth is the only way to see it.
5. The answer came back, and it is not informative
The most interesting refusal category is the one where the simulation succeeded. Approximate engines return a number together with a bound on how far that number can be from the truth, and a bound wide enough to admit any answer makes the number worthless. Reporting it without comment would be the more dangerous behaviour, so our executor rejects instead:
the simulation discarded 31.2% of the state's weight, giving an error
bound of 0.79 on every outcome probability; raise max_bond (or lower
the circuit depth) and run againFor a matrix-product-state simulation the bound follows from the discarded weight as sqrt(2 x epsilon), so a truncation that throws away a third of the state admits a probability error near one. The remedy is to raise the bond dimension, which is the parameter controlling how much entanglement the representation can hold, at a cost in time and memory. When raising it far enough is not affordable, that is the honest signal that the circuit is genuinely beyond tensor-network reach and not merely under-resourced.
For Pauli propagation the bound comes instead from the total coefficient mass discarded by the truncation cutoff, and the remedy is to lower the cutoff. The two mechanisms are different; the discipline is identical.
The diagnostic order
Working through refusals in this order resolves nearly all of them without a support conversation:
- Count the qubits. Above roughly 32, exact statevector simulation is out on memory grounds regardless of what else is true. Choose a method, not a machine.
- Scan the gate list. If it is entirely Clifford, a stabilizer simulator runs it at any width, exactly and almost for free. If it contains gates outside the target engine's basis, transpile rather than switch.
- Bind the parameters. Anything symbolic will fail at serialisation, whatever the engine.
- Check shot bounds against the specific device, both floor and ceiling, before submitting to hardware.
- Read the error bound, not just the result. An approximate answer without a bound is an assertion; with one it is a measurement.
The general principle underneath all five is that these systems have no useful notion of trying harder. Each refusal identifies a specific exhausted resource, and the only productive response is to supply more of that resource or to choose a method that does not consume it. A router that names which one has done the diagnostic work already.
Our own service applies these checks before a job is charged rather than after it runs, and publishes the resulting error bound on every approximate result. The reasoning behind that bound is set out in How we certify simulation error, and the memory wall in category 1 is treated at length in The 34-Qubit Wall.
Run your own 100-qubit circuit, with an error bar.
