9-2. 量子エラー

量子ビットに生じるエラーの根本的な要因自体は実は古典ビットとそれほど違いはない。

一つは、外部との環境の相互作用によって一定のレートで外部に情報が漏れ出てしまうことで生じるエラーである。 特に物質を量子ビットとして光やマイクロ波などの電磁波で情報を読み書きする場合、電磁波を注入する経路を確保せねばならず、そこから一定量の情報が漏れ出てしまう。 また、希釈冷凍機で実験をしていてもマイクロ波はエネルギースケールが環境温度と近いため、熱雑音の影響を大きく受けてしまい、これも定常的なノイズの原因となる。 一方、イオンや中性原子のようなトラップを用いて作成する物質の場合、デコヒーレンスに加えて物質がトラップから外れてしまうエラーも問題となる。 光を量子ビットとして用いた場合は、温度スケールの違いから上記のようなデコヒーレンスは問題とならない。 ただし、光量子ビットをスケールする上ではファイバによる誘導かミラー反射によるパスの確保が必要となり、またレーザの揺らぎの時間単位があるため、 これにともなう光子の損失やコヒーレンス時間の経過によるコヒーレンスの消失が実効的には何もしないでも生じる量子ビットの寿命となる。

もう一つは、デバイスに対する操作に際して生じるノイズである。 超伝導量子ビットなどのマイクロ波を用いる操作の場合、操作に際するエラーはマイクロ波の周波数やパワーの揺らぎによるノイズ、また意図しない相互作用項に寄与によるバイアスなどが挙げられる。 こうしたノイズは反転などにより打ち消しが可能なバイアスノイズとそうでない確率的なノイズに分けられる。また、ノイズの結果も最終的に0,1のどちらかに移動してしまうノイズと、0,1のどちらでもない状態に移動してしまうleakageに分けられる。確率的なノイズやleakageのようなノイズはエラー訂正の観点からするとより深刻である。 こうしたエラーは原理的にはパルスのキャリブレーションやキャンセルパルスの印加で回避可能だが、膨大なビットのキャリブレーションは原則的に困難を極める。イオンや中性原子の場合も事情は同様である。 光を量子ビットとして用いる場合、光学結晶との相互作用を介して操作を行うが、この場合物質とはまた異なった問題が生じる。 第一に、光量子ビットをユニバーサルに操作するには3次以上の非線形性を有する相互作用が必須であり、こうした高次非線形性は強い吸収をともなうものが多い。 このため、非破壊に3次非線形操作を行うのをあきらめ、光量子ビットのエンタングルメントを用いて破壊を伴う光子測定とフィードバックで所望の操作をゲートテレポーテーションという形で非線形操作を実現することが多い。こうした操作は単なる非破壊操作に比べ大きなオーバーヘッドを伴う。 また、電磁波を用いた操作は「量子ビットは動かず電磁波が時間とともに送出される」ため、システムのサイズは計算を行う時間に依存しないが、 光量子ビットを用いた通常の実験は物質を固定して光学定盤に配置するために、使用する光学機器の数は計算時間に依存して増えてしまう。 これを回避するためには、一度使用した光学装置をループを介して再度利用する時間多重化が必要となる。こうした光学系の時間多重化は現在盛んに研究がおこなわれている。

量子エラー訂正

量子ビットは通常のビットを拡張し、状態の重ね合わせを許したものである。量子エラー訂正では量子ビットを冗長化し、古典誤り訂正と同様に実行的に小さなエラー確率の獲得を目指す。この際、量子ビットの性質から多くの古典符号と事情の異なる点がいくつかあり、そのほとんどが量子誤り訂正を困難にする方向に働く。

