量子テレポーテーションと超高密度符号化
Kifumi Numata (2024年4月26日)
元の講義のPDFをダウンロードしてください。コードスニペットは静的画像のため、一部が非推奨になっている場合があります。
この実験を実行するおおよそのQPU時間は10秒です。
1. はじめに
ユーティリティスケールの量子問題を解くためには、量子コンピューター上である Qubit から別の Qubit へと情報を移動させる必要があります。これを行うためのよく知られたプロトコルがいくつかありますが、最も基礎的なものは遠く離った 当事者間で情報を送信するという文脈で考案されました。このレッスンでは、そのような文脈に沿った「遠くにいる友人への情報送信」といった表現を使うことがありますが、これらのプロトコルは量子コンピューティング全般においてより広い意味を持つことを念頭に置いてください。このレッスンでは、以下の量子通信プロトコルを取り上げます。
- 量子テレポーテーション 共有されたエンタングル状態(e-bitと呼ばれることもあります)を使用して、未知の量子状態を遠くにいる友人へ送信します。この際、古典的な通信の補足が必要です。
- 量子超高密度符号化 事前に共有されたエンタングル Qubit を使用して、単一の Qubit を遠くにいる友人に送ることで2ビットの情報を送信する方法です。
これらのトピックに関連する背景については、Entanglement in action の Basics of Quantum Information レッスン4をお勧めします。
上記の説明における「未知の量子状態」とは、前のレッスンで説明した以下の形式の状態を指します。
ここで と は を満たす複素数です。これにより、量子状態を次のように書くことができます。
任意のランダムな量子状態に含まれる情報を転送できるようにしたいため、そのような状態を生成するところからこのレッスンを始めます。
2. 密度行列
量子状態 は密度行列として書くこともできます。この形式は純粋な量子状態の確率的な混合を表すのに便利です。単一の Qubit の場合、次のように書けます。
密度行列 は以下のように、パウリ行列の線形和で表せることに注意してください。
あるいは、一般的には次のようになります。
ここで です。
また、ブロッホベクトルは です。
それでは、乱数を使って任意の量子状態を作成してみましょう。
# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit qiskit-aer qiskit-ibm-runtime
import numpy as np
# create a random 1-qubit state from a random (theta, varphi) to define r vector
np.random.seed(1) # fixing seed for repeatibility
theta = np.random.uniform(0.0, 1.0) * np.pi # from 0 to pi
varphi = np.random.uniform(0.0, 2.0) * np.pi # from 0 to 2*pi
def get_r_vec(theta, varphi):
rx = np.sin(theta) * np.cos(varphi)
ry = np.sin(theta) * np.sin(varphi)
rz = np.cos(theta)
return (rx, ry, rz)
# get r vector
rx, ry, rz = get_r_vec(theta, varphi)
print("theta=" + str(theta), ",varphi=" + str(varphi))
print("(rx, ry, rz) = (" + str(rx) + ", " + str(ry) + ", " + str(rz) + ")")
theta=1.3101132663588946 ,varphi=4.525932273597346
(rx, ry, rz) = (-0.1791150283307452, -0.9494670044331133, 0.2577405946274022)
このブロッホベクトルをブロッホ球上に表示することができます。
from qiskit.visualization import plot_bloch_vector
r = [rx, ry, rz]
plot_bloch_vector(r)
3. 量子状態トモグラフィー
計算基底( と )で量子状態を測定するだけでは、位相情報(複素数の情報)が失われてしまいます。しかし、準備プロセスを繰り返すことで の多くのコピーを持てる場合(状態をクローンすることはできませんが、準備プロセスを繰り返すことはできます)、密度行列 に対する量子状態トモグラフィーを実行することで、 の値を推定することができます。次の形式を考えると、
次のことが成り立ちます。
の場合、
最後の等式の変換は に対するものです。したがって、 は の確率 の確率として求めることができます。
値の推定
を推定するために、量子状態を生成して測定します。この準備と測定を何度も繰り返し、最終的に測定の統計を使って上記の確率を推定し、 を見積もります。
ランダムな量子状態を生成するために、パラメーター を使った汎用ユニタリーゲート を使用します。(詳細は U-gate を参照してください。)
from qiskit import QuantumCircuit
# create a 1-qubit quantum state psi from theta, varphi parameters
qc = QuantumCircuit(1, 1)
qc.u(theta, varphi, 0.0, 0)
# measure in computational basis
qc.measure(0, 0)
qc.draw(output="mpl")
AerSimulator を使用して、計算基底で測定を行い を推定します。
# see if the expected value of measuring in the computational basis
# approaches the limit of rz
from qiskit_aer import AerSimulator
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit_ibm_runtime import Sampler
from qiskit.visualization import plot_histogram
# Define backend
backend = AerSimulator()
nshots = 1000 # or 10000
# nshots = 10000
# Transpile to backend
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_qc = pm.run(qc)
# Run the job
sampler = Sampler(mode=backend)
job = sampler.run([isa_qc], shots=nshots)
result = job.result()
# Extract counts data
counts = result[0].data.c.get_counts()
print(counts)
# Plot the counts in a histogram
plot_histogram(counts)
{'1': 375, '0': 625}
rz_approx = (counts["0"] - counts["1"]) / nshots
print("rz = ", rz, " and approx of rz = ", rz_approx)
rz = 0.2577405946274022 and approx of rz = 0.25
量子状態トモグラフィーの手法を使って の値を推定しました。この場合、「ランダム」な状態のパラメーターを自分たちで選んでいるため、 の真の値を知っており、結果を確認できます。しかし、その性質上、ユーティリティスケールの作業は常にそれほど簡単に確認できるわけではありません。量子結果の検証については、このコースの後半で詳しく説明します。今は、推定がかなり正確であったことを確認するだけで十分です。
演習1: 値の推定
IBM® 量子コンピューターは 軸に沿って測定を行います(「 基底で」または「計算基底で」と表現されることもあります)。しかし、測定前に回転を適用することで、量子状態の 軸への射影も測定できます。より正確に言えば、 方向を向いていたものが 方向を向くようにシステムを回転させれば、 方向の同じ測定ハードウェアを使いながら、少し前まで 方向にあった状態について知ることができます。これが、ほとんどの量子コンピューター(そしてすべてのIBM量子コンピューター)が複数の軸に沿って測定を行う方法です。
この理解をもとに、量子状態トモグラフィーを使って の値を推定するコードを書いてみましょう。
解答:
# create a 1-qubit quantum state psi from theta, varphi parameters
qc = QuantumCircuit(1, 1)
qc.u(theta, varphi, 0.0, 0)
qc.h(0)
qc.measure(0, 0)
qc.draw(output="mpl")
# Define backend
backend = AerSimulator()
nshots = 10000
# Transpile to backend
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_qc = pm.run(qc)
# Run the job
sampler = Sampler(mode=backend)
job = sampler.run([isa_qc], shots=nshots)
result = job.result()
# Extract counts data
counts = result[0].data.c.get_counts()
print(counts)
# Plot the counts in a histogram
plot_histogram(counts)
{'1': 5925, '0': 4075}
rx_approx = (counts["0"] - counts["1"]) / nshots
print("rx = ", rx, " and approx of rx = ", rx_approx)
rx = -0.1791150283307452 and approx of rx = -0.185
演習2:の値を推定する
先ほどと同じ論理的な手順で、測定の前にシステムを回転させることで の情報を得ることができます。
量子状態トモグラフィーを使って の値を推定するコードを自分で書いてみてください。前の例をベースにしつつ、異なる回転を適用すると良いでしょう。(sdg を含む各種ゲートについての詳細は、APIリファレンスを参照してください。)
解答:
# create a 1-qubit quantum state psi from theta, varphi parameters
qc = QuantumCircuit(1, 1)
qc.u(theta, varphi, 0.0, 0)
qc.sdg(0)
qc.h(0)
qc.measure(0, 0)
qc.draw(output="mpl")
# Define backend
backend = AerSimulator()
nshots = 10000
# Transpile to backend
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_qc = pm.run(qc)
# Run the job
sampler = Sampler(mode=backend)
job = sampler.run([isa_qc], shots=nshots)
result = job.result()
# Extract counts data
counts = result[0].data.c.get_counts()
print(counts)
# Plot the counts in a histogram
plot_histogram(counts)
{'1': 9759, '0': 241}
ry_approx = (counts["0"] - counts["1"]) / nshots
print("ry = ", ry, " and approx of ry = ", ry_approx)
ry = -0.9494670044331133 and approx of ry = -0.9518
これで のすべての成分を推定でき、完全なベクトルを書き出すことができます。
print("Estimated vector is (", rx_approx, ",", ry_approx, ",", rz_approx, ").")
print("Original random vector was (" + str(rx) + ", " + str(ry) + ", " + str(rz) + ").")
Estimated vector is ( -0.185 , -0.9518 , 0.25 ).
Original random vector was (-0.1791150283307452, -0.9494670044331133, 0.2577405946274022).
この量子状態トモグラフィーの手法を使って、元のランダムベクトルをかなり正確に推定できました。
4. 量子テレポーテーション
アリスというキャラクターが、未知の量子状態 を遠くにいる友人のボブへ送りたいという状況を考えてみましょう。二人はメールや電話のような古典的な通信しか使えないと仮定します。アリスは量子状態をコピーすることができません(複製不可能定理によるものです)。もし同じ準備プロセスを何度も繰り返せば、先ほどのように統計を積み上げることができます。しかし、未知の状態が1つしかない場合はどうすればよいでしょうか?その状態は研究したい物理プロセスから生まれたものかもしれませんし、より大きな量子計算の一部かもしれません。そのような場合、アリスはボブへ状態を送ることができるのでしょうか?実は、アリスとボブが貴重な量子リソース——共有されたエンタングル状態——を持っていれば可能です。前のレッスンで紹介したベル状態 がその例です。これは「EPRペア」や「eビット」(エンタングルメントの基本単位)と呼ばれることもあります。アリスとボブがこのようなエンタングル状態を共有していれば、アリスは一連の量子演算を行い、2ビットの古典情報をボブに送ることで、未知の量子状態をテレポートさせることができます。
4.1 量子テレポーテーションのプロトコル
前提:アリスはボブに送りたい未知の量子状態 を持っています。アリスとボブは2 Qubitのエンタングル状態(eビット)を共有しており、それぞれが物理的に自分の場所でそのうちの1 Qubitを持っています。
以下に、説明なしで手順を概要として示します。詳細は後ほど実装します。
- アリスは CNOT Gate を使って を自分のeビットの部分とエンタングルさせます。
- アリスは にアダマールゲートを適用し、自分の2つの Qubit を計算基底で測定します。
- アリスはボブに測定結果("00"、"01"、"10"、または "11")を送ります。
- ボブはアリスの2ビットの情報に基づき、自分が持つeビットに補正演算子を適用します。
- "00" の場合、ボブは何もしない
- "01" の場合、ボブは X Gate を適用する
- "10" の場合、ボブは Z Gate を適用する
- "11" の場合、ボブは iY = ZX Gate を適用する
- ボブのeビットの部分が になります。
これについては量子情報の基礎でより詳しく解説されています。しかし、Qiskitでこれを実装していくうちに、状況がより明確になるでしょう。
4.2 量子テレポーテーションをシミュレートする量子Circuit
いつも通り、Qiskitパターンのフレームワークを適用します。このサブセクションではマッピングのみに焦点を当てます。
ステップ1:問題を量子CircuitおよびOperatorにマッピングする
上記のシナリオを記述するには、3つの Qubit を持つ Circuit が必要です。アリスとボブが共有するエンタングルペア用に2つ、未知の量子状態 用に1つです。
from qiskit import QuantumCircuit
import numpy as np
# create 3-qubits circuit
qc = QuantumCircuit(3, 3)
qc.draw(output="mpl")
最初に、アリスは未知の量子状態 を持っています。これを Gate を使って作成します。
# Create the unknown quantum state using the u-gate. Alice has this.
qc.u(theta, varphi, 0.0, 0)
qc.barrier() # for visual separation
qc.draw(output="mpl")
作成した状態を視覚化することができますが、それは Gate に使ったパラメータを知っているからです。もしこの状態が複雑な量子プロセスから生まれたものであれば、トモグラフィーのようにプロセスを何度も実行して統計を集めない限り、状態を知ることはできません。
# show the quantum state on bloch sphere
from qiskit.quantum_info import Statevector
from qiskit.visualization import plot_bloch_multivector
out_vector = Statevector(qc)
plot_bloch_multivector(out_vector)

