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:
- Pauli string:
Pauli('XZ')→ Z on q0, X on q1. (Pauli('IZ')diag = [1,−1,1,−1] = Z on q0.) - Counts key:
x(0)on 2 qubits → counts{'01': ...}— string reads q1q0, NOT '10'. - 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_mitigation9. 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 viaqasm2.loads/QuantumCircuit.from_qasm_str). - QASM3 adds: classical control flow (
if,while),inputparameters, typed classical data.
12. Top-15 exam traps
- Little-endian everywhere — Pauli strings, counts keys, statevector indices (q0 rightmost).
- Sampler needs measurements; Estimator must not have them.
- Observables need
obs.apply_layout(isa_circuit.layout)after transpiling. - Hardware primitives reject non-ISA circuits — always
generate_preset_pass_manager(...).run(qc)first. resilience_levelis Estimator-only; Sampler options have no resilience at all.- Data attribute = creg name:
data.measformeasure_all(),data.cforQuantumCircuit(n, m). - List parameter binding follows
circuit.parameters= alphabetical order, not insertion order. - Session = iterative/dependent; Batch = independent-parallel; don't swap them.
dumps/loads= strings;dump/load= files.with qc.if_test((clbit, 1)):—.c_if()is dead;if_testtakes a tuple.cx(a, b): first arg control, second target. CZ is symmetric, CX is not.- Resilience levels are cumulative: 2 = TREX + twirling + ZNE (Q16 dispute).
- Entangled qubits in
plot_bloch_multivectorshow zero-length vectors; qsphere color = phase. - Error ∝ 1/√shots: halve error ⇒ 4× shots; precision p ⇒ shots ∝ 1/p².
- V1 is gone: no
qiskit.execute, nobackend.runfor primitives, nojob.result().get_counts()— go throughresult[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.