Single-qubit Quantum Random Access Coding (QRAC)

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


Contributors

Rudy Raymond, Takashi Imamichi

Introduction

Random Access Coding (RAC) is one of few examples where a small number of quantum bits can exhibit properties that cannot be achieved by the same amount of classical bits. To demonstrate the power of Quantum RAC with a single qubit, consider a case where the sender, say, Alice, wants to encode $n$ bits of information into a qubit. The qubit is then sent to the receiver, say, Bob, who will decode it to obtain one bit (out of $n$ bits sent by Alice) of his choice (which is unknown to Alice). It is known that there is no way for Alice to encode her $2$ bits into one classical bit so that Bob can then recover any bit with success probability better than half.

On the other hand, with a single qubit it is possible to encode up to $3$ bits of information such that the receiver is quaranteed to observe his choice of bit with probability better than random guessing. The encoding and decoding scheme for encoding 2 bits and 3 bits of information into 1 qubit are denoted as, respectively, $(2,1)$-QRAC and $(3,1)$-QRAC.

Here, using QuantumProgram of IBM Q Experience we describe how to construct the $(2,1)$-QRAC, i.e., encoding $2$ bits of information into $1$ qubit so that any bit can be recovered with probability at least $0.85$, as well as the $(3,1)$-QRAC, i.e., encoding $3$-bits of information into $1$ qubit so that any bit can be recovered with probability at least $0.78$. All necessary unitary gates and measurements can be realized by the $u3$ rotation gates included in the qelib1.inc whose mathematical definition can be found here.

First, we prepare the environment.


In [1]:
# useful math functions
from math import pi

# importing the QISKit
from qiskit import Aer, IBMQ
from qiskit import QuantumCircuit, ClassicalRegister, QuantumRegister, execute

# import basic plot tools
from qiskit.tools.visualization import plot_histogram

# useful additional packages 
from qiskit.wrapper.jupyter import *
from qiskit.backends.ibmq import least_busy

In [2]:
IBMQ.load_accounts()

Encoding 2 bits into 1 qubit with $(2,1)$-QRAC

We follow Example 1 described in the paper here. Alice encodes her $2$ bits $x_1x_2$ by preparing the following 1-qubit states $|\phi_{x_1x_2}\rangle$.

\begin{eqnarray} |\phi_{00}\rangle &=& \cos\left(\pi/8\right)|0\rangle + \sin\left(\pi/8\right)|1\rangle\\ |\phi_{10}\rangle &=& \cos\left(3\pi/8\right)|0\rangle + \sin\left(3\pi/8\right)|1\rangle\\ |\phi_{11}\rangle &=& \cos\left(5\pi/8\right)|0\rangle + \sin\left(5\pi/8\right)|1\rangle\\ |\phi_{01}\rangle &=& \cos\left(7\pi/8\right)|0\rangle + \sin\left(7\pi/8\right)|1\rangle\\ \end{eqnarray}

Bob recovers his choice of bit by measuring the qubit in the following way. If he wants to recover the first bit (i.e., $x_1$), he measures the qubit in the $\left\{|0\rangle, |1\rangle\right\}$ basis, namely, he concludes $0$ if he observes $|0\rangle$, and $1$ otherwise. On the other hand, if he wants to recover the second bit (i.e., $x_2$), he measures the qubit in the $\left\{|+\rangle, |-\rangle\right\}$ basis, where $|+\rangle = 1/\sqrt{2}\left(|0\rangle + |1\rangle\right)$, and $|-\rangle = 1/\sqrt{2}\left(|0\rangle - |1\rangle\right)$.

Below is the code to create quantum circuits for performing experiments of $(2,1)$-QRAC. Each of the circuits consits of encoding $2$ bits of information into $1$ qubit and decoding either the first or the second bit by performing measurement on the qubit. Notice that because in the IBM Q Experience we can only perform measurement in the $\left\{|0\rangle, |1\rangle\right\}$ basis, the measurement in the $\left\{|+\rangle, |-\rangle\right\}$ basis is performed by first applying the Hadamard gate.


In [3]:
backend = Aer.get_backend('qasm_simulator') # the device to run on
shots = 1024    # the number of shots in the experiment 

