Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

スタビライザ形式論と Steane 符号

タグ: algorithm error-correction

量子誤り訂正入門(前編)では、3量子ビット bit-flip / phase-flip 符号と Shor の9量子ビット符号を作り、最後に「パリティ演算子=スタビライザ」という名前を与えました。

後編では、このスタビライザを形式的に扱います。そして古典の Hamming 符号から系統的に量子符号を作る CSS 構成 と、その代表例 Steane 符号 を実装します。Steane 符号は、Shor 符号と同じ d=3d=3 を9量子ビットより少ない7量子ビットで実現します。

この記事で扱うこと:

  1. スタビライザ形式論 ― スタビライザ、生成子、シンドロームを正式に整理する。

  2. CSS 構成 ― 古典 Hamming 符号から Steane 符号のスタビライザを作る。

  3. Steane 符号の符号化・シンドローム測定・訂正を実装する。

  4. 7つの物理 Hadamard が論理 Hadamard になることを確かめる。

前提知識: 前編の内容(シンドローム測定、Pauli エラー、スタビライザという用語)。

# 最新のQamomileをpipからインストールします。
# !pip install qamomile
# # or
# !uv add qamomile

1. スタビライザ形式論

前編では、Z0Z1Z_0Z_1X0X1X_0X_1 のようなパリティ演算子を スタビライザ と呼びました。まずこの言葉を正式に整理します。

1.1 スタビライザとは

スタビライザ とは、符号空間のすべての状態を変えない Pauli 演算子 SS のことです。任意の符号語 ψ\lvert\psi\rangle に対して

Sψ=ψS\lvert\psi\rangle = \lvert\psi\rangle

が成り立ちます。「符号空間とは、スタビライザの固有値 +1 の固有空間である」と言い換えられます。

たとえば bit-flip 符号の符号語は 000\lvert000\rangle111\lvert111\rangle です。Z0Z1Z_0Z_1 をかけると、Z0Z1000=000Z_0Z_1\lvert000\rangle=\lvert000\rangleZ0Z1111=(1)(1)111=111Z_0Z_1\lvert111\rangle=(-1)(-1)\lvert111\rangle=\lvert111\rangle となり、どちらも変わりません。だから Z0Z1Z_0Z_1 は bit-flip 符号のスタビライザです。

1.2 生成子とシンドローム

1つの符号にはたくさんのスタビライザがありますが、すべてを並べる必要はありません。いくつかの 生成子(generator) を選べば、残りはその積で得られます。bit-flip 符号なら Z0Z1Z_0Z_1Z0Z2Z_0Z_2 の2つが生成子です。

スタビライザ生成子を測定すると、結果は固有値 +1-1 になります。

  • エラーがなければ、状態は符号空間にあり、すべての生成子が +1 を返します。

  • エラー EE が入ると、EE反交換する 生成子の測定値が -1 に変わります。

この ±1\pm1(ビットで表せば 0/10/1)のパターンが シンドローム です。前編で補助量子ビットに取り出していた値は、まさにスタビライザ生成子の測定結果でした。どの生成子が -1 になったかを見れば、エラーの位置と種類が分かります。

1.3 前編の符号をスタビライザで見る

前編の3つの符号は、すべてスタビライザ生成子で記述できます。

符号スタビライザ生成子
3量子ビット bit-flipZ0Z1, Z0Z2Z_0Z_1,\ Z_0Z_2
3量子ビット phase-flipX0X1, X0X2X_0X_1,\ X_0X_2
Shor 9量子ビットZ0Z1, Z0Z2, Z3Z4, Z3Z5, Z6Z7, Z6Z8, X0X1X2X3X4X5, X3X4X5X6X7X8Z_0Z_1,\ Z_0Z_2,\ Z_3Z_4,\ Z_3Z_5,\ Z_6Z_7,\ Z_6Z_8,\ X_0X_1X_2X_3X_4X_5,\ X_3X_4X_5X_6X_7X_8

