Step 06 of 12 · 25 min

How Qiskit Works: One Bell State, End to End

Map the Bell pair, choose a primitive, transpile only when a device requires it, execute, then post-process the counts. Sampler is the hands-on primitive. Estimator is conceptual.

Logical circuit and ISA circuit

The logical circuit is the one you wrote: H, CX, and measurement, with no device constraints.

An ISA circuit is that program rewritten for one backend’s basis gates and qubit connections. A local statevector sampler can run the logical circuit. A QPU cannot, until a pass manager has produced the ISA circuit.

IBM’s hello-world guide uses this same shape and then continues into a much larger example. That larger example is not a second lab here.

Map

Write the problem as a circuit. In this workshop the problem is the Bell pair.

Map
from qiskit import QuantumCircuit

bell_circuit = QuantumCircuit(2)
bell_circuit.h(0)
bell_circuit.cx(0, 1)
Input
Two qubits, both starting in |0⟩, and the gates you intend.
Output
A QuantumCircuit object. Nothing has been run.
Vocabulary
Circuit, gate, qubit. H and CX are the gates in this map.
Why it matters
Every later step uses this object. If the gates land on different qubits, the run still finishes, and it shows a different state.
If the result looks unexpected
What may have happened: H is on qubit 1, or the CX control and target are swapped. Check those two wires and run the simulator again. A facilitator can look with you, and you can keep learning from the histogram while you adjust.

Choose Sampler or Estimator

A primitive is the interface that runs the circuit. A sampler returns a distribution of bitstrings. An estimator returns an expectation value of an observable.

Choose Sampler or Estimator
from qiskit.primitives import StatevectorSampler

# Sampler returns counts. This is the hands-on primitive.
sampler = StatevectorSampler()

# Estimator estimates an expectation value.
# This workshop explains it and does not assign an Estimator lab.
Input
The question you are asking: counts, or an expectation value.
Output
A primitive object. The Bell lab chooses StatevectorSampler.
Vocabulary
Sampler, Estimator, primitive. Counts belong to the sampler.
Why it matters
The lab reads a histogram. That is sampler data. Estimator answers a different question.
If the result looks unexpected
What may have happened: an estimator was asked for a histogram, or a sampler was asked for an expectation value. This lab uses a sampler because it wants counts. Switch the primitive and run again. The glossary defines both, and a facilitator can point at the line.

Transpile

Rewrite the logical circuit into the instructions a chosen backend can run. This ideal lab does not transpile.

Transpile
# The local lab runs the logical circuit on StatevectorSampler.
# A QPU needs an ISA circuit: the device's gates and connections.
# from qiskit.transpiler import generate_preset_pass_manager
# isa_circuit = generate_preset_pass_manager(
#     backend=backend, optimization_level=1
# ).run(measured_circuit)
Input
The logical circuit and, on hardware, a backend.
Output
An ISA circuit when a backend is present. On the local sampler, you keep the logical circuit.
Vocabulary
Transpile, backend, ISA circuit, basis gates.
Why it matters
A QPU rejects gates and connections it does not have. The simulator used here does not need that rewrite.
If the result looks unexpected
What may have happened: the circuit was transpiled before the logical gates were checked, so a wiring slip and hardware noise showed up together. Confirm the logical circuit on the simulator first. That saves QPU time and makes the next step easier to read.

Execute

Hand the measured circuit to the primitive for a number of shots. The job is the handle for that run.

Execute
measured_circuit = bell_circuit.copy()
measured_circuit.measure_all()
job = sampler.run([measured_circuit], shots=1024)
Input
The measured circuit and shots=1024.
Output
A job. On the local sampler it finishes on your computer.
Vocabulary
Job, shot, backend. A shot is one execution of the measured circuit.
Why it matters
Quantum outcomes are samples. One shot is one bitstring, not the distribution.
If the result looks unexpected
What may have happened: shots was 1, so one bitstring was read as the whole state. Raise the shot count and run the simulator again. One shot is one sample, and that is a normal thing to discover.

Post-process

Turn the finished job into counts and read them. Qubit 0 is the rightmost bit.

Post-process
result = job.result()
counts = result[0].data["meas"].get_counts()
print(counts)
Input
The finished job.
Output
A dictionary of bitstrings and tallies. Ideal mass sits on 00 and 11.
Vocabulary
Result, counts, histogram. Counts are tallies, not amplitudes.
Why it matters
The histogram is the observation. It is not, by itself, a complete proof of entanglement. You are not expected to prove that a quantum method is better than a classical one from this picture alone.
If the result looks unexpected
What may have happened: the left bit was read as qubit 0, or hardware noise was read as a new Bell state. Qubit 0 is the rightmost bit. Noise on 01 and 10 can appear on a QPU. Compare with the simulator, and ask a facilitator if the picture still looks surprising. You can keep learning from the ideal counts.
What does a sampler return?

Choose one answer when you are ready. This is practice.

Choose a pathway