Demo 1:BB84 QKD

第 7 章 量子信息 · 本章演示 · 代码演示总索引

可视化:双子图 —— 1000 轮 QBER 与密钥长度对比

对应教程:第 7 章 量子信息 中相关小节 · 依赖安装:pip install "unified-quantum[visualization,simulation]"

实现要点

Alice 随机选 (bit, basis) 制备 4 态 |0>,|1>,|+>,|-> Bob 随机选基 (Z/X) 测量 → 保留 bases 一致的回合作为原始 key。 Eve 拦截-重发:随机选基测量后重发 → 错误率 25% (理论)。

注:本 demo 后端 shots=1 永远返回确定结果,所以按 (basis, bit) 聚合 用 shots=2000 给出真实统计分布;Eve 的影响在聚合阶段叠加。

完整源码

demos/ch07-quantum-info/01_bb84_qkd.py
  1"""
  2ch07-quantum-info demo 1: BB84 QKD protocol simulation
  3
  4对应教程:chenzhaoyun.com/quantum-tutorial/chch07-quantum-info/01-bb84-qkd.html
  5依赖:pip install unified-quantum[visualization]
  6运行:python demos/ch07-quantum-info/01_bb84_qkd.py
  7
  8Alice 随机选 (bit, basis) 制备 4 态 |0>,|1>,|+>,|->
  9Bob 随机选基 (Z/X) 测量 → 保留 bases 一致的回合作为原始 key。
 10Eve 拦截-重发:随机选基测量后重发 → 错误率 25% (理论)。
 11
 12注:本 demo 后端 shots=1 永远返回确定结果,所以按 (basis, bit) 聚合
 13   用 shots=2000 给出真实统计分布;Eve 的影响在聚合阶段叠加。
 14"""
 15
 16from __future__ import annotations
 17
 18from pathlib import Path
 19
 20import matplotlib
 21matplotlib.use("Agg")
 22import matplotlib.pyplot as plt
 23import numpy as np
 24
 25from uniqc import Circuit, submit_task, wait_for_result
 26
 27OUT = Path(__file__).parent
 28SHOTS = 2000
 29N_ROUNDS = 1000
 30
 31
 32def bb84_round(alice_basis: str, alice_bit: int, bob_basis: str,
 33               shots: int = SHOTS) -> dict:
 34    """跑 (alice_basis, alice_bit, bob_basis) 一种组合,返回 Bob 的概率分布。
 35
 36    Alice X|1> 制备为 |-> = H·X|0>(顺序:先 X 再 H)。
 37    """
 38    c = Circuit()
 39    if alice_basis == "X":
 40        if alice_bit == 1:
 41            c.x(0)
 42        c.h(0)
 43    elif alice_bit == 1:
 44        c.x(0)
 45    if bob_basis == "X":
 46        c.h(0)
 47    c.measure(0)
 48    tid = submit_task(circuit=c, backend="dummy:local:simulator", shots=shots)
 49    return wait_for_result(tid).probabilities
 50
 51
 52def aggregate_qber(with_eve: bool, n_rounds: int = N_ROUNDS,
 53                   rng: np.random.Generator | None = None) -> dict:
 54    """聚合 BB84 协议统计。"""
 55    if rng is None:
 56        rng = np.random.default_rng(20250909)
 57    alice_bits = rng.integers(0, 2, n_rounds)
 58    alice_bases = rng.choice(["Z", "X"], n_rounds)
 59    bob_bases = rng.choice(["Z", "X"], n_rounds)
 60    if with_eve:
 61        eve_bases = rng.choice(["Z", "X"], n_rounds)
 62    else:
 63        eve_bases = [None] * n_rounds
 64
 65    cache: dict[tuple[str, str], dict[int, dict[str, float]]] = {}
 66    for ab in ["Z", "X"]:
 67        for bb in ["Z", "X"]:
 68            cache[(ab, bb)] = {abit: bb84_round(ab, abit, bb) for abit in [0, 1]}
 69
 70    matched = errors = 0
 71    for i in range(n_rounds):
 72        if alice_bases[i] != bob_bases[i]:
 73            continue
 74        if with_eve:
 75            bob_out = int(alice_bits[i] if eve_bases[i] == alice_bases[i]
 76                          else rng.integers(0, 2))
 77        else:
 78            p = cache[(alice_bases[i], bob_bases[i])][int(alice_bits[i])]
 79            bob_out = int(rng.choice([0, 1], p=[p.get("0", 0.0),
 80                                                  p.get("1", 0.0)]))
 81        matched += 1
 82        if bob_out != int(alice_bits[i]):
 83            errors += 1
 84    return {
 85        "matched": matched, "qber": errors / max(matched, 1),
 86        "key_len": matched - errors, "scenario": "with Eve" if with_eve else "no Eve",
 87    }
 88
 89
 90print("ch07-quantum-info demo 1: BB84 QKD simulation")
 91res_no_eve = aggregate_qber(with_eve=False, rng=np.random.default_rng(20250909))
 92res_eve = aggregate_qber(with_eve=True, rng=np.random.default_rng(20250909))
 93sane_check = [(f"{ab}|{abit}>→{bb}",
 94               bb84_round(ab, abit, bb).get(str(abit), 0.0))
 95              for ab in ["Z", "X"] for abit in [0, 1] for bb in ["Z", "X"]]
 96
 97# 画 2 子图:QBER + 密钥长度堆叠柱
 98fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(11.5, 4.6))
 99scenarios = ["no Eve", "with Eve"]
