# 使用 ArcQML 求解 H₂ 基态能量

本教程使用变分量子本征求解器（Variational Quantum Eigensolver，VQE）估计氢分子 H₂ 的基态能量，并展示 ArcQML 的电路、Pauli 可观测量、伴随自动微分和 Adam 优化器如何组成完整训练流程。

完整可运行代码位于 [`examples/rust/h2_vqe.rs`](../../../examples/rust/h2_vqe.rs)。


```rust
use arcqml::prelude::*;

type AppResult<T> = std::result::Result<T, Box<dyn std::error::Error>>;
```

## 问题定义

本例采用以下量子化学设置：

| 配置 | 数值 |
| --- | --- |
| 分子几何 | H `(0, 0, -0.35 Å)`，H `(0, 0, 0.35 Å)` |
| 原子间距 | `0.70 Å` |
| 基组 | STO-3G |
| 活性电子数 | 2 |
| 活性轨道数 | 2 |
| 费米子映射 | Jordan–Wigner |
| 量子比特数 | 4 |
| Hamiltonian 项数 | 15 |
| 固定粒子数子空间维数 | 6 |
| Hartree–Fock 能量 | `-1.1173490350562805 Ha` |
| 精确基态能量 | `-1.1361894542078266 Ha` |

VQE 最小化参数化量子态的能量期望值：

$$
E(\boldsymbol{\theta}) =
\langle \psi(\boldsymbol{\theta}) \rvert H \lvert \psi(\boldsymbol{\theta}) \rangle
$$

根据变分原理，任意归一化试探态均满足 `E(θ) ≥ E₀`。优化的目标就是让试探态逐渐接近基态。

## 构造 Hamiltonian

Jordan–Wigner 变换后，H₂ Hamiltonian 是 15 个 Pauli 字符串的实系数线性组合。ArcQML 使用 `SparsePauliOp` 表示这种算符，并原生支持从 JSON 导入。

本例将完整数据保存在 [`examples/data/h2_hamiltonian.json`](../../../examples/data/h2_hamiltonian.json)，直接调用：

```rust
let hamiltonian = SparsePauliOp::from_json(include_str!(
    "../data/h2_hamiltonian.json"
))?;

assert_eq!(hamiltonian.num_qubits(), 4);
assert_eq!(hamiltonian.len(), 15);
```

`include_str!` 在编译时把 JSON 嵌入可执行文件，运行时无需寻找数据文件。如果 Hamiltonian 来自用户文件，也可以用 `std::fs::read_to_string` 读取后传给同一个 API：

```rust
let json = std::fs::read_to_string("examples/data/h2_hamiltonian.json")?;
let hamiltonian = SparsePauliOp::from_json(&json)?;
```

ArcQML 的 JSON 格式由顶层 `num_qubits` 和 `terms` 组成；每个 Pauli 操作显式记录 `qubit` 与 `pauli`。例如常数项和 `X(q0)X(q1)Y(q2)Y(q3)` 项写作：

```json
{
  "num_qubits": 4,
  "terms": [
    {
      "coefficient": -0.042078985845795724,
      "paulis": []
    },
    {
      "coefficient": -0.04475014386992153,
      "paulis": [
        { "qubit": 0, "pauli": "X" },
        { "qubit": 1, "pauli": "X" },
        { "qubit": 2, "pauli": "Y" },
        { "qubit": 3, "pauli": "Y" }
      ]
    }
  ]
}
```

空 `paulis` 数组表示单位算符。

## 准备 Hartree–Fock 初态

在 Jordan–Wigner 编码中，每个量子比特表示一个自旋轨道是否被占据。两个活性电子占据最低的两个自旋轨道，因此从 `|0000⟩` 出发对 `q0`、`q1` 施加 X 门：

```rust
let mut circuit = Circuit::new(4)?;
circuit.x(0)?.x(1)?;
```

ArcQML 抽样字符串按 `q3q2q1q0` 显示，所以这个状态显示为 `|0011⟩`；内部状态索引采用 little-endian。

## 构造参数化 ansatz

本例使用 6 层硬件高效 ansatz。每层首先在每个量子比特上施加 `RY`、`RZ`，随后使用环形 CNOT 纠缠：

