The new Qiskit Textbook beta is now available. Try it out now
Hello Qiskitゲーム

量子ビットと量子ゲートを使い始めるための簡単なパズル。

レベル 1: ビットから始める

このテキストブックの他の章と同様に、このドキュメントはJupyter notebookです。ただし、他の多くの章とは異なり、実際に実行して機能させる必要があります。

これまでJupyter notebookを使用したことがなくても心配しないでください。これは、以下のように、コードが入った灰色のボックスがたくさん表示されることを意味します。 これらはセルとして知られています。

print("Hello! I'm a code cell")

セル内でコードを実行する方法は、使用しているデバイスと入力方法によって異なります。ほとんどの場合、セルをクリックしてShiftとEnterキーを押す必要があります。ただし、QiskitテキストブックのWebサイトでこれを実行している場合は、下のRUNボタンをクリックするだけです。

以下のセルに対してこれを実行することから始めます(実行には1〜2秒かかります)。

print('Set up started...')
from qiskit_textbook.games import hello_quantum
print('Set up complete!')

このJupyter notebookの残りのセルには、解くためのパズルを設定するコードが含まれています。パズルを解くには、セルを実行するだけです。パズルを再開するには、パズルを再実行するだけです。

パズル 1

導入

量子コンピューターは量子ビットに基づいています:ビットの量子力学のルールに従います。しかし、正確にはビットとは何ですか?そして、それらはコンピューターでどのように使用されていますか?

ビットの特徴は、2つの可能な出力値があることです。これらは、10、または「オン」と「オフ」、または「真」と「偽」と呼ぶことができます。私たちが使用する名前は重要ではありません。重要な点は、2つあるということです。

ビットに慣れるために、ビットで遊んでみましょう。ビットでできる最も簡単なことは(そのままにしておく以外に)、その値を反転することです。この単純な操作に派手な名前を付けます。これはNOTゲートと呼ばれます。

以下で試してみてください。

練習問題

*NOTゲートを3回使用してみましょう。

initialize = []
success_condition = {}
allowed_gates = {'0': {'NOT': 3}, '1': {}, 'both': {}}
vi = [[1], False, False]
qubit_names = {'0':'the only bit', '1':None}
puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names)

終結

ここでは、オン(白)またはオフ(黒)の円を使用してビットを視覚化しました。NOTゲートの効果は、ビットの2つの状態を切り替えて、オンとオフを切り替えることでした。

パズル 2

導入

1つだけではなく2つのビットで遊ぶ方が面白いです。ここに遊ぶための別のものがあります。以前と同じように見えます。しかし、それは別のビットなので、別の場所にあります。

練習問題

  • もう一方のビットをオンにします。
initialize = []
success_condition = {}
allowed_gates = {'0': {}, '1': {'NOT': 0}, 'both': {}}
vi = [[], False, False]
qubit_names = {'0':'the bit on the left', '1':'the bit on the right'}
puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names)

終結

これで、コンピューティングの最も基本的な構成要素であるNOTゲートをマスターしました。

パズル 3

導入

「ゲート」という言葉を使用して、量子ビットを操作するために使用できる簡単なツールを説明します。NOTゲートは最も単純な例です。しかし、ビットを使って面白いことをするためには、単にオンとオフを切り替える以上のことをする必要があります。

もう少し洗練されたゲートはCNOTと呼ばれ、「controled-NOT」の略です。これはビットのペアで使用されます。

「コントロール」となるビットの1つを選択します。これにより、CNOTが実際に何かを実行するかどうかが決まります。もう1つのビットは「ターゲット」です。簡単に言えば、CNOTはターゲットビットに対してNOTを実行しますが、コントロールビットがオンの場合に限ります。

これを理解する最良の方法は、試してみることです。

練習問題

*CNOTを使用して、右側のビットをオンにします。

*注:ビットを選択するように求められた場合は、どちらをターゲットビットにするかを選択しています。

initialize = [['x', '0']]
success_condition = {'IZ': -1.0}
allowed_gates = {'0': {'CNOT': 0}, '1': {'CNOT': 0}, 'both': {}}
vi = [[], False, False]
qubit_names = {'0':'the bit on the left', '1':'the bit on the right'}
puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names)

パズル 4

導入・導入

私たちが実行するプログラムはすべて、多くの量子ビット上の多くのゲートで構成されています。これに向けたステップとして、CNOTをいくつか使用する必要があるものを試してみましょう。

練習問題

  • 複数のCNOTを使用して、左ビットをオフにし、右ビットをオンにします。
initialize = [['x', '0']]
success_condition = {'ZI': 1.0, 'IZ': -1.0}
allowed_gates = {'0': {'CNOT': 0}, '1': {'CNOT': 0}, 'both': {}}
vi = [[], False, False]
qubit_names = {'0':'the bit on the left', '1':'the bit on the right'}
puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names)

終結

素晴らしい!

これらの種類の操作は、あらゆるコンピューティングがコンパイルするものです。より多くのビットと制御制御NOTゲートがあれば、テトリスから自動運転車まですべてを構築できます。

パズル 5

導入

お気づきかもしれませんが、パズルボードにはたくさんの空きスペースがあります。これは、ビットに関するより多くの情報を視覚化するために使用できるためです。たとえば、ビット値がランダムなプロセスによって生成された場合はどうなるでしょうか?

0または1のビット値を表すために黒と白の円を使用しました。したがって、等確率で0または1が得られるランダムビットの場合、灰色の円を使用します。

これで、ゲートがこのランダム性をどのように操作するかを見ることができます。

練習問題

  • CNOTを使用して、右側のビットをランダムにします。
initialize = [['h', '0']]
success_condition = {'IZ': 0.0}
allowed_gates = {'0': {'CNOT': 0}, '1': {'CNOT': 0}, 'both': {}}
vi = [[], False, False]
qubit_names = {'0':'the bit on the left', '1':'the bit on the right'}
puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names)

終結

素晴らしいでしょう!

ここで何が起こったのか考えてみましょう。ランダムにオンまたはオフのいずれかであるビットによってコントロールされるCNOTを実行しました。オフの場合、CNOTは何もせず、右ビットはオフのままです。左側のビットがオンの場合、CNOTは右側のビットでもNOTを実行してオンにします。

このプロセスは、左ビットのランダム値を右に効果的にコピーします。したがって、両方からの出力はランダムに見えますが、重要な特性があります。それらは常に互いに同じランダムな結果を出力します。

上のパズルの終わりに、2つのビットは2つの灰色の円で表されました。これは、両方がランダムであることを示していますが、常に一致しているという事実については何も教えてくれません。ビットが独立してランダムである場合、またはランダムであるが常に一致しない場合にも、同じ2つの灰色の円が使用されます。 この違いを見分けるために,問題ボードに何かを追加する必要があります。

パズル 6

導入

下のパズルでは、新しい円が表示されます。

これまでに見た円とは異なり、新しいビットを表すものではありません。代わりに、2ビットが一致するかどうかを示します。

それらが一致することが確実であるとき、それはオフになります。それらが一致しないことが確実な場合、それはオンになります。それらの一致と不一致がランダムである場合、それは灰色になります。

練習問題

  • 2つのビットを常に一致させないようにします(つまり、真ん中の円を白にします)。
initialize = [['h', '0']]
success_condition = {'ZZ': -1.0}
allowed_gates = {'0': {'NOT': 0, 'CNOT': 0}, '1': {'NOT': 0, 'CNOT': 0}, 'both': {}}
vi = [[], False, True]
qubit_names = {'0':'the bit on the left', '1':'the bit on the right'}
puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names)

