Cheat sheet

Lookup reference. Identities that hold only up to a global phase are marked as such. c = cos(θ/2), s = sin(θ/2), w = 1/sqrt(2) ≈ 0.7071.

On this page
  1. Single-qubit gates
  2. Multi-qubit gates
  3. Identities and rewrite rules
  4. Common states
  5. Measuring in a rotated basis
  6. Qiskit API
  7. OpenQASM 2 vs 3
  8. Classical crypto glue
  9. Constants
  10. CTF conventions

1. Single-qubit gates

GateMatrixQiskitQASMDoes
I[[1,0],[0,1]]qc.id(0)id q[0];nothing; padding / timing
X[[0,1],[1,0]]qc.x(0)x q[0];bit flip; |0>↔|1>; π about x
Y[[0,-i],[i,0]]qc.y(0)y q[0];bit+phase flip; = iXZ‑ordered, π about y
Z[[1,0],[0,-1]]qc.z(0)z q[0];phase flip; |1>→−|1>; |+>↔|−>
H[[1,1],[1,-1]] * wqc.h(0)h q[0];Z basis ↔ X basis; makes superposition
S[[1,0],[0,i]]qc.s(0)s q[0];quarter turn about z (90°); = P(pi/2)
S†[[1,0],[0,-i]]qc.sdg(0)sdg q[0];inverse of S; used before H to read the Y basis
T[[1,0],[0,e^(i*pi/4)]]qc.t(0)t q[0];eighth turn about z (45°); = P(pi/4); non-Clifford
T†[[1,0],[0,e^(-i*pi/4)]]qc.tdg(0)tdg q[0];inverse of T
Rx(θ)[[c,-i*s],[-i*s,c]]qc.rx(th,0)rx(th) q[0];rotation θ about x
Ry(θ)[[c,-s],[s,c]]qc.ry(th,0)ry(th) q[0];rotation θ about y; real amplitudes, no phases
Rz(θ)[[e^(-i*th/2),0],[0,e^(i*th/2)]]qc.rz(th,0)rz(th) q[0];rotation θ about z; invisible to a Z-basis measurement
P(λ)
(=U1)
[[1,0],[0,e^(i*lam)]]qc.p(lam,0)p(lam) q[0];
QASM2 legacy: u1(lam)
phase on |1> only; P = Rz up to global phase e^(i*lam/2)
U(θ,φ,λ)[[c, -e^(i*lam)*s],
[e^(i*phi)*s, e^(i*(phi+lam))*c]]
qc.u(th,phi,lam,0)QASM3 U(th,phi,lam) q[0];
QASM2 u(...) / u3(...)
most general 1-qubit unitary up to global phase
√X0.5*[[1+i,1-i],[1-i,1+i]]qc.sx(0)sx q[0];native on IBM hardware; sx*sx = X

Useful specialisations: P(pi)=Z, P(pi/2)=S, P(pi/4)=T, U(pi/2,0,pi)=H, U(pi,0,pi)=X. Rx(pi)=X, Ry(pi)=Y, Rz(pi)=Z each up to the global phase -i.

2. Multi-qubit gates

Matrices below are in the textbook basis ordering |control target> = 00, 01, 10, 11 (and |c1 c2 t> for 3 qubits). Operator(qc).data prints the reversed ordering — see the endianness warning in §6. Reproduce these exact matrices in Qiskit by putting the control on the higher qubit index, e.g. qc.cx(1,0).

