メインコンテンツへスキップ
このプロジェクトはベータ版です。バグを見つけましたか?GitHub Issueを開く。質問やアイデアがありますか?Discussionsで質問する — フィードバックをお待ちしています!
このページの翻訳品質はいかがですか?

量子テレポーテーションと超高密度符号化

備考

Kifumi Numata (2024年4月26日)

元の講義のPDFをダウンロードしてください。コードスニペットは静的画像のため、一部が非推奨になっている場合があります。

この実験を実行するおおよそのQPU時間は10秒です。

1. はじめに​

任意のユーティリティスケールの量子問題を解くには、量子コンピューター上であるキュビットから別のキュビットへ情報を移動させる必要があります。これを行うためのよく知られたプロトコルがありますが、その中でも最も基本的なものの一部は、遠く離れた当事者間で情報を送信するという文脈で考案されました。このレッスンを通して、「遠く離れた友人に情報を送る」というような、この文脈に沿った表現を使用することがあります。しかし、これらのプロトコルには量子コンピューティングにおけるより広範な意義があることを念頭に置いてください。このレッスンでは、以下の量子通信プロトコルを検討します。

  • 量子テレポーテーション 共有されたエンタングル状態(eビットと呼ばれることもあります)を使用して、未知の量子状態を遠く離れた友人に送信する方法で、追加の古典通信が必要です。
  • 量子超高密度符号化 単一のキュビットを遠く離れた友人に送信することで、2ビットの情報を送信する方法(これも事前に共有されたエンタングル・キュビットを使用します)。

これらのトピックに関するより詳しい背景については、Basics of Quantum Informationのレッスン4にあるEntanglement in actionをお勧めします。

上記の説明において、「未知の量子状態」とは、単に前のレッスンで説明した以下の形式の状態を指します。

∣ψ⟩=α∣0⟩+β∣1⟩|\psi\rangle =\alpha|0\rangle+\beta|1\rangle

ここで、α\alphaとβ\betaは∣α∣2+∣β∣2=1|\alpha|^2+|\beta|^2 = 1を満たす複素数です。これにより、量子状態を次のように書くことができます。

∣ψ⟩=cos⁡θ2∣0⟩+eiφsin⁡θ2∣1⟩=(cos⁡θ2eiφsin⁡θ2)|\psi\rangle =\cos\frac{\theta}{2}|0\rangle+e^{i\varphi}\sin\frac{\theta}{2}|1\rangle= \left( \begin{matrix} \cos\frac{\theta}{2}\\ e^{i\varphi}\sin\frac{\theta}{2} \end{matrix} \right)

ランダムな任意の量子状態の情報を転送できるようにしたいので、そのような状態を生成することがこのレッスンの出発点になります。

2. 密度行列​

量子状態 ∣ψ⟩|\psi \rangle は密度行列として書くこともできます。この形式は純粋な量子状態の確率的な混合を表すのに便利です。単一のキュビットの場合、次のように書けます。

∣ψ⟩⟨ψ∣≡ρ=((cos⁡θ2eiφsin⁡θ2))((cos⁡θ2e−iφsin⁡θ2))=12((1+cos⁡θe−iφsin⁡θe−iφsin⁡θ1−cos⁡θ))|\psi \rangle \langle \psi| \equiv \rho = \left( \begin{pmatrix} \cos\frac{\theta}{2}\\ e^{i\varphi}\sin\frac{\theta}{2} \end{pmatrix} \right) \left( \begin{pmatrix} \cos\frac{\theta}{2} & e^{-i\varphi}\sin\frac{\theta}{2} \end{pmatrix} \right) =\frac{1}{2}\left(\begin{pmatrix} 1+\cos\theta & e^{-i\varphi}\sin\theta\\ e^{-i\varphi}\sin\theta & 1-\cos\theta \end{pmatrix}\right)

密度行列 ρ\rho は以下のように、パウリ行列の線形和で表せることに注意してください。

ρ=12(I+(sin⁡θcos⁡φ)X+(sin⁡θsin⁡φ)Y+(cos⁡θ)Z)\rho = \frac{1}{2}\bigl( \textbf{I} + (\sin{\theta}\cos{\varphi})\textbf{X}+ (\sin{\theta}\sin{\varphi})\textbf{Y} + (\cos{\theta})\textbf{Z} \bigr)

あるいは、一般的には次のようになります。

ρ=12(I+rxX+ryY+rzZ)\rho = \frac{1}{2}(\textbf{I} + r_{x}\textbf{X}+ r_{y}\textbf{Y} + r_{z}\textbf{Z})

ここで rx2+ry2+rz2=1r_{x}^2+r_{y}^2+r_{z}^2=1 です。

また、ブロッホベクトルは r=(rx,ry,rz)\textbf{r} = (r_{x}, r_{y}, r_{z}) です。

それでは、乱数を使って任意の量子状態を作成してみましょう。

# Added by doQumentation — installs the packages this notebook needs if they are missing
import importlib.util

_needed = {"numpy": "numpy", "qiskit": "qiskit", "qiskit_aer": "qiskit-aer", "qiskit_ibm_runtime": "qiskit-ibm-runtime"}
_missing = [pip for module, pip in _needed.items()
if importlib.util.find_spec(module) is None]
# One at a time, so a package that fails to install does not block the others
for _pip in _missing:
%pip install -q {_pip}
if not _missing:
print("\u2713 All required packages are installed")
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)

Output of the previous code cell

3. 量子状態トモグラフィー​

計算基底(∣0⟩|0 \rangle と ∣1⟩|1 \rangle)で量子状態を測定するだけでは、位相情報(複素数の情報)が失われてしまいます。しかし、準備プロセスを繰り返すことで ∣ψ⟩|\psi \rangle の多くのコピーを持てる場合(状態をクローンすることはできませんが、準備プロセスを繰り返すことはできます)、密度行列 ρ\rho に対する量子状態トモグラフィーを実行することで、rx,ry,rzr_{x}, r_{y}, r_{z} の値を推定することができます。次の形式を考えると、

ρ=12(I+rxX+ryY+rzZ)\rho = \frac{1}{2}(\textbf{I} + r_{x}\textbf{X}+ r_{y}\textbf{Y} + r_{z}\textbf{Z})

次のことが成り立ちます。

Tr(Xρ)=rx,Tr(Yρ)=ry,Tr(Zρ)=rzTr(\textbf{X} \rho) = r_{x}, \quad Tr(\textbf{Y} \rho) = r_{y}, \quad Tr(\textbf{Z} \rho) = r_{z}

rzr_{z} の場合、

Tr(Zρ)=⟨0∣Zρ∣0⟩+⟨1∣Zρ∣1⟩Tr(\textbf{Z} \rho) = \langle 0|\textbf{Z} \rho|0 \rangle + \langle 1|\textbf{Z} \rho|1 \rangle =⟨0∣(∣0⟩⟨0∣−∣1⟩⟨1∣)ρ∣0⟩+⟨1∣(∣0⟩⟨0∣−∣1⟩⟨1∣)ρ∣1⟩= \langle 0|(|0 \rangle\langle 0|-|1 \rangle\langle 1|) \rho|0 \rangle +\langle 1|(|0 \rangle\langle 0|-|1 \rangle\langle 1|) \rho|1 \rangle =⟨0∣ρ∣0⟩−⟨1∣ρ∣1⟩=\langle 0|\rho|0 \rangle- \langle 1| \rho|1 \rangle =⟨0∣ψ⟩⟨ψ∣0⟩−⟨1∣ψ⟩⟨ψ∣1⟩=\langle 0|\psi\rangle\langle \psi|0 \rangle - \langle 1| \psi\rangle\langle \psi|1 \rangle =∣α∣2−∣β∣2=|\alpha|^2-|\beta|^2

最後の等式の変換は ∣ψ⟩=α∣0⟩+β∣1⟩|\psi \rangle =\alpha|0\rangle+\beta|1\rangle に対するものです。したがって、rzr_{z} は ∣0⟩|0 \rangle の確率 −- ∣1⟩|1 \rangle の確率として求めることができます。

rzr_z 値の推定​

rzr_z を推定するために、量子状態を生成して測定します。この準備と測定を何度も繰り返し、最終的に測定の統計を使って上記の確率を推定し、rzr_z を見積もります。