パズル 7

導入

これで、ビットについて知る必要があるすべてがほぼ必要であることがわかりました。先に進む前に、もう1つ演習を行いましょう。

練習問題

  • 右側のビットをオンにします。
initialize = [['h', '1']]
success_condition = {'IZ': -1.0}
allowed_gates = {'0': {'NOT': 0, 'CNOT': 0}, '1': {'NOT': 0, 'CNOT': 0}, 'both': {}}
vi = [[], False, True]
qubit_names = {'0':'the bit on the left', '1':'the bit on the right'}
puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names)

終結

次に、ビットが量子になったときに何が起こるかを見てみましょう。量子ビットの時間です!

レベル 2: 基本的な単一量子ビットゲート

パズル 1

導入

量子力学の講義から始める代わりに、ここで遊ぶための量子ビットがあります。Xと呼ばれる最も単純な量子ビットゲートを試してみてください。

練習問題

  • Xゲートを3回使用して、何が起こるかを確認します。
initialize = [ ["x","0"] ]
success_condition = {"ZI":1.0}
allowed_gates = { "0":{"x":3}, "1":{}, "both":{} }
vi = [[1],True,True]
qubit_names = {'0':'the only qubit', '1':None}
puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names)

終結

このパズルはとてもなじみのあるものに思えたはずです。何も変化しなかった円があったこと以外は、ビットの最初のものとまったく同じでした。 XゲートはNOTゲートとまったく同じ効果がありました。

パズル 2

導入

さきほどのパズルには2つの円がありました。これらは異なるビットではなく、両方とも同じ量子ビットを表しています。

量子ビットはビットの量子バージョンです。それらはビットと同じプロパティのいくつかを持っていますが、いくつかの追加機能も持っています。

ビットと同じように、量子ビットも使える値は2つに制限されています。量子ビットから出力を抽出すると、0または1の単純なビット値が得られます。ただし、量子ビットでは、このビットを抽出するための方法が複数あります。得られる結果は、使用する方法によって異なります。

さきほどのパズルの2つの円は、同じ量子ビットからビットを取り出す2つの異なる手法を表しています。それらはXおよびZ測定と呼ばれます。下の円はZ測定の出力を表し、上の円はX出力を表します。

円の色は以前とまったく同じように使用されます。円が黒の場合、対応する出力は値0になります。白い円は1を取得することを意味します。

練習問題

  • Z出力をオフにします。
initialize = [['x', '0']]
success_condition = {'ZI': 1.0}
allowed_gates = {'0': {'x': 0}, '1': {}, 'both': {}}
vi = [[1], True, True]
qubit_names = {'0':'the only qubit', '1':None}
puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names)

終結

量子ビットからビットを抽出するプロセスは、「測定」と呼ばれます。

X出力とZ出力の両方を個別に抽出することはできません。1つだけ選択する必要があります。

パズル 3

導入

このパズルでは、別の量子ビットが表示されます。これもまた、2つの円で表される内部動作を持ちます。この量子ビットの場合、これらの円はパズルボードの右側にあります。

練習問題

  • もう片方の量子ビットのZ出力をオフにします。
initialize = [['x', '1']]
success_condition = {'IZ': 1.0}
allowed_gates = {'0': {}, '1': {'x': 0}, 'both': {}}
vi = [[0], True, True]
qubit_names = {'0':None, '1':'the other qubit'}
puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names)

終結

これから、プログラムで使用される名前で量子ビットの呼び出しを開始します。左側の量子ビットはq[0]になり、右側の量子ビットはq[1]になります。

また、Z出力およびX出力という用語は広く使用されていないことに注意してください。「Z測定の出力」などと言うのがより一般的です。しかし、それはこれらのパズルにとって少し長い表現です。

パズル 4

導入

次に、新しいゲートであるHゲートを試してみましょう。

これは、単純なビットでは不可能なことです。それは、それが適用される量子ビットの2つの円を交換する効果があります。

これを素敵なアニメーション形式で見たい場合は、Hello Quantumアプリをチェックしてください。しかし、ここでは、3回繰り返すという古いトリックでこれをテストしてください。

練習問題

  • Hゲートを3回使用します。
initialize = []
success_condition = {'ZI': 0.0}
allowed_gates = {'0': {'h': 3}, '1': {}, 'both': {}}
vi = [[1], True, True]
qubit_names = {'0':'q[0]', '1':'q[1]'}
puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names)

終結

ここから先は、下のコードをパズルの下に置き、量子プログラムを表示します。

これらは、実際の量子コンピューターで実行できる実際のQiskitプログラムです。それらは、いわゆる回路図で表すこともできます。量子プログラムの回路図を表示するには、以下のコードセルを実行します。

puzzle.get_circuit().draw(output='mpl')

パズル 5

導入

Z出力が一定の場合(完全にオフまたはオンの場合)、Xゲートは単に値を反転します。しかし、さきほどのパズルで見たように、Z出力がランダムである場合はどうなりますか?この練習問題で、その答えがわかります。

練習問題

  • Z出力を完全にオフにします。 hゲートはいくつでも使用できますが、xは正確に3回使用します。
initialize = [['h', '1']]
success_condition = {'IZ': 1.0}
allowed_gates = {'0': {}, '1': {'x': 3, 'h': 0}, 'both': {}}
vi = [[0], True, True]
qubit_names = {'0':'q[0]', '1':'q[1]'}
puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names)
puzzle.get_circuit().draw(output='mpl')

終結

たとえそれをひっくり返したとしても、ランダムな結果はただのランダムな結果であることがわかります。

パズル 6

導入

xゲートがZ出力を反転することを確認しましたが、X出力には影響しません。そのためには、新しいゲート、zゲートが必要です。

練習問題

  • X出力をオフにします。
initialize = [['h', '0'], ['z', '0']]
success_condition = {'XI': 1.0}
allowed_gates = {'0': {'z': 0, 'h': 0}, '1': {}, 'both': {}}
vi = [[1], True, True]
qubit_names = {'0':'q[0]', '1':'q[1]'}
puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names)
puzzle.get_circuit().draw(output='mpl')

終結

xゲートはZ出力を反転し、zゲートはx出力を反転します。今のところ、これは物事に名前を付ける奇妙な方法のように思えるかもしれません。しかし、量子コンピューティングについてさらに学ぶにつれて、それは意味を持つようになるでしょう。

パズル 7

導入

ゲートを組み合わせることで、新しい効果を得ることができます。簡単な例として、zhを組み合わせることで、xの作用をすることができます。

練習問題

  • xゲートを使用せずにZ出力をオンにします
initialize = []
success_condition = {'ZI': -1.0}
allowed_gates = {'0': {'z': 0, 'h': 0}, '1': {}, 'both': {}}
vi = [[1], True, True]
qubit_names = {'0':'q[0]', '1':'q[1]'}
puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names)
puzzle.get_circuit().draw(output='mpl')

パズル 8

導入

Z出力が完全にオンまたはオフの場合、X出力は常にランダムであることに気付くかもしれません。これは、量子ビットがそれぞれの出力について同時に確実になることは決してないためです。それらが一方について確実である場合、もう一方はランダムでなければなりません。

もし上記ではない場合、Z出力とX出力を使用して2ビットを格納できてしまいます。つまり量子ビットが持っているより多くのメモリーを持つことになってしまいます。複数の方法で出力を抽出できるという事実にもかかわらず、それでも、量子ビットにビット以上を格納することは許可されていません。

