Understanding Quantum Superposition
Twenty-four gates put a register into a superposition over sixteen million outcomes. Getting a useful answer out of it is the hard part.
About the author
Quantum superposition is often summed up as "a quantum computer tries every answer at once". The phrase is about the state inside the circuit, which holds weights on outcomes rather than answers, and a run still returns one measured outcome per shot.
What is quantum superposition?
Superposition is a qubit carrying a weight on each outcome it could produce, instead of carrying a value. Measuring turns those weights into one answer, and running the same circuit again can give you a different one.
The weights are part of the qubit. You set them with rotation gates, you can tune them continuously rather than only to fifty-fifty, and each qubit you add with its own Hadamard doubles how many outcomes carry weight. The weights themselves never come back out; a quantum computer returns only a tally of measured outcomes.
An ordinary bit holds a 0 or a 1. If you have not looked at it you do not know which, but the bit is already one or the other, and looking only tells you which.
A qubit in superposition has no value to find before you measure.
The two situations look the same from outside: both give you a 0 or a 1, and neither lets you predict which. Three polarising filters show that measuring can change what is measured.
Adding a filter lets more light through
A polarising filter passes light lined up with its own axis and blocks light across it. Two filters at right angles pass nothing, because the second blocks all the light the first passes.
Put a third filter between those two at forty-five degrees and about a quarter of the light that cleared the first reaches the far side.
A sorting picture cannot explain the extra light: if each photon carried a polarisation all along and the filters only sorted them, one more filter could only subtract.
Instead, every photon that comes out of a filter is polarised along that filter's axis, whatever its polarisation was before. The middle filter passes light at forty-five degrees to the last filter, where the first filter's light was at ninety. Half the light clears the middle filter, half of that clears the last, and a half of a half is a quarter.
The middle filter's angle sets how much gets through: about a fifth of the light at thirty degrees, a quarter at forty-five, nothing at ninety. Each filter passes cos² of the angle between it and the one before, which is Malus's law, written down for light in 1809. Two of those in a row multiply, so the light through all three peaks with the middle filter at forty-five degrees.
A qubit follows the same rule. Rotate one by an angle θ away from a certain 0, measure it, and 0 comes back cos²(θ/2) of the time: the same cos² as Malus's law, with the qubit's rotation angle at twice the filter's.
Where superposition came from
Physicists first described superposition as a property of waves, then argued about what it meant for matter.
Schrödinger meant the cat as an objection. A cat both alive and dead is ridiculous, and his point was that a theory letting you write one down needed better rules about when superposition stops. The phrase "both at once" is still in use, without the argument he made with it.
None of that has to be settled to write a circuit.
The smallest superposition you can make
The smallest superposition takes one qubit and one gate: the Hadamard leaves a qubit weighted evenly between 0 and 1.
# 'backend' is pre-created for you in the Qollab Playground.
from qiskit import QuantumCircuit
1qc = QuantumCircuit(1, 1)
2qc.h(0)
3qc.measure(0, 0)
4job = backend.run(qc, shots=1000)
5print(job.result().get_counts())
- 1A circuit is a list of operations to run in order. This one declares one qubit and one ordinary bit to read the answer into.
- 2A gate is one operation applied to a qubit. This is the Hadamard, applied to qubit 0, and it leaves that qubit with no fixed value and equal weight on
0 and 1. - 3Measuring is the only way to see a qubit. It forces this one to a definite
0 or 1 and copies that into the bit. - 4One run gives one answer and which one is random, so run it a thousand times. Each run is called a shot.
- 5
{'0': 503, '1': 497}: the weights were even, so a thousand shots come back about level.
Add a second qubit and a second Hadamard, and the outcomes go from two to four. Two or more qubits addressed together are a register.
qc = QuantumCircuit(2, 2)
1qc.h(0)
qc.h(1)
qc.measure([0, 1], [0, 1])
2print(backend.run(qc, shots=1000).result().get_counts())
- 1An even superposition on each qubit separately: each qubit is in the same state as in the single-qubit circuit above.
- 2
{'00': 254, '01': 248, '10': 249, '11': 249}: four outcomes now, each carrying about a quarter of the weight.
Each qubit you add with its own Hadamard doubles the number of outcomes carrying weight: two for one qubit, four for two, 1,024 for ten and 16,777,216 for twenty-four.
The cost of exact simulation doubles with each qubit too. To simulate a circuit exactly, an ordinary computer stores a complex number per outcome, sixteen bytes each, and applies every gate to all of them.
Twenty-four qubits is about 270 MB and runs on a laptop. Thirty is 17 GB, forty is 18 TB and fifty is 18 PB, about the total memory of the largest supercomputers. A quantum processor has no such memory cost, because its qubits hold the state themselves.
Those numbers are for exact simulation. Circuits with little entanglement, or with a lot of repetition in them, are simulated well past fifty qubits by methods that never build the full list. Quantum Courier's MaxCut benchmark, at twenty-four qubits, ran on a simulator.
A big state space makes exact simulation expensive, but it does not on its own make a quantum computer faster at anything. A useful answer needs a circuit that concentrates the weights on outcomes worth measuring.
Reading the weights back does not get cheaper either: a thousand shots return a thousand outcomes, whether the register has two possible outcomes or sixteen million.
The gates in these circuits
Five gates and one instruction cover every excerpt below.
Hadamard
Takes a qubit sitting at 0 and leaves it weighted evenly between 0 and 1: measure it and either answer is equally likely.
Many algorithms start by applying it to every qubit of a register, which puts equal weight on every possible outcome at one gate per qubit.
Rotates a qubit by an angle. The angle comes before the qubit in the call and sets how likely a qubit starting at 0 is to read 1.
At 0 it is certain to read 0, at π certain to read 1, and at π/2 it gives the same even split the Hadamard does. Any angle in between sets a bias, so a circuit can have odds other than fifty-fifty.
A rotation that does not touch how often the qubit reads 0 or 1. It moves the phase instead: the relative angle between the two halves of the state.
Measure directly and nothing in the counts depends on the phase. Put an h in front of the measurement, which asks the qubit a different question, and the phase shows up.
Rotates a qubit about the x axis. On a qubit that already carries weight on both outcomes, how much weight it shifts depends on the phase: on the even superposition a Hadamard makes, it shifts none.
The MaxCut circuit below uses it to move weight between candidate answers.
CNOT
Flips the second qubit when the first is 1. With the first qubit in superposition and the second at 0, it entangles the two.
Below it appears in a sandwich, cx then rz then cx, a common way to write a two-qubit interaction from standard gates.
Reads a qubit into an ordinary bit and forces it to a definite 0 or 1. Measurement cannot be undone: whatever weights the qubit carried are gone once you look.
A single shot therefore gives one outcome and says little about the weights behind it, so the circuits here are run many times over.
Superposition Sequencer: an angle sets the rhythm
Francisco Estivallet's Superposition Sequencer is a step sequencer where you place gates instead of drum hits. On every beat the backend measures the circuit once, and the result decides which tracks fire.
A qubit that reads 0 every time is a track that plays every beat, and a qubit weighted evenly plays about half the time. Any weighting between those sets how often a track plays, and the sequencer's mapping is three lines:
def trigger_probability(theta: float) -> float:
"""P = cos²(θ/2). θ=0 → 1.0; θ=π → 0.0."""
1 return math.cos(theta / 2) ** 2
- 1θ is the polar angle: how far the qubit has been rotated away from a certain
0. The track fires on the beats where the measurement returns 0.
The README puts it in terms of the Bloch sphere: a qubit at the north pole fires every beat, at the equator it fires on about half the beats, and at the south pole it stays silent.
The sequencer's Qollab quickstart sets one track with circuit.ry(0.976411, 3), and the function above turns that angle into cos²(0.4882) = 0.78: the track on qubit 3 fires on about 78% of beats.
Superposition SequencerFrancisco Estivallet built a quantum music sequencer where every note comes from a circuit you design, turning a quantum computer into a playable instrument.Phase: the second angle, which the counts do not show
trigger_probability above takes only θ. The second angle is φ, the phase: the relative angle between the 0 and 1 halves of the state. The sequencer's other mapping uses φ for timing:
MAX_SWING = 0.35 # fraction of beat interval
def swing_offset(phi: float, max_swing: float = MAX_SWING) -> float:
"""Map azimuthal angle φ in [0, 2π) to swing in [-max_swing, +max_swing]."""
1 normalized = (phi % (2 * math.pi)) / (2 * math.pi)
2 return (2 * normalized - 1) * max_swing
- 1φ is the phase.
- 2φ=0 plays maximally early, φ=π lands on the grid, φ approaching 2π plays maximally late.
Two circuits with the same θ and different φ draw from the same distribution. Run enough shots and the two tallies converge on each other; the differences that remain are shot noise. The sequencer still plays them differently, because it does not read φ off the shots. Instead it reads φ off a simulated state, and uses the shots only to decide which beats fire.
An rz gate sets the phase, and measuring directly never reports it. A z gate is an rz by π: it flips a sign, which changes the state without changing any count, as the measurement article shows.
A weight is not only a size: it also has a sign, which the phase sets. Unlike probabilities, weights can be negative, so two weights of opposite sign subtract.
Phase shows up in the counts when two contributions to the same outcome combine and their weights reinforce or cancel, which is called interference; circuits use it to concentrate a flat superposition on an answer.
Sampling a register as one distribution
The sequencer's sampling module opens with a warning:
"""Correlated measurement sampling from the full statevector.
IMPORTANT (spec §16.2): for entangled qubits, sampling each qubit independently
from its marginal destroys the correlations. Always sample bitstrings from the
full statevector's probability distribution.
"""
def sample_correlated(sv: Statevector, num_shots: int, rng=None) -> list[str]:
1 probs = sv.probabilities()
probs = probs / probs.sum()
n = sv.num_qubits
2 indices = rng.choice(len(probs), size=num_shots, p=probs)
return [format(int(i), f"0{n}b") for i in indices]
- 1
probs holds one probability per outcome of the whole register: sixteen numbers for four qubits. - 2Each shot draws one outcome from that joint distribution.
Four qubits each at fifty-fifty form one distribution over sixteen outcomes. If the qubits are unentangled, that distribution is four independent coins; entangling gates can change it while each qubit's odds stay at fifty-fifty. A Hadamard on one qubit and a cx onto a second qubit left at 0 is the smallest example: each still reads fifty-fifty, and only 00 and 11 come back.
A marginal is the distribution for one qubit once you ignore the others. Sampling each qubit from its own marginal gives four independent coins every time, which loses the correlations the circuit built. The sequencer's README describes what that does to the music: the entanglement presets become meaningless noise, while sampling the joint distribution keeps two entangled qubits firing together.
A superposition belongs to the whole register, and inspecting one qubit at a time misses any entanglement between the qubits.
Quantum Courier: a 24-qubit MaxCut circuit
Dr. Siti Fariya's Quantum Courier races classical and quantum solvers across logistics problems. Its repository also has a separate MaxCut benchmark, the part that maps onto a circuit most directly.
MaxCut splits the nodes of a graph into two groups so that as many edges as possible run between the groups rather than inside them. The benchmark uses twenty-four nodes with three edges each, thirty-six edges in total. Splitting twenty-four nodes two ways gives 16,777,216 possible splits, the same 2^24 as twenty-four qubits that each get a Hadamard.
A graph is not written in gates, so it gets rewritten twice before it becomes a circuit. First as a cost function: every edge contributes -x_u - x_v + 2·x_u·x_v, which is at its lowest when the two endpoints land on opposite sides. Then the substitution x = (1 - z)/2 turns those 0/1 variables into -1/+1 values, which match the two outcomes of measuring a qubit (0 for +1, 1 for -1).
Single-variable terms would become an rz on one qubit, but for MaxCut they cancel out, so each edge contributes only its paired term, the cx, rz, cx sandwich: every edge of the graph becomes a two-qubit interaction in the circuit.
def build_maxcut_qaoa(h: dict, J: dict, n: int, gamma: float, beta: float):
"""Build the standard depth-1 QAOA circuit for MaxCut."""
from qiskit import QuantumCircuit
qc = QuantumCircuit(n, n)
# Initial state: uniform superposition
1 qc.h(range(n))
# Cost layer: exp(-i * gamma * H_cost)
for i, hi in h.items():
if abs(hi) > 1e-12:
qc.rz(2 * gamma * hi, i)
for (i, j), Jij in J.items():
if abs(Jij) > 1e-12:
qc.cx(i, j)
2 qc.rz(2 * gamma * Jij, j)
qc.cx(i, j)
# Mixer layer: exp(-i * beta * H_mixer)
for i in range(n):
3 qc.rx(2 * beta, i)
qc.measure(range(n), range(n))
return qc
- 1One Hadamard per node. With n=24 that is twenty-four gates, and the register now carries equal weight on all 16,777,216 possible splits.
- 2One interaction per edge, which shifts the phase by an amount that depends on whether that edge ends up cut.
- 3The mixer moves weight between splits, turning the cost layer's phase differences into differences in weight.
gamma and beta are two numbers, and the circuit above is only one evaluation of them. A classical optimiser sits outside it, rebuilding and rerunning the whole circuit at each step and adjusting the pair from the counts each run returns. It starts at gamma = π/8 and beta = π/4, which the repo takes from the 2014 QAOA paper by Farhi, Goldstone and Gutmann. The 0.393 and 0.785 in Quantum Courier's Playground excerpt are those two constants rounded.
The committed benchmark ran five random graphs at 4,096 shots on a local simulator, and the repository reports QAOA ahead of its classical baseline on all five.
That baseline is a one-step local search where each node decides using only its immediate neighbours and never sees the whole graph. The repo picks it deliberately and cites Carlson et al. 2023 for the choice. At depth 1 the quantum circuit also reaches only one edge away from each node, so the repo argues that a solver with a global view would not be like-for-like.
The twenty-four Hadamards put weight on 16,777,216 outcomes, and the 4,096 shots sample at most 0.024% of them.
Quantum CourierDr. Siti Fariya built a browser game that races classical and quantum solvers across five real logistics problems, and shows honestly which one wins, and why.What the rest of the MaxCut circuit does
An even mix over sixteen million splits, sampled 4,096 times, gives you 4,096 arbitrary splits, no better than guessing. Everything after qc.h(range(n)) in build_maxcut_qaoa is there to move weight off the bad splits and onto the good ones. With well-tuned gamma and beta, more of the 4,096 samples land on good splits; with poorly chosen ones they can land on worse ones.
The two layers split the work. Splits that cut more edges end up with a different phase from splits that cut fewer, and the mixer turns those phase differences into differences in weight. The cost layer is set by gamma and the mixer by beta, and the surrounding optimiser spends its time tuning those two numbers.
Phase differences turning into weight differences is interference, the same effect as in the phase section above.
Reading amplitudes from a simulator
The sequencer's interface shows you a Bloch sphere per qubit and the amplitudes of the full state, updating as you add gates. Alongside that, it plays audio driven by measurements. Those are two different code paths, and only the measurement path could run on a quantum computer.
def state_at_step(spec: CircuitSpec, step: int) -> StepState:
qc = build_subcircuit(spec, up_to_step=step)
bloch = get_bloch_vectors(qc)
1 sv = Statevector.from_instruction(qc)
2 return StepState(step=step, bloch_vectors=bloch, statevector=_amplitudes(sv))
- 1This computes the state directly, with the exact complex amplitude of every basis state and no sampling.
- 2
_amplitudes sends the browser a real and an imaginary part per outcome for the amplitude bars; the spheres come from bloch_vectors.
Statevector.from_instruction is a simulator doing linear algebra: it gives the weight and phase of every outcome, the information a measurement destroys.
On hardware, state_at_step has no counterpart: no call returns the state, because measuring a quantum state ends it.
Keep a simulator in the loop while you are still learning a circuit, until the register grows past what exact simulation can hold.
What a real machine does to a superposition
The sequencer also reads the length of each qubit's Bloch vector, which entanglement shortens in its simulation and noise shortens on hardware.
for q in range(n):
others = [i for i in range(n) if i != q]
1 rho = partial_trace(sv, others).data
r_x, r_y, r_z = _bloch_from_density(rho)
2 r = float(np.sqrt(r_x * r_x + r_y * r_y + r_z * r_z))
- 1This traces out every other qubit and keeps what is left of this one.
- 2
r is the length of that vector: r=1 means the qubit has a state of its own, and r below 1 means it is mixed; in the sequencer's noiseless simulation, entanglement is the cause.
The sequencer maps r to timbre, and its velocity mapping turns a shorter vector into a quieter note. The README says this is deliberate: entanglement makes an individual qubit's state mixed, and the instrument plays that instead of normalising it away.
On real hardware r also shrinks for a second reason: a superposition decays through contact with its environment, every gate takes time, and a deeper circuit gives it more time to decay. Your counts then carry noise on top of the distribution the circuit set.
Shivani Mayekar's QuantumCanvas drops rare outcomes, which on hardware are often noise, with one line:
counts = job.get_counts()
1counts = {b: c for b, c in counts.items() if c > shots * low_prob}
- 1Drops outcomes that turned up too rarely to be signal. With low_prob=0.05, anything under 5% of the shots goes.
For a Bell state, a pair of entangled qubits that should only produce 00 and 11, the filter removes the 01 and 10 counts that hardware adds. It works there because the expected answer is known; on a circuit whose answer you do not know, a 5% cut can also remove a rare outcome that is part of the answer.
QuantumCanvasShivani Mayekar built a visual sandbox where you drag and drop quantum operations to compose new algorithms and watch each step.The docs list the backends available on Qollab and what to check when a circuit ran cleanly and still gave you something unexpected.
Start with one qubit
A superposition is a weight on each outcome, set before you measure. Four consequences follow:
- An even spread of weights costs one gate per qubit and many shots to read back; the rest of the circuit has to do the useful work.
- The phase never reaches your counts directly, and it can still decide an algorithm's outcome, through interference.
- The weights belong to the whole register, so the per-qubit views cannot be put back together into the register's state.
- On hardware the superposition decays while the circuit runs, and a deeper circuit collects more noise.
Change the angle and watch the split move.
Fork the superposition project, replace qc.h(0) with qc.rx(theta, 0) and define theta above it, then change theta until you can predict the split before the tally comes in. Then open the MaxCut circuit above and find the single line that sets up sixteen million weights.
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.
- 1About the author
- 2What is quantum superposition?
- 3Adding a filter lets more light through
- 4Where superposition came from
- 5The smallest superposition you can make
- 6The gates in these circuits
- 7Superposition Sequencer: an angle sets the rhythm
- 8Phase: the second angle, which the counts do not show
- 9Sampling a register as one distribution
- 10Quantum Courier: a 24-qubit MaxCut circuit
- 11What the rest of the MaxCut circuit does
- 12Reading amplitudes from a simulator
- 13What a real machine does to a superposition
- 14Start with one qubit
