1. 量子算法基准测试环境搭建与版本锁定

为什么基准测试环境必须先“锁死”

量子算法的基准结果对运行环境极其敏感:同一段随机线路,在 Qiskit 的 AerSimulator、Cirq 的 cirq.Simulator 与 PennyLane 的 default.qubit 上,若随机种子、门分解规则、全局相位约定或浮点精度不同,输出态矢量的数值就会有差异。基准测试要回答的是“算法本身好不好”,而不是“环境漂移了多少”,因此第一步是把框架版本、模拟器后端、随机种子与比较容差全部固定下来,形成一份可复现的环境清单。

环境清单与版本锁定策略

本例采用“三框架 + 统一随机线路 + 统一比较口径”的最小可复现方案。核心约束如下:

  • 版本锁定:用 requirements-bench.txt 精确固定 qiskit==1.2.4、cirq-core==1.4.1、pennylane==0.38.0,避免 >= 带来的隐式升级。
  • 后端锁定:Qiskit 用 AerSimulator(method="statevector"),Cirq 用 cirq.Simulator(dtype=np.complex128),PennyLane 用 default.qubit,三者均为无噪声理想态矢量模拟。
  • 随机性锁定:线路结构由固定种子 SEED=20240501 生成,各框架的采样种子单独固定,保证线路本身可重建。
  • 比较口径:以 Qiskit 输出态为参考,比较各框架态矢量的全局相位对齐后保真度 F = |⟨ψ_ref|ψ⟩|²,通过阈值设为 1 - 1e-9。
全局相位不影响物理可观测量,但会直接破坏逐元素比较。因此比较前必须做相位对齐:取参考态与目标态内积的相位因子 e^{iθ},将目标态乘以 e^{-iθ} 后再算保真度。
基准环境锁定与跨框架一致性验证 环境锁定清单 qiskit==1.2.4 cirq-core==1.4.1 pennylane==0.38.0 numpy==1.26.4 后端:statevector 理想模拟 SEED = 20240501 容差:F ≥ 1 − 1e−9 统一随机线路 n_qubits = 4 depth = 6 门集:H / RX / RZ / CX 结构由 SEED 生成 初态 |0000⟩ 输出:16 维复态矢量 (同一线路三处重建) 三框架独立执行 Qiskit AerSimulator statevector Cirq cirq.Simulator complex128 PennyLane default.qubit analytic 各自输出 16 维复态矢量 ψ_q / ψ_c / ψ_p 记录版本号、后端名、dtype 到清单 任一版本不符即中止基准 相位对齐 θ = arg⟨ψ_ref|ψ⟩ ψ′ = ψ · e^(−iθ) 消除全局相位差异 参考态取 Qiskit 输出 保真度判定 F_qc = |⟨ψ_q|ψ_c′⟩|² F_qp = |⟨ψ_q|ψ_p′⟩|² 通过条件:F ≥ 1 − 1e−9 否则标记环境不一致并排查 一致性通过 → 环境基线成立,可进入后续门保真度、纠缠保真度等基准;不一致 → 先查版本、后端 method、dtype 与相位约定

可复现的环境锁定文件

把版本与后端配置写成文件,是让“环境”本身成为可版本管理的对象。下面这份清单可直接作为基准测试的起点:

# requirements-bench.txt
qiskit==1.2.4
qiskit-aer==0.15.1
cirq-core==1.4.1
pennylane==0.38.0
numpy==1.26.4

# env_lock.json(由脚本生成并随结果一起归档)
{
  "seed": 20240501,
  "n_qubits": 4,
  "depth": 6,
  "backends": {
    "qiskit": {"simulator": "AerSimulator", "method": "statevector"},
    "cirq":   {"simulator": "cirq.Simulator", "dtype": "complex128"},
    "pennylane": {"device": "default.qubit", "shots": null}
  },
  "tolerance": 1e-9
}

同一随机线路在三框架中的构建与比较

关键实现点在于:线路结构只生成一次(用固定种子),然后分别翻译成三种框架的门序列,而不是各自独立随机。下面代码给出完整可运行路径,输出为各框架态矢量与两两保真度。

import json, numpy as np

SEED = 20240501
N_QUBITS = 4
DEPTH = 6
TOL = 1e-9

