1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Qiskit 3量子ビットのビット反転符号

Last updated at Posted at 2023-12-19

エラーについて以前扱ったので,せっかくなのでエラー訂正についても書いていきたいと思います.参考にしているのはNielsen-Chuang1です.

3量子ビットのビット反転符号

ビット反転エラーに対する符号を考えていきます.ここで,ビット反転エラーが小さい確率$p$で起き,$3$量子ビットあった場合には,$1$量子ビット以下しかエラーが起きない,つまり

$$
\begin{align}
p^3+3(1-p)p^2\ll1
\end{align}
$$

であるとします.

符号化

論理量子ビットを次のように与える符号を考えます.

$$
\begin{align}
\ket{0_L}&=\ket{000}\\
\ket{1_L}&=\ket{111}
\end{align}
$$

これより,論理Xゲートの固有状態は

$$
\begin{align}
\ket{+_L}&=\frac{1}{\sqrt{2}}\left(\ket{000}+\ket{111}\right)\\
\ket{-_L}&=\frac{1}{\sqrt{2}}\left(\ket{000}-\ket{111}\right)
\end{align}
$$

と表されます.論理Zゲートや論理Xゲートは,

$$
\begin{align}
Z_L&=ZZZ\\
X_L&=XXX
\end{align}
$$

を採用すれば辻褄が合うことが分かります.また,符号化するためには

$$
\begin{align}
\operatorname{CNOT}_{12}\left(a\ket{0}+b\ket{1}\right)\otimes\ket{0}&=a\ket{00}+b\ket{11}
\end{align}
$$

より,$CNOT$を2回作用させれば良いことが分かります.

エラー検知

仮定よりエラーは多くともどれか1つにしか起きません.そこで,次のような射影測定$ \{P_i\}_{i=0}^3 $を考えてみます.

$$
\begin{align}
P_0&=\ket{000}\bra{000}+\ket{111}\bra{
111}\\
P_1&=\ket{100}\bra{100}+\ket{011}\bra{011}\\
P_2&=\ket{010}\bra{010}+\ket{101}\bra{101}\\
P_3&=\ket{001}\bra{001}+\ket{110}\bra{110}
\end{align}
$$

例えば,一つ目の量子ビットにエラーが起きた場合,

