科学しよう

量子計算のプログラミングの解説をメインに、データサイエンス・機械学習について勉強したことをご紹介します

MENU

量子コンピュータで行う論理演算(3)

前回はMultiple Controlled Toffoli(MCT)を使って複数の条件のANDゲートの実装を紹介しました。
今回もCXゲート関連のテクニックの紹介です。
これで基本事項の紹介は最後にして次回からはちょっとした計算を紹介したいと思います。

今回は通常のプログラミングで

if not A: 
    処理

という場合の量子回路の作り方をご紹介します。

対象とする読者

  • 量子プログラミングに興味を持ち始めたばかりのエンジニア
  • 量子計算に興味を持ち始めたばかりの高校生・大学生 など

前提とする知識

  • 論理演算(AND, OR, NOT)が分かること
  • 基本的な量子ゲート(X, CX, CCX)が分かること

お断り
僕は基本Qiskitしか使わないので解説ソースもQiskitで書きます。
Q#やIonQなど他の言語・フレームワーク・ハードウェアに興味ある人には申し訳ないですが、考え方は共通していると思うのでご参考程度に、ということでお願いします。

バージョン情報

目次

量子回路で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')

f:id:sakumadaisuke32:20210131181542p:plain
図1. if not Aを判断する量子回路

見やすさのためにqc.barrier()を追加していますが、計算上は不要です。

この測定結果は、下記のように制御ビット(ビット列の右側)が0の時に標的ビット(ビット列の左側)が1になります。
したがって、if文の条件に対応する制御ビットの値が0の時に条件が満たされてCXゲートが機能しているということです。

f:id:sakumadaisuke32:20210131181642p:plain
図2. 図1の回路の量子状態の分布

解説

「制御ビットの端子(黒丸)の直前でXゲートがかかって標的ビットが1になるんだから、CXゲートがかかって当然じゃん」
と思った方、その通りです。
それを利用しています。

この図の読み方を説明します。
この図は2つのXゲートと制御ビットの黒丸を1セットで読みます。
Xゲート - 黒丸 - Xゲートで一つの端子と思ってまとめて白丸に置き換えます。 その直前の状態がこの新しい端子への入力で、中でどんなゲートがかかるかはブラックボックスとして読んでください。

f:id:sakumadaisuke32:20210131182246p:plain
図3. 制御ビット入力端子の置き換え

そうすると、

  • 制御ビットは |0>状態で初期化
  • 新しいCXゲートの端子(制御ビットの状態が |0>の状態で標的ビットを反転する)
  • CXゲートを過ぎた後の制御ビットの状態はCXゲートに入る前の状態( |0>)のまま

という挙動になります。

ブラックボックスと書きましたが、左側の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')

f:id:sakumadaisuke32:20210131183414p:plain
図4. CCXゲートの制御ビットの入力が|00>状態の時に標的ビットの状態を反転する量子回路

この結果は下記で、標的ビットが反転しています。

f:id:sakumadaisuke32:20210131183544p:plain
図5. 図4の量子回路の量子状態の分布

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

f:id:sakumadaisuke32:20210131183623p:plain
図6. 制御ビットが3つ以上の場合

f:id:sakumadaisuke32:20210131183652p:plain
図7. 図6の量子回路の量子状態の分布

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

f:id:sakumadaisuke32:20210131183819p:plain
図8. 制御ビットの状態が|010>の時に標的ビットの状態を反転させる量子回路

これはこのまま実行しても第2制御ビットが条件を満たさないので標的ビットは反転しません。

f:id:sakumadaisuke32:20210131183947p:plain
図9. 図8の量子回路の量子状態の分布

標的ビットを反転させるには下記のようにします。

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

f:id:sakumadaisuke32:20210131184020p:plain
図10. 図8の量子回路の制御ビットの入力を|010>とした場合

見やすさのために、MCTの前にもqc.barrier()を追加しています。

ソースにコメントしていますが、この回路はMCTゲートへの入力は |010>としていて、標的ビットがちゃんと反転していることが下記の測定結果からも分かります。

f:id:sakumadaisuke32:20210131184112p:plain
図11. 図10の量子回路の量子状態の分布

まとめ

  • 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