Superdense Coding

Superdense Coding#


Just like quantum teleportation, superdense coding is a quantum communication protocol that exploits the phenomenon of entanglement. In some sense, it can be thought as being complementary to teleportation. The protocol enables the transmission of two classical bits of information by encoding them into a single qubit. In this way, it is essentially the reverse of teleportation, where the state of one qubit is transmitted using two classical bits. The caveat, however, is that, just like in teleportation, superdense coding requires the sender and receiver to share an additional pair of entangled qubits, which must be distributed ahead of time.

Superdense coding is claimed to have been first proposed by Charles Bennett and Stephen Wiesner in 1970 (see handwritten note from Bennet), but the results were not formally published until 1992 [Bennett92].

1. Protocol Description#

Similar to what we described in the chapter for quantum teleportation, superdense coding relies on Alice and Bob first sharing an entangled state such as:

\[ |\Phi^+\rangle = \frac{1}{\sqrt{2}} \big(|0\rangle_A|0\rangle_B + |1\rangle_A|1\rangle_B \big),\]

where the subscripts \(A\) and \(B\) are used to denote Alice’s and Bob’s qubits, respectively. A way to accomplish this is by, for example, having Alice prepare \(|\Phi^+\rangle\) on a quantum computer, and then “sending” Bob one of the qubits via a quantum link.

After the entangled qubit is shared with Bob, Alice will encode two bits of classical information on her entangled qubit, and send that one to Bob as well. This means that their quantum link must be capable of transmitting that second qubit.

../../_images/03_03_01_superdense_transmit.png

However, unlike quantum teleportation, Alice and Bob do not need to establish a classical channel of information.

To understand the details of how this works, we can use a circuit diagram and analyze the evolution of the shared quantum state each step of the way:

../../_images/03_03_02_superdense_circuit.png
  1. Alice starts by initializing her qubits in the all-zeros state: \(|0 0 \rangle_A.\)

  2. She then prepares the \(|\Phi^+\rangle\) Bell state:

\[\begin{split} \begin{aligned} |\psi\rangle_1 &= |\Phi^+\rangle_A \\ \\ |\psi\rangle_1 &= \frac{1}{\sqrt{2}} \big(|0\rangle_A|0\rangle_A + |1\rangle_A|1\rangle_A \big). \end{aligned}\end{split}\]

Alice then proceeds to send one of the entangled qubits to Bob. Doing this doesn’t change the state itself; we just relabel the subscripts to indicate who has which qubit:

\[ |\psi\rangle_1 = \frac{1}{\sqrt{2}} \big(|0\rangle_A|0\rangle_B + |1\rangle_A|1\rangle_B \big). \]
  1. With the entangled pair of qubits in place, Alice can then encode two classical bits of information \((j,i),\) which can take values of \((0,0), (0,1), (1,0), (1,1)\). She does this by applying an \(X\) gate condition on the value of \(i,\) and a \(Z\) gate condition on the value of \(j.\) The overall system state is then given by:

\[\begin{split} \begin{aligned} |\psi\rangle_2 &= (Z^j \otimes I) (X^i \otimes I) |\psi\rangle_1 \\ \\ |\psi\rangle_2 &= \frac{1}{\sqrt{2}} \left[\big(Z^j\, X^i \big)|0 \rangle_A \otimes |0\rangle_B + \big(Z^j\, X^i \big)|1 \rangle_A \otimes |1\rangle_B \right]. \end{aligned} \end{split}\]

Granted that \(X^0 = Z^0 = I,\) we then have four possible states that depend on the value of the bits Alice encoded:

\[\begin{split} \begin{split} |\psi \rangle_2 = \begin{cases} \frac{1}{\sqrt{2}} \left[\big(I\, I \big)|0 \rangle_A \otimes |0\rangle_B + \big(I\, I \big)|1 \rangle_A \otimes |1\rangle_B \right] & \text{if } (j,i) = (0,0) \\ \\ \frac{1}{\sqrt{2}} \left[\big(I\, X \big)|0 \rangle_A \otimes |0\rangle_B + \big(I\, X \big)|1 \rangle_A \otimes |1\rangle_B \right] & \text{if } (j,i) = (0,1) \\ \\ \frac{1}{\sqrt{2}} \left[\big(Z\, I \big)|0 \rangle_A \otimes |0\rangle_B + \big(Z\, I \big)|1 \rangle_A \otimes |1\rangle_B \right] & \text{if } (j,i) = (1,0) \\ \\ \frac{1}{\sqrt{2}} \left[\big(Z\, X \big)|0 \rangle_A \otimes |0\rangle_B + \big(Z\, X \big)|1 \rangle_A \otimes |1\rangle_B \right] & \text{if } (j,i) = (1,1). \end{cases} \end{split} \end{split}\]

Applying the unitary operations to the qubit held by Alice then yields the following four possible states:

\[\begin{split} \begin{split} |\psi \rangle_2 = \begin{cases} \frac{1}{\sqrt{2}} \big(|0 \rangle_A \otimes |0\rangle_B + |1 \rangle_A \otimes |1\rangle_B \big) & \text{if } (j,i) = (0,0) \\ \\ \frac{1}{\sqrt{2}} \big(|1 \rangle_A \otimes |0\rangle_B + |0 \rangle_A \otimes |1\rangle_B \big) & \text{if } (j,i) = (0,1) \\ \\ \frac{1}{\sqrt{2}} \big(|0 \rangle_A \otimes |0\rangle_B - |1 \rangle_A \otimes |1\rangle_B \big)& \text{if } (j,i) = (1,0) \\ \\ \frac{-1}{\sqrt{2}} \big(|1 \rangle_A \otimes |0\rangle_B - |0 \rangle_A \otimes |1\rangle_B \big)& \text{if } (j,i) = (1,1). \end{cases} \end{split} \end{split}\]