GateMatrix (|c t> order)QiskitQASMDoes
CX / CNOT[[1,0,0,0],
[0,1,0,0],
[0,0,0,1],
[0,0,1,0]]
qc.cx(c,t)cx q[c],q[t];X on target iff control is |1>; the entangler
CY[[1,0,0,0],
[0,1,0,0],
[0,0,0,-i],
[0,0,i,0]]
qc.cy(c,t)cy q[c],q[t];Y on target iff control |1>
CZdiag(1,1,1,-1)qc.cz(a,b)cz q[a],q[b];phase −1 on |11>; symmetric — control and target interchangeable
CH[[1,0,0,0],
[0,1,0,0],
[0,0,w,w],
[0,0,w,-w]]
qc.ch(c,t)ch q[c],q[t];H on target iff control |1>; used building W states
CP(λ)
(=CU1)
diag(1,1,1,e^(i*lam))qc.cp(lam,a,b)cp(lam) q[a],q[b];phase on |11>; symmetric; the QFT workhorse
SWAP[[1,0,0,0],
[0,0,1,0],
[0,1,0,0],
[0,0,0,1]]
qc.swap(a,b)swap q[a],q[b];exchanges the two qubits
iSWAP[[1,0,0,0],
[0,0,i,0],
[0,i,0,0],
[0,0,0,1]]
qc.iswap(a,b)not in qelib1/stdgates — Qiskit emits a gate iswap definitionswap plus i phase on the swapped amplitudes
CCX / Toffoli8×8 identity with the last 2×2 block swapped: |110>↔|111>qc.ccx(c1,c2,t)ccx q[a],q[b],q[c];classical AND into the target; reversible-logic primitive
CSWAP / Fredkin8×8 identity with |101>↔|110>qc.cswap(c,a,b)cswap q[a],q[b],q[c];swap a,b iff control |1>; core of the swap test
CCZdiag(1,1,1,1,1,1,1,-1)qc.ccz(a,b,c)emitted as a gate ccz definitionphase −1 on |111>; symmetric in all three qubits
MCXidentity except |1…10>↔|1…11>qc.mcx([c0,c1,c2],t)expands to a long gate mcx definitionn-control X; the Grover oracle/diffuser core

3. Identities and rewrite rules

"Up to phase" means the two operators differ by a scalar of modulus 1, which is unobservable unless the block sits inside a controlled operation — then the phase becomes relative and matters.

Pauli algebra

IdentityUse it when
XX = YY = ZZ = Iadjacent duplicates cancel — first pass of any golf
HH = I, S S† = I, T T† = Isame
XZ = -ZX, XY = -YX, YZ = -ZYreordering Paulis costs only a global sign
XY = iZ, YZ = iX, ZX = iYcollapse two Paulis into one
XYZ = iIthree Paulis vanish entirely

Conjugations (the golf ammunition)

IdentityUse it when
H X H = Zeliminate an X by moving it through Hadamards
H Z H = X (so X = HZH)rebuild X when only H and Z are allowed
H Y H = -YY survives conjugation up to a global sign
H = (X + Z) * wsanity check on a decomposition
S X S† = Y, S† X S = -Y, S Y S† = -Xconvert between X and Y legs
H Rz(θ) H = Rx(θ), H Rx(θ) H = Rz(θ)retarget a rotation axis without new gate types
Rz(a) Rz(b) = Rz(a+b), same for Rx, Rymerge consecutive rotations about one axis
H S H = e^(i*pi/4) Rx(pi/2) (up to phase)Clifford rewriting when phase is irrelevant

Phase-gate ladder

IdentityUse it when
S = T*T, Z = S*S = T^4, T^8 = Ibuild S/Z from a T-only gate set, or count T depth
Rz(λ) = P(λ) up to global phase e^(-i*lam/2)swapping rz↔p is free at top level, not under a control
P(λ) commutes with Z, S, T and Rz (all diagonal)slide diagonal gates past each other freely

Two-qubit rewrites

IdentityUse it when
CZ = H(t) · CX(c,t) · H(t)hardware/QASM only offers one of the two
CX(c,t) = H(t) · CZ(c,t) · H(t)same, other direction
CX(b,a) = H(a) H(b) · CX(a,b) · H(a) H(b)direction reversal with 4 H's — connectivity is one-way
SWAP(a,b) = CX(a,b) CX(b,a) CX(a,b)no native SWAP; costs 3 CNOTs
CY(c,t) = S†(t) · CX(c,t) · S(t)CY not in the allowed basis
CH(c,t) = Ry(pi/4)(t) · CX(c,t) · Ry(-pi/4)(t)
(circuit order left→right)
CH not in the allowed basis
iSWAP = S(a) S(b) H(a) CX(a,b) CX(b,a) H(b)iSWAP is not a stdgates gate
CX·CX = I (same control, same target)cancel duplicate CNOTs
CZ and CP are symmetric: cz(a,b) = cz(b,a)relabel to satisfy a coupling map for free