# ---------- 1. 用固定种子生成统一线路描述 ----------
rng = np.random.default_rng(SEED)
ops = []  # 每项: ("h", q) / ("rx", q, theta) / ("rz", q, theta) / ("cx", c, t)
for layer in range(DEPTH):
    for q in range(N_QUBITS):
        g = rng.choice(["h", "rx", "rz"])
        if g == "h":
            ops.append(("h", q))
        else:
            ops.append((g, q, float(rng.uniform(0, 2*np.pi))))
    for q in range(0, N_QUBITS - 1, 2):
        ops.append(("cx", q, q + 1))

# ---------- 2. Qiskit ----------
from qiskit import QuantumCircuit
from qiskit.quantum_info import Statevector
qc = QuantumCircuit(N_QUBITS)
for op in ops:
    if op[0] == "h":   qc.h(op[1])
    elif op[0] == "rx": qc.rx(op[2], op[1])
    elif op[0] == "rz": qc.rz(op[2], op[1])
    else:               qc.cx(op[1], op[2])
psi_q = np.asarray(Statevector.from_instruction(qc).data, dtype=complex)

# ---------- 3. Cirq ----------
import cirq
qubits = cirq.LineQubit.range(N_QUBITS)
circuit = cirq.Circuit()
for op in ops:
    if op[0] == "h":   circuit.append(cirq.H(qubits[op[1]]))
    elif op[0] == "rx": circuit.append(cirq.rx(op[2])(qubits[op[1]]))
    elif op[0] == "rz": circuit.append(cirq.rz(op[2])(qubits[op[1]]))
    else:               circuit.append(cirq.CNOT(qubits[op[1]], qubits[op[2]]))
psi_c = cirq.Simulator(dtype=np.complex128).simulate(circuit).final_state_vector

# ---------- 4. PennyLane ----------
import pennylane as qml
dev = qml.device("default.qubit", wires=N_QUBITS, shots=None)

@qml.qnode(dev)
def pl_circuit():
    for op in ops:
        if op[0] == "h":   qml.Hadamard(wires=op[1])
        elif op[0] == "rx": qml.RX(op[2], wires=op[1])
        elif op[0] == "rz": qml.RZ(op[2], wires=op[1])
        else:               qml.CNOT(wires=[op[1], op[2]])
    return qml.state()

psi_p = np.asarray(pl_circuit(), dtype=complex)

# ---------- 5. 相位对齐 + 保真度 ----------
def fidelity(ref, vec):
    phase = np.vdot(ref, vec)
    if abs(phase) < 1e-15:
        return 0.0
    aligned = vec * np.exp(-1j * np.angle(phase))
    return float(abs(np.vdot(ref, aligned)) ** 2)

f_qc = fidelity(psi_q, psi_c)
f_qp = fidelity(psi_q, psi_p)
print(f"F(Qiskit, Cirq)      = {f_qc:.12f}")
print(f"F(Qiskit, PennyLane) = {f_qp:.12f}")
print("PASS" if min(f_qc, f_qp) >= 1 - TOL else "FAIL: 环境不一致")
若保真度低于阈值,优先排查三类原因:① 某框架默认把 rz 分解为含全局相位的门序列;② Cirq 的 dtype 未显式设为 complex128 导致精度下降;③ 线路翻译时 CX 控制位与目标位顺序写反。这三类问题都会让态矢量“看起来对、数值不对”。

结果判读与基线归档

在理想态矢量模拟下,三框架对同一线路的保真度应达到机器精度量级(示例中预期 F 与 1 的偏差在 1e−12 以内,具体数值随平台浮点实现略有差异,属于预期现象而非实测结论)。判定规则如下:

检查项通过条件不通过时的动作
版本一致性三框架版本与 requirements-bench.txt 完全一致重建虚拟环境,禁止就地升级
后端一致性均为无噪声态矢量模拟,shots 为 None检查是否误用了含噪声或采样后端
态矢量保真度F ≥ 1 − 1e−9按相位、dtype、CX 顺序逐项排查
清单归档env_lock.json 与结果同目录保存补写清单,否则结果不可复现

把 env_lock.json、随机线路描述 ops 的序列化结果与保真度输出一起归档,就得到了一份“环境基线”。后续任何基准测试只要复用这份基线,就能把性能差异归因到算法或噪声模型,而不是环境漂移。