練習問題

  • X出力をオフにし、Z出力をランダムにします。
initialize = [['h', '0']]
success_condition = {'IX': 1.0}
allowed_gates = {'0': {}, '1': {'z': 0, 'h': 0}, 'both': {}}
vi = [[0], True, True]
qubit_names = {'0':'q[0]', '1':'q[1]'}
puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names)
puzzle.get_circuit().draw(output='mpl')

パズル 9

導入

量子ビットの限られた確実性は、2つの出力間で共有することもできます。たとえば、両方の出力を、それらが提供する出力についてほぼ確実(完全に確実ではない)にすることで妥協することができます。

さまざまなグレーの色合いを使用して、これを視覚化します。円が暗いほど、出力は0になる可能性が高くなります。円が明るいほど、出力は1になる可能性が高くなります。

練習問題

  • q[1]の2つの円を両方とも明るい灰色にします。これは、1を出力する可能性が高いことを意味しますが、確実ではありません。
initialize = [['ry(pi/4)', '1']]
success_condition = {'IZ': -0.7071, 'IX': -0.7071}
allowed_gates = {'0': {}, '1': {'z': 0, 'h': 0}, 'both': {}}
vi = [[0], True, True]
qubit_names = {'0':'q[0]', '1':'q[1]'}
puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names)
puzzle.get_circuit().draw(output='mpl')

パズル 10

導入

これで基本的なツールがわかったので、両方の量子ビットに同時に取り組むことができます。

練習問題

  • 両方のZ出力をランダムにします。
initialize = [['x', '1']]
success_condition = {'ZI': 0.0, 'IZ': 0.0}
allowed_gates = {'0': {'x': 0, 'z': 0, 'h': 0}, '1': {'x': 0, 'z': 0, 'h': 0}, 'both': {}}
vi = [[], True, False]
qubit_names = {'0':'q[0]', '1':'q[1]'}
puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names)
puzzle.get_circuit().draw(output='mpl')

終結

ここでの各Z出力は、ランダムに0または1を出力します。しかし、それらの出力は相関しますか? 反相関? 完全に無関係ですか?

ビットで行ったように、いくつかの追加の円を使用してこの情報を追跡します。

パズル 11

導入

このパズルでは、4つの新しい円が表示されます。そのうちの1つは、レベル1ですでに見たものです。そこでは、2つのビット値が確実に一致するか(黒)、一致しないか(白)を追跡していました。ここでは、両方の量子ビットのZ出力に対して同じ作用をします。

練習問題

  • Z出力が確実に一致しないようにします。
initialize = [['h','0'],['h','1']]
success_condition = {'ZZ': -1.0}
allowed_gates = {'0': {'x': 0, 'z': 0, 'h': 0}, '1': {'x': 0, 'z': 0, 'h': 0}, 'both': {}}
vi = [[], True, True]
qubit_names = {'0':'q[0]', '1':'q[1]'}
puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names)
puzzle.get_circuit().draw(output='mpl')

パズル 12

導入

一番上の新しい円にも同様の作用があります。この場合、両方の量子ビットのX出力について一致する確率と一致しない確率を追跡します。

練習問題

  • X出力が確実に一致するようにします。
initialize = [['x','0']]
success_condition = {'XX': 1.0}
allowed_gates = {'0': {'x': 0, 'z': 0, 'h': 0}, '1': {'x': 0, 'z': 0, 'h': 0}, 'both': {}}
vi = [[], True, True]
qubit_names = {'0':'q[0]', '1':'q[1]'}
puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names)
puzzle.get_circuit().draw(output='mpl')

終結

この情報を表すのがこの新しい円である理由がわからない場合は、一方の量子ビットのX出力から伸びる行と、もう一方の量子ビットのX出力から伸びる別の行について考えてみてください。最上部の円は、これら2つの行が交わる場所にあります。これが、2つのX出力間の一致を表す理由です。

パズル 13

導入

説明する必要のある新しい円がまだ2つあります。1つは、q[0]からのZ出力行がq[1]からのX出力行と交わる場所です。これは、q[0]のZ出力がq[1]からのX出力と一致するかどうかを示します。もう1つも同じですが、q[0]がX出力とq[1]がZ出力です。

練習問題

  • q[0]のX出力がq[1]のZ出力と一致しないことを確認します。
initialize = []
success_condition = {'XZ': -1.0}
allowed_gates = {'0': {'x': 0, 'z': 0, 'h': 0}, '1': {'x': 0, 'z': 0, 'h': 0}, 'both': {}}
vi = [[], True, True]
qubit_names = {'0':'q[0]', '1':'q[1]'}
puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names)
puzzle.get_circuit().draw(output='mpl')

パズル 14

導入

xzhゲートが新しい円にどのように影響するかに注目してください。具体的には、xゲートは、単一のZ出力だけでなく、それらの行全体に影響を与えます。つまり、各円を黒から白(または暗い色から明るい色)に、またはその逆に反転します。

練習問題

  • 2つのZ出力をオンにします。
initialize = [['ry(-pi/4)', '1'], ['ry(-pi/4)','0']]
success_condition = {'ZI': -0.7071, 'IZ': -0.7071}
allowed_gates = {'0': {'x': 0}, '1': {'x': 0}, 'both': {}}
vi = [[], True, True]
qubit_names = {'0':'q[0]', '1':'q[1]'}
puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names)
puzzle.get_circuit().draw(output='mpl')

終結

次のパズルで分かるように、Zゲートは同じような方法でX出力の列に影響します。

パズル 15

導入

以前、hゲートが円のペアを交換する方法を見てきました。これと同じように行のペアに影響を与えます。行全体が反転されるまで、円の各ペアを反転します。再度、Hello Quantumアプリで素敵なアニメーションをチェックすることをお勧めします。

練習問題

  • X出力をオフにします。
initialize = [['x', '1'], ['x','0']]
success_condition = {'XI':1, 'IX':1}
allowed_gates = {'0': {'z': 0, 'h': 0}, '1': {'z': 0, 'h': 0}, 'both': {}}
vi = [[], True, True]
qubit_names = {'0':'q[0]', '1':'q[1]'}
puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names)
puzzle.get_circuit().draw(output='mpl')

結果

これで、2つの量子ビットがどのように見えるか、およびそれらを個別に操作する方法の基本を理解しました。しかし、2つの量子ビットゲートを使い始めると、本当の楽しみが出てきます。

レベル 3: 2量子ビットのゲート

パズル 1

導入

ビットの演習では、CNOTゲートを使用しました。量子ビットの場合、NOTの量子バージョンとしてxを使用して、同様のゲートがあります。このため、Qiskitプログラムではcxと呼びます。

従来のCNOTと同様に、cxゲートには「コントロール」と「ターゲット」があります。コントロールのZ出力を調べ、その結果で、xがターゲット量子ビットに適用されるかどうかを決定します。

このゲートを適用すると、選択した量子ビットがターゲットとして機能します。その場合、他の量子ビットがコントロールになります。

練習問題

  • cxを1回か2回使用して、q[1]のZ出力をオンにし、q[0]のZ出力をオフにします。
initialize = [['x', '0']]
success_condition = {'ZI': 1.0, 'IZ': -1.0}
allowed_gates = {'0': {'cx': 0}, '1': {'cx': 0}, 'both': {}}
vi = [[], True, True]
qubit_names = {'0':'q[0]', '1':'q[1]'}
puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names)
puzzle.get_circuit().draw(output='mpl')

パズル 2

