Skip to content
Kartikey Purohit
← Course overview

Reference

Final Review Cheatsheet

Read once the night before (Day 10 evening) and once exam morning (20 min max). No new material. Verified against qiskit 2.5.1 / qiskit-ibm-runtime 0.48.0.


1. Gate matrices (memorize cold)

Gate Matrix Notes
I [[1,0],[0,1]] identity
X [[0,1],[1,0]] bit flip; X|0⟩=|1⟩
Y [[0,−i],[i,0]] Y = iXZ
Z [[1,0],[0,−1]] phase flip; Z|1⟩=−|1⟩
H (1/√2)[[1,1],[1,−1]] |0⟩→|+⟩, |1⟩→|−⟩
S [[1,0],[0,i]] √Z; S† = sdg: [[1,0],[0,−i]]
T [[1,0],[0,e^{iπ/4}]] √S; T† = tdg
SX ½[[1+i,1−i],[1−i,1+i]] √X
P(λ) [[1,0],[0,e^{iλ}]] p(π)=Z, p(π/2)=S, p(π/4)=T
RX(θ) [[cos θ/2, −i sin θ/2],[−i sin θ/2, cos θ/2]] exp(−iθX/2)
RY(θ) [[cos θ/2, −sin θ/2],[sin θ/2, cos θ/2]] real entries
RZ(θ) [[e^{−iθ/2},0],[0,e^{iθ/2}]] rz(π) ≡ Z up to global phase
CX control flips target when control=1 qc.cx(control, target)
CZ diag(1,1,1,−1) symmetric — control/target interchangeable
SWAP swaps qubit states 3 CXs
CCX Toffoli, 2 controls qc.ccx(c1, c2, t)

Identities: HZH = X, HXH = Z, HYH = −Y, S² = Z, T² = S. X and Z anticommute. Bloch positions: |0⟩=+Z, |1⟩=−Z, |±⟩=±X, |±i⟩=±Y.

2. Little-endian — qubit 0 is ALWAYS rightmost

Three verified examples:

  1. Pauli string: Pauli('XZ') → Z on q0, X on q1. (Pauli('IZ') diag = [1,−1,1,−1] = Z on q0.)
  2. Counts key: x(0) on 2 qubits → counts {'01': ...} — string reads q1q0, NOT '10'.
  3. Statevector index: x(0) on 2 qubits → [0, 1, 0, 0] — amplitude at index 0b01 = 1.

Also: Statevector.from_label('01') means q1=0, q0=1. Circuit diagrams draw q0 on top.

3. PUB shapes

Primitive PUB tuple (in order)
SamplerV2 (circuit,) · (circuit, param_values) · (circuit, param_values, shots)
EstimatorV2 (circuit, observables) · (circuit, observables, param_values) · (..., precision)

run() always takes a list of PUBs: sampler.run([qc]), estimator.run([(qc, obs)]). Bare-circuit shorthand sampler.run([qc]) is fine; Estimator always needs the tuple with observables.

4. Sampler vs Estimator

SamplerV2 EstimatorV2
Returns per-shot bitstrings / counts expectation values ⟨ψ|O|ψ⟩
Circuit must contain measurements NOT contain measurements
Data access result[0].data.<creg> → BitArray result[0].data.evs / .stds
Accuracy knob shots / default_shots precision / default_precision (= target std err)
Mitigation none — suppression only (DD, twirling) resilience_level 0/1/2 + fine-grained resilience.*
Observables n/a SparsePauliOp, needs apply_layout

5. Execution modes decision table

Mode Use when Key fact
Job one-off single primitive request queues independently
Batch many independent jobs submitted together, scheduled in parallel, no shared state
Session iterative/dependent jobs (VQE, QAOA) dedicated window, sequential without re-queuing
with Session(backend=backend) as session:   # or Batch(backend=backend)
    sampler = SamplerV2(mode=session)

mode= accepts a Session, a Batch, or a backend object (= job mode). Local testing: StatevectorSampler/StatevectorEstimator (qiskit.primitives) or fake backends (FakeManilaV2).

6. Resilience levels (Estimator ONLY) — cumulative

