Hands-on 5

import qiskit.tools.jupyter
%qiskit_version_table

Version Information

Qiskit SoftwareVersion
qiskit-terra0.22.0
qiskit-aer0.11.0
qiskit-ibmq-provider0.19.2
qiskit0.39.0
System information
Python version3.8.13
Python compilerGCC 7.5.0
Python builddefault, Mar 28 2022 11:38:47
OSLinux
CPUs24
Memory (Gb)62.64844512939453
Thu Oct 27 12:48:10 2022 +03

Quantum Coin Game

Please check Ted talk by Sohini Ghosh and Quantum Coin Flipping from Wikipedia first.

Quantum Coin Game is one of the fundamental concept of quantum computing, which uses simple implementation of quantum gates or more precisely uses the wierdness of quantum mechanics, to win about 97% of the time, when played against an opponent. Flipping of coin and say heads or tails. The concept of Quantum Coin Game came from the idea of classical coin game which can only show heads and tails. But since the game utilizes the concepts of quantum mechanics, it would be interesting to see what could be the outcome of the whole experiment. The main concept of this game is how the quantum computer uses the power of quantum superposition, which tells an object can exists in 2 different states at the same time, to win absolutely everytime.

What are the rules of this game ?

  1. Quantum Computer plays a move but it is not revealed to the Opponent(Human).

  2. Opponent(Human) plays a move and it is also not revealed to the Quantum Computer.

  3. Finally Quantum Computer plays a move.

  4. Results are shown. If its heads, then Quantum Computer wins. Else, Opponent(Human) wins.

NOTE: “Playing a move” refers to “Flipping the coin” and we consider the coin as fair coin.

# Importing all the necessary library
from qiskit import QuantumCircuit, Aer, IBMQ, QuantumRegister, ClassicalRegister, execute
from qiskit.tools.jupyter import *
from qiskit.visualization import *
import qiskit.tools.jupyter
import ipywidgets as widgets

# Layout
button_p = widgets.Button(
    description='Play')
gate_p = widgets.Dropdown(
    options=[('Identity', 'i'), ('Bit Flip', 'x')],
    description='Choice: ',
    disabled=False,
)
out_p = widgets.Output()
def on_button_clicked(b):
    with out_p:
        
        # Initial Circuit
        circuit_p = QuantumRegister(1, 'circuit')
        measure_p = ClassicalRegister(1, 'result')
        qc_p = QuantumCircuit(circuit_p, measure_p)
        
        # Turn 1
        qc_p.h(circuit_p[0])
        
        # Turn 2
        if gate_p.value == 'i':
            qc_p.i(circuit_p[0])
        if gate_p.value == 'x':
            qc_p.x(circuit_p[0])
        
        # Turn 3
        qc_p.h(circuit_p[0])
        
        # Measure  
        qc_p.measure(circuit_p, measure_p)
        
        # QASM
        backend_p = Aer.get_backend('aer_simulator')
        job_p = execute(qc_p, backend_p, shots=8192)
        res_p = job_p.result().get_counts()
        
        # Result
        if len(res_p) == 1 and list(res_p.keys())[0] == '0':
            print("You Lose to Quantum. Quantum Computer Wins")
        if len(res_p) == 1 and list(res_p.keys())[0] == '1':
            print("You Win against Quantum Computer")
        if len(res_p) == 2:
            print("Either Quantum or You Wins")

button_p.on_click(on_button_clicked)
widgets.VBox([gate_p, button_p, out_p])

Analogy

Now that we know what is a quantum coin game, what is it based on and most importantly what are the rules of this game, lets convert the concept of this game in quantum computing terminology.

  • The ‘coin’ in flipping a coin we referring here is a ‘single qubit gate’.

\[\begin{split} |\psi\rangle=\begin{bmatrix}\alpha \\ \beta\end{bmatrix} \end{split}\]

where \(\alpha, \beta \in \mathbb{C}\) and \(|\alpha|^2 + |\beta|^2 = 1\)

  • “Flipping” the coin is application of the bit-flip operator

