量子コンピュータで行う論理演算(3)
前回はMultiple Controlled Toffoli(MCT)を使って複数の条件のANDゲートの実装を紹介しました。
今回もCXゲート関連のテクニックの紹介です。
これで基本事項の紹介は最後にして次回からはちょっとした計算を紹介したいと思います。
今回は通常のプログラミングで
if not A: 処理
という場合の量子回路の作り方をご紹介します。
対象とする読者
- 量子プログラミングに興味を持ち始めたばかりのエンジニア
- 量子計算に興味を持ち始めたばかりの高校生・大学生 など
前提とする知識
- 論理演算(AND, OR, NOT)が分かること
- 基本的な量子ゲート(X, CX, CCX)が分かること
お断り
僕は基本Qiskitしか使わないので解説ソースもQiskitで書きます。
Q#やIonQなど他の言語・フレームワーク・ハードウェアに興味ある人には申し訳ないですが、考え方は共通していると思うのでご参考程度に、ということでお願いします。
バージョン情報
- Python 3.7.3
- Qiskit 0.23.1
目次
量子回路でif not A
を実装する方法
先ほどご紹介しましたが、下記のような場合です。
if not A: 処理
実装方法
先に結論を述べますと、下記のようにCXゲート
の制御ビットをXゲート
で挟めばよいです。
qr_input = QuantumRegister(1, 'input') qr_output = QuantumRegister(1, 'output') cr = ClassicalRegister(2) qc = QuantumCircuit(qr_input, qr_output, cr) qc.x(qr_input) qc.cx(qr_input, qr_output) qc.x(qr_input) qc.barrier() qc.measure(qr_input, cr[:1]) qc.measure(qr_output, cr[1]) qc.reverse_bits() qc.draw(output='mpl')
見やすさのためにqc.barrier()
を追加していますが、計算上は不要です。
この測定結果は、下記のように制御ビット(ビット列の右側)が0
の時に標的ビット(ビット列の左側)が1
になります。
したがって、if文の条件に対応する制御ビットの値が0
の時に条件が満たされてCXゲートが機能しているということです。
解説
「制御ビットの端子(黒丸)の直前でXゲートがかかって標的ビットが1
になるんだから、CXゲートがかかって当然じゃん」
と思った方、その通りです。
それを利用しています。
この図の読み方を説明します。
この図は2つのXゲート
と制御ビットの黒丸を1セットで読みます。
Xゲート - 黒丸 - Xゲート
で一つの端子と思ってまとめて白丸に置き換えます。
その直前の状態がこの新しい端子への入力で、中でどんなゲートがかかるかはブラックボックスとして読んでください。
そうすると、
- 制御ビットは状態で初期化
- 新しいCXゲートの端子(制御ビットの状態がの状態で標的ビットを反転する)
- CXゲートを過ぎた後の制御ビットの状態はCXゲートに入る前の状態()のまま
という挙動になります。
ブラックボックスと書きましたが、左側のXゲートで反転させられた制御ビットの状態は右側のXゲートで元に戻すので、CXゲートはかかるけどXゲートはなかったことになります。
(実機ではXゲートを掛けている分ゲートエラーは乗りますが、数学上は影響なしです。)
※なお、この「元に戻す」というのは用語としてはUncomputation・Uncomputing(逆計算)
といいます。
端子での値が1(True)
の時に条件を満たすのが黒丸
に対して、端子での値が0(False)
の時に条件を満たすのが白丸
ということで、役割が逆になっています。
この対応は量子計算のバイブルNielsen-Chuangでもちゃんと(しれっと)紹介されています。 *1
現在出回っている教科書では当たり前すぎるのか白丸については解説無いままのことが多いと思います。
量子回路でif not A and not B
のような複数の条件を実装する方法
すべてnotの場合
下記のような場合です。
if not A and not B: 処理
これは1つの場合のCXゲート
をCCXゲート
に置き換えればよいです。
qr_input = QuantumRegister(2, 'input') qr_output = QuantumRegister(1, 'output') cr = ClassicalRegister(3) qc = QuantumCircuit(qr_input, qr_output, cr) qc.x(qr_input) qc.ccx(qr_input[0], qr_input[1], qr_output) qc.x(qr_input) qc.barrier() qc.measure(qr_input, cr[:2]) qc.measure(qr_output, cr[2]) qc.reverse_bits() qc.draw(output='mpl')
この結果は下記で、標的ビットが反転しています。
3つ以上の場合はMCTを使います。
if not A and not B and not C: 処理
qr_input = QuantumRegister(3, 'input') qr_output = QuantumRegister(1, 'output') cr = ClassicalRegister(4) qc = QuantumCircuit(qr_input, qr_output, cr) qc.x(qr_input) qc.mct(qr_input, qr_output) qc.x(qr_input) qc.barrier() qc.measure(qr_input, cr[:3]) qc.measure(qr_output, cr[3]) qc.reverse_bits() qc.draw(output='mpl')
notが一部混ざっている場合
下記のような場合です。
if not A and B and not C: 処理
AとCがFalse(0)で、BがTrue(1)の時にif文の条件が満たされてif文の中の処理が実行されます。
これはソースは若干書くのが手間ですが、not xx
になる端子だけXゲート
を掛けます。
qr_input = QuantumRegister(3, 'input') qr_output = QuantumRegister(1, 'output') cr = ClassicalRegister(4) qc = QuantumCircuit(qr_input, qr_output, cr) qc.x([qr_input[0], qr_input[2]]) qc.mct(qr_input, qr_output) qc.x([qr_input[0], qr_input[2]]) qc.barrier() qc.measure(qr_input, cr[:3]) qc.measure(qr_output, cr[3]) qc.reverse_bits() qc.draw(output='mpl')
これはこのまま実行しても第2制御ビットが条件を満たさないので標的ビットは反転しません。
標的ビットを反転させるには下記のようにします。
qr_input = QuantumRegister(3, 'input') qr_output = QuantumRegister(1, 'output') cr = ClassicalRegister(4) qc = QuantumCircuit(qr_input, qr_output, cr) qc.x(qr_input[1]) #|010>状態を入力とするということ qc.barrier() qc.x([qr_input[0], qr_input[2]]) qc.mct(qr_input, qr_output) qc.x([qr_input[0], qr_input[2]]) qc.barrier() qc.measure(qr_input, cr[:3]) qc.measure(qr_output, cr[3]) qc.reverse_bits() qc.draw(output='mpl')
見やすさのために、MCTの前にもqc.barrier()
を追加しています。
ソースにコメントしていますが、この回路はMCTゲートへの入力はとしていて、標的ビットがちゃんと反転していることが下記の測定結果からも分かります。
まとめ
if not A
の量子回路での実装は、制御ビットの端子をXゲート
で挟む。- 複数の端子がある場合は、
not
が対応する端子だけXゲート
で挟む。
*1:Quantum Computation and Quantum Information (10th anniversary edition), 2010, M. A. Nielsen and I. L. Chuang, p185, Figure 4.11