導入

cxゲートだけでなく、czもあります。これは同じことを行いますが、xではなくzをターゲットに適用する可能性がある点が異なります。

練習問題

  • q [0]のX出力をオンにします。
initialize = [['h', '0'],['x', '1']]
success_condition = {'XI': -1.0, 'IZ': 1.0}
allowed_gates = {'0': {'cz': 0}, '1': {}, 'both': {}}
vi = [[], True, True]
qubit_names = {'0':'q[0]', '1':'q[1]'}
puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names)
puzzle.get_circuit().draw(output='mpl')

パズル 2b

導入

量子ゲートの興味深い点は、量子ゲートが何をしているのかを説明する方法が複数あることが多いということです。これらの説明は完全に互換性がないように見える場合がありますが、同じ操作です。

たとえば、czは、ターゲットの潜在的なZ出力に応じて、コントロール量子ビットにzを適用するゲートとして説明することもできます。以前とまったく同じ説明ですが、量子ビットの役割が逆になっています。それにもかかわらず、それは等しく真実です。

練習問題

  • 前回の練習問題と同じですが、量子ビットが逆になっています。ただし、czは以前と同じ方法です。
initialize = [['h', '1'],['x', '0']]
success_condition = {'IX': -1.0, 'ZI': 1.0}
allowed_gates = {'0': {}, '1': {'cz': 0},  'both': {}}
vi = [[],  True,  True]
qubit_names = {'0':'q[0]', '1':'q[1]'}
puzzle = hello_quantum.run_game(initialize, success_condition,  allowed_gates, vi, qubit_names)
puzzle.get_circuit().draw(output='mpl')

パズル 3

導入

ここで、czについて別の説明をします。hゲートと同様に、円が入れ替わっていると考えることができます。czの作用は以下のようなものです。

  • q[0]のX出力を右上の隣接する円と交換します。
  • q[1]のX出力(左上の隣の場合)でも同じことを行います。czはまた、盤面の上部にある円で何か奇妙なことをしますが、それは後で解決される謎です! ここでも、Hello Quantumアプリで素晴らしいアニメーションをみることができます。

練習問題

  • コントロールとして各量子ビットで2回czを実行し、何が起こるかを確認します。
initialize = [['h', '0'],['x', '1'],['h', '1']]
success_condition = { }
allowed_gates = {'0':{'cz': 2}, '1':{'cz': 2}, 'both': {}}
vi = [[], True, True]
qubit_names = {'0':'q[0]', '1':'q[1]'}
puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names)
puzzle.get_circuit().draw(output='mpl')

終結

これで、czについて非常に役立つことがわかりました。コントロールとしてどの量子ビットを選択しても、czはどちらの場合も同じことを行います。このため、これからはczのコントロール量子ビットを選択する必要はありません。

パズル 3b

導入

前述のように、XおよびZ出力は、量子ビットから出力を取得する2つの方法に対応します。XおよびZ測定です。これらの名前が示すように、Y測定として知られる3番目の方法もあります。

これらのパズルでは、物事を少し簡単にするために、Y測定をほとんど無視します。ただし、量子ビットの完全な説明については、Y出力がどのように見えるかを追跡する必要があります。これは、各量子ビットのY出力に対して、さらに2、3行の円を追加することを意味します。

次の演習は、Y出力行が表示されることを除いて、前回とまったく同じです。これらを使用すると、最後のパズルで見られた奇妙な効果はまったく奇妙になりません。ご自分の目で確かめてみてください!

練習問題

  • czを2回実行して、何が起こるかを確認します。
initialize = [['h', '0'],['x', '1'],['h', '1']]
success_condition = { }
allowed_gates = {'0': {}, '1': {}, 'both': {'cz': 2}}
vi = [[], True, True]
qubit_names = {'0':'q[0]', '1':'q[1]'}
puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names, mode='y')
puzzle.get_circuit().draw(output='mpl')

終結

ここでは、czによって行われた3番目の円の交換が表示されます。上部の円(両方の量子ビットのX出力の相関を表す)が中央の円(両方の量子ビットのY出力の相関を表す)と交換されます。

量子ビットの説明が完全ではなかったという理由だけで、中央の行が欠落しているとき、これは奇妙に見えました。それでも、Y出力のない単純な盤面を引き続き使用します。hello_quantum.run_game()mode = 'y'引数を使用すると自分でY出力を追加できます。

パズル 4

導入

前の練習問題では、zゲートといくつかのhゲートからxを作成しました。同様に、czといくつかのhからcxを構築することも可能です。

練習問題

  • q[1]のZ出力をオンにします。
initialize = [['x', '0']]
success_condition = {'IZ': -1.0}
allowed_gates = {'0': {'h':0}, '1': {'h':0}, 'both': {'cz': 0}}
vi = [[], True, True]
qubit_names = {'0':'q[0]', '1':'q[1]'}
puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names)
puzzle.get_circuit().draw(output='mpl')

終結

czとは異なり、cxは対称ではありません。代わりに、ターゲットがq[0]であるcxを作成したい場合は、代わりにq[0]でhを実行する必要があります。

パズル 5

導入

czの「逆方向性」を解釈することができました:つまり、ターゲット量子ビットがコントロール量子ビットの役割を果たし、また、その逆の役割も果たすという解釈です。cxでも同じことをします。ただし、このゲートには対称効果がないため、これはもう少し注意が必要です。

具体的には、コントロールのZ出力の実行内容に応じてターゲットに対してxを実行すると考えるのではなく、ターゲットのX出力の実行内容に応じてコントロールに対してzを実行すると考えることができます。

この演習では、ターゲットがコントロールを行っているように見え、コントロールがターゲットであることがわかります。

練習問題

  • q[0]のX出力をオンにします。
initialize = [['h', '0'],['h', '1']]
success_condition = {'XI': -1.0, 'IX': -1.0}
allowed_gates = {'0': {}, '1': {'z':0,'cx': 0}, 'both': {}}
vi = [[], True, True]
qubit_names = {'0':'q[0]', '1':'q[1]'}
puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names)
puzzle.get_circuit().draw(output='mpl')

終結

cxがどのように機能するかについてのこれらの2つの異なる話は矛盾しているように見えるかもしれませんが、それらは等しく有効な説明です。量子ゲートの奇妙で素晴らしい性質の素晴らしい例です。

パズル 6

導入

これで、cxのこれら2つの解釈がわかったので、非常に便利なことを行うことができます。

このパズルでは、q[1]をターゲットとしてcxを取得しますが、q[0]をターゲットとしてcxが必要になります。いくつかのhゲートのヘルプを使用して同じ効果を得る方法を理解できるかどうかを確認してください。

練習問題

  • q[1]のZ出力を維持しますが、q[0]のZ出力をオフにします。
initialize = [('x','0'),('x','1')]
success_condition = {'ZI': 1.0,'IZ': -1.0}
allowed_gates = {'0': {'h':0}, '1': {'h':0,'cx':0}, 'both': {}}
vi = [[], True, True]
qubit_names = {'0':'q[0]', '1':'q[1]'}
puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names)
puzzle.get_circuit().draw(output='mpl')

終結

これらの練習問題から何かを覚えているなら、それはおそらくこれであるはずです。実際の量子ビットデバイスでは、cxを実行できる方法を制限するのが一般的であるため、それらを方向転換する機能は非常に便利です。

パズル 7

導入

もう1つの有用な量子ゲートはswapです。これは、名前が示すとおり、2つの量子ビットの状態を交換します。Qiskitでは単にswapコマンドを呼び出すことができますが、このゲートをczまたはcxゲートから作成する方が興味深いです。

