4. 受控门与多量子比特门实现
在状态向量模拟器中,单量子比特门只作用于一个振幅分量内部的成对索引,而受控门的关键在于:控制位决定“是否执行”,目标位决定“执行什么”。当控制条件满足时,目标位上的操作才被施加;否则该振幅对保持不变。理解这一点,是把CNOT、CPhase、Toffoli统一到同一套索引位操作框架的基础。
一、控制逻辑的位级本质
设系统有 n 个量子比特,状态向量长度为 2^n,基态索引 i 的二进制位从高位到低位依次对应 q_{n-1} … q_0。约定:位序采用 q0 为最低位(LSB),即索引 i 的第 k 位表示第 k 个量子比特的取值。受控门的通用判断可以写成:
控制条件成立 ⇔ (i >> c) & 1 == 1 (对每个控制位 c)
目标翻转 ⇔ i ^ (1 << t) (CNOT / Toffoli 的 X 型目标)
相位施加 ⇔ 当目标位为 1 时乘 e^{iθ}(CPhase)
CNOT 是单控制、单目标:控制位 c 为 1 时,对目标位 t 执行 X。CPhase 是单控制、单目标,但目标操作不是翻转,而是当目标位为 1 时给该振幅乘上相位因子 e^{iθ};当 θ=π 时退化为 CZ。Toffoli(CCNOT)有两个控制位 c1、c2 和一个目标位 t,只有两个控制位同时为 1 时才翻转目标位。三者的共同点是:先按控制位筛选索引,再对目标位做局部变换。
二、基于索引位操作的受控门应用函数
在实现上,不需要遍历所有 2^n 个索引再逐个判断,而是可以只枚举“控制位已满足”的那一半索引。以 CNOT 为例,固定控制位 c=1,其余位自由变化,目标位 t 的两种取值构成一对索引 (i, i^(1<<t))。对每一对执行交换即可。下面给出一个通用函数框架(Python 伪代码,状态向量为复数列表):
def apply_controlled_x(state, n, controls, target):
# controls: 控制位列表;target: 目标位
ctrl_mask = 0
for c in controls:
ctrl_mask |= (1 << c)
t_mask = 1 << target
for i in range(1 << n):
if (i & ctrl_mask) != ctrl_mask:
continue # 控制条件不满足,跳过
if i & t_mask:
continue # 只处理目标位为 0 的索引,避免重复交换
j = i | t_mask
state[i], state[j] = state[j], state[i]
def apply_controlled_phase(state, n, controls, target, theta):
ctrl_mask = 0
for c in controls:
ctrl_mask |= (1 << c)
t_mask = 1 << target
phase = complex(math.cos(theta), math.sin(theta))
for i in range(1 << n):
if (i & ctrl_mask) != ctrl_mask:
continue
if i & t_mask:
state[i] *= phase
这里有两个工程细节值得注意。第一,控制位掩码 ctrl_mask 的构造决定了判断效率;对 Toffoli,只需把 controls 设为 [c1, c2] 即可复用同一函数。第二,目标位为 0 的索引只处理一次,否则同一对会被交换两次而抵消。CPhase 则不需要成对处理,因为相位只乘在目标位为 1 的分量上,天然不会重复。
三、4量子比特系统中的GHZ态生成测试
GHZ 态是最大纠缠态的代表:|GHZ⟩ = (|0000⟩ + |1111⟩)/√2。在 4 量子比特系统中,一种标准制备线路是:对 q0 施加 H,然后依次以 q0 为控制、q1/q2/q3 为目标施加 CNOT。下面给出完整的测试代码与预期中间状态。
import math
n = 4
N = 1 << n
state = [0j] * N
state[0] = 1.0 + 0j # 初态 |0000⟩
# 1) H on q0
s = 1 / math.sqrt(2)
state[0], state[1] = s * (state[0] + state[1]), s * (state[0] - state[1])
# 2) CNOT q0 -> q1, q0 -> q2, q0 -> q3
apply_controlled_x(state, n, [0], 1)
apply_controlled_x(state, n, [0], 2)
apply_controlled_x(state, n, [0], 3)
# 3) 输出非零振幅
for i, a in enumerate(state):
if abs(a) > 1e-9:
print(format(i, '04b'), a)
预期输出只有两个非零分量:索引 0000 的振幅为 1/√2 ≈ 0.7071,索引 1111 的振幅也为 1/√2。其余 14 个振幅应严格为 0(浮点误差范围内)。若出现其他非零项,说明控制位判断或目标位交换逻辑有误。例如,若忘记在 CNOT 中跳过目标位为 1 的索引,0000 与 0001 会被交换两次,最终 0000 分量可能被错误地置回,导致 GHZ 态不成立。
为了验证相位类受控门,可以在 GHZ 态上追加一个 CPhase:以 q0 为控制、q1 为目标、θ=π/2。此时只有 |1111⟩ 分量满足控制位 q0=1 且目标位 q1=1,因此只有它被乘上 i。检查输出中 1111 的振幅应变为 i/√2,而 0000 保持不变。这一测试同时覆盖了 CNOT 与 CPhase 两条路径。
四、控制逻辑的对比与常见错误
| 门 | 控制位 | 目标操作 | 索引处理方式 |
|---|---|---|---|
| CNOT | 1 个 | X 翻转 | 成对交换 (i, i^(1<<t)) |
| CPhase | 1 个 | 乘 e^{iθ} | 仅对目标位为 1 的索引乘相位 |
| Toffoli | 2 个 | X 翻转 | 控制掩码为两位,成对交换 |
实现中最容易出错的三点:位序约定不统一(把 q0 当成最高位会导致控制位错位)、成对交换重复执行(未跳过目标位为 1 的索引)、控制条件写成“任一控制位为 1”(Toffoli 要求全部控制位为 1,应使用 (i & ctrl_mask) == ctrl_mask)。在 4 量子比特 GHZ 测试中,只要最终非零分量恰好是 0000 与 1111 且振幅模长相等,就可以判定受控门应用函数在控制逻辑与索引操作两个层面都通过了验证。
把上述函数封装成模块后,CNOT、CPhase、Toffoli 都只是 controls 与 target 的不同组合。在 4 量子比特 GHZ 测试中,最终只保留 0000 与 1111 两个非零振幅,即说明控制逻辑、索引位操作与相位处理三部分协同正确。后续更复杂的多控制门,也可以沿用同一套“控制掩码 + 目标位操作”的索引框架直接扩展。