Skip to content

Circuit formats

circuit is text in every format, and circuit_format says which format it is. A Backend reports what it accepts as supported_formats; a Backend that reports none places no constraint of its own.

CircuitFormat What it is
OPENQASM_3 OpenQASM 3 program text. The default, and what this documentation uses.
OPENQASM_2 OpenQASM 2 program text, for Backends that accept it.
QPY Qiskit’s binary circuit serialisation.
QIR Quantum Intermediate Representation.
IONQ_CIRCUIT A JSON circuit document — a gateset, a qubit count and a list of gates. Still passed as a string.

OpenQASM 3 is a large language, and no importer implements all of it. The platform accepts static circuits with parameters and rejects dynamic circuits:

Accepted Rejected
include "stdgates.inc"; for and while loops
input declarations — this is how sweeps bind def subroutines
both measure forms — c = measure q; and measure q -> c; classical int declarations
gate modifiers ctrl@, inv@, pow@ if on a single bit compared to an integer
custom gate declarations
barrier and reset

A circuit that stays inside the accepted column:

OPENQASM 3.0;
include "stdgates.inc";
gate bell a, b {
h a;
cx a, b;
}
input float[64] theta;
qubit[3] q;
bit[3] c;
bell q[0], q[1];
ry(theta) q[2];
ctrl @ x q[0], q[2];
barrier q;
c = measure q;

Custom gates, a parameter to sweep, a control modifier, a barrier and a measurement — all accepted, and sufficient for most textbook and benchmark circuits.

The rejected column is one idea: anything whose control flow is decided while the circuit runs. A for loop, a subroutine call, a classical register being computed, a branch on a mid-circuit measurement — each requires the executing device to make a decision the compiler cannot make in advance. The platform does not currently execute dynamic circuits.

For running the same circuit at many values, use parameter_values rather than a loop in the circuit — see Primitives and PUBs. For the rest, unroll: write out the repetitions rather than looping, and inline what would have been a subroutine. Custom gate declarations are accepted, so the readable factoring is still available.

A row of parameter_values holds one value per input declaration, positionally, in the order the declarations appear in the circuit text. Nothing in the row names a parameter, so the order is the whole contract.

When you write the OpenQASM yourself, you also write the declarations, and reading them top to bottom gives you the row. When a framework adapter writes it for you, the rule is that declarations are sorted by parameter name — the order Qiskit’s circuit.parameters already uses, and the order OpenQARP’s emitter declares its input floats in. So a circuit with alpha, gamma and theta takes rows of [alpha, gamma, theta] whatever order the gates used them in, on every path into the platform.

input float[64] alpha;
input float[64] theta;
...
parameter_values=[[0.1, 0.9]] # alpha=0.1, theta=0.9
from qubitra import CircuitFormat
backend = client.backends.get("sim-statevector-26q")
for candidate in (CircuitFormat.OPENQASM_3, CircuitFormat.OPENQASM_2):
if candidate in backend.supported_formats:
chosen = candidate
break

Prefer OpenQASM 3 where a Backend accepts it, and check supported_formats rather than assuming. A format this SDK version does not recognise is dropped from supported_formats rather than failing the read, so the tuple only ever contains formats both you and the platform understand.

Submitting a format a Backend does not accept is rejected at submit with an error naming the formats it does accept. A format the SDK does not know at all raises InvalidRequestError without a request being sent.