SWAPゲート・制御SWAPゲート
先日アダマールゲートを解説したので続いてSWAPテストを解説したいのですが、その前にSWAPゲートと制御SWAPゲートを解説します。
対象とする読者
- 量子プログラミングに興味を持ち始めたばかりのエンジニア
- 量子計算に興味を持ち始めたばかりの高校生・大学生
- 数学・論理演算・電子回路に慣れていない方
など
前提とする知識
- 簡単な論理演算(AND, OR, NOT)が分かること
- 基本的な量子ゲート(X, CX, CCX)が分かること
バージョン情報
- Python 3.9.13
- Qiskit 0.36.2
目次
SWAPゲート
SWAPゲート実装
英単語「swap」の意味は「交換する」という動詞です。
2つの量子ビットの値を交換するというゲートです。
下図のようにCXゲートを3つ組み合わせます。
この時の入出力の対応表は以下になっております。
入力 | 出力 | ||
---|---|---|---|
量子ビット1(q1) | 量子ビット2(q2) | 量子ビット1(q1) | 量子ビット2(q2) |
0 | 0 | 0 | 0 |
0 | 1 | 1 | 0 |
1 | 0 | 0 | 1 |
1 | 1 | 1 | 1 |
それぞれの入力を用いて出力を測定した結果が下図です。
図のキャプションに書いた通りグラフのx軸に書かれたビット列は右から第一量子ビットで読みます。 そうすると入力のビット列と出力のビット列が交換されていることが図の(2)(3)を読んでいただくとわかっていただけると思います。
この時のソースは以下です。
qrs = [ [0, 0], [0, 1], [1, 0], [1, 1] ] fig_result, axes = plt.subplots(2, 2, figsize=(10, 10)) for idx, (q1, q2) in enumerate(qrs): qr_1 = QuantumRegister(1, 'q1') qr_2 = QuantumRegister(1, 'q2') cr = ClassicalRegister(2) qc = QuantumCircuit(qr_1, qr_2, cr) if q1 == 1: qc.x(qr_1) if q2 == 1: qc.x(qr_2) qc.barrier() qc.cx(qr_1, qr_2) qc.cx(qr_2, qr_1) qc.cx(qr_1, qr_2) qc.barrier() qc.measure(qr_1, cr[0]) qc.measure(qr_2, cr[1]) fig, ax = plt.subplots(1, 1) qc.draw(output='mpl', ax=ax) plt.show() plt.close() backend = Aer.get_backend('qasm_simulator') job = execute(qc, backend, shot=1024) result = job.result() plt.figure(fig_result) plot_histogram(result.get_counts(), ax=axes[q1][q2]) axes[q1][q2].set_title(f'({idx+1}) 入力が|{q1}{q2}>の場合', fontsize=12) plt.figure(fig_result) plt.tight_layout() plt.show() plt.close()
重ね合わせ状態のSWAP
重ね合わせ状態もその係数ごと交換されます。 つまり
ができます。
入力の状態が交換されて、出力は以下の式に対応する確率になっていれば良いです。
ビット列の読み順は式とグラフとで逆順なのでグラフ上でのが式上のに対応していて確率振幅0.75の自乗で約0.56、グラフ上のが式上のに対応していて確率振幅0.25の自乗で約0.05、とはそのままで確率振幅0.43の自乗で0.18に近いので、計算通りでしょう。
ということで、重ね合わせ状態もSWAPできることを確かめました。
この時のソースです。
qr_1 = QuantumRegister(1, 'q1') qr_2 = QuantumRegister(1, 'q2') cr = ClassicalRegister(2) qc = QuantumCircuit(qr_1, qr_2, cr) qc.ry(np.pi / 3, qr_1) qc.ry(2 * np.pi / 3, qr_2) qc.cx(qr_1, qr_2) qc.cx(qr_2, qr_1) qc.cx(qr_1, qr_2) qc.barrier() qc.measure(qr_1, cr[0]) qc.measure(qr_2, cr[1]) qc.draw(output='mpl') backend = Aer.get_backend('qasm_simulator') job = execute(qc, backend, shot=1024) result = job.result() plot_histogram(result.get_counts())
複数量子ビットへのSWAPゲート実装
感覚的にはこういう交換をしたいですね。
これを行うには互いの量子ビットのiビット目を交換すれば良いです。 従って下図のような量子回路になります。
SWAPが起きるか確認した結果です。
第一量子ビットの状態がで第二量子ビットの状態がになっていることを示しています。 よってちゃんと思った通りにSWAPできていますね。
この時のソースです。
qr_1 = QuantumRegister(3, 'q1') qr_2 = QuantumRegister(3, 'q2') cr = ClassicalRegister(6) qc = QuantumCircuit(qr_1, qr_2, cr) qc.x(qr_2[1]) qc.x(qr_2[2]) qc.barrier() qc.cx(qr_1[0], qr_2[0]) qc.cx(qr_2[0], qr_1[0]) qc.cx(qr_1[0], qr_2[0]) qc.barrier() qc.cx(qr_1[1], qr_2[1]) qc.cx(qr_2[1], qr_1[1]) qc.cx(qr_1[1], qr_2[1]) qc.barrier() qc.cx(qr_1[2], qr_2[2]) qc.cx(qr_2[2], qr_1[2]) qc.cx(qr_1[2], qr_2[2]) qc.barrier() qc.measure(qr_1, cr[:len(qr_1)]) qc.measure(qr_2, cr[len(qr_1):]) qc.draw(output='mpl') backend = Aer.get_backend('qasm_simulator') job = execute(qc, backend, shot=1024) result = job.result() plot_histogram(result.get_counts())
制御SWAPゲート
感覚的にはこんな感じですよね。
SWAPゲートの中身はCXゲートなので、CXがかかるのに必要な制御量子ビットを一つ増やすことで実装可能です。
SWAPが起きることを確認します。
まずはSWAPが起きない場合です。
入力状態が (第一量子ビットが制御量子ビット)に対して、測定結果はのため、SWAPは起きていません。
この時のソースです。
qr_control = QuantumRegister(1, 'control') qr_1 = QuantumRegister(1, 'q1') qr_2 = QuantumRegister(1, 'q2') cr = ClassicalRegister(3) qc = QuantumCircuit(qr_control, qr_1, qr_2, cr) qc.x(qr_1) qc.barrier() qc.ccx(qr_control, qr_1, qr_2) qc.ccx(qr_control, qr_2, qr_1) qc.ccx(qr_control, qr_1, qr_2) qc.barrier() qc.measure(qr_control, cr[0]) qc.measure(qr_1, cr[1]) qc.measure(qr_2, cr[2]) qc.draw(output='mpl') backend = Aer.get_backend('qasm_simulator') job = execute(qc, backend, shot=1024) result = job.result() plot_histogram(result.get_counts())
そしてSWAPが起きる場合です。
入力状態が (第一量子ビットが制御量子ビット)に対して、測定結果はのため、SWAPが起きています。
この時のソースです。
qr_control = QuantumRegister(1, 'control') qr_1 = QuantumRegister(1, 'q1') qr_2 = QuantumRegister(1, 'q2') cr = ClassicalRegister(3) qc = QuantumCircuit(qr_control, qr_1, qr_2, cr) qc.x(qr_control) qc.x(qr_1) qc.barrier() qc.ccx(qr_control, qr_1, qr_2) qc.ccx(qr_control, qr_2, qr_1) qc.ccx(qr_control, qr_1, qr_2) qc.barrier() qc.measure(qr_control, cr[0]) qc.measure(qr_1, cr[1]) qc.measure(qr_2, cr[2]) qc.draw(output='mpl') backend = Aer.get_backend('qasm_simulator') job = execute(qc, backend, shot=1024) result = job.result() plot_histogram(result.get_counts())
以上より、制御SWAPゲートの動作を確認できました。
効率の良い制御SWAPゲート
よく考えたら制御SWAPゲートは以下で簡単に実装できました。
ソースは割愛しますが、最初と最後のCCXをただのCXに直しました。
CCXや2つ以上の制御量子ビットを持つ制御ユニタリーゲートは実際には多数の制御ユニタリーゲートに分解して実行されます。 (これは本質的には物理では2体問題しか扱えないからですが、詳細は省きます。)
この事情によって、ソフトウェア的にはどちらでも良い制御SWAPゲートの実装ですが、ハードウェアの事情を加味するとゲートの数が増えるほどゲートエラーが発生するリスクが高まります。
実際に比較してみました。
not used
と記した量子ビットは実際には未使用ということではなく、実行するibmq_quito
の1
の量子ビットを想定して、ancilaとして使われる想定です。
ただ、実行したjobを確認すると思ったようには動いてなさそうなので、transplieの仕組みを理解する必要があります。
ひとまず実行結果を確認すると期待していた状態の確率は後者の回路の方が高いことがわかりました。 やはり、なるべくゲート数を減らすことが重要であることがわかりました。
嶋田さんの本 を読むとこの回路はFredkinゲートというそうです。
まとめ
- SWAPゲートは入力した量子ビットの状態を交換する
- 重ね合わせ状態の確率振幅も交換できる
- 複数量子ビットのSWAPゲートは対応する量子ビット同士でそれぞれSWAPゲートを実行する
- 制御SWAPゲートは制御量子ビットの状態に応じてSWAPゲートの実行を切り替える
- 制御SWAPゲートはFredkinゲートで実装できる
次回はこのSWAPゲート・制御SWAPゲートを使ってスワップテストを解説します。