量子テレポーテーション、やってみた

$$
\def\bra#1{\mathinner{\left\langle{#1}\right|}}
\def\ket#1{\mathinner{\left|{#1}\right\rangle}}
\def\braket#1#2{\mathinner{\left\langle{#1}\middle|#2\right\rangle}}
$$

自作の量子計算シミュレータを使って、量子テレポーテーション、やってみたよ。

* [量子計算シミュレータ、つくってみた](https://qiita.com/Sam_N/items/310b18c584e6fc686387)
* [qlazy - Quantum Computer Simulator](https://github.com/samn33/qlazy)

## はじめに

「量子テレポーテーション」のお勉強です。以下を参考にさせていただきました。

* [量子テレポーテーション - Wikipedia](https://ja.m.wikipedia.org/wiki/量子テレポーテーション)
* [EMANの物理学:量子力学:量子テレポーテーション](http://eman-physics.net/quantum/computer9.html)
* [吾輩は量子プログラミング言語である。名前はまだ無い。 Microsoftで生れ量子テレポーテーションした事だけは記憶している。](https://qiita.com/piyo7/items/0c80181c567b3075c00f)
* [量子テレポーテーションを実際にやってみた話](http://kadora.hatenablog.com/entry/2016/09/24/195753)

量子テレポーテーションというのは、量子もつれを利用して、ある量子状態をそのまま他方に転送することを言います。「テレポーテーション」といっても物体が瞬間移動するわけではありませんし、光速を超えて情報伝達がなされるわけでもありません。

いま、送信者をAlice、受信者をBobと呼ぶことにします。まず、２つの「量子（例えば、光子）」をもつれさせて、一方をAlice、他方をBobに渡します。光子は電子スピンと同様、２つの状態の重ね合わせで表現できる「量子」なので、スピンのように、もつれさせることができます（と思ってください）。

Aliceは、もう一つ別の「量子」をもっていて、これを適当な重ね合わせ状態にします。この量子状態をBobに転送（テレポート）することを考えます。どうやるかというと、、、

Aliceは転送したい「量子」ともつれている片割れの「量子」の２つを合わせて「ベル測定」という特別な測定を行います。ベル測定というのは、２つの量子状態を一度に測定するもので、結果は４つの状態のいずれかになります。

AliceはBobに、この測定結果を通常の通信路で伝達します。Bobはそれ受け取り、その測定結果に応じて、自分がもっている、もつれている片割れの「量子」に、ある操作（量子ゲート）を適用します。そうすると、あーら不思議、Bobがもっている「量子」が、Aliceが転送したかった量子状態そのものにすり替わっているではありませんか！驚き！となります。

ざっくり、文書で書くと以上のようなことなのですが、なぜこんなことになるのか、次に、数式で確認してみましょう。

## 原理

３つの量子ビットを用意します。０番目をAliceがBobに送信したい量子ビットとします。１番目と２番目はもつれさせて、１番目をAliceに渡し、２番目をBobに渡すものとします。

０番目（AliceがBobに送信したいもの）を、とりあえず、

```math
\ket{\psi} = \alpha \ket{0} + \beta \ket{1}
```

と書いておきます（後述するシミュレータではX軸周りとZ軸周りの回転ゲートで、適当な量子状態を作成して、$\alpha$と$\beta$を決めています）。

１番目と２番目は、以下のようにアダマールゲートと制御NOTゲートを通して、もつれさせます。

```sh
q1 |0> ----H--*---
q2 |0> -------CX--
```
そうすると、１番目と２番目の量子ビットに関して、以下の量子状態ができます。

```math
\frac{1}{\sqrt{2}} (\ket{0}_{1}\ket{0}_{2} + \ket{1}_{1}\ket{1}_{2})
```

先程の０番目と合わせると、トータルで以下のような状態になります。

```math
\frac{1}{\sqrt{2}} \ket{\psi}_{0} (\ket{0}_{1}\ket{0}_{2} + \ket{1}_{1}\ket{1}_{2}) \tag{1}
```

ここで、Aliceが「ベル測定」を行うのですが、その説明をします。いま、２量子系を考えているので、その測定は、数学的には４つの基底への射影演算をやることに相当します。ベル測定とは、以下の４つの基底への射影演算として定義されます。

```math
\ket{\Phi^{+}} = \frac{1}{\sqrt{2}} (\ket{00} + \ket{11}) \\
\ket{\Phi^{-}} = \frac{1}{\sqrt{2}} (\ket{00} - \ket{11}) \\
\ket{\Psi^{+}} = \frac{1}{\sqrt{2}} (\ket{01} + \ket{10}) \\
\ket{\Psi^{-}} = \frac{1}{\sqrt{2}} (\ket{01} - \ket{10})
```

定義がわかったので、(1)式の０番目と１番目の量子ビットに対してベル測定をやってみます。射影演算は４つあります。各々、測定後の結果を列挙します。

* $\ket{\Phi^{+}}$への射影

```math
\begin{align}
&\frac{1}{\sqrt{2}} \ket{\Phi^{+}}_{01} \bra{\Phi^{+}}_{01} \ket{\psi}_{0} (\ket{0}_{1}\ket{0}_{2} + \ket{1}_{1}\ket{1}_{2}) \\
&= \frac{1}{\sqrt{2}} \ket{\Phi^{+}}_{01} (\alpha \bra{0}_{1} + \beta\bra{1}_{1}) (\ket{0}_{1} \ket{0}_{2} + \ket{1}_{1} \ket{1}_{2}) \\
&= \frac{1}{\sqrt{2}} \ket{\Phi^{+}}_{12} (\alpha \ket{0}_{2} + \beta \ket{1}_{2}) \tag{2}
\end{align}\\
```

同様にして、他の３つも計算できます。

* $\ket{\Phi^{-}}$への射影

```math
\begin{align}
&\frac{1}{\sqrt{2}} \ket{\Phi^{-}}_{01} \bra{\Phi^{-}}_{01} \ket{\psi}_{0} (\ket{0}_{1}\ket{0}_{2} + \ket{1}_{1}\ket{1}_{2}) \\
&= \frac{1}{\sqrt{2}} \ket{\Phi^{-}}_{01} (\alpha \ket{0}_{2} - \beta \ket{1}_{2}) \tag{3}
\end{align}
```

* $\ket{\Psi^{+}}$への射影

```math
\begin{align}
&\frac{1}{\sqrt{2}} \ket{\Psi^{+}}_{01} \bra{\Psi^{+}}_{01} \ket{\psi}_{0} (\ket{0}_{1}\ket{0}_{2} + \ket{1}_{1}\ket{1}_{2}) \\
&= \frac{1}{\sqrt{2}} \ket{\Psi^{+}}_{01} (\beta \ket{0}_{2} + \alpha \ket{1}_{2}) \tag{4}
\end{align}
```

* $\ket{\Psi^{-}}$への射影

```math
\begin{align}
&\frac{1}{\sqrt{2}} \ket{\Psi^{-}}_{01} \bra{\Psi^{-}}_{01} \ket{\psi}_{0} (\ket{0}_{1}\ket{0}_{2} + \ket{1}_{1}\ket{1}_{2}) \\
&= \frac{1}{\sqrt{2}} \ket{\psi^{-}}_{01} (-\beta \ket{0}_{2} + \alpha \ket{1}_{2}) \tag{5}
\end{align}
```

上の４つの式を眺めてみると、どうでしょう。なんとなく２番目の量子ビットの状態が、もともとAliceが設定した状態に似ていることがわかると思います（(2)式はそのものです）。２番目の量子ビットは、Bobに渡してあったものなのですが、Aliceが測定することで、遠隔地にいるBobが持っている量子に影響を及ぼすことができる、というのが量子もつれの不思議なところであり、量子テレポーテーションの肝でもあります。

さて、あとはBobがもっている２番目の量子ビットを操作して、Aliceの最初の０番目の状態に完全に一致させることを考えます。これは、Aliceからベル測定の結果が何だったかを教えてもらえれば、簡単で、

* $\ket{\Phi^{+}}$の場合

```sh
q2 --I-- # 何もしない
```

* $\ket{\Phi^{-}}$の場合

```sh
q2 --Z--
```

* $\ket{\Psi^{+}}$の場合

```sh
q2 --X--
```

* $\ket{\Psi^{-}}$の場合

```sh
q2 --X--Z--
```

という量子ゲートに通してあげれば、Aliceが転送したかった状態を２番目の量子ビットに完全再現することができる、というわけです。

## シミュレータで実行（その１）

では、シミュレータで、以上のことが本当にできるか確認してみましょう。が、ちょっと待ってください。ベル測定ってシミュレータでどうやるんでしたっけ？という疑問がわいてきますよね。

実は、普通の測定（Z方向の測定）でベル測定できるようにするために、ちょっとした工夫が必要になります。具体的には、以下の回路を通してあげます。

```sh
q0 --*---H--M  -> b0
q1 --CX-----M  -> b1
```

この制御NOTゲートとアダマールゲートの組み合わせで、通常の基底からベル基底に変換することができるのです（ここで、b0,b1は最後の測定結果に応じて0/1の値が格納される古典レジスタです）。

ベル基底の４つの状態を上の回路に（頭の中で！？）通してみれば、すぐにわかります（以下、正規化の係数はいちいち書くのが面倒なので省略しました）。

```math
\begin{align}
\ket{\Phi^{+}} &= \ket{00} + \ket{11} \\
&\rightarrow (\ket{0} + \ket{1}) \ket{0} + (\ket{0} - \ket{1}) \ket{0} = \ket{00} \\
\ket{\Phi^{-}} &= \ket{00} - \ket{11} \\
&\rightarrow (\ket{0} + \ket{1}) \ket{0} - (\ket{0} - \ket{1}) \ket{0} = \ket{10} \\
\ket{\Psi^{+}} &= \ket{01} + \ket{10} \\
&\rightarrow (\ket{0} + \ket{1}) \ket{1} + (\ket{0} - \ket{1}) \ket{1} = \ket{01} \\
\ket{\Psi^{-}} &= \ket{01} - \ket{10} \\
&\rightarrow (\ket{0} + \ket{1}) \ket{1} - (\ket{0} - \ket{1}) \ket{1} = \ket{11} \\
\end{align}
```

となって、ベル基底とb0,b1の値のセットとが対応づけられるようになります。つまり、b0,b1の観測をすることが、すなわちベル測定したことになる、という寸法です。

さて、ようやくシミュレーションの準備が整いました。
[qlazy](https://github.com/samn33/qlazy)で実行します。測定結果に応じて、適用する量子回路を変えないといけないので、Linuxコマンドではちょっとやりにくいです。なので、Pythonでやってみます。コードは、以下の通りです。

```python
from qlazypy import QState

qs = QState(3)

# prepare qubit (id=0) that Alice want to send to Bob by rotating around X,Z
qs.ry(0,phase=0.3).rz(0,phase=0.4)

# make entangled 2 qubits (id=1 for Alice, id=2 for Bob)
qs.h(1).cx(1,2)

# initial state (before teleportation)
print("== Alice (initial) ==")
qs.show(id=[0])
print("== Bob (initial) ==")
qs.show(id=[2])
    
# Alice execute Bell-measurement to her qubits 0,1
qs.cx(0,1).h(0)
b0 = qs.m(id=[0],shots=1).lst
b1 = qs.m(id=[1],shots=1).lst
print("== Bell measurement ==")
print("b0,b1 = ", b0,b1)

# Bob operate his qubit (id=2) according to the result
if b0 == 0 and b1 == 0:  # phi+
    pass
elif b0 == 0 and b1 == 1:  # psi+
    qs.x(2)
elif b0 == 1 and b1 == 0:  # psi-
    qs.z(2)
elif b0 == 1 and b1 == 1:  # phi-
    qs.x(2).z(2)

# final state (after teleportation)
print("== Alice (final) ==")
qs.show(id=[0])
print("== Bob (final) ==")
qs.show(id=[2])

del qs
```

これを実行すると、

```sh
== Alice (initial) ==
c[0] = +0.8910+0.0000*i : 0.7939 |+++++++++
c[1] = +0.1403+0.4318*i : 0.2061 |+++
== Bob (initial) ==
c[0] = +0.0000+0.0000*i : 0.0000 |
c[1] = +1.0000+0.0000*i : 1.0000 |+++++++++++
== Bell measurement ==
b0,b1 =  1 0
== Alice (final) ==
c[0] = +0.0000+0.0000*i : 0.0000 |
c[1] = +1.0000+0.0000*i : 1.0000 |+++++++++++
== Bob (final) ==
c[0] = +0.8910+0.0000*i : 0.7939 |+++++++++
c[1] = +0.1403+0.4318*i : 0.2061 |+++
```

となり、確かに、最初Aliceが持っていた量子状態が、きちんとBobの量子ビットに乗り移っていることがわかります。めでたしめでたし！

## シミュレータで実行（その２）

ここで、説明を終了しても良いのですが、もうひとつの実行例を掲載しておきます。実は、ベル測定そのものを実装したので、その動作も確認してみました。mb()というメソッドでベル測定を実行できます。コードは以下の通りです。

```python
from qlazypy import QState

BELL_PHI_PLUS  = 0
BELL_PHI_MINUS = 3
BELL_PSI_PLUS  = 1
BELL_PSI_MINUS = 2

qs = QState(3)

# prepare qubit (id=0) that Alice want to send to Bob by rotating around X,Z
qs.ry(0,phase=0.3).rz(0,phase=0.4)

# make entangled 2 qubits (id=1 for Alice, id=2 for Bob)
qs.h(1).cx(1,2)

# initial state (before teleportation)
print("== Alice (initial) ==")
qs.show(id=[0])
print("== Bob (initial) ==")
qs.show(id=[2])
    
# Alice execute Bell-measurement to her qubits 0,1
print("== Bell measurement ==")
result = qs.mb(id=[0,1],shots=1).lst

# Bob operate his qubit (id=2) according to the result
if result == BELL_PHI_PLUS:
    print("result: phi+")
elif result == BELL_PSI_PLUS:
    print("result: psi+")
    qs.x(2)
elif result == BELL_PSI_MINUS:
    print("result: psi-")
    qs.x(2).z(2)
elif result == BELL_PHI_MINUS:
    print("result: phi-")
    qs.z(2)

# final state (after teleportation)
print("== Alice (final) ==")
qs.show(id=[0])
print("== Bob (final) ==")
qs.show(id=[2])

del qs
```

これを実行すると、

```sh
== Alice (initial) ==
c[0] = +0.8910+0.0000*i : 0.7939 |+++++++++
c[1] = +0.1403+0.4318*i : 0.2061 |+++
== Bob (initial) ==
c[0] = +1.0000+0.0000*i : 1.0000 |+++++++++++
c[1] = +0.0000+0.0000*i : 0.0000 |
== Bell measurement ==
result: psi-
== Alice (final) ==
c[0] = +1.0000+0.0000*i : 1.0000 |+++++++++++
c[1] = +0.0000+0.0000*i : 0.0000 |
== Bob (final) ==
c[0] = +0.8910+0.0000*i : 0.7939 |+++++++++
c[1] = +0.1403+0.4318*i : 0.2061 |+++
```

となり、こちらも無事テレポーテーション成功です！


## 補足（2019.4.20）

qlazyには特定の量子ビットを指定してshowメソッドで量子状態を表示する機能があり、上の例ではそれを使っています。もちろんシミュレータならではの機能なのですが、だとしても、重ね合わさって、場合によってはもつれているトータルな状態に対して、そんなことできるの、とか、どうやってんの、という疑問があるかもしれないので、ちょっと説明しておきます。

内部的には、表示したい特定の量子ビット以外のビットを仮に測定し無理やり波束を収縮させます。結果得られた確率振幅の係数たちの中から、表示したい量子ビットの組み合わせに相当するノンゼロの係数をピックアップして表示します。ノンゼロの係数がなかったらゼロとします。最後に、測定前の状態に戻します。以上がやっている内容です。

表示したい量子ビットとそれ以外の量子ビットの世界がきっぱりとテンソル積で分解できる形になっていれば、こんな実装でいいのかなと。問題はもつれていてテンソル積の形になっていない場合です。この実装だとshowで表示するたびに結果が変わりますね。まー、これはこれで面白いし（もつれていたということがわかる）、こんな適当実装でも結構使える場合も多いかなと思っています。

ちなみに上の量子テレポーテーションの例で言うと、Aliceがベル測定した段階で、Bobの量子ビットのもつれはほどけるので、何回showしても結果は同じです（のはずです）。

## 補足(2021.9.5)

[qlazy](https://github.com/samn33/qlazy)最新版でのソースコードは[ここ](https://github.com/samn33/qlazy/blob/master/example/py/QuantumTeleportation)に置いてあります。


以上