Search
Multiple Qubits and Entangled States

Single qubits are interesting, but individually they offer no computational advantage. We will now look at how we represent multiple qubits, and how these qubits can interact with each other. We have seen how we can represent the state of a qubit using a 2D-vector, now we will see how we can represent the state of multiple qubits.

Contents

  1. Representing Multi-Qubit States
    1.1 Exercises
  2. Single Qubit Gates on Multi-Qubit Statevectors
    2.1 Exercises
  3. Multi-Qubit Gates
    3.1 The CNOT-gate
    3.2 Entangled States
    3.3 Exercises

1. Representing Multi-Qubit States

We saw that a single bit has two possible states, and a qubit state has two complex amplitudes. Similarly, two bits have four possible states:

00 01 10 11

And to describe the state of two qubits requires four complex amplitudes. We store these amplitudes in a 4D-vector like so:

$$ |a\rangle = a_{00}|00\rangle + a_{01}|01\rangle + a_{10}|10\rangle + a_{11}|11\rangle = \begin{bmatrix} a_{00} \\ a_{01} \\ a_{10} \\ a_{11} \end{bmatrix} $$

The rules of measurement still work in the same way:

$$ p(|00\rangle) = |\langle 00 | a \rangle |^2 = |a_{00}|^2$$

And the same implications hold, such as the normalisation condition:

$$ |a_{00}|^2 + |a_{01}|^2 + |a_{10}|^2 + |a_{11}|^2 = 1$$

If we have two separated qubits, we can describe their collective state using the tensor product:

$$ |a\rangle = \begin{bmatrix} a_0 \\ a_1 \end{bmatrix}, \quad |b\rangle = \begin{bmatrix} b_0 \\ b_1 \end{bmatrix} $$$$ |ab\rangle = |a\rangle \otimes |b\rangle = \begin{bmatrix} a_0 \times \begin{bmatrix} b_0 \\ b_1 \end{bmatrix} \\ a_1 \times \begin{bmatrix} b_0 \\ b_1 \end{bmatrix} \end{bmatrix} = \begin{bmatrix} a_0 b_0 \\ a_0 b_1 \\ a_1 b_0 \\ a_1 b_1 \end{bmatrix} $$

And following the same rules, we can use the tensor product to describe the collective state of any number of qubits. Here is an example with three qubits:

$$ |abc\rangle = \begin{bmatrix} a_0 b_0 c_0 \\ a_0 b_0 c_1 \\ a_0 b_1 c_0 \\ a_0 b_1 c_1 \\ a_1 b_0 c_0 \\ a_1 b_0 c_1 \\ a_1 b_1 c_0 \\ a_1 b_1 c_1 \\ \end{bmatrix} $$

If we have $n$ qubits, we will need to keep track of $2^n$ complex amplitudes. As we can see, these vectors grow exponentially with the number of qubits. This is the reason quantum computers with large numbers of qubits are so difficult to simulate. A modern laptop can easily simulate around 20 qubits, but 100 qubits is too difficult for the largest supercomputers.

Let's look at an example circuit:

from qiskit import *
from math import pi
import numpy as np
from qiskit.visualization import plot_bloch_multivector, plot_histogram
# For Jupyter Notebooks:
%config InlineBackend.figure_format = 'svg' # Makes the images look nice
qc = QuantumCircuit(3)
# Apply H-gate to each qubit:
for qubit in range(3):
    qc.h(qubit)
# See the circuit:
qc.draw('mpl')

Each qubit is in the state $|+\rangle$, so we should see the vector:

$$ |{+++}\rangle = \frac{1}{\sqrt{8}}\begin{bmatrix} 1 \\ 1 \\ 1 \\ 1 \\ 1 \\ 1 \\ 1 \\ 1 \\ \end{bmatrix} $$
# Let's see the result
backend = Aer.get_backend('statevector_simulator')
final_state = execute(qc,backend).result().get_statevector()

