Programming your first quantum circuitsLesson 5 of 6

Interference and your first algorithm

Make a hidden phase visible, then run a two-qubit Grover search.

The Playground's Circuit panel: qubit q0 runs through H, then Rz(0.5π), then H, into a measurement.

Interference is two paths through a circuit meeting again: where they agree they add up, where they disagree they cancel, and the tally shows which. Lesson 4 spun a fresh qubit around the measurement axis and nothing showed, because a lone path has nothing to differ from.

Put a Hadamard before the spin and there are two paths; the spin becomes a phase difference between them. A second Hadamard brings them back together and turns that difference into odds you can read. Grover's search, later in this lesson, runs on the same effect.

Two paths, and whether they agree

The first Hadamard spreads a qubit at 0 over two paths: the two weighted options of lesson 2, 0 and 1, not routes anything travels. The RZ gate turns the phase of one path by phi.

A phase is the continuous version of the sign a weight carried in lesson 2, and turning it changes nothing a measurement can see.

The second Hadamard brings the two paths back together, and here the phase matters: paths that agree add up, and paths that disagree cancel.

Two panels. A qubit at 0 is split by H into path 0 and path 1, a phase box sits on path 1, and a second H merges the paths. With the phase at 0 every shot reads 0; with the phase at pi every shot reads 1.
Same circuit, one number changed. At φ = 0 the two paths add on 0 and every shot reads 0. At φ = π they cancel on 0, add on 1, and every shot reads 1. In between, the phase sets the odds and each shot is still a draw.

The rule is the one from lesson 4 with a new driver. After h, rz(phi), h on a fresh qubit, the share of shots that read 1 is sin²(φ/2).

At phi = 0 nothing is turned and every shot reads 0; at phi = pi every shot reads 1; at pi / 2 it is the even split.

h · rz · h

A phase, made visible

Hadamard, phase rotation, Hadamard. The first H opens two paths, RZ turns one of them, the second H closes them and lets them add or cancel. The share of 1 after it is sin²(φ/2), the RY rule with the phase as the dial.

The circuit, then the code

One qubit, three gates, the phase at the top of the file:

phi = pi / 2

qc = QuantumCircuit(1, 1)
qc.h(0)                       # two paths
qc.rz(phi, 0)                 # turn one of them
qc.h(0)                       # bring them back together
qc.measure(0, 0)

The plumbing is lesson 4's: the measured share of 1 beside what the rule says.

interference.py Python · PlaygroundOpen in Playground ↗
# 'backend' already exists when this runs: it is whatever you pick in the Run Experiment dialog.
from math import pi, sin
from qiskit import QuantumCircuit
from qiskit.providers.jobstatus import JobStatus
import time
shots = 1000
1phi = pi / 2
qc = QuantumCircuit(1, 1)
2qc.h(0)
3qc.rz(phi, 0)
4qc.h(0)
qc.measure(0, 0)
print(qc)
job = backend.run(qc, shots=shots)
while job.status() not in (JobStatus.DONE, JobStatus.ERROR, JobStatus.CANCELLED):
    time.sleep(5)
counts = job.result().get_counts()
print(counts)
measured = counts.get("1", 0) / shots
5expected = sin(phi / 2) ** 2
print(f"phi = {phi:.4f}   share of 1: measured {measured:.3f}   rule says {expected:.3f}")
  1. 1the phase RZ sets. On its own it is invisible; change it and press Run
  2. 2spread the qubit over 0 and 1, two paths
  3. 3turn the invisible dial on one of them
  4. 4fold the two paths back together: they add, or they cancel
  5. 5the same rule as RY, now driven by a phase

Run the circuit, three times

Predict the share of 1 for pi / 2 first. Then open the project, press Run on Qollab, choose IonQ Aria 1, and press Run. Then fork it, as in lesson 1, and in your fork set phi to 0 and to pi, one press each.

Our three presses on 15 September 2026, on the Aria 1 noise model:

phicountsmeasured share of 1rule says
0{'0': 1000}0.0000.000
pi / 2{'0': 474, '1': 526}0.5260.500
pi{'1': 1000}1.0001.000

Lesson 4 ended with rz doing nothing visible. The same gate, between two Hadamards, now moves the share from all 0 to all 1. The phase was there all along; the second Hadamard is what makes it count.

Assignment: take the second Hadamard away

  1. Keep phi = pi and delete the second qc.h(0). What is left is a Hadamard, then a phase, then a measurement.
  2. Predict the tally: lesson 4 showed that a phase on its own leaves the share of 1 where it was, so ask where the first Hadamard left it. Then run.
  3. Change phi to anything else and run again. The rule line keeps printing sin²(φ/2), which no longer applies to this circuit; the tally is the answer.
Solution

The circuit is h then rz(pi) then measure: an even split with a phase on one path and nothing to make the phase count.

qc.h(0)
qc.rz(phi, 0)
qc.measure(0, 0)

The share of 1 is one half, whatever phi is, because the measurement reads the split the first Hadamard made and cannot see the phase. The second Hadamard was the whole mechanism.

Now two qubits. Two Hadamards put four candidates in play, 00, 01, 10 and 11, a quarter each: four weights of one half, since a share is a weight's size squared.

