Deutsch-Jozsaアルゴリズム
Deutschのアルゴリズムは、クエリ問題では古典的なアルゴリズムよりも優れているが、その優位性は1クエリ対2クエリという極めてささやかなものである。 Deutsch-Jozsaアルゴリズムは、この利点を拡張するもので、実際、2つの異なるクエリー問題を解くのに使うことができる。
ドイチュ・ヨッサ・アルゴリズムの量子回路の説明である。 図には示されていないが、解決しようとする特定の問題によっては、さらに古典的な後処理ステップが必要になることもある。
もちろん、このアルゴリズムがどのような問題を解決するのかについては、実際には議論していない。
ドイッチュ=ヨージャ問題
まず、Deutsch-Jozsaアルゴリズムが元々解こうとしていたクエリー問題( Deutsch-Jozsa問題として知られている)から始めよう。
この問題の入力関数は、任意の正の整数に対して の形をとる。 Deutschの問題と同様に、 が定数なら を、 が釣り合いなら を出力することである。これは、関数が値 をとる入力文字列の数と、関数が値 をとる入力文字列の数が等しいことを意味する。
が より大きい場合、定数でも釣り合いでもない という形の関数が存在することに注意。 例えば、関数 は次のように定義される
この2つのカテゴリーには当てはまらない。 Deutsch-Jozsa問題では、このような関数は気にしない。 つまり、この問題では、 が一定か均衡のどちらかであるという約束がある。
入力:関数 \ 約束: が一定か均衡のいずれかである。 \ 出力: が定数なら , が釣り合いなら
Deutsch-Jozsaアルゴリズムは、単一のクエリを用いて、以下の意味でこの問題を解決する。 の測定結果のすべてが である場合、関数 は定数である。 それ以外の場合、すなわち測定結果のうち少なくとも1つが である場合、関数 は平衡である。 別の言い方をすれば、上述の回路の後に、測定結果の論理和(OR)を計算して、ドイッチュ・ジョザ問題の出力ビットを生成する、古典的な後処理ステップが続くということである。
アルゴリズム分析
Deutsch-Jozsa問題に対するDeutsch-Jozsaアルゴリズムの性能を分析するためには、ハダマードゲートの単一層の作用について考えることから始めると役に立つ。 ハダマード演算は、通常の方法で行列として表すことができる、
しかし、この操作を標準的な基底状態に対する作用という観点から表現することもできる:
この2つの方程式は1つの式にまとめることができる、
のどちらを選んでも同じである。
ここで、1つの量子ビットの代わりに 、それぞれの量子ビットに対してハダマード演算が行われたとする。 量子ビットの複合演算は、テンソル積 ( 回)によって記述される。ここでは簡潔かつ明瞭にするために と表記する。 上記の公式を使い、展開して単純化すると、 の標準基底状態に対するこの複合操作の作用を次のように表すことができる:
ちなみにここでは、 の長さのバイナリ文字列を、Qiskitのインデックス規則に従って、 と のように書いています。
この式は、上記の量子回路を解析するための便利なツールを提供してくれる。 ハダマードゲートの第1層が実行された後、 の量子ビット(左端/下端の量子ビットを含み、他とは別に扱われる)の状態は次のようになる
、この状態は次のように変換される
ドイチュのアルゴリズムの分析で見られたのとまったく同じ位相キックバック現象によって。
次に、2層目のハダマードゲートが実行され、(上式によって)この状態は次のように変換される
この式はやや複雑に見えるので、関数についてもっと詳しく知らなければ、異なる測定結果を得る確率についてあまり多くのことを結論づけることはできない。
幸いなことに、私たちが知る必要があるのは、すべての測定結果が である確率だけである。それは、 が一定であるとアルゴリズムが判断する確率だからである。 この確率には簡単な公式がある。
なお、これらの値は、 という状態が観測される確率に対応するものであり、 ドイッチュ・ジョザ問題の最終的な古典的な出力ビットに直接対応するものではないことに注意してください。 このアルゴリズムは、すべての測定結果が (が定数であることを示す)である場合にを出力し、それ以外の場合は を出力する (が平衡状態にあることを示す)。
より詳細には、 が定数である場合、すべての文字列について のどちらかとなる。 となり、その場合の和の値は となる。 である。 で割り、絶対値の2乗をとると、次のようになる。
一方、 が釣り合っている場合、 は文字列の半分に値 をとり、 と残りの半分に値 をとる。したがって、合計の の項と の項は相殺され、次のような値が残る。
約束が果たされる限り、アルゴリズムは正しく動作すると結論づける。
古典的難度
Deutsch-Jozsaアルゴリズムは毎回機能し、約束が守られれば常に正しい答えを出し、クエリーは1回で済む。 Deutsch-Jozsa問題に対する古典的なクエリーアルゴリズムと比較してどうか?
まず、Deutsch-Jozsa問題を正しく解く決定論的古典アルゴリズムは、指数関数的に多くの問い合わせを行わなければならない: 最悪の場合、クエリーは指数関数的に多くなる。 その理由は、もし決定論的アルゴリズムが 、 以下の異なる文字列を問い合わせ、毎回同じ関数値を得るなら、両方の答えが可能であるということだ。 関数は一定かもしれないし、バランスが取れているかもしれないが、運が悪いことにクエリーはすべて同じ関数値を返す。
しかし、決定論的アルゴリズムにはランダム性や不確実性がないため、特定の関数でシステマティックに失敗することになる。 従って、この点では古典的アルゴリズムよりも量子の方が大きな利点がある。
しかし、 確率的な古典的アルゴリズムは、わずか数回のクエリーで非常に高い確率でDeutsch-Jozsa問題を解くことができるというキャッチがある。 特に、長さ の数種類の文字列をランダムに選び、それらの文字列に対して 問い合わせた場合、 のバランスが取れたときに、すべての文字列に対して同じ関数値が得られる可能性は低い。
具体的には、 入力文字列 を一様にランダムに選び、 を評価し、関数値がすべて同じであれば に、そうでなければ に答えるとすると、 が一定の場合は常に正解となり、 がちょうど釣り合う場合は確率で不正解となる。 を例にとると、このアルゴリズムは %以上の確率で正解となる。
このような理由から、古典的アルゴリズムに対する量子の利点はまだ控えめであるが、それでもドイチュのアルゴリズムに対する改善を示す定量的な利点である。
Deutsch-Jozsa with Qiskit
from qiskit import QuantumCircuit
from qiskit_aer import AerSimulator
import numpy as npQiskitでDeutsch-Jozsaアルゴリズムを実装するために、まずDeutsch-Jozsa問題の約束を満たす関数をランダムに選び、クエリーゲートを実装した量子回路を生成する関数 dj_query 。
50%の確率で機能は一定であり、50%の変化で機能は均衡する。
この2つの可能性のそれぞれについて、そのタイプの関数から一様に関数が選択される。
引数は関数の入力ビット数。
def dj_query(num_qubits):
# Create a circuit implementing for a query gate for a random function
# satisfying the promise for the Deutsch-Jozsa problem.
qc = QuantumCircuit(num_qubits + 1)
if np.random.randint(0, 2):
# Flip output qubit with 50% chance
qc.x(num_qubits)
if np.random.randint(0, 2):
# return constant circuit with 50% chance
return qc
# Choose half the possible input strings
on_states = np.random.choice(
range(2**num_qubits), # numbers to sample from
2**num_qubits // 2, # number of samples
replace=False, # makes sure states are only sampled once
)
def add_cx(qc, bit_string):
for qubit, bit in enumerate(reversed(bit_string)):
if bit == "1":
qc.x(qubit)
return qc
for state in on_states:
qc.barrier() # Barriers are added to help visualize how the functions are created.
qc = add_cx(qc, f"{state:0b}")
qc.mcx(list(range(num_qubits)), num_qubits)
qc = add_cx(qc, f"{state:0b}")
qc.barrier()
return qcクエリーゲートの量子回路実装は、通常通り draw 。
display(dj_query(3).draw(output="mpl"))Output:
次に、クエリーゲートの量子回路実装を引数として、Deutsch-Jozsa回路を作成する関数を定義する。
def compile_circuit(function: QuantumCircuit):
# Compiles a circuit for use in the Deutsch-Jozsa algorithm.
n = function.num_qubits - 1
qc = QuantumCircuit(n + 1, n)
qc.x(n)
qc.h(range(n + 1))
qc.compose(function, inplace=True)
qc.h(range(n))
qc.measure(range(n), range(n))
return qc最後に、Deutsch-Jozsa回路を一度実行する関数が定義されている。
def dj_algorithm(function: QuantumCircuit):
# Determine if a function is constant or balanced.
qc = compile_circuit(function)
result = AerSimulator().run(qc, shots=1, memory=True).result()
measurements = result.get_memory()
if "1" in measurements[0]:
return "balanced"
return "constant"ランダムに関数を選び、その関数に対するクエリゲートの量子回路実装を表示し、その関数に対してDeutsch-Jozsaアルゴリズムを実行することで、我々の実装をテストすることができる。
f = dj_query(3)
display(f.draw("mpl"))
display(dj_algorithm(f))Output:
'balanced'
バーンスタイン=ヴァジラニ問題
次に、 バーンスタイン=ヴァジラニ問題として知られる問題について説明しよう。 これはフーリエ・サンプリング問題とも呼ばれるが、この問題にはもっと一般的な定式化もあり、その名前でも呼ばれている。
まず、いくつかの表記法を紹介しよう。 長さ の任意の2つのバイナリ文字列 と に対して、次のように定義する
この演算を 2進ドット積と呼ぶことにする。 別の言い方をすれば、こうなる。
これは対称的な操作であり、 と を入れ替えても結果は変わらない。 バイナリのドット積 を、文字列 が を持つ位置における のビットのパリティ、あるいは同等に、文字列 が を持つ位置における のビットのパリティと考えると便利なことがある。
この表記法を手にして、我々は今、Bernstein-Vazirani問題を定義することができる。
入力:関数 \ 約束:すべての に対して が成り立つバイナリ文字列 が存在する。 \ 出力:文字列
この問題には、実は新しい量子アルゴリズムは必要ない。ドイチュ・ヨッサ・アルゴリズムが解決してくれる。 わかりやすくするために、ORを計算するという古典的な後処理を含まない上記の量子回路を、 ドイチュ・ヨッサ回路と呼ぶことにしよう。
アルゴリズム分析
Bernstein-Vazirani問題の約束を満たす関数に対してDeutsch-Jozsa回路がどのように働くかを分析するために、まず簡単な観察から始めよう。 バイナリー・ドット・プロダクトを用いると、 ハダマード・ゲートの標準基底状態( クビット)に対する作用を次のように記述することができる。
Deutschのアルゴリズムを分析したときに見たのと同様、これは、任意の整数 に対する値 が、 が偶数か奇数かにのみ依存するからである。
Deutsch-Jozsa回路に目を向けると、第1層のハダマードゲートが実行された後、 の量子ビットの状態は次のようになる
その後、クエリーゲートが実行され、(位相キックバック現象によって)状態は次のように変換される
ハダマードゲートの層の作用に関する公式を使えば、ハダマードゲートの第2層はこの状態を次のように変換することがわかる
ここで、和の中の指数 、いくつかの簡略化を行うことができる。 、文字列 、次のように表現できる
と は2進数値なので、加算を排他的論理和に置き換えることができる。 の指数で整数にとって重要なのは、偶数か奇数かということだけだからである。 バイナリー・ドット・プロダクトの対称性を利用して、このような表現が得られる:
(わかりやすくするために括弧がつけられているが、2進のドット積は排他的論理和よりも優先順位が高いものとして扱われるのが通例であるため、実際には必要ない)
この時点では、以下の公式を使用する。
この公式は、ビットに関する同様の公式によって求めることができる、
バイナリのドット積とビット単位の排他的論理和の展開と一緒に:
これにより、測定直前の回路の状態をこのように表現することができる:
最後のステップは、すべてのバイナリ文字列に対して機能する、さらに別の公式を使うことである。
ここでは、このレッスンであと何度か使うことになる、文字列の簡単な表記法を使う。 は、長さがすべてゼロの文字列である。
この公式が機能することを論証する簡単な方法は、2つのケースを別々に考えることである。 もし ならば、すべての文字列に対して 。したがって、和の各項の値は となり、和と除算によって が得られる。 一方、 のいずれかのビットが に等しい場合、バイナリドット積 は、 の選択肢のちょうど半分について に等しく、残りの半分について に等しくなります。 の値が反転する( から へ、または から へ)のは、 が
ここで、この式を測定前の回路の状態を単純化するために適用すると、次のようになる
という事実によって、 if and only if したがって、この測定は、我々が探しているストリング を正確に明らかにしている。
古典的難度
Deutsch-Jozsa回路は1回の問い合わせでBernstein-Vazirani問題を解くが、古典的な問い合わせアルゴリズムはこの問題を解くために少なくとも 。
これは、いわゆる情報理論的な議論によって推論することができるが、この場合は非常に単純である。 古典的なクエリーは、それぞれ解に関する1ビットの情報を明らかにする。 ビットの情報を明らかにする必要があるので、少なくとも クエリーが必要になる。
実際、バーンスタイン-ヴァジラニ問題を古典的に解くことは可能である。それは、 の文字列のそれぞれについて、 を1つずつ、それ以外のすべてのビットについては を持つ関数を問い合わせることで、 のビットを1つずつ明らかにすることである。 したがって、この問題に対する古典的アルゴリズムに対する量子の利点は、 クエリ対 クエリである。
バーンスタイン=ヴァジラニ問題とQiskit
ドイチュ・ヨツァ回路はすでに実装したが、ここではそれを使ってバーンスタイン・ヴァジラニ問題を解く。 まず、任意の2進文字列が与えられた場合のBernstein-Vazirani問題のクエリーゲートを実装する関数を定義する。
def bv_query(s):
# Create a quantum circuit implementing a query gate for the
# Bernstein-Vazirani problem.
qc = QuantumCircuit(len(s) + 1)
for index, bit in enumerate(reversed(s)):
if bit == "1":
qc.cx(index, len(s))
return qc
display(bv_query("1011").draw(output="mpl"))Output:
ここで、先に定義した compile_circuit 関数を使って、関数上でDeutsch-Jozsa回路を実行する関数を作ることができる。
def bv_algorithm(function: QuantumCircuit):
qc = compile_circuit(function)
result = AerSimulator().run(qc, shots=1, memory=True).result()
return result.get_memory()[0]
display(bv_algorithm(bv_query("1011")))Output:
'1011'
命名法に関する注記
Bernstein-Vazirani問題の文脈では、Deutsch-Jozsaアルゴリズムが "Bernstein-Vaziraniアルゴリズム "と呼ばれるのが一般的である というのも、BernsteinとVaziraniが彼らの研究で明確にしていたように*、* このアルゴリズムはDeutsch-Jozsaアルゴリズムだからだ。
Deutsch-JozsaアルゴリズムがBernstein-Vazirani問題を解くことを示した後、BernsteinとVaziraniが行ったことは(上記の通り)、 再帰的フーリエサンプリング問題として知られる、より複雑な問題を定義することだった。 これは高度に仕組まれた問題で、問題のさまざまなインスタンスに対する解答が、ツリー状に配置された問題の新たなレベルを効果的に解き放つ。 Bernstein-Vazirani問題は本質的に、この複雑な問題の基本ケースに過ぎない。
再帰的フーリエサンプリング問題は、量子アルゴリズムが確率的アルゴリズムに対していわゆる超多項式的な優位性を持ち、Deutsch-Jozsaアルゴリズムによる古典的アルゴリズムに対する量子の優位性を凌駕する問い合わせ問題の最初の例として知られている。 直観的に言えば、この問題の再帰的バージョンは、量子アルゴリズムの 対 の利点を、より大きなものに増幅する。
この優位性を確立する数学的分析で最も困難な点は、古典的なクエリーアルゴリズムでは多くのクエリーを行わないと問題を解決できないことを示すことである。 これは極めて典型的な例である。多くの問題において、それらを効率的に解決する創造的な古典的アプローチを除外することは非常に難しい。
サイモンの問題と、次のセクションで説明するそのアルゴリズムは、古典的アルゴリズムに対する量子の超多項式的な(実際には指数関数的な)利点を示す、より単純な例を提供している。 とはいえ、それ自体が興味深い計算問題ではある。