```rust
const NUM_QUBITS: usize = 4;
const LAYERS: usize = 6;

let mut parameter_index = 0usize;
for _ in 0..LAYERS {
    for qubit in 0..NUM_QUBITS {
        let ry = 0.04 * (0.37 * (parameter_index + 1) as f64).sin();
        parameter_index += 1;
        let rz = 0.04 * (0.37 * (parameter_index + 1) as f64).sin();
        parameter_index += 1;
        circuit.ry(ry, qubit)?;
        circuit.rz(rz, qubit)?;
    }
    for control in 0..NUM_QUBITS {
        circuit.cnot(control, (control + 1) % NUM_QUBITS)?;
    }
}
```

线路共有 `6 × 4 × 2 = 48` 个可训练参数。非零、非对称且确定性的初值可以打破参数对称性，同时保证重复运行从同一点开始。

这个硬件高效 ansatz 不严格保持粒子数。它适合演示通用 VQE 和 ArcQML 自动微分，但在化学计算中也可以改用保持粒子数的激发 ansatz。

## 执行 VQE 优化

`StateVectorSimulator::run` 返回能量期望值 `Tensor`。调用 `backward()` 后，ArcQML 使用整条量子线路的伴随反向传播计算所有参数梯度。

```rust
let simulator = StateVectorSimulator::new(4)?;
let mut optimizer = Adam::new(0.05, 0.9, 0.999, 1e-8, 0.0)?;

for step in 1..=100 {
    let energy = simulator.run(&circuit, &hamiltonian)?;
    energy.backward()?;
    optimizer.step(circuit.parameters())?;
    optimizer.zero_grad(circuit.parameters());

    if step == 1 || step % 10 == 0 {
        let current = simulator.run(&circuit, &hamiltonian)?.value()?;
        println!("step {step:>3}: energy = {current:.12} Ha");
    }
}
```

每轮训练的顺序不能颠倒：

1. `run` 计算当前参数对应的能量。
2. `backward` 计算参数梯度。
3. `step` 使用当前梯度更新线路参数。
4. `zero_grad` 清除本轮梯度，为下一轮反向传播做准备。

线路参数刚创建时梯度为空，因此第一次迭代不需要预先调用 `zero_grad`。之后每轮都在参数更新后清空梯度，避免下一轮 `backward` 累加旧梯度。

模拟器始终保存 `|0000⟩`。Hartree–Fock 制备门已包含在线路中，而 `run` 不会修改模拟器自身，因此同一个模拟器可以在全部训练轮次中复用。

## 运行示例

在仓库根目录执行：

```bash
cargo run --release -p arcqml --example h2_vqe
```

程序每 10 步输出一次能量以及相对精确基态能量的绝对误差，最后判断是否达到化学精度：

```text
H2/STO-3G, Jordan-Wigner, 4 qubits
Hamiltonian terms : 15
Trainable params  : 48
Hartree-Fock      : -1.117349035056 Ha
Exact ground      : -1.136189454208 Ha
...
VQE energy        : <优化得到的能量> Ha
Absolute error    : <与精确值的差> Ha
Chemical accuracy: reached/not reached (threshold 1.6e-3 Ha)
```

具体迭代值由优化轨迹决定。由于本例初值固定，在相同 ArcQML 版本与数值环境下结果应当可复现。

## 如何理解结果

- 能量应从初始值总体向下收敛，并受变分原理约束，不应显著低于精确基态能量。
- Hartree–Fock 与精确能量之差约为 `0.0188404 Ha`，这部分差异主要来自电子关联。
- 绝对误差小于 `1.6 × 10⁻³ Ha` 时，通常称为达到化学精度。
- 若 100 步尚未收敛，可增加训练步数、调整学习率，或增加 ansatz 层数；更深线路也更可能出现优化平台或冗余参数。

## 下一步

可以在此示例上继续尝试：

- 将 Adam 替换为 `Sgd`，比较收敛速度。
- 使用保持粒子数的激发 ansatz，限制搜索空间到 6 维双电子子空间。
- 保存训练后的线路权重，并在新进程中通过 `load_weights` 恢复。
- 改变 H–H 键长，计算势能曲线。