#to record the rotation number for encoding 00, 10, 11, 01
rotationNumbers = {"00":1, "10":3, "11":5, "01":7}

# Creating registers
# qubit for encoding 2 bits of information
qr = QuantumRegister(1)
# bit for recording the measurement of the qubit
cr = ClassicalRegister(1)

# dictionary for encoding circuits
encodingCircuits = {}
# Quantum circuits for encoding 00, 10, 11, 01
for bits in ("00", "01", "10", "11"):
    circuitName = "Encode"+bits
    encodingCircuits[circuitName] = QuantumCircuit(qr, cr, name=circuitName)
    encodingCircuits[circuitName].u3(rotationNumbers[bits]*pi/4.0, 0, 0, qr[0])
    encodingCircuits[circuitName].barrier()

# dictionary for decoding circuits
decodingCircuits = {}
# Quantum circuits for decoding the first and second bit
for pos in ("First", "Second"):
    circuitName = "Decode"+pos
    decodingCircuits[circuitName] = QuantumCircuit(qr, cr, name=circuitName)
    if pos == "Second": #if pos == "First" we can directly measure
        decodingCircuits[circuitName].h(qr[0])
    decodingCircuits[circuitName].measure(qr[0], cr[0])

#combine encoding and decoding of QRACs to get a list of complete circuits
circuitNames = []
circuits = []
for k1 in encodingCircuits.keys():
    for k2 in decodingCircuits.keys():
        circuitNames.append(k1+k2)
        circuits.append(encodingCircuits[k1]+decodingCircuits[k2])

print("List of circuit names:", circuitNames) #list of circuit names


List of circuit names: ['Encode00DecodeFirst', 'Encode00DecodeSecond', 'Encode01DecodeFirst', 'Encode01DecodeSecond', 'Encode10DecodeFirst', 'Encode10DecodeSecond', 'Encode11DecodeFirst', 'Encode11DecodeSecond']

Now, we can perform various experiments of $(2,1)$-QRAC. Below, we execute all circuits of QRACs and plot some experimental results.


In [4]:
job = execute(circuits, backend=backend, shots=shots)
results = job.result()
print("Experimental Result of Encode01DecodeFirst")
#We should measure "0" with probability 0.85
plot_histogram(results.get_counts(circuits[circuitNames.index("Encode01DecodeFirst")]))
print("Experimental Result of Encode01DecodeSecond")
#We should measure "1" with probability 0.85
plot_histogram(results.get_counts(circuits[circuitNames.index("Encode01DecodeSecond")]))
print("Experimental Result of Encode11DecodeFirst")
#We should measure "1" with probability 0.85
plot_histogram(results.get_counts(circuits[circuitNames.index("Encode11DecodeFirst")]))
print("Experimental Result of Encode11DecodeSecond")
#We should measure "1" with probability 0.85
plot_histogram(results.get_counts(circuits[circuitNames.index("Encode11DecodeSecond")]))


Experimental Result of Encode01DecodeFirst
Experimental Result of Encode01DecodeSecond
Experimental Result of Encode11DecodeFirst
Experimental Result of Encode11DecodeSecond

From the above simulations, we can see that each of the encoded bits can be decoded with probability closed to the theoretical values. We can now proceed to perform experiments on the real devices or local simulator, as below.


In [9]:
%%qiskit_job_status

# Use the IBM Quantum Experience
backend = least_busy(IBMQ.backends(simulator=False))

job_exp = execute(circuits, backend=backend, shots=shots)



In [10]:
results = job_exp.result()
print("Experimental Result of Encode01DecodeFirst")
#We should measure "0" with probability 0.85
plot_histogram(results.get_counts(circuits[circuitNames.index("Encode01DecodeFirst")]))
print("Experimental Result of Encode01DecodeSecond")
#We should measure "1" with probability 0.85
plot_histogram(results.get_counts(circuits[circuitNames.index("Encode01DecodeSecond")]))


Experimental Result of Encode01DecodeFirst
Experimental Result of Encode01DecodeSecond

Encoding 3 bits into 1 qubit with $(3,1)$-QRAC

