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).