How to use qiskit and a single qubit

I am going to use qiskit to study quantum algorithms in my own way. Since this is a record of my personal study, I may have left out a lot of explanations.

I am following the qiskit website.

github

  • The file in jupyter notebook format is here

google colaboratory

  • To run it in google colaboratory here

Author’s environment

!sw_vers
ProductName: Mac OS X
ProductVersion: 10.14.6
BuildVersion: 18G103
Python -V
Python 3.8.5

Import the basic libraries and check their versions.

%matplotlib inline
%config InlineBackend.figure_format = 'svg'

import matplotlib
import matplotlib.pyplot as plt
import scipy
import numpy as np
import pandas as pd

print('matplotlib version :', matplotlib.__version__)
print('scipy version :', scipy.__version__)
print('numpy version :', np.__version__)
print('pandas version :', pd.__version__)
matplotlib version : 3.3.2
scipy version : 1.5.2
numpy version : 1.19.2
pandas version : 1.1.3
import qiskit
import json

dict(qiskit.__qiskit_version__)
{'qiskit-terra': '0.17.4',
 'qiskit-aer': '0.8.2',
 'qiskit-ignis': '0.6.0',
 'qiskit-ibmq-provider': '0.13.1',
 'qiskit-aqua': '0.9.1',
 'qiskit': '0.26.2',
 'qiskit-nature': None,
 'qiskit-finance': None,
 'qiskit-optimization': None,
 'qiskit-machine-learning': None}

Basics of Qiskit syntax

We will run through the basics by hand and learn the operations.

qc = QuantumCircuit()
qr = QuantumRegister(2, 'qreg')
qc.add_register(qr)
qc.qregs
[QuantumRegister(2, 'qreg')].
qc.draw(output='mpl')
# Apply an adamantine gate to the first qubit
qc.h(qr[0])

# Apply CNOT gate to qr0 and qr1
qc.cx(qr[0], qr[1]);
qc.draw(output='mpl')
# Prepare the backend simulator
vector_sim = Aer.get_backend('statevector_simulator')
# Prepare the backend
Aer.backends()
[AerSimulator('aer_simulator'),
 AerSimulator('aer_simulator_statevector'),
 AerSimulator('aer_simulator_density_matrix'),
 AerSimulator('aer_simulator_stabilizer'),
 AerSimulator('aer_simulator_matrix_product_state'),
 AerSimulator('aer_simulator_extended_stabilizer'),
 AerSimulator('aer_simulator_unitary'),
 AerSimulator('aer_simulator_superop'),
 QasmSimulator('qasm_simulator'),
 StatevectorSimulator('statevector_simulator'),
 UnitarySimulator('unitary_simulator'),
 PulseSimulator('pulse_simulator')]
# Run the simulator
job = execute(qc, vector_sim)
job.result().get_statevector()
array([0.70710678+0.j, 0. +0.j, 0. +0.j, 0.70710678+0.j])

Now we can get the bell state. $$ \frac{|00\rangle+|11\rangle}{\sqrt{2}} $$

Measure

To measure, we need a classical register as well as a qubit.

cr = ClassicalRegister(2,'creg')
qc.add_register(cr)
qc.measure(qr[0], cr[0])
qc.measure(qr[1], cr[1])

qc.draw(output='mpl')

Specify the number of measurements to get statistics based on the probability amplitude.

emulator = Aer.get_backend('qasm_simulator')
job = execute(qc, emulator, shots=8192)
hist = job.result().get_counts()
print(hist)
{'00': 4006, '11': 4186}
# Display the histogram
from qiskit.visualization import plot_histogram

plot_histogram(hist)
# Get the list of results
job = execute(qc, emulator, shots=10, memory=True)
samples = job.result().get_memory()
print(samples)
['11', '11', '11', '00', '00', '11', '11', '00', '11', '00', '00']

Notation for bits

Bits are labeled from right to left.

qubit = QuantumRegister(8)
bit = ClassicalRegister(8)
circuit = QuantumCircuit(qubit,bit)

circuit.x(qubit[7])
circuit.measure(qubit,bit) # this is a way to do all the qc.measure(qr8[j],cr8[j]) at once

execute(circuit, emulator, shots=8192).result().get_counts()
{'10000000': 8192}

This represents a binary representation like the following.

$$ b_{n-1} b_{n-2} \ldots b_{1} b_{0}=\sum_{j} b_{j} 2^{j} $$

simplified notation

### A single quantum register only circuit with no classical registers

qc = QuantumCircuit(3)
# Can be manipulated by specifying a number

qc.h(1)
qc.draw(output='mpl')
# To define a classical register at the same time, set two arguments

qc = QuantumCircuit(2,1)

qc.h(0)
qc.cx(0,1)
qc.measure(1,0)

qc.draw(output='mpl')

1 qubit

from qiskit import QuantumCircuit, execute, Aer
from qiskit.visualization import plot_histogram, plot_bloch_vector
from math import sqrt, pi

backend = Aer.get_backend('statevector_simulator')
qc = QuantumCircuit(1)
qc.draw('mpl')
result = execute(qc, backend).result()
out_state = result.get_statevector()
plot_bloch_multivector(out_state)
## Display the state vector
out_state
array([0.+0.j, 1.+0.j])
counts = result.get_counts()
plot_histogram(counts)

Set the initial state

You can set the initial state with initial_state

$$ |q\rangle=|1\rangle $$

qc = QuantumCircuit(1)
initial_state = [0,1].
qc.initialize(initial_state, 0)
qc.draw('mpl')
result = execute(qc,backend).result()
out_state = result.get_statevector()
plot_bloch_multivector(out_state)
out_state
array([0.+0.j, 1.+0.j])
counts = result.get_counts()
plot_histogram(counts)

Setting the initial state 2

You can set the initial state with initial_state

$$ \left|q\right\rangle=\frac{1}{\sqrt{2}}|0\rangle+\frac{i}{\sqrt{2}}|1\rangle $$

qc = QuantumCircuit(1)
initial_state = [1/sqrt(2), complex(0, 1/sqrt(2))]]
qc.initialize(initial_state, 0)
qc.draw('mpl')
result = execute(qc,backend).result()
out_state = result.get_statevector()
plot_bloch_multivector(out_state)
out_state
array([0.70710678+0.j , 0. +0.70710678j])
counts = result.get_counts()
plot_histogram(counts)

For now, we can display the quantum circuit, use the Bloch sphere, display the state vector, and make measurements.

Manipulating a single qubit

A single qubit can be represented as follows. where $\theta$ and $\phi$ are real numbers.

$$ |q\rangle=\cos \left(\frac{\theta}{2}\right)|0\rangle+e^{i \phi}\sin \left(\frac{\theta}{2}\right)|1\rangle $$

Gates in classical computers

These are typical logic circuits, except for the NOT circuit, which is a two-input, one-output circuit. We will implement these equivalent circuits in our quantum circuit.

AND circuit

$$ \begin{array}{|c|c||c|} \hline A & B & Z \\ \hline 0 & 0 & 0 \\ \hline 0 & 1 & 0 \\ \hline 1 & 0 & 0 \\ \hline 1 & 1 & 1 \\ \hline \end{array} $$

OR circuit

$$ \begin{array}{|c|c||c|} \hline A & B & Z \\ \hline 0 & 0 & 0 \\ \hline 0 & 1 & 1 \\ \hline 1 & 0 & 1 \\ \hline 1 & 1 & 1 \\ \hline \end{array} $$

NOT circuit

$$ \begin{array}{|c||c|} \hline A & Z \\ \hline 0 & 1 \\ \hline 1 & 0 \\ \hline \end{array} $$

NAND circuit

$$ \begin{array}{|c|c||c|} \hline A & B & Z \\ \hline 0 & 0 & 1 \\ \hline 0 & 1 & 1 \\ \hline 1 & 0 & 1 \\ \hline 1 & 1 & 0 \\ \hline \end{array} $$