Then an oracle: the part of a search that checks the condition and marks whatever passes it. Here the condition is fixed, "both qubits are 1", and the marking is a CZ gate.

cz

The controlled-Z gate

cz(a, b). Flips the sign of the weight on the state where both qubits are 1, and does nothing anywhere else. A sign is a phase, so a CZ on its own is as invisible as an RZ: after it the tally still shows four quarters, and the mark cannot be seen.

The diffusion step is what makes the mark count, and it is the second Hadamard's job done for four candidates instead of two paths. It reflects every weight about their average.

After the oracle the three unmarked weights are one half each and the marked one is minus one half, so the average is one quarter.

Reflecting about it sends each unmarked weight to zero and the marked one to a full one, with a minus sign in front in this circuit that measurement cannot see. One round, oracle then diffusion, and the marked answer is the only one left.

The five lines below are that reflection written in gates. The two Hadamards on both qubits change to a frame where the average is a single state; X on both qubits and a CZ flip the sign of that one state and leave the other three alone; the Hadamards change back.

The five lines are the same for any oracle on two qubits, so you can reuse them without re-deriving them.

The oracle holds the problem and the diffusion step never changes, so the same five lines find whatever a different oracle marks. That is what makes it a search rather than a circuit that happens to output 11.

grover.py Python · PlaygroundOpen in Playground ↗
# 'backend' already exists when this runs: it is whatever you pick in the Run Experiment dialog.
from qiskit import QuantumCircuit
from qiskit.providers.jobstatus import JobStatus
import time
shots = 1000
qc = QuantumCircuit(2, 2)
1qc.h([0, 1])
2qc.cz(0, 1)
3qc.h([0, 1])
qc.x([0, 1])
qc.cz(0, 1)
qc.x([0, 1])
qc.h([0, 1])
qc.measure([0, 1], [0, 1])
print(qc)
job = backend.run(qc, shots=shots)
while job.status() not in (JobStatus.DONE, JobStatus.ERROR, JobStatus.CANCELLED):
    time.sleep(5)
counts = job.result().get_counts()
print(counts)
for bits, n in sorted(counts.items()):
    print(f"{bits}: {n:4d}  {100 * n / shots:5.1f}%  {'█' * (50 * n // shots)}")
  1. 1all four answers at once, a quarter each
  2. 2the oracle: flip the sign of 11 and nothing else. Invisible on its own
  3. 3the diffusion step, five lines: it turns that hidden sign into probability

Predict the tally, then open the search project and run it on IonQ Aria 1. Fork it too; the last two assignments edit it.

The run console after the Grover search: the counts dict {'11': 1000} and one full-length bar at 11
One round, one answer. In the text drawing the first pair of H gates and the CZ are the oracle side; the block after them is the diffusion step. On a perfect simulator the search never misses; on this press the noise model did not touch it either.

Our run on 15 September 2026, on the Aria 1 noise model, came back {'11': 1000}: the marked answer on all 1,000 shots, and not a single miss. The noise model can put a few shots elsewhere on another press; the ideal is exactly this.

Changing the oracle

The CZ fires on 11. To mark a different string, surround it with X gates on the qubits that should read 0 in that string. An X before the CZ turns that qubit's 0 into a 1 for the CZ to see, and an X after it turns it back.

The diffusion step stays exactly as it is.

With exactly four candidates one round lands the marked answer on 100%. With more candidates it takes more rounds, about the square root of their number, which is where Grover's speed-up comes from; Grover's search on Qollab runs it at a size where the rounds matter.

Assignment: mark a different answer

  1. In your fork of the search, work out which qubit reads 0 in the string 10. Remember which character is qubit 0.
  2. Put an X gate on that qubit immediately before the first qc.cz(0, 1) and another immediately after it, as the section above describes. Do not touch the diffusion step.
  3. Predict the tally, then run.
Solution
qc.h([0, 1])
qc.x(0)
qc.cz(0, 1)
qc.x(0)
qc.h([0, 1])
qc.x([0, 1])
qc.cz(0, 1)
qc.x([0, 1])
qc.h([0, 1])

The oracle now marks 10, and the unchanged diffusion step finds it: 10 on every shot, bar the tail. The diffusion step did not know which answer was marked; it never does.

Assignment: search without the diffusion step

  1. Put the oracle back to 11 by removing the two X lines you added around the first CZ.
  2. Delete the five diffusion lines, the block from the second qc.h([0, 1]) down to the last one, so only the opening Hadamards, the CZ and the measurements remain.
  3. Predict the tally: after the oracle the mark is a sign, and a sign is a phase, so ask what a tally looks like when nothing follows the phase to make it count. Then run.
Solution

Four strings, a quarter each, bar the tail. The oracle did its job and marked 11, and without the diffusion step the mark stays invisible.

qc.h([0, 1])
qc.cz(0, 1)
qc.measure([0, 1], [0, 1])

Interference is not the oracle and not the mark. It is the step that turns a phase into something a measurement can read.

Some runs in this course came back with a small tail of wrong answers, and each time the lesson called it the noise model and moved on. The last lesson stops and measures it, on four machines' models, and then looks at the real machines behind them.

Stay in the loop.

Get the latest tutorials, demos, and project showcases straight to your inbox. No noise, just the good stuff.