2. 表面码综合征数据格式不匹配的排查与转换
问题定位:先分清“维度不符”还是“编码不符”
表面码译码器读入的综合征数据通常是一个二维数组:行对应测量轮次(time slice),列对应稳定子测量结果(detector/ancilla 索引)。常见的不匹配有两类:维度不匹配(轮次数或稳定子数与译码图期望的 num_detectors、num_rounds 不一致)和编码不匹配(数据是 0/1 整数、±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 |
| dtype | uint8 | 转换后断言 dtype |
| 打包比特串 | 按位展开后 reshape | 先 unpackbits 再校验维度 |