量子テレポーテーション(2023年 Lab2)#

アリスとボブが抱えている問題を解決する量子テレポーテーションのLabへようこそ。ご安心ください、人間関係の問題ではありませんよ!アリスは未知の状態 \(\lvert \psi \rangle\) の量子ビットを所持しており、この量子状態をボブに転送したいと考えています。しかしながら彼らは非常に離れた場所におり、量子情報を直接転送する手段はなく、転送できるのは古典情報のみです。彼らの目標を達成することは可能でしょうか?

アリスとボブがエンタングルした量子ビットのペアを共有している場合、彼女が2ビットの古典情報を送信することで、量子ビットの状態をボブに転送できることがわかっています。最終的に、ボブが \(\lvert \psi \rangle\) を所有し、アリスはそれを持たなくなるため、このプロセスはテレポーテーションとして知られています。

背景#

量子テレポーテーションは、エンタングルメントと古典通信を使用し、ある量子ビットから別の量子ビットへと量子情報の転送を可能にするプロトコルです。このプロトコルは、1993年に Charles Bennett、Gilles Brassard、Claude Crépeau、Richard Jozsa、Asher Peres および William Wootters によって提案されました。このプロセスは、量子ビット自体を送信するのではなく、ソース量子ビットからターゲット量子ビットに量子状態を転送します。

プロトコルには3つの量子ビットが必要です。

  1. テレポートする量子ビット(アリスの量子ビット)

  2. エンタングルした量子ビットのペアの片方(アリスの2番目の量子ビット)

  3. エンタングルしたペアのもう片方(ボブの量子ビット)

プロトコルは、次の手順に要約できます。

  1. アリスとボブの間で共有される、エンタングルした量子ビットのペア(Bellペア)を生成します。

  2. アリスは、自身が持つ2つの量子ビットに対しBell基底測定を実行します。

  3. アリスは、古典測定の結果をボブに送信します。

  4. ボブは、アリスの測定結果に基づいて適切な量子ゲートを適用し、テレポート状態を取得します。

実装#

量子ビットを転送するためには、アリスとボブにエンタングルした量子ビットのペアを提供してくれる第三者の助けが必要です。次に、アリスは量子ビットに対して特定の操作を実行し、その結果を古典通信チャネルを介してボブと共有します。最後に、ボブは自分の側で一連の操作を実行し、アリスの量子ビットを正常に取得します。それでは、これらの各ステップについて詳しく見ていきましょう。

量子回路は、3つの量子ビットと3つの古典ビットで構成されます。量子ビットは次のように命名されます。

  • \(s\): アリスがボブに送信したい、状態 \(\lvert \psi \rangle\) を含む「ソース」量子ビット。

  • \(a\): エンタングルしたBellペアのうち、アリスが持つ片方を最初に格納する量子ビット。

  • \(b\): エンタングルしたBellペアのうち、ボブが持つもう片方を最初に格納する量子ビット。

テレポーテーションプロトコル自体には2つの古典ビットが必要であり、ボブの最終状態を測定するために使用する3つ目の古典ビットも含まれます。古典ビットは次のように命名されます。

  • \(c0\): アリスが \(a\) の測定に使用する古典ビット。

  • \(c1\): アリスが \(s\) の測定に使用する古典ビット。

  • \(c2\): ボブが \(b\) の測定に使用する古典ビット。

Exercise 1#

Bell状態の生成

2つの量子ビットを利用して、エンタングルしたBellペアの状態 \(\frac{\lvert 00 \rangle + \lvert 11 \rangle}{\sqrt{2}}\) を生成してください。量子ビット \(a\) はアリスに割り当てられ、量子ビット \(b\) はボブのものです。

Hint

Bell状態はアダマールゲートとCNOTゲートを使うことで生成できます。

from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister
from qiskit.circuit import Qubit, Clbit

def create_bell_pair(qr: QuantumRegister, cr: ClassicalRegister) -> QuantumCircuit:
    """Creates a bell pair between qubits a and b."""
    qc = QuantumCircuit(qr, cr)
    # unpack qubits
    # the first qubit is s but we won't be using it in this exercise
    _, a, b = qr

    ####### your code goes here #######

    return qc

Exercise 1 解答例#

from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister
from qiskit.circuit import Qubit, Clbit

def create_bell_pair(qr: QuantumRegister, cr: ClassicalRegister) -> QuantumCircuit:
    """Creates a bell pair between qubits a and b."""
    qc = QuantumCircuit(qr, cr)
    # unpack qubits
    # the first qubit is s but we won't be using it in this exercise
    _, a, b = qr

    qc.h(a)
    qc.cx(a, b)

    return qc
qr = QuantumRegister(3, name="q")
cr = ClassicalRegister(3, name="c")
qc = create_bell_pair(qr, cr)

qc.draw("mpl")
_images/1830355a7e93fb34d5293f409a9398918669f802b59f2fc367461eb7310ae5cb.png
# # Submit your circuit
# from qc_grader.challenges.spring_2023 import grade_ex2a
# grade_ex2a(qc)

解説

ヒントにあるように、Bell状態はアダマールゲートとCNOTゲートを使うことで生成できます。
CNOTゲートは2量子ビットゲートで、入力に制御量子ビットとターゲット量子ビットを指定する必要があります。
ここではアリスのもつ量子ビットを制御量子ビットに、ボブのもつ量子ビットをターゲット量子ビットに設定します。

ここで、メッセージ量子ビット\(s\)の状態を\(\ket{\psi}\)とおくと、3量子ビットの量子状態は以下のように表すことが出来ます。

(1)#\[\begin{align} \ket{\psi}\ket{B_{00}} &= (\alpha\ket{0}+\beta\ket{1})(\frac{1}{\sqrt{2}}(\ket{00}+\ket{11})) \\ &= \frac{1}{\sqrt{2}}(\alpha\ket{000}+\alpha\ket{011}+\beta\ket{100}+\beta\ket{111}) \end{align}\]

アリスが量子ビット \(a\) を持ち、ボブが量子ビット \(b\) を持っていると仮定しましょう。彼らは本当に関係性に問題を抱えているかもしれませんね😉。

Exercise 2#

メッセージ送信回路の構築

次のプロトコルを実装してください。

  1. アリスが、 \(s\) (\(\lvert \psi \rangle\) を含む量子ビット)を制御、 \(a\) をターゲットとしてCNOTゲートを適用する。

  2. アリスが \(s\) にアダマールゲートを適用する。

def alice_gates(qr: QuantumRegister, cr: ClassicalRegister):
    """Creates Alices's gates"""
    qc = create_bell_pair(qr, cr)
    qc.barrier()  # Use barrier to separate steps
    s, a, b = qr

    ####### your code goes here #######
    
    return qc

Exercise 2 解答例#

def alice_gates(qr: QuantumRegister, cr: ClassicalRegister):
    """Creates Alices's gates"""
    qc = create_bell_pair(qr, cr)
    qc.barrier()  # Use barrier to separate steps
    s, a, b = qr

    qc.cx(s, a)
    qc.h(s)
    
    return qc
qc = alice_gates(qr, cr)
qc.draw("mpl")
_images/38e34520dd2fba6fc0aac9989f3be5cd31d64946767f86f06120972e9eafd989.png
# # Submit your circuit
# from qc_grader.challenges.spring_2023 import grade_ex2b
# grade_ex2b(qc)

解説

問題文の手順通りに量子ゲートを配置します。

構築した回路を通した量子状態は以下のように表されます。  $\( \frac{1}{\sqrt{2}}(\alpha\ket{000}+\alpha\ket{100}+\alpha\ket{011}+\alpha\ket{111}+\beta\ket{010}-\beta\ket{110}+\beta\ket{001}-\beta\ket{101}) \)$

Exercise 3#

量子状態の測定

このステップでは、アリスは所有している両方の量子ビットで測定を実行し、結果を2つの古典ビットに保存します。その後、彼女はこれら2つのビットをボブに送信します。

彼女は量子ビット \(a\) を古典ビット \(c0\) に、量子ビット \(s\) を古典ビット \(c1\) に測定して入れるように以下のコードセルを完成させてください。

def measure_and_send(qr: QuantumRegister, cr: ClassicalRegister):
    """Measures qubits a & b and 'sends' the results to Bob"""
    qc = alice_gates(qr, cr)
    qc.barrier()  # Use barrier to separate steps
    s, a, b = qr
    c0, c1, c2 = cr

    ####### your code goes here #######
    
    return qc

Exercise 3 解答例#

