The latest version of this notebook is available on https://github.com/qiskit/qiskit-tutorial.
Jay Gambetta, Antonio Córcoles, Anna Phan
In creating entanglement, we introduced you to the quantum concept of entanglement. We made the quantum state $|\psi\rangle= (|00\rangle+|11\rangle)/\sqrt{2}$ and showed that (accounting for experimental noise) the system has perfect correlations in both the computational and superposition bases. This means if $q_0$ is measured in state $|0\rangle$, we know $q_1$ is in the same state; likewise, if $q_0$ is measured in state $|+\rangle$, we know $q_1$ is also in the same state.
To understand the implications of this in more detail, we will look at the following topics in this notebook:
An observable is a Hermitian matrix where the real eigenvalues represent the outcome of the experiment, and the eigenvectors are the states to which the system is projected under measurement. That is, an observable $A$ is given by
$$ A = \sum_j a_j|a_j\rangle\langle a_j|$$where $|a_j\rangle$ is the eigenvector of the observable with result $a_j$. The expectation value of this observable is given by
$$\langle A \rangle = \sum_j a_j |\langle \psi |a_j\rangle|^2 = \sum_j a_j \mathrm{Pr}(a_j|\psi).$$We can see there is the standard relationship between average (expectation value) and probability.
For a two-qubit system, the following are important two-outcome ($\pm1$) single-qubit observables:
$$ Z= |0\rangle\langle 0| - |1\rangle\langle 1|$$$$ X= |+\rangle\langle +| - |-\rangle\langle -|$$
These are also commonly referred to as the Pauli $Z$ and $X$ operators. These can be further extended to the two-qubit space to give
$$\langle I\otimes Z\rangle =\mathrm{Pr}(00|\psi) - \mathrm{Pr}(01|\psi) + \mathrm{Pr}(10|\psi)- \mathrm{Pr}(11|\psi)$$$$\langle Z\otimes I\rangle =\mathrm{Pr}(00|\psi) + \mathrm{Pr}(01|\psi) - \mathrm{Pr}(10|\psi)- \mathrm{Pr}(11|\psi)$$ $$\langle Z\otimes Z\rangle =\mathrm{Pr}(00|\psi) - \mathrm{Pr}(01|\psi) - \mathrm{Pr}(10|\psi)+ \mathrm{Pr}(11|\psi)$$
$$\langle I\otimes X\rangle =\mathrm{Pr}(++|\psi) - \mathrm{Pr}(+-|\psi) + \mathrm{Pr}(-+|\psi)- \mathrm{Pr}(--|\psi)$$$$\langle X\otimes I\rangle =\mathrm{Pr}(++|\psi) + \mathrm{Pr}(+-|\psi) - \mathrm{Pr}(-+|\psi)- \mathrm{Pr}(--|\psi)$$ $$\langle X\otimes X\rangle =\mathrm{Pr}(++|\psi) - \mathrm{Pr}(+-|\psi) - \mathrm{Pr}(-+|\psi)+ \mathrm{Pr}(--|\psi)$$
$$\langle Z\otimes X\rangle =\mathrm{Pr}(0+|\psi) - \mathrm{Pr}(0-|\psi) - \mathrm{Pr}(1+|\psi)+ \mathrm{Pr}(1-|\psi)$$$$\langle X\otimes Z\rangle =\mathrm{Pr}(+0|\psi) - \mathrm{Pr}(+1|\psi) - \mathrm{Pr}(-0|\psi)+ \mathrm{Pr}(-1|\psi)$$
In [1]:
# Imports
import matplotlib.pyplot as plt
%matplotlib inline
import numpy as np
from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister, execute
from qiskit.tools.visualization import matplotlib_circuit_drawer as circuit_drawer
from qiskit.tools.visualization import plot_histogram, qx_color_scheme
from qiskit.wrapper.jupyter import *
In [2]:
from qiskit import IBMQ, Aer
from qiskit.backends.ibmq import least_busy
IBMQ.load_accounts()
# use simulator to learn more about entangled quantum states where possible
sim_backend = Aer.get_backend('qasm_simulator')
sim_shots = 8192
# use device to test entanglement
device_shots = 1024
device_backend = least_busy(IBMQ.backends(operational=True, simulator=False))
device_coupling = device_backend.configuration()['coupling_map']
print("the best backend is " + device_backend.name() + " with coupling " + str(device_coupling))
Recall that to make the Bell state $|\psi\rangle= (|00\rangle+|11\rangle)/\sqrt{2}$ from the initial state $|00\rangle$, the quantum circuit first applies a Hadamard on $q_0$, followed by a CNOT from $q_0$ to $q_1$. Using Qiskit, this can done by using the script below to measure the above expectation values; we run four different experiments with measurements in the standard basis, superposition basis, and a combination of both.
In [3]:
# Creating registers
q = QuantumRegister(2)
c = ClassicalRegister(2)
# quantum circuit to make an entangled bell state
bell = QuantumCircuit(q, c)
bell.h(q[0])
bell.cx(q[0], q[1])
# quantum circuit to measure q in the standard basis
measureZZ = QuantumCircuit(q, c)
measureZZ.measure(q[0], c[0])
measureZZ.measure(q[1], c[1])
bellZZ = bell+measureZZ
# quantum circuit to measure q in the superposition basis
measureXX = QuantumCircuit(q, c)
measureXX.h(q[0])
measureXX.h(q[1])
measureXX.measure(q[0], c[0])
measureXX.measure(q[1], c[1])
bellXX = bell+measureXX
# quantum circuit to measure ZX
measureZX = QuantumCircuit(q, c)
measureZX.h(q[0])
measureZX.measure(q[0], c[0])
measureZX.measure(q[1], c[1])
bellZX = bell+measureZX
# quantum circuit to measure XZ
measureXZ = QuantumCircuit(q, c)
measureXZ.h(q[1])
measureXZ.measure(q[0], c[0])
measureXZ.measure(q[1], c[1])
bellXZ = bell+measureXZ
circuits = [bellZZ,bellXX,bellZX,bellXZ]
In [4]:
circuit_drawer(bellZZ,style=qx_color_scheme())
In [5]:
circuit_drawer(bellXX,style=qx_color_scheme())
In [6]:
circuit_drawer(bellZX,style=qx_color_scheme())
In [7]:
circuit_drawer(bellXZ,style=qx_color_scheme())
In [8]:
%%qiskit_job_status
HTMLProgressBar()
job = execute(circuits, backend=device_backend, coupling_map=device_coupling, shots=device_shots)
In [9]:
result = job.result()
In [10]:
observable_first ={'00': 1, '01': -1, '10': 1, '11': -1}
observable_second ={'00': 1, '01': 1, '10': -1, '11': -1}
observable_correlated ={'00': 1, '01': -1, '10': -1, '11': 1}
In [11]:
print('IZ = ' + str(result.average_data(bellZZ,observable_first)))
print('ZI = ' + str(result.average_data(bellZZ,observable_second)))
print('ZZ = ' + str(result.average_data(bellZZ,observable_correlated)))
print('IX = ' + str(result.average_data(bellXX,observable_first)))
print('XI = ' + str(result.average_data(bellXX,observable_second)))
print('XX = ' + str(result.average_data(bellXX,observable_correlated)))
print('ZX = ' + str(result.average_data(bellZX,observable_correlated)))
print('XZ = ' + str(result.average_data(bellXZ,observable_correlated)))
Here we see that for the state $|\psi\rangle= (|00\rangle+|11\rangle)/\sqrt{2}$, expectation values (within experimental errors) are
Observable | Expected value | Observable | Expected value | Observable | Expected value |
---|---|---|---|---|---|
ZZ | 1 | XX | 1 | ZX | 0 |
ZI | 0 | XI | 0 | XZ | 0 |
IZ | 0 | IX | 0 |
How do we explain this situation? Here we introduce the concept of a hidden variable model. If we assume there is a hidden variable $\lambda$ and follow these two assumptions:
Locality: No information can travel faster than the speed of light. There is a hidden variable $\lambda$ that defines all the correlations so that $$\langle A\otimes B\rangle = \sum_\lambda P(\lambda) A(\lambda) B(\lambda).$$
Realism: All observables have a definite value independent of the measurement ($A(\lambda)=\pm1$ etc.).
then can we describe these observations? --- The answer is yes!
Assume $\lambda$ has two bits, each occurring randomly with probability 1/4. The following predefined table would then explain all the above observables:
$\lambda$ | Z (qubit 1) | Z (qubit 2) | X (qubit 1) | X (qubit 2) |
---|---|---|---|---|
00 | 1 | 1 | 1 | 1 |
01 | 1 | 1 | -1 | -1 |
10 | -1 | -1 | -1 | -1 |
11 | -1 | -1 | 1 | 1 |
Thus, with a purely classical hidden variable model, we are able to reconcile the measured observations we had for this particular Bell state. However, there are some states for which this model will not hold. This was first observed by John Stewart Bell in 1964. He proposed a theorem that suggests that there are no hidden variables in quantum mechanics. At the core of Bell's theorem is the famous Bell inequality. Here we'll use a refined version of this inequality (known as the CHSH inequality, derived by John Clauser, Michael Horne, Abner Shimony, and Richard Holt in 1969) to demonstrate Bell's proposal.
In the CHSH inequality, we measure the correlator of four observables: $A$ and $A'$ on $q_0$, and $B$ and $B'$ on $q_1$, which have eigenvalues $\pm 1$. The CHSH inequality says that no local hidden variable theory can have
$$|C|>2$$where
$$C = \langle B\otimes A\rangle + \langle B\otimes A'\rangle+\langle B'\otimes A'\rangle-\langle B'\otimes A\rangle.$$What would this look like with some hidden variable model under the locality and realism assumptions from above? $C$ then becomes
$$C = \sum_\lambda P(\lambda) \{ B(\lambda) [ A(\lambda)+A'(\lambda)] + B'(\lambda) [ A'(\lambda)-A(\lambda)]$$and $[A(\lambda)+A'(\lambda)]=2$ (or 0) while $[A'(\lambda)-A(\lambda)]=0$ (or 2) respectively. That is, $|C|=2$, and noise will only make this smaller.
If we measure a number greater than 2, the above assumptions cannot be valid. (This is a perfect example of one of those astonishing counterintuitive ideas one must accept in the quantum world.) For simplicity, we choose these observables to be
$$C = \langle Z\otimes Z\rangle + \langle Z\otimes X\rangle+\langle X\otimes X\rangle-\langle X\otimes Z\rangle.$$
$Z$ is measured in the computational basis, and $X$ in the superposition basis ($H$ is applied before measurement). The input state $$|\psi(\theta)\rangle = I\otimes Y(\theta)\frac{|00\rangle + |11\rangle}{\sqrt(2)} = \frac{\cos(\theta/2)|00\rangle + \cos(\theta/2)|11\rangle+\sin(\theta/2)|01\rangle-\sin(\theta/2)|10\rangle}{\sqrt{2}}$$ is swept vs. $\theta$ (think of this as allowing us to prepare a set of states varying in the angle $\theta$).
Note that the following demonstration of CHSH is not loophole-free.
In [12]:
CHSH = lambda x : x[0]+x[1]+x[2]-x[3]
measure = [measureZZ, measureZX, measureXX, measureXZ]
In [13]:
# Theory
sim_chsh_circuits = []
sim_x = []
sim_steps = 30
for step in range(sim_steps):
theta = 2.0*np.pi*step/30
bell_middle = QuantumCircuit(q,c)
bell_middle.ry(theta,q[0])
for m in measure:
sim_chsh_circuits.append(bell+bell_middle+m)
sim_x.append(theta)
In [14]:
job = execute(sim_chsh_circuits, backend=sim_backend, shots=sim_shots)
result = job.result()
In [15]:
sim_chsh = []
circ = 0
for x in range(len(sim_x)):
temp_chsh = []
for m in range(len(measure)):
temp_chsh.append(result.average_data(sim_chsh_circuits[circ].name,observable_correlated))
circ += 1
sim_chsh.append(CHSH(temp_chsh))
In [16]:
# Experiment
real_chsh_circuits = []
real_x = []
real_steps = 10
for step in range(real_steps):
theta = 2.0*np.pi*step/10
bell_middle = QuantumCircuit(q,c)
bell_middle.ry(theta,q[0])
for m in measure:
real_chsh_circuits.append(bell+bell_middle+m)
real_x.append(theta)
In [17]:
%%qiskit_job_status
HTMLProgressBar()
job = execute(real_chsh_circuits, backend=device_backend, coupling_map=device_coupling, shots=device_shots)
In [18]:
result = job.result()
In [19]:
real_chsh = []
circ = 0
for x in range(len(real_x)):
temp_chsh = []
for m in range(len(measure)):
temp_chsh.append(result.average_data(real_chsh_circuits[circ].name,observable_correlated))
circ += 1
real_chsh.append(CHSH(temp_chsh))
In [20]:
plt.plot(sim_x, sim_chsh, 'r-', real_x, real_chsh, 'bo')
plt.plot([0, 2*np.pi], [2, 2], 'b-')
plt.plot([0, 2*np.pi], [-2, -2], 'b-')
plt.grid()
plt.ylabel('CHSH', fontsize=20)
plt.xlabel(r'$Y(\theta)$', fontsize=20)
plt.show()
The resulting graph created by running the previous cell compares the simulated data (sinusoidal line) and the data from the real experiment. The graph also gives lines at $\pm 2$ for reference. Did you violate the hidden variable model?
Here is the saved CHSH data.
In [21]:
print(real_chsh)
What does entanglement look like beyond two qubits? An important set of maximally entangled states are known as GHZ states (named after Greenberger, Horne, and Zeilinger). These are the states of the form
$|\psi\rangle = \left (|0...0\rangle+|1...1\rangle\right)/\sqrt{2}$. The Bell state previously described is merely a two-qubit version of a GHZ state. The next cells prepare GHZ states of two, three, and four qubits.
In [22]:
# 2 - qubits
# quantum circuit to make GHZ state
q2 = QuantumRegister(2)
c2 = ClassicalRegister(2)
ghz = QuantumCircuit(q2, c2)
ghz.h(q2[0])
ghz.cx(q2[0],q2[1])
# quantum circuit to measure q in standard basis
measureZZ = QuantumCircuit(q2, c2)
measureZZ.measure(q2[0], c2[0])
measureZZ.measure(q2[1], c2[1])
ghzZZ = ghz+measureZZ
measureXX = QuantumCircuit(q2, c2)
measureXX.h(q2[0])
measureXX.h(q2[1])
measureXX.measure(q2[0], c2[0])
measureXX.measure(q2[1], c2[1])
ghzXX = ghz+measureXX
circuits2 = [ghzZZ, ghzXX]
In [23]:
circuit_drawer(ghzZZ,style=qx_color_scheme())
In [24]:
circuit_drawer(ghzXX,style=qx_color_scheme())
In [25]:
job2 = execute(circuits2, backend=sim_backend, shots=sim_shots)
In [26]:
result2 = job2.result()
plot_histogram(result2.get_counts(ghzZZ))
plot_histogram(result2.get_counts(ghzXX))
In [27]:
# 3 - qubits
# quantum circuit to make GHZ state
q3 = QuantumRegister(3)
c3 = ClassicalRegister(3)
ghz3 = QuantumCircuit(q3, c3)
ghz3.h(q3[0])
ghz3.cx(q3[0],q3[1])
ghz3.cx(q3[1],q3[2])
# quantum circuit to measure q in standard basis
measureZZZ = QuantumCircuit(q3, c3)
measureZZZ.measure(q3[0], c3[0])
measureZZZ.measure(q3[1], c3[1])
measureZZZ.measure(q3[2], c3[2])
ghzZZZ = ghz3+measureZZZ
measureXXX = QuantumCircuit(q3, c3)
measureXXX.h(q3[0])
measureXXX.h(q3[1])
measureXXX.h(q3[2])
measureXXX.measure(q3[0], c3[0])
measureXXX.measure(q3[1], c3[1])
measureXXX.measure(q3[2], c3[2])
ghzXXX = ghz3+measureXXX
circuits3 = [ghzZZZ, ghzXXX]
In [28]:
circuit_drawer(ghzZZZ,style=qx_color_scheme())
In [29]:
circuit_drawer(ghzXXX,style=qx_color_scheme())
In [30]:
job3 = execute(circuits3, backend=sim_backend, shots=sim_shots)
In [31]:
result3 = job3.result()
plot_histogram(result3.get_counts(ghzZZZ))
plot_histogram(result3.get_counts(ghzXXX))
In [32]:
# 4 - qubits
# quantum circuit to make GHZ state
q4 = QuantumRegister(4)
c4 = ClassicalRegister(4)
ghz4 = QuantumCircuit(q4, c4)
ghz4.h(q4[0])
ghz4.cx(q4[0],q4[1])
ghz4.cx(q4[1],q4[2])
ghz4.h(q4[3])
ghz4.h(q4[2])
ghz4.cx(q4[3],q4[2])
ghz4.h(q4[3])
ghz4.h(q4[2])
# quantum circuit to measure q in standard basis
measureZZZZ = QuantumCircuit(q4, c4)
measureZZZZ.measure(q4[0], c4[0])
measureZZZZ.measure(q4[1], c4[1])
measureZZZZ.measure(q4[2], c4[2])
measureZZZZ.measure(q4[3], c4[3])
ghzZZZZ = ghz4+measureZZZZ
measureXXXX = QuantumCircuit(q4, c4)
measureXXXX.h(q4[0])
measureXXXX.h(q4[1])
measureXXXX.h(q4[2])
measureXXXX.h(q4[3])
measureXXXX.measure(q4[0], c4[0])
measureXXXX.measure(q4[1], c4[1])
measureXXXX.measure(q4[2], c4[2])
measureXXXX.measure(q4[3], c4[3])
ghzXXXX = ghz4+measureXXXX
circuits4 = [ghzZZZZ, ghzXXXX]
In [33]:
circuit_drawer(ghzZZZZ,style=qx_color_scheme())
In [34]:
circuit_drawer(ghzXXXX,style=qx_color_scheme())
In [35]:
job4 = execute(circuits4, backend=sim_backend, shots=sim_shots)
In [36]:
result4 = job4.result()
plot_histogram(result4.get_counts(ghzZZZZ))
plot_histogram(result4.get_counts(ghzXXXX))
In case the violation of Bell's inequality (CHSH) by two qubits is not enough to convince you to believe in quantum mechanics, we can generalize to a more stringent set of tests with three qubits, which can give a single-shot violation (rather than taking averaged statistics). A well-known three-qubit case is Mermin's inequality, which is a test we can perform on GHZ states.
An example of a three-qubit GHZ state is $|\psi\rangle = \left (|000\rangle+|111\rangle\right)/\sqrt{2}$. You can see this is a further generalization of a Bell state and, if measured, should give $|000\rangle$ half the time and $|111 \rangle$ the other half of the time.
In [37]:
# quantum circuit to make GHZ state
q3 = QuantumRegister(3)
c3 = ClassicalRegister(3)
ghz3 = QuantumCircuit(q3, c3)
ghz3.h(q3[0])
ghz3.cx(q3[0],q3[1])
ghz3.cx(q3[0],q3[2])
# quantum circuit to measure q in standard basis
measureZZZ = QuantumCircuit(q3, c3)
measureZZZ.measure(q3[0], c3[0])
measureZZZ.measure(q3[1], c3[1])
measureZZZ.measure(q3[2], c3[2])
ghzZZZ = ghz3+measureZZZ
circuits5 = [ghzZZZ]
In [38]:
circuit_drawer(ghzZZZ,style=qx_color_scheme())
In [39]:
job5 = execute(circuits5, backend=sim_backend, shots=sim_shots)
In [40]:
result5 = job5.result()
plot_histogram(result5.get_counts(ghzZZZ))
Suppose we have three independent systems, $\{A, B, C\}$, for which we can query two particular questions (observables) $X$ and $Y$. In each case, either query can give $+1$ or $-1$. Consider whether it is possible to choose some state of the three boxes, such that we can satisfy the following four conditions: $X_A Y_B Y_C = 1$, $Y_A X_B Y_C =1$, $Y_A Y_B X_C = 1$, and $X_A X_B X_C = -1$. Classically, this can be shown to be impossible... but a three-qubit GHZ state can in fact satisfy all four conditions.
In [41]:
MerminM = lambda x : x[0]*x[1]*x[2]*x[3]
In [42]:
observable ={'000': 1, '001': -1, '010': -1, '011': 1, '100': -1, '101': 1, '110': 1, '111': -1}
In [43]:
# quantum circuit to measure q XXX
measureXXX = QuantumCircuit(q3, c3)
measureXXX.h(q3[0])
measureXXX.h(q3[1])
measureXXX.h(q3[2])
measureXXX.measure(q3[0], c3[0])
measureXXX.measure(q3[1], c3[1])
measureXXX.measure(q3[2], c3[2])
ghzXXX = ghz3+measureXXX
# quantum circuit to measure q XYY
measureXYY = QuantumCircuit(q3, c3)
measureXYY.s(q3[1]).inverse()
measureXYY.s(q3[2]).inverse()
measureXYY.h(q3[0])
measureXYY.h(q3[1])
measureXYY.h(q3[2])
measureXYY.measure(q3[0], c3[0])
measureXYY.measure(q3[1], c3[1])
measureXYY.measure(q3[2], c3[2])
ghzXYY = ghz3+measureXYY
# quantum circuit to measure q YXY
measureYXY = QuantumCircuit(q3, c3)
measureYXY.s(q3[0]).inverse()
measureYXY.s(q3[2]).inverse()
measureYXY.h(q3[0])
measureYXY.h(q3[1])
measureYXY.h(q3[2])
measureYXY.measure(q3[0], c3[0])
measureYXY.measure(q3[1], c3[1])
measureYXY.measure(q3[2], c3[2])
ghzYXY = ghz3+measureYXY
# quantum circuit to measure q YYX
measureYYX = QuantumCircuit(q3, c3)
measureYYX.s(q3[0]).inverse()
measureYYX.s(q3[1]).inverse()
measureYYX.h(q3[0])
measureYYX.h(q3[1])
measureYYX.h(q3[2])
measureYYX.measure(q3[0], c3[0])
measureYYX.measure(q3[1], c3[1])
measureYYX.measure(q3[2], c3[2])
ghzYYX = ghz3+measureYYX
circuits6 = [ghzXXX, ghzYYX, ghzYXY, ghzXYY]
In [44]:
circuit_drawer(ghzXXX,style=qx_color_scheme())
In [45]:
circuit_drawer(ghzYYX,style=qx_color_scheme())
In [46]:
circuit_drawer(ghzYXY,style=qx_color_scheme())
In [47]:
circuit_drawer(ghzXYY,style=qx_color_scheme())
In [48]:
%%qiskit_job_status
HTMLProgressBar()
job6 = execute(circuits6, backend=device_backend, coupling_map=device_coupling, shots=device_shots)
In [49]:
result6 = job6.result()
In [50]:
temp=[]
temp.append(result6.average_data(ghzXXX,observable))
temp.append(result6.average_data(ghzYYX,observable))
temp.append(result6.average_data(ghzYXY,observable))
temp.append(result6.average_data(ghzXYY,observable))
print(MerminM(temp))
The above shows that the average statistics are not consistent with a local hidden variable theory. To demonstrate with single shots, we can run 50 single experiments, with each experiment chosen randomly, and the outcomes saved. If there was a local hidden variable theory, all the outcomes would be $+1$.
In [ ]: