Skip to main content
IBM Quantum Platform

ドイッチュのアルゴリズム

ドイッチュのアルゴリズムは、 n=1n = 1 という特例におけるパリティ問題を解くものである。 量子コンピューティングの分野では、この問題は「 ドイッチュの問題 」と呼ばれることもあり、このレッスンではその呼称に従うことにする。

正確には、入力は1ビットから1ビットへの関数( f:Σ→Σf:\Sigma \rightarrow \Sigma )で表される。 このような機能は4つある:

af1(a)0010af2(a)0011af3(a)0110af4(a)0111\rule[-10mm]{0mm}{10mm} \begin{array}{c|c} a & f_1(a)\\ \hline 0 & 0\\ 1 & 0 \end{array} \qquad \begin{array}{c|c} a & f_2(a)\\ \hline 0 & 0\\ 1 & 1 \end{array} \qquad \begin{array}{c|c} a & f_3(a)\\ \hline 0 & 1\\ 1 & 0 \end{array} \qquad \begin{array}{c|c} a & f_4(a)\\ \hline 0 & 1\\ 1 & 1 \end{array}

これらの関数の最初と最後は定数であり、真ん中の2つは釣り合いが取れている。つまり、関数の2つの可能な出力値は、入力を範囲指定したときに同じ回数だけ発生する。 ドイッチュの問題は、入力関数がこの2つのカテゴリーのどちらに属するかを決定することである。

Deutsch's problem

入力:関数 f:{0,1}→{0,1}f:\{0,1\}\rightarrow\{0,1\} \ 出力: ff が定数なら 00, ff が釣り合いなら 11

ドイチュの問題における入力関数 ff を、文字列へのランダムアクセスを表すものと見なすと、2ビットの文字列、すなわち f(0)f(1)f(0)f(1) について考えていることになる。

functionstringf100f201f310f411\begin{array}{cc} \mathsf{function} & \mathsf{string}\\ \hline f_1 & 00 \\ f_2 & 01 \\ f_3 & 10 \\ f_4 & 11 \end{array}

このように考えると、ドイッチュの問題は、2つのビットのパリティ(あるいは等価的に排他的論理和)を計算することである。

この問題を正しく解くすべての古典的なクエリアルゴリズムは、 f(0)f(0) と f(1)f(1) の両方のビットをクエリする必要があります。 例えば、 f(1)=1f(1) = 1 であることがわかったとしても、 f(0)=1f(0) = 1 であるか、 f(0)=0f(0) = 0 であるかによって、答えは 00 あるいは 11 のいずれかになる可能性があります。 他のすべてのケースも同様であり、2ビットのうち1ビットだけがわかっていても、そのパリティに関する情報はまったく得られません。 つまり、前節で説明したブール回路は、この問題を解くために必要なクエリ数の点で、現時点で最善のものです。


量子回路記述

Deutschのアルゴリズムは、単一のクエリーを用いてDeutschの問題を解くため、古典的な計算に対する量子の定量的な利点を提供する。 これはささやかな利点かもしれない。 科学の進歩は時として、一見地味な起源を持つ。

ドイッチュのアルゴリズムを説明する量子回路を紹介しよう:

ドイッチュのアルゴリズム

解析

Deutschのアルゴリズムを解析するために、上の回路の動作をトレースし、この図が示唆する時間における量子ビットの状態を特定する:

ドイチュのアルゴリズム中の状態

初期状態は ∣1⟩∣0⟩\vert 1\rangle \vert 0 \rangle であり、回路の左側にある2つのアダマール演算によって、この状態は次のように変換される

∣π1⟩=∣−⟩∣+⟩=12(∣0⟩−∣1⟩)∣0⟩+12(∣0⟩−∣1⟩)∣1⟩.\vert \pi_1 \rangle = \vert - \rangle \vert + \rangle = \frac{1}{2} \bigl( \vert 0\rangle - \vert 1\rangle \bigr) \vert 0\rangle + \frac{1}{2} \bigl( \vert 0\rangle - \vert 1\rangle \bigr) \vert 1\rangle.

(いつものように、Qiskitの量子ビットの並び順の規則に従っている。上の量子ビットを右に、下の量子ビットを左に置く) この積の状態を部分的に分散して書くのは直感的でないと感じるかもしれないが(量子ビット1の状態を因数分解したままにしておく)、こうすることで後の式がよりコンパクトになる。

次に、 UfU_f。 UfU_f ゲートの定義によれば、一番上/右端の量子ビットの古典的状態に対する関数 ff の値が一番下/左端の量子ビットにXORされ、 ∣π1⟩\vert \pi_1\rangle が次の状態に変換される

∣π2⟩=12(∣0⊕f(0)⟩−∣1⊕f(0)⟩)∣0⟩+12(∣0⊕f(1)⟩−∣1⊕f(1)⟩)∣1⟩.\vert \pi_2 \rangle = \frac{1}{2} \bigl( \vert 0 \oplus f(0) \rangle - \vert 1 \oplus f(0) \rangle \bigr) \vert 0 \rangle + \frac{1}{2} \bigl( \vert 0 \oplus f(1) \rangle - \vert 1 \oplus f(1) \rangle \bigr) \vert 1 \rangle.

この式を単純化するには、次の式を使用する

∣0⊕a⟩−∣1⊕a⟩=(−1)a(∣0⟩−∣1⟩)\vert 0 \oplus a\rangle - \vert 1 \oplus a\rangle = (-1)^a \bigl( \vert 0\rangle - \vert 1\rangle \bigr)

a∈Σa\in\Sigma の両方の値について成立する。 より具体的に言えば、2つのケースは以下の通りである。

∣0⊕0⟩−∣1⊕0⟩=∣0⟩−∣1⟩=(−1)0(∣0⟩−∣1⟩)∣0⊕1⟩−∣1⊕1⟩=∣1⟩−∣0⟩=(−1)1(∣0⟩−∣1⟩)\begin{aligned} \vert 0 \oplus 0\rangle - \vert 1 \oplus 0\rangle & = \vert 0 \rangle - \vert 1 \rangle = (-1)^0 \bigl( \vert 0\rangle - \vert 1\rangle \bigr)\\ \vert 0 \oplus 1\rangle - \vert 1 \oplus 1\rangle & = \vert 1 \rangle - \vert 0\rangle = (-1)^1 \bigl( \vert 0\rangle - \vert 1\rangle \bigr) \end{aligned}

したがって、 ∣π2⟩\vert\pi_2\rangle を次のように表現することもできる:

∣π2⟩=12(−1)f(0)(∣0⟩−∣1⟩)∣0⟩+12(−1)f(1)(∣0⟩−∣1⟩)∣1⟩=∣−⟩((−1)f(0)∣0⟩+(−1)f(1)∣1⟩2).\begin{aligned} \vert\pi_2\rangle & = \frac{1}{2} (-1)^{f(0)} \bigl( \vert 0 \rangle - \vert 1 \rangle \bigr) \vert 0 \rangle + \frac{1}{2} (-1)^{f(1)} \bigl( \vert 0 \rangle - \vert 1 \rangle \bigr) \vert 1 \rangle \\ & = \vert - \rangle \biggl( \frac{(-1)^{f(0)} \vert 0\rangle + (-1)^{f(1)} \vert 1\rangle}{\sqrt{2}}\biggr). \end{aligned}

面白いことが起きた! 標準的な基底状態に対する UfU_f ゲートの動作は、一番上/右端の量子ビットはそのままにしておき、一番下/左端の量子ビットに関数値をXORします。しかし、ここでは、一番上/右端の量子ビットの状態が(一般的に)変化している一方で、一番下/左端の量子ビットの状態は同じままであることがわかります。具体的には、 UfU_f ゲートが実行される前と後では、 ∣−⟩\vert - \rangle の状態になっています。 この現象はフェイズ・キックバックとして知られており、これについては後ほど詳しく説明する。

最後の単純化として、 (−1)f(0)(-1)^{f(0)} の因子を和の外側に引っ張り出すことで、状態 ∣π2⟩\vert\pi_2\rangle の式が得られる:

∣π2⟩=(−1)f(0)∣−⟩(∣0⟩+(−1)f(0)⊕f(1)∣1⟩2)={(−1)f(0)∣−⟩∣+⟩if f(0)⊕f(1)=0(−1)f(0)∣−⟩∣−⟩if f(0)⊕f(1)=1.\begin{aligned} \vert\pi_2\rangle & = (-1)^{f(0)} \vert - \rangle \biggl( \frac{\vert 0\rangle + (-1)^{f(0) \oplus f(1)} \vert 1\rangle}{\sqrt{2}}\biggr) \\ & = \begin{cases} (-1)^{f(0)} \vert - \rangle \vert + \rangle & \text{if $f(0) \oplus f(1) = 0$}\\[1mm] (-1)^{f(0)} \vert - \rangle \vert - \rangle & \text{if $f(0) \oplus f(1) = 1$}. \end{cases} \end{aligned}

この式では、 −1-1 の指数に f(0)⊕f(1)f(0) \oplus f(1) が含まれていることに注意してください。これは、純粋に代数的な観点からは f(1)−f(0)f(1) - f(0) となることが予想されますが、どちらの場合でも同じ結果が得られます。 これは、任意の整数 kk に対して、 (−1)k(-1)^k の値が、 kk が偶数か奇数かということのみに依存するためである。

最後のハダマードゲートをトップ量子ビットに適用すると、次のような状態になる

∣π3⟩={(−1)f(0)∣−⟩∣0⟩if f(0)⊕f(1)=0(−1)f(0)∣−⟩∣1⟩if f(0)⊕f(1)=1,\vert \pi_3 \rangle = \begin{cases} (-1)^{f(0)} \vert - \rangle \vert 0 \rangle & \text{if $f(0) \oplus f(1) = 0$}\\[1mm] (-1)^{f(0)} \vert - \rangle \vert 1 \rangle & \text{if $f(0) \oplus f(1) = 1$}, \end{cases}

右/一番上の量子ビットが測定されたとき、確率 11、正しい結果を導く。


位相キックバックに関する補足説明

次に進む前に、位相のキックバック現象を解明するために、上記の分析を少し違った角度から見てみよう。

まず、次の式が、 b,c∈Σb,c\in\Sigma といったあらゆるビットの組み合わせに対して成り立つことに注意してください。

∣b⊕c⟩=Xc∣b⟩\vert b \oplus c\rangle = X^c \vert b \rangle

これは、 c=0c = 0 と c=1c = 1 の2つの可能な値についてチェックすることで検証できる:

∣b⊕0⟩=∣b⟩=I∣b⟩=X0∣b⟩∣b⊕1⟩=∣¬b⟩=X∣b⟩=X1∣b⟩.\begin{aligned} \vert b \oplus 0 \rangle & = \vert b\rangle = \mathbb{I} \vert b \rangle = X^0 \vert b \rangle\\ \vert b \oplus 1 \rangle & = \vert \neg b\rangle = X \vert b \rangle = X^1 \vert b \rangle. \end{aligned}

この式を使うと、次のようになる

Uf(∣b⟩∣a⟩)=∣b⊕f(a)⟩∣a⟩=(Xf(a)∣b⟩)∣a⟩U_f \bigl(\vert b\rangle \vert a \rangle\bigr) = \vert b \oplus f(a) \rangle \vert a \rangle = \bigl(X^{f(a)}\vert b \rangle\bigr) \vert a \rangle

ビットの組み合わせ a,b∈Σa,b\in\Sigma について、すべて この式は、 b=0b=0 および b=1b=1 についても成り立つため、線形性から、

Uf(∣ψ⟩∣a⟩)=(Xf(a)∣ψ⟩)∣a⟩U_f \bigl( \vert \psi \rangle \vert a \rangle \bigr) = \bigl(X^{f(a)}\vert \psi \rangle\bigr) \vert a \rangle

すべての量子ビットの状態ベクトル ∣ψ⟩\vert \psi\rangle について、したがって

Uf(∣−⟩∣a⟩)=(Xf(a)∣−⟩)∣a⟩=(−1)f(a)∣−⟩∣a⟩.U_f \bigl( \vert - \rangle \vert a \rangle \bigr) = \bigl(X^{f(a)} \vert - \rangle \bigr) \vert a \rangle = (-1)^{f(a)} \vert - \rangle \vert a \rangle.

これが成り立つ鍵となるのは、 X∣−⟩=−∣−⟩X\vert - \rangle = - \vert - \rangle である。 数学的に言えば、ベクトル ∣−⟩\vert - \rangle は、 固有値 −1-1 を持つ行列 XX の固有ベクトルである。

固有ベクトルと固有値については、次回の「 位相推定と因数分解 」のレッスンでさらに詳しく説明します。このレッスンでは、位相キックバック現象を他のユニタリー演算に一般化します。

スカラーはテンソル積を通して自由に浮遊することを念頭に置いて、上記の分析において、 UfU_f という演算が ∣π1⟩\vert \pi_1\rangle を ∣π2⟩\vert \pi_2\rangle に変換する方法を推論する別の方法を見つける:

∣π2⟩=Uf(∣−⟩∣+⟩)=12Uf(∣−⟩∣0⟩)+12Uf(∣−⟩∣1⟩)=∣−⟩((−1)f(0)∣0⟩+(−1)f(1)∣1⟩2).\begin{aligned} \vert \pi_2 \rangle & = U_f \bigl( \vert - \rangle \vert + \rangle \bigr)\\ & = \frac{1}{\sqrt{2}} U_f \bigl(\vert - \rangle \vert 0\rangle \bigr) + \frac{1}{\sqrt{2}} U_f \bigl(\vert - \rangle \vert 1\rangle \bigr)\\ & = \vert - \rangle \biggl( \frac{(-1)^{f(0)} \vert 0\rangle + (-1)^{f(1)} \vert 1\rangle}{\sqrt{2}}\biggr). \end{aligned}

Qiskitでの実装

では、DeutschのアルゴリズムをQiskitでどのように実装できるかを見てみよう。 まずはバージョンチェックから始め、この実装だけに必要なインポートを実行する。 この後に続く他のアルゴリズムの実装については、モジュール性を高めるために、必要なインポートを個別に行うことにする。

from qiskit import __version__

print(__version__)

Output:

2.1.1
from qiskit import QuantumCircuit
from qiskit_aer import AerSimulator

まず、前述した1ビットから1ビットへの4つの関数( f1f_1、 f2f_2、 f3f_3、または f4f_4 )のうちの1つに対応するクエリゲートを実装する量子回路を定義します。 すでに述べたように、クエリゲートの実装は、厳密にはドイッチュのアルゴリズムそのものの一部ではありません。 ここでは、クエリゲートの回路実装という形で、入力を準備する一つの方法を単に示しているに過ぎません。

def deutsch_function(case: int):
    # This function generates a quantum circuit for one of the 4 functions
    # from one bit to one bit

    if case not in [1, 2, 3, 4]:
        raise ValueError("`case` must be 1, 2, 3, or 4.")

    f = QuantumCircuit(2)
    if case in [2, 3]:
        f.cx(0, 1)
    if case in [3, 4]:
        f.x(1)
    return f

この draw メソッドを使えば、各回路がどのようなものかを確認することができます。 f3f_3 関数の回路は以下の通りです。

display(deutsch_function(3).draw(output="mpl"))

Output:

Output of the previous code cell

次に、クエリーゲートを引数として与えられた量子回路実装に置き換えて、ドイチュのアルゴリズムの実際の量子回路を作成する。 まもなく、先に定義した関数 deutsch_function で定義された4つの回路のうちの1つを接続する。 バリアは、クエリーゲートの実装と回路の他の部分との間の視覚的な分離を示すために含まれている。

def compile_circuit(function: QuantumCircuit):
    # Compiles a circuit for use in Deutsch's algorithm.

    n = function.num_qubits - 1
    qc = QuantumCircuit(n + 1, n)

    qc.x(n)
    qc.h(range(n + 1))

    qc.barrier()
    qc.compose(function, inplace=True)
    qc.barrier()

    qc.h(range(n))
    qc.measure(range(n), range(n))

    return qc

もう一度、 draw 。

display(compile_circuit(deutsch_function(3)).draw(output="mpl"))

Output:

Output of the previous code cell

最後に、先に定義した回路を1回実行し、適切な結果を出力する関数を作成する:"コンスタント "または "バランス "である

def deutsch_algorithm(function: QuantumCircuit):
    # Determine if a one-bit function is constant or balanced.

    qc = compile_circuit(function)

    result = AerSimulator().run(qc, shots=1, memory=True).result()
    measurements = result.get_memory()
    if measurements[0] == "0":
        return "constant"
    return "balanced"

これでDeutschのアルゴリズムを、上で定義した4つの関数のいずれかに対して実行することができる。

f = deutsch_function(3)
display(deutsch_algorithm(f))

Output:

'balanced'
このページは役に立ちましたか?
バグや誤字の報告、またはコンテンツの要求はGitHubで行ってください。