\[\begin{split} X = \begin{bmatrix} 0 & 1 \\ 1 & 0 \end{bmatrix} \end{split}\]
  • The “heads” state is defined as $\( |0\rangle = \begin{bmatrix} 1 \\ 0 \end{bmatrix} \)\( and "tails" as \)\( |1\rangle = \begin{bmatrix} 0 \\ 1 \end{bmatrix} \)$

  • The quantum computer “plays” by applying the Hadamard \(H\) operator $\( H = \frac{1}{\sqrt{2}} \begin{bmatrix} 1 & 1 \\ 1 & -1 \end{bmatrix} \)$

Approach

Lets see how to approach the game in quantum computing terminology-

  • The coin is initialized to the \(|0\rangle\) “heads” state.

  • The computer plays, applying the Hadamard \(H\) operator to the coin (operators are applied using matrix multiplication). $\( H|0\rangle = \frac{1}{\sqrt2}(|0\rangle + |1\rangle) \)\( The coin enters the \)\( H|0\rangle = |+\rangle = \frac{1}{\sqrt{2}} \begin{bmatrix} 1 \\ 1 \end{bmatrix} \)$ state.

  • The human plays, choosing whether to flip the coin (apply the \(X\) operator) or do nothing (apply the \(I\) operator). However, since the \(X\) operator just flips the state vector upside down, \(X\) has no effect. Same goes for \(I\). $\( X|+\rangle=|+\rangle \)\( \)\( I|+\rangle=|+\rangle \)\( No matter what, the state is \)|+\rangle$ after the human plays.

  • The computer plays, applying the Hadamard \(H\) operator again, taking the coin to the \(|0⟩\) “heads” state. $\( H|+\rangle = |0\rangle \)$

# Importing all the necessary library

from qiskit import QuantumCircuit, Aer, IBMQ, QuantumRegister, ClassicalRegister, execute
from qiskit.tools.jupyter import *
from qiskit.visualization import *
import qiskit.tools.jupyter
import ipywidgets as widgets
# Building the initial circuit

def initial_circuit():
    circuit = QuantumRegister(1, 'circuit')
    measure = ClassicalRegister(1, 'result')
    qc = QuantumCircuit(circuit, measure)
    qc.draw('mpl')
    return qc, circuit, measure
# Widget Initialization

gate = widgets.Dropdown(
    options=[('Identity', 'i'), ('Bit Flip', 'x')],
    description='Choice: ',
    disabled=False,
)

Optimal Strategy

Using the above approach the possibility table reduces to-

Start State Quantum Classical Quantum Result Who Wins?
$|0\rangle$ $H$ $I$ $H$ $|0\rangle$ Quantum
$|0\rangle$ $H$ $X$ $H$ $|0\rangle$ Quantum

Now lets look at the possibilities-

  1. Quantum Computer Wins ( \(|0\rangle\) ):

\[ \frac{2}{2} = 100 \% \]
  1. Classical Human Wins ( \(|1\rangle\) ):

\[ \frac{0}{2} = 0 \% \]
  1. Either Quantum Computer or Classical Human Wins ( \(|0\rangle + |1\rangle\) ):

\[ \frac{0}{2} = 0 \% \]

This table shows the quantum computer wins \(100\%\) of the time. But in Shohini’s talk it is \(~97\%\), due to errors.

Lets play this version using Qiskit

Building the initial circuit

qc, circuit, measure = initial_circuit()

Turn 1. Quantum Computer

# Use H Gate

qc.h(circuit[0])
qc.draw('mpl')
../../../../_images/45d80a959542a6366c59dfb2674a9b71a94785cd0d5b05bbd1e3b08f0f9c7f53.png

Turn 2. Classical Human

if gate.value == 'i':
    qc.i(circuit[0])
if gate.value == 'x':
    qc.x(circuit[0])

