Appendix F: OpenQASM Reference
OpenQASM 3 is the interchange format for quantum circuits — the closest thing the field has to a lingua franca. This is the working subset.
Minimal program
OPENQASM 3.0;
include "stdgates.inc";
qubit[2] q;
bit[2] c;
h q[0];
cx q[0], q[1];
c[0] = measure q[0];
c[1] = measure q[1];
stdgates.inc provides the standard gate set. Without it you have only U and gphase.
Types
qubit[4] q; // qubit register
bit[4] c; // classical bit register
int[32] n = 5;
uint[8] k;
float[64] theta = 0.7853981633974483;
angle[32] phi = pi / 4;
bool flag = true;
const int SHOTS = 1024;
pi, tau, and euler are built in.
Standard gates
From stdgates.inc:
// single-qubit
x q[0]; y q[0]; z q[0]; h q[0]; s q[0]; sdg q[0]; t q[0]; tdg q[0];
sx q[0];
rx(pi/2) q[0]; ry(theta) q[0]; rz(pi/4) q[0];
p(lambda) q[0];
U(theta, phi, lambda) q[0];
// two-qubit
cx q[0], q[1]; cy q[0], q[1]; cz q[0], q[1];
ch q[0], q[1]; swap q[0], q[1];
crx(theta) q[0], q[1]; cry(theta) q[0], q[1]; crz(theta) q[0], q[1];
cp(lambda) q[0], q[1];
cu(theta, phi, lambda, gamma) q[0], q[1];
// three-qubit
ccx q[0], q[1], q[2];
cswap q[0], q[1], q[2];
Custom gates
gate bell a, b {
h a;
cx a, b;
}
gate myrot(theta) a {
rz(theta) a;
sx a;
rz(-theta) a;
}
bell q[0], q[1];
myrot(pi/3) q[2];
⚠️ Custom gates are the least portable feature. Chapter 18 measured them as a leading cause of round-trip failure — the definition survives, the semantics your tool attached to it may not.
Modifiers
ctrl @ x q[0], q[1]; // controlled-X, i.e. cx
ctrl(2) @ x q[0], q[1], q[2]; // Toffoli
negctrl @ x q[0], q[1]; // controlled on |0>
inv @ t q[0]; // T-dagger
pow(2) @ x q[0]; // X squared = identity
Modifiers compose: ctrl @ inv @ pow(2) @ rx(pi/4) q[0], q[1];
Measurement and reset
c[0] = measure q[0];
c = measure q; // whole register
reset q[0];
reset q;
Classical control — dynamic circuits
bit m;
m = measure q[0];
if (m == 1) {
x q[1];
}
// loops
for int i in [0:3] {
h q[i];
}
int j = 0;
while (j < 4) {
cx q[j], q[j+1];
j += 1;
}
This is OpenQASM 3's headline addition over 2.0, and it is what makes teleportation and repeat-until-success expressible (Chapter 9).
Subroutines
def parity(bit[4] b) -> bit {
return b[0] ^ b[1] ^ b[2] ^ b[3];
}
Timing
delay[100ns] q[0];
barrier q;
Durations use ns, us, ms, s, or dt (the backend's sample time — Chapter 39 measured dt = 4e-09
on a 133-qubit device).
Qiskit round trip
from qiskit.qasm3 import dumps, loads, dump, load
qasm = dumps(circuit)
circuit2 = loads(qasm)
with open("circuit.qasm", "w") as f:
dump(circuit, f)
For OpenQASM 2 (legacy):
from qiskit.qasm2 import dumps as dumps2, loads as loads2
What survives a round trip
Measured in Chapter 6 and Chapter 18:
| Feature | Survives? |
|---|---|
| Standard gates | yes |
| Registers and names | yes |
| Measurement, reset | yes |
| Parameterized rotations with bound values | yes |
Custom gate definitions |
usually — semantics may not |
if/for/while |
version-dependent; check your importer |
| Unbound parameters | no — bind before exporting |
| Pulse calibrations | no — and qiskit.pulse was removed in Qiskit 2.0 |
| Transpiler layout | no — a QASM file is logical, not physical |
★ The last row matters most. A QASM file has no memory of which physical qubits it ran on. Chapter 39 §39.8 lists what you must record separately for a hardware result to be reproducible, and the circuit is only one of nine fields.
OpenQASM 2 vs 3
| 2.0 | 3.0 | |
|---|---|---|
| Declaration | qreg q[2]; creg c[2]; |
qubit[2] q; bit[2] c; |
| Classical control | if (c == 1) x q[0]; only |
full if/for/while, subroutines |
| Types | bits only | int, float, angle, bool, arrays |
| Modifiers | none | ctrl, negctrl, inv, pow |
| Timing | none | delay, duration types |
Much published code is still 2.0. It is largely a subset, and Qiskit reads both.
See also: Chapter 6 (OpenQASM in practice), Chapter 9 (dynamic circuits), Chapter 18 (interoperability measured), Appendix E (framework translation).