4. 构建第一个参数化量子电路

参数化量子电路(Parameterized Quantum Circuit, PQC)是变分量子算法的最小可运行单元。它和固定量子线路的本质区别在于:线路中至少有一个门的角度不是常数,而是由外部传入的变量控制。训练时,优化器不断调整这些变量,量子态随之改变,测量结果的统计分布也随之改变。本节以 Qiskit 为主线,搭建一个含两个可训练参数的两量子比特线路,并说明如何读取测量结果。

一、先明确线路结构

我们构造的线路包含四个阶段:初始化、参数化旋转、纠缠、测量。具体设计如下:

  • 在 q0 上施加 RY(θ₁),在 q1 上施加 RY(θ₂),这两个角度就是可训练参数。
  • 用 CX(q0, q1) 引入纠缠,使两个量子比特的测量结果产生关联。
  • 在 q0 上再施加 RY(θ₃),增加一层可调自由度。
  • 对两个量子比特做 measure,把量子态投影到计算基,得到经典比特串。

这里约定:角度单位为弧度,RY(θ) 的旋转轴为 y 轴,测量基为 Z 基,测量结果 0 和 1 分别对应自旋向上和向下。全文与图示均采用这一约定。

含参量子线路结构:RY(θ₁)、RY(θ₂) → CX → RY(θ₃) → 测量 q0 |0⟩ q1 |0⟩ RY(θ₁) RY(θ₂) CX 纠缠 RY(θ₃) M c0 M c1 可训练参数向量 θ = [θ₁, θ₂, θ₃],由优化器迭代更新

二、用 Qiskit 搭建线路

Qiskit 中,参数化角度用 Parameter 对象表示,它可以在构造线路时占位,在运行时再绑定具体数值。下面给出完整代码:

from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister
from qiskit.circuit import Parameter
import numpy as np

# 1. 定义可训练参数
theta1 = Parameter('θ1')
theta2 = Parameter('θ2')
theta3 = Parameter('θ3')

# 2. 创建寄存器和线路
q = QuantumRegister(2, 'q')
c = ClassicalRegister(2, 'c')
qc = QuantumCircuit(q, c)

# 3. 参数化旋转层
qc.ry(theta1, q[0])
qc.ry(theta2, q[1])

# 4. 纠缠层
qc.cx(q[0], q[1])

# 5. 第二层参数化旋转
qc.ry(theta3, q[0])

# 6. 测量
qc.measure(q, c)

print(qc.parameters)   # 输出:ParameterView([θ1, θ2, θ3])
print(qc.num_parameters)  # 输出:3

运行后,qc.parameters 会返回三个参数对象,qc.num_parameters 返回 3。这说明线路已经正确识别出可训练变量。如果参数数量为 0,说明角度被写成了常数,需要检查是否误用了 np.pi/4 这类固定值。

三、绑定参数并运行

参数化线路不能直接运行,必须先给参数赋值。Qiskit 提供两种方式:assign_parameters 返回新线路,bind_parameters 原地修改。推荐使用前者,便于保留原始模板。

from qiskit_aer import AerSimulator

# 示例参数值(弧度)
params = {theta1: 0.5, theta2: 1.2, theta3: -0.8}

# 绑定参数,得到可执行线路
bound_qc = qc.assign_parameters(params)

# 选择模拟器并运行
sim = AerSimulator()
job = sim.run(bound_qc, shots=4096)
result = job.result()
counts = result.get_counts()

print(counts)

假设运行结果为 {'00': 1024, '01': 1024, '10': 1024, '11': 1024},这只是均匀分布的一种示例,实际结果会随参数变化。更常见的输出形如 {'00': 2100, '01': 900, '10': 800, '11': 296},说明不同比特串的出现概率不同。这里的数值是示例,不是真实运行结果,读者应以自己运行得到的 counts 为准。

四、如何判断结果是否合理

拿到 counts 后,先做三件事:

  1. 检查总 shots 是否一致。四个比特串的计数之和应等于 4096。若不等,说明测量或后处理有误。
  2. 观察分布是否随参数变化。固定 θ₂ 和 θ₃,只改变 θ₁,如果 counts 几乎不变,说明 θ₁ 没有真正进入线路,需要检查参数绑定是否生效。
  3. 检查纠缠是否起作用。把 CX 去掉后重新运行,如果分布从关联态退化为两个独立比特的乘积分布,说明 CX 确实在制造纠缠。

还可以把 counts 转成概率:prob = counts[k] / shots。例如 '00' 出现 2100 次,则概率约为 0.513。这个概率就是后续构造损失函数的基础。

五、用 PennyLane 实现同一线路

PennyLane 的写法更紧凑,参数直接作为函数入参传入。下面是与上面等价的实现:

import pennylane as qml
from pennylane import numpy as np

dev = qml.device('default.qubit', wires=2, shots=4096)

@qml.qnode(dev)
def circuit(theta1, theta2, theta3):
    qml.RY(theta1, wires=0)
    qml.RY(theta2, wires=1)
    qml.CNOT(wires=[0, 1])
    qml.RY(theta3, wires=0)
    return qml.counts()

params = np.array([0.5, 1.2, -0.8], requires_grad=True)
print(circuit(*params))

PennyLane 的优势在于 requires_grad=True 让参数自动支持梯度计算,后续可以直接接入 qml.grad 或优化器。Qiskit 则需要借助 Estimator 和 SPSA 等优化器完成梯度估计。两者都能完成同一件事,选择取决于后续工作流。

六、一个可复用的参数配置示例

下面给出一个适合初学阶段反复实验的参数表,读者可以直接替换数值观察 counts 变化:

实验编号θ₁θ₂θ₃观察重点
E10.00.00.0基线,应接近均匀分布
E2π/20.00.0q0 翻转,q1 保持
E3π/2π/20.0两比特同时翻转
E4π/2π/2π/2第二层旋转改变相位关系

运行 E1 时,如果 counts 明显偏离均匀分布,说明线路中存在未预期的固定角度或测量错误。运行 E2 时,'10' 和 '11' 的计数应显著上升,因为 q0 被旋转到接近 |1⟩。这些判断依据可以帮助读者在没有外部参考的情况下自行验证线路是否正确。

参数化线路的调试顺序建议是:先确认参数数量正确,再确认绑定后线路可运行,最后确认 counts 随参数变化。三步都通过,才说明线路真正可用。

完成本节后,读者应能独立写出一个含参线路,绑定具体角度,运行模拟器,并读懂 counts 的含义。这个最小单元就是后续变分算法、量子神经网络和量子近似优化算法的起点。