Level Adds Notes
0 nothing raw results
1 TREX measurement-error mitigation (+ measurement twirling) default
2 ZNE (+ gate twirling) twirling applies here too — official sample Q16 key is disputed (docs issue #4298)

PEC is never auto-enabled: opt-in via resilience.pec_mitigation; ZNE and PEC cannot both be True (verified ValidationError).

7. Suppression vs Mitigation

  • Suppression (before/during execution): dynamical decoupling, twirling — both primitives.
  • Mitigation (post-processing): TREX (measure), ZNE, PEC — Estimator only.
  • ZNE knobs: noise_factors (e.g. (1, 3, 5)), extrapolator ("linear"/"exponential"/"polynomial…").

8. Option paths (verified by instantiation on 0.48.0)

# Both primitives
options.default_shots = 4096
options.dynamical_decoupling.enable = True
options.dynamical_decoupling.sequence_type = "XY4"   # valid: 'XX', 'XpXm', 'XY4'
options.twirling.enable_gates = True
options.twirling.enable_measure = True
options.twirling.num_randomizations = 32
options.twirling.shots_per_randomization = 64
 
# Estimator only  (setting these on Sampler raises ValidationError)
options.default_precision = 0.015625        # service default = 1/√4096
options.resilience_level = 2
options.resilience.measure_mitigation = True
options.resilience.zne_mitigation = True
options.resilience.zne.noise_factors = (1, 3, 5)
options.resilience.zne.extrapolator = "exponential"
options.resilience.pec_mitigation = True    # mutually exclusive with zne_mitigation

9. Result-access cookbook (all verified)

result = job.result()                    # PrimitiveResult; result.metadata = {'version': 2}
pub = result[0]                          # one PubResult per PUB
 
# Sampler — field name == creg name!
pub.data.meas                            # measure_all()/measure_active() → creg 'meas'
pub.data.c                               # QuantumCircuit(n, m) default creg → 'c'
pub.data.meas.get_counts()               # {'11': 497, '00': 503}
pub.data.meas.get_bitstrings()           # one string per shot
pub.data.meas.num_shots / .num_bits
pub.join_data()                          # multiple cregs → single BitArray
 
# Estimator
pub.data.evs                             # expectation value(s)
pub.data.stds                            # standard error(s)
pub.metadata                             # {'target_precision': ..., 'shots': ...}
 
# Analysis
probs = {b: n/sum(c.values()) for b, n in c.items()}          # counts → probabilities
expZ  = (c.get('0',0) - c.get('1',0)) / shots                 # single-qubit ⟨Z⟩
expZZ = sum((-1)**b.count('1') * n for b, n in c.items())/shots   # parity
# error ∝ 1/√shots → 4× shots = ½ error; shots ∝ 1/precision²

Jobs: job.job_id(), job.status() → string in {INITIALIZING, QUEUED, RUNNING, CANCELLED, DONE, ERROR}; final = (DONE, CANCELLED, ERROR). Later: service.job(id); list: service.jobs(...). QPU seconds: job.usage(); full dict: job.metrics().

10. Visualization — function → what it shows

Function Shows Trap
circuit.draw('mpl'/'text'/'latex') circuit; q0 on top, time →right reverse_bits, idle_wires, fold
plot_histogram(counts) bar chart of counts accepts list of dicts + legend
plot_distribution() (quasi-)probabilities can show negatives
plot_bloch_vector([x,y,z]) ONE Bloch vector (raw coords) doesn't take a state
plot_bloch_multivector(sv) one sphere per qubit entangled ⇒ zero-length vectors
plot_state_qsphere(sv) amplitude = blob size, phase = color global state, one sphere
plot_state_city(sv) 3D bars of density-matrix re/im
plot_state_paulivec(sv) Pauli expectation decomposition
plot_state_hinton(sv) density matrix square sizes

11. OpenQASM 3 skeleton (verified qasm3.dumps output)

OPENQASM 3.0;
include "stdgates.inc";
bit[2] c;
qubit[2] q;
h q[0];
cx q[0], q[1];
c[0] = measure q[0];
c[1] = measure q[1];
  • dumps → string · dump(qc, f) → file · loads(str) / load(path) → circuit.
  • QASM2: OPENQASM 2.0; include "qelib1.inc"; qreg q[2]; creg c[2]; measure q[0] -> c[0]; (import via qasm2.loads / QuantumCircuit.from_qasm_str).
  • QASM3 adds: classical control flow (if, while), input parameters, typed classical data.

12. Top-15 exam traps

  1. Little-endian everywhere — Pauli strings, counts keys, statevector indices (q0 rightmost).
  2. Sampler needs measurements; Estimator must not have them.
  3. Observables need obs.apply_layout(isa_circuit.layout) after transpiling.
  4. Hardware primitives reject non-ISA circuits — always generate_preset_pass_manager(...).run(qc) first.
  5. resilience_level is Estimator-only; Sampler options have no resilience at all.
  6. Data attribute = creg name: data.meas for measure_all(), data.c for QuantumCircuit(n, m).
  7. List parameter binding follows circuit.parameters = alphabetical order, not insertion order.
  8. Session = iterative/dependent; Batch = independent-parallel; don't swap them.
  9. dumps/loads = strings; dump/load = files.
  10. with qc.if_test((clbit, 1)):.c_if() is dead; if_test takes a tuple.
  11. cx(a, b): first arg control, second target. CZ is symmetric, CX is not.
  12. Resilience levels are cumulative: 2 = TREX + twirling + ZNE (Q16 dispute).
  13. Entangled qubits in plot_bloch_multivector show zero-length vectors; qsphere color = phase.
  14. Error ∝ 1/√shots: halve error ⇒ 4× shots; precision p ⇒ shots ∝ 1/p².
  15. V1 is gone: no qiskit.execute, no backend.run for primitives, no job.result().get_counts() — go through result[0].data.<creg>.

13. Pacing plan — 68 questions / 90 minutes

  • Budget ~79 s/question; work at 60–70 s.
  • First pass (~60 min): answer everything you know immediately; anything > 90 s → pick best guess, flag, move on. Never leave blanks (no negative marking).
  • Second pass (~25 min): flagged questions only. Re-read stems for "NOT"/"EXCEPT".
  • Last 5 min: confirm every question has an answer.
  • Passing = 47/68 (~69%). You can drop 21 questions. One hard question is worth the same as one easy one — harvest easy points first.
  • When stuck between two options: pick the one consistent with little-endian / ISA / apply_layout rules — traps are the test-writers' favorite wrong answers.