Interference and your first algorithm
Make a hidden phase visible, then run a two-qubit Grover search.

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 single path has nothing to differ from.
A Hadamard on each side of the spin makes the phase show in the tally, and Grover's search, later in this lesson, uses the same effect.
Two paths, and whether they agree
The first Hadamard spreads a qubit at 0 over two paths, which here means the two weighted options of lesson 2, 0 and 1. 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: paths with the same phase add up, and paths with opposite phases cancel.
The rule is lesson 4's with the phase in place of the angle: 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.
Hadamard, RZ, Hadamard
Hadamard, phase rotation, Hadamard. The first H splits the qubit into two paths, RZ turns the phase of one, and the second H recombines them so they add or cancel. The share of 1 after it is sin²(φ/2), the RY rule with the phase in place of the angle.
The circuit, then the code
The circuit is one qubit and three gates, with 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.
# 'backend' already exists when this runs: it is whatever you pick in the Run on Qollab 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}")
- 1the phase RZ sets. On its own it is invisible; change it and press Run
- 2spread the qubit over 0 and 1, two paths
- 3turn the invisible dial on one of them
- 4fold the two paths back together: they add, or they cancel
- 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:
phi | counts | measured share of 1 | rule says |
|---|---|---|---|
0 | {'0': 1000} | 0.000 | 0.000 |
pi / 2 | {'0': 474, '1': 526} | 0.526 | 0.500 |
pi | {'1': 1000} | 1.000 | 1.000 |
In lesson 4 rz changed nothing visible. Between two Hadamards the same gate moves the share from all 0 to all 1.
Assignment: take the second Hadamard away
- Keep
phi = piand delete the secondqc.h(0). The circuit is then a Hadamard, a phase and a measurement. - Predict the tally: lesson 4 showed that a phase on its own leaves the share of
1where it was, so ask where the first Hadamard left it. - Press Run on Qollab and choose IonQ Aria 1.
- Change
phito anything else and press 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 no gate after it to turn the phase into a share.
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.
Grover's search on two qubits
The search uses two qubits. Hadamards on both spread them evenly over four candidates, 00, 01, 10 and 11, a quarter each: four weights of one half, since a share is a weight's size squared.
An oracle is 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.
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.
The diffusion step turns the mark into a change in the tally. It does the second Hadamard's job for four candidates instead of two paths: it reflects every weight about their average. Each weight moves to the same distance on the other side of the average, so the new value is twice the average minus the old one.
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.
Twice one quarter is one half, and one half minus one half is zero, so each unmarked weight goes to zero. For the marked weight, one half minus a negative one half is one, so it goes to a full one; the gates below produce that reflection with a minus sign in front, which measurement cannot see. After one round of oracle and diffusion, the marked answer is the only one left.
The five lines below are that reflection written in gates; the diffuser section of the interference article shows how they do it, and you can use them without re-deriving them. The oracle encodes the problem and the diffusion step never changes, so the same five lines find whatever a different oracle marks.
# 'backend' already exists when this runs: it is whatever you pick in the Run on Qollab 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)}")
- 1all four answers at once, a quarter each
- 2the oracle: flip the sign of 11 and nothing else. Invisible on its own
- 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 the search project too; the last two assignments edit it.

Our press on 15 September 2026, on the Aria 1 noise model, came back {'11': 1000}: the marked answer on all 1,000 shots. The noise model can put a few shots elsewhere on another press; the ideal is all 1,000.
Changing the oracle
The CZ marks 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: the count grows with the square root of their number, about 0.8 times that root, and Grover's speed-up comes from that square root. Grover's search on Qollab runs it on eight candidates, where two rounds are needed.
Assignment: mark a different answer
- In your fork of the search, work out which qubit reads
0in the string10. Remember which character is qubit 0. - 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. - Predict the tally.
- Press Run on Qollab and choose IonQ Aria 1.
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 diffusion step finds it: 10 on every shot, apart from the tail.
Assignment: search without the diffusion step
- Put the oracle back to
11by removing the two X lines you added around the first CZ. - 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. - 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 no gate after the phase turns it into a share.
- Press Run on Qollab and choose IonQ Aria 1.
Solution
All four strings come up, a quarter each, apart from the tail. The oracle 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])
Some presses in this course came back with a small tail of wrong answers. Lesson 6 measures that tail on the noise models of four IonQ machines, then looks at the real machines in the same dialog.
Stay in the loop.
Get the latest tutorials, demos, and project showcases straight to your inbox. No noise, just the good stuff.
Enable JavaScript to sign up.
Qollab updates only. Unsubscribe any time. Privacy Policy.
On this page
