Qiskitを用いて量子回路を作成し、量子デバイス実機で量子測定を行う
前提知識
量子力学、線形代数
準備
準備はAnacondaインストール→仮想環境の作成・Qiskitのインストールの順で行います。準備ができたらAnaconda-NavigatorあるいはVScodeのjupyternotebookを起動させてQiskitがインストールされている仮想環境を選択します。
Anacondaのインストールに関して
Anacondaインストール
仮想環境の作成とQiskitのインストールに関して
はじめに
Qiskitのインストール
JupyterNotebookに関して
JupyterNotebook
コンテンツ
0.イントロダクション
1.古典コンピュータでの量子回路のシュミレーション
2.量子デバイス実機での測定
0.イントロダクション
作りたい量子回路
今回作成したのは2量子ビット系で、HadamardゲートとCNOTゲートをからなるシンプルな量子回路です。量子測定を行う場合は最初に計算基底を0に初期化をし、量子ビットにゲートを作用させて得られた結果を古典ビットに保存して測定を行います。この測定を古典コンピュータでのシュミレーションとIBM Quantum(IBMQ)の量子デバイス実機で測定を行いました。
補足
qはqubit(量子ビット)の頭文字,cはclassical bit(古典ビット)の頭文字をとっています。古典ビットの$/^{2}$は古典ビットが2本まとめられていることを表しています。
1.古典コンピュータで量子回路のシュミレーション
import numpy as np
from qiskit import QuantumCircuit, transpile
from qiskit.providers.aer import QasmSimulator
from qiskit.visualization import plot_histogram
上から順にnumpy(モジュール)というpythonファイルを,qiskit(モジュール)からQuantumCircuit(クラス)とtranspile(関数)を、qiskit.providers.aer(モジュール)からQasmSimulator(クラス)を、qiskit.visualizationからplot_histogram(関数)を読み出しています。
参考:
モジュールについて
importについて
#2量子ビットと2古典ビットの回路のインスタンス生成
circuit = QuantumCircuit(2, 2)
# 量子ビットの初期化
circuit.reset([0,1])
#Hadamrdゲート
circuit.h(0)
# CNOTゲート
circuit.cx(0,1)
# ゲート操作の結果を古典レジスタへ保存する
circuit.measure([0,1], [0,1])
# 回路の描画
circuit.draw(output='mpl')
QuantumCircuit
QuantumCircuit(,)
のようにカンマで区切って、引数を(量子ビットの数,古典ビットの数)
という組みのタプルを取ります。今回のコードでは2つの量子ビットと2つの古典ビットからなる回路を作成しています。これを用いてcircuitという変数名を持つ2つの量子ビットと2つの古典ビットからなる回路をインスタンスとして生成しました。
参考:
QuantumCircuit
タプル
補足
QuantumCircuit()
はどうやらデフォルトで量子ビットを初期化しているので次のcircuit.reset([0,1])
は不要かもしれない。
量子回路の作成
h,cxはcircuit変数がもつメソッドです。これらは量子回路にゲート操作を適用するメソッドです。引数はこの場合0からの自然数でいま量子ビットは2つなので上から1番目の量子ビットを0、2番目の量子ビットを1としています。
測定と回路の描画
量子計算後の測定結果を古典レジスターに保存するためにcircuit変数のmeasureメソッドを用いています。引数は(量子ビット,古典ビット)
という組み合わせ、つまりタプルを引数として持っています。量子ビット=[0,1]
,古典ビット=[0,1]
のようなリストで引数を取ることで第1量子ビット→第1古典ビット
のように対応します。以上作りたい回路ができたので回路の描画をcircuit変数のdrawメソッドを用いています。引数はなくても問題ないですがoutput='mpl'
のようにキーワード引数としてmpl
を取ることでmatplotlib形式の図が出力されます。
蛇足
回路の描画でcircuit.draw().savefig("text.png")
のようにを加えると開いているディレクトリ上にpngファイルの図を保存してくれます。
# QasumSimulatorのインスタンス生成
simulator = QasmSimulator()
# transpile関数呼び出し
compiled_circuit = transpile(circuit, simulator)
# qasmシュミレータで回路を実行する。
job1 = simulator.run(compiled_circuit, shots=10000)
# 実行した結果
result1 = job1.result()
# コンパイル後の状態をカウントする
counts = result.get_counts(compiled_circuit)
print("\nTotal count for 00 and 11 are:",counts)
# ヒストグラムの描画
plot_histogram(counts,title='CLASSICAL QUANTUM SIMULATION')
QasmSimulator
複数のシュミレーション方法やその方法それぞれに対して設定可能なオプションに対応しています。実行時のオプションとして引数を辞書のようにとるrunメソッドを指定することができます。辞書というのは例えば次の辞書オブジェクトを考えます。
kudamono = {apple:りんご, orange:みかん}
これはappleと検索すればりんごが、orangeと検索すればみかんが出てきます。この検索する時に指定したデータ(apple,orange)をキー、検索の結果となるデータ(りんご、みかん)を値と呼びます。
参考
QasmSimulator
辞書
transpile
transpileは引数として単一の対応を持つインスタンスかリストが与えられます。
run
runメソッドはQasmSimulatorで説明したように辞書型の引数を取ります。しかし(,)はタプル型ですが、shots=10000
のようにキーワード引数を辞書型として取ることも可能です。
plot_histogram
実デバイスまたはqasm_simulator上で実行される量子回路からのデータを可視化するための関数です。
オプションなどはQiskit Visualizationに詳しく載っています。
量子計算
$$
\def\bra#1{\mathinner{\left\langle{#1}\right|}}
\def\ket#1{\mathinner{\left|{#1}\right\rangle}}
\def\braket#1#2{\mathinner{\left\langle{#1}\middle|#2\right\rangle}}
$$
シュミレーションの結果に移る前にどの状態がどんな確率で出るのかを計算します。
回路の初期の量子状態は
\ket{\Psi_{ini}}=\ket{0}_{q_0} \otimes \ket{0}_{q_1}
最終の状態を$\ket{\Psi_{fin}}$としてこれを求める。
\begin{align}
\ket{\Psi_{fin}}=
C_{NOT}H_{q_{0}}\ket{\Psi_{ini}}
&=
C_{NOT}\frac{1}{\sqrt{2}}(X_{q_{0}} + Z_{q_{0}})\ket{\Psi_{ini}}\\
&=
\frac{1}{\sqrt{2}}C_{NOT}(\ket{0}_{q_{0}} + \ket{1}_{q_{0}})\otimes\ket{0}_{q_{1}}\\
&=
\frac{1}{\sqrt{2}}(\ket{0}_{q_{0}}\bra{0} + \ket{1}_{q_{0}}\bra{1}\otimes X_{q_{1}})(\ket{0}_{q_{0}} + \ket{1}_{q_{0}})\otimes\ket{0}_{q_{1}}\\
&=
\frac{1}{\sqrt{2}}(\ket{0}_{q_{0}}\otimes\ket{0}_{q_1} + \ket{1}_{q_0}\otimes X_{q_{1}}\ket{0})\\
&=
\frac{1}{\sqrt{2}}(\ket{0}_{q_{0}}\otimes\ket{0}_{q_1} + \ket{1}_{q_0}\otimes \ket{1}_{q_{1}})\\
&=
\frac{1}{\sqrt{2}}(\ket{00} + \ket{11})
\end{align}
計算結果から状態00と11のいずれかが出るあるいは測定されることがわかります。それぞれの確率$p(00),p(11)$はBornの確率規則から次のように計算できる。
\begin{align}
p(00) = \left|\braket{00}{\Psi_{fin}} \right|^{2} = \frac{1}{2}\\
p(11) = \left|\braket{11}{\Psi_{fin}} \right|^{2} = \frac{1}{2}
\end{align}
シュミレーション結果
状態00と11のいずれも0.5に近い確率で結果が出ています。0.5からずれる理由としては試行回数を有限回数で行ったためです。
2.量子デバイス実機での測定
IBMQアカウント
IBMQのクラウド上に量子回路を送って量子デバイス実機での測定を行うのですが、その際IBMQの登録アカウント固有のAPIトークンが必要です。そのためにまずIBMQのアカウント登録をします。
#省略
from qiskit import QuantumCircuit, transpile, IBMQ, execute
from qiskit.tools.monitor import job_monitor
#省略
登録ができたら上のコードのように追加してください。ここで追加するというタイミングは問題ないです。最初に追加してもいいですし、そちらの方が面倒くさくないです。
IBMQ.save_account('YOUR_API_TOKEN')
IBMQ.load_account()
IBMQ.save_account('YOUR_API_TOKEN')
を実行するとqiskitrcというファイルにトークンの情報が保存されます。APIが変わらなければ、これをもう一度実行する必要はありません。もしAPIが変われば、引数にオプションとしてoverwirte=True
と記述してください。この次にIBMQ.load_account()
で先ほどの情報を読み込んでアカウントをロードします。
APIトークンは重要な情報なので外部には必ず漏らさないでください。
バックエンドの設定
provider = IBMQ.get_provider('ibm-q')
qcomp = provider.get_backend('ibmq_lima')
ここでは測定されたデータを何で処理するかを設定しています。get_providerの引数としてibm-q
をとっているのはIBMQを経由するためです。つぎにどの量子デバイス実機で処理するのかを設定しています。今回はlima
を選択しました。他にもデバイスはあるのですが6量子ビット以上のデバイスはアクセス権を獲得する必要があります。
描画
job2 = execute(circuit, backend=qcomp, shots=10000)
result2 = job2.result()
plot_histogram(result2.get_counts(circuit), title="REAL DEVICE MEASUREMENT")
測定結果
実機での量子測定を行うと01と10が測定されているのに加え、00と11が測定される確率が古典コンピュータのシュミレーション結果と比べてずれが大きいことが分かります。このようなエラーを起こす理由としては古典コンピュータと違って量子デバイスの物理的な問題からエラーを起こすからです。