このプロトコルが始まる前に、アリスとボブは共有エンタングルペアをすでに持っていると仮定します。アリスとボブが本当に離れた場所にいる場合、未知の状態 が作られる前に共有状態をセットアップしていたかもしれません。これらは異なる Qubit 上で行われるため、ここでの順序は問題ありません。この順序は視覚化において都合が良いものです。
# Alice and Bob are together in the same place and set up an entangled pair.
qc.h(1)
qc.cx(1, 2)
qc.barrier() # for visual separation.
# We can consider that Alice and Bob might move their qubits to different physical locations, now.
qc.draw(output="mpl")
次に、アリスは Gate と Gate を使って を共有eビットの自分の部分とエンタングルさせ、計算基底で測定します。
# Alice entangles the unknown state with her part of the e-bit, using the CNOT gate and H gate.
qc.cx(0, 1)
qc.h(0)
qc.barrier()
# Alice measures the two qubits.
qc.measure(0, 0)
qc.measure(1, 1)
qc.draw(output="mpl")
アリスはボブに測定結果("00"、"01"、"10"、または "11")を送り、ボブはアリスの2ビットの情報に基づいて共有eビットの自分の部分に補正演算子を適用します。その後、ボブの部分が になります。
# Alice sent the results to Bob. Bob applies correction
with qc.if_test((0, 1)):
qc.z(2)
with qc.if_test((1, 1)):
qc.x(2)
qc.barrier()
qc.draw(output="mpl")
量子テレポーテーション Circuit が完成しました!状態ベクトルシミュレーターを使って、この Circuit の出力状態を確認しましょう。
from qiskit_aer import StatevectorSimulator
backend = StatevectorSimulator()
out_vector = backend.run(qc, shots=1).result().get_statevector() # set shots = 1
plot_bloch_multivector(out_vector)

