Quantum Teleportation

This notebook demonstrates quantum teleportation. We first use Qiskit's built-in simulator to test our quantum circuit, and then try it out on a real quantum computer.


Abraham Asfaw

The concept

This story will involve three individuals. Let's call them Abraham, Alice and Bob.

1: Abraham generates a quantum state $|\psi\rangle = \alpha|0\rangle + \beta|1\rangle$.

2: Abraham "gives" this quantum state to Alice - effectively, what this means is that Alice can now act on this quantum state. However, Abraham gives the state to Alice with the caveat that she doesn't know the coefficients $\alpha$ and $\beta$.

3: Alice now wants to pass on this quantum state to Bob, who is very far away. Alice and Bob can talk over the phone.

So how does Alice pass on the information to Bob without knowing the coefficients? She uses quantum teleportation.

How does quantum teleportation work?

1: Before they parted ways, Alice and Bob created an entangled Bell pair. Each one of them held on to one of the two qubits in the Bell pair when they parted ways.

In quantum circuit language, the way to create a Bell pair between two qubits is to first transfer one of them to the Bell basis ($|+\rangle$ and $|-\rangle$) by using a Hadamard gate, and then to apply a CNOT gate onto the other qubit controlled by the one in the Bell basis. Let's say Alice owns $q_1$ and Bob owns $q_2$ after they part ways.

2: Once Abraham gives Alice control of $|\psi\rangle$, Alice applies a CNOT gate on $q_1$, controlled by $|\psi\rangle$.

3: Next, Alice applies a Hadamard gate to $|\psi\rangle$, and applies a measurement to both qubits that she owns.

4: Then, it's time for a phone call to Bob. She tells Bob the outcome of her two qubit measurement. Depending on what she says, Bob does this to his qubit, $q_2$:

00 $\rightarrow$ Do nothing

01 $\rightarrow$ Apply $X$ gate

10 $\rightarrow$ Apply $Z$ gate

11 $\rightarrow$ Apply $ZX$ gate

And voila! At the end of this protocol, the qubit that Abraham gave Alice has now teleported to Bob. You are encouraged to write out the states that result from this protocol and verify that it works.

How will we test this result on a real quantum computer?

In this notebook, we will pretend to be Abraham by giving Alice a secret state $|\psi\rangle$. This state will be generated by applying a series of unitary gates on a qubit that is initialized to the ground state, $|0\rangle$. Go ahead and fill in the secret unitary that will be applied to $|0\rangle$ before passing on the qubit to Alice.

In [8]:
secret_unitary = 'hzxyt'

If the quantum teleportation circuit works, then at the output of the protocol discussed above will be the same state passed on to Alice by Abraham. Then, we can undo the applied secret_unitary (by applying its conjugate transpose), to yield the $|0\rangle$ that we started with.

We will then do repeated measurements of Bob's qubit to see how many times it gives 0 and how many times it gives 1.

What do we expect?

In the ideal case, and assuming our teleportation protocol works, we will always measure 0 from Bob's qubit because we started off with $|0\rangle$.

In a real quantum computer, errors in the gates will cause a small fraction of the results to be 1. We'll see how it looks.

1. Simulating the teleportation protocol

In [9]:
# make the imports that are necessary for our work
import qiskit as qk
from qiskit import ClassicalRegister, QuantumRegister, QuantumCircuit
from qiskit import execute, Aer
from qiskit import IBMQ
from qiskit.backends.ibmq import least_busy
from qiskit.wrapper.jupyter import *
from qiskit.tools.visualization import plot_histogram, circuit_drawer
In [10]:
# simple function that applies a series of unitary gates from a given string
def apply_secret_unitary(secret_unitary, qubit, quantum_circuit, dagger):
    functionmap = {
    if dagger: functionmap['t'] = quantum_circuit.tdg
    if dagger:
        [functionmap[unitary](qubit) for unitary in secret_unitary]
        [functionmap[unitary](qubit) for unitary in secret_unitary[::-1]]
In [11]:
# Create the quantum circuit
q = QuantumRegister(3)
c = ClassicalRegister(3)
qc = QuantumCircuit(q, c)

''' Qubit ordering as follows (classical registers will just contain measured values of the corresponding qubits):
q[0]: qubit to be teleported (Alice's first qubit. It was given to her after the application of a secret unitary 
      which she doesn't know)
q[1]: Alice's second qubit
q[2]: Bob's qubit, which will be the destination for the teleportation

# Apply the secret unitary that we are using to generate the state to teleport. You can change it to any unitary
apply_secret_unitary(secret_unitary, q[0], qc, dagger = 0)
# Next, generate the entangled pair between Alice and Bob (Remember: Hadamard followed by CX generates a Bell pair)
qc.cx(q[1], q[2])
# Next, apply the teleportation protocol. 
qc.cx(q[0], q[1])
qc.measure(q[0], c[0])
qc.measure(q[1], c[1])
qc.cx(q[1], q[2])
qc.cz(q[0], q[2])

In principle, if the teleportation protocol worked, we have q[2] = secret_unitary|0>
As a result, we should be able to recover q[2] = |0> by applying the reverse of secret_unitary
since for a unitary u, u^dagger u = I.
apply_secret_unitary(secret_unitary, q[2], qc, dagger=1)
qc.measure(q[2], c[2])

# Draw the circuit that was generated

backend = Aer.get_backend('qasm_simulator')
job_sim = execute(qc, backend, shots=1024)
sim_result = job_sim.result()

measurement_result = sim_result.get_counts(qc)
WARNING: Unable to convert pdf to image. Is `poppler` installed? Skipping circuit drawing...
{'000': 245, '001': 259, '010': 273, '011': 247}

Note that the results on the x-axis in the histogram above are ordered as $c_2c_1c_0$. We can see that only results where $c_2 = 0$ appear, indicating that the teleporation protocol has worked.

2. Teleportation on a real quantum computer

In [12]:
# First, get permissions to use the quantum computer at IBM
import qconfig
('Credentials are already in use.')
[<IBMQBackend('ibmqx4') from IBMQ()>,
 <IBMQBackend('ibmqx5') from IBMQ()>,
 <IBMQBackend('ibmqx2') from IBMQ()>,
 <IBMQBackend('ibmq_16_melbourne') from IBMQ()>,
 <IBMQBackend('ibmq_qasm_simulator') from IBMQ()>]
In [13]:
# get the least-busy backend at IBM and run the quantum circuit there
backend = least_busy(IBMQ.backends(simulator=False))
job_exp = execute(qc, backend=backend, shots=8192)
exp_result = job_exp.result()

exp_measurement_result = exp_result.get_counts(qc)
{'000': 1540, '010': 2009, '001': 1019, '011': 1686, '100': 661, '110': 333, '101': 685, '111': 259}

As we see here, there are a few results that contain the case when $c_2 = 1$ in a real quantum computer. These arise due to errors in the gates that were applied. Another source of error is the way we're checking for teleportation - we need the series of operators on $q_2$ to be exactly the inverse unitary of those that we applied to $q_0$ at the beginning.

In contrast, our simulator in the earlier part of the notebook had zero errors in its gates, and allowed error-free teleportation.

In [14]:
error_rate_percent = sum([exp_measurement_result[result] for result in exp_measurement_result.keys() if result[0]=='1']) \
                    * 100./ sum(list(exp_measurement_result.values()))
In [ ]: