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')
# 一つ目の量子ビットにアダマールゲートを適用
qc.h(qr[0])
# qr0とqr1にCNOTゲートを適用
qc.cx(qr[0], qr[1]);
qc.draw(output='mpl')
# バックエンドシミュレータの準備
vector_sim = Aer.get_backend('statevector_simulator')
# バックエンドの準備
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')]
# シミュレータの実行
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}
# ヒストグラムの表示
from qiskit.visualization import plot_histogram
plot_histogram(hist)
# 結果のリストの取得
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
# 古典レジスタを持たない単一量子レジスタのみの回路
qc = QuantumCircuit(3)
# 数字を指定することで操作可能
qc.h(1)
qc.draw(output='mpl')
# 古典レジスタも同時に定義するには、二つ引数を設定する
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)
## 状態ベクトルの表示
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 states obtained by applying a Hadamard gate 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.