練習問題

  • 2つの量子ビットを交換します。
    • q[0]のZ出力を白、X出力を灰色にします。
    • q[1]のZ出力を濃い灰色にし、X出力を薄い灰色にします。
initialize = [['ry(-pi/4)','0'],['ry(-pi/4)','0'],['ry(-pi/4)','0'],['x','0'],['x','1']]
success_condition = {'ZI': -1.0,'XI':0,'IZ':0.7071,'IX':-0.7071}
allowed_gates = {'0': {'h':0}, '1': {'h':0}, 'both': {'cz': 0}}
vi = [[], True, True]
qubit_names = {'0':'q[0]', '1':'q[1]'}
puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names)
puzzle.get_circuit().draw(output='mpl')

終結

このパズルの解決策は、汎用のswapではない可能性があることに注意してください。今回の解決策を、次のパズルの解決策と比較してください。

パズル 8

導入

これは、swapを作成するというアイデアに基づいた別のパズルです。

練習問題

  • 2つの量子ビットを交換します。
    • q[0]のX出力を黒にします。
    • q[1]のZ出力を白にします。
  • そして、3つのczゲートでそれを行います
initialize = [['x','0'],['h','1']]
success_condition = {'XI':1,'IZ':-1}
allowed_gates = {'0': {'h':0}, '1': {'h':0}, 'both': {'cz':3}}
vi = [[], True, True]
qubit_names = {'0':'q[0]', '1':'q[1]'}
puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names,shots=2000)
puzzle.get_circuit().draw(output='mpl')

パズル 9

導入

また別のswapベースのパズル。

練習問題

  • 2つの量子ビットを交換します。
    • q[0]のZ出力をオンにします。
    • Z出力q[1]をオフにします。
initialize = [['x','1']]
success_condition = {'IZ':1.0,'ZI':-1.0}
allowed_gates = {'0': {'h':0}, '1': {'h':0}, 'both': {'cz':0}}
vi = [[], True, True]
qubit_names = {'0':'q[0]', '1':'q[1]'}
puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names,shots=2000)
puzzle.get_circuit().draw(output='mpl')

レベル 4:クリフォードゲートを越えて

パズル 1a

導入

これまでに見たゲートは「クリフォードゲート」と呼ばれています。それらは、量子コンピューターで情報を移動および操作するために非常に重要です。ただし、クリフォードゲートのみを使用して標準的なコンピューターよりも優れたパフォーマンスを発揮できるアルゴリズムを作成することはできません。新しいゲートが必要です。

このパズルで試すことができます。数回実行して、何ができるかを確認してください。

練習問題

  • ry(pi/4)をq[0]に4回適用します。
initialize = []
success_condition = {}
allowed_gates = {'0': {'ry(pi/4)': 4}, '1': {}, 'both': {}}
vi = [[], True, True]
qubit_names = {'0':'q[0]', '1':'q[1]'}
puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names)
puzzle.get_circuit().draw(output='mpl')

終結

よくできました!残りの問題で、これを理解するために新しいことを試してみましょう。

パズル 1b

導入

今見たゲートをよりよく理解するために、量子ビットを視覚化するために少し異なる方法を使用します。この場合、確実に0になる出力は、白い円ではなく白い線で表されます。1を与えることが確実な出力は、黒い円ではなく黒い線になります。ランダムな出力の場合、灰色の円ではなく、一部が白で一部が黒の線が表示されます。

これは、この新しい視覚化に慣れるのに役立つ古い練習問題です。

練習問題

  • X出力が確実に一致するようにします。
initialize = [['x','0']]
success_condition = {'XX': 1.0}
allowed_gates = {'0': {'x': 0, 'z': 0, 'h': 0}, '1': {'x': 0, 'z': 0, 'h': 0}, 'both': {}}
vi = [[], True, True]
qubit_names = {'0':'q[0]', '1':'q[1]'}
puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names, mode='line')
puzzle.get_circuit().draw(output='mpl')

パズル 1c

導入

このパズルでは、ブロッホで新しいことができることがわかります。これは実際にはゲートではなく、量子プログラムには表示されません。代わりに、各量子ビットの2本の線を互いに重ねて描画することにより、見た目を変更するだけです。また、それらのレベルが交差するポイントを配置します。ブロッホを使用すると、ry(pi / 4)がどのように機能するかを理解できるはずです。

練習問題

  • q[0]の一番下の行を完全にオンにして、ブロッホゲートを使用します。
initialize = []
success_condition = {'ZI': -1.0}
allowed_gates = {'0': {'bloch':1, 'ry(pi/4)': 0}, '1':{}, 'both': {'unbloch':0}}
vi = [[], True, True]
qubit_names = {'0':'q[0]', '1':'q[1]'}
puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names, mode='line')
puzzle.get_circuit().draw(output='mpl')

終結

ポイントをたどった場合、ry(pi/4)の効果はポイントを$\pi/4$ラジアン(45度)回転させることであることに気付くはずです。

線のレベルもそれに合わせて変化します。ry(-pi/4)ゲートの効果は、回転が反対方向であることを除いて同じです。

おそらくお気づきかもしれませんが、ブロッホを使用すると、各量子ビットの2つの線が組み合わされるだけではなく、行全体が組み合わされます。

パズル 2

導入

では、これらのゲートを他の量子ビットでも使用しましょう。

練習問題

  • ボトムラインを完全にオンにします。
initialize = [['h','0'],['h','1']]
success_condition = {'ZI': -1.0,'IZ': -1.0}
allowed_gates = {'0': {'bloch':0, 'ry(pi/4)': 0, 'ry(-pi/4)': 0}, '1': {'bloch':0, 'ry(pi/4)': 0, 'ry(-pi/4)': 0}, 'both': {'unbloch':0}}
vi = [[], True, True]
qubit_names = {'0':'q[0]', '1':'q[1]'}
puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names, mode='line')
puzzle.get_circuit().draw(output='mpl')

パズル 3

導入

これはcxczhがあれば解くことができるパズルですが、残念ながら、今回は、cxとhがありません。そのため、czryがどのように機能するかを理解する必要があります。

練習問題

  • Z出力を一致させます。
initialize = [['h','0']]
success_condition = {'ZZ': 1.0}
allowed_gates = {'0': {}, '1': {'bloch':0, 'ry(pi/4)': 0, 'ry(-pi/4)': 0}, 'both': {'unbloch':0,'cz':0}}
vi = [[], True, True]
qubit_names = {'0':'q[0]', '1':'q[1]'}
puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names, mode='line')
puzzle.get_circuit().draw(output='mpl')

パズル 4

導入

xまたはzを使用すると、ry反転させて、反対方向に向けることができます。

練習問題

  • それぞれに1つのry(pi/4)を付けて、Z出力を完全にオフにします。
initialize = [['ry(pi/4)','0'],['ry(pi/4)','1']]
success_condition = {'ZI': 1.0,'IZ': 1.0}
allowed_gates = {'0': {'bloch':0, 'z':0, 'ry(pi/4)': 1}, '1': {'bloch':0, 'x':0, 'ry(pi/4)': 1}, 'both': {'unbloch':0}}
vi = [[], True, True]
qubit_names = {'0':'q[0]', '1':'q[1]'}
puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names, mode='line')
puzzle.get_circuit().draw(output='mpl')

パズル 5

導入

ryを使用すると、czcxよりも興味深い条件付きゲートを作成できます。たとえば、制御hを作成できます。