qc.draw('mpl')
../../../../_images/1862ca6e8ad70392a3d1f2dec3c1d1524a8c59b61bd6af7dcc034d73395d1b47.png

Turn 3. Quantum Computer

Quantum Computer uses Hadamard \(H\) on its first turn

# Used H Gate

qc.h(circuit[0])
qc.draw('mpl')
../../../../_images/58b649a5406f67acae532aecef0774e93fe21e05a7c304a7a0ba32a753db14be.png

Measurement

qc.measure(circuit, measure)
qc.draw('mpl')
../../../../_images/49becac765f39b2ddec6302e42eb7e9397795bf65d946a8e41a08abc384c53b4.png

QASM_Simulator

backend = Aer.get_backend('aer_simulator')
job = execute(qc, backend, shots=8192)
res = job.result().get_counts()
print(res)
plot_histogram(res)
{'0': 8192}
../../../../_images/7e079f030b343e52ccceaeee1bd49f0fd736b7e39f08561a7f8faf562507ecfa.png

Lets see who wins

if len(res) == 1 and list(res.keys())[0] == '0':
    print("Quantum Computer Wins")
if len(res) == 1 and list(res.keys())[0] == '1':
    print("Classical Human Wins")
if len(res) == 2:
    print("Either Quantum Computer or Classical Human Wins")
Quantum Computer Wins

Running on Quantum Computer

provider = IBMQ.load_account()
backend_real = provider.get_backend('ibmq_manila')
job_real = execute(qc, backend_real, shots=8192)
res_real = job_real.result().get_counts()
print(res_real)
plot_histogram(res_real)
---------------------------------------------------------------------------
IBMQAccountCredentialsNotFound            Traceback (most recent call last)
Input In [13], in <cell line: 1>()
----> 1 provider = IBMQ.load_account()
      2 backend_real = provider.get_backend('ibmq_manila')
      3 job_real = execute(qc, backend_real, shots=8192)

File ~/Prog/miniconda3/envs/qml/lib/python3.8/site-packages/qiskit/providers/ibmq/ibmqfactory.py:167, in IBMQFactory.load_account(self)
    164 credentials_list = list(stored_credentials.values())
    166 if not credentials_list:
--> 167     raise IBMQAccountCredentialsNotFound(
    168         'No IBM Quantum Experience credentials found.')
    170 if len(credentials_list) > 1:
    171     raise IBMQAccountMultipleCredentialsFound(
    172         'Multiple IBM Quantum Experience credentials found. ' + UPDATE_ACCOUNT_TEXT)

IBMQAccountCredentialsNotFound: 'No IBM Quantum Experience credentials found.'

Unlike the perfect simulation, the real quantum computer only wins ~\(99\ \%\) of the time, the \(1\ \%\) in which it loses is due to errors. Quantum computers have improved a bit since Shohini’s talk where the error is closer to \(3\ \%\).

Conclusion

This simple and yet fun little game shows the basic quantum states \(|0\rangle\), \(|1\rangle\), \(|+\rangle\) and \(|−\rangle\), plus the common ways of moving between them with the \(X\), \(H\), \(I\), \(Z\) gates.

Send it after class 1:

Think of other variations of the game, and tweak the game as follows

  1. What if, instead of quantum computer taking first turn, the classical human take the first turn ?

  2. What if, instead of representing head as \(|0\rangle\), the tail is represented as \(|0\rangle\) ?

  3. What if, instead of using fair coin, we used unfair coin ?

  4. What if, instead of playing against a classical human, the quantum computer plays with another quantum computer ?

  5. What if, instead of having 3 turns, there are \(n\) number of turns ?

  6. What if, instead of using all gates, restrict the use of some gates ?

how did the results change?

Local Reality and the CHSH inequality

We have seen in a previous module how quantum entanglement results in strong correlations in a multi-partite system. In fact these correlations appear to be stronger than anything that could be explained using classical physics.

The historical development of quantum mechanics is filled with agitated discussions about the true nature of reality and the extent to which quantum mechanics can explain it. Given the spectacular empirical success of quantum mechanics, it was going to be clear that people would not simply give it up just because some of its aspects were hard to reconcile with intuition.