bit-flip 符号は ZZ 型の生成子だけ、phase-flip 符号は XX 型の生成子だけを持ちます。Shor 符号は両方を持ち、ZZ 型が XX エラーを、XX 型が ZZ エラーを検出します。この「XX 型と ZZ 型を別々に持つ」構造が、次節の CSS 構成につながります。

1.4 [[n,k,d]][[n,k,d]] と符号距離

スタビライザ符号は [[n,k,d]][[n,k,d]] という3つの数で特徴づけられます。

  • nn:物理量子ビット数。

  • kk:守る論理量子ビット数。生成子1つにつき自由度が1つ減るので、k=n(生成子の数)k = n - (\text{生成子の数}) です。

  • dd:符号距離。スタビライザすべてと交換するが、それ自身はスタビライザでない Pauli 演算子 ― これを 論理演算子 と呼びます ― のうち、最小の重み(かかる量子ビット数)です。

距離 dd の符号は (d1)/2\lfloor(d-1)/2\rfloor 個までのエラーを訂正できます。任意の単一量子ビットエラーを訂正するには d3d\ge3 が必要です。前編の3量子ビット符号は d=1d=1、Shor 符号は d=3d=3 でした。これから作る Steane 符号は [[7,1,3]][[7,1,3]] です。

2. 古典 Hamming 符号から CSS 符号へ

CSS 符号(Calderbank-Shor-Steane 符号) は、古典の誤り訂正符号から量子符号を系統的に作る方法です。Steane 符号はその代表例で、古典の Hamming 符号をもとにします。

2.1 古典 Hamming [7,4,3] 符号

古典の Hamming [7,4,3] 符号は、7ビットで4ビットの情報を守り、1ビットの誤りを訂正できる古典符号です。この符号は パリティ検査行列 HH で定義されます。

H=(000111101100111010101)H = \begin{pmatrix} 0 & 0 & 0 & 1 & 1 & 1 & 1 \\ 0 & 1 & 1 & 0 & 0 & 1 & 1 \\ 1 & 0 & 1 & 0 & 1 & 0 & 1 \end{pmatrix}

7ビットの語 cc が符号語であることは、Hc=0Hc=0(各行とのパリティがすべて偶数)と同値です。

2.2 列が誤り位置を指す仕掛け

HH の列をよく見ると、第 jj 列は数 j+1j+1 の2進表現になっています(第0列は 001、第1列は 010、…、第6列は 111)。

1ビットの誤りが位置 jj に入ると、HcHc はちょうど第 jj 列に等しくなります。つまり HcHc の3ビットを2進数として読めば、それがそのまま誤りの位置を指します。古典 Hamming 符号は、この仕掛けで誤り位置を一発で特定します。

2.3 CSS 構成:XX 型と ZZ 型のスタビライザ

CSS 構成は、このパリティ検査行列 HH から2種類のスタビライザを作ります。

  • ZZ 型スタビライザ:HH の各行を、1 の立つ位置に ZZ を置いた演算子に読み替える。XX エラーを検出する。

  • XX 型スタビライザ:同じ各行を、XX を置いた演算子に読み替える。ZZ エラーを検出する。

XX エラーは ZZ 型スタビライザと反交換し、ZZ エラーは XX 型スタビライザと反交換します。だから ZZ 型で XX エラーの位置を、XX 型で ZZ エラーの位置を、それぞれ古典 Hamming 符号と同じやり方で特定できます。前編の Shor 符号で見た「XXZZ を独立に直す」が、ここでは古典符号から自動的に出てきます。

2.4 Steane 符号の6つの生成子

Hamming 行列 HH の3つの行から、ZZ 型・XX 型それぞれ3つ、合わせて6つのスタビライザ生成子が得られます。

スタビライザ検出するエラー
XXX3X4X5X6X_3X_4X_5X_6ZZ
XXX1X2X5X6X_1X_2X_5X_6ZZ
XXX0X2X4X6X_0X_2X_4X_6ZZ
ZZZ3Z4Z5Z6Z_3Z_4Z_5Z_6XX
ZZZ1Z2Z5Z6Z_1Z_2Z_5Z_6XX
ZZZ0Z2Z4Z6Z_0Z_2Z_4Z_6XX

