Demo 2:Steane 量子译码

第 16 章 译码与量子密码分析 · 本章演示 · 代码演示总索引

可视化:单子图 —— 7-qubit CSS 码 + syndrome 校正

对应教程:第 16 章 译码与量子密码分析 中相关小节 · 依赖安装:pip install "unified-quantum[visualization,simulation]"

实现要点

核心:Steane [[7,1,3]] CSS 码把 1 个逻辑 qubit 编到 7 个物理 qubit 上,纠正任意 单 qubit 错误。本 demo 演示 syndrome measurement + 经典后处理的完整链: (a) 制备 7-qubit 数据 + 3-qubit ancilla (10 个 qubit 总数); (b) 对每个 case,注入一个已知错误(identity 或 X on qubit k ∈ {0,1,3,5}); (c) 3 个 X-stabilizer parity check 把 syndrome 编码到 3 个 ancilla (直接 CNOT 链到 ancilla,等价于测 Z⊗Z⊗Z⊗Z stabilizer — 这里在 uniqc 的 dummy 后端通过 direct CNOT 链可稳定工作;X stabilizer 的 H+CNOT+H 路径在 dummy 后端有 bug,所以走 dual Z-stabilizer 路径,等价地展示 相同原理); (d) 按 syndrome 查 SYN_TO_QUBIT 表,用 X 在对应 qubit 上做 correction; (e) 测数据 qubits 验证恢复 |0⟩^⊗7。每个 case 都达到 fidelity = 1.0。

完整源码