At the root of these different points of views was the question of the nature of measurement. We know there is an element of randomness in quantum measurements, but is that really so? Is there a sneaky way by which the Universe has already decided beforehand which value a given measurement is going to yield at a future time? This hypothesis was the basis for different hidden variable theories. But these theories did not only need to explain randomness at the single particle level. They also needed to explain what happens when different observers measure different parts of a multi-partite entangled system! This went beyond just hidden variable theories. Now a local hidden variable theory was needed in order to reconcile the observations of quantum mechanics with a Universe in which local reality was valid.

What is local reality? In an Universe where locality holds, it should be possible to separate two systems so far in space that they could not interact with each other. The concept of reality is related to whether a measurable quantity holds a particular value in the absence of any future measurement.

In 1963, John Stewart Bell published what could be argued as one of the most profound discoveries in the history of science. Bell stated that any theory invoking local hidden variables could be experimentally ruled out. In this section we are going to see how, and we will run a real experiment that demonstrates so! (with some remaining loopholes to close…)

The CHSH inequality

Imagine Alice and Bob are given each one part of a bipartite entangled system. Each of them then performs two measurements on their part in two different bases. Let’s call Alice’s bases A and a and Bob’s B and b. What is the expectation value of the quantity \(\langle CHSH \rangle = \langle AB \rangle - \langle Ab \rangle + \langle aB \rangle + \langle ab \rangle\) ?

Now, Alice and Bob have one qubit each, so any measurement they perform on their system (qubit) can only yield one of two possible outcomes: +1 or -1. Note that whereas we typically refer to the two qubit states as \(|0\rangle\) and \(|1\rangle\), these are eigenstates, and a projective measurement will yield their eigenvalues, +1 and -1, respectively.

Therefore, if any measurement of A, a, B, and b can only yield \(\pm 1\), the quantities \((B-b)\) and \((B+b)\) can only be 0 or \(\pm2\). And thus, the quantity \(A(B-b) + a(B+b)\) can only be either +2 or -2, which means that there should be a bound for the expectation value of the quantity we have called \(|\langle CHSH \rangle| =|\langle AB \rangle - \langle Ab \rangle + \langle aB \rangle + \langle ab \rangle| \leq 2\).

Now, the above discussion is oversimplified, because we could consider that the outcome on any set of measurements from Alice and Bob could depend on a set of local hidden variables, but it can be shown with some math that, even when that is the case, the expectation value of the quantity \(CHSH\) should be bounded by 2 if local realism held.

But what happens when we do these experiments with an entangled system? Let’s try it!

#import qiskit tools
import qiskit
from qiskit import QuantumCircuit, ClassicalRegister, QuantumRegister, transpile, Aer, IBMQ
from qiskit.tools.visualization import circuit_drawer
from qiskit.tools.monitor import job_monitor, backend_monitor, backend_overview
from qiskit.providers.aer import noise

#import python stuff
import matplotlib.pyplot as plt
import numpy as np
import time
# Set devices, if using a real device
IBMQ.load_account()
provider = IBMQ.get_provider('ibm-q')
quito = provider.get_backend('ibmq_quito')
sim = Aer.get_backend('aer_simulator')

First we are going to define a function to create our CHSH circuits. We are going to choose, without loss of generality, that Bob always uses the computational (\(Z\)) and the \(X\) bases for his \(B\) and \(b\) measurements, respectively, whereas Alice chooses also orthogonal bases but whose angle we are going to vary between \(0\) and \(2\pi\) with respect to Bob’s bases. This set of angles is going to be the input argument to our \(CHSH\) circuit building function.