物理量子ビット7個、生成子6個なので、守れる論理量子ビットは 76=17-6=1 個。これが Steane [[7,1,3]][[7,1,3]] 符号です。

実装に入る前に、QamomileとQiskit連携を読み込み、補助関数を用意します。_bits7 / _passes_hamming_checks / _is_steane_zero_wordは、測定結果がHamming符号語や0L\lvert0_L\rangleの符号語かを判定するユーティリティです。QECの本筋ではないので、読み飛ばして構いません。

import qamomile.circuit as qmc
from qamomile.qiskit import QiskitTranspiler

transpiler = QiskitTranspiler()

# ドキュメントの出力を再現可能にするため、シード付きのバックエンドを用意します。
from qiskit_aer import AerSimulator

_seeded_backend = AerSimulator(seed_simulator=42, max_parallel_threads=1)
_seeded_executor = transpiler.executor(backend=_seeded_backend)


def _bits7(outcome) -> list[int]:
    """測定結果を、量子ビット番号順の7ビットのリストで返す。"""
    if isinstance(outcome, (list, tuple)):
        return list(outcome)
    return [(outcome >> i) & 1 for i in range(7)]


def _passes_hamming_checks(outcome) -> bool:
    """測定結果が Hamming [7,4,3] 符号語(3つのパリティ検査をすべて満たす)かを返す。"""
    bits = _bits7(outcome)
    h_checks = [
        bits[3] ^ bits[4] ^ bits[5] ^ bits[6],
        bits[1] ^ bits[2] ^ bits[5] ^ bits[6],
        bits[0] ^ bits[2] ^ bits[4] ^ bits[6],
    ]
    return all(check == 0 for check in h_checks)


def _is_steane_zero_word(outcome) -> bool:
    """測定結果が |0_L> の符号語(偶数重みの Hamming 符号語)かを返す。"""
    return _passes_hamming_checks(outcome) and sum(_bits7(outcome)) % 2 == 0

3. 論理 0L\lvert0_L\rangle の符号化

3.1 論理ゼロは偶数重み Hamming 符号語の重ね合わせ

Steane 符号の論理 0L\lvert0_L\rangle は、重みが偶数の Hamming 符号語をすべて足し合わせた重ね合わせです。

0L=122cC, w(c)は偶数c\lvert0_L\rangle = \frac{1}{2\sqrt2}\sum_{c\in C,\ w(c)\,\text{は偶数}} \lvert c\rangle

ここで CC は Hamming 符号語の集合、w(c)w(c) はその重みです。偶数重みの Hamming 符号語は8個あるので、8項の重ね合わせになります。

3.2 符号化回路

次の回路は、07\lvert0\rangle^{\otimes7} から 0L\lvert0_L\rangle を作ります。3つの XX 型スタビライザのパターン(X3X4X5X6X_3X_4X_5X_6X1X2X5X6X_1X_2X_5X_6X0X2X4X6X_0X_2X_4X_6)を、Hadamard と CNOT で順に書き込みます。

@qmc.qkernel
def encode_steane_zero(data: qmc.Vector[qmc.Qubit]) -> qmc.Vector[qmc.Qubit]:
    # X3 X4 X5 X6 のパターンを書き込む。
    data[3] = qmc.h(data[3])
    data[3], data[4] = qmc.cx(data[3], data[4])
    data[3], data[5] = qmc.cx(data[3], data[5])
    data[3], data[6] = qmc.cx(data[3], data[6])

    # X1 X2 X5 X6 のパターンを書き込む。
    data[1] = qmc.h(data[1])
    data[1], data[2] = qmc.cx(data[1], data[2])
    data[1], data[5] = qmc.cx(data[1], data[5])
    data[1], data[6] = qmc.cx(data[1], data[6])

    # X0 X2 X4 X6 のパターンを書き込む。
    data[0] = qmc.h(data[0])
    data[0], data[2] = qmc.cx(data[0], data[2])
    data[0], data[4] = qmc.cx(data[0], data[4])
    data[0], data[6] = qmc.cx(data[0], data[6])
    return data