$$
\begin{align}
\ket{\psi_L}&=a\ket{0_L}+b\ket{1_L}\\
&=a\ket{000}+b\ket{111}\\
&\to a\ket{100}+b\ket{011}=:\ket{\psi'_L}
\end{align}
$$

となりますが,$\bra{\psi'_L}P_1\ket{\psi'_L}=1$より,一つ目の量子ビットにエラーが起きていることを検知することができます.

例えば$ZZI-2IZZ$といった物理量を測定し,その測定値から対応する射影演算子を推測することができます.
しかし,実際の量子コンピュータでは各量子ビットに対して個別に$Z$測定を行うことしか許されず,そのまま測定すると状態が壊れてしまいます.例えば一つ目の量子ビットを$Z$測定して,$+1$が返ってきた場合,

$$
\begin{align}
\ket{\psi'_L}&=a\ket{100}+b\ket{011}\\
&\to \ket{100}
\end{align}
$$

と射影されてしまっており,係数$a,b$の情報が消えてしまいます.そこで,$ZZI$と$IZZ$に関する測定を,状態が壊れないように行うのが肝です.これらの固有値は$\pm1$であり,$ZII$の固有値と等しいことに着目すれば,これらの間のユニタリ変換が存在するはずです.そのユニタリ変換を施した後に,一つ目の量子ビットを$Z$測定すれば良いわけです.例えば$ZZI$について考えれば,一つ目と二つ目の量子ビットのみを取り出すと

$$
\begin{align}
ZZ&=\ket{00}\bra{00}-\ket{10}\bra{10}-\ket{01}\bra{01}+\ket{11}\bra{11}\\
\operatorname{CNOT} _ {21} ZZ\operatorname{CNOT} _ {21}&=\left(I\ket{0}\bra{0}+X\ket{1}\bra{1}\right)ZZ\left(I\ket{0}\bra{0}+X\ket{1}\bra{1}\right)\\
&=\ket{00}\bra{00}-\ket{10}\bra{10}-\ket{11}\bra{11}+\ket{01}\bra{01}\\
&=ZI
\end{align}
$$

が成り立つため,$\operatorname{CNOT} _ {21}$を作用させれば良いことが分かります.実際に状態の変化を追ってみると,

$$
\begin{align}
\operatorname{CNOT} _ {21}I_3\ket{\psi'_L}&=a\ket{100}+b\ket{111}
\end{align}
$$

となるため,一つ目の量子ビットに対する$Z$測定を行うことで,測定結果$-1$を得ることができます.これより,$\ket{\psi'_L}は$$ZZI$の$-1$の固有値に属する固有状態であったことが分かります.よって,一つ目の量子ビットか二つ目の量子ビットにビット反転エラーが生じていることが判明しました.

同様にして$IZZ$測定を行い,エラーが生じている量子ビットを特定します.

状態回復

測定しエラーを特定したら,次にエラーが入る前の状態に戻していきます.これは,エラーの入っている量子ビットにXゲートを作用させ反転させれば良いだけです.

復号

計算の最後に復号し,1物理量子ビットに情報を集約させることを考えます.これは符号化の逆の操作,つまり$CNOT$を2回作用させれば良いです.実際,

$$
\begin{align}
\operatorname{CNOT}_{12}\left(a\ket{00}+b\ket{11}\right)&=\left(a\ket{0}+b\ket{1}\right)\otimes\ket{0}
\end{align}
$$

となり,一番目の量子ビットに情報が集約されていることが分かります.

実装

from qiskit.circuit import QuantumCircuit
from qiskit.visualization import plot_bloch_multivector
from qiskit import Aer, execute

まずは,論理Xゲートを作用させた後に,ビット反転エラーが乗った場合を考えてみます.

circ = QuantumCircuit(3, 2)


# 論理Xゲート
circ.x([0, 1, 2])

# ビット反転エラー
circ.x([0])


simulator = Aer.get_backend('statevector_simulator')
result = execute(circ, simulator).result()
state = result.get_statevector()
plot_bloch_multivector(state)

biterror_1.png

一番目の量子ビット(qubit 0)のみエラーが起きているのが確認できます.
そこで,エラーを訂正してみましょう.

circ = QuantumCircuit(3, 2)

# 論理Xゲート
circ.x([0, 1, 2])

# ビット反転エラー
circ.x([0])

# 誤り検出
circ.cx(1, 0)
circ.measure(0, 0)
circ.cx(1, 0)

circ.cx(2, 1)
circ.measure(1, 1)
circ.cx(2, 1)

# 古典レジスタの値を確認するために一旦シミュレート
simulator = Aer.get_backend('statevector_simulator')
result = execute(circ, simulator).result()
result.get_counts()

c0_result = int(list(result.get_counts().keys())[0][1])
c1_result = int(list(result.get_counts().keys())[0][0])

# 状態回復
if c0_result == 1 and c1_result == 0:
    circ.x(0)
elif c0_result == 1 and c1_result == 1:
    circ.x(1)
elif c0_result == 0 and c1_result == 1:
    circ.x(2)

# 最終状態確認
mulator = Aer.get_backend('statevector_simulator')
result = execute(circ, simulator).result()
state = result.get_statevector()
plot_bloch_multivector(state)

bitflip_2.png

確かにエラーが訂正されています.

次は,もう少し複雑な回路を実行し,無事復号できるかどうか試してみましょう.論理$RX$ゲートや論理$RZ$ゲートは$RXXX$や$RZZZ$によって与えられることに注意すると2,論理$RX$ゲートや論理$RZ$ゲートを適用した回路は以下のように表されます.

circ = QuantumCircuit(3, 2)

# 論理RXゲート
circ.h([0, 1, 2])
circ.cx(0, 1)
circ.rzz(np.pi / 4, 1, 2)
circ.cx(0, 1)
circ.h([0, 1, 2])

# 論理RZゲート
circ.cx(0, 1)
circ.rzz(3 * np.pi / 4, 1, 2)
circ.cx(0, 1)

# 論理RXゲート
circ.h([0, 1, 2])
circ.cx(0, 1)
circ.rzz(np.pi / 4, 1, 2)
circ.cx(0, 1)
circ.h([0, 1, 2])

# ビット反転エラー
circ.x([0])

simulator = Aer.get_backend('statevector_simulator')
result = execute(circ, simulator).result()
state = result.get_statevector()
plot_bloch_multivector(state)

bitflip_3.png

最後にエラーを乗せたため,一つ目の量子ビットのみ反転していることが分かります.ここで,それぞれの量子ビットが$\ket{0},\ket{1}$の確率混合として表されているのは,

$$
\begin{align}
\operatorname{Tr}_ {23}\ket{\psi_L}\bra{\psi_L}&=|a|^2\ket{0}\bra{0}+|b|^2\ket{1}\bra{1}\\
\operatorname{Tr}_ {23}\ket{\psi_L'}\bra{\psi_L'}&=|a|^2\ket{1}\bra{1}+|b|^2\ket{0}\bra{0}
\end{align}
$$

より理解できます.このエラーを修正し,さらに復号までしてみましょう.

circ = QuantumCircuit(3, 2)

# 論理RXゲート
circ.h([0, 1, 2])
circ.cx(0, 1)
circ.rzz(np.pi / 4, 1, 2)
circ.cx(0, 1)
circ.h([0, 1, 2])

# 論理RZゲート
circ.cx(0, 1)
circ.rzz(3 * np.pi / 4, 1, 2)
circ.cx(0, 1)

# 論理RXゲート
circ.h([0, 1, 2])
circ.cx(0, 1)
circ.rzz(np.pi / 4, 1, 2)
circ.cx(0, 1)
circ.h([0, 1, 2])

# ビット反転エラー
circ.x([0])

# 誤り検出
circ.cx(1, 0)
circ.measure(0, 0)
circ.cx(1, 0)

circ.cx(2, 1)
circ.measure(1, 1)
circ.cx(2, 1)

# 古典レジスタの値を確認するために一旦シミュレート
simulator = Aer.get_backend('statevector_simulator')
result = execute(circ, simulator).result()
result.get_counts()

c0_result = int(list(result.get_counts().keys())[0][1])
c1_result = int(list(result.get_counts().keys())[0][0])

# 状態回復
if c0_result == 1 and c1_result == 0:
    circ.x(0)
elif c0_result == 1 and c1_result == 1:
    circ.x(1)
elif c0_result == 0 and c1_result == 1:
    circ.x(2)

# 復号
circ.cx(0, 1)
circ.cx(0, 2)

# 最終状態確認
mulator = Aer.get_backend('statevector_simulator')
result = execute(circ, simulator).result()
state = result.get_statevector()
plot_bloch_multivector(state)

bitflip_4.png

二つ目と三つ目の量子ビットは初期化され,一つ目の量子ビットのみに情報が戻りました.これが合っているのかどうか,エラーのない1量子ビットでの計算を行なってみると,

circ = QuantumCircuit(1)

# RXゲート
circ.rx(np.pi / 4, 0)

# RZゲート
circ.rz(3 * np.pi / 4, 0)

# RXゲート
circ.rx(np.pi / 4, 0)

# 最終状態確認
mulator = Aer.get_backend('statevector_simulator')
result = execute(circ, simulator).result()
state = result.get_statevector()
plot_bloch_multivector(state)

bitflip_5.png

確かに一致することが分かりました.

Version Information

Software Version
qiskit 0.45.0
System information
Python version 3.10.12
  1. Nielsen, Michael A., and Isaac L. Chuang. Quantum computation and quantum information. Cambridge university press, 2010.

  2. この実装については,以前の記事で紹介しました.

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?