We follow Example 2 described in the paper here. Alice encodes her $3$ bits $x_1x_2x_3$ by preparing the following 1-qubit states $|\phi_{x_1x_2x_3}\rangle$ as below, where $\cos^2\left(\theta\right) = 1/2 + \sqrt{3}/6 > 0.788$.

\begin{eqnarray} |\phi_{000}\rangle &=& \cos\left(\theta\right)|0\rangle + e^{i\pi/4}\sin\left(\theta\right)|1\rangle\\ |\phi_{001}\rangle &=& \cos\left(\theta\right)|0\rangle + e^{-i\pi/4}\sin\left(\theta\right)|1\rangle\\ |\phi_{010}\rangle &=& \cos\left(\theta\right)|0\rangle + e^{3i\pi/4}\sin\left(\theta\right)|1\rangle\\ |\phi_{011}\rangle &=& \cos\left(\theta\right)|0\rangle + e^{-3i\pi/4}\sin\left(\theta\right)|1\rangle\\ |\phi_{100}\rangle &=& \sin\left(\theta\right)|0\rangle + e^{i\pi/4}\cos\left(\theta\right)|1\rangle\\ |\phi_{101}\rangle &=& \sin\left(\theta\right)|0\rangle + e^{-i\pi/4}\cos\left(\theta\right)|1\rangle\\ |\phi_{110}\rangle &=& \sin\left(\theta\right)|0\rangle + e^{3i\pi/4}\cos\left(\theta\right)|1\rangle\\ |\phi_{111}\rangle &=& \sin\left(\theta\right)|0\rangle + e^{-i\pi/4}\cos\left(\theta\right)|1\rangle\\ \end{eqnarray}

Bob recovers his choice of bit by measuring the qubit similarly as in the $(2,1,0.85)$-QRAC for the first and second bit, and to recover the third bit, he measures the qubit in the $\left\{|+'\rangle, |-'\rangle\right\}$ basis, where $|+'\rangle = 1/\sqrt{2}\left(|0\rangle + i |1\rangle\right)$, and $|-'\rangle = 1/\sqrt{2}\left(|0\rangle - i |1\rangle\right)$.

Intuitively, the encoding of $(3,1)$-QRAC correspond to assigning the $8$ states to the corners of the unit cube inside the Bloch Sphere as depicted in the figure below.

Below is the code to create quantum circuits for performing experiments of $(3,1)$-QRAC. Similarly to the $(2,1)$-QRAC, each of the circuits consists of encoding $3$ bits of information into $1$ qubit and decoding either the first, the second, or the third bit by performing measurement on the qubit.


In [11]:
backend = Aer.get_backend('qasm_simulator') # the device to run on
shots = 1024    # the number of shots in the experiment 
from math import sqrt, cos, acos

#compute the value of theta
theta = acos(sqrt(0.5 + sqrt(3.0)/6.0))

#to record the u3 parameters for encoding 000, 010, 100, 110, 001, 011, 101, 111
rotationParams = {"000":(2*theta, pi/4, -pi/4), "010":(2*theta, 3*pi/4, -3*pi/4), 
                  "100":(pi-2*theta, pi/4, -pi/4), "110":(pi-2*theta, 3*pi/4, -3*pi/4), 
                  "001":(2*theta, -pi/4, pi/4), "011":(2*theta, -3*pi/4, 3*pi/4), 
                  "101":(pi-2*theta, -pi/4, pi/4), "111":(pi-2*theta, -3*pi/4, 3*pi/4)}

# Creating registers
# qubit for encoding 3 bits of information
qr = QuantumRegister(1)
# bit for recording the measurement of the qubit
cr = ClassicalRegister(1)

# dictionary for encoding circuits
encodingCircuits = {}
# Quantum circuits for encoding 000, ..., 111
for bits in rotationParams.keys():
    circuitName = "Encode"+bits
    encodingCircuits[circuitName] = QuantumCircuit(qr, cr, name=circuitName)
    encodingCircuits[circuitName].u3(*rotationParams[bits], qr[0])
    encodingCircuits[circuitName].barrier()