NOR circuit

$$ \begin{array}{|c|c||c|} \hline A & B & Z \\ \hline 0 & 0 & 1 \\ \hline 0 & 1 & 0 \\ \hline 1 & 0 & 0 \\ \hline 1 & 1 & 0 \\ \hline \end{array} $$

XOR circuit

$$ \begin{array}{|c|c||c|} \hline A & B & Z \\ \hline 0 & 0 & 0 \\ \hline 0 & 1 & 1 \\ \hline 1 & 0 & 1 \\ \hline 1 & 1 & 0 \\ \hline \end{array} $$

Pauli Gate

The Pauli gate is the equivalent of the classical gate NOT circuit, except that there is a NOT for each of the X, Y, and Z axes. There is a NOT for each of the X, Y, and Z axes, but the bit is rotated by $\pi$ for each axis.

X gate

$$ X=\left(\begin{array}{ll} 0 & 1 \\ 1 & 0 \end{array}\right)=|0\rangle\langle 1|+| 1\rangle\langle 0| $$

$$ X|0\rangle=\left(\begin{array}{ll} 0 & 1 \\ 1 & 0 \end{array}\right)\left(\begin{array}{l} 1 \\ 0 \end{array}\right)=\left(\begin{array}{l}) 0 \\ 1 \end{array}\right)=|1\rangle $$

$$ X|1\rangle=\left(\begin{array}{ll} 0 & 1 \\ 1 & 0 \end{array}\right)\left(\begin{array}{l} 0 \\ 1 \end{array}\right)=\left(\begin{array}{l}) 1 \\ 0 \end{array}\right)=|0\rangle $$

Y-gate

$$ Y=\left(\begin{array}{cc} 0 & -i \\ i & 0 \end{array}\right)=i|0\rangle\langle 1|-i| 1\rangle\langle 0| $$

$$ Y|1\rangle=\left(\begin{array}{ll} 0 & -i \\ i & 0 \end{array}\right)\left(\begin{array}{l} 1 \\ 0 \end{array}\right)=i\left(\begin{array}{l} 0 \\ 1 \end{array}\right)=i|0\rangle $$

$$ Y|0\rangle=\left(\begin{array}{ll} 0 & -i \\ i & 0 \end{array}\right)\left(\begin{array}{l} 0 \\ 1 \end{array}\right)=-i\left(\begin{array}{l} 1 \\ 0 \end{array}\right)=-i|1\rangle $$

Z-gate

$$ Z=\left(\begin{array}{cc} 1 & 0 \\ 0 & -1 \end{array}\right)=|0\rangle\langle 0|-| 1\rangle\langle 1| $$

$$ Z|1\rangle=\left(\begin{array}{ll} 1 & 0 \\ 0 & -1 \end{array}\right)\left(\begin{array}{l} 1 \\ 0 \end{array}\right)=\left(\begin{array}{l}) 1 \\ 0 \end{array}\right)=|1\rangle $$

$$ Z|0\rangle=\left(\begin{array}{ll} 1 & 0 \\ 0 & -1 \end{array}\right)\left(\begin{array}{l} 0 \\ 1 \end{array}\right)=\left(\begin{array}{l}) 0 \\ -1 \end{array}\right)=|0\rangle $$

Adamar gate

In the xz-plane, the gate will be rotated by $\pi$ around the $z=x$ line.

$$ H=\frac{1}{\sqrt{2}}\left(\begin{array}{cc} 1 & 1 \\ 1 & -1 \end{array}\right) $$

Let’s look at the result with the Bloch vector.

qc = QuantumCircuit(1)
qc.h(0)
out = execute(qc,backend).result().get_statevector()
plot_bloch_multivector(out)

The adamantine gates applied to $|0\rangle$ and $|1\rangle$ are written as $|+\rangle$ and $|-\rangle$, respectively. These are the orthonormal bases, called Y-bases, respectively.

