Superdense coding¶
Superdense coding lets you send two classical bits of information by physically transmitting only one qubit. It sounds like it violates information theory, but it doesn't - the trick is that the sender and receiver share an entangled pair in advance, and that shared entanglement is what carries the extra capacity.
Why this exists¶
A single classical bit can carry exactly one bit of information: it's a 0 or a 1, nothing more. A single qubit, measured on its own, also gives you at most one bit - you measure it, you get 0 or 1. So how could sending one qubit ever deliver two bits?
The answer is that the qubit being sent isn't alone. It's one half of an entangled Bell state, and the pair has four perfectly distinguishable configurations - the four Bell states. Superdense coding is the protocol that exploits this: the sender steers the shared pair into one of the four Bell states by acting only on her own qubit, sends that qubit over, and the receiver identifies which of the four states the pair is in. Four distinguishable outcomes = two bits.
This is the conceptual twin of quantum teleportation, just run in reverse: teleportation spends one entangled pair plus two classical bits to move one qubit; superdense coding spends one entangled pair plus one qubit to move two classical bits.
What you need to know first¶
- The four Bell states - read the Bell states page first. The key facts: there are exactly four of them, they're perfectly distinguishable from each other, and the plus/minus sign difference is invisible to a plain measurement but real.
-
Local gates can move between Bell states - this is the non-obvious ingredient. If the pair is in \( |\Phi^+\rangle \) and Alice applies a gate to just her qubit, the joint state of the pair changes:
Alice applies (to her qubit only) Pair becomes nothing \( \lvert\Phi^+\rangle \) X \( \lvert\Psi^+\rangle \) Z \( \lvert\Phi^-\rangle \) X then Z \( \lvert\Psi^-\rangle \) One qubit's worth of local action selects among four global states. That's the entire engine of the protocol.
How it works, step by step¶
Call the sender Alice and the receiver Bob.
- Setup (before any message exists): someone prepares a Bell pair \( |\Phi^+\rangle = \frac{|00\rangle + |11\rangle}{\sqrt{2}} \) and gives one qubit to Alice (
q[0]) and one to Bob (q[1]). They can then travel arbitrarily far apart. - Encoding: Alice decides on her two-bit message \( (z, x) \). She applies X to her qubit if \( x = 1 \), then Z if \( z = 1 \). Per the table above, the pair is now in one of the four Bell states, one per message.
- Transmission: Alice sends her single qubit to Bob. This is the only thing that travels.
- Decoding: Bob now holds both qubits. He applies a CNOT (Alice's qubit as control) followed by H on Alice's qubit. This maps each Bell state to a distinct pair of definite bits.
- Measurement: Bob measures both qubits and reads the message directly:
q[0]gives \( z \),q[1]gives \( x \).
A worked example: sending 11¶
Start with the shared pair:
Alice applies X (because \( x = 1 \)). Her qubit is the first one, so \( |0\rangle \leftrightarrow |1\rangle \) in the first slot:
Alice applies Z (because \( z = 1 \)). Z flips the sign of any term where her qubit is \( |1\rangle \):
She sends her qubit to Bob. Bob applies CNOT (q[0] controls q[1]):
Notice the second qubit has factored out as a definite \( |1\rangle \) - that's bit \( x = 1 \) recovered. Bob applies H to the first qubit, and since \( H\left(\frac{|0\rangle - |1\rangle}{\sqrt{2}}\right) = |1\rangle \):
Bob measures 11. Both bits arrive intact, and only one qubit ever crossed the channel.
The same computation for the other three messages gives 00, 01, and 10 - each Bell state lands on exactly one outcome, with certainty. There's no probability involved in a noiseless run; the four outcomes are perfectly distinguishable.
The circuit¶
Build it in three blocks on a 2-qubit circuit (or load it from the Ready algorithms panel):
- Entangle: H on
q[0], CNOTq[0]→q[1] - Encode (Alice, on
q[0]only): depends on the message - Decode (Bob): CNOT
q[0]→q[1], H onq[0], then measure both qubits
No encoding gates at all. The circuit is: H q[0], CNOT q[0]→q[1], CNOT q[0]→q[1], H q[0], measure. Output: 00.
An X on q[0] between the two blocks: H q[0], CNOT, X q[0], CNOT, H q[0], measure. Output: 01.
A Z on q[0] between the two blocks: H q[0], CNOT, Z q[0], CNOT, H q[0], measure. Output: 10.
An X then a Z on q[0]: H q[0], CNOT, X q[0], Z q[0], CNOT, H q[0], measure. Output: 11.
Good external references for the circuit layout: Wikipedia: Superdense coding and IBM Quantum Learning: Entanglement in action.
The circuit in code¶

Barriers mark the three phases of the protocol (entangle, encode, decode) as vertical lines on the circuit diagram; they don't affect the quantum state or the output.
OPENQASM 2.0;
include "qelib1.inc";
qreg q[2];
creg c[2];
// Entangle
h q[0];
cx q[0], q[1];
barrier q;
// Alice encodes 11: X then Z on q[0]
x q[0];
z q[0];
barrier q;
// Bob decodes
cx q[0], q[1];
h q[0];
measure q -> c;
OPENQASM 3.0;
include "stdgates.inc";
qubit[2] q;
bit[2] c;
// Entangle
h q[0];
cx q[0], q[1];
barrier q;
// Alice encodes 11: X then Z on q[0]
x q[0];
z q[0];
barrier q;
// Bob decodes
cx q[0], q[1];
h q[0];
c = measure q;
from qiskit import QuantumCircuit
from qiskit_aer import AerSimulator
# Two qubits (Alice q0, Bob q1) and two classical bits for the decoded message
message = "11"
qc = QuantumCircuit(2, 2)
# Step 1: shared Bell pair (imagine the qubits being separated afterward)
qc.h(0)
qc.cx(0, 1)
qc.barrier()
# Step 2: Alice encodes her message on q0 alone
# X flips the correlation (01/10 instead of 00/11), Z flips the phase
if message[1] == "1":
qc.x(0)
if message[0] == "1":
qc.z(0)
qc.barrier()
# Step 3: Bob decodes — undoes the Bell recipe, mapping each
# Bell state to a distinct pair of plain bits
qc.cx(0, 1)
qc.h(0)
qc.measure([0, 1], [0, 1])
# Every shot decodes correctly: {'11': 1024}
counts = AerSimulator().run(qc, shots=1024).result().get_counts()
print(counts)
import cirq
q = cirq.LineQubit.range(2)
circuit = cirq.Circuit()
# Entangle
circuit.append(cirq.H(q[0]))
circuit.append(cirq.CNOT(q[0], q[1]))
circuit.append(cirq.ops.Moment()) # barrier
# Alice encodes 11: X then Z on q0
circuit.append(cirq.X(q[0]))
circuit.append(cirq.Z(q[0]))
circuit.append(cirq.ops.Moment()) # barrier
# Bob decodes
circuit.append(cirq.CNOT(q[0], q[1]))
circuit.append(cirq.H(q[0]))
circuit.append(cirq.measure(*q, key='result'))
print(circuit)
namespace QompileCircuit {
open Microsoft.Quantum.Canon;
open Microsoft.Quantum.Intrinsic;
operation Circuit() : Result[] {
use q = Qubit[2];
mutable c = [Zero, size = 2];
// Entangle
H(q[0]);
CNOT(q[0], q[1]);
// Alice encodes 11: X then Z on q0
X(q[0]);
Z(q[0]);
// Bob decodes
CNOT(q[0], q[1]);
H(q[0]);
set c w/= 0 <- M(q[0]);
set c w/= 1 <- M(q[1]);
ResetAll(q);
return c;
}
}
What you'll see¶
- Probabilities - after the full circuit, a single bar at 100% on the outcome matching the encoded message. Place a phase disk snapshot right after the encoding step instead, and you'll see both qubits still at 50/50 - the message is stored in which Bell state the pair is in, not in either qubit alone.
- Q-Sphere - after encoding, two points (an entangled superposition, with phase coloring showing the minus sign for messages
10and11); after decoding, a single point on one basis state. - Statevector - after decoding, a single amplitude of 1.0 on the message state, all others zero.