As can be seen, these are the four Bell states we’ve discussed before:

\[\begin{split} \begin{split} |\psi \rangle_2 = \begin{cases} |\Phi^+\rangle & \text{if } (j,i) = (0,0) \\ \\ |\Psi^+\rangle & \text{if } (j,i) = (0,1) \\ \\ |\Phi^-\rangle & \text{if } (j,i) = (1,0) \\ \\ |\Psi^-\rangle & \text{if } (j,i) = (1,1). \end{cases} \end{split} \end{split}\]

Next, Alice proceeds to transmit to Bob her qubit, so now Bob has in his possession one of four possible two-qubit states shown above, which depends on the two-bit classical value Alice encoded on her qubit.

  1. Lastly, Bob applies a \(CX\) gate followed by an \(H\) gate, which converts the state from the Bell basis into the computational basis:

\[ |\psi\rangle_3 = (H \otimes I) \, CX |\psi\rangle_2, \]

resulting in the following four possible outputs:

\[\begin{split} \begin{split} |\psi \rangle_2 = \begin{cases} |00\rangle & \text{if } (j,i) = (0,0) \\ \\ |01\rangle & \text{if } (j,i) = (0,1) \\ \\ |10\rangle & \text{if } (j,i) = (1,0) \\ \\ |11\rangle & \text{if } (j,i) = (1,1). \end{cases} \end{split} \end{split}\]

If Bob then performs a measurement on his qubits, he will get with \(100 \%\) probability the exact same bits that Alice had encoded.

Let’s run this circuit in qiskit to verify that this is in fact the case.

import numpy as np
from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister, transpile
from qiskit_aer import AerSimulator
# Alice selects qubits to encode (picked at random for demo purposes)
alice_bits = np.random.randint(2,size=2)

qr = QuantumRegister(2,name="q")
cr = ClassicalRegister(2, name="Bob c")

qc = QuantumCircuit(qr,cr)

# Alice and Bob share entangled Bell state
qc.h(qr[1])
qc.cx(qr[1],qr[0])
qc.barrier()

# Alice encodes classical bits on her qubit
if alice_bits[1] == 1: qc.x(1)
if alice_bits[0] == 1: qc.z(1)
qc.barrier()

# Bob changes from Bell to Computational basis
qc.cx(qr[1],qr[0])
qc.h(qr[1])
qc.barrier()

# Bob measures all qubits
qc.measure(qr,cr)
display(qc.draw(cregbundle=False))
../../_images/8ffe880eac7142e264eba4e991c03a7a0de49c72dc64d65cae87fffbeb9bdf59.png
# run simulation
simulator = AerSimulator()
qc_t = transpile(qc, simulator)

bob_bits = simulator.run(qc_t, shots=1, memory=True).result().get_memory()[0]

print(f'Alice enconded bits ({alice_bits[1]},{alice_bits[0]})')
print(f'Bob recovered bits ({bob_bits[1]},{bob_bits[0]})')
Alice enconded bits (0,0)
Bob recovered bits (0,0)

2. Encoding More Bits#

Encoding more qubits is relatively simple. If Alice and Bob replicate the protocol described above but for \(n\) pairs of qubits rather than just \(1\) pair, Alice will be able to teleport \(2n\) additional classical bits.

The code below shows an example when Alice and Bob share \(n = 3\) pairs of qubits. This allows Alice to teleport a bitstring of length \(2n = 6\)

n = 3 # Number of entangled pairs

# Alice selects qubits to encode (picked at random for demo purposes)
alice_bit_pairs = [np.random.randint(2,size=2) for _ in range(n)]

qr_entangle = QuantumRegister(n,name="entangle")
qr_encode = QuantumRegister(n,name="encode")
cr = ClassicalRegister(2*n, name="Bob meas")

qc = QuantumCircuit(qr_entangle,qr_encode,cr)

# Alice and Bob share entangled Bell state
qc.h(qr_encode)
qc.cx(qr_encode,qr_entangle)
qc.barrier()

# Alice encodes classical bits on her qubit
for qbit, bit_pair in enumerate(reversed(alice_bit_pairs)):
    if bit_pair[1] == 1: qc.x(qr_encode[qbit])
    if bit_pair[0] == 1: qc.z(qr_encode[qbit])

qc.barrier()

# Bob changes from Bell to Computational basis
qc.cx(qr_encode,qr_entangle)
qc.h(qr_encode)
qc.barrier()

# Bob measures all qubits
for qbit in range(n):
    qc.measure(qr_entangle[qbit],cr[2*qbit])
    qc.measure(qr_encode[qbit],cr[2*qbit+1])

display(qc.draw(cregbundle=False))
../../_images/129e09b206e896ef7c4d86806fb362aa82fbae3fffbfa24a0b12c6fe1524c105.png
# run simulation
simulator = AerSimulator()
qc_t = transpile(qc, simulator)

alice_bits = "".join(np.concatenate(alice_bit_pairs).astype(str))
bob_bits = simulator.run(qc_t, shots=1, memory=True).result().get_memory()[0]

print(f'Alice encoded bits {alice_bits}')
print(f'Bob recovered bits {bob_bits}')
Alice encoded bits 101010
Bob recovered bits 101010