Testing Entanglement

The latest version of this notebook is available on https://github.com/qiskit/qiskit-tutorial.


Contributors

Jay Gambetta, Antonio Córcoles, Anna Phan

Entanglement

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:

Two-Qubit Correlated Observables

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))


the best backend is ibmqx4 with coupling [[1, 0], [2, 0], [2, 1], [3, 2], [3, 4], [4, 2]]

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)))


IZ = 0.046875
ZI = 0.00390625
ZZ = 0.72265625
IX = 0.052734375
XI = 0.056640625
XX = 0.75390625
ZX = -0.1328125
XZ = 0.044921875

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.

CHSH Inequality

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)


[1.2578125, 1.900390625, 1.736328125, 0.90234375, -0.271484375, -1.29296875, -1.916015625, -1.87890625, -1.07421875, 0.267578125]

Despite the presence of loopholes in our demonstration, we can see that this experiment is compatible with quantum mechanics as a theory with no local hidden variables. See the original experimental demonstrations of this test with superconducting qubits here and here.

Two-, Three-, and Four-Qubit GHZ States

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))


Mermin's Test and the Three Box Game

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))


-0.16302223701495677

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 [ ]: