4. 量子比特索引越界与寄存器映射排障

问题场景:多寄存器线路中的索引越界与映射错配

在 Qiskit 中,量子比特的"地址"由寄存器名 + 寄存器内偏移共同决定,而门操作最终作用在全局物理索引上。当线路同时包含多个量子寄存器(如 q、anc)和多个经典寄存器(如 c、m)时,三类错误最容易出现:

  • 寄存器内越界:q[3] 但 q 只有 2 个比特,报 IndexError 或 QiskitError: Qubit index out of range。
  • 全局索引越界:直接写 qc.cx(0, 5),但线路总比特数只有 4,报 QiskitError: Not enough qubits。
  • 寄存器错配:测量时把量子寄存器 q 映射到经典寄存器 m,但 m 宽度与 q 不一致,或 measure 的经典目标索引超出 m 范围。

下面这段代码故意制造了上述三类问题,用于观察报错栈与线路结构:

from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister

q = QuantumRegister(2, "q")      # 2 个量子比特
anc = QuantumRegister(2, "anc")  # 2 个辅助比特
c = ClassicalRegister(2, "c")    # 2 个经典比特
m = ClassicalRegister(3, "m")    # 3 个经典比特

qc = QuantumCircuit(q, anc, c, m)

qc.h(q[0])
qc.cx(q[0], q[2])        # ① 寄存器内越界:q 只有 0、1
qc.cx(0, 5)              # ② 全局索引越界:总比特数 4
qc.measure(q[0], m[3])   # ③ 经典寄存器越界:m 只有 0、1、2
qc.measure(q, c)         # ④ 宽度错配:q 宽 2,c 宽 2,但 anc 未测量
注意:qc.cx(q[0], q[2]) 在构造阶段就会抛出 IndexError,因此后续语句不会执行。实际排障时应逐条注释、逐条恢复,让每个错误单独暴露,才能看清各自的报错栈。

用报错栈定位:从异常类型反推错误类别

Qiskit 的报错信息通常包含三层线索:异常类型、出错对象、以及被引用的索引值。把这三层对应到"寄存器—偏移—全局索引"的映射关系上,就能快速判断问题类别:

异常类型典型消息片段指向的错误定位方法
IndexErrorlist index out of range寄存器内偏移越界检查 reg.size 与偏移值
QiskitErrorNot enough qubits全局索引越界检查 qc.num_qubits
QiskitErrorregister size mismatch寄存器宽度错配比较量子/经典寄存器宽度
CircuitErrorduplicate bit argument同一比特被重复映射检查 measure 目标是否重复

寄存器映射关系可视化

下图把本例的四个寄存器、各自的宽度、以及"寄存器内偏移 → 全局物理索引"的映射关系画在一起。红色标记为越界访问点,橙色标记为宽度错配点。理解这张映射表,是修正比特映射关系的关键。

多寄存器线路的比特映射与越界点 量子寄存器 q (size=2) q[0] q[1] q[2] ← 越界 anc (size=2) anc[0] anc[1] 全局物理索引 qc.num_qubits = 4 0 1 2 3 5 ← 越界 经典寄存器 c (size=2) c[0] c[1] m (size=3) m[0] m[1] m[2] m[3] ← 越界 measure(q, c) 宽度匹配 ✓ 宽度错配风险 q(2) → c(2) 匹配,但 anc(2) 未测量 图例 量子寄存器 q 量子寄存器 anc 经典寄存器 c / m 越界访问点 寄存器内偏移 → 全局索引 越界映射

修正策略:把"寄存器名 + 偏移"翻译成合法全局索引

定位到越界点后,修正的核心是重新核对每个寄存器在全局索引中的起始位置。Qiskit 按构造时传入寄存器的顺序依次分配全局索引:q 占 0–1,anc 占 2–3。因此:

  • q[2] 应改为 anc[0](全局索引 2),或确认本意是访问 q 的第 3 个比特则需扩大 q 宽度。
  • qc.cx(0, 5) 中的 5 超出 0–3 范围,应改为 qc.cx(q[0], anc[1])(全局索引 3)。
  • qc.measure(q[0], m[3]) 中 m 只有 3 位,应改为 m[0] 或扩大 m 宽度。
  • 若希望 q 与 c 一一对应测量,qc.measure(q, c) 是合法的;但 anc 若参与计算,应补充 qc.measure(anc, m[0:2]) 之类的映射,避免"算了但没测"。

修正后的线路如下,并用 qc.num_qubits、qc.num_clbits 与各寄存器 size 做一次自检:

from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister

q = QuantumRegister(2, "q")
anc = QuantumRegister(2, "anc")
c = ClassicalRegister(2, "c")
m = ClassicalRegister(2, "m")   # 与 anc 宽度对齐

qc = QuantumCircuit(q, anc, c, m)

qc.h(q[0])
qc.cx(q[0], anc[0])            # 原 q[2] → anc[0](全局索引 2)
qc.cx(q[0], anc[1])            # 原 (0,5) → (q[0], anc[1])(全局索引 3)
qc.measure(q[0], m[0])         # 原 m[3] → m[0]
qc.measure(q, c)               # q 宽 2,c 宽 2,匹配
qc.measure(anc, m)             # anc 宽 2,m 宽 2,匹配

# 自检
assert qc.num_qubits == q.size + anc.size == 4
assert qc.num_clbits == c.size + m.size == 4
print("qubits:", qc.num_qubits, "clbits:", qc.num_clbits)
排障口诀:先看异常类型定类别,再看索引值定偏移,最后用 num_qubits / num_clbits 与各寄存器 size 做一次总量核对。多寄存器线路中,任何"看起来对"的索引都要回到全局索引表里验证一遍。

快速复查清单

  • 每个 reg[i] 的 i 是否小于 reg.size?
  • 直接写整数的门参数是否小于 qc.num_qubits?
  • measure(qreg, creg) 两侧宽度是否一致?
  • 参与计算的每个量子寄存器是否都有对应的经典测量目标?
  • 修正后是否用 assert 或打印总量做过一次自检?