ランダムな量子状態を生成するために、パラメーター θ,φ\theta, \varphi を使った汎用ユニタリーゲート UU を使用します。(詳細は 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")

Output of the previous code cell

AerSimulator を使用して、計算基底で測定を行い rzr_z を推定します。

# 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}

Output of the previous code cell

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

量子状態トモグラフィーの手法を使って rzr_z の値を推定しました。この場合、「ランダム」な状態のパラメーターを自分たちで選んでいるため、rzr_z の真の値を知っており、結果を確認できます。しかし、その性質上、ユーティリティスケールの作業は常にそれほど簡単に確認できるわけではありません。量子結果の検証については、このコースの後半で詳しく説明します。今は、推定がかなり正確であったことを確認するだけで十分です。

演習1: rxr_x 値の推定​

IBM® 量子コンピューターは zz 軸に沿って測定を行います(「zz 基底で」または「計算基底で」と表現されることもあります)。しかし、測定前に回転を適用することで、量子状態の xx 軸への射影も測定できます。より正確に言えば、xx 方向を向いていたものが zz 方向を向くようにシステムを回転させれば、zz 方向の同じ測定ハードウェアを使いながら、少し前まで xx 方向にあった状態について知ることができます。これが、ほとんどの量子コンピューター(そしてすべてのIBM量子コンピューター)が複数の軸に沿って測定を行う方法です。

この理解をもとに、量子状態トモグラフィーを使って rxr_x の値を推定するコードを書いてみましょう。

解答:

# 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")

Output of the previous code cell

# 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}

Output of the previous code cell

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:ryr_yの値を推定する​

先ほどと同じ論理的な手順で、測定の前にシステムを回転させることで ryr_y の情報を得ることができます。 量子状態トモグラフィーを使って ryr_y の値を推定するコードを自分で書いてみてください。前の例をベースにしつつ、異なる回転を適用すると良いでしょう。(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")

Output of the previous code cell

# 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}

Output of the previous code cell

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

これでr⃗\vec{r}のすべての成分を推定できたので、完全なベクトルを書き出すことができます。

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. 量子テレポーテーション​

アリスというキャラクターが、未知の量子状態 ∣ψ⟩|\psi \rangle を遠くにいる友人のボブへ送りたいという状況を考えてみましょう。二人はメールや電話のような古典的な通信しか使えないと仮定します。アリスは量子状態をコピーすることができません(複製不可能定理によるものです)。もし同じ準備プロセスを何度も繰り返せば、先ほどのように統計を積み上げることができます。しかし、未知の状態が1つしかない場合はどうすればよいでしょうか?その状態は研究したい物理プロセスから生まれたものかもしれませんし、より大きな量子計算の一部かもしれません。そのような場合、アリスはボブへ状態を送ることができるのでしょうか?実は、アリスとボブが貴重な量子リソース——共有されたエンタングル状態——を持っていれば可能です。前のレッスンで紹介したベル状態 ∣00⟩+∣11⟩2\frac{|00\rangle + |11\rangle}{\sqrt{2}} がその例です。これは「EPRペア」や「eビット」(エンタングルメントの基本単位)と呼ばれることもあります。アリスとボブがこのようなエンタングル状態を共有していれば、アリスは一連の量子演算を行い、2ビットの古典情報をボブに送ることで、未知の量子状態をテレポートさせることができます。

4.1 量子テレポーテーションのプロトコル​

前提:アリスはボブに送りたい未知の量子状態 ∣ψ⟩|\psi \rangle を持っています。アリスとボブは2キュビットのエンタングル状態(eビット)を共有しており、それぞれが物理的に自分の場所でそのうちの1キュビットを持っています。

ここでは、説明なしに手順の概要を示します。これらは以下で詳細に実装されます。

  1. アリスはCNOTゲートを使用して、∣ψ⟩|\psi \rangleを彼女のeビットの部分とエンタングルさせます。
  2. アリスは∣ψ⟩|\psi \rangleにアダマールゲートを適用し、彼女の両方のキュビットを計算基底で測定します。
  3. アリスはボブに測定結果(「00」、「01」、「10」、または「11」のいずれか)を送信します。
  4. ボブはアリスの2ビットの情報に基づいて、彼のeビットペアの部分に補正演算子を実行します。
    • 「00」の場合、ボブは何もしません
    • 「01」の場合、ボブはXゲートを適用します
    • 「10」の場合、ボブはZゲートを適用します
    • 「11」の場合、ボブはiY = ZXゲートを適用します
  5. ボブのeビットの部分は∣ψ⟩|\psi \rangleになります。

これについては量子情報の基礎でより詳しく解説されています。しかし、Qiskitでこれを実装していくうちに、状況がより明確になるでしょう。

4.2 量子テレポーテーションをシミュレートする量子Circuit​

いつも通り、Qiskitパターンのフレームワークを適用します。このサブセクションではマッピングのみに焦点を当てます。

ステップ1:問題を量子CircuitおよびOperatorにマッピングする​

上記のシナリオを記述するには、3つのキュビットを持つ Circuit が必要です。アリスとボブが共有するエンタングルペア用に2つ、未知の量子状態 ∣ψ⟩|\psi\rangle 用に1つです。

from qiskit import QuantumCircuit
import numpy as np
# create 3-qubits circuit
qc = QuantumCircuit(3, 3)

qc.draw(output="mpl")

Output of the previous code cell

最初に、アリスは未知の量子状態 ∣ψ⟩|\psi \rangle を持っています。これを UU 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")

Output of the previous code cell

作成した状態を視覚化することができますが、それは UU 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)

Output of the previous code cell

このプロトコルが始まる前に、アリスとボブは共有エンタングルペアをすでに持っていると仮定します。アリスとボブが本当に離れた場所にいる場合、未知の状態 ∣ψ⟩|\psi\rangle が作られる前に共有状態をセットアップしていたかもしれません。これらは異なるキュビット上で行われるため、ここでの順序は結果に影響しません。この順序にしているのは視覚化に都合が良いためです。

# 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")

Output of the previous code cell

次に、アリスは CXCX Gate と HH Gate を使って ∣ψ⟩|\psi \rangle を共有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")

Output of the previous code cell

アリスはボブに測定結果("00"、"01"、"10"、または "11")を送り、ボブはアリスの2ビットの情報に基づいて共有eビットの自分の部分に補正演算子を適用します。その後、ボブの部分が ∣ψ⟩|\psi \rangle になります。

# 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")

Output of the previous code cell

量子テレポーテーション 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)

