今更学ぶ魔法状態蒸留
この記事の目的
2024年9月に魔法状態栽培 [1] が提案されて以降、魔法状態蒸留は魔法状態を生成する手続きとしては非効率的なものとなり、研究も下火になってきたように感じます。ただ、魔法状態栽培の基礎や量子誤り訂正符号の応用としての側面があるため、2026年現在でも勉強することに意味はあると思います。本稿では、魔法状態蒸留について数式レベルで理解するための最短経路をたどります。プログラミングによる理解の補助も行います。具体的には、Bravyi-Kitaev (2005) [2] の 5-to-1 魔法状態蒸留を数式で追える形に整理します。
背景
FTQC では非 Clifford 操作の実装が高コストです。このため、非 Clifford 資源としての魔法状態を用意し、Clifford 操作と Pauli 測定のみで目的のゲートを実現する枠組みが研究されてきました。たとえば、
魔法状態蒸留は、精度の低い魔法状態を複数用意して高精度な状態へと濃縮する手続きです。必要に応じてこれを反復し、十分に高い精度の魔法状態を得ます。
注意点
文献ごとに記号や定義が統一されていない点には注意が必要です。特に Bravyi-Kitaev では
記号と基本事項
まず Pauli 演算子を
魔法状態の定義
魔法状態
とすると、共役作用は
となります。
[[5,1,3]] perfect code
Bravyi-Kitaev の魔法状態蒸留は [[5,1,3]] perfect code [4] を利用した手続きです。まずはこの量子誤り訂正符号についていくつかの性質を証明します。
定義
スタビライザー生成元を
とします。
証明は次の通りです。
スタビライザー群の要素をすべて列挙してみましょう。
import itertools
from qiskit.quantum_info import Pauli
stabilizer_generators = ["XZZXI", "IXZZX", "XIXZZ", "ZXIXZ"]
stabilizer_group = []
for exps in itertools.product([0, 1], repeat=len(stabilizer_generators)):
pauli = Pauli("IIIII")
for gen, exp in zip(stabilizer_generators, exps):
if exp == 1:
pauli *= Pauli(gen)
stabilizer_group.append(pauli.to_label())
stabilizer_group = sorted(list(set(stabilizer_group)))
print("stabilizer group size:", len(stabilizer_group))
print("stabilizer group:")
for h in stabilizer_group:
print(h)
stabilizer group size: 16
stabilizer group:
IIIII
IXZZX
IYXXY
IZYYZ
XIXZZ
XXYIY
XYIYX
XZZXI
YIYXX
YXXYI
YYZIZ
YZIZY
ZIZYY
ZXIXZ
ZYYZI
ZZXIX
この出力から、非自明な 15 要素がすべて重み 4 であることが分かります。
性質1(パウリの transversality)
スタビライザー空間の基底を
性質2(K の transversality)
が成立します。これは transversal な定義から明らかです。このため、
性質3(射影と固有状態)
が成り立ちます。ここで
証明は
これにより、
証明は
パウリ文字列のうちトレースが非ゼロなのは恒等演算子のみであり、
これらを用いて命題を証明します。
よって
従って
魔法状態蒸留の手順
魔法状態蒸留は、精度の低い魔法状態を複数用意し、スタビライザー測定によってスタビライザー空間に射影することで精度を高める手続きです。
具体的には次のようにして蒸留を行います。
エラー解析
精度の低い魔法状態を
となります。スタビライザー測定でエラーが検出されなかった場合、測定後の状態は
このとき
となります。出力誤り率を
が得られます。したがって
成功確率は
と書けます。
魔法状態蒸留の閾値は