練習問題

  • q[1]の量子ビットで1回のry(pi/4)ry(-pi/4)を使用して、q[1]のZ出力をオフにします。
initialize = [['x','0'],['h','1']]
success_condition = {'IZ': 1.0}
allowed_gates = {'0': {}, '1': {'bloch':0, 'cx':0, 'ry(pi/4)': 1, 'ry(-pi/4)': 1}, 'both': {'unbloch':0}}
vi = [[], True, True]
qubit_names = {'0':'q[0]', '1':'q[1]'}
puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names, mode='line')
puzzle.get_circuit().draw(output='mpl')

ボーナスレベル: Sandbox

これで、完全に強力な量子プログラムを構築するのに十分な基本的な量子ゲートを理解できました。最終レベルでこれを味わうことができます。しかしその前に、ここにいくつかのボーナスレベルがあります。

まず、新しいスキルを試すためのSandboxです。すべてのゲートが有効になっている2つの図面で、遊んでみてください。

クリフォード以外のゲートもサポートするために、線での描写になっています。

initialize = []
success_condition = {'IZ': 1.0,'IX': 1.0}
allowed_gates = {'0': {'bloch':0, 'x':0, 'z':0, 'h':0, 'cx':0, 'ry(pi/4)': 0, 'ry(-pi/4)': 0}, '1': {'bloch':0, 'x':0, 'z':0, 'h':0, 'cx':0, 'ry(pi/4)': 0, 'ry(-pi/4)': 0}, 'both': {'cz':0, 'unbloch':0}}
vi = [[], True, True]
qubit_names = {'0':'q[0]', '1':'q[1]'}
line_sandbox = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names, mode='line')
line_sandbox.get_circuit().draw(output='mpl')

これは、y測定の出力を表す中央の線のある図面です。新しい非クリフォードゲート、rx(pi/4)rx(-pi /4)も試すことができます。

initialize = []
success_condition = {'IZ': 1.0,'IX': 1.0}
allowed_gates = {'0': {'x':0, 'z':0, 'h':0, 'cx':0, 'ry(pi/4)': 0, 'rx(pi/4)': 0, 'ry(-pi/4)': 0, 'rx(-pi/4)': 0}, '1': {'x':0, 'z':0, 'h':0, 'cx':0, 'ry(pi/4)': 0, 'rx(pi/4)': 0, 'ry(-pi/4)': 0, 'rx(-pi/4)': 0}, 'both': {'cz':0}}
vi = [[], True, True]
qubit_names = {'0':'q[0]', '1':'q[1]'}
y_sandbox = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names, mode='y')
y_sandbox.get_circuit().draw(output='mpl')

ボーナスレベル: 自分でパズルをつくってみましょう。

こちらからパズルを提供するだけでなく、みなさんがご自分でパズルを作成できるようにしました。Quantum Experienceを使用すると、自分のパズルのnotebookを簡単に作成できます。

このページの「New Notebook」のボタンをクリックして開始します。次に、importのリストに次の行を追加します。

from qiskit_textbook.games import hello_quantum

これにより、パズルを作成するために必要なツールが提供されます。具体的には、次のフォームを使用してrun_game関数への独自の呼び出しを作成する必要があります。

puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names, mode=None)

これのすべての要素を以下に説明します。

puzzle

  • これは、作成したパズルを含むオブジェクトです。
  • プレイヤーが作成した量子回路には、puzzle.get_circuit()を使用してアクセスできます。

initialize

  • 初期状態を準備するために、パズルが始まる前に適用されたゲートのリスト。
  • これが空の場合、量子ビットのデフォルトの初期状態が使用されます(Z出力は確実に0になります)。
  • サポートされている単一量子ビットゲート(量子ビット「0」または「1」に適用)は、xyzh、およびry(pi/ 4)です。
  • サポートされている2量子ビットゲートはczcxです。これらには、ターゲット量子ビットのみを指定します。
  • 例: initialize = [['x', '0'],['cx', '1']]

success_condition

  • パズルが一致を宣言するために取得する必要のあるパウリの観測値。
  • 次の形式で、キーが円の名前である辞書として表されます。
    • ZIは左側の量子ビットのZ出力を示し、XIはそのX出力を示します。
    • IZIXも同様に、右側の量子ビットの出力です。
    • ZXは、左側の量子ビットのZ出力と右側のX出力の間の相関関係の円を指します。
    • 上記以外にも様々です。
  • 辞書の値は、パズルを完成させるためにこれらの円が保持しなければならない値です。
    • 黒の場合は1.0
    • 白の場合は-1.0
    • 灰色の場合は0.0
    • 上記以外にも様々です。
  • 条件に含めたい円のみをリストする必要があります。
  • 例:success_condition = {'IZ':1.0}

allowed_gates

  • 量子ビットごとに、このパズルで許可される操作を指定します。
  • 量子ビットを指定する必要のない操作(czおよびunbloch)の場合は、量子ビット0または1ではなく「両方」に操作を割り当てます。
  • ゲートは、値が整数の辞書として表されます。
    • 整数がゼロ以外の場合、パズルを正常に解くためにゲートを使用する必要がある正確な回数を指定します。
    • ゼロの場合、プレイヤーはゲートを何度でも使用できます。
  • 例: allowed_gates = {'0':{'h':0}, '1':{'h':0}, 'both':{'cz':1}}

vi

  • 3つの要素リストとしてのいくつかの視覚化情報: vi = [hidden,qubit,corr] これらは以下を指定します:
    • hidden:非表示の量子ビット(両方が表示されている場合は空のリスト)。
    • qubit:各量子ビットに両方の円が表示されているかどうか。(量子ビットパズルにはTrueを使用し、ビットパズルにはFalseを使用します)。
    • corr:相関円(中央の4つ)が表示されているかどうか。
  • 例: vi = [[],True,True]

qubit_names

  • 2つの量子ビットは常に内部で0および1と呼ばれます。ただし、プレーヤーの場合は、別の名前を表示できます。
  • 例: qubit_names = {'0': 'qubit 0', '1': 'qubit 1'}

mode

  • オプションの引数。これにより、Y出力行を含めるか、行ベースの視覚化を使用するかを選択できます。
  • mode = Noneを使用するか、mode引数をまったく含めない場合、デフォルトのモードになります。
  • mode = 'y'には、Y出力の行が含まれます。
  • mode = 'line'は、線ベースの視覚化を提供します。

ここに示した例で定義されているパズルは、次のセルで実行できます。

initialize = [['x', '0'],['cx', '1']]
success_condition = {'IZ': 1.0}
allowed_gates = {'0': {'h':0}, '1': {'h':0}, 'both': {'cz': 1}}
vi = [[], True, True]
qubit_names = {'0':'qubit 0', '1':'qubit 1'}
mode = None
puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names, mode=mode)

プレイヤーに何を目指すべきかを伝えることを忘れないでください。このノートブックのすべてのパズルについて、ターゲットの状態がテキストブックで記述されていました。ただし、代わりに、次の方法で、プレーヤーへの短いメッセージを含む、ターゲット状態のイメージを作成できます。これも、上記のパラメータの例を使用しています。

message = '\nRules:\n    Use exactly one cz gate.'
grid = hello_quantum.pauli_grid(mode=mode)
grid.update_grid(rho=success_condition, hidden=vi[0], qubit=vi[1], corr=vi[2], message=message)

一致させる必要のある円のみが表示されていることに注意してください。

