4. 最小权重完美匹配译码结果全为同一逻辑类的排障
问题现象与排查切入点
MWPM(最小权重完美匹配)译码器在表面码上的输出,本质上是把综合征事件(探测器翻转)两两配对,再沿匹配边回溯出对应的数据比特翻转链,最后把这条链的奇偶性映射成一个逻辑类(logical class,通常记为 0 或 1)。当译码结果恒为同一逻辑类时,说明匹配过程已经退化:要么所有综合征都被配成了"零权重/同侧"的平凡对,要么边界顶点被错误地当成万能吸收端,要么权重设置让某类边永远优先,导致回溯链的奇偶性被固定。
排查顺序建议从"图是否连通、边界是否被滥用、权重是否单调"三条线并行检查,而不是先怀疑匹配算法本身。下面这张图把三条排查线与它们各自的典型症状、定位手段对应起来。
用一段可运行的检查脚本定位退化点
下面这段代码不依赖具体译码库,只针对"匹配图"这一数据结构做体检。它假设你已经能从译码器内部导出节点列表、边列表(含权重)以及边界顶点标记。三个检查函数分别对应上图中的三条排查线,输出结果直接告诉你哪条线出了问题。
import math
from collections import defaultdict, deque
# 匹配图导出格式(示例):
# nodes: [0, 1, 2, ...] 探测器节点 id
# edges: [(u, v, w), ...] u,v 为节点 id,w 为浮点权重
# boundary: {b1, b2, ...} 边界顶点 id 集合(虚拟节点)
def check_connectivity(nodes, edges):
"""① 连通性:返回连通分量列表与孤立节点"""
adj = defaultdict(list)
for u, v, w in edges:
adj[u].append(v)
adj[v].append(u)
seen, comps = set(), []
for n in nodes:
if n in seen:
continue
q, comp = deque([n]), []
seen.add(n)
while q:
x = q.popleft()
comp.append(x)
for y in adj[x]:
if y not in seen:
seen.add(y)
q.append(y)
comps.append(comp)
isolated = [n for n in nodes if len(adj[n]) == 0]
return comps, isolated
def check_boundary_weights(edges, boundary):
"""② 边界条件:统计边界边权重分布"""
bw = [w for u, v, w in edges if u in boundary or v in boundary]
if not bw:
return {"count": 0, "min": None, "max": None, "zero_ratio": None}
zero = sum(1 for w in bw if abs(w) < 1e-12)
return {"count": len(bw), "min": min(bw), "max": max(bw),
"zero_ratio": zero / len(bw)}
def check_weight_monotonic(edges):
"""③ 权重设置:检查是否存在负权、NaN、以及大量重复值"""
ws = [w for _, _, w in edges]
bad = [w for w in ws if math.isnan(w) or w < 0]
uniq = len(set(round(w, 9) for w in ws))
return {"total": len(ws), "bad": len(bad),
"unique_ratio": uniq / len(ws) if ws else 0.0,
"min": min(ws) if ws else None, "max": max(ws) if ws else None}
# ---- 教学示例:一个被错误配置的匹配图 ----
nodes = list(range(8))
boundary = {100, 101}
edges = [
(0, 1, 1.0), (1, 2, 1.0), (2, 3, 1.0),
(4, 5, 1.0), (5, 6, 1.0), (6, 7, 1.0),
(0, 100, 0.0), (1, 100, 0.0), (2, 100, 0.0), (3, 100, 0.0),
(4, 101, 0.0), (5, 101, 0.0), (6, 101, 0.0), (7, 101, 0.0),
]
comps, isolated = check_connectivity(nodes, edges)
print("连通分量:", comps)
print("孤立节点:", isolated)
print("边界权重:", check_boundary_weights(edges, boundary))
print("权重检查:", check_weight_monotonic(edges))
对这段示例数据运行后,会看到两个连通分量 [0,1,2,3] 与 [4,5,6,7],没有孤立节点;边界边权重全部为 0,zero_ratio = 1.0;权重唯一值比例很低(大量 0.0 与 1.0)。这三条输出正好对应"边界边权重恒为 0"这一典型退化配置——匹配器会把所有事件优先配到边界,回溯链的奇偶性因此被固定,逻辑类自然恒定。
修复配置并验证逻辑类不再恒定
修复的核心是把边界边权重从"恒 0"改为"与到边界的几何距离成正比",并保证边界顶点只作为吸收端、不与普通探测器节点混用同一 id 空间。下面给出修复后的边构造逻辑与验证方法。
def build_edges_with_boundary(detectors, boundary_dist, d):
"""
detectors: {id: (x, y)} 探测器坐标
boundary_dist: {id: 到最近边界的曼哈顿距离}
d: 码距,用于归一化
返回: 边列表,边界边权重 = 距离 / d,普通边权重 = 曼哈顿距离
"""
edges = []
ids = list(detectors)
for i in range(len(ids)):
for j in range(i + 1, len(ids)):
u, v = ids[i], ids[j]
(x1, y1), (x2, y2) = detectors[u], detectors[v]
dist = abs(x1 - x2) + abs(y1 - y2)
if dist <= 2: # 只连近邻,避免稠密图
edges.append((u, v, float(dist)))
for u in ids:
w = boundary_dist[u] / d
edges.append((u, 1000 + u, w)) # 边界顶点独立 id 空间
return edges
# 验证:用同一批综合征分别译码,统计逻辑类分布
def verify_logical_distribution(decoder, syndromes, n_rounds=200):
from collections import Counter
cnt = Counter()
for s in syndromes[:n_rounds]:
logical = decoder.decode(s)
cnt[logical] += 1
return cnt
# 预期:修复后 Counter 中 0 与 1 都应出现;
# 若仍只有一个键,说明边界权重或连通性仍有问题。
验证时不要只看"是否出现 1",还要看 0/1 的比例是否随物理错误率变化而合理偏移。若比例极端偏向一侧(例如 199:1),说明边界权重虽非零但仍过小,匹配器依旧倾向走边界,需要继续调大边界权重系数。
常见误区:把边界顶点 id 设为 0 或 -1 并与普通探测器共用邻接表,导致匹配器把真实探测器误判为边界,所有事件都被"吸收"。修复时应使用独立 id 空间(如上例的
1000 + u),并在导出图时显式标记边界集合。排查结论对照
| 检查项 | 退化特征 | 修复动作 | 验证通过条件 |
|---|---|---|---|
| 连通性 | 连通分量 > 1 或存在孤立节点 | 补齐缺失边,检查坐标映射 | 全图单一连通分量,无孤立点 |
| 边界条件 | 边界边权重恒 0 或 zero_ratio = 1.0 | 边界权重改为距离/d,独立 id 空间 | 边界权重随距离单调,0/1 均出现 |
| 权重设置 | 负权、NaN、唯一值比例过低 | 权重取绝对值、检查数据类型 | 无负权/NaN,唯一值比例合理 |
按上表逐项核对后,若逻辑类分布恢复为 0/1 混合且比例随错误率合理变化,即可判定本次"输出恒为同一逻辑类"的退化已被定位并修复。