# 验证 · 给量子线路写断言和测试

> 「看起来对」不等于「真的对」。这一章把量子程序当软件工程对待：用状态向量断言、幺正矩阵比对、统计检验，给量子线路写可复现的单元测试(pytest)，并直面一个根本难题——大规模量子程序为什么难以经典验证。

- 作者：David（道雾轩）
- 专栏：写给程序员的量子计算（https://daiw.org/manual/quantum-computing.md）
- 最后更新：2026-07-14
- 原文：https://daiw.org/manual/quantum-computing/verification
- 转载与引用：请注明出处并附原文链接（https://daiw.org/about/copyright）

# 验证 · 给量子线路写断言和测试

[上一章](https://daiw.org/manual/quantum-computing/debugging)靠肉眼「打印状态、逐步核对」抓 bug。但作为工程师，你不会满足于「我盯着看了一遍，应该没问题」——你要**自动化、可复现的正确性保证**：单元测试、断言、CI 里一跑就知道对不对。这一章讲怎么给量子线路写测试。

## 断言一：状态向量相等

最直接的验证：**跑出来的状态向量，等不等于理论上应该得到的那个。** Qiskit 的 `Statevector` 支持相等判断：

```python
import numpy as np
from qiskit import QuantumCircuit
from qiskit.quantum_info import Statevector

def make_bell():
    qc = QuantumCircuit(2)
    qc.h(0)
    qc.cx(0, 1)
    return qc

def test_bell_statevector():
    got  = Statevector(make_bell())
    want = Statevector([1/np.sqrt(2), 0, 0, 1/np.sqrt(2)])   # (|00⟩+|11⟩)/√2
    assert got.equiv(want)
```

<Callout type="info">
  **用 `.equiv()`，而不是 `==`。** 量子态有个特点：整体乘一个「全局相位」（比如整个向量乘 $-1$ 或 $e^{i\theta}$）是**物理上完全等价**的态——测量分布一模一样，没有任何可观测差别。`.equiv()` 会忽略这个全局相位，只比较「本质上是不是同一个态」；直接用 `==` 比原始数组，则会被这种无意义的相位差异误判为「不相等」。**验证量子态相等，认准 `.equiv()`。**
</Callout>

## 断言二：整段线路的矩阵

如果你验证的是一个**门 / 子线路**（而不是某个具体状态），更该比的是它的**幺正矩阵**——它对**任意**输入的作用都对，才算真对。用 `Operator`：

```python
from qiskit.quantum_info import Operator

def test_hzh_equals_x():
    qc = QuantumCircuit(1)
    qc.h(0); qc.z(0); qc.h(0)          # 我们在「叠加与干涉」一章声称过 HZH = X
    assert Operator(qc).equiv(Operator.from_label('X'))
```

这条测试**证明**了「$HZH$ 确实等于 $X$」对所有输入成立——比「拿几个具体输入试试」强得多。**验证一段变换逻辑，比矩阵；验证一个具体结果，比状态向量。**

## 断言三：统计检验（测量分布）

有时你没有模拟器的上帝视角（或想模拟真机行为），只能靠**测量统计**来验证。这时验证变成**统计检验**：跑足够多 shots，断言频率落在容差内、或断言某些结果**不该出现**。

```python
from qiskit import transpile
from qiskit_aer import AerSimulator

def test_bell_only_correlated():
    qc = make_bell(); qc.measure_all()
    sim = AerSimulator()
    counts = sim.run(transpile(qc, sim), shots=2000).result().get_counts()
    # Bell 态只能测出 00 或 11,绝不会有 01 / 10
    assert set(counts.keys()) <= {'00', '11'}
    # 两者各约一半,给个宽松容差(统计涨落)
    assert abs(counts.get('00', 0) - counts.get('11', 0)) < 200
```

<Callout type="warn">
  **统计检验必须留容差，别写死。** 量子测量是[概率的](https://daiw.org/manual/quantum-computing/measurement)，2000 次里 `00` 和 `11` 不会精确各 1000，而是 1000 上下浮动。断言写成 `== 1000` 必然时不时挂掉(flaky test)。正确做法：① 用**足够多的 shots**（shots 越多，频率越稳）；② 断言落在一个**合理容差**内（像上面的 `< 200`）；③ 对「绝不该出现」的结果（如这里的 `01`/`10`）才用硬断言。**把量子测试当成统计实验来写，而不是确定性函数。**
</Callout>

## 一个务实的验证策略

把上面拼成一套可操作的流程：

1. **小规模用状态向量 / 矩阵精确验证**(`.equiv()`)——逻辑对不对，一测便知，不看运气。
2. **端到端用统计检验**（带容差）——模拟真机的采样行为。
3. **拿已知答案的输入回归测试**——比如让 [Grover](https://daiw.org/manual/quantum-computing/grover) 在一个你知道答案的小搜索问题上跑，断言它高概率返回正确解。
4. **把这些塞进 pytest + CI**——每次改线路自动跑，防止改坏。

## 一个绕不开的根本难题

最后必须诚实面对一件事，它是量子验证独有的困境：

<Callout type="warn">
  **能用经典电脑「精确验证」的，只有小规模量子程序。** 上面的 `Statevector` / `Operator` 验证，靠的是**经典模拟**——可经典模拟一个 $n$-qubit 系统要 $2^n$ 的内存，**四十几个 qubit 就模拟不动了**。而量子计算真正有价值的，恰恰是那些**大到经典模拟不了**的规模——可那时，你又**没法用经典办法算出「正确答案」来对比**。这是个深刻的悖论：**量子优势的验证本身就是难题。**

  现实中的应对：① 在**小规模**上把算法逻辑验证到底（相信它对大规模同样成立）；② 用**结构化的自检**（比如某些算法的答案能被经典地**快速验证**，即使不能被经典地**快速求出**——想想大数分解：分解很难，但验证「这两个因子乘起来等于原数」很容易）；③ 在**含噪模拟器**上评估噪声影响。**「求解难、验证易」的问题，天然更适合量子——因为你至少能验证它的输出。**
</Callout>

## 小结与下一章

把量子程序当软件工程：用 `Statevector.equiv()` 断言状态、用 `Operator.equiv()` 断言整段变换的矩阵、用带容差的统计检验断言测量分布，塞进 pytest + CI。务实策略是「小规模精确验证 + 端到端统计检验 + 已知答案回归」。但要清醒：**大规模量子程序难以经典验证**，这是量子优势自带的悖论，「求解难验证易」的问题因此格外适合量子。

零件、调试、验证都备齐了，是时候看看这些东西**拼成真正的算法**能干什么。下一站从一个关键概念开始——量子算法怎么把一个经典函数，变成能被叠加「一次性喂进去」的黑箱。👉 [预言机：把经典函数变成量子黑箱](https://daiw.org/manual/quantum-computing/oracle)。

## 参考文献

- [1] Qiskit 官方文档.「Statevector.equiv / Operator.equiv.」https://docs.quantum.ibm.com/api/qiskit/quantum_info —— 相等判断 API。
- [2] Qiskit.「Test your quantum programs.」https://docs.quantum.ibm.com/guides —— 测试实践。
- [3] Scott Aaronson.「How to verify a quantum computation.」—— 量子验证的理论困境综述。