# In Jupyter Notebooks we can display this nicely using Latex.
# If not using Jupyter Notebooks you may need to remove the 
# vector2latex function and use print(final_state) instead.
from qiskit_textbook.tools import vector2latex
vector2latex(final_state, pretext="\\text{Statevector} = ")
$$ \text{Statevector} = \begin{bmatrix} 0.35355 \\ 0.35355 \\ 0.35355 \\ 0.35355 \\ 0.35355 \\ 0.35355 \\ 0.35355 \\ 0.35355\end{bmatrix} $$

And we have our expected result.

1.1 Quick Exercises:

  1. Write down the tensor product of the qubits:
    a) $|0\rangle|1\rangle$
    b) $|0\rangle|+\rangle$
    c) $|+\rangle|1\rangle$
    d) $|-\rangle|+\rangle$
  2. Write the state: $|\psi\rangle = \tfrac{1}{\sqrt{2}}|00\rangle + \tfrac{i}{\sqrt{2}}|01\rangle $ as two separate qubits.

2. Single Qubit Gates on Multi-Qubit Statevectors

We have seen that an X-gate is represented by the matrix:

$$ X = \begin{bmatrix} 0 & 1 \\ 1 & 0 \end{bmatrix} $$

And that it acts on the state $|0\rangle$ as so:

$$ X|0\rangle = \begin{bmatrix} 0 & 1 \\ 1 & 0 \end{bmatrix}\begin{bmatrix} 1 \\ 0 \end{bmatrix} = \begin{bmatrix} 0 \\ 1\end{bmatrix} $$

but it may not be clear how an X-gate would act on a qubit in a multi-qubit vector. Fortunately, the rule is quite simple; just as we used the tensor product to calculate multi-qubit statevectors, we use the tensor product to calculate matrices that act on these statevectors. For example, in the circuit below:

qc = QuantumCircuit(2)
qc.h(0)
qc.x(1)
qc.draw('mpl')

we can represent the simultaneous operations (H & X) using their tensor product:

$$ X|q_1\rangle \otimes H|q_0\rangle = (X\otimes H)|q_1 q_0\rangle $$

The operation looks like this:

$$ X\otimes H = \begin{bmatrix} 0 & 1 \\ 1 & 0 \end{bmatrix} \otimes \tfrac{1}{\sqrt{2}}\begin{bmatrix} 1 & 1 \\ 1 & -1 \end{bmatrix} = \frac{1}{\sqrt{2}} \begin{bmatrix} 0 \times \begin{bmatrix} 1 & 1 \\ 1 & -1 \end{bmatrix} & 1 \times \begin{bmatrix} 1 & 1 \\ 1 & -1 \end{bmatrix} \\ 1 \times \begin{bmatrix} 1 & 1 \\ 1 & -1 \end{bmatrix} & 0 \times \begin{bmatrix} 1 & 1 \\ 1 & -1 \end{bmatrix} \end{bmatrix} = \frac{1}{\sqrt{2}} \begin{bmatrix} 0 & 0 & 1 & 1 \\ 0 & 0 & 1 & -1 \\ 1 & 1 & 0 & 0 \\ 1 & -1 & 0 & 0 \\ \end{bmatrix} $$

Which we can then apply to our 4D statevector $|q_1 q_0\rangle$. This can become quite messy, you will often see the clearer notation:

$$ X\otimes H = \begin{bmatrix} 0 & H \\ H & 0\\ \end{bmatrix} $$

Instead of calculating this by hand, we can use Qiskit’s unitary_simulator to calculate this for us. The unitary simulator multiplies all the gates in our circuit together to compile a single unitary matrix that performs the whole quantum circuit:

backend = Aer.get_backend('unitary_simulator')
unitary = execute(qc,backend).result().get_unitary()

and view the results:

# In Jupyter Notebooks we can display this nicely using Latex.
# If not using Jupyter Notebooks you may need to remove the 
# unitary2latex function and use print(unitary) instead.
from qiskit_textbook.tools import unitary2latex
unitary2latex(unitary, pretext="\\text{Circuit = }\n")
$$ \text{Circuit = } \begin{bmatrix} 0 & 0 & 0.70711 & 0.70711 \\ 0 & 0 & 0.70711 & -0.70711 \\ 0.70711 & 0.70711 & 0 & 0 \\ 0.70711 & -0.70711 & 0 & 0 \\ \end{bmatrix} $$

If we want to apply a gate to only one qubit at a time, we must fill in the space on the other qubits with the identity gate:

qc = QuantumCircuit(2)
qc.x(1)
qc.draw('mpl')
# Simulate the unitary
backend = Aer.get_backend('unitary_simulator')
unitary = execute(qc,backend).result().get_unitary()
# Display the results:
unitary2latex(unitary, pretext="\\text{Circuit = } ")
$$ \text{Circuit = } \begin{bmatrix} 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \\ 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ \end{bmatrix} $$

We can see Qiskit has performed the tensor product: $$ X \otimes I = \begin{bmatrix} 0 & I \\ I & 0\\ \end{bmatrix} = \begin{bmatrix} 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \\ 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ \end{bmatrix} $$

2.1 Quick Exercises:

  1. Calculate the single qubit unitary ($U$) created by the squence of gates: $U = XZH$. Use Qiskit's unitary simulator to check your results.
  2. Try changing the gates in the circuit above. Calculate their tensor product, and then check your answer using the unitary simulator.

Note: Different books, softwares and websites order their qubits differently. This means the tensor product of the same circuit can look very different and this can result in headaches. Try to bear this in mind when consulting other sources.

3. Multi-Qubit Gates

Now we know how to represent the state of multiple qubits, we are now ready to learn how qubits interact with each other. The most popular two-qubit gate is the CNOT-gate.

3.1 The CNOT-Gate

You have come across this gate before in The Atoms of Computation. This gate is a conditional gate that performs an X-gate on the second qubit (target), if the state of the first qubit (control) is $|1\rangle$. The gate is drawn on a circuit like this, with q0 as the control and q1 as the target:

qc = QuantumCircuit(2)
# Apply CNOT
qc.cx(0,1)
# See the circuit:
qc.draw('mpl')

When our qubits are not in superposition of $|0\rangle$ or $|1\rangle$, this gate is very simple and intuitive to understand. Acting on our 4D-statevector, it has one of the two matrices:

$$ \text{CNOT} = \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 0 & 0 & 1 \\ 0 & 0 & 1 & 0 \\ 0 & 1 & 0 & 0 \\ \end{bmatrix}, \quad \text{CNOT} = \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 0 & 1 \\ 0 & 0 & 1 & 0 \\ \end{bmatrix} $$

depending on which qubit is the control and which is the target. Different books, simulators and papers order their qubits differently, this can prove very confusing when starting out. In our case, the left matrix corresponds to the CNOT in the circuit above. This matrix swaps the amplitudes of $|01\rangle$ and $|11\rangle$ in our statevector:

$$ |a\rangle = \begin{bmatrix} a_{00} \\ a_{01} \\ a_{10} \\ a_{11} \end{bmatrix}, \quad \text{CNOT}|a\rangle = \begin{bmatrix} a_{00} \\ a_{11} \\ a_{10} \\ a_{01} \end{bmatrix} \begin{matrix} \\ \leftarrow \\ \\ \leftarrow \end{matrix} $$

We have seen how this acts on classical states, but let’s now see how it acts on a qubit in superposition. We will put one qubit in the state $|+\rangle$:

