Skip to main content

Cram sheet — Section 2: Visualize quantum circuits, measurements, and states

One-screen review layer for this section: short background primers per objective, then the key facts — each backed by an official source or by an executed proof from the question bank.

Visualize quantum circuits

Four renderers, and a signature that does not forgive typos

QuantumCircuit.draw() and the standalone circuit_drawer() are the same machinery, and output= accepts exactly four names.

outputReturnsReach for it when
text (default)a TextDrawing — printable, but not a str and not Noneyou are in a terminal or notebook
mpla matplotlib.figure.Figureyou need to style or savefig a publication figure
latexa PIL Image, and it needs a LaTeX toolchainyou want typeset output
latex_sourcethe raw LaTeX stringyou are pasting into a document

Anything else raises VisualizationError whose message lists those four. The one exception to memorize: pass your own axes with draw("mpl", ax=ax) and the return value is None, because the caller already owns the figure.

KeywordEffectNotes
idle_wires=Falsedeletes any wire carrying no operations"auto" keeps them unless the circuit carries a transpiler layout
reverse_bits=Truedraws the highest-index qubit on the top wireworks in every renderer
initial_state=Trueprefixes each wire with its starting label
plot_barriers=Falsehides barriersworks in every renderer
filename=saves the rendering to disk
fold, scale, stylewrap width, size, stylingrenderer-specific

The signature is closed — there is no **kwargs catch-all, so a near-miss keyword is a hard TypeError, never a silently ignored option.

Sources: visualize-circuits · visualization

Must know:

  • A classically-controlled gate that draws as an If block is built with with qc.if_test((qc.clbits[0], 1)):.c_if(), a condition= kwarg, a .condition attribute, and if_test(clbit == 1) all fail in Qiskit 2.x. — ⚙️ proven in s2-q013
  • idle_wires=False removes wires that carry no operations, so a 3-qubit circuit acting only on qubits 0 and 2 draws two wires. — ⚙️ proven in s2-q011

Traps:

  • output="matplotlib" and output="figure" are not valid — both raise VisualizationError listing the four real names, from the method and from circuit_drawer alike. — ⚙️ proven in s2-q015
  • The standalone drawer is from qiskit.visualization import circuit_drawer called as circuit_drawer(qc, output="text"); qiskit.tools no longer exists and the kwarg is output=, never format=. — ⚙️ proven in s2-q014
  • qc.draw("mpl", ax=...) returns None, not a Figure — supplying your own axes hands figure ownership back to you. — ⚙️ proven in s2-q017
  • reverse_bits=True is rendering-only: it puts the highest-index qubit on the top wire and changes nothing about the circuit, the gate order, or the endianness of the results. — ⚙️ proven in s2-q012
  • initial_state=True (default False) draws the |0⟩ labels; the plural initial_states= is a TypeError, and style={"initial_state": True} is silently ignored by the text renderer. — ⚙️ proven in s2-q016

Visualize quantum measurements

Counts in, Figure out — and the bar labels are little-endian

Both measurement plotters take a counts mapping — bitstring to integer — and return a matplotlib.figure.Figure.

counts = result[0].data.meas.get_counts() # SamplerV2 -> BitArray -> counts
plot_histogram([before, after], legend=["before", "after"])
plot_histogramplot_distribution
plotsraw countsa normalized quasi-probability distribution
y-axis labelCountQuasi-probability
heights add up tothe shot count1.0
where normalization comes fromthe counts themselves

The display options are a small, memorizable set: legend (a LIST of strings, one per execution), sort ("asc" or "desc"), number_to_keep, color, bar_labels and figsize.

Reading the axis is the last piece. Qiskit labels outcomes little-endian, q_(n−1)…q_1 q_0, with the highest-index qubit on the LEFT. When a circuit has several classical registers — measure_all appends a fresh meas register — the key is space-separated, one group per register, the LAST-declared register printed leftmost.

Sources: visualize-results · visualization

The device plots draw the hardware, not the results

Three device plots draw the same qubit graph and differ only in what they colour.

FunctionTakesColours
plot_gate_map(backend)a BackendV2nothing; plot_directed=True adds arrows, not error data
plot_error_map(backend)a BackendV2every qubit and every link, from calibration data
plot_circuit_layout(isa, backend)a transpiled circuit plus its backenda two-tone used/unused highlight, labelled with virtual indices

plot_gate_map is a picture of the HARDWARE: it reads the backend's qubit count and coupling map and draws one node per qubit plus one line per connected pair. Reach for this family when the question is about the DEVICE — which qubits exist, how they connect, how good they are, and where the transpiler put your circuit — never about measurement outcomes.

Sources: visualization · visualize-results

Must know:

  • SamplerV2 output must be converted first: result[0].data.meas.get_counts(). The BitArray itself is not a counts mapping and has no bare .counts() method. — ⚙️ proven in s2-q022
  • number_to_keep=2 on four outcomes draws three bars — the two largest plus rest, whose height is the SUM of the folded counts (20 + 4 → 24), never their average. — ⚙️ proven in s2-q021
  • Overlaying runs takes a LIST of counts dicts plus a matching list legend: plot_histogram([before, after], legend=["before", "after"]). labels= does not exist and a bare string legend raises. — ⚙️ proven in s2-q023
  • plot_circuit_layout reads circuit.layout, which only the circuit RETURNED by transpile carries — transpile never mutates its input, so plotting the original raises QiskitError: 'Circuit has no layout. Perhaps it has not been transpiled.' at every optimization level. — ⚙️ proven in s2-q036
  • Statevector.sample_counts(shots) returns a qiskit.result.Counts mapping summing to shots with only nonzero-amplitude outcomes, so it feeds plot_histogram with no backend or primitive involved; sample_memory(shots) is the per-shot list and sv.seed(...) makes the draw reproducible. — ⚙️ proven in s2-q039

