3. 实验环境配置与版本控制
量子电路实验的复现,第一步不是写量子门,而是把“运行环境”本身固定下来。同一段 Qiskit 代码,在 Qiskit 0.45 与 1.0 上可能因为 transpile 参数、默认优化级别或后端接口变化而得到不同的电路结构;同一份 requirements.txt,在不同操作系统上也可能因为二进制依赖解析出不同版本。因此,可复现实验环境的核心目标是:让任何人、在任何时间、任何机器上,都能重建出与原始实验一致的软件栈。这需要同时管理三层内容:Python 解释器版本、Qiskit 及其生态包的精确版本、以及依赖解析工具本身。
一、环境分层:解释器、包管理器与依赖锁
建议把环境拆成三个可独立控制的层次。最底层是 Python 解释器,量子计算生态对版本较敏感,例如 Qiskit 1.x 要求 Python 3.8 及以上,而某些旧版 qiskit-aer 对 3.11 的支持存在滞后,因此应在项目文档中明确写出 python=3.10 这类约束。中间层是依赖管理工具,负责把“我用了哪些包”解析成“具体安装哪些版本”。最上层是锁文件,记录解析后的精确版本与哈希,是复现的真正依据。
常见工具有三类组合:pip + venv + requirements.txt 最轻量,但 requirements.txt 默认只记录直接依赖,间接依赖会浮动;pipenv 通过 Pipfile 声明直接依赖、Pipfile.lock 锁定全部依赖树,适合中小项目;conda(或 mamba)擅长管理包含非 Python 二进制依赖的科学计算栈,配合 environment.yml 可同时锁定 Python 版本与包版本。对量子实验项目,推荐 conda 管理解释器 + pip 安装 Qiskit 生态 的混合方式,因为 Qiskit 更新快,pip 源通常比 conda 频道更及时。
二、用 conda 固定解释器,用 pip 锁定 Qiskit
下面给出一个可直接参考的 environment.yml 示例。它声明 Python 3.10,并通过 pip 子节安装 Qiskit 生态。注意 qiskit 在 1.0 之后拆分为 qiskit 元包与 qiskit-aer、qiskit-ibm-runtime 等独立包,锁定时应分别写版本,而不是只锁元包。
name: qexp-repro
channels:
- conda-forge
dependencies:
- python=3.10.13
- pip=23.3.1
- pip:
- qiskit==1.0.2
- qiskit-aer==0.14.1
- qiskit-ibm-runtime==0.21.1
- numpy==1.26.4
- matplotlib==3.8.3
- pylatexenc==2.10
创建与激活:
conda env create -f environment.yml
conda activate qexp-repro
python -c "import qiskit, qiskit_aer; print(qiskit.__version__, qiskit_aer.__version__)"
预期输出形如 1.0.2 0.14.1。这一步是环境自检:如果版本与锁文件不符,说明解析过程被本地缓存或频道优先级干扰,应清理后重建,而不是“先跑起来再说”。
三、pipenv 路线与锁文件生成
若项目不涉及复杂二进制依赖,pipenv 更简洁。初始化后安装并锁定:
pipenv --python 3.10
pipenv install qiskit==1.0.2 qiskit-aer==0.14.1
pipenv lock
pipenv sync --dev
Pipfile 记录直接依赖,Pipfile.lock 记录完整依赖树与哈希。pipenv sync 严格按锁文件安装,是复现时应使用的命令;pipenv install 则可能重新解析并升级间接依赖,不适合复现场景。团队协作时,Pipfile.lock 必须提交到版本库,且代码评审时把它当作“实验参数”一样审查。
四、版本控制策略:把环境当代码管理
环境文件应纳入 Git,并与实验代码同仓管理。推荐目录结构:仓库根目录放 environment.yml、Pipfile、Pipfile.lock、requirements.txt(由 pip freeze 导出作为兜底),实验代码按 experiments/ 分目录,每个实验目录内放 README.md 记录该实验使用的环境文件版本(可用 Git commit 短哈希标注)。
关键实践有三点。第一,提交锁文件而非仅提交声明文件,因为声明文件允许浮动,锁文件才可逐字节复现。第二,为环境变更单独提交,例如“升级 qiskit-aer 至 0.14.1”应是一个独立 commit,便于用 git bisect 定位某次结果变化是否由依赖升级引起。第三,在实验输出中记录环境指纹,例如在结果 JSON 中写入 qiskit.__version__、qiskit_aer.__version__、Python 版本与 Git commit,这样即使环境文件丢失,也能从结果反查当时软件栈。
import sys, platform, subprocess
import qiskit, qiskit_aer
def env_fingerprint():
try:
commit = subprocess.check_output(
["git", "rev-parse", "--short", "HEAD"], text=True).strip()
except Exception:
commit = "unknown"
return {
"python": sys.version.split()[0],
"platform": platform.platform(),
"qiskit": qiskit.__version__,
"qiskit_aer": qiskit_aer.__version__,
"git_commit": commit,
}
五、常见陷阱与判断依据
第一类陷阱是“本地能跑、别人装不上”,多因锁文件未提交或 requirements.txt 只写了直接依赖。判断方法:在干净容器中仅用锁文件安装,若失败或版本漂移,即说明锁定不完整。第二类陷阱是 conda 与 pip 混装导致同一包出现两份,可用 conda list 与 pip list 交叉比对同名包版本,若不一致应统一由一层管理。第三类陷阱是 Qiskit 后端默认值随版本变化,例如 transpile 的 optimization_level 默认值或 AerSimulator 的 method 默认行为,复现时应显式写出这些参数,而不是依赖默认值。
最后给出一个可执行的验收标准:在一台未安装过量子计算包的机器上,按 README 执行“创建环境 → 安装锁文件 → 运行自检脚本”,自检脚本打印的版本指纹与原始实验记录完全一致,且同一量子电路在 AerSimulator 上运行得到的计数分布(在固定 seed_simulator 下)逐位相同,方可认为环境复现成功。环境配置不是一次性任务,而是与实验代码一同演进、一同版本化的工程资产。