Pauli propagation through CNOT (conjugation, control c → target t)

Push throughBecomesNote
X on controlX on control and targetX copies forward
X on targetX on targetcommutes; move it freely
Z on controlZ on controlcommutes; move it freely
Z on targetZ on control and targetZ copies backward

Three-qubit rewrites

IdentityUse it when
CCX(a,b,c) = H(c) · CCZ(a,b,c) · H(c), and CCZ = diag(1,…,1,-1)phase-oracle ↔ bit-oracle conversion in Grover
CSWAP(c,a,b) = CX(b,a) · CCX(c,a,b) · CX(b,a)Fredkin from Toffoli
CCX is symmetric in its two controlsrelabel to fit connectivity
Standard 6-CNOT + T decomposition of ccx(a,b,c) (verified exact, no phase slack):
h c; cx b,c; tdg c; cx a,c; t c; cx b,c; tdg c; cx a,c; t b; t c; h c; cx a,b; t a; tdg b; cx a,b;
Use when the basis is {h,t,tdg,cx} — 6 CNOTs, 7 T-type gates.

4. Common states

Written in circuit order |q0 q1> — leftmost symbol is qubit 0. Qiskit counts strings print the reverse (§6).

StateAmplitudesRecipe from |0…0>Notes
|0>[1,0]Z-basis +1
|1>[0,1]xZ-basis −1
|+>[w,w]hX-basis +1
|−>[w,-w]x; hX-basis −1
|i> (|+i>)[w, i*w]h; sY-basis +1
|−i>[w, -i*w]h; sdgY-basis −1

Bell states

StateDefinitionRecipe<ZZ><XX><YY>
Φ⁺(|00> + |11>)*wh q0; cx q0,q1;+1+1−1
Φ⁻(|00> − |11>)*wx q0; h q0; cx q0,q1;
or Φ⁺ then z on either qubit
+1−1+1
Ψ⁺(|01> + |10>)*wx q1; h q0; cx q0,q1;
or Φ⁺ then x on either qubit
−1+1+1
Ψ⁻ (singlet)(|01> − |10>)*wx q0; x q1; h q0; cx q0,q1;
or Φ⁺ then z q0; x q0;
−1−1−1

Reading the correlations: <ZZ> = +1 means both Z-basis measurements always agree (outcomes 00/11); −1 means they always disagree (01/10). Φ⁺ is the only one with <ZZ>=<XX>=+1; Ψ⁻ is the only one anti-correlated in all three bases, which identifies it instantly from a transcript.

Multi-qubit

StateDefinitionRecipeNotes
GHZ (n=3)(|000> + |111>)*wh q0; cx q0,q1; cx q0,q2;maximally fragile — tracing out any qubit kills all entanglement
W (n=3)(|100>+|010>+|001>)/sqrt(3)ry(2*acos(1/sqrt(3))) q0;
ch q0,q1; cx q1,q2; cx q0,q1; x q0;
robust — stays entangled after losing one qubit

5. Measuring in a rotated basis

Hardware only measures in the Z basis. To measure observable M, apply the unitary that maps M's eigenbasis onto the Z basis, then measure. Outcome 0 ⇒ eigenvalue +1, outcome 1−1.

BasisApply before measureThen 0 means1 means
Z (computational)nothing|0>|1>
Xh q;|+>|−>
Ysdg q; h q;|i>|−i>
angle θ in the X–Z plane
cos θ|0> + sin θ|1>
ry(-2*theta) q;the θ directionthe θ+π/2 direction
arbitrary (φ,λ)u(-theta,-lam,-phi) q; — i.e. the inverse of the state-prep unitarythe prepared directionits antipode