$$ \begin{aligned} &H|0\rangle=|+\rangle \\ &H|1\rangle=|-\rangle \end{aligned} $$

R_{\phi}Gate

The $R_{\phi}$ gate is a gate that rotates around the Z axis by $\phi$. In matrix form, it looks like this.

$$ R_{\phi}=\left(\begin{array}{cc} 1 & 0 \\ 0 & e^{i \phi} \end{array}\right) $$

$$ \cos \frac{\theta}{2}|0\rangle+e^{i \varphi} \sin \frac{\theta}{2}|1\rangle \\ $$ Let us apply the above $R_{\phi}$ to the qubit of

$$ \begin{aligned} & R_{\phi} \left(\cos \frac{\theta}{2}|0\rangle+e^{i \varphi} \sin \frac{\theta}{2}|1\rangle\right) \\ =&\left.\left(\begin{array}{ll} 1 & 0 \\ 0 & e^{i\phi} \end{array}\right)\left(\cos \frac{\theta}{2} \left(\begin{array}{l} 1 \\ 0 \end{array}\right)\right)+e^{i \varphi} \sin \frac{\theta}{2}\left(\begin{array}{l} 0 \\ 1 \end{array}\right)\right) \\ =& \cos \frac{\theta}{2}\left(\begin{array}{l} 1 \\ 0 \end{array}\right)+e^{i(\varphi+\phi)} \sin \frac{\theta}{2}\left(\begin{array}{l} 0 \\ 1 \end{array}\right) \end{aligned} $$

So, we can see that it is indeed rotated by $\phi$ with respect to the Z axis.

Let’s see it in action.

From the following initial state, let’s act on each gate operator and see how the Bloch vector changes.

$$ \left|q_0\right\rangle=\frac{1}{\sqrt{2}}|0\rangle+\frac{i}{\sqrt{2}}|1\rangle $$

Initial state

qc = QuantumCircuit(1)
initial_state = [1/sqrt(2), complex(0, 1/sqrt(2))]]
qc.initialize(initial_state, 0)
out = execute(qc,backend).result().get_statevector()
plot_bloch_multivector(out)

X-gates

qc = QuantumCircuit(1)
initial_state = [1/sqrt(2), complex(0, 1/sqrt(2))].
qc.initialize(initial_state, 0)
qc.x(0)
out = execute(qc,backend).result().get_statevector()
plot_bloch_multivector(out)

Y-gate

qc = QuantumCircuit(1)
initial_state = [1/sqrt(2), complex(0, 1/sqrt(2))].
qc.initialize(initial_state, 0)
qc.y(0)
out = execute(qc,backend).result().get_statevector()
plot_bloch_multivector(out)

There is no change for the Y axis. It is an eigenvector.

Z-gate

qc = QuantumCircuit(1)
initial_state = [1/sqrt(2), complex(0, 1/sqrt(2))].
qc.initialize(initial_state, 0)
qc.z(0)
out = execute(qc,backend).result().get_statevector()
plot_bloch_multivector(out)

H-gate

qc = QuantumCircuit(1)
initial_state = [1/sqrt(2), complex(0, 1/sqrt(2))].
qc.initialize(initial_state, 0)
qc.h(0)
out = execute(qc,backend).result().get_statevector()
plot_bloch_multivector(out)

R_{\phi}gate

qc = QuantumCircuit(1)
initial_state = [1/sqrt(2), complex(0, 1/sqrt(2))]]
qc.initialize(initial_state, 0)
qc.rz(pi/4, 0)
out = execute(qc,backend).result().get_statevector()
plot_bloch_multivector(out)

You can see that it has been properly rotated on the Z axis by $\displaystyle \frac{\pi}{4}$.

Summary

For now, I’ve tried to understand the basic usage and manipulation of one qubit using qiskit. Well, it is very useful. It’s also easy to understand visually. I’m going to study it further to deepen my understanding.