はやしくん雑記

はやしです


Blueqatで量子コンピューター入門をしてみた(量子テレポーテーション)


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

はじめに

GWの量子コンピューター入門第二弾です。

blog.hayashikun.com

今日は量子テレポーテーションをBlueqatで実装してみます。

まず、チュートリアルにある2者間エンタングルメントを使った量子テレポーテーションを実装してみます。
Blueqat/012_algo_teleportation.ipynb at master · Blueqat/Blueqat · GitHub

その次に、3者間エンタングルメントによる量子テレポーテーションネットワークを実装します。

量子テレポーテーションとは

量子テレポーテーションとは、量子状態を伝送することである。
「任意の量子状態は複製できない」という要請を満たして量子状態を伝送するためには、送信側でオリジナルの量子状態が消滅し、受信側で再生される必要がある。

量子複製不可能定理 - Wikipedia

量子テレポーテーション

以下に示しているのは、ベネットらによって提案された量子ビットのテレポーテーション回路である。

f:id:hayashikunsan:20190503143321p:plain

Vの状態  \ket{\psi}_V はAへと渡されて、Bのところで再生される。
0, 1, 2ではわかりにくいので、それぞれV, A, Bとしている。

この回路では、AとBの間で2者間エンタングルメントを共有し、Vの情報をAからBへと状態を送っている。

赤線で囲んだ箇所で、AとBの量子ビットはエンタングルされる。

c1 = Circuit().h[1].cx[1,2]
c1.m[:].run(shots=1000)
## Counter({'011': 493, '000': 507})

重要なのは、青線で囲んだ箇所である。ここで、量子複製不可能定理を回避するために、ベル測定という方法を用いる。

ベル測定とは、ベル基底の射影を測定することである。ベル基底とは、2量子系がエンタングルした基底のことで、以下の4つがある。


\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:1対応する。


\ket{\Phi}^+ \to \ket{00} \\
\ket{\Phi}^- \to \ket{10} \\
\ket{\Psi}^+ \to \ket{01} \\
\ket{\Psi}^- \to \ket{11} \\

# ベル測定の回路
c2 = Circuit().cx[0, 1].h[0].m[0, 1]

# |Φ>+
(Circuit().h[0].cx[0, 1] + c2).run(shots=1000)
## Counter({'00': 1000})

# |Φ>-
(Circuit().x[0].h[0].cx[0, 1] + c2).run(shots=1000)
## Counter({'10': 1000})

# |Ψ>+
(Circuit().h[0].cx[0, 1].x[0] + c2).run(shots=1000)
## Counter({'01': 1000})

# |Ψ>-
(Circuit().x[0].h[0].cx[0, 1].x[0] + c2).run(shots=1000)
## Counter({'11': 1000})

つまり、赤線で囲んだ箇所からの出力は  \ket{\Phi}^+_{AB} と書ける。

ベル測定前の全体は、送りたい量子状態  \ket{\psi}_V = c_0 \ket{0}_V + c_1 \ket{1}_V \ket{\Phi}^+_{AB} の直積であるから、以下のように変形できる。


(c_0 \ket{0}_V + c_1 \ket{1}_V) \otimes \ket{\Phi}^+_{AB} \\
= \frac{1}{2} \left(
\ket{\Phi}^+_{VA} \otimes (c_0 \ket{0}_B + c_1 \ket{1}_B) \\
\\+ \ket{\Phi}^-_{VA} \otimes (c_0 \ket{0}_B - c_1 \ket{1}_B) \\
\\+ \ket{\Psi}^+_{VA} \otimes (c_0 \ket{1}_B + c_1 \ket{0}_B) \\
\\+ \ket{\Psi}^-_{VA} \otimes (c_0 \ket{1}_B - c_1 \ket{0}_B)
\right)

この状態でベル測定を行うと、状態は4つのうちのどれかに収束する。つまり、Bの状態はベル測定によって


c_0 \ket{0}_B + c_1 \ket{1}_B, \\
c_0 \ket{0}_B - c_1 \ket{1}_B, \\
c_0 \ket{1}_B + c_1 \ket{0}_B, \\
c_0 \ket{1}_B - c_1 \ket{0}_B

のどれかに収束する。

Bで状態を再生させるためには、ベル測定の結果(V, A)をBに送って、適切なユニタリー変換を行う必要がある。