CHSH angle convention: for the observable n·σ = cos(a)·Z + sin(a)·X, the pre-measurement gate is ry(-a). The optimal CHSH settings on Φ⁺ are Alice a ∈ {0, pi/2}, Bob b ∈ {pi/4, -pi/4}, giving S = E(a0,b0) + E(a0,b1) + E(a1,b0) − E(a1,b1) = 2*sqrt(2) (verified). Watch the sign pattern — which of the four terms is negated depends on the challenge's stated CHSH form.

BB84 basis labels: bit 0 = rectilinear/Z (|0>,|1>), bit 1 = diagonal/X (|+>,|−>). Prepare X-basis with h after the optional x; measure X-basis with h before measure.

6. Qiskit API (2.5.0)

Endianness. Qiskit is little-endian: qubit 0 is the RIGHTMOST character of a counts string and of Statevector basis labels. QuantumCircuit(3); qc.x(0) gives counts {'001': …}, not '100'. Same for Operator(qc).data — the row/column ordering is |q(n-1) … q1 q0>. When a challenge quotes a bitstring, decide which end is qubit 0 before writing any code. Fixes: read key[::-1], or call qc.reverse_bits() once at the end.

Build

from qiskit import QuantumCircuit, transpile
qc = QuantumCircuit(3, 3)          # 3 qubits, 3 classical bits
qc.h(0); qc.cx(0, 1); qc.ccx(0, 1, 2)
qc.rz(0.3, 1); qc.barrier()
qc.measure([0, 1, 2], [0, 1, 2])   # or qc.measure_all() (adds its own creg)
qc.reset(0)
qc.initialize([0.6, 0.8], 0)       # explicit amplitudes
print(qc.draw())                   # text diagram
qc.depth(); qc.count_ops(); qc.size()
qc2 = qc.inverse(); qc3 = qc.compose(other); qc.reverse_bits()
g = other.to_gate(); qc.append(g.control(2), [0, 1, 2, 3])

Mid-circuit conditionals

# c_if is REMOVED in Qiskit 2.x — use the context manager:
qc.measure(0, 0)
with qc.if_test((qc.clbits[0], 1)):
    qc.x(1)

Simulate

from qiskit_aer import AerSimulator
sim = AerSimulator()
res = sim.run(transpile(qc, sim), shots=1024).result()
counts = res.get_counts()          # {'00': 504, '11': 520}

# or the reference primitive (no Aer needed):
from qiskit.primitives import StatevectorSampler
r = StatevectorSampler().run([qc], shots=1024).result()
counts = r[0].data.c.get_counts()  # .c = name of the classical register

quantum_info (exact, no shots — use this whenever possible)

from qiskit.quantum_info import (Statevector, Operator, DensityMatrix,
    partial_trace, entropy, purity, state_fidelity, Pauli, SparsePauliOp)

sv = Statevector(qc_without_measurements)
sv.data                     # numpy amplitude array
sv.probabilities_dict()     # {'00': 0.5, '11': 0.5}
sv.expectation_value(Pauli('XX'))
Statevector.from_label('+')                 # also '0','1','-','r'(=|i>),'l'
state_fidelity(sv_a, sv_b)

U = Operator(qc).data       # unitary as numpy array
Operator(qc_a).equiv(Operator(qc_b))        # True if equal UP TO GLOBAL PHASE
Operator(qc_a) == Operator(qc_b)            # exact, phase-sensitive

rho = partial_trace(sv, [1])                # trace out qubit 1
entropy(rho)                # 1.0 for a Bell state half; 0.0 if separable
purity(rho)                 # 1.0 pure, 0.5 maximally mixed (1 qubit)

Transpile / basis change

