4. 最小权重完美匹配译码结果全为同一逻辑类的排障

问题现象与排查切入点

MWPM(最小权重完美匹配)译码器在表面码上的输出,本质上是把综合征事件(探测器翻转)两两配对,再沿匹配边回溯出对应的数据比特翻转链,最后把这条链的奇偶性映射成一个逻辑类(logical class,通常记为 0 或 1)。当译码结果恒为同一逻辑类时,说明匹配过程已经退化:要么所有综合征都被配成了"零权重/同侧"的平凡对,要么边界顶点被错误地当成万能吸收端,要么权重设置让某类边永远优先,导致回溯链的奇偶性被固定。

排查顺序建议从"图是否连通、边界是否被滥用、权重是否单调"三条线并行检查,而不是先怀疑匹配算法本身。下面这张图把三条排查线与它们各自的典型症状、定位手段对应起来。

MWPM 输出恒为同一逻辑类:三条排查线 症状:多次采样、不同综合征模式下,decode() 返回的 logical 始终相同 ① 匹配图连通性 探测器节点是否全部可达? ② 边界条件 边界顶点是否被滥用为吸收端? ③ 权重设置 边权是否单调、是否被截断? 典型症状 • 部分探测器无任何边 • 图被拆成多个连通分量 • 匹配器对孤立点直接跳过 典型症状 • 边界边权重恒为 0 或极小 • 所有事件都配到同一条边界 • 回溯链奇偶性被固定 典型症状 • 权重取整后大量边相等 • 负权重或溢出导致排序错乱 • 某类边永远被优先选中 定位手段 统计每个探测器度数 检查连通分量数量 定位手段 打印边界边权重分布 对比有无边界时的匹配结果 定位手段 输出边权直方图 检查权重数据类型与范围 修复后验证:不同综合征下逻辑类应出现 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 混合且比例随错误率合理变化,即可判定本次"输出恒为同一逻辑类"的退化已被定位并修复。