qc = QuantumCircuit(2)
# Apply H-gate to the first:
qc.h(0)
qc.draw('mpl')
# Let's see the result:
backend = Aer.get_backend('statevector_simulator')
final_state = execute(qc,backend).result().get_statevector()
# Print the statevector neatly:
vector2latex(final_state, pretext="\\text{Statevector = }")
$$ \text{Statevector = }\begin{bmatrix} 0.70711 \\ 0.70711 \\ 0 \\ 0\end{bmatrix} $$

As expected, this produces the state $|0\rangle \otimes |{+}\rangle = |0{+}\rangle$:

$$ |0{+}\rangle = \tfrac{1}{\sqrt{2}}(|00\rangle + |01\rangle) $$

And let’s see what happens when we apply the CNOT gate:

qc = QuantumCircuit(2)
# Apply H-gate to the first:
qc.h(0)
# Apply a CNOT:
qc.cx(0,1)
qc.draw('mpl')
# Let's see the result:
backend = Aer.get_backend('statevector_simulator')
final_state = execute(qc,backend).result().get_statevector()
# Print the statevector neatly:
vector2latex(final_state, pretext="\\text{Statevector = }")
$$ \text{Statevector = }\begin{bmatrix} 0.70711 \\ 0 \\ 0 \\ 0.70711\end{bmatrix} $$

We see we have the state:

$$ \text{CNOT}|0{+}\rangle = \tfrac{1}{\sqrt{2}}(|00\rangle + |11\rangle) $$

This state is very interesting to us, because it is entangled. This leads us neatly on to the next section.

3.2 Entangled States

You have probably heard of entanglement before, there are many cursory explanations throughout the internet, aimed at audiences without a mathematical background. Fortunately, we now have everything we need to cover entanglement properly, and hopefully avoid some of the misconceptions surrounding it.

We saw in the previous section we could create the state:

$$ \tfrac{1}{\sqrt{2}}(|00\rangle + |11\rangle) $$

This is known as a Bell state. We can see that this state has 50% probability of being measured in the state $|00\rangle$, and 50% chance of being measured in the state $|11\rangle$. Most interestingly, it has a 0% chance of being measured in the states $|01\rangle$ or $|10\rangle$. We can see this in Qiskit:

results = execute(qc,backend).result().get_counts()
plot_histogram(results)

This has interesting implications. Although our qubits are in superposition, measuring one will tell us the state of the other and collapse its superposition. For example, if we measured the top qubit and got the state $|1\rangle$, the collective state of our qubits changes:

$$ \tfrac{1}{\sqrt{2}}(|00\rangle + |11\rangle) \quad \xrightarrow[]{\text{measure}} \quad |11\rangle $$

This is the apparent ‘spooky action at a distance’ that upset so many physicists in the early 20th century. Even if we separated these qubits light-years away, it appears as if measuring one qubit has an effect on the other, with faster-than-light communication between them.

This is one interpretation of the effects of entanglement, you may have heard of others such as the 'many worlds' interpretation, but these interpretations are untestable and thus stray from physics into philosophy. There is however an important thing to note:

Since the measurement result is random, we can not use entangled states to communicate in this way.

(This is the result of a theorem known as the no-communication theorem). There is a common misconception that we could use entangled states to communicate faster than the speed of light, we can see from the above statement that this is not true.

3.3 Exercise:

  1. Create a quantum circuit that produces the Bell state: $\tfrac{1}{\sqrt{2}}(|01\rangle + |10\rangle)$. Use the statevector simulator to verify your result.
  1. The circuit you created in question 1 transforms the state $|00\rangle$ to $\tfrac{1}{\sqrt{2}}(|01\rangle + |10\rangle)$, calculate the unitary of this circuit using Qiskit's simulator. Verify this unitary does in fact perform the correct transformation.
import qiskit
qiskit.__qiskit_version__
{'qiskit-terra': '0.12.0',
 'qiskit-aer': '0.4.0',
 'qiskit-ignis': '0.2.0',
 'qiskit-ibmq-provider': '0.4.6',
 'qiskit-aqua': '0.6.4',
 'qiskit': '0.15.0'}