def make_chsh_circuit(theta_vec):
    """Return a list of QuantumCircuits for use in a CHSH experiemnt
    (one for each value of theta in theta_vec)
    
        Args:
            theta_vec (list): list of values of angles between the bases of Alice and Bob
        
        Returns:
            List[QuantumCircuit]: CHSH QuantumCircuits for each value of theta
    """
    chsh_circuits = []
    
    for theta in theta_vec:
        obs_vec = ['00', '01', '10', '11']
        for el in obs_vec:
            qc = QuantumCircuit(2,2)
            qc.h(0)
            qc.cx(0, 1)
            qc.ry(theta, 0)
            for a in range(2):
                if el[a] == '1':
                    qc.h(a) 
            qc.measure(range(2),range(2))
            chsh_circuits.append(qc)

    return chsh_circuits  

Next, we are going to define a function for estimating the quantity \(\langle CHSH \rangle\). One can define two of such quantities, actually, \(\langle CHSH1 \rangle = \langle AB \rangle - \langle Ab \rangle + \langle aB \rangle + \langle ab \rangle\) and \(\langle CHSH2 \rangle = \langle AB \rangle + \langle Ab \rangle - \langle aB \rangle + \langle ab \rangle\). Once chosen the corresponding measurement axes for both parties, each expectation value can be simply estimated by adding the counts from the output bitstrings with the appropriate sign (plus for the even terms \(00\) and \(11\) and minus for odd terms \(01\) and \(10\).

def compute_chsh_witness(counts):
    """Computes expectation values for the CHSH inequality, for each
    angle (theta) between measurement axis.

        Args: counts (list[dict]): dict of counts for each experiment
              (4 per value of theta)

        Returns:
            Tuple(List, List): Tuple of lists with the two CHSH witnesses
    """
    # Order is ZZ,ZX,XZ,XX
    
    CHSH1 = []
    CHSH2 = []
    # Divide the list of dictionaries in sets of 4
    for i in range(0, len(counts), 4):  
        theta_dict = counts[i:i + 4]
        zz = theta_dict[0]
        zx = theta_dict[1]
        xz = theta_dict[2]
        xx = theta_dict[3]

        no_shots = sum(xx[y] for y in xx)

        chsh1 = 0
        chsh2 = 0

        for element in zz:
            parity = (-1)**(int(element[0])+int(element[1]))
            chsh1+= parity*zz[element]
            chsh2+= parity*zz[element]

        for element in zx:
            parity = (-1)**(int(element[0])+int(element[1]))
            chsh1+= parity*zx[element]
            chsh2-= parity*zx[element]

        for element in xz:
            parity = (-1)**(int(element[0])+int(element[1]))
            chsh1-= parity*xz[element]
            chsh2+= parity*xz[element]

        for element in xx:
            parity = (-1)**(int(element[0])+int(element[1]))
            chsh1+= parity*xx[element]
            chsh2+= parity*xx[element]

        CHSH1.append(chsh1/no_shots)
        CHSH2.append(chsh2/no_shots)
    
    return CHSH1, CHSH2

Finally, we are going to split the interval \([0, 2\pi)\) into 15 angles and will build the corresponding set of \(CHSH\) circuits.

number_of_thetas = 15
theta_vec = np.linspace(0,2*np.pi,number_of_thetas)
my_chsh_circuits = make_chsh_circuit(theta_vec)

Now, let’s have a brief look at how four of these circuits look like for a given \(\theta\).

my_chsh_circuits[4].draw()
my_chsh_circuits[5].draw()
my_chsh_circuits[6].draw()
my_chsh_circuits[7].draw()

These circuits are simply creating a Bell pair, and then measuring each party in a different basis. While Bob (\(q_1\)) always measures either in the computational basis or the \(X\) basis, Alice’s measurement basis rotates by the angle \(\theta\) with respect to Bob’s.

# Execute and get counts
result_ideal = sim.run(my_chsh_circuits).result()

tic = time.time()
transpiled_circuits = transpile(my_chsh_circuits, quito)
job_real = quito.run(transpiled_circuits, shots=8192)
job_monitor(job_real)
result_real = job_real.result()
toc = time.time()

print(toc-tic)
CHSH1_ideal, CHSH2_ideal = compute_chsh_witness(result_ideal.get_counts())
CHSH1_real, CHSH2_real = compute_chsh_witness(result_real.get_counts())

Now we plot the results.

plt.figure(figsize=(12,8))
plt.rcParams.update({'font.size': 22})
plt.plot(theta_vec,CHSH1_ideal,'o-',label = 'CHSH1 Noiseless')
plt.plot(theta_vec,CHSH2_ideal,'o-',label = 'CHSH2 Noiseless')

plt.plot(theta_vec,CHSH1_real,'x-',label = 'CHSH1 Quito')
plt.plot(theta_vec,CHSH2_real,'x-',label = 'CHSH2 Quito')

plt.grid(which='major',axis='both')
plt.rcParams.update({'font.size': 16})
plt.legend()
plt.axhline(y=2, color='r', linestyle='-')
plt.axhline(y=-2, color='r', linestyle='-')
plt.axhline(y=np.sqrt(2)*2, color='k', linestyle='-.')
plt.axhline(y=-np.sqrt(2)*2, color='k', linestyle='-.')
plt.xlabel('Theta')
plt.ylabel('CHSH witness')

Note what happened! There are some particular combination of measurement bases for which \(|CHSH| \geq 2\). How is this possible? Let’s look at our entangled bipartite system. It is easy to show that if \(|\psi \rangle = 1/\sqrt{2} (|00\rangle + |11\rangle)\), then the expectation value \(\langle AB \rangle = \langle \psi|A \otimes B| \psi \rangle = -\cos \theta_{AB}\) where \(\theta_{AB}\) is the angle between the measurement bases \(A\) and \(B\). Therefore, for the particular choice of bases \(A = 1/\sqrt{2}(\sigma_z - \sigma_x)\) and \(a = 1/\sqrt{2}(\sigma_z + \sigma_x)\), letting Bob measure with \(B=\sigma_z\) and \(b=\sigma_x\), we see that \(|\langle CHSH1 \rangle| = 2\sqrt{2} > 2\). It can also be shown that \(2\sqrt{2}\) is the maximum possible value attainable, even in the quantum case (dash-dotted line in the plot).

The above inequality is called CHSH after Clauser, Horne, Shimony, and Holt, and it is the most popular way of presenting the original inequality from Bell.

The fact that we violated the CHSH inequality in our real device is of significance. Just a decade ago such an experiment would have been of great impact. Nowadays, quantum devices have become significantly better and these results can be replicated easily in state-of-the-art hardware. However, there are a number of loopholes that have to be closed when violating the inequality in order to claim that either locality or realism have been disproven. These are the detection loophole (where our detector is faulty and fails to provide meaningful statistics) and the locality/causality loophole (where the two parts of the entangled system are separated by a distance smaller than the distance covered by the light in the time it takes to perform a measurement). Given we can generate entangled pairs with high fidelity and every measurement yields a result (this is, no measured particle is “lost”), we have closed the detection loophole in our experiments above. However, given the distance between our qubits (a few mm) and the time it takes to perform a measurement (order of \(\mu\)s), we cannot claim we closed the causality loophole.

Send it after class 2

Consider a game where Alice and Bob are put in separate rooms and each is given a bit \(x\) and \(y\), respectively. These bits are chosen at random and independently of each other. On receiving the bit each of them replies with a bit of their own, \(a\) and \(b\). Now, Alice and Bob win the game if \(a\) and \(b\) are different whenever \(x=y=1\) and equal otherwise. It is easy to see that the best possible strategy for Alice and Bob is to always give \(a=b=0\) (or \(1\)). With this strategy, Alice and Bob can win the game at most 75% of the time.

Imagine Alice and Bob are allowed to share an entangled two-qubit state. Is there a strategy they can use that would give them a better chance of winning than 75%? (Remember that they can agree upon any strategy beforehand, but once they are given the random bits they cannot communicate anymore. They can take their respective parts of the entangled pair with them at all times, of course.)