t = transpile(qc, basis_gates=['u', 'cx'], optimization_level=3)
t = transpile(qc, basis_gates=['h','t','tdg','cx'])
t = transpile(qc, coupling_map=[[0,1],[1,2]], optimization_level=3)
t.count_ops(); t.depth()
qc.decompose()              # one level of unrolling, no optimisation

QASM in / out

from qiskit import qasm2, qasm3
qc = qasm2.loads(src); qc = qasm2.load('c.qasm')
src = qasm2.dumps(qc)          # fails on non-register-equality conditionals
src = qasm3.dumps(qc)
# qasm3.loads/load needs an extra package: pip install qiskit_qasm3_import

Counts handling

max(counts, key=counts.get)                     # most likely bitstring
sorted(counts.items(), key=lambda kv: -kv[1])[:5]
sum(v for k, v in counts.items() if k[-1] == '1') / shots   # P(qubit0 = 1)
int(bitstring[::-1], 2)                         # reverse then parse to int

7. OpenQASM 2 vs OpenQASM 3

FeatureOpenQASM 2OpenQASM 3
HeaderOPENQASM 2.0;OPENQASM 3.0; (also 3;)
Includeinclude "qelib1.inc";include "stdgates.inc";
Qubitsqreg q[2];qubit[2] q;
Classical bitscreg c[2];bit[2] c;
Gate callcx q[0],q[1];cx q[0], q[1]; (identical)
Parametrisedrz(pi/4) q[1];rz(pi/4) q[1];
Measuremeasure q[0] -> c[0];
whole register: measure q -> c;
c[0] = measure q[0];
also measure q[0]; as a statement
Resetreset q[0];reset q[0];
Barrierbarrier q;barrier q;
Custom gategate mg a,b { h a; cx a,b; }gate mg a, b { h a; cx a, b; }
Conditionalif (c==3) x q[0]; — register equality only, single statementif (c[0]) { x q[1]; } — real blocks, else, for, while
Extrasnoneint/float/angle vars, def subroutines, ctrl @ / inv @ / pow(k) @ gate modifiers, timing (delay, duration)
Std gate setBoth cover id x y z h s sdg t tdg rx ry rz p u cx cy cz ch cp crx cry crz swap ccx cswap sx. Not included: iswap, ccz, mcx — these must be defined with a gate block (Qiskit emits one automatically).

Minimal complete OpenQASM 2 program (parses in Qiskit 2.5.0)

OPENQASM 2.0;
include "qelib1.inc";
qreg q[2];
creg c[2];
gate mygate a, b { h a; cx a, b; }
h q[0];
cx q[0], q[1];
rz(pi/4) q[1];
mygate q[0], q[1];
measure q -> c;
if (c==3) x q[0];

Minimal complete OpenQASM 3 program

OPENQASM 3.0;
include "stdgates.inc";
qubit[2] q;
bit[2] c;
gate mygate a, b { h a; cx a, b; }
h q[0];
cx q[0], q[1];
rz(pi/4) q[1];
mygate q[0], q[1];
c[0] = measure q[0];
c[1] = measure q[1];
if (c[0]) { x q[1]; }        // register form: if (c == 3) { ... }

QASM golf checklist: strip barrier; cancel adjacent inverse pairs; merge same-axis rotations; convert CZ↔CX with the H sandwich to reuse an existing H; reverse a CNOT with 4 H's if that lets two H's cancel; replace SWAP with relabelled wires when the qubits are never measured separately. Verify every rewrite with Operator(a).equiv(Operator(b)) before submitting.

8. Classical crypto glue

The quantum part usually ends with a bitstring. Everything after that is normal CTF work.

Bits ↔ bytes