Output of the previous code cell

キュビット 0 の UU Gate によって作られた量子状態(元々の秘密状態を保持していたキュビット)が、キュビット 2(ボブのキュビット)に転送されたことがわかります。

上のセルを何度か実行して確認してみてください。キュビット 0 と 1 の状態は変化しますが、キュビット 2 は常に ∣ψ⟩|\psi\rangle の状態にあることに気づくでしょう。

4.3 実行し、Uの逆行列の適用で結果を確認する​

上では、テレポートされた状態が正しく見えることを目視で確認しました。量子状態が正しくテレポートされたかどうかを確認するもう一つの方法は、ボブのキュビットに UU Gate の逆行列を適用して '0' を測定することです。つまり、U−1UU^{-1}U は恒等変換なので、ボブのキュビットが U∣0⟩U|0\rangle から作られた状態にあれば、逆行列を適用すると U−1U∣0⟩=∣0⟩U^{-1}U|0\rangle=|0\rangle が得られるはずです。

# 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")

Output of the previous code cell

実際の量子コンピューターに進む前に、まず 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}

Output of the previous code cell

リトルエンディアン表記では、キュビット 2 が最も左(または列ラベルでは最も下)のキュビットであることを思い出してください。列ラベルの左端・下端のキュビットは、すべての可能な結果において 0 であることに注目してください。これは、q2q_2 を状態 ∣0⟩|0\rangle で測定する確率が 100% であることを示しています。これは期待通りの結果であり、テレポーテーションプロトコルが正常に機能したことを示しています。

4.4 実際の量子コンピューターでのテレポーテーション​

次に、実際の量子コンピューターでテレポーテーションを実行します。動的回路機能を使うことで、測定結果に基づいてCircuitの途中で演算を行うことができ、テレポーテーション Circuit における条件分岐演算をリアルタイムで実装できます。実際の量子コンピューターで問題を解くために、Qiskitパターンの4つのステップに従います。

  1. 問題を量子CircuitおよびOperatorにマッピングする

  2. ターゲットハードウェア向けに最適化する

  3. ターゲットハードウェア上で実行する

  4. 結果を後処理する

演習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")

Output of the previous code cell

念のため補足すると、UU ゲートの逆行列を適用するのは、期待通りの動作を確認するためだけです。これはボブへ状態を送ること自体の一部ではなく、量子情報の転送のみが目的であれば、この逆 UU ゲートは使用しません。

ステップ 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)

前のコードセルの出力