パズルを他の人と共有するには、作成したノートブックを保存して他の人に送信するだけです。Quantum Experienceのこのページの「インポート」ボタンを使用して、実行して、プレイできます。

レベル 5: 量子変数の一意性の証明

古典変数を用いたベルのテスト

ここでは、量子変数(量子ビットに基づく)が標準のもの(ビットに基づく)とどのように異なるかを調査します。

これを行うには、ABと呼ぶ変数のペアを作成します。これらが何であるか、またはどのように初期化されるかについては、条件を設定しません。したがって、多くの可能性があります。

  • それらは、次のような任意の種類の変数である可能性があります。

    • 整数
    • リスト
    • 辞書
    • ...
  • それらは、次のようなあらゆる種類のプロセスによって初期化できます。

    • 空のまま
    • 与えられた値のセットで満たす
    • 与えられたランダムプロセスによって生成する
      • AとBに独立して適用する
      • AとBに一緒に適用され、それらのランダム性における相関を与える

変数がランダムプロセスによって初期化される場合、プログラムを実行するたびに異なる値を持つことを意味します。これはまったく問題ありません。従う必要がある唯一のルールは、ランダム性を生成するプロセスがすべての実行において同じであるということです。

以下の関数を使用して、これらの変数を設定します。これには現在、部分的に相関するランダム浮動小数点数として定義されたABがあります。しかし、あなたはそれをあなたが望むものに変えることができます。

import random
def setup_variables ():
    
    ### セクションを必要なものに置き換えます ###
    
    r = random.random()
    
    A = r*(2/3)
    B = r*(1/3)
    
    ### セクションの最後 ###
    
    return A, B

次の仕事は、ハッシュ関数を定義することです。これを行うには、変数の1つを入力として受け取り、ビット値を出力として与える必要があります。

この関数は、2つの異なるタイプのハッシュを実行できる必要もあります。つまり、変数をかみ砕いて、さまざまな方法で出力する必要があります。したがって、ハッシュの種類を関数に指示します。

プログラムの残りの部分と一貫性を保つために、2つの可能なハッシュタイプはHおよびVと呼ばれる必要があります。 また、出力は0または1 のいずれかの単一値ビット文字列の形式である必要があります。

次の(かなり恣意的な)例では、AとBを特定の値と比較することで、ビットを作ります。その値を下回る場合、出力は1であり、それ以外の場合、出力は0です。 ハッシュのタイプによって、使用される値が決まります。

def hash2bit ( variable, hash ):
    
    ### セクションを必要なものに置き換えます ###
    
    if hash=='V':
        bit = (variable<0.5)
    elif hash=='H':
        bit = (variable<0.25)
        
    bit = str(int(bit)) # Turn True or False into '1' and '0'
    
    ### セクションの最後 ###
        
    return bit

これらが定義されたら、計算したい4つの量があります:P['HH']P['HV']P['VH']、およびP['VV']

例としてP['HV']に焦点を当てましょう。これは、AHタイプのハッシュから派生したビット値がBVタイプのハッシュから派生したビット値と異なる確率です。何度もサンプリングし、対応するビット値が一致しないサンプルの割合を決定することにより、この確率を推定します。

他の確率も同様に定義されます。P['HH']はAとBの両方でHタイプのハッシュを比較し、P['VV']は両方でVタイプのハッシュを比較し、P['VH']は、AのVタイプのハッシュとBのHタイプのハッシュを比較します。 P['HH']ABの両方でHタイプのハッシュを比較し、P['VV']は両方でVタイプのハッシュを比較し、P['VH']はのAVタイプのハッシュとBHタイプのハッシュを比較します。

これらの確率は、辞書内のPのすべての値を返す次の関数で計算されます。パラメータショットは、使用するサンプルの数です。

shots = 8192
def calculate_P ( ):
    
    P = {}
    for hashes in ['VV','VH','HV','HH']:
        
        # calculate each P[hashes] by sampling over `shots` samples
        P[hashes] = 0
        for shot in range(shots):

            A, B = setup_variables()

            a = hash2bit ( A, hashes[0] ) # hash type for variable `A` is the first character of `hashes`
            b = hash2bit ( B, hashes[1] ) # hash type for variable `B` is the second character of `hashes`

            P[hashes] += (a!=b) / shots
 
    return P

次に、変数を設定してハッシュするために選択したメソッドのこれらの値を実際に計算してみましょう。

P = calculate_P()
print(P)

これらの値は、有限数のショットしか使用しないため、実行ごとにわずかに異なります。それらを大幅に変更するには、変数の開始方法やハッシュ関数の定義方法を変更する必要があります。

これらの関数がどのように定義されていても、Pの値が常に従う、ある制限があります。

たとえば、P['HV']P['VH']、およびP['VV']がすべて0.0である場合を考えてみます。これが可能な唯一の方法は、P['HH']0.0にすることです。

この理由を理解するために、hash2bit(A, H)hash2bit(B,V)がどの実行でも違う値にならないことをP['HV'] = 0.0が示していることから始めましょう。つまり、これは常にそれらが等しいことを期待できることを意味します。

hash2bit(A, H) = hash2bit(B, V)        (1)

P['VV'] = 0.0およびP['VH'] = 0.0から、同様に以下が取得できます。

hash2bit(A, V) = hash2bit(B, V)        (2)

hash2bit(A, V) = hash2bit(B, H)        (3)

(1)と(2)を組み合わせると、

hash2bit(A, H) = hash2bit(A, V)        (4)

これを(3)と組み合わせると、

hash2bit(A, H) = hash2bit(B, H)        (5)

そして、これらの値が常に等しい場合、異なる実行はありえません。これはまさに私たちが証明しようとしたものです: P['HH'] = 0.0

より一般的には、P['HV']P['VH']、およびP['VV']の値を使用して、P['HH']の上限を設定できます。CHSH不等式を適応させることにより、次のことがわかります。

$\,\,\,\,\,\,\,$ P['HH'] $\, \leq \,$ P['HV'] + P['VH'] + P['VV']