具体的な回路
魔法状態蒸留を回路として実装してみましょう。ここでは Qiskit の Clifford クラスを使って perfect code の encode/decode 回路を実装します。そのためにまず destabilizer を見つけます。実装は次のような流れで進めます。
- destabilizer を見つける
- encode/decode 回路を実装する
- 魔法状態蒸留を検証する
destabilizer を見つける
ここでは stabilizer を拡張し
D_5=XXXXX -
は可換D_1,D_2,D_3,D_4,D_5 -
はD_i と反可換で、S_i と可換S_j (j \neq i)
これを満たすパウリ文字列を全探索します。
destabilizer の探索コード
import itertools
from qiskit.quantum_info import Pauli
S = ["XZZXI", "IXZZX", "XIXZZ", "ZXIXZ"]
LOGICAL_Z = "ZZZZZ"
LOGICAL_X = "XXXXX"
def _commutes_all(labels):
objs = [Pauli(s) for s in labels]
for i in range(len(objs)):
for j in range(i + 1, len(objs)):
if not objs[i].commutes(objs[j]):
return False
return True
def _anticommutes_only_with(target, stabilizers, index):
for j, stab in enumerate(stabilizers):
if j == index:
if not target.anticommutes(stab):
return False
else:
if not target.commutes(stab):
return False
return True
def _find_good_destabilizer(destabilizers_candidates):
rets = []
for cand in destabilizers_candidates:
# すべての destabilizer が XXZZI の順番を入れ替えたものであるものを探す
x2z2i1 = True
for pauli in cand:
if not (
pauli.count("X") == 2
and pauli.count("Z") == 2
and pauli.count("I") == 1
):
x2z2i1 = False
break
if x2z2i1:
rets.append(cand)
return rets
def find_destabilizers():
paulis = ["I", "X", "Y", "Z"]
stabilizers = [Pauli(s) for s in S] + [Pauli(LOGICAL_Z)]
logical_x = Pauli(LOGICAL_X)
# destabilizer の候補を探す。
# candidates_by_idx[i] は s[i] と非可換で、ほかのスタビライザーと
# XXXXX とは可換なパウリ文字列のリストとなるよう計算する。
candidates_by_idx = {i: [] for i in range(4)}
for p in itertools.product(paulis, repeat=5):
label = "".join(p)
if label == "IIIII":
continue
pobj = Pauli(label)
if not pobj.commutes(logical_x):
continue
for idx in range(4):
if _anticommutes_only_with(pobj, stabilizers, idx):
candidates_by_idx[idx].append(label)
# destabilizer のすべての可能性を列挙し、 destabilizer が可換となる組み合わせを見つける
destabilizers_candidates = []
for d0 in candidates_by_idx[0]:
for d1 in candidates_by_idx[1]:
for d2 in candidates_by_idx[2]:
for d3 in candidates_by_idx[3]:
cand = [d0, d1, d2, d3]
if _commutes_all(cand + [LOGICAL_X]):
destabilizers_candidates.append(cand)
break
if len(destabilizers_candidates) == 0:
raise RuntimeError("No commuting destabilizer set found.")
return destabilizers_candidates
ds = find_destabilizers()
goods = _find_good_destabilizer(ds)
print("stabilizers:", S + [LOGICAL_Z])
print("Number of destabilizers candidates:", len(ds))
print("Number of good destabilizers:", len(goods))
print("destabilizers:", goods[0] + [LOGICAL_X])
実行結果は次のようになります。 destabilizer は 512 通りも選び方がありますが、今回はその中でも
stabilizers: ['XZZXI', 'IXZZX', 'XIXZZ', 'ZXIXZ', 'ZZZZZ']
Number of destabilizers candidates: 512
Number of good destabilizers: 1
destabilizers: ['ZXIZX', 'ZIXXZ', 'IZZXX', 'XZXZI', 'XXXXX']
encode/decode 回路を実装する
Qiskit の Clifford クラスを使って encode/decode 回路を実装します。decode 回路は encode の逆として実装できます。また、 encode 回路について以下を検証します。
- encode 前の補助量子ビットへの
演算子が、 encode 後にスタビライザーZ に map されるS_1,S_2,S_3,S_4 - encode 前の量子ビットへの
演算子が、 encode 後に論理演算子X,Y,Z に map されるXXXXX,YYYYY,ZZZZZ
encode/decode 回路の実装
from qiskit.quantum_info import Pauli, Clifford
S = ["XZZXI", "IXZZX", "XIXZZ", "ZXIXZ"]
D = ["ZXIZX", "ZIXXZ", "IZZXX", "XZXZI"]
LOGICAL_Z = "ZZZZZ"
LOGICAL_X = "XXXXX"
def build_encoder_decoder():
stabilizers = [
"+" + LOGICAL_Z,
"+" + S[3],
"+" + S[2],
"+" + S[1],
"+" + S[0],
]
destabilizers = [
"+" + LOGICAL_X,
"+" + D[3],
"+" + D[2],
"+" + D[1],
"+" + D[0],
]
encoder = Clifford.from_dict(
{"stabilizer": stabilizers, "destabilizer": destabilizers}
).to_circuit()
decoder = encoder.inverse()
return encoder, decoder
encoder, decoder = build_encoder_decoder()
print("encoder circuit:")
print(encoder)
print("decoder circuit:")
print(decoder)
##########################################
# encoder の検証
##########################################
mapping = {}
ancilla_z = ["ZIIII", "IZIII", "IIZII", "IIIZI"]
for i, lab in enumerate(ancilla_z):
out = Pauli(lab).evolve(encoder, frame="s").to_label()
mapping[f"Z on ancilla q{i}"] = out
mapping["logical Z"] = Pauli("IIIIZ").evolve(encoder, frame="s").to_label()
mapping["logical X"] = Pauli("IIIIX").evolve(encoder, frame="s").to_label()
mapping["logical Y"] = Pauli("IIIIY").evolve(encoder, frame="s").to_label()
for key, value in mapping.items():
print(f"{key} -> {value}")
実行結果は次のようになります。 encode/decode 回路の取得と encode 回路の検証ができました。
encoder circuit:
┌───┐ ┌───┐ ┌───┐
q_0: ──■──┤ X ├─────┤ X ├──■──┤ Y ├───────────────────────────────────────────────────────────────────────────────────────────
┌─┴─┐└─┬─┘ └─┬─┘ │ └───┘ ┌───┐┌───┐ ┌───┐┌───┐┌───┐ ┌───┐┌───┐┌───┐
q_1: ┤ X ├──┼─────────┼────┼────■────────────────────────────────────────┤ X ├┤ H ├──■──┤ S ├┤ H ├┤ S ├─X───■──┤ H ├┤ S ├┤ Y ├
└───┘ │ │ ┌─┴─┐ │ ┌───┐ ┌───┐┌───┐ ┌───┐└─┬─┘├───┤ │ └───┘└───┘└───┘ │ ┌─┴─┐├───┤└───┘└───┘
q_2: ───────┼────■────┼──┤ X ├──┼──┤ H ├──■──┤ H ├┤ S ├───────────X─┤ X ├──┼──┤ H ├──┼──────────────────X─┤ X ├┤ Y ├──────────
┌───┐ │ ┌─┴─┐ │ └───┘ │ └───┘ │ ├───┤├───┤ ┌───┐ │ └─┬─┘ │ └───┘┌─┴─┐┌───┐ └───┘└───┘
q_3: ┤ H ├──┼──┤ X ├──■─────────┼─────────┼──┤ X ├┤ H ├──■──┤ S ├─X───■────■───────┤ X ├┤ Y ├─────────────────────────────────
└───┘ │ ├───┤ ┌─┴─┐ ┌─┴─┐└─┬─┘└───┘┌─┴─┐├───┤ └───┘└───┘
q_4: ───────■──┤ H ├──────────┤ X ├─────┤ X ├──■───────┤ X ├┤ Y ├─────────────────────────────────────────────────────────────
└───┘ └───┘ └───┘ └───┘└───┘
decoder circuit:
┌───┐ ┌───┐ ┌───┐
q_0: ┤ Y ├─────────────────────────────────────────────────────────────────────────────────────────────────────■──┤ X ├─────┤ X ├──■──
├───┤┌─────┐┌───┐ ┌─────┐┌───┐┌─────┐ ┌───┐┌───┐ │ └─┬─┘ └─┬─┘┌─┴─┐
q_1: ┤ Y ├┤ Sdg ├┤ H ├──■───X─┤ Sdg ├┤ H ├┤ Sdg ├──■──┤ H ├┤ X ├─────────────────────────────────────■─────────┼────┼─────────┼──┤ X ├
├───┤└─────┘└───┘┌─┴─┐ │ └┬───┬┘└───┘└─────┘ │ └───┘└─┬─┘┌───┐ ┌─────┐┌───┐ │ ┌───┐┌─┴─┐ │ │ └───┘
q_2: ┤ Y ├────────────┤ X ├─X──┤ H ├───────────────┼─────────┼──┤ X ├─X─┤ Sdg ├┤ H ├────────────■────┼──┤ H ├┤ X ├──┼────■────┼───────
├───┤ └───┘ └───┘ ┌─┴─┐ │ └─┬─┘ │ ├─────┤└───┘┌───┐┌───┐ │ │ └───┘└───┘ │ ┌─┴─┐ │ ┌───┐
q_3: ┤ Y ├───────────────────────────────────────┤ X ├───────■────■───X─┤ Sdg ├──■──┤ H ├┤ X ├──┼────┼──────────────■──┤ X ├──┼──┤ H ├
├───┤ └───┘ └─────┘┌─┴─┐└───┘└─┬─┘┌─┴─┐┌─┴─┐┌───┐ └───┘ │ └───┘
q_4: ┤ Y ├─────────────────────────────────────────────────────────────────────┤ X ├───────■──┤ X ├┤ X ├┤ H ├─────────────────■───────
└───┘ └───┘ └───┘└───┘└───┘
Z on ancilla q0 -> XZZXI
Z on ancilla q1 -> IXZZX
Z on ancilla q2 -> XIXZZ
Z on ancilla q3 -> ZXIXZ
logical X -> XXXXX
logical Y -> YYYYY
logical Z -> ZZZZZ
魔法状態蒸留を検証する
魔法状態蒸留の手続きを愚直に再現して
魔法状態蒸留の実装
まずは入力状態を作成するヘルパー関数 make_input を定義します。
import cmath
import math
import numpy as np
def k_projectors():
z = 1 / math.sqrt(3)
cos_t = math.sqrt((1 + z) / 2)
sin_t = math.sqrt((1 - z) / 2)
phase = cmath.exp(1j * math.pi / 4)
k0 = np.array([cos_t, phase * sin_t], dtype=complex)
k1 = np.array([sin_t, -phase * cos_t], dtype=complex)
p0 = np.outer(k0, k0.conj())
p1 = np.outer(k1, k1.conj())
return p0, p1
def kron_n(mat, n):
out = mat
for _ in range(n - 1):
out = np.kron(out, mat)
return out
def make_input(eps):
p0, p1 = k_projectors()
rho = (1 - eps) * p0 + eps * p1
return kron_n(rho, 5)
蒸留手続きを distill() 関数で実装します。
- decoder 回路の適用 : Qiskit の
Operatorクラスを使ってCliffordクラスに対応する行列を作成します。 - 射影演算子の適用: decoder 回路を適用した後にスタビライザー空間に射影します。 decoder 回路はスタビライザーを補助量子ビットの
に map するため、Z 固有空間への射影演算子は非常に単純な形をしています。+1 -
の計算:\epsilon_{out} の係数を求めるために、射影演算子を適用してからトレースを計算しています。\ket{K_0}\bra{K_0}, \ket{K_1}\bra{K_1}
from qiskit.quantum_info import Operator, DensityMatrix, partial_trace
def distill(eps, decoder):
# 入力状態 \rho^{\otimes 5} の作成
rho5 = make_input(eps)
# decode 回路の適用
u = Operator(decoder).data
rho_dec = u @ rho5 @ u.conj().T
# 射影演算子の適用
# decode 回路の適用後なので、論理演算子におけるスタビライザーは補助量子ビットの Z に map されている
# このため、射影演算子は下の proj のように実装できる
proj_anc = np.zeros((16, 16), dtype=complex)
proj_anc[0, 0] = 1.0
proj = np.kron(proj_anc, np.eye(2))
rho_post = proj @ rho_dec @ proj
# 魔法状態蒸留の成功確率の計算
p_success = np.trace(rho_post).real
# \epsilon_{out} の計算
rho_post = rho_post / p_success
dm = DensityMatrix(rho_post)
rho_data = partial_trace(dm, [1, 2, 3, 4]).data
p0, p1 = k_projectors()
e0 = np.real(np.trace(p0 @ rho_data))
e1 = np.real(np.trace(p1 @ rho_data))
eps_out = e0 / (e0 + e1)
return eps_out, p_success
これまでに実装した decoder を使って distill() 関数を様々な

まとめ
perfect code では
魔法状態蒸留に関する素晴らしい理論である Bravyi-Haah [6] や、中性原子型量子コンピュータでの魔法状態蒸留の実証実験 [7] 等についても今後紹介できればと考えています。
-
Gidney, Craig, Noah Shutty, and Cody Jones. "Magic state cultivation: growing T states as cheap as CNOT gates." arXiv preprint arXiv:2409.17595 (2024). ↩︎
-
Bravyi, Sergey, and Alexei Kitaev. "Universal quantum computation with ideal Clifford gates and noisy ancillas." Physical Review A—Atomic, Molecular, and Optical Physics 71.2 (2005): 022316. ↩︎ ↩︎
-
Bravyi, Sergey, and Jeongwan Haah. "Magic-state distillation with low overhead." Physical Review A—Atomic, Molecular, and Optical Physics 86.5 (2012): 052329. ↩︎
-
Sales Rodriguez, Pedro, et al. "Experimental demonstration of logical magic state distillation." Nature 645.8081 (2025): 620-625. ↩︎
Discussion