どうも、量子の人を目指して頑張ってます原田です。
普段は量子の理論の方を学習しているので、コードを書くのは初だったのですが、予想以上に難しかった。というか、コードを書く=アプリケーションを開発するという認識しかなかったので、ビットを直接いじるというのはなかなか新鮮だった。
今回は以下ベル状態と呼ばれる4つの回路を作ってみます。
今のところまだベル状態が意味するものはいまいち分かっていないけど、2つの量子ビットがもつれている基底ベクトルというような理解。
環境
Google Colaboratoryで動かすとローカルで環境を構築しなくていいのでとても楽。
クラウド上の機械学習の実行環境というような説明をされているのを目にするが、とりあえずPythonが動いてくれる環境で、別に機械学習に関わらず使える。アプリケーションのコードを書いていくというより、小さいコードのブロックを実行していて、その結果を記録していくようなドキュメントとコーディングをセットで出来る環境って感じ。
まあいわゆるJupyter NotebookのGoogle版。
まずは必要なパッケージのインストールとインポートをしておく。ざっとコピペなので、今回必要ないのも混ざっているかもしれん。
!pip install qiskit>=1 !pip install pylatexenc !pip install qiskit-aer>=0.15
import numpy as np from qiskit_aer import Aer, AerSimulator from qiskit_aer.noise import NoiseModel, pauli_error from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit from qiskit.visualization import plot_histogram from qiskit import user_config from qiskit.quantum_info import partial_trace from IPython.display import Javascript sv_sim = Aer.get_backend('statevector_simulator') qasm_sim = Aer.get_backend('qasm_simulator')
ヘルパー関数
def show_state_vector(qc, use_statevector_only=False): qc_remove_measurement = qc.remove_final_measurements(inplace=False) statevector = sv_sim.run(qc_remove_measurement).result().get_statevector() display(statevector.draw(output='latex'))
まずは1量子ビットをいじくってみる
qc = QuantumCircuit(1) show_state_vector(qc)
実行結果:
1つの量子ビットを初期化しただけなので、状態が0の量子ビットだけがある。
qc = QuantumCircuit(1) qc.x(0) show_state_vector(qc)
実行結果:
Xゲートは量子ビットの状態の0を1に変え、1を0に変える操作。もともと0だったので、ビットが1になっている。
qc = QuantumCircuit(1) qc.h(0) show_state_vector(qc)
実行結果:
量子ビットに馴染みがないと、この辺からなんじゃこりゃあ!!ってなる。状態ベクトルの項が一つ増えているが、これがつまり「重ね合わせ」の状態で、この量子ビットは0でもあり1でもあるという状態を表現している。
qc.h()
はアダマールゲートというもので、量子ビットを重ね合わせ状態にする。
という状態ベクトルの、とは確率振幅というもので、という関係がある。要するに、二乗した値が、それぞれのビットになる確率を表している。このベクトル状態で言えば、この量子ビットが0,1になる確率は同じで50%ということになる。
2量子ビットに拡張する
さていよいよ2量子ビットにする。量子ビットが増えると、「量子もつれ」という概念が登場する。詳しくは説明が出来ないが、簡単に言うと、「一方の量子ビットの状態が、もう一方の量子ビットの状態に影響する」という状態。
qc = QuantumCircuit(2) show_state_vector(qc)
実行結果:
最も簡単な2量子ビットの状態。どちらも0である。
qc = QuantumCircuit(2) qc.x(0) show_state_vector(qc)
実行結果:
あれ?0番目の量子ビットをXゲートで反転させたはずなのに、右側の量子ビットが反転している。実はこれ、右側から0番目、その左隣が1番目という順序になっている模様。最初これが分からず1時間くらいいじくり回してようやく気付いた。直感と逆やろ。。なんでや。。
qc = QuantumCircuit(2) qc.h(0) show_state_vector(qc)
実行結果:
2量子ビットを初期化し、0番目(右側)の量子ビットにアダマールゲートをかけたので、右側のビットが0と1の重ね合わせになっていて、左側のビットは0固定のまま。
この状態ベクトルの読み方がちょっと慣れる必要があるんだけど、このケットベクトルが1量子ビットを表しているように見えるが違う。との右側の数字(つまり0と1)が0番目の量子ビットの状態で、左側の数字(つまり0と0)が1番目の量子ビットの状態を表している。
これでベル状態を作る準備が整ったのでやっていく。まずは1つ目。
qc = QuantumCircuit(2, 2) qc.h(0) qc.cx(0, 1) show_state_vector(qc)
実行結果:
先程の実行結果が だったので、0番目の量子ビットを制御ビットとして、1番目のビットを操作ビットとして、CNOTゲートをかける。CNOTゲートは、制御ビットが0ならば何もせず、1ならば操作ビットを反転させるという操作。
これで1つ目のベル状態が出来た。
qc = QuantumCircuit(2, 2) qc.h(0) qc.cx(0, 1) qc.z(0) show_state_vector(qc)
実行結果:
Zゲートを使うと位相反転出来るとのこと。いまいち位相反転について理解が出来ていないが、とりあえず状態ベクトルの符号が反転してくれるので、2つ目のベル状態が出来た。ちなみに、Zゲートは0番目の量子ビットでも1番目の量子ビットでも、どちらにかけても同じ結果になる(理屈はよく分かっていない)。
qc = QuantumCircuit(2, 2) qc.h(0) qc.cx(0, 1) qc.x(0) show_state_vector(qc)
実行結果:
1つ目のベル状態の0番目の重ね合わせ状態が反転しているものを作りたいので、0番目の量子ビットにXゲートをかければ出来る。
qc = QuantumCircuit(2, 2) qc.h(0) qc.cx(0, 1) qc.x(0) qc.z(0) show_state_vector(qc)
実行結果:
一つ前のベル状態の符号反転しているだけなので、ここもZゲートをかけるだけで符号反転出来る。ちなみに冒頭にあげた4つ目のベル状態と少し違う形をしているが等価らしい。うーん。確率振幅がとが等しいから認められそうだけど、もし違ったら等価じゃなくなりそう。知らんけど。
まとめ
というわけで4つのベル状態の量子回路を作ることが出来た。やってみた感想としては古典ビットの演算は、0と1がそのまま0と1として表現出来るので、直感でそのまま理解できるが、量子ビットは確率振幅かける状態の重ね合わせという形を取るので、やや直感的に理解するには時間がかかる印象。
あと状態ベクトルの表現は「ブロッホ球」というものがあって、XゲートやYゲート、Zゲートなどの操作が軸に対応していて、視覚的にわかりやすい表現もある。理論的にはたぶんなんかを球面座標系にマップして可視化してるんだろうけども、ちょっとちゃんと理解するには手間がかかりそうなので、今はやらないでおこう。