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 的报错信息通常包含三层线索:异常类型、出错对象、以及被引用的索引值。把这三层对应到"寄存器—偏移—全局索引"的映射关系上,就能快速判断问题类别:
| 异常类型 | 典型消息片段 | 指向的错误 | 定位方法 |
|---|---|---|---|
IndexError | list index out of range | 寄存器内偏移越界 | 检查 reg.size 与偏移值 |
QiskitError | Not enough qubits | 全局索引越界 | 检查 qc.num_qubits |
QiskitError | register size mismatch | 寄存器宽度错配 | 比较量子/经典寄存器宽度 |
CircuitError | duplicate bit argument | 同一比特被重复映射 | 检查 measure 目标是否重复 |
寄存器映射关系可视化
下图把本例的四个寄存器、各自的宽度、以及"寄存器内偏移 → 全局物理索引"的映射关系画在一起。红色标记为越界访问点,橙色标记为宽度错配点。理解这张映射表,是修正比特映射关系的关键。
修正策略:把"寄存器名 + 偏移"翻译成合法全局索引
定位到越界点后,修正的核心是重新核对每个寄存器在全局索引中的起始位置。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或打印总量做过一次自检?