2. 表面码综合征数据格式不匹配的排查与转换

问题定位:先分清“维度不符”还是“编码不符”

表面码译码器读入的综合征数据通常是一个二维数组:行对应测量轮次(time slice),列对应稳定子测量结果(detector/ancilla 索引)。常见的不匹配有两类:维度不匹配(轮次数或稳定子数与译码图期望的 num_detectors、num_rounds 不一致)和编码不匹配(数据是 0/1 整数、±1 符号、布尔值或打包的比特串,而译码器期望其中某一种)。排查顺序应是先校验维度,再校验编码,因为维度错误会让后续编码转换产生误导性的“成功”。

综合征数据格式检查与转换链路 原始综合征数据 shape / dtype / 取值 ① 维度校验 rounds × detectors 对比译码图期望值 ② 编码校验 0/1 · ±1 · bool 比特打包顺序 ③ 规范化转换 统一为 uint8 0/1 reshape 到期望维度 维度不符处理 检查轮次是否含末轮边界 reshape / 截断 / 补零 编码不符处理 ±1 → (x+1)//2 bool → astype(uint8) 译码器输入 uint8 0/1 · 维度匹配 → 完成一次译码 不符则分支 不符则分支

格式检查函数:把两类错误分开报出

下面的检查函数以“译码图期望的探测器数与轮次数”为基准,先比对维度,再比对取值集合。它返回结构化的诊断信息,而不是直接抛异常,便于在批量数据上定位是哪一批、哪一列出了问题。

import numpy as np

def check_syndrome(syn, num_detectors, num_rounds):
    """返回 (ok, report)。syn 为待检查的综合征数组。"""
    report = {}
    arr = np.asarray(syn)
    report["shape"] = arr.shape
    report["dtype"] = str(arr.dtype)

    # ① 维度校验:允许 (rounds, detectors) 或展平的一维
    if arr.ndim == 1:
        if arr.size != num_detectors * num_rounds:
            return False, {**report, "error": "一维长度与期望不符",
                           "expected": num_detectors * num_rounds, "got": arr.size}
        arr2d = arr.reshape(num_rounds, num_detectors)
    elif arr.ndim == 2:
        if arr.shape != (num_rounds, num_detectors):
            return False, {**report, "error": "二维形状与期望不符",
                           "expected": (num_rounds, num_detectors), "got": arr.shape}
        arr2d = arr
    else:
        return False, {**report, "error": f"不支持 {arr.ndim} 维输入"}

    # ② 编码校验:取值必须落在已知集合内
    uniq = np.unique(arr2d)
    report["unique_values"] = uniq.tolist()
    if set(uniq.tolist()) <= {0, 1}:
        report["encoding"] = "0/1"
    elif set(uniq.tolist()) <= {-1, 1}:
        report["encoding"] = "±1"
    elif set(uniq.tolist()) <= {False, True}:
        report["encoding"] = "bool"
    else:
        return False, {**report, "error": "取值集合无法识别", "unique_values": uniq.tolist()}

    return True, report

规范化转换:统一到 uint8 的 0/1

确认维度与编码后,把数据统一转换为译码器最常用的 uint8 0/1 形式。±1 编码用 (x + 1) // 2 映射;布尔值直接 astype(np.uint8);若原始数据是打包比特串,需要先按位展开再 reshape。

def normalize_syndrome(syn, num_detectors, num_rounds):
    ok, report = check_syndrome(syn, num_detectors, num_rounds)
    if not ok:
        raise ValueError(f"综合征格式检查失败: {report}")

    arr = np.asarray(syn)
    if arr.ndim == 1:
        arr = arr.reshape(num_rounds, num_detectors)

    enc = report["encoding"]
    if enc == "0/1":
        out = arr.astype(np.uint8)
    elif enc == "±1":
        out = ((arr + 1) // 2).astype(np.uint8)
    elif enc == "bool":
        out = arr.astype(np.uint8)
    else:
        raise ValueError(f"未知编码: {enc}")

    # 最终一致性断言:形状与取值都必须符合译码器契约
    assert out.shape == (num_rounds, num_detectors)
    assert out.dtype == np.uint8
    assert set(np.unique(out).tolist()) <= {0, 1}
    return out

用示例数据验证转换闭环

假设译码图期望 num_detectors=8、num_rounds=3。构造三组示例输入:一组形状正确但为 ±1 编码,一组形状错误(少一轮),一组为布尔编码。预期前两组分别触发编码转换与维度报错,第三组直接通过。

num_detectors, num_rounds = 8, 3

# 示例 A:±1 编码,形状正确
syn_a = np.array([[1,-1,1,1,-1,1,-1,1],
                  [-1,1,1,-1,1,1,1,-1],
                  [1,1,-1,1,1,-1,1,1]])
out_a = normalize_syndrome(syn_a, num_detectors, num_rounds)
print("A 转换后:", out_a.dtype, out_a.shape, np.unique(out_a))

# 示例 B:少一轮,维度不符
syn_b = np.zeros((2, 8), dtype=np.uint8)
ok_b, rep_b = check_syndrome(syn_b, num_detectors, num_rounds)
print("B 检查:", ok_b, rep_b.get("error"), rep_b.get("expected"), rep_b.get("got"))

# 示例 C:布尔编码
syn_c = np.array([[True,False,True,True,False,True,False,True]] * 3)
out_c = normalize_syndrome(syn_c, num_detectors, num_rounds)
print("C 转换后:", out_c.dtype, out_c.shape, np.unique(out_c))

预期输出(示例数据直接推出,非实测):A 组得到 uint8 (3, 8) 且取值集合为 {0, 1};B 组返回 False,错误信息为“二维形状与期望不符”,期望 (3, 8)、实际 (2, 8);C 组同样得到 uint8 (3, 8)。三组结果分别对应“编码转换成功”“维度拦截”“编码直通”三种路径。

若译码器报“探测器数不匹配”,先确认末轮边界:部分实验数据在最后一轮只记录边界稳定子,导致列数比 num_detectors 少。此时应在转换前补齐边界列,而不是简单截断。
不要用 astype(np.uint8) 直接处理 ±1 数据:-1 会回绕成 255,取值集合检查会失败,但若跳过检查直接送入译码器,可能得到看似正常却错误的译码结果。

排查清单

检查项期望不符时的动作
数组维度(num_rounds, num_detectors)reshape 或补齐末轮边界列
取值集合{0, 1}±1 用 (x+1)//2;bool 用 astype
dtypeuint8转换后断言 dtype
打包比特串按位展开后 reshape先 unpackbits 再校验维度