bits = [1,0,1,0,0,0,0,1]                       # or "10100001"
s    = "".join(map(str, bits))
data = int(s, 2).to_bytes(len(s)//8, 'big')    # b'\xa1'
# per-byte, avoids leading-zero loss:
data = bytes(int(s[i:i+8], 2) for i in range(0, len(s), 8))
# back:
s = "".join(f"{b:08b}" for b in data)
# MSB-first vs LSB-first: if the flag is garbage, try s[::-1] and per-byte reversal

XOR / encodings

xor = bytes(a ^ b for a, b in zip(ct, key))                  # equal length
xor = bytes(c ^ key[i % len(key)] for i, c in enumerate(ct)) # repeating key
import base64, binascii
base64.b64decode("aGk=")        # b'hi'
bytes.fromhex("6869")           # b'hi'
binascii.hexlify(b'hi')         # b'6869'
data.decode('utf-8', 'replace')

AES

from Crypto.Cipher import AES              # pycryptodome
from Crypto.Util.Padding import unpad
AES.new(key, AES.MODE_ECB).decrypt(ct)                 # no IV, blocks independent
unpad(AES.new(key, AES.MODE_CBC, iv).decrypt(ct), 16)  # IV = first 16 bytes of ct, usually
AES.new(key, AES.MODE_CTR, nonce=n).decrypt(ct)        # stream, no padding
AES.new(key, AES.MODE_GCM, nonce=n).decrypt(ct)        # +16-byte tag at the end

The standard QKD challenge chain

1. Parse the transcript          -> Alice bits, Alice bases, Bob bases, Bob results
2. Sift: keep positions where    -> alice_bases[i] == bob_bases[i]     (~50% survive)
3. Drop the disclosed sample     -> the bits used for the QBER estimate
4. Key bits -> bytes             -> 128 sifted bits = AES-128 key (256 -> AES-256)
5. Decrypt                       -> AES-CBC/ECB with that key, IV often prepended
6. Flag                          -> if it decodes to noise, try: bit order reversed,
                                    bases inverted (0=X not Z), key derived via
                                    SHA-256(bitstring) instead of raw packing

Also try hashlib.sha256(bits_str.encode()).digest() and hashlib.sha256(key_bytes).digest() as key derivations — both are common.

9. Constants

QuantityValueWhere it shows up
CHSH classical boundS = 2, win probability 0.75any local hidden-variable strategy caps here
CHSH quantum (Tsirelson) boundS = 2*sqrt(2) = 2.8284, win probability cos^2(pi/8) = 0.85355optimal entangled strategy; a measured 0.85 means the challenge wants the Bell strategy
Optimal CHSH angles (Φ⁺)Alice {0, pi/2}, Bob {pi/4, -pi/4}; each |E| = w = 0.7071plug straight into a CHSH solver
BB84 sifting rate~50% of raw bits survive basis reconciliationsanity-check the transcript length
Intercept-resend QBER25%Eve measures every qubit in a random basis. QBER ≈ 0 ⇒ no eavesdropper; ≈ 0.25 ⇒ full intercept-resend; in between ⇒ partial attack, fraction ≈ 4·QBER
BB84 abort threshold~11% QBERabove this no secret key can be distilled
Grover iterationsfloor((pi/4) * sqrt(N/M))N = search space, M = marked items. N=16,M=1 → 3. N=1024,M=1 → 25. Overshooting reduces success probability
Grover speedupO(sqrt(N)) — halves effective key lengthAES-128 → ~2^64 quantum work
Common values1/sqrt(2)=0.70711, 1/sqrt(3)=0.57735, cos(pi/8)=0.92388, sin(pi/8)=0.38268recognising amplitudes in a printed statevector
Entropy landmarks1 qubit maximally mixed → entropy = 1.0, purity = 0.5; pure → 0.0, 1.0detecting entanglement with partial_trace

10. CTF conventions

Flag format is typically flag{...} or QV{...} — check the scoreboard rules page first and grep for both. Submit exactly as printed, braces included, no trailing whitespace. Flags are case-sensitive. If a decrypted blob contains printable text but no braces, the flag may need one more decode step (base64, hex, rot13) or a different bit ordering.

Other habits worth keeping: each category's intro challenge unlocks the rest, so clear all intros first; save every transcript and circuit file before editing; when a numeric answer is requested, check whether the scoreboard wants it raw, rounded, or wrapped in the flag format.