@qmc.qkernel
def encode_zero_and_measure() -> qmc.Vector[qmc.Bit]:
    data = qmc.qubit_array(7, name="data")
    data = encode_steane_zero(data)
    return qmc.measure(data)

3.3 符号化の確認

符号化器の出力を測定し、観測されるビット列がすべて偶数重みの Hamming 符号語(0L\lvert0_L\rangle の符号語)になっているかを確かめます。

print("Encode and measure |0_L>")
exe = transpiler.transpile(encode_zero_and_measure)
result = exe.sample(_seeded_executor, shots=1024).result()
total = sum(count for _, count in result.results)
valid = sum(count for outcome, count in result.results if _is_steane_zero_word(outcome))
print(f"  |0_L> codeword ratio: {valid / total:.3f}")
print(f"  distinct codewords observed: {len(result.results)}")
# Steane の |0_L> はちょうど 8 つの偶重み Hamming 符号語の均等重ね合わせなので、
# 各ショットは有効な |0_L> 符号語を返し、分布は高々その 8 通りに収まる。
assert total == 1024
assert valid == total
assert len(result.results) <= 8
Encode and measure |0_L>
  |0_L> codeword ratio: 1.000
  distinct codewords observed: 8

4. シンドローム測定と訂正

4.1 XXZZ を独立にデコードする

CSS 符号の利点は、XX エラーと ZZ エラーを完全に独立に扱えることです。

  • ZZ 型スタビライザを測ると、XX エラーのシンドロームが得られる。

  • XX 型スタビライザを測ると、ZZ エラーのシンドロームが得られる。

それぞれ3ビットのシンドロームで、2.2 で見た Hamming 行列の列の仕掛けがそのまま使えます。

4.2 シンドローム表

3ビットのシンドローム (s2,s1,s0)(s_2,s_1,s_0) は、エラーの位置をそのまま2進数で表します。

エラー位置シンドローム (s2,s1,s0)(s_2,s_1,s_0)
なし(0,0,0)(0,0,0)
q0q_0(0,0,1)(0,0,1)
q1q_1(0,1,0)(0,1,0)
q2q_2(0,1,1)(0,1,1)
q3q_3(1,0,0)(1,0,0)
q4q_4(1,0,1)(1,0,1)
q5q_5(1,1,0)(1,1,0)
q6q_6(1,1,1)(1,1,1)

ZZ 型スタビライザから得た (s2,s1,s0)(s_2,s_1,s_0)XX エラーの位置を、XX 型から得たものは ZZ エラーの位置を指します。

4.3 実装

error_type1=X2=Y3=Zerror_pos はエラーを入れる量子ビット番号 0..6 です。