\ket{\Phi}^+_{VA} \to \ket{00} : I(c_0 \ket{0}_B + c_1 \ket{1}_B) = c_0 \ket{0}_B + c_1 \ket{1}_B \\
\ket{\Phi}^-_{VA} \to \ket{10} : Z(c_0 \ket{0}_B - c_1 \ket{1}_B) = c_0 \ket{0}_B + c_1 \ket{1}_B \\
\ket{\Psi}^+_{VA} \to \ket{01} : X(c_0 \ket{1}_B + c_1 \ket{0}_B) = c_0 \ket{0}_B + c_1 \ket{1}_B \\
\ket{\Psi}^-_{VA} \to \ket{11} : XZ(c_0 \ket{1}_B - c_1 \ket{0}_B) = c_0 \ket{0}_B + c_1 \ket{1}_B

これはCXゲート(Aが1の場合Xを適用)とCZゲート(Vが1の場合Zを適用)で実現できる。

c3 = Circuit().cx[1,2].cz[0,2]

A, Bをエンタングルさせる回路(c1)、ベル測定回路 (c2)、ユニタリー変換を行う回路 (c3)をつなげると、量子テレポーテーションの回路が完成する。

c1 = Circuit().h[1].cx[1,2]
c2 = Circuit().cx[0, 1].h[0].m[0, 1]
c3 = Circuit().cx[1,2].cz[0,2]
c = c1 + c2 + c3

# Vが1であれば、必ずBでも1が再生される
(Circuit().x[0] + c).m[:].run(shots=100)
## Counter({'001': 20, '101': 29, '111': 25, '011': 26})


# Vの状態は|0>と|1>が3:1
c4 = Circuit().ry(np.pi / 3)[0]
c4.m[:].run(shots=10000)
## Counter({'0': 7555, '1': 2445})

out = (c4 + c).m[:].run(shots=10000)
counts = [0, 0]
for bits, count in out.items():
    counts[int(bits[2])] += count

# Bも|0>と|1>が3:1になる
counts
## [7544, 2456]

というわけでVの状態をBにテレポーテーションさせることができた。

量子テレポーテーションネットワーク

次に、3者間でエンタングルメントを共有し、相互に状態を送り合う量子テレポーテーションネットワークを構築する。

f:id:hayashikunsan:20190503175940p:plain

A, B, Cの3者間でエンタングルメントを共有(赤線で囲んだ箇所で生成する)し、Vの情報はAからBへと送られる。テレポーテーションを成功させるためには、Cの情報が必要である。

ここで、最後のZはCCZであるが、令和元年5月3日(使ってみたかった)現在、pip経由でインストールしたBlueqat (v0.3.7)ではCCZGateは使えない。ただし、すでに実装済みっぽいので、すぐに使えるようになると思われる。

Add numpy backend ccx implementation to improve performance · Blueqat/Blueqat@9db46fd · GitHub

とりあえず、これで無理くり使えるようにした。

import blueqat

class CCZGate(blueqat.gate.Gate):
    lowername = "ccz"

class MyBackend(blueqat.backends.numpy_backend.NumPyBackend):
    def gate_ccz(self, gate, ctx):
        c1, c2, t = gate.targets
        qubits = ctx.qubits
        n_qubits = ctx.n_qubits
        i = ctx.indices
        indices = (i & (1 << c1)) != 0
        indices &= (i & (1 << c2)) != 0
        indices &= (i & (1 << t)) != 0
        qubits[indices] *= -1
        return ctx

blueqat.circuit.BACKENDS["my_backend"] = MyBackend
blueqat.circuit.DEFAULT_BACKEND_NAME = "my_backend"
blueqat.circuit.GATE_SET["ccz"] = CCZGate

上の回路図の通りに量子回路を組むと、以下のようになる。

c1 = Circuit().h[1].cx[1, 2].cx[1, 3]
c2 = Circuit().cx[0,1].h[0].m[0,1]
c3 = Circuit().h[3].m[3]
c4 = Circuit().cx[1,2].ccz[0,3,2]
c = c1 + c2 + c3 + c4

# Vを1にすると、Bも1になる
(Circuit().x[0] + c).m[:].run(shots=1000)
## Counter({'0010': 120,
##  '0111': 143,
##  '1010': 121,
##  '0011': 112,
##  '1110': 124,
##  '0110': 131,
##  '1011': 123,
##  '1111': 126})

# Vの状態を|0>と|1>が3:1になるように
c5 = Circuit().ry(np.pi / 3)[0]
out = (c5 + c).m[:].run(shots=10000)
counts = [0, 0]
for bits, count in out.items():
    counts[int(bits[2])] += count

counts
## [7558, 2442]

というわけで、量子テレポーテーションと、量子テレポーテーションネットワークを実装できた。

はてなブログで数式を書くのがつらいので、数式を書くのもうやめたい。