qiskitの使い方と単一の量子ビット

qiskitを利用して、量子アルゴリズムについて自分なりに勉強していこうと思います。 個人的な勉強の記録なので、説明などを大幅に省いている可能性があります。

qiskitのウェブサイト通りに勉強を進めています。

github

  • jupyter notebook形式のファイルはこちら

google colaboratory

  • google colaboratory で実行する場合はこちら

筆者の環境

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

基本的なライブラリをインポートしそのバージョンを確認しておきます。

%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}

Qiskitの文法の基礎

基礎から手を動かして実行し、操作を覚えていきます。

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

ベル状態を取得できました。 $$ \frac{|00\rangle+|11\rangle}{\sqrt{2}} $$

測定

測定するには、量子ビットの他に、古典レジスタが必要になります。

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

qc.draw(output='mpl')

確率振幅に基づいた統計値を得るために、測定回数を指定する。

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']

ビットの表記

ビットには右から左にラベルがついている。

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}

以下の様なバイナリ表現を表している。

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

簡略表記

# 古典レジスタを持たない単一量子レジスタのみの回路

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量子ビット

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)

初期状態の設定

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)

初期状態の設定 2

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)

とりあえず、量子回路の表示、ブロッホ球を用いた表示、状態ベクトルの表示、測定など一通り出来ました。

単一の量子ビットの操作

単一の量子ビットは以下の様に表現できます。$\theta$、$\phi$は実数です。

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

古典コンピュータのゲート

代表的な論理回路です。NOT回路以外は、二入力一出力の回路になります。 これらに相当する回路を量子回路でも実装する事になります。

AND回路

$$ \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回路

$$ \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回路

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

NAND回路

$$ \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回路

$$ \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回路

$$ \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} $$

パウリゲート

パウリゲートは古典ゲートのNOT回路に相当する物で、ただ、NOTとは言ってもX、Y、Z各軸に対してのNOTが存在します。 NOTとは言っても、各軸に対して$\pi$だけ回転させたビットになります。

Xゲート

$$ 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ゲート

$$ 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ゲート

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

アダマールゲート

xz平面において、$z=x$の直線を中心として$\pi$だけ回転させるゲートになります。

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

ブロッホベクトルで結果を見てみます。

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

$|0\rangle$,$|1\rangle$ にアダマールゲートを適用したものをそれぞれ、$|+\rangle$、$|-\rangle$と書きます。 これらはそれぞれY基底と言われる正規直交基底になります。

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

R_{\phi}ゲート

$R_{\phi}$ゲートは、Z軸を中心に$\phi$だけ回転させるゲートです。 行列で表すと以下の様になります。

$$ 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 \\ $$ の量子ビットに対して、上記の$R_{\phi}$を適用させます。

$$ \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} $$

となり、確かにZ軸に対して、$\phi$だけ回転していることが分かります。

実際に見てみる

以下の初期状態から、各ゲート演算子を作用させ、ブロッホベクトルがどのように変化するか見てみます。

$$ \left|q_0\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)
out = execute(qc,backend).result().get_statevector()
plot_bloch_multivector(out)

Xゲート

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ゲート

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)

Y軸に対しては何も変化がありません。固有ベクトルとなっています。

Zゲート

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ゲート

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}ゲート

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)

ちゃんとZ軸に$\displaystyle \frac{\pi}{4}$だけ回転されていることが分かります。

まとめ

とりあえず、基本的な使い方と1量子ビットの操作をqiskitを利用して理解してみました。 いやーとても便利です。視覚的にもわかりやすいし。 さらに勉強を進めて理解を深めようと思います。