@qmc.qkernel
def steane_run(
    error_type: qmc.UInt,
    error_pos: qmc.UInt,
) -> qmc.Vector[qmc.Bit]:
    # データ用に7量子ビット、シンドローム測定用に補助量子ビットを6つ確保する。
    data = qmc.qubit_array(7, name="data")
    anc = qmc.qubit_array(6, name="anc")

    # |0_L> に符号化する。
    data = encode_steane_zero(data)

    # error_type / error_pos で指定した X / Y / Z エラーを注入する。
    for i in qmc.range(7):
        if (error_type == 1) & (error_pos == i):  # 1: X エラー
            data[i] = qmc.x(data[i])
        if (error_type == 2) & (error_pos == i):  # 2: Y エラー
            data[i] = qmc.y(data[i])
        if (error_type == 3) & (error_pos == i):  # 3: Z エラー
            data[i] = qmc.z(data[i])

    # Z 型スタビライザ: X エラーのシンドローム (sx_2, sx_1, sx_0) を測る。
    data[3], anc[0] = qmc.cx(data[3], anc[0])
    data[4], anc[0] = qmc.cx(data[4], anc[0])
    data[5], anc[0] = qmc.cx(data[5], anc[0])
    data[6], anc[0] = qmc.cx(data[6], anc[0])
    sx_2 = qmc.measure(anc[0])

    data[1], anc[1] = qmc.cx(data[1], anc[1])
    data[2], anc[1] = qmc.cx(data[2], anc[1])
    data[5], anc[1] = qmc.cx(data[5], anc[1])
    data[6], anc[1] = qmc.cx(data[6], anc[1])
    sx_1 = qmc.measure(anc[1])

    data[0], anc[2] = qmc.cx(data[0], anc[2])
    data[2], anc[2] = qmc.cx(data[2], anc[2])
    data[4], anc[2] = qmc.cx(data[4], anc[2])
    data[6], anc[2] = qmc.cx(data[6], anc[2])
    sx_0 = qmc.measure(anc[2])

    # X 型スタビライザ: Z エラーのシンドローム (sz_2, sz_1, sz_0) を測る。
    anc[3] = qmc.h(anc[3])
    anc[3], data[3] = qmc.cx(anc[3], data[3])
    anc[3], data[4] = qmc.cx(anc[3], data[4])
    anc[3], data[5] = qmc.cx(anc[3], data[5])
    anc[3], data[6] = qmc.cx(anc[3], data[6])
    anc[3] = qmc.h(anc[3])
    sz_2 = qmc.measure(anc[3])

    anc[4] = qmc.h(anc[4])
    anc[4], data[1] = qmc.cx(anc[4], data[1])
    anc[4], data[2] = qmc.cx(anc[4], data[2])
    anc[4], data[5] = qmc.cx(anc[4], data[5])
    anc[4], data[6] = qmc.cx(anc[4], data[6])
    anc[4] = qmc.h(anc[4])
    sz_1 = qmc.measure(anc[4])

    anc[5] = qmc.h(anc[5])
    anc[5], data[0] = qmc.cx(anc[5], data[0])
    anc[5], data[2] = qmc.cx(anc[5], data[2])
    anc[5], data[4] = qmc.cx(anc[5], data[4])
    anc[5], data[6] = qmc.cx(anc[5], data[6])
    anc[5] = qmc.h(anc[5])
    sz_0 = qmc.measure(anc[5])

    # X 成分の訂正: シンドローム (sx_2, sx_1, sx_0) が指す位置に X をかける。
    if (~sx_2) & (~sx_1) & sx_0:
        data[0] = qmc.x(data[0])
    if (~sx_2) & sx_1 & (~sx_0):
        data[1] = qmc.x(data[1])
    if (~sx_2) & sx_1 & sx_0:
        data[2] = qmc.x(data[2])
    if sx_2 & (~sx_1) & (~sx_0):
        data[3] = qmc.x(data[3])
    if sx_2 & (~sx_1) & sx_0:
        data[4] = qmc.x(data[4])
    if sx_2 & sx_1 & (~sx_0):
        data[5] = qmc.x(data[5])
    if sx_2 & sx_1 & sx_0:
        data[6] = qmc.x(data[6])

    # Z 成分の訂正: シンドローム (sz_2, sz_1, sz_0) が指す位置に Z をかける。
    if (~sz_2) & (~sz_1) & sz_0:
        data[0] = qmc.z(data[0])
    if (~sz_2) & sz_1 & (~sz_0):
        data[1] = qmc.z(data[1])
    if (~sz_2) & sz_1 & sz_0:
        data[2] = qmc.z(data[2])
    if sz_2 & (~sz_1) & (~sz_0):
        data[3] = qmc.z(data[3])
    if sz_2 & (~sz_1) & sz_0:
        data[4] = qmc.z(data[4])
    if sz_2 & sz_1 & (~sz_0):
        data[5] = qmc.z(data[5])
    if sz_2 & sz_1 & sz_0:
        data[6] = qmc.z(data[6])

    return qmc.measure(data)

4.4 検証:21通りの単一エラー

XXYYZZ を7つの量子ビットそれぞれに入れた、計21通りの単一エラーを試します。訂正後、測定されるビット列はすべて 0L\lvert0_L\rangle の符号語に戻るはずです。

