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つの可能な出力値があることです。これらは、1
と0
、または「オン」と「オフ」、または「真」と「偽」と呼ぶことができます。私たちが使用する名前は重要ではありません。重要な点は、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)
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)
パズル 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)
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)
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つの灰色の円が使用されます。 この違いを見分けるために,問題ボードに何かを追加する必要があります。
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)
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)
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)
パズル 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)
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)
パズル 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)
puzzle.get_circuit().draw(output='mpl')
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')
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')
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')
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')
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')
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')
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')
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')
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')
パズル 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')
パズル 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')
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')
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')
パズル 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')
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')
パズル 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')
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')
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')
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')
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')
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')
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')
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')
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')
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')
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')
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')
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」に適用)は、
x
、y
、z
、h
、およびry(pi/ 4)
です。 - サポートされている2量子ビットゲートは
cz
とcx
です。これらには、ターゲット量子ビットのみを指定します。 - 例:
initialize = [['x', '0'],['cx', '1']]
success_condition
- パズルが一致を宣言するために取得する必要のあるパウリの観測値。
- 次の形式で、キーが円の名前である辞書として表されます。
ZI
は左側の量子ビットのZ出力を示し、XI
はそのX出力を示します。IZ
とIX
も同様に、右側の量子ビットの出力です。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のこのページの「インポート」ボタンを使用して、実行して、プレイできます。
古典変数を用いたベルのテスト
ここでは、量子変数(量子ビットに基づく)が標準のもの(ビットに基づく)とどのように異なるかを調査します。
これを行うには、A
とB
と呼ぶ変数のペアを作成します。これらが何であるか、またはどのように初期化されるかについては、条件を設定しません。したがって、多くの可能性があります。
それらは、次のような任意の種類の変数である可能性があります。
- 整数
- リスト
- 辞書
- ...
それらは、次のようなあらゆる種類のプロセスによって初期化できます。
- 空のまま
- 与えられた値のセットで満たす
- 与えられたランダムプロセスによって生成する
- AとBに独立して適用する
- AとBに一緒に適用され、それらのランダム性における相関を与える
変数がランダムプロセスによって初期化される場合、プログラムを実行するたびに異なる値を持つことを意味します。これはまったく問題ありません。従う必要がある唯一のルールは、ランダム性を生成するプロセスがすべての実行において同じであるということです。
以下の関数を使用して、これらの変数を設定します。これには現在、部分的に相関するランダム浮動小数点数として定義されたA
とB
があります。しかし、あなたはそれをあなたが望むものに変えることができます。
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']
に焦点を当てましょう。これは、A
のH
タイプのハッシュから派生したビット値がB
のV
タイプのハッシュから派生したビット値と異なる確率です。何度もサンプリングし、対応するビット値が一致しないサンプルの割合を決定することにより、この確率を推定します。
他の確率も同様に定義されます。P['HH']はAとBの両方でHタイプのハッシュを比較し、P['VV']は両方でVタイプのハッシュを比較し、P['VH']は、AのVタイプのハッシュとBのHタイプのハッシュを比較します。
P['HH']
はA
とB
の両方でH
タイプのハッシュを比較し、P['VV']
は両方でV
タイプのハッシュを比較し、P['VH']
はのA
のV
タイプのハッシュとB
のH
タイプのハッシュを比較します。
これらの確率は、辞書内の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つが大幅に破られるようにします。
量子プログラムを作成するときは、使用する前に量子ビットとビットを設定する必要があります。これは、以下の関数によって実行されます。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)
ここでは、変数A
のH
タイプのハッシュから取得する値をV
タイプのハッシュの値と比較します。
古典的な変数の場合、これは完全に理にかなっています。両方のハッシュを計算して結果を比較することを妨げるものは何もありません。変数のハッシュを計算すると変数が変更されても、それは問題ではありません。事前にコピーするだけで、両方のハッシュを問題なく実行できます。
同じことは量子変数には当てはまりません。ハッシュの結果は、実際に実行するまでわかりません。量子ビットが実際に与えるビット値を決定するのはその時だけです。そして、あるタイプのハッシュの値を決定すると、別のタイプのハッシュを使用した場合に何を決定したかを判断することはできません。量子変数はコピーできないため、量子変数をコピーしてもこれを回避することはできません。つまり、hash2bit(A, H)
とhash2bit(A, V)
の値が同時に明確に定義されているコンテキストがないため、それらを比較することはできません。
もう1つの隠れた仮定は、hash2bit(A,hash)
は、変数A
に選択されたハッシュのタイプにのみ依存し、変数B
に選択されたハッシュのタイプには依存しないということです。ただし、上界が違反しているという事実は、それぞれの変数が他の変数に対してどのようなハッシュが行われているかを知っているということを暗示しているように見えます。したがって、両方がHタイプのハッシュを持っている場合、それらは共謀して非常に異なる動作を与えることができます。
そうは言っても、我々の選択したハッシュが他のビットでの結果に影響を与えるとは言えません。その効果はそれよりも微妙です。たとえば、どの変数がどの変数に影響を与えているかを判断することはできません。ハッシュが実行される順序を変更したり、効果的に同時に実行したりすると、同じ結果が得られます。私たちが言えることは、結果は文脈的であるということです。ある変数からの結果を完全に理解するには、別の変数に対して何が行われたかを調べる必要がある場合があります。
これはすべて、量子変数が常に私たちが慣れている論理に従うとは限らないことを示しています。それらは異なる規則、つまり量子力学の規則に従います。これにより、新しい異なる方法で計算を実行する方法を見つけることができます。