def measure_and_send(qr: QuantumRegister, cr: ClassicalRegister):
    """Measures qubits a & b and 'sends' the results to Bob"""
    qc = alice_gates(qr, cr)
    qc.barrier()  # Use barrier to separate steps
    s, a, b = qr
    c0, c1, c2 = cr

    qc.measure(a, c0)
    qc.measure(s, c1)
    
    return qc
qc = measure_and_send(qr, cr)
qc.draw("mpl", cregbundle=False)
_images/a5fb2f633ad3b860f6f587675202cc6fd07a49aa4a901d995e0edfebab8437a8.png
# # Submit your circuit
# from qc_grader.challenges.spring_2023 import grade_ex2c
# grade_ex2c(qc)

解説

測定を実行するためには.measure()メソッドを用います。
入力には測定対象である量子ビットと、測定結果を格納する古典ビットが必要となります。

量子ビット\(a\)\(b\)はエンタングルしているので量子ビット\(a\)の測定結果は\(b\)の状態に影響を与えます。

  • 測定結果が00:量子ビット\(b\)の状態は\(\alpha\ket{0}+\beta\ket{1}\) ・・・送信したかった正しい状態

  • 測定結果が01:量子ビット\(b\)の状態は\(\alpha\ket{1}+\beta\ket{0}\) ・・・Xゲートを適用すると正しい状態に戻すことが出来る

  • 測定結果が10:量子ビット\(b\)の状態は\(\alpha\ket{0}-\beta\ket{1}\) ・・・Zゲートを適用すると正しい状態に戻すことが出来る

  • 測定結果が11:量子ビット\(b\)の状態は\(\alpha\ket{1}-\beta\ket{0}\) ・・・ZXゲートを適用すると正しい状態に戻すことが出来る

Exercise 4#

動的回路の生成

このステップでは、すでに量子ビット \(b\) を所有しているボブが、アリスから受け取った古典ビットの状態に基づいて、回路に特定のゲートを動的に追加します。

  • ビットが 00 の場合、アクションは必要ありません。

  • ビットが 01 の場合、 \(X\) ゲート(Pauli-X または ビット・フリップ・ゲートとも呼ばれます)を適用する必要があります。

  • ビット 10 には、 \(Z\) ゲート(Pauli-Z または フェーズ・フリップ・ゲートとも呼ばれます)を適用する必要があります。

  • 最後に、古典ビットが 11 の場合は、 \(ZX\) ゲートを適用する必要があります。これには、 \(Z\)ゲートと \(X\) ゲートの両方を順番に適用する必要があります。

def bob_gates(qr: QuantumRegister, cr: ClassicalRegister):
    """Uses qc.if_test to control which gates are dynamically added"""
    qc = measure_and_send(qr, cr)
    qc.barrier()  # Use barrier to separate steps
    s, a, b = qr
    c0, c1, c2 = cr

    ####### your code goes here #######
    
    return qc

Exercise 4 解答例#

def bob_gates(qr: QuantumRegister, cr: ClassicalRegister):
    """Uses qc.if_test to control which gates are dynamically added"""
    qc = measure_and_send(qr, cr)
    qc.barrier()  # Use barrier to separate steps
    s, a, b = qr
    c0, c1, c2 = cr

    with qc.if_test((c0, 1)):
      qc.x(b)
    with qc.if_test((c1, 1)):
      qc.z(b)
    
    return qc
qc = bob_gates(qr, cr)
qc.draw("mpl", cregbundle=False)
_images/27b19e7abeac3846fd5499419417c66586492ba46fcfb74c244c3446cc113d34.png
# # Submit your circuit
# from qc_grader.challenges.spring_2023 import grade_ex2d
# grade_ex2d(qc)

解説

古典ビットの状態に対して必要な操作をまとめると、「\(c_0\)が状態1であるときはXゲートを適用し、\(c_1\)が状態1であるときはZゲートを適用する」となります。
場合分けが発生するような動的回路は”.if_test”メソッドを用いて実装することが可能です。

ここで、ボブは自分の量子ビットを測定して、古典ビット \(c2\) に入れます。実験全体を複数回繰り返した後、測定結果に関する統計を収集して、テレポーテーションが正しく機能したことを確認できます。

teleportation_circuit = bob_gates(qr, cr)
s, a, b = qr
c0, c1, c2 = cr
teleportation_circuit.measure(b, c2)
teleportation_circuit.draw("mpl")
_images/9572cc2a64c56c6705804b0a7d96a3968f0b6c59b4be44ed7c39078c29fa1e6a.png