print("Steane code: correct X/Y/Z on all 7 locations")
print(f"  {'err':4s} | {'pos':5s} | |0_L> codeword")
print(f"  {'-' * 4}-+-{'-' * 5}-+-{'-' * 14}")
for name, error_type in [("X", 1), ("Y", 2), ("Z", 3)]:
    for pos in range(7):
        exe = transpiler.transpile(
            steane_run,
            bindings={"error_type": error_type, "error_pos": pos},
        )
        result = exe.sample(_seeded_executor, shots=128).result()
        total = sum(count for _, count in result.results)
        valid = sum(
            count for outcome, count in result.results if _is_steane_zero_word(outcome)
        )
        print(f"  {name:4s} | q[{pos}]  | {valid / total:.3f}")
        # Steane は 7 量子ビット上の任意の単一 Pauli エラーを訂正するので、
        # 訂正後の状態は完全に |0_L> 符号空間に収まり、各ショットは有効な
        # 符号語を返す。
        assert total == 128
        assert valid == total
Steane code: correct X/Y/Z on all 7 locations
  err  | pos   | |0_L> codeword
  -----+-------+---------------
  X    | q[0]  | 1.000
  X    | q[1]  | 1.000
  X    | q[2]  | 1.000
  X    | q[3]  | 1.000
  X    | q[4]  | 1.000
  X    | q[5]  | 1.000
  X    | q[6]  | 1.000
  Y    | q[0]  | 1.000
  Y    | q[1]  | 1.000
  Y    | q[2]  | 1.000
  Y    | q[3]  | 1.000
  Y    | q[4]  | 1.000
  Y    | q[5]  | 1.000
  Y    | q[6]  | 1.000
  Z    | q[0]  | 1.000
  Z    | q[1]  | 1.000
  Z    | q[2]  | 1.000
  Z    | q[3]  | 1.000
  Z    | q[4]  | 1.000
  Z    | q[5]  | 1.000
  Z    | q[6]  | 1.000

比率が 1.000 なら、その単一 Pauli エラーに対して状態が 0L\lvert0_L\rangle の符号空間に戻ったことを意味します。Y=iXZY=iXZ のエラーは XX 成分と ZZ 成分の訂正の両方を引き起こしますが、CSS 符号ではこの2つが独立なので、そのまま訂正できます。

5. Transversal な Hadamard ゲート

5.1 transversal ゲートとは

論理量子ビットに論理ゲートをかけるとき、各物理量子ビットに それぞれ独立に 物理ゲートをかけるだけで済む場合、そのゲートを transversal(横断的) と言います。

transversal なゲートはフォールトトレラント量子計算で重要です。物理ゲートが互いに独立なので、1つの物理ゲートが故障しても、その誤りが1ブロック内の複数の量子ビットに広がりません。

Steane 符号の大きな特徴は、論理 Hadamard Hˉ\bar H が transversal なことです。7つの物理量子ビットそれぞれに HH をかけるだけで、論理 Hadamard になります。

Hˉ=H7\bar H = H^{\otimes 7}

これは、Steane 符号の XX 型と ZZ 型のスタビライザが同じ Hamming パターンを持つ ― CSS 構成で同じ行列 HH を使った ― ことの帰結です。

5.2 論理 Hadamard を確かめる

Hˉ=H7\bar H = H^{\otimes7} であることを、2つの性質で確かめます。

性質1: Hˉ\bar H0L\lvert0_L\rangle+L\lvert+_L\rangle に移す。 +L\lvert+_L\rangle0L\lvert0_L\rangle1L\lvert1_L\rangle の重ね合わせです。0L\lvert0_L\rangle が偶数重みの Hamming 符号語からなるのに対し、1L\lvert1_L\rangle は奇数重みの Hamming 符号語からなります。したがって +L\lvert+_L\rangle を測ると、偶数・奇数いずれの重みの Hamming 符号語も現れます(0L\lvert0_L\rangle なら偶数重みのみ)。

@qmc.qkernel
def transversal_h_to_plus() -> qmc.Vector[qmc.Bit]:
    data = qmc.qubit_array(7, name="data")
    data = encode_steane_zero(data)
    # transversal Hadamard を1回かける: |0_L> -> |+_L>。
    data = qmc.h(data)
    return qmc.measure(data)