Qubit 0 の Gate によって作られた量子状態(元々の秘密状態を保持していた Qubit)が、Qubit 2(ボブの Qubit)に転送されたことがわかります。
上のセルを何度か実行して確認してみてください。Qubit 0 と 1 の状態は変化しますが、Qubit 2 は常に の状態にあることに気づくでしょう。
4.3 実行し、Uの逆行列の適用で結果を確認する
上では、テレポートされた状態が正しく見えることを目視で確認しました。量子状態が正しくテレポートされたかどうかを確認するもう一つの方法は、ボブの Qubit に Gate の逆行列を適用して '0' を測定することです。つまり、 は恒等変換なので、ボブの Qubit が から作られた状態にあれば、逆行列を適用すると が得られるはずです。
# Apply the inverse of u-gate to measure |0>
qc.u(theta, varphi, 0.0, 2).inverse() # inverse of u(theta,varphi,0.0)
qc.measure(2, 2) # add measurement gate
qc.draw(output="mpl")
実際の量子コンピューターに進む前に、まず AerSimulator を使って Circuit を実行します。
from qiskit_aer import AerSimulator
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit_ibm_runtime import Sampler
from qiskit.visualization import plot_histogram
# Define backend
backend = AerSimulator()
# Transpile to backend
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_qc = pm.run(qc)
# Run the job
sampler = Sampler(mode=backend)
job = sampler.run([isa_qc], shots=nshots)
result = job.result()
# Extract counts data
counts = result[0].data.c.get_counts()
print(counts)
# Plot the counts in a histogram
plot_histogram(counts)
{'011': 2510, '010': 2417, '000': 2635, '001': 2438}
リトルエンディアン表記では、Qubit 2 が最も左(または列ラベルでは最も下)の Qubit であることを思い出してください。列ラベルの左端・下端の Qubit は、すべての可能な結果において 0 であることに注目してください。これは、 を状態 で測定する確率が 100% であることを示しています。これは期待通りの結果であり、テレポーテーションプロトコルが正常に機能したことを示しています。
4.4 実際の量子コンピューターでのテレポーテーション
次に、実際の量子コンピューターでテレポーテーションを実行します。動的回路機能を使うことで、測定結果に基づいてCircuitの途中で演算を行うことができ、テレポーテーション Circuit における条件分岐演算をリアルタイムで実装できます。実際の量子コンピューターで問題を解くために、Qiskitパターンの4つのステップに従います。
- 問題を量子CircuitおよびOperatorにマッピングする
- ターゲットハードウェア向けに最適化する
- ターゲットハードウェア上で実行する
- 結果を後処理する
演習3:テレポーテーション Circuit を作成する
理解を確認するために、テレポーテーション Circuit をゼロから作成してみてください。必要であれば上にスクロールして確認してください。
解答:
# Step 1: Map problem to quantum circuits and operators
# Create the circuit with 3-qubits and 1-bit
qc = QuantumCircuit(3, 3)
# Alice creates an unknown quantum state using the u-gate.
qc.u(theta, varphi, 0.0, 0)
qc.barrier() # for visual separation
# Eve creates EPR pair and sends q1 to Alice and q2 to Bob
##your code goes here##
qc.h(1)
qc.cx(1, 2)
qc.barrier()
# Alice entangles the unknown state with her EPR part, using the CNOT gate and H gate.
##your code goes here##
qc.cx(0, 1)
qc.h(0)
qc.barrier()
# Alice measures the two qubits.
##your code goes here##
qc.measure(0, 0)
qc.measure(1, 1)
# Alice sent the results to Bob. Now, Bob applies correction
##your code goes here##
with qc.if_test((0, 1)):
qc.z(2)
with qc.if_test((1, 1)):
qc.x(2)
qc.barrier()
# Apply the inverse of u-gate to measure |0>
qc.u(theta, varphi, 0.0, 2).inverse()
qc.measure(2, 2)
qc.draw(output="mpl")
念のために申し上げると、 Gate の逆行列を適用するのは、期待通りの動作を確認するためだけです。これはボブへ状態を送ること自体の一部ではなく、量子情報の転送のみが目的であれば、この逆 Gate は使用しません。
ステップ 2: ターゲットハードウェア向けの最適化
ハードウェア上で実行するには、QiskitRuntimeService をインポートして保存済みの認証情報を読み込みます。キューにあるジョブ数が最も少ないバックエンドを選択してください。
from qiskit_ibm_runtime import QiskitRuntimeService
service = QiskitRuntimeService()
service.backends()
[<IBMBackend('ibm_brisbane')>,
<IBMBackend('ibm_torino')>]
# You can also identify the least busy device
backend = service.least_busy(operational=True)
print("The least busy device is ", backend)
The least busy device is <IBMBackend('ibm_brisbane')>
# You can specify the device
# backend = service.backend('ibm_brisbane')
選択したデバイスのカップリングマップを確認してみましょう。
from qiskit.visualization import plot_gate_map
plot_gate_map(backend)