これは、P['HH’]だけの特別な特徴ではありません。他のすべての確率にも当てはまります。これらの確率のそれぞれは、他の確率の合計より大きくすることはできません。

この論理が成り立つかどうかをテストするために、確率がこれらの不等式にどれだけよく従うかを確認します。P値が正確ではなく、限られた数のサンプルを使用して推定されているため、わずかな違反が発生する可能性があることに注意してください。

def bell_test (P):
    
    sum_P = sum(P.values())
    for hashes in P:
        
        bound = sum_P - P[hashes]
        
        print("The upper bound for P['"+hashes+"'] is "+str(bound))
        print("The value of P['"+hashes+"'] is "+str(P[hashes]))
        if P[hashes]<=bound:
            print("The upper bound is obeyed :)\n")
        else:
            if P[hashes]-bound < 0.1:
                print("This seems to have gone over the upper bound, but only by a little bit :S\nProbably just rounding errors or statistical noise.\n")
            else:
                print("!!!!! This has gone well over the upper bound :O !!!!!\n")
bell_test(P)

このノートブックで提供されている初期化関数とハッシュ関数を使用すると、P('HV')の値は上界とほぼ同じになります。数値は統計的に推定されており、統計的なノイズの影響で若干近似的な値になっており、少しオーバーすることもあるかもしれません。しかし、それが限界を大幅に超えることは決してありません。

あなたが私を信じられないのであれば、自分で試してみてください。変数の初期化方法とハッシュの計算方法を変更し、境界の1つが大幅に破られるようにします。

量子変数を用いたベルのテスト

ここで、変数AとBが量子変数になることを除いて、同じことをもう一度やり直します。具体的には、それらは最も単純な種類の量子変数、つまり量子ビットになります。

量子プログラムを作成するときは、使用する前に量子ビットとビットを設定する必要があります。これは、以下の関数によって実行されます。2ビットのレジスタを定義し、それらを変数AおよびBとして割り当てます。次に、出力を受信するために2ビットのレジスタを設定し、それらをaおよびbとして割り当てます。

最後に、これらのレジスタを使用して空の量子プログラムを設定します。これはqcと呼ばれます。

from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit

def initialize_program ():
    
    qubit = QuantumRegister(2)
    A = qubit[0]
    B = qubit[1]
    
    bit = ClassicalRegister(2)
    a = bit[0]
    b = bit[1]
    
    qc = QuantumCircuit(qubit, bit)
    
    return A, B, a, b, qc

変数を設定するための量子プログラムを書き始める前に、プログラムの最後に何が必要かを考えてみましょう。ここで、量子ビットをビットに変換するさまざまなハッシュ関数を定義します。

量子ビットからビットを抽出する最も簡単な方法は、measureゲートを使用することです。これは、これまで使用してきた視覚化における量子ビットのZ出力に対応します。これをVタイプのハッシュとして使用しましょう。

X出力に対応する出力の場合、直接アクセスする手段はありません。ただし、最初にhを実行してトップ出力とZ出力を交換し、次にmeasureゲートを使用することで、間接的に行うことができます。これがHタイプのハッシュになります。

この関数には、従来の関数よりも多くの入力があることに注意してください。結果を書き込むビットと、ゲートを書き込む量子プログラムqcを指定する必要があります。

def hash2bit  ( variable, hash, bit, qc ):
    
    if hash=='H':
        qc.h( variable )
        
    qc.measure( variable, bit )

次に、変数AとBを設定します。このプログラムを作成するには、以下の盤面を使用できます。提案された演習に従うか、好きなことをすることができます。準備ができたら、次に進みます。setup_variables()関数を含むセルは、盤面で作成したプログラムを使用します。

選択するということは、確率P['HH']P['HV']P['VH']、およびP['VV']が盤面上の円に明示的に対応することを意味することに注意してください。たとえば、一番上の円は、2つのX出力が一致しない可能性がどの程度あるかを示しています。これが白の場合、P['HH'] = 1、黒の場合、P['HH'] = 0です。

練習問題

  • 両方の量子ビットのX出力が一致しない可能性が高く、他のすべての出力の組み合わせが一致する可能性が高くなるようにします。
initialize = []
success_condition = {'ZZ':+0.7071,'ZX':+0.7071,'XZ':+0.7071,'XX':-0.7071}
allowed_gates = {'0': {'bloch':0, 'x':0, 'z':0, 'h':0, 'cx':0, 'ry(pi/4)': 0, 'ry(-pi/4)': 0}, '1': {'bloch':0, 'x':0, 'z':0, 'h':0, 'cx':0, 'ry(pi/4)': 0, 'ry(-pi/4)': 0}, 'both': {'cz':0, 'unbloch':0}}
vi = [[], True, True]
qubit_names = {'0':'A', '1':'B'}
puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names, mode='line')

ここで、上記のプログラムを使用して量子変数を設定します。

import numpy as np
def setup_variables ( A, B, qc ):
    
    for line in puzzle.program:
        eval(line)

Pの値は、以下の関数で計算されます。これでは、ここでは、このnotebookの他の部分のパズルと同様に、可能な出力を与えたサンプルの数がわかる結果を得ています。出力はビット文字列、文字列として提供され、Qiskitは右から左に番号を付けます。 これは、bit[0]に対応するaの値が右から1番目であることを意味します。

a = string[-1]

bの値は、右から2番目のすぐ隣にあります。

b = string[-2]


このビット文字列のサンプル数は、結果の辞書型statsによってstats [string]として提供されます。

shots = 8192
from qiskit import execute

def calculate_P ( backend ):
    
    P = {}
    program = {}
    for hashes in ['VV','VH','HV','HH']:

        A, B, a, b, program[hashes] = initialize_program ()

        setup_variables( A, B, program[hashes] )

        hash2bit ( A, hashes[0], a, program[hashes])
        hash2bit ( B, hashes[1], b, program[hashes])
            
    # submit jobs
    job = execute( list(program.values()), backend, shots=shots )

    # get the results
    for hashes in ['VV','VH','HV','HH']:
        stats = job.result().get_counts(program[hashes])
        
        P[hashes] = 0
        for string in stats.keys():
            a = string[-1]
            b = string[-2]
            
            if a!=b:
                P[hashes] += stats[string] / shots

    return P

次に、実際に使用するデバイスを選択してセットアップします。デフォルトでは、シミュレーターを使用します。代わりに、バックエンドを適宜変更することで、実際のクラウドベースのデバイスを使用できます。

from qiskit import Aer
device = 'qasm_simulator'
backend = Aer.get_backend(device)
P = calculate_P( backend )
print(P)
bell_test( P )

練習問題で状態の提案を準備した場合、P['HH']の上限に重大な違反があることがわかります。では、ここで何が起こっているのでしょうか?ベルのテストに基づいた論理の連鎖は、明らかに量子変数には適用されません。しかし、なぜでしょうか?

答えは、その論理には隠された仮定があるということです。その理由を理解するために、式(4)に戻りましょう。

hash2bit ( A, H ) = hash2bit ( A, V )        (4)

ここでは、変数AHタイプのハッシュから取得する値をVタイプのハッシュの値と比較します。

古典的な変数の場合、これは完全に理にかなっています。両方のハッシュを計算して結果を比較することを妨げるものは何もありません。変数のハッシュを計算すると変数が変更されても、それは問題ではありません。事前にコピーするだけで、両方のハッシュを問題なく実行できます。

同じことは量子変数には当てはまりません。ハッシュの結果は、実際に実行するまでわかりません。量子ビットが実際に与えるビット値を決定するのはその時だけです。そして、あるタイプのハッシュの値を決定すると、別のタイプのハッシュを使用した場合に何を決定したかを判断することはできません。量子変数はコピーできないため、量子変数をコピーしてもこれを回避することはできません。つまり、hash2bit(A, H)hash2bit(A, V)の値が同時に明確に定義されているコンテキストがないため、それらを比較することはできません。

もう1つの隠れた仮定は、hash2bit(A,hash)は、変数Aに選択されたハッシュのタイプにのみ依存し、変数Bに選択されたハッシュのタイプには依存しないということです。ただし、上界が違反しているという事実は、それぞれの変数が他の変数に対してどのようなハッシュが行われているかを知っているということを暗示しているように見えます。したがって、両方がHタイプのハッシュを持っている場合、それらは共謀して非常に異なる動作を与えることができます。

そうは言っても、我々の選択したハッシュが他のビットでの結果に影響を与えるとは言えません。その効果はそれよりも微妙です。たとえば、どの変数がどの変数に影響を与えているかを判断することはできません。ハッシュが実行される順序を変更したり、効果的に同時に実行したりすると、同じ結果が得られます。私たちが言えることは、結果は文脈的であるということです。ある変数からの結果を完全に理解するには、別の変数に対して何が行われたかを調べる必要がある場合があります。

これはすべて、量子変数が常に私たちが慣れている論理に従うとは限らないことを示しています。それらは異なる規則、つまり量子力学の規則に従います。これにより、新しい異なる方法で計算を実行する方法を見つけることができます。