print("Transversal H: |0_L> -> |+_L>")
exe = transpiler.transpile(transversal_h_to_plus)
result = exe.sample(_seeded_executor, shots=1024).result()
total = sum(count for _, count in result.results)
hamming = sum(
    count for outcome, count in result.results if _passes_hamming_checks(outcome)
)
odd = sum(count for outcome, count in result.results if sum(_bits7(outcome)) % 2 == 1)
print(f"  Hamming codeword ratio: {hamming / total:.3f}")
print(f"  odd-weight (|1_L>) fraction: {odd / total:.3f}")
# |+_L> は 16 個の Hamming 符号語の均等重ね合わせ。各ショットは何らかの
# Hamming 符号語を返し、奇重み (|1_L>) 半分は約半数 — 1024 ショットの
# 標準誤差 < 0.02 に対して 5% の窓は安全側。
assert total == 1024
assert hamming == total
assert len(result.results) <= 16
assert abs(odd / total - 0.5) < 0.05
Transversal H: |0_L> -> |+_L>
  Hamming codeword ratio: 1.000
  odd-weight (|1_L>) fraction: 0.518

Hamming 符号語の比率は 1.000 で、奇数重みの符号語も約半分現れます。偶数重みしか出ない 0L\lvert0_L\rangle とは異なる ― 状態が +L\lvert+_L\rangle に移っていることが確認できます。

性質2: Hˉ\bar H を2回かけると恒等変換に戻る(Hˉ2=I\bar H^2 = I)。 0L\lvert0_L\rangleH7H^{\otimes7} を2回かけると、0L\lvert0_L\rangle に戻るはずです。

@qmc.qkernel
def transversal_h_round_trip() -> qmc.Vector[qmc.Bit]:
    data = qmc.qubit_array(7, name="data")
    data = encode_steane_zero(data)
    # transversal Hadamard を2回かける: H^2 = I なので |0_L> に戻る。
    data = qmc.h(data)
    data = qmc.h(data)
    return qmc.measure(data)
print("Transversal H round trip: |0_L> -> H -> H -> |0_L>")
exe = transpiler.transpile(transversal_h_round_trip)
result = exe.sample(_seeded_executor, shots=1024).result()
total = sum(count for _, count in result.results)
valid = sum(count for outcome, count in result.results if _is_steane_zero_word(outcome))
print(f"  |0_L> codeword ratio: {valid / total:.3f}")
# H^2 = I なので往復で |0_L> がそのまま復元される — 各ショットは |0_L> 符号語。
assert total == 1024
assert valid == total
Transversal H round trip: |0_L> -> H -> H -> |0_L>
  |0_L> codeword ratio: 1.000

比率は 1.000 ― 2回の transversal Hadamard で 0L\lvert0_L\rangle に戻りました。7つの物理 HH が、確かに論理 Hˉ\bar H として働いています。

6. まとめ

この記事では、スタビライザ形式論と Steane [[7,1,3]][[7,1,3]] 符号を扱いました。

  • スタビライザ形式論 ― 符号空間を Pauli 演算子(スタビライザ)の +1 固有空間として捉え、生成子の測定値としてシンドロームを定義した。

  • CSS 構成 ― 古典 Hamming [7,4,3] 符号のパリティ検査行列から、XX 型と ZZ 型のスタビライザを作った。

  • Steane 符号0L\lvert0_L\rangle の符号化、6つのスタビライザによるシンドローム測定、21通りの単一 Pauli エラーの訂正を実装した。

  • transversal な Hadamard ― 7つの物理 HH が論理 Hˉ\bar H になることを確かめた。

Steane 符号は、Shor 符号と同じ d=3d=3 を9量子ビットより少ない7量子ビットで実現し、CSS 構成と transversal な Clifford ゲートのきれいな例になっています。

その先

  • 表面符号(surface code) ― スタビライザを2次元格子上の局所的な演算子にした符号。現在の超伝導量子コンピュータでの誤り訂正の主役で、シンドローム測定を繰り返し行う点が新しい。

  • フォールトトレラント量子計算 ― 符号化したまま論理ゲートを実行し、誤りを増幅させずに計算を進める枠組み。transversal なゲートはその出発点です。