デバイスによってカップリングマップは異なり、またデバイスごとにパフォーマンスが高いキュビットやカプラーが存在します。さらに、量子コンピューターによってネイティブゲート(ハードウェアが実行できるゲート)が異なる場合があります。Circuit をトランスパイルすると、抽象的な量子Circuit がターゲット量子コンピューターで実行可能なゲートを使って書き直され、物理キュビットへの最適なマッピングが選択されます(その他の処理も行われます)。トランスパイルは奥深く複雑なトピックです。詳細については 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 を使用して他のキュビットをトレースアウト(周辺化)し、キュビット 2 上のボブの結果だけを取り出すこともできます。

# 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)

前のコードセルの出力

ここで分かるように、∣1⟩|1 \rangle を測定した結果がいくつか存在します。これらはノイズやエラーによるものです。特に、動的Circuit はCircuit の途中での測定に時間がかかるため、エラー率が高くなる傾向があります。

4.5 量子テレポーテーションのまとめ​

エンタングルしたキュビットのペア(eビット)を共有することで、離れた相手に量子状態を転送できます。

  1. 量子テレポーテーションは光速より速く量子状態を送ることができますか? いいえ。アリスが測定結果をボブに古典的な方法で伝える必要があるためです。

  2. 量子テレポーテーションは、量子状態のコピーを禁じる「複製不可能定理」を破りますか? いいえ。アリスのキュビットにあった元の量子状態は測定によって失われており、∣0⟩|0\rangle または ∣1⟩|1\rangle に崩壊しています。

5. 超高密度符号化​

ほぼ同じセットアップを別の目的に使用することができます。アリスがボブに2ビットの古典情報を送りたいが、ボブとの古典的な通信手段がないとします。しかし、アリスとボブはエンタングルペアを共有しており、アリスは自分のキュビットをボブの場所に送ることができます。量子テレポーテーションプロトコルとの違いに注目してください。テレポーテーションでは古典通信が利用可能であり、目的は量子状態を送ることでした。ここでは古典通信は利用できず、キュビットの転送を使って2ビットの古典情報を共有します。

5.1 超高密度符号化のプロトコル​

前提: アリスは2ビットの情報、たとえば a1a2∈{00,01,10,11}a_1a_2 \in \{00, 01, 10, 11\} を持っています。アリスとボブはエンタングルペア(eビット)を共有していますが、古典的な通信はできません。

  1. アリスは eビット の自分の部分に対して以下のいずれかの操作を行います。

    • a1a2=00a_1a_2 = 00 の場合、何もしない

    • a1a2=01a_1a_2 = 01 の場合、Z Gate を適用する

    • a1a2=10a_1a_2 = 10 の場合、X Gate を適用する

    • a1a2=11a_1a_2 = 11 の場合、Z Gate と X Gate を適用する。

  2. アリスは eビット の自分の部分をボブの場所に送ります。

  3. ボブはアリスからのキュビットをコントロール、自分のキュビットをターゲットとして CNOT Gate を適用し、次にアリスからのキュビットに H Gate を適用して、2つのキュビットを測定します。可能な初期状態とボブの操作の結果は次のとおりです:

∣00⟩+∣11⟩2→CX01⊗H0→∣00⟩\frac {|00\rangle + |11\rangle}{\sqrt 2} \rightarrow CX_{01}\otimes H_0 \rightarrow |00\rangle ∣00⟩−∣11⟩2→CX01⊗H0→∣01⟩\frac {|00\rangle - |11\rangle}{\sqrt 2} \rightarrow CX_{01}\otimes H_0 \rightarrow |01\rangle ∣10⟩+∣01⟩2→CX01⊗H0→∣10⟩\frac {|10\rangle + |01\rangle}{\sqrt 2} \rightarrow CX_{01}\otimes H_0 \rightarrow |10\rangle ∣10⟩−∣01⟩2→CX01⊗H0→−∣11⟩\frac {|10\rangle - |01\rangle}{\sqrt 2} \rightarrow CX_{01}\otimes H_0 \rightarrow -|11\rangle

−∣11⟩-|11\rangle のマイナス符号はグローバル位相であるため、測定することはできません。

5.2 超高密度符号化をシミュレートする量子Circuit​

超高密度符号化のプロトコルに基づいて、以下のように超高密度符号化Circuit を構築できます。アリスがボブに転送したいメッセージ 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)

前のコードセルの出力

ボブがアリスの送りたかったメッセージを受け取ったことが分かります。

次に、実際の量子コンピューターで試してみましょう。

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つのキュビットの転送により、2ビットの古典情報を通信できます。

# See the version of Qiskit
import qiskit

qiskit.__version__
'2.0.2'