デバイスによ ってカップリングマップは異なり、またデバイスごとにパフォーマンスが高いQubitやカプラーが存在します。さらに、量子コンピューターによってネイティブゲート(ハードウェアが実行できるゲート)が異なる場合があります。Circuit をトランスパイルすると、抽象的な量子Circuit がターゲット量子コンピューターで実行可能なゲートを使って書き直され、物理Qubitへの最適なマッピングが選択されます(その他の処理も行われます)。トランスパイルは奥深く複雑なトピックです。詳細については APIリファレンス を参照してください。
# Step 2: Optimize for target hardware
# Transpile the circuit into basis gates executable on the hardware
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
pm = generate_preset_pass_manager(backend=backend, optimization_level=2)
qc_compiled = pm.run(qc)
qc_compiled.draw("mpl", idle_wires=False, fold=-1)
ステップ 3: Circuit を実行する
Sampler Runtimeプリミティブを使用して、ターゲットCircuit を実行します。
# Step 3: Execute the target circuit
sampler = Sampler(backend)
job = sampler.run([qc_compiled])
job_id = job.job_id()
print("job id:", job_id)
job id: d13nkhpn2txg008jt0d0
# Check the job status
job.status()
'DONE'
IBM Quantum® ダッシュボード からジョブのステータスを確認することもできます。
# If the Notebook session got disconnected you can also check your job status by running the following code
from qiskit_ibm_runtime import QiskitRuntimeService
service = QiskitRuntimeService()
job_real = service.job(job.job_id()) # Input your job-id between the quotations
job_real.status()
'DONE'
'DONE' と表示されたら、以下のセルを実行して結果を取得できます。
# Execute after 'DONE' is displayed
result_real = job_real.result()
print(result_real[0].data.c.get_counts())
{'001': 992, '110': 430, '011': 579, '010': 605, '111': 402, '000': 925, '100': 57, '101': 106}
ステップ 4: 結果の後処理
# Step 4: Post-process the results
from qiskit.visualization import plot_histogram
plot_histogram(result_real[0].data.c.get_counts())
上の結果はそのまま解釈できます。あるいは、marginal_count を使用して、Qubit 2 における Bob の結果を追跡することもできます。
# trace out Bob's results on qubit 2
from qiskit.result import marginal_counts
bobs_qubit = 2
real_counts = result_real[0].data.c.get_counts()
bobs_counts = marginal_counts(real_counts, [bobs_qubit])
plot_histogram(bobs_counts)
ここで分かるように、 を測定した結果がいくつか存在します。これらはノイズやエラーによるものです。特に、動的Circuit はCircuit の途中での測定に時間がかかるため、エラー率が高くなる傾向があります。
4.5 量子テレポーテーションのまとめ
もつれ合ったQubitのペア(e-bit)を共有することで、離れた相手に量子状態を転送できます。
-
量子テレポーテーションは光速より速く量子状態を送ることができますか? いいえ。Alice が測定結果を Bob に古典的な方法で伝える必要があるためです。
-
量子テレポーテーションは、量子状態のコピーを禁じる「量子複製不可能定理」を破りますか? いいえ。Alice のQubitにあった元の量子状態は測定によって失われており、 または に崩壊しています。
5. 超高密度符号化
ほぼ同じセットアップを別の目的に使用することができます。Alice が Bob に2ビットの古典情報を送りたいが、Bob との古典的な通信手段がないとします。しかし、Alice と Bob はもつれペアを共有しており、Alice は自分のQubitを Bob の場所に送ることができます。量子テレポーテーションプロトコルとの違いに注目してください。テレポーテーションでは古典通信が利用可能であり、目的は量子状態を送ることでした。ここでは古典通信は利用できず、Qubitの転送を使って2ビットの古典情報を共有します。
5.1 超高密度符号化のプロトコル
前提: Alice は2ビットの情報、たとえば を持っています。Alice と Bob はもつれペア(e-bit)を共有していますが、古典的な通信はできません。
- Alice は e-bit の自分の部分に対して以下のいずれかの操作を行います。
- の場合、何もしない
- の場合、Z Gate を適用する
- の場合、X Gate を適用する
- の場合、Z Gate と X Gate を適用する。
- Alice は e-bit の自分の部分を Bob の場所に送ります。
- Bob は Alice からのQubitをコントロール、自分のQubitをターゲットとして CNOT Gate を適用し、次に Alice からのQubitに H Gate を適用して、2つのQubitを測定します。可能な初期状態と Bob の操作の結果は次のとおりです:
のマイナス符号はグローバル位相であるため、測定することはできません。
5.2 超高密度符号化をシミュレートする量子Circuit
超高密度符号化のプロトコルに基づいて、以下のように超高密度符号化Circuit を構築できます。Alice が Bob に転送したいメッセージ msg を変えてみてください。
from qiskit import QuantumCircuit
Qiskit パターンのステップはコードコメントで示されています。
# Step 1: Map problem to quantum circuits and operators
# Create 2-qubits circuit
qc = QuantumCircuit(2, 2)
# Eve creates EPR pair and send q0 to Alice and q1 to Bob
qc.h(0)
qc.cx(0, 1)
qc.barrier()
# set message which Alice wants to transform to Bob
msg = "11" # You can change the message
if msg == "00":
pass
elif msg == "10":
qc.x(0)
elif msg == "01":
qc.z(0)
elif msg == "11":
qc.z(0)
qc.x(0)
qc.barrier()
# Bob receives EPR qubit from Alice and performs unitary operations
qc.cx(0, 1)
qc.h(0)
qc.barrier()
# Bob measures q0 and q1
qc.measure(0, 0)
qc.measure(1, 1)
qc.draw(output="mpl")
# We will execute on a simulator first
from qiskit_aer import AerSimulator
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit_ibm_runtime import Sampler
# Define backend
backend = AerSimulator()
shots = 1000
# Transpile to backend
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_qc = pm.run(qc)
# Run the job
sampler = Sampler(mode=backend)
job_sim = sampler.run([isa_qc], shots=shots)
result_sim = job_sim.result()
# Extract counts data
counts = result_sim[0].data.c.get_counts()
print(counts)
{'11': 1000}
# Visualize the results
from qiskit.visualization import plot_histogram
plot_histogram(counts)
Bob が Alice の送りたかったメッセージを受け取ったことが分かります。
次に、実際の量子コンピューターで試してみましょう。
from qiskit_ibm_runtime import QiskitRuntimeService
service = QiskitRuntimeService()
backend = service.least_busy(operational=True)
print("The least busy device is ", backend)
The least busy device is <IBMBackend('ibm_brisbane')>
# Step 1 was already completed before the simulator job above.
# Step 2: Optimize for target hardware
# Transpile the circuit into basis gates executable on the hardware
pm = generate_preset_pass_manager(backend=backend, optimization_level=2)
qc_compiled = pm.run(qc)
qc_compiled.draw("mpl", idle_wires=False)
# Step 3:Execute the target circuit
sampler = Sampler(backend)
job = sampler.run([qc_compiled])
job_id = job.job_id()
print("job id:", job_id)
job id: d13nnyq3grvg008j0zag
# Check the job status
job.status()
'DONE'
# If the Notebook session got disconnected you can also check your job status by running the following code
# from qiskit_ibm_runtime import QiskitRuntimeService
# service = QiskitRuntimeService()
job = service.job(job_id) # Input your job-id between the quotations
job.status()
'DONE'
# Execute after job has successfully run
real_result = job.result()
print(real_result[0].data.c.get_counts())
{'11': 3942, '01': 107, '10': 41, '00': 6}
# Step 4: post-process the results
from qiskit.visualization import plot_histogram
plot_histogram(real_result[0].data.c.get_counts())
結果は期待通りのものです。実際の量子コンピューター上での超高密度符号化は、量子テレポーテーションの場合と比べてエラーが少なかったことに注目してください。その理由の一つとして、量子テレポーテーションは動的Circuit を使用しているのに対し、超高密度符号化は使用していないことが挙げられます。量子Circuit におけるエラーについては、後続のレッスンで詳しく学びます。
6. まとめ
このセッションでは、2つの量子プロトコルを実装しました。どちらも離れた友人同士というシナリオは単一の QPU 上での量子コンピューティングとは少し異なりますが、量子コンピューティングへの応用があり、量子情報の転送をより深く理解するうえで役立ちます。
- 量子テレポーテーション: 量子状態をコピーすることはできませんが、もつれを共有することで未知の量子状態をテレポートすることができます。
- 量子超高密度符号化: 共有されたもつれペアと1つのQubitの転送により、2ビットの古典情報を通信できます。
# See the version of Qiskit
import qiskit
qiskit.__version__
'2.0.2'