Traps:

  • Histogram labels are little-endian q_(n−1)…q_0: flipping qubits 0 and 2 of a 3-qubit register puts the single bar at '101', not '011'. — ⚙️ proven in s2-q019
  • Neither plotter has a shots= parameter — plot_distribution(counts, shots=1024) raises TypeError; the V1-era habit plot_histogram(counts, shots=1024) is simply gone. — ⚙️ proven in s2-q020
  • With no options, plot_histogram sorts bars in ascending lexicographic bitstring order — not insertion order and not by bar height — and returns a Figure. — ⚙️ proven in s2-q025
  • plot_gate_map needs a BackendV2: a counts mapping, a CouplingMap or a Target all raise AttributeError — and plot_histogram(backend) raises too. — ⚙️ proven in s2-q037

Visualize quantum states

Each state plot answers a different question

Pick a state plot by the question it answers; each is a free function, and also a token of sv.draw(output).

PlotDrawsAnswers
plot_bloch_multivector(state)one sphere per qubit, from that qubit's reduced statewhere each qubit points
plot_state_qsphere(state)one node per nonzero amplitude, sized and colouredwhich amplitudes exist, and their relative phases
plot_state_city(state)two 3-D bar charts, the real and imaginary parts of ρindividual density-matrix elements
plot_state_hinton(state)squares whose size is the magnitudethe same matrix, at a glance
plot_state_paulivec(state)the expansion coefficients over Pauli stringsthe Pauli decomposition

plot_bloch_multivector takes a quantum STATE and traces out the other qubits; plot_bloch_vector is the different function that takes an explicit three-number vector [x, y, z].

On sv.draw(output), output is the first positional parameter and the valid tokens are text, latex, latex_source, qsphere, hinton, bloch, city and paulivec. Unlike the other state plots, plot_state_qsphere accepts only figsize, not title.

Sources: plot-quantum-states · visualization

Reading an arrow, reading a node

The Bloch vector is (⟨X⟩, ⟨Y⟩, ⟨Z⟩), so reading an arrow is memorization.

ArrowStateHow you land there
+Z (north pole)the zero statethe starting point
−Z (south pole)the one state
+Xthe plus stateh alone
−Xthe minus state, the −1 eigenstate of X
+Ythe 'r' state, a +i relative phaseh then s, a 90° rotation about Z
−Ythe 'l' state, a −i relative phase

Anywhere on the equator means ⟨Z⟩ = 0. Because this plot only ever shows single-qubit expectation values, it cannot represent correlations between qubits.

A q-sphere encodes different information: node SIZE is the amplitude magnitude and node COLOUR is its complex phase, so a q-sphere is the plot that shows relative phase between basis states.

Sources: plot-quantum-states

Must know:

  • plot_bloch_multivector draws one sphere PER QUBIT (3 qubits → 3 spheres, idle ones included) and packs them all into a single returned Figure. — ⚙️ proven in s2-q026
  • For the Bell state both reduced Bloch vectors are exactly (0, 0, 0) with purity 0.5 — the arrows collapse to the centre, the visual signature of maximal entanglement. — ⚙️ proven in s2-q030
  • Two equal-magnitude q-sphere amplitudes of +0.7071 and −0.7071 give same-size nodes at phase 0 and phase π, hence two different colours. — ⚙️ proven in s2-q032
  • A q-sphere shows a node only where the amplitude is nonzero — H on qubit 0 with X on qubit 1 gives nodes at |10⟩ and |11⟩ only (little-endian labels). — ⚙️ proven in s2-q031

Traps:

  • Passing a quantum state to plot_bloch_vector does not produce per-qubit spheres — that function wants an explicit [x, y, z], and only plot_bloch_multivector takes a state. — ⚙️ proven in s2-q034
  • sv.draw("qsphere") works, but sv.qsphere(), sv.plot(...) and importing plot_qsphere all fail, and any other draw token raises ValueError listing the valid ones. — ⚙️ proven in s2-q029
  • rz(π/2) on |0⟩ changes only a global phase, so the arrow stays pinned at the north pole, while h and h; s both land on the equator. — ⚙️ proven in s2-q035
Exam checklist
  • Name the four draw outputs and the return type of each before choosing one.
  • Reject any drawing keyword you cannot name — the signature has no **kwargs, so typos raise.
  • Convert a SamplerV2 result with result[0].data.meas.get_counts() before plotting anything.
  • Read every bitstring right-to-left: the leftmost character is the highest-index qubit.
  • Ask whether a plot wants counts, a quantum state, or a backend before picking it.
  • Treat Bloch arrows at the origin as the signature of maximal entanglement, not an error.
  • Transpile first, then plot a layout — the original circuit carries none.
  • Decide what the question needs: per-qubit direction, amplitudes and phases, or matrix elements.

Every fact above is sourced: 📖 links go to official documentation, ⚙️ marks facts observed by executing code against the pinned Qiskit stack (the linked section page shows the proof evidence on its practice questions).