# dictionary for decoding circuits
decodingCircuits = {}
# Quantum circuits for decoding the first, second and third bit
for pos in ("First", "Second", "Third"):
    circuitName = "Decode"+pos
    decodingCircuits[circuitName] = QuantumCircuit(qr, cr, name=circuitName)
    if pos == "Second": #if pos == "First" we can directly measure
        decodingCircuits[circuitName].h(qr[0])
    elif pos == "Third":
        decodingCircuits[circuitName].u3(pi/2, -pi/2, pi/2, qr[0])
    decodingCircuits[circuitName].measure(qr[0], cr[0])

#combine encoding and decoding of QRACs to get a list of complete circuits
circuitNames = []
circuits = []
for k1 in encodingCircuits.keys():
    for k2 in decodingCircuits.keys():
        circuitNames.append(k1+k2)
        circuits.append(encodingCircuits[k1]+decodingCircuits[k2])

print("List of circuit names:", circuitNames) #list of circuit names


List of circuit names: ['Encode000DecodeFirst', 'Encode000DecodeSecond', 'Encode000DecodeThird', 'Encode010DecodeFirst', 'Encode010DecodeSecond', 'Encode010DecodeThird', 'Encode100DecodeFirst', 'Encode100DecodeSecond', 'Encode100DecodeThird', 'Encode110DecodeFirst', 'Encode110DecodeSecond', 'Encode110DecodeThird', 'Encode001DecodeFirst', 'Encode001DecodeSecond', 'Encode001DecodeThird', 'Encode011DecodeFirst', 'Encode011DecodeSecond', 'Encode011DecodeThird', 'Encode101DecodeFirst', 'Encode101DecodeSecond', 'Encode101DecodeThird', 'Encode111DecodeFirst', 'Encode111DecodeSecond', 'Encode111DecodeThird']

Now, we can perform various experiments of $(3,1)$-QRAC. Below, we execute all circuits of QRACs and plot some experimental results.


In [12]:
job = execute(circuits, backend=backend, shots=shots)
results = job.result()
print("Experimental Result of Encode010DecodeFirst")
# We should measure "0" with probability 0.78
plot_histogram(results.get_counts(circuits[circuitNames.index("Encode010DecodeFirst")]))
print("Experimental Result of Encode010DecodeSecond")
# We should measure "1" with probability 0.78
plot_histogram(results.get_counts(circuits[circuitNames.index("Encode010DecodeSecond")]))
print("Experimental Result of Encode010DecodeThird")
# We should measure "0" with probability 0.78
plot_histogram(results.get_counts(circuits[circuitNames.index("Encode010DecodeThird")]))


Experimental Result of Encode010DecodeFirst
Experimental Result of Encode010DecodeSecond
Experimental Result of Encode010DecodeThird

Now, we can perform various experiments of $(3,1)$-QRAC. Below, we execute all circuits of QRACs and plot some experimental results.


In [14]:
%%qiskit_job_status

# Use the IBM Quantum Experience
backend = least_busy(IBMQ.backends(simulator=False))

job_exp = execute(circuits, backend=backend, shots=shots)



In [15]:
results = job_exp.result()
print("Experimental Result of Encode010DecodeFirst")
plot_histogram(results.get_counts(circuits[circuitNames.index("Encode010DecodeFirst")]))
#We should measure "0" with probability 0.78
print("Experimental Result of Encode010DecodeSecond")
#We should measure "1" with probability 0.78
plot_histogram(results.get_counts(circuits[circuitNames.index("Encode010DecodeSecond")]))
print("Experimental Result of Encode010DecodeThird")
#We should measure "0" with probability 0.78
plot_histogram(results.get_counts(circuits[circuitNames.index("Encode010DecodeThird")]))


Experimental Result of Encode010DecodeFirst
Experimental Result of Encode010DecodeSecond
Experimental Result of Encode010DecodeThird

About Quantum Random Access Coding

The notion of Quantum RAC was first presented here along with the concrete example of $(2,1)$-QRAC which apparently was discovered much earlier in 1983 by Wiesner. The idea of the $(3,1)$-QRAC appeared in the classic book of Mike and Ike. The first experiments on $(2,1)$-QRAC and $(3,1)$-QRAC were presented here. It is not possible to perform $(4,1)$-QRAC as shown here.