量子伴随自动微分

适用版本 · ArcQML 0.1.0

本页目录

前向定义

对于按执行顺序排列的多个量子门,ArcQML 的 run 从初态开始逐门演化得到最终态,并计算 Hamiltonian 的期望值。第 k 个门对应的状态演化和最终目标函数定义为:

∣ψk⟩=Uk∣ψk−1⟩,f=Re⁡ ⁣(⟨ψm∣H∣ψm⟩).\lvert\psi_k\rangle =U_k\lvert\psi_{k-1}\rangle, \qquad f=\operatorname{Re}\!\left( \langle\psi_m\rvert H\lvert\psi_m\rangle \right).

若初态或任一 Circuit Parameter 需要梯度,且当前线程启用梯度记录,run 会保存最终态、Hamiltonian 作用于最终态的结果、已绑定操作和参数槽位映射,并创建一个自定义 F64 Tensor autograd 节点。否则它只执行前向,不保存伴随上下文。

单态伴随反向公式

设外层实标量损失为 L,单态期望值为 f,标量上游梯度为 g = ∂L/∂f。为避免重复计入上游权重,以下公式使用未缩放的伴随态:

∣λm⟩=H∣ψm⟩,∣λk−1⟩=Uk†∣λk⟩.\lvert\lambda_m\rangle=H\lvert\psi_m\rangle, \qquad \lvert\lambda_{k-1}\rangle=U_k^\dagger\lvert\lambda_k\rangle.

按逆序施加门的共轭转置可以恢复每个门的输入态。参数 p 在多个门中出现时,梯度为各处贡献之和:

∂L∂p=2g∑k: p 出现在 UkRe⁡ ⁣[⟨λk∣∂Uk∂p∣ψk−1⟩].\frac{\partial L}{\partial p} =2g\sum_{k:\,p\text{ 出现在 }U_k} \operatorname{Re}\!\left[ \langle\lambda_k\rvert \frac{\partial U_k}{\partial p} \lvert\psi_{k-1}\rangle \right].

实现可在反向开始时把 g 乘入伴随缓冲区,此后不再额外乘 g。若初态需要梯度,ArcQML 返回的复梯度采用 ∂L/∂Re(ψ₀) + i ∂L/∂Im(ψ₀) 约定:

∇ψ0L=2g∣λ0⟩.\nabla_{\psi_0}L=2g\lvert\lambda_0\rangle.

它等于两倍的共轭 Wirtinger 偏导,不应与未乘 2 的偏导符号混用。

参数梯度则按照 Parameter 的 F32 或 F64 dtype 构造成标量 Tensor。该过程只需保存最终态和已绑定门信息,不需要保存每一层完整状态。

batch 的上游梯度与 VJP

batch run 的输出不是标量而是 [B] 向量,因此它本身不能直接调用无参 backward()。通常应先用 mse_loss 或二元 logits 交叉熵等经典损失函数将其归约为标量。若上层图传回 grad_output,batch 伴随节点要求它是 shape 为 [B] 的 F64 Tensor,并把每个上游梯度乘到对应 batch 行的 adjoint_state 中。

因此 batch 模式实现的是向量-雅可比积(VJP):外部经典网络或损失决定每个样本输出的上游权重,量子伴随器再将其回传到共有 Circuit 参数和可微初态。

no_grad 与训练图内存

用 no_grad() 或 Python 的 with arcqml.no_grad(): 包围 run 时,records_gradients 为 false,运行不保留伴随上下文,返回普通前向 Tensor。验证、推理、基准测试和只读状态分析应采用此模式;需要反向时,不应先把预测转为 f64 或 NumPy 数组,因为那会离开 Tensor 图。

Rust 必须绑定作用域守卫,单独执行 no_grad(); 会在语句结束时立即恢复记录:

rust
{
    let _guard = no_grad();
    let prediction = simulator.run(&circuit, &observable)?;
    // 在此作用域内读取 prediction。
}

梯度记录开关仅作用于当前线程。Python 当前应每次新建上下文,即使用 with arcqml.no_grad():;不要缓存或重复进入同一个上下文对象。