アリスがボブに量子情報を送りたいとしましょう。これを具体的に、アリスがボブに状態 $\vert\psi\rangle = \alpha\vert0\rangle + \beta\vert1\rangle$ を送りたい、と仮定します。その為にはまず、$\alpha$ と $\beta$ の情報をボブに渡す必要があります。
量子力学には、未知の量子状態の単純な複製を正確に作ることは出来ない、という定理が存在します。量子複製不可能定理(no-cloning theorem)として知られているものです。この定理ゆえ、アリスが単純に $\vert\psi\rangle$ の複製を生成してそれをボブに渡す、という事は出来ません。状態 の複製は古典的な計算でのみ可能なのです(重ね合わせはコピーできません)。
しかし、2つの古典的ビットとエンタングルメントを利用することで、アリスは状態 $\vert\psi\rangle$ をボブに転送できます。その結果、最終的にボブが $\vert\psi\rangle$ を持ち、アリスがもはや何も持っていない事から、我々はこれをテレポーテーションと呼んでいます。
2. 量子テレポーテーション・プロトコル
量子ビットを転送するためには、アリスとボブは第三者(テラモン)を使ってエンタングルした量子ビットのペアを作り、それを彼ら自身に送ってもらわなくてはなりません。その後アリスは、彼女の量子ビットにいくつかの演算子を作用させ、その結果を古典通信チャンネルを通してボブに送信します。ボブはそれに従って彼の量子ビットにいくつか演算子を作用させることで、結果的にアリスの量子ビットを受け取ることが出来ます。
これらの手順を、以下では量子回路を使って説明していきます。ここでは実際に量子ビットを「送る」わけではありませんので、想像しながら進んでください!
まず初めに、セッションをセットアップしていきます:
# Do the necessary imports
import numpy as np
from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister
from qiskit import IBMQ, Aer, transpile, assemble
from qiskit.visualization import plot_histogram, plot_bloch_multivector, array_to_latex
from qiskit.extensions import Initialize
from qiskit_textbook.tools import random_state
それでは量子回路を作成していきます:
## SETUP
# Protocol uses 3 qubits and 2 classical bits in 2 different registers
qr = QuantumRegister(3, name="q") # Protocol uses 3 qubits
crz = ClassicalRegister(1, name="crz") # and 2 classical bits
crx = ClassicalRegister(1, name="crx") # in 2 different registers
teleportation_circuit = QuantumCircuit(qr, crz, crx)
def create_bell_pair(qc, a, b):
"""Creates a bell pair in qc using qubits a & b"""
qc.h(a) # Put qubit a into state |+>
qc.cx(a,b) # CNOT with a as control and b as target
## SETUP
# Protocol uses 3 qubits and 2 classical bits in 2 different registers
qr = QuantumRegister(3, name="q")
crz, crx = ClassicalRegister(1, name="crz"), ClassicalRegister(1, name="crx")
teleportation_circuit = QuantumCircuit(qr, crz, crx)
## STEP 1
# In our case, Telamon entangles qubits q1 and q2
# Let's apply this to our circuit:
create_bell_pair(teleportation_circuit, 1, 2)
# And view the circuit so far:
teleportation_circuit.draw()
def alice_gates(qc, psi, a):
qc.cx(psi, a)
qc.h(psi)
## SETUP
# Protocol uses 3 qubits and 2 classical bits in 2 different registers
qr = QuantumRegister(3, name="q")
crz, crx = ClassicalRegister(1, name="crz"), ClassicalRegister(1, name="crx")
teleportation_circuit = QuantumCircuit(qr, crz, crx)
## STEP 1
create_bell_pair(teleportation_circuit, 1, 2)
## STEP 2
teleportation_circuit.barrier() # Use barrier to separate steps
alice_gates(teleportation_circuit, 0, 1)
teleportation_circuit.draw()
def measure_and_send(qc, a, b):
"""Measures qubits a & b and 'sends' the results to Bob"""
qc.barrier()
qc.measure(a,0)
qc.measure(b,1)
## SETUP
# Protocol uses 3 qubits and 2 classical bits in 2 different registers
qr = QuantumRegister(3, name="q")
crz, crx = ClassicalRegister(1, name="crz"), ClassicalRegister(1, name="crx")
teleportation_circuit = QuantumCircuit(qr, crz, crx)
## STEP 1
create_bell_pair(teleportation_circuit, 1, 2)
## STEP 2
teleportation_circuit.barrier() # Use barrier to separate steps
alice_gates(teleportation_circuit, 0, 1)
## STEP 3
measure_and_send(teleportation_circuit, 0 ,1)
teleportation_circuit.draw()
# This function takes a QuantumCircuit (qc), integer (qubit)
# and ClassicalRegisters (crz & crx) to decide which gates to apply
def bob_gates(qc, qubit, crz, crx):
# Here we use c_if to control our gates with a classical
# bit instead of a qubit
qc.x(qubit).c_if(crx, 1) # Apply gates if the registers
qc.z(qubit).c_if(crz, 1) # are in the state '1'
## SETUP
# Protocol uses 3 qubits and 2 classical bits in 2 different registers
qr = QuantumRegister(3, name="q")
crz, crx = ClassicalRegister(1, name="crz"), ClassicalRegister(1, name="crx")
teleportation_circuit = QuantumCircuit(qr, crz, crx)
## STEP 1
create_bell_pair(teleportation_circuit, 1, 2)
## STEP 2
teleportation_circuit.barrier() # Use barrier to separate steps
alice_gates(teleportation_circuit, 0, 1)
## STEP 3
measure_and_send(teleportation_circuit, 0, 1)
## STEP 4
teleportation_circuit.barrier() # Use barrier to separate steps
bob_gates(teleportation_circuit, 2, crz, crx)
teleportation_circuit.draw()
できました!プロトコルの最後に、アリスの量子ビットはボブにテレポートされます。
この notebook では、アリスの量子ビットをランダム状態 $\vert\psi\rangle$ (psi
)で初期化します。この状態はInitialize
(初期化)ゲートを $|q_0\rangle$ に作用させる事で作られます。この章ではpsi
を選ぶためにrandom_state
関数を使いますが、psi
を任意の量子ビット状態に設定してもかまいません。
# Create random 1-qubit state
psi = random_state(1)
# Display it nicely
display(array_to_latex(psi, prefix="|\\psi\\rangle ="))
# Show it on a Bloch sphere
plot_bloch_multivector(psi)
$|0\rangle$ から $|\psi\rangle$ を作る初期化ゲートを作りましょう:
init_gate = Initialize(psi)
init_gate.label = "init"
(Initialize
にはリセット操作が含まれているため、技術的にはゲートではなく、元に戻すことはできません。代わりに「命令」と呼びます。)量子テレポーテーション回路が動作すれば、回路の最後に量子ビット $|q_2\rangle$ を状態がこの状態になるはずです。状態ベクトル・シミュレーターを使ってこれをチェックしてみましょう。
## SETUP
qr = QuantumRegister(3, name="q") # Protocol uses 3 qubits
crz = ClassicalRegister(1, name="crz") # and 2 classical registers
crx = ClassicalRegister(1, name="crx")
qc = QuantumCircuit(qr, crz, crx)
## STEP 0
# First, let's initialize Alice's q0
qc.append(init_gate, [0])
qc.barrier()
## STEP 1
# Now begins the teleportation protocol
create_bell_pair(qc, 1, 2)
qc.barrier()
## STEP 2
# Send q1 to Alice and q2 to Bob
alice_gates(qc, 0, 1)
## STEP 3
# Alice then sends her classical bits to Bob
measure_and_send(qc, 0, 1)
## STEP 4
# Bob decodes qubits
bob_gates(qc, 2, crz, crx)
# Display the circuit
qc.draw()
以下でみるように、この状態ベクトル・シミュレーターを用いると、状態$|q_2\rangle$ は上で作った状態 $|\psi\rangle$ と同じですが、状態 $|q_0\rangle$ と $|q_1\rangle$ は、状態 $|0\rangle$ か $|1\rangle$ のどちらかに崩れている事がわかります。状態 $|\psi\rangle$ は 量子ビット 0 から 2 にテレポートされています。
sim = Aer.get_backend('aer_simulator')
qc.save_statevector()
out_vector = sim.run(qc).result().get_statevector()
plot_bloch_multivector(out_vector)
このセルを何回か実行してみてください。すると、量子ビット 0 と 1 の状態が変化している事に気づくと思いますが、量子ビット 2 は常に状態 $|\psi\rangle$ のままです。
3.3 QASM シミュレーターを使った方法
量子テレポーテーションは2つのグループ間で量子ビットを送るようにデザインされています。これを実証するハードウェアを私たちは持ち合わせていませんが、単一の量子チップ上でなら、ゲートが正しい変換を行う事を実証することができます。ここでは、QASM シミュレーターを使用して、プロトコルをテストする方法をシミュレートします。
実際の量子コンピューターでは、状態ベクトルをサンプリングする事はできません。なので、テレポーテーション回路が稼働しているかをチェックしたければ手段を変える必要があります。みなさんは、量子ビット $|0\rangle$ を状態 $|\psi\rangle$ に変えるために初期化したことを覚えているでしょう:
$$ |0\rangle \xrightarrow{\text{初期化}} |\psi\rangle $$全ての量子ゲートは可逆性を持っているので、次のようにして初期化の逆変換を得ることが出来ます:
inverse_init_gate = init_gate.gates_to_uncompute()
この演算子は次のような性質を持っています:
$$ |\psi\rangle \xrightarrow{\text{逆初期化}} |0\rangle $$量子ビット $|q_0\rangle$ が $|q_2\rangle$ にテレポートした事を証明するには、$|q_2\rangle$ に対してこの逆初期化を行えば確実に $|0\rangle$ が測定できる事を期待すればよいのです。下の回路ではこれを実行しています:
## SETUP
qr = QuantumRegister(3, name="q") # Protocol uses 3 qubits
crz = ClassicalRegister(1, name="crz") # and 2 classical registers
crx = ClassicalRegister(1, name="crx")
qc = QuantumCircuit(qr, crz, crx)
## STEP 0
# First, let's initialize Alice's q0
qc.append(init_gate, [0])
qc.barrier()
## STEP 1
# Now begins the teleportation protocol
create_bell_pair(qc, 1, 2)
qc.barrier()
## STEP 2
# Send q1 to Alice and q2 to Bob
alice_gates(qc, 0, 1)
## STEP 3
# Alice then sends her classical bits to Bob
measure_and_send(qc, 0, 1)
## STEP 4
# Bob decodes qubits
bob_gates(qc, 2, crz, crx)
## STEP 5
# reverse the initialization process
qc.append(inverse_init_gate, [2])
# Display the circuit
qc.draw()
回路図では「disentangler」とラベルのついた、inverse_init_gate
が表示されているのがわかります。最後に、3番目の古典ビットを測定して、その結果を古典ビットに確認します:
# Need to add a new ClassicalRegister
# to see the result
cr_result = ClassicalRegister(1)
qc.add_register(cr_result)
qc.measure(2,2)
qc.draw()
それでは実験を実行しましょう:
t_qc = transpile(qc, sim)
t_qc.save_statevector()
counts = sim.run(t_qc).result().get_counts()
plot_histogram(counts)
$q_2$ (文字列の左端のビット)の測定で 状態 $|0\rangle$ を得る確率が 100% であることがわかります。これは予想された結果であり、テレポーテーション・プロトコルが正常に動作した事を示しています。
量子テレポーテーションを実装したので、次にこのプロトコルの背景にある数学を理解しましょう。
ステップ 1
量子テレポーテーションは、アリスがボブに$|q\rangle = a|0\rangle + b|1\rangle$(ランダムな量子ビット)を送信する必要があるという事実から始まります。彼女は量子ビットの状態を知りません。このために、アリスとボブは第三者(テラモン)の助けを借ります。テラモンは、アリスとボブのためにエンタングルした量子ビットのペアを準備します。 エンタングルした量子ビットは、ディラック記法で次のように書くことができます:
$$ |\psi \rangle = \frac{1}{\sqrt{2}} (|00\rangle + |11\rangle) $$アリスとボブはそれぞれ、エンタングルした量子ビットのペアの1量子ビット(それぞれAとBとして示されます)ずつを持っています。
$$|\psi\rangle = \frac{1}{\sqrt{2}} (|0\rangle_A |0\rangle_B + |1\rangle_A |1\rangle_B) $$この量子ビットのペアを使って、アリスが始めの2量子ビットを持ち、ボブが残りの1量子ビットを持つ、3量子ビット量子システムを作成します。
$$ \begin{align*} |q\rangle \otimes |\psi\rangle &= \frac{1}{\sqrt{2}} (a |0\rangle \otimes (|00\rangle + |11\rangle) + b |1\rangle \otimes (|00\rangle + |11\rangle))\\ &= \frac{1}{\sqrt{2}} (a|000\rangle + a|011\rangle + b|100\rangle + b|111\rangle) \end{align*}$$ステップ 2
プロトコルに従って、アリスは2つの量子ビットにCNOTゲートを適用し、続いて最初の量子ビットにアダマールゲートを適用します。 これにより、次の状態になります:
$$ \begin{align*} (H \otimes I \otimes I) (CNOT \otimes I) (|q\rangle \otimes |\psi\rangle) &=(H \otimes I \otimes I) (CNOT \otimes I) \frac{1}{\sqrt{2}} (a|000\rangle + a|011\rangle + b|100\rangle + b|111\rangle) \\ &= (H \otimes I \otimes I) \frac{1}{\sqrt{2}} (a|000\rangle + a|011\rangle + b|110\rangle + b|101\rangle) \\ &= \frac{1}{2} (a(|000\rangle + |011\rangle + |100\rangle + |111\rangle) + b(|010\rangle + |001\rangle - |110\rangle - |101\rangle)) \\ \end{align*} $$次に、これを分離して次のように書くことができます:
$$ \begin{align*} = \frac{1}{2}( & \phantom{+} |00\rangle (a|0\rangle + b|1\rangle) \hphantom{\quad )} \\ & + |01\rangle (a|1\rangle + b|0\rangle) \hphantom{\quad )}\\[4pt] & + |10\rangle (a|0\rangle - b|1\rangle) \hphantom{\quad )}\\[4pt] & + |11\rangle (a|1\rangle - b|0\rangle) \quad )\\ \end{align*} $$ステップ 3
アリスは(彼女が所有している)最初の2量子ビットを測定し、それらを2つの古典ビットとしてボブに送信します。彼女が取得する結果は、常に4つの標準基底状態$|00\rangle, |01\rangle, |10\rangle, |11\rangle$のいずれかであり、確率は同じです。
彼女の測定に基づいて、ボブの状態は次のように予測されます。 $$ |00\rangle \rightarrow (a|0\rangle + b|1\rangle)\\ |01\rangle \rightarrow (a|1\rangle + b|0\rangle)\\ |10\rangle \rightarrow (a|0\rangle - b|1\rangle)\\ |11\rangle \rightarrow (a|1\rangle - b|0\rangle)$$.
5.1 IBMハードウェアと遅延測定
IBMの量子コンピュータは現在、測定の後に行う命令系をサポートしていないため、実際のハードウェア上ではこれまでの形の回路による量子テレポーテーションを実行できません。ただ幸いなことに、[1]の4.4で説明されている「遅延測定原理」のおかげで、計算実行能力を制限することはありません。この原理は、どのような測定も回路の終端まで延期することができるというものです。つまりすべての測定を最後に持ってくる事が可能で、そこで測定しても同じ結果が得られるはずなのです。
早い段階で測定を行うことで得られるメリットは、ハードウェアと関連しています。早期に測定できれば、量子ビットを再利用できるかもしれませんし、量子ビットが壊れやすい重ね合わせ状態にある時間を短縮できるかもしれません。この例では、量子テレポーテーションにおける早期測定が、直接量子通信チャンネルを使わず量子ビットの状態を送信出来たはずです。
ゲートを動かすことで実際のハードウェア上で「テレポーテーション」回路を実証することができる一方で、テレポーテーションプロセス(古典的なチャネルを介した量子状態の転送)の恩恵が失われていることに注意してください。
それでは、bob_gates
関数をnew_bob_gates
に書き直してみましょう:
def new_bob_gates(qc, a, b, c):
qc.cx(b, c)
qc.cz(a, c)
そして新しい回路を作ります:
qc = QuantumCircuit(3,1)
# First, let's initialize Alice's q0
qc.append(init_gate, [0])
qc.barrier()
# Now begins the teleportation protocol
create_bell_pair(qc, 1, 2)
qc.barrier()
# Send q1 to Alice and q2 to Bob
alice_gates(qc, 0, 1)
qc.barrier()
# Alice sends classical bits to Bob
new_bob_gates(qc, 0, 1, 2)
# We undo the initialization process
qc.append(inverse_init_gate, [2])
# See the results, we only care about the state of qubit 2
qc.measure(2,0)
# View the results:
qc.draw()
# First, see what devices we are allowed to use by loading our saved accounts
IBMQ.load_account()
provider = IBMQ.get_provider(hub='ibm-q')
# get the least-busy backend at IBM and run the quantum circuit there
from qiskit.providers.ibmq import least_busy
from qiskit.tools.monitor import job_monitor
backend = least_busy(provider.backends(filters=lambda b: b.configuration().n_qubits >= 3 and
not b.configuration().simulator and b.status().operational==True))
t_qc = transpile(qc, backend, optimization_level=3)
job = backend.run(t_qc)
job_monitor(job) # displays job status under cell
# Get the results and display them
exp_result = job.result()
exp_counts = exp_result.get_counts(qc)
print(exp_counts)
plot_histogram(exp_counts)
このように、$|1\rangle$ の測定結果は複数あります。これらはゲートと量子ビットのエラーに起因したものです。対照的に、この notebook のはじめに使ったシミュレーターではゲートのエラーはゼロであり、エラーのないテレポーテーションが可能でした。
print(f"The experimental error rate : {exp_counts['1']/sum(exp_counts.values()):.3f}%")
6. 参考文献
[1] M. Nielsen and I. Chuang, Quantum Computation and Quantum Information, Cambridge Series on Information and the Natural Sciences (Cambridge University Press, Cambridge, 2000).
[2] Eleanor Rieffel and Wolfgang Polak, Quantum Computing: a Gentle Introduction (The MIT Press Cambridge England, Massachusetts, 2011).
import qiskit.tools.jupyter
%qiskit_version_table