100qbers = [res_no_eve["qber"], res_eve["qber"]]
101keys = [res_no_eve["key_len"], res_eve["key_len"]]
102matched = [res_no_eve["matched"], res_eve["matched"]]
103colors = ["#2ca02c", "#d62728"]
104
105bars = ax1.bar(scenarios, qbers, color=colors, alpha=0.85, edgecolor="black")
106for b, q in zip(bars, qbers):
107    ax1.text(b.get_x() + b.get_width() / 2, q + 0.005, f"{q:.3f}",
108             ha="center", va="bottom", fontsize=11, fontweight="bold")
109ax1.axhline(0.11, color="#888", ls="--", lw=1.0,
110            label="Eve-detection threshold (~11%)")
111ax1.set_ylabel("QBER (error rate on matched bases)")
112ax1.set_title("Quantum Bit Error Rate", fontsize=12)
113ax1.set_ylim(0, max(0.30, max(qbers) * 1.15))
114ax1.legend(loc="upper left", fontsize=9)
115ax1.grid(axis="y", alpha=0.3)
116
117x = np.arange(len(scenarios))
118errs = [m - k for m, k in zip(matched, keys)]
119ax2.bar(x, keys, color=colors, alpha=0.85, edgecolor="black", label="final key bits")
120ax2.bar(x, errs, bottom=keys, color="#bbbbbb", alpha=0.7,
121        edgecolor="black", label="discarded (errors)")
122for i, (k, e) in enumerate(zip(keys, errs)):
123    ax2.text(i, k + e + 12, f"{k}", ha="center", va="bottom",
124             fontsize=11, fontweight="bold")
125    ax2.text(i, k / 2, f"key\n{k}", ha="center", va="center",
126             color="white", fontsize=10, fontweight="bold")
127    ax2.text(i, k + e / 2, f"err\n{e}", ha="center", va="center",
128             color="black", fontsize=10)
129ax2.set_xticks(x); ax2.set_xticklabels(scenarios)
130ax2.set_ylabel("Number of bits")
131ax2.set_title(f"Final key length (N={N_ROUNDS} rounds)", fontsize=12)
132ax2.set_ylim(0, max(matched) * 1.15)
133ax2.legend(loc="upper right", fontsize=9)
134ax2.grid(axis="y", alpha=0.3)
135
136plt.suptitle("ch07 BB84 — Quantum Key Distribution: no-Eve vs intercept-resend Eve",
137             fontsize=13, y=1.01)
138plt.tight_layout()
139out = OUT / "01_bb84_qkd.png"
140plt.savefig(out, dpi=120, bbox_inches="tight")
141plt.close()
142
143print(f"  no-Eve  : QBER = {res_no_eve['qber']:.4f}  "
144      f"matched = {res_no_eve['matched']:4d}  key = {res_no_eve['key_len']:4d}")
145print(f"  with-Eve: QBER = {res_eve['qber']:.4f}  "
146      f"matched = {res_eve['matched']:4d}  key = {res_eve['key_len']:4d}")
147print(f"  theory  : QBER_no_eve=0.000  QBER_with_eve=0.250  (intercept-resend)")
148print(f"  matched-rate = {res_no_eve['matched']/N_ROUNDS:.3f} "
149      "(expected 0.500, random bases)")
150print("  per-basis detection probabilities (sanity):")
151for label, pc in sane_check:
152    print(f"    Alice {label}: P(Bob=Alice) = {pc:.3f}")
153print(f"  PNG -> {out}")

运行结果

Demo 1:BB84 QKD

运行方式

# 仓库根目录执行;输出 PNG 到 demos/ch07-quantum-info/01_bb84_qkd.png,关键统计到 stdout
.venv/bin/python3 demos/ch07-quantum-info/01_bb84_qkd.py

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