テレポーテーション回路ができたので、~~カーク船長を見知らぬ惑星の地表に転送して~~ 量子状態の生成とテレポートを、シミュレーター上の回路で実行しましょう。

Exercise 5#

回路の結合

次のコードセルでは、完全な量子テレポーテーション回路を teleport_superposition_circuit 変数に構築します。次のステップに従ってください。

  • 状態を準備する回路を構築します。角度 \(\pi / 4\)\(R_x\) 回転ゲートを適用して量子ビット \(s\) を準備してください。

  • 状態準備回路と先ほど構築したテレポーテーション回路を結合してください。

import math

teleport_superposition_circuit: QuantumCircuit

####### your code goes here #######

# Uncomment this line to draw your circuit
teleport_superposition_circuit.draw("mpl", cregbundle=False)

Exercise 5 解答例#

import math

teleport_superposition_circuit: QuantumCircuit

state_prep = QuantumCircuit(qr, cr)
state_prep.rx(math.pi / 4, s)
state_prep.barrier()
teleport_superposition_circuit = state_prep.compose(teleportation_circuit)
teleport_superposition_circuit.draw("mpl", cregbundle=False)

# Uncomment this line to draw your circuit
teleport_superposition_circuit.draw("mpl", cregbundle=False)
_images/47831a032e292c78ad7d834c70ab2590b324a56050ee1c3866f28d8187a3899f.png
from qiskit import transpile
from qiskit_aer import AerSimulator
from qiskit.visualization import plot_histogram

sim = AerSimulator()
transpiled_circuit = transpile(teleport_superposition_circuit, sim)

# run job
shots = 1000
job = sim.run(transpiled_circuit, shots=shots, dynamic=True)

# Get the results and display them
exp_result = job.result()
exp_counts = exp_result.get_counts()
plot_histogram(exp_counts)
_images/bdd7a9aae9d48e3fa522089cdc2b5c64f2043cc38f83e5e4ed552e1de5646073.png

解説

問題のステップ通りに回路を組み立てます。.composeメソッドを用いることで、定義済みの回路の結合が可能となります。

他の測定ビットを無視して、ボブの測定値だけの分布を計算してみましょう。

# trace out Bob's results on qubit 2
from qiskit.result import marginal_counts

bobs_counts = marginal_counts(exp_counts, [qr.index(b)])
plot_histogram(bobs_counts)
_images/f690ec8fceb7ca5ba07815e94ca8d02cf69b7b853b8900140c67329b9e2ab78b.png

周辺分布は、理想的な確率にある程度は近いはずです。

# from qc_grader.challenges.spring_2023 import grade_ex2e
# grade_ex2e(bobs_counts)

~~カーク船長~~ アリスの量子ビットが安全にテレポートすることがほぼ確実になったので、実際のハードウェアで量子テレポーテーション回路を実行してみましょう。

Caution

以下のコードは実機を扱うためコメントアウトしています

# from qiskit_ibm_provider import IBMProvider

# provider = IBMProvider()
# hub = "YOUR_HUB"
# group = "YOUR_GROUP"
# project = "YOUR_PROJECT"

# backend_name = "ibm_peekskill"
# backend = provider.get_backend(backend_name, instance=f"{hub}/{group}/{project}")
# # backend.target.add_instruction(IfElseOp, name="if_else") # Uncomment if necessary
# qc_transpiled = transpile(teleport_superposition_circuit, backend)
# job = backend.run(qc_transpiled, shots=1000, dynamic=True)

実際のバックエンドで実行するには時間がかかるため、通常はjob_idを使って時間をおいてからジョブを呼び出すことになります。以下のコードは、job_idを通してジョブを呼び出し、実行状況を確認するものです。

# retrieve_job = provider.retrieve_job(job.job_id())
# retrieve_job.status()

無事に終了したら、結果をインポートしましょう。

# # Get the results and display them
# exp_result = retrieve_job.result()
# exp_counts = exp_result.get_counts()
# plot_histogram(exp_counts)
# # trace out Bob's results on qubit 2
# from qiskit.result import marginal_counts

# bobs_qubit = 2
# bobs_counts = marginal_counts(exp_counts, [bobs_qubit])
# plot_histogram(bobs_counts)