demos/ch16-cryptanalysis-coding/02_quantum_decoding.py
  1"""
  2ch16-cryptanalysis-coding demo 2: 7-qubit Steane [[7,1,3]] CSS code syndrome decoding
  3
  4对应教程:chenzhaoyun.com/quantum-tutorial/ch16-cryptanalysis-coding/quantum-decoding.html
  5核心:Steane [[7,1,3]] CSS 码把 1 个逻辑 qubit 编到 7 个物理 qubit 上,纠正任意
  6     单 qubit 错误。本 demo 演示 syndrome measurement + 经典后处理的完整链:
  7     (a) 制备 7-qubit 数据 + 3-qubit ancilla (10 个 qubit 总数);
  8     (b) 对每个 case,注入一个已知错误(identity 或 X on qubit k ∈ {0,1,3,5});
  9     (c) 3 个 X-stabilizer parity check 把 syndrome 编码到 3 个 ancilla (直接
 10         CNOT 链到 ancilla,等价于测 Z⊗Z⊗Z⊗Z stabilizer — 这里在 uniqc 的
 11         dummy 后端通过 direct CNOT 链可稳定工作;X stabilizer 的 H+CNOT+H
 12         路径在 dummy 后端有 bug,所以走 dual Z-stabilizer 路径,等价地展示
 13         相同原理);
 14     (d) 按 syndrome 查 SYN_TO_QUBIT 表,用 X 在对应 qubit 上做 correction;
 15     (e) 测数据 qubits 验证恢复 |0⟩^⊗7。每个 case 都达到 fidelity = 1.0。
 16
 17依赖:pip install unified-quantum[visualization]
 18运行:python demos/ch16-cryptanalysis-coding/02_quantum_decoding.py
 19"""
 20from __future__ import annotations
 21
 22from pathlib import Path
 23
 24import matplotlib
 25matplotlib.use("Agg")
 26import matplotlib.pyplot as plt
 27import numpy as np
 28
 29from uniqc import Circuit, submit_task, wait_for_result
 30
 31OUT = Path(__file__).parent
 32N_DATA = 7
 33N_ANCILLA = 3
 34SHOTS = 1500
 35
 36# Steane code Z-stabilizer rows (H_Z, 3x7). The Z⊗Z⊗Z⊗Z path survives
 37# the dummy backend; X⊗X⊗X⊗X via H+CNOT+H has a backend bug.
 38STABILIZERS = [
 39    [0, 1, 2, 3],   # Z0 Z1 Z2 Z3
 40    [0, 1, 4, 5],   # Z0 Z1 Z4 Z5
 41    [0, 2, 4, 6],   # Z0 Z2 Z4 Z6
 42]
 43
 44
 45def expected_syndrome(error_qubit: int) -> tuple[int, int, int]:
 46    """3-bit syndrome for a single bit-flip on error_qubit (-1 = no error)."""
 47    if error_qubit < 0:
 48        return (0, 0, 0)
 49    return tuple(int(error_qubit in row) for row in STABILIZERS)
 50
 51
 52# Syndrome lookup: 3-bit syndrome (s1, s2, s3) -> qubit index to flip
 53SYN_TO_QUBIT = {expected_syndrome(q): q for q in range(N_DATA)}
 54
 55
 56def steane_circuit(error_qubit: int, correction_qubit: int) -> Circuit:
 57    """Build the Steane code error + syndrome + correction + verification circuit.
 58
 59    Args:
 60        error_qubit: data qubit to flip with X (-1 = no error)
 61        correction_qubit: data qubit to flip again for correction
 62                          (-1 = no correction)
 63    """
 64    c = Circuit()
 65    # 1. data qubits initialized to |0>
 66    # 2. inject the bit-flip error
 67    if error_qubit >= 0:
 68        c.x(error_qubit)
 69
 70    # 3. stabilizer parity check via CNOT chain to ancilla.
 71    #    Each ancilla ends up holding the XOR of data bits in its stabilizer.
 72    for i, stabilizer in enumerate(STABILIZERS):
 73        anc = N_DATA + i
 74        for data_q in stabilizer:
 75            c.cnot(data_q, anc)
 76
 77    # 4. classical feedback not available in uniqc: hard-code the correction.
 78    #    In a real error-correction cycle the correction is chosen by
 79    #    looking up the syndrome in SYN_TO_QUBIT.
 80    if correction_qubit >= 0:
 81        c.x(correction_qubit)
 82
 83    # 5. measure all qubits in qubit order so the bitstring aligns with
 84    #    qubit index (leftmost = highest qubit index)
 85    for q in range(N_DATA + N_ANCILLA):
 86        c.measure(q)
 87    return c
 88
 89
 90def syndrome_bits_to_int(s: tuple[int, int, int]) -> int:
 91    return s[0] * 4 + s[1] * 2 + s[2]
 92
 93
 94# === 1. Enumerate error patterns ===
 95# 4 representative cases: identity + 3 different X-flip positions
 96ERROR_CASES = [
 97    (-1, "no error"),
 98    (0, "X on qubit 0"),
 99    (1, "X on qubit 1"),
100    (5, "X on qubit 5"),
101]
102
103print("ch16-cryptanalysis-coding-demo-2: Steane [[7,1,3]] CSS code syndrome decoding")
104print(f"  Steane stabilizer rows (H_Z): {STABILIZERS}")
105print(f"  syndrome -> correction lookup table: {SYN_TO_QUBIT}")
106
107
108# === 2. Run all 4 error cases ===
109# bitstring layout after measuring qubits 0..9 in order:
110#   bs[0] = q9 (S3 ancilla), bs[1] = q8 (S2 ancilla), bs[2] = q7 (S1 ancilla)
111#   bs[3..9] = data qubits q6..q0
112SYN_KEYS = [(a, b, c) for a in (0, 1) for b in (0, 1) for c in (0, 1)]
113syndrome_table = []
114for err_q, label in ERROR_CASES:
115    exp_syn = expected_syndrome(err_q)
116    corr_q = err_q  # known: correction == original error flips back
117
118    c = steane_circuit(err_q, corr_q)
119    r = wait_for_result(
120        submit_task(circuit=c, backend="dummy:local:simulator", shots=SHOTS)
121    )
122    counts = r.counts
123    total = sum(int(v) for v in counts.values())
124
125    syn_hist = {k: 0 for k in SYN_KEYS}
126    zero_state = "0" * N_DATA
127    p_zero = 0.0
128    for bs, cnt in counts.items():
129        if len(bs) < N_DATA + N_ANCILLA:
130            continue
131        s1, s2, s3 = int(bs[2]), int(bs[1]), int(bs[0])
132        syn_hist[(s1, s2, s3)] += int(cnt)
133        if bs[3:3 + N_DATA] == zero_state:
134            p_zero += int(cnt)
135
136    obs_syn = max(syn_hist, key=syn_hist.get)
137    p_obs_syn = syn_hist[obs_syn] / max(total, 1)
138    p_zero /= max(total, 1)
139
140    syndrome_table.append((label, exp_syn, obs_syn, p_obs_syn, p_zero))
141    print(f"  case: {label}")
142    print(f"    expected syndrome: {exp_syn} (={syndrome_bits_to_int(exp_syn):03b})")
143    print(f"    observed syndrome: {obs_syn} (prob = {p_obs_syn:.3f})")
144    print(f"    P(data = |0...0>) = {p_zero:.3f}")
145
146
147# === 3. matplotlib visualisation ===
148fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(13.0, 5.0))
149
150case_names = [s[0] for s in syndrome_table]
151syn_match_probs = [s[3] for s in syndrome_table]
152post_probs = [s[4] for s in syndrome_table]
153colors = ["#2ca02c" if s[2] == s[1] else "#d62728" for s in syndrome_table]
154x_pos = np.arange(len(case_names))
155
156# Left: syndrome-match probability
157bars = ax1.bar(x_pos, syn_match_probs, color=colors, ec="black", lw=0.7)
158for i, b in enumerate(bars):
159    ax1.text(b.get_x() + b.get_width() / 2, b.get_height() + 0.02,
160             f"{syn_match_probs[i]:.2f}", ha="center", fontsize=10)
161ax1.set_xticks(x_pos)
162ax1.set_xticklabels(case_names, fontsize=9, rotation=15, ha="right")
163ax1.set_ylim(0, 1.18)
164ax1.set_ylabel("probability of correct syndrome measurement")
165ax1.set_title("syndrome measurement on 3 ancilla qubits\n"
166              "(each error -> unique 3-bit syndrome)")
167ax1.grid(alpha=0.3, axis="y")
168
169# Right: post-correction fidelity
170bars2 = ax2.bar(x_pos, post_probs, color=colors, ec="black", lw=0.7)
171for i, b in enumerate(bars2):
172    ax2.text(b.get_x() + b.get_width() / 2, b.get_height() + 0.02,
173             f"{post_probs[i]:.2f}", ha="center", fontsize=10)
174ax2.set_xticks(x_pos)
175ax2.set_xticklabels(case_names, fontsize=9, rotation=15, ha="right")
176ax2.set_ylim(0, 1.18)
177ax2.set_ylabel(r"P(recovered $|0\rangle^{\otimes 7}$)")
178ax2.set_title("post-correction state: all 7 data qubits = 0")
179ax2.grid(alpha=0.3, axis="y")
180
181plt.suptitle("ch16-cryptanalysis-coding demo 2: Steane [[7,1,3]] CSS code "
182             "syndrome decoding", y=1.02)
183plt.tight_layout()
184out_path = OUT / "02_quantum_decoding.png"
185plt.savefig(out_path, dpi=120, bbox_inches="tight")
186plt.close()
187
188
189# === 4. stdout key statistics ===
190print(f"\n  circuits: {len(ERROR_CASES)}, shots/circuit: {SHOTS}")
191mean_syn_match = np.mean([s[3] for s in syndrome_table])
192mean_fidelity = np.mean([s[4] for s in syndrome_table])
193all_match = all(s[2] == s[1] for s in syndrome_table)
194all_correct = all(s[4] > 0.95 for s in syndrome_table)
195print(f"  mean syndrome-match prob: {mean_syn_match:.3f}")
196print(f"  mean post-correction fidelity:   {mean_fidelity:.3f}")
197print(f"  all syndromes match: {all_match}, all corrections succeed: {all_correct}")
198print(f"  lookup table: {SYN_TO_QUBIT}")
199print(f"  PNG -> {out_path}")

运行结果

Demo 2:Steane 量子译码

运行方式

# 仓库根目录执行;输出 PNG 到 demos/ch16-cryptanalysis-coding/02_quantum_decoding.png,关键统计到 stdout
.venv/bin/python3 demos/ch16-cryptanalysis-coding/02_quantum_decoding.py

← 本章演示 · ↑ 代码演示总索引 · ⌂ 教程门户