現代提案されている量子エラー訂正の手法は、原則として線形符号を量子版へと拡張したものである。確認のため、線形符号の枠組みを再掲する。

  • 符号:生成行列\(G\)と検査行列\(H_c\)で特徴づけられる。これらは\(GH_c = 0\)を満たす。

  • 符号化前:\(k\)-bitの横ベクトルデータ \(v\) がある。

  • 符号化: \(k \times n\)の生成行列\(G\)を用いて、符号化した状態\(v'\)\(v'=vG\)となる。

  • エラー: 符号化後の\(v'\)にランダムに\(n\)-bit列 \(e\) が足される。

  • エラー検査: \(n \times (n-k)\)の検査行列\(H_c\)を用いてシンドローム\(s = e H_c\)が得られる。\(s \neq 0\)ならばエラーがあると検知できる。\(s=0\)ならば、エラーが生じていないか、検査できる限界以上のエラーが生じている。

  • エラー訂正: \((v'+e)\)の値に従って、\(k\)-bitの横ベクトルデータ \(v\) を推測する。\(v'+e\)から\(v\)を求めるアルゴリズムを復号アルゴリズムと呼ぶ。

  • 距離: \(e H_c = 0\)となる\(e\)の最小weightが符号の距離\(d\)である。

量子ビットは\(|0\rangle, |1\rangle\)の二状態のみを用い、重ね合わせを利用しない場合、古典ビットの枠組みに乗せることができる。このことを利用し、上記を古典誤り訂正を量子情報の言葉で書きなおすと以下のようになる。

  • 符号:生成行列\(G\)と検査行列\(H_c\)で特徴づけられる。これらは\(GH_c = 0\)を満たす。

  • 符号化前:\(k\)-qubitの計算基底の量子状態 \(|v\rangle\) がある。

  • 符号化: CNOTとPauli-\(X\)のみからなる\(2^n \times 2^n\)のユニタリ行列\(U_G\)で、\(|vG\rangle = U_G (|\psi\rangle \otimes |0\rangle^{n-k})\)となるものを\(G\)から構成できる。

  • エラー: 符号化後の\(|vG\rangle\)にランダムなビットフリップ演算\(E = X_1^{e_1} X_2 ^{e_2} \ldots X_n^{e_n}\)が作用し、\(E |vG \rangle = |vG + e \rangle\)となる。

  • エラー検査: パリティが整合するかを調べる。これは以下の操作に等しい。検査行列\(H_c\)\(i\)列ベクトルを\(h_i\)とした時、パウリ行列\(P_i = Z_1^{h_{i1}} Z_2^{h_{i2}} \ldots Z_n^{h_{in}}\)というパウリ行列を考え、\(M_0^{(i)} = (I+P_i)/2, M_1^{(i)} = (I-P_i)/2\)というPOVM\(\{M_0^{(i)}, M_1^{(i)}\}\)を考える。状態\(E |\psi' \rangle\)\(n-k\)個のPOVM\(\{\{M_0^{(i)}, M_1^{(i)}\}\}_i\)で測定し、\(n-k\)個のビット\(s = (vG+e)H_c = eH_c\)を得る。この\(s\)が0であるかを調べる。

  • 復号: \(|vG+e'\rangle\)\(Z\)基底で全て測定すると、\(n\)ビット列\((vG+e)\)を得る。\((vG+e)\)の値に従って、\(R E|vG\rangle = |vG\rangle\)となるようなユニタリ操作\(R = X_1^{e'_1} X_2 ^{e'_2} \ldots X_n^{e'_n}\)を構成する。\((vG+e)\)から\(e'\)を求めるアルゴリズムを復号アルゴリズムと呼ぶ。

  • 距離: \(s = 0\)となる\(E\)の最小weightが符号の距離\(d\)である。

上記のプロトコルは、量子ビットで古典誤り訂正を行う枠組みであり、言っていることは等価である。 例として、1bitを3bitに増やして多数決を行う符号を示す。この符号の生成行列は

\[G =\left( \begin{matrix} 1 & 1 & 1 \end{matrix} \right)\]

であり、検査行列は

\[\begin{split}H_c = \left( \begin{matrix} 1 & 0 \\ 1 & 1 \\ 0 & 1 \end{matrix} \right)\end{split}\]

である。

[120]:
# set code info
repetition = 3
error_probability = 0.2
initial_bit = 1
[121]:
def show_quantum_state(state, eps = 1e-10, round_digit=3):
    vector = state.get_vector()
    state_str = ""
    qubit_count = int( np.log2(len(vector)+eps))
    binary_format = "{" + ":0{}b".format(qubit_count) + "}"
    for ind in range(len(vector)):
        if abs(vector[ind]) > 1e-10:
            if len(state_str) > 0:
                state_str += " + "
            state_str += ("{} |" + binary_format + ">").format(round(vector[ind],round_digit ),ind)
    print(state_str)

量子状態を確保し、初期状態を0-thビットに書き込む。

[122]:
import numpy as np
from qulacs import QuantumState
state = QuantumState(repetition)
state.set_computational_basis(initial_bit)
show_quantum_state(state)
(1+0j) |001>

符号化の回路を作成する。

[123]:
from qulacs import QuantumCircuit
encode_circuit = QuantumCircuit(repetition)
for ind in range(1,repetition):
    encode_circuit.add_CNOT_gate(0, ind)
encode_circuit.update_quantum_state(state)
show_quantum_state(state)
(1+0j) |111>

シンドローム値を測定する量子回路を作る。現地点では全てのシンドローム値は0である。

[124]:
from qulacs.gate import Instrument
from qulacs.gate import DenseMatrix

def parity_measure_gate(fst,scn,register_pos):
    parity_measure_matrix_0 = np.zeros( (4,4) )
    parity_measure_matrix_1 = np.zeros( (4,4) )
    parity_measure_matrix_0[0,0] = parity_measure_matrix_0[3,3] = 1
    parity_measure_matrix_1[1,1] = parity_measure_matrix_1[2,2] = 1
    parity_measure_0 = DenseMatrix([fst,scn],parity_measure_matrix_0)
    parity_measure_1 = DenseMatrix([fst,scn],parity_measure_matrix_1)
    parity_measure = Instrument([parity_measure_0, parity_measure_1],register_pos)
    return parity_measure

parity_measure_circuit = QuantumCircuit(repetition)
for ind in range(repetition-1):
    parity_measure_circuit.add_gate(parity_measure_gate(ind,ind+1,ind))

parity_measure_circuit.update_quantum_state(state)
show_quantum_state(state)
for ind in range(repetition-1):
    print("parity({},{}): {}".format(ind,ind+1,state.get_classical_value(ind)))
(1+0j) |111>
parity(0,1): 0
parity(1,2): 0

ランダムなデータ量子ビットに一つビットエラーを起こす。この時、ビットエラーが起きるとは意図しないPauli-\(X\)操作が入ってしまうことに相当する。量子状態は\(|vG+e\rangle\)となる。

[125]:
# invoke random error
from qulacs.gate import Pauli
def random_X_error(num_qubit, error_probability):
    error_array = []
    for _ in range(num_qubit):
        if np.random.rand() < error_probability:
            error_array.append(1)
        else:
            error_array.append(0)
    return Pauli(np.arange(num_qubit), error_array)
error_operator = random_X_error(repetition, error_probability)
error_operator.update_quantum_state(state)
show_quantum_state(state)
(1+0j) |011>

再度パリティ測定を回収する。

[126]:
parity_measure_circuit.update_quantum_state(state)
show_quantum_state(state)
for ind in range(repetition-1):
    print("parity({},{}): {}".format(ind,ind+1,state.get_classical_value(ind)))
(1+0j) |011>
parity(0,1): 0
parity(1,2): 1

復号を行う。

[135]:
# decoding process
def compute_recovery_operation(state, repetition):
    cand1 = []
    cand2 = []
    cand1.append(0)
    cand2.append(1)
    flag = True
    for ind in range(repetition-1):
        val = state.get_classical_value(ind)
        if val == 1:
            flag = not flag
        if flag:
            cand1.append(0)
            cand2.append(1)
        else:
            cand1.append(1)
            cand2.append(0)
    if np.sum(cand1) < np.sum(cand2):
        cand = cand1
    else:
        cand = cand2
    return Pauli(np.arange(repetition), cand)

recovery_operation = compute_recovery_operation(state,repetition)
recovery_operation.update_quantum_state(state)
show_quantum_state(state)
(1+0j) |111>

一連の操作は最初の量子状態が計算基底でなくても行うことができる。

[146]:
# set code info
repetition = 3
error_probability = 0.2
initial_bit = 1

from qulacs.gate import RandomUnitary
state = QuantumState(repetition)
state.set_computational_basis(0)
RandomUnitary([0]).update_quantum_state(state)
print("initial state")
show_quantum_state(state)

encode_circuit.update_quantum_state(state)
print("encode")
show_quantum_state(state)

error_operator = random_X_error(repetition, error_probability)
error_operator.update_quantum_state(state)
print("error")
show_quantum_state(state)

parity_measure_circuit.update_quantum_state(state)
print("parity measurement")
show_quantum_state(state)

recovery_operation = compute_recovery_operation(state,repetition)
recovery_operation.update_quantum_state(state)
print("recovery")
show_quantum_state(state)

initial state
(-0.111+0.987j) |000> + (0.053-0.101j) |001>
encode
(-0.111+0.987j) |000> + (0.053-0.101j) |111>
error
(-0.111+0.987j) |010> + (0.053-0.101j) |101>
parity measurement
(-0.111+0.987j) |010> + (0.053-0.101j) |101>
recovery
(-0.111+0.987j) |000> + (0.053-0.101j) |111>

量子誤り訂正とは、量子状態にビットフリップ(Pauli-\(X\))だけではなく、位相フリップ(Pauli-\(Z\))のエラーが起きても誤り訂正が可能な枠組ということができる。生じるエラーが確率的なパウリ演算子であると仮定した場合の、量子誤り訂正の枠組みは以下である。

  • 符号:\(n\)-qubitに作用する生成ユニタリ \(U_G\)\(n-k\)個のPOVMの集合\(\{(M_0^{(i)}, M_1^{(i)})\}_i\)で特徴づけられる。これらは任意の\(k\)-qubit状態 \(|\psi\rangle\) に対して、\(\langle \psi,0 | U_G^{\dagger} M_j^{(i)} U_G | \psi,0 \rangle\)を満たす。

  • 符号化前:\(k\)-qubitの計算基底の量子状態 \(|\psi\rangle\) がある。

  • 符号化: ユニタリ行列\(U_G\)で、\(|\psi' \rangle = U_G (|\psi\rangle \otimes |0\rangle^{n-k})\)を生成する。

  • エラー: 符号化後の\(|\psi' \rangle\)にランダムなパウリ演算\(E = X_1^{e_1} X_2 ^{e_2} \ldots X_n^{e_n} Z_1^{e_{n+1}} Z_2 ^{e_{n+2}} \ldots Z_n^{e_{2n}}\)が作用し、\(E |\psi' \rangle\)となる。

  • エラー検査: 状態\(E |\psi' \rangle\)\(n-k\)個のPOVM\(\{\{M_0^{(i)}, M_1^{(i)}\}\}_i\)で測定し、\(n-k\)個のビット\(s\)を得る。この\(s\)が0であるかを調べる。

  • 復号: \(s\)の値に従って、\(R E|vG\rangle = |vG\rangle\)となるようなユニタリ操作\(R = X_1^{e'_1} X_2 ^{e'_2} \ldots X_n^{e'_n} Z_1^{e'_{n+1}} Z_2 ^{e'_{n+2}} \ldots Z_n^{e'_{2n}}\)を構成する。\(s\)から\(R\)を求めるアルゴリズムを量子誤り訂正の復号アルゴリズムと呼ぶ。

  • 距離: \(s = 0\)となる\(E\)の最小weightが符号の距離\(d\)である。

古典誤り訂正から量子誤り訂正の枠組みを構成するにあたっての本質的な変更点がある。 量子誤り訂正では位相反転を生じるエラーが発生する。このため、エラーのパウリ演算子\(E\)はPauli-\(X\)のみでなく、Pauli-\(Z\)を含む形になっている。 この変更は、符号が位相反転に対しても誤り訂正が可能であることを要請する。符号はPauli-\(Z\)のエラーに対しても\(1\)より大きな距離を持たなければならない。古典符号を量子符号の枠組みに直したものはPauli-\(Z\)に対して\(d=1\)の符号である。また、一方の符号によるシンドローム測定が\(k\)-qubitの量子状態を破壊してはならない。このことは、いかなる符号化された状態も \((n-k)\) 個のPOVM測定では区別がつかないということに相当する。従って、全てのPOVMの要素は任意の\(k\)-qubitを符号化した状態 \(|\psi'\rangle = U_B (|\psi\rangle \otimes |0\rangle^{n-k})\) に対して\(\langle \psi' |M_j^{(i)} | \psi' \rangle\)\(|\psi'\rangle\)によらない値である必要がある。

後述のスタビライザー符号は上記を満たすような\(U_B\)とPOVMを与える枠組みである。

最後に、古典符号では復号にあたって全ての量子状態を\(Z\)基底で測定し\((vG+e)\)を得ることができたが、量子符号では量子状態の\(Z\)基底の測定は情報を内部に埋め込んだ情報を破壊するためこうした測定は行えない。また、訂正に用いる操作もPauli-\(X\)\(Z\)が混ざったパウリ演算子になっている。したがって、得られたシンドローム値\(s\)のみから復号操作\(R\)を推定するアルゴリズムも量子のための変更が必要になる。

スタビライザー符号

パリティ検査符号においてパリティを構成する行列\(B\)が与えられたように、量子符号においてはPOVMを構成するスタビライザー符号という枠組みがある。 スタビライザー符号とは、符号空間を直接指定する代わりにスタビライザー演算子という特定の性質を満たすパウリ演算子の集合を用いて符号空間を指定するものである。

スタビライザー演算子の生成元\(S\)は、以下のような性質を満たすパウリ演算子の集合\(S\)である。

  • \(S\)の各要素は全てに互いに可換である。すなわち、どのような\(s,s' \in S\)についても、\(ss' = s's\)である。

  • \(S\)の各要素は全て独立である。すなわち、どのような\(s \in S\)についても\(S\setminus \{s\}\)の集合から生成される群に\(s\)は含まれない。

  • \(S\)から生成される群に\(-I\)が含まれない。

上記が満たされるとき、スタビライザー演算子が指定する論理符号とは、以下のような性質を満たす量子状態の張る部分空間である。

  • \(|\psi\rangle\)は全ての\(s \in S\)について、固有値+1の固有状態である。

独立なスタビライザーの生成元が\(l\)個あるとき、このような量子状態の張る空間の次元は\(2^{n-l}\)次元である。 従って、\(n\)-qubitの空間に \(k\) 論理量子ビットの空間を構築したいとき、\(l = n-k\)である必要がある。

この符号において、シンドローム値は量子状態を\(s \in S\)で射影測定した場合の測定結果として与えられる。 今計算のために利用されているのは全てのスタビライザー演算子の+1固有空間であるので、スタビライザー演算子の射影測定は興味のある論理量子ビットを破壊することはない。 今、簡単のために、それぞれの量子ビットは確率的にパウリ\(X,Y,Z\)のどれかが起きると仮定する。パウリ\(Y\)\(X\)\(Z\)の積なので、パウリの\(X\)\(Z\)の両方が起きる場合とみなすことができる。

この時、\(i\)番目の量子ビットに\(X\)(\(Z\))エラーが起きているかどうかを、\(e^{(X)}_i\) (\(e^{(Z)}_i\))というバイナリで表すことにすると、 ある量子ビットを\(Z\)で測定するということは\(e^{(X)}_i\)を得ることに相当し、\(X\)で測定する行為は\(e^{(Z)}_i\)を得ることに相当する。 複数の量子ビットに作用するパウリ演算子で測定した場合、我々はそれぞれのビットの結果のxorを得ることになる。すなわち、 \(P=Z_2 X_3 Z_4 Z_5\)であれば、測定結果は\(\left(e^{(X)}_2 + e^{(Z)}_3 + e^{(X)}_4 + e^{(X)}_5\right) \bmod 2\)となる。

もし、\(P\)がパウリ\(Z\)の積のみで、量子ビットに作用するエラーがパウリ\(X\)によるビットフリップのみであれば、これはいくつかのビットのパリティを計算するパリティ検査と同義である。 従って、スタビライザー符号とはパリティ検査符号をパウリ\(Z\)に関するエラーに対しても適用できるようにした、一種の拡張であることがわかる。

あるスタビライザーの生成元が与えられたとき、その符号に対する距離は以下のように考えることができる。 まず、スタビライザー群に対する正規化群(Normalizer)を考える。正規化群とは以下のような性質を満たす群である。

\[C(S) = \{p \in P \mid \forall s \in S, sp = ps \}\]

この時、正規化群からグローバル位相の違いを無視してスタビライザー群を除いたものを論理演算子と呼ぶ。 論理演算子とは、シンドロームによってエラーを検知できないが、論理量子状態を変化させる演算の集合である。 従って論理演算子を実行するのに十分な量子ビットにエラーが起きてしまうと、我々には検知できないエラーが生じる。

あるパウリ演算子\(P\)が非自明に作用する(パウリ\(X,Y,Z\)のどれかが作用する)量子ビットの数を\(P\)のweight \(w(P)\)とする。 この時、符号の距離とは論理演算子の集合で最も小さなweight、すなわち

\[d := \min_{P \in C(S)\setminus \langle S \rangle} w(P)\]

である。

トポロジカル符号

トポロジカル符号はトポロジカルな物質の縮退した基底状態を論理量子ビットの空間として用いるというアイデアから生まれた符号である。実験の観点からはトポロジカル符号は下記の実験的によい性質満たすスタビライザー符号の一種である。

  • 検査行列\(H_c\)が疎行列である。こうした符号は低密度パリティ検査符号との類似性から性能が良いことが知られており、しかも個々のシンドロームは\(O(1)\)個のパリティとしてあらわされる。

  • 個々のシンドロームの検査対象のデータ量子ビットが空間的に\(O(1)\)の距離に固まっている。従って、隣接した量子ビットにしかCNOTがかけられない物理系でも効率的にシンドローム測定が可能である。

トポロジカル符号の中でも、二次元平面上に測定量子ビットも含め埋め込むことができる表面符号が実現に最も近いとされている。

トポロジカル符号は低密度パリティ検査符号の欠点も引き継いでおり、一般には復号はNP困難となる。現実にはエラーの性質にいくつかの近似を用いることで、効率的に復号アルゴリズムが解ける形にすることが多い。

スタビライザー符号上での計算

量子計算は操作に伴ってエラーが生じるため、計算の間は量子状態を符号化したまま演算を行う必要がある。この計算においても、古典の場合とは大きな違いがある。

パリティ検査符号では1論理ビットに対する反転操作は、少なくとも\(O(d)\)、高々\(O(n)\)ビットに対する反転操作に対応する。これは、明らかに\(O(n)\)回のビットフリップで実現可能であり、並列化すれば1stepで完了できる。

スタビライザー符号では、1論理ビットに対する量子操作は、同様に少なくとも\(O(d)\)量子ビットに対する何らかのユニタリ操作に対応付けられる。ここで、古典の場合と異なるのは、\(O(d)\)量子ビットのユニタリ操作を実現するには、一般に\(O(2^d)\)個のゲートが必要になることである。従って、量子符号上においては1論理ビットに対する操作すら一般に効率的に行うことはできず、誤り訂正操作が新たな誤りを生んでしまい符号上での計算がスケールしなくなってしまう。

一方、符号によっては1論理ビットの操作\(U\)に対応する\(O(d)\)量子ビットの量子操作\(U'\)\(O(1)\)ステップで完了できるような\(U\)が存在する。こうした符号上でも効率的に実現可能な論理ビットに対する操作をトランスバーサルな操作と呼ぶ。もし、トランスバーサルな操作がユニバーサルなゲートセットを構築すれば効率的に符号上でユニバーサルな量子計算が行えるが、残念ながらそのようなスタビライザー符号が無いことが証明されている。

従って、ユニバーサルな操作のために足りない操作を何らかの形で調達してやる必要がある。表面符号上は\(X, Y, Z, H\)などはトランスバーサルである。CNOTが可能かは非自明であるが、lattice surgeryなどで効率的に行えることが知られている。\(S\),\(T\)などがトランスバーサルではないため、別の符号で\(S\),\(T\)gate状態などを生成し、これをゲートテレポートする魔法状態蒸留という手法を用いることでユニバーサルなセットをそろえることができる。