Qiskit 1.0.0升级指南:如何用transpile和run替换execute函数(附完整代码示例)
Qiskit 1.0.0升级实战:从execute到transpile与run的平滑迁移
量子计算开发者们注意了!Qiskit 1.0.0版本的发布带来了API的重大变革,其中最引人关注的莫过于execute函数的退役。这个曾经是我们量子电路执行"万能钥匙"的函数,现在被更模块化的transpile和run组合所取代。本文将带你深入理解这一变化背后的设计哲学,并提供可立即投入使用的迁移方案。
1. 为什么Qiskit要弃用execute函数?
在Qiskit的早期版本中,execute函数就像是一个黑盒子——你把量子电路丢进去,它帮你处理所有事情:电路优化、后端适配、作业提交。这种设计虽然方便初学者快速上手,但却隐藏了量子计算工作流中的关键步骤,导致开发者对底层过程缺乏控制。
新版本将执行流程明确拆分为两个阶段:
- transpile阶段:负责电路的优化和适配特定后端
- run阶段:处理实际的作业提交和结果获取
这种分离带来了几个显著优势:
- 更精细的控制:可以单独调整transpilation参数或运行参数
- 更高的透明度:清楚地知道电路在提交前经历了哪些转换
- 更好的性能:避免了每次执行都重复进行相同的transpilation
# 旧版单步执行模式 result = execute(circuit, backend, shots=1000).result() # 新版两步执行模式 transpiled_circuit = transpile(circuit, backend) result = backend.run(transpiled_circuit, shots=1000).result()2. 基础迁移:从execute到transpile+run
让我们从一个简单的量子电路开始,展示如何将旧代码迁移到新API。假设我们有一个创建Bell态的电路:
from qiskit import QuantumCircuit from qiskit_aer import Aer # 创建量子电路 qc = QuantumCircuit(2, 2) qc.h(0) qc.cx(0, 1) qc.measure([0, 1], [0, 1])2.1 旧版execute实现
# 旧版代码(Qiskit <1.0.0) backend = Aer.get_backend('qasm_simulator') job = execute(qc, backend, shots=1024) result = job.result() counts = result.get_counts()2.2 新版transpile+run实现
# 新版代码(Qiskit ≥1.0.0) backend = Aer.get_backend('qasm_simulator') # 第一步:transpile电路 transpiled_qc = transpile(qc, backend) # 第二步:运行电路 job = backend.run(transpiled_qc, shots=1024) result = job.result() counts = result.get_counts()关键变化点:
execute函数被拆分为transpile和run两个独立调用run现在是后端对象的方法,而不是全局函数- transpile后的电路可以重复使用,提高效率
3. 高级迁移技巧与参数映射
许多开发者在使用execute时会传递各种参数来控制电路执行。下面我们来看看这些参数在新API中如何映射:
| execute参数 | 新版位置 | 注意事项 |
|---|---|---|
shots | run方法 | 保持不变 |
optimization_level | transpile函数 | 优化级别0-3 |
initial_layout | transpile函数 | 量子比特映射 |
seed_simulator | run的noise_model参数 | 需要创建噪声模型 |
coupling_map | transpile函数 | 硬件拓扑约束 |
# 带参数的复杂示例 backend = Aer.get_backend('qasm_simulator') # transpile阶段参数 transpiled_qc = transpile( qc, backend, optimization_level=3, coupling_map=[[0, 1], [1, 0]], initial_layout=[0, 1] ) # run阶段参数 noise_model = NoiseModel().from_backend(backend) job = backend.run( transpiled_qc, shots=5000, noise_model=noise_model, seed_simulator=42 )注意:
basis_gates参数现在也属于transpile阶段,用于指定目标后端支持的基本门集合。
4. 常见问题与调试技巧
在迁移过程中,开发者可能会遇到一些典型问题。以下是几个常见场景及其解决方案:
4.1 结果不一致问题
现象:迁移后结果统计与旧版不同
原因:transpile优化改变了电路结构
解决方案:
- 比较transpile前后的电路:
print(f"原始电路:\n{qc.draw()}") print(f"Transpile后电路:\n{transpiled_qc.draw()}") - 固定随机种子保证可重复性:
from qiskit.utils import algorithm_globals algorithm_globals.random_seed = 42
4.2 性能下降问题
现象:同样的电路执行时间变长
原因:每次运行都重新transpile
解决方案:
- 缓存transpile结果:
# 首次运行 transpiled_qc = transpile(qc, backend) # 后续运行直接使用transpile结果 job = backend.run(transpiled_qc, shots=1000)
4.3 自定义pass manager集成
对于高级用户,新版API提供了更灵活的pass manager集成方式:
from qiskit.transpiler import PassManager from qiskit.transpiler.passes import Unroller # 创建自定义pass manager pm = PassManager([Unroller(['u3', 'cx'])]) # 应用自定义transpile transpiled_qc = transpile(qc, backend, pass_manager=pm)5. 实际项目迁移案例
让我们看一个完整的项目迁移示例,计算分子基态能量的VQE算法:
5.1 旧版实现
from qiskit.algorithms import VQE from qiskit.algorithms.optimizers import SPSA from qiskit.circuit.library import TwoLocal from qiskit.opflow import PauliSumOp # 创建哈密顿量 hamiltonian = PauliSumOp.from_list([("ZZ", 1.0), ("II", -0.5)]) # 创建ansatz ansatz = TwoLocal(2, rotation_blocks="ry", entanglement_blocks="cz") # 创建VQE实例 vqe = VQE(ansatz, optimizer=SPSA(), quantum_instance=backend) # 运行计算 result = vqe.compute_minimum_eigenvalue(hamiltonian)5.2 新版实现
from qiskit.algorithms.minimum_eigensolvers import VQE from qiskit.algorithms.optimizers import SPSA from qiskit.circuit.library import TwoLocal from qiskit.quantum_info import SparsePauliOp from qiskit.primitives import Estimator # 创建哈密顿量(新版Operator格式) hamiltonian = SparsePauliOp.from_list([("ZZ", 1.0), ("II", -0.5)]) # 创建ansatz ansatz = TwoLocal(2, rotation_blocks="ry", entanglement_blocks="cz") # 创建Estimator(新版Primitives接口) estimator = Estimator() # 创建VQE实例 vqe = VQE(estimator, ansatz, SPSA()) # 运行计算 result = vqe.compute_minimum_eigenvalue(hamiltonian)关键改进:
- 使用新的
Primitives接口替代旧的QuantumInstance - Operator表示更改为
SparsePauliOp - 算法接口更加模块化和可组合
6. 迁移后的最佳实践
完成API迁移后,建议采用以下实践来提升代码质量和性能:
电路缓存:对不变电路只transpile一次
from functools import lru_cache @lru_cache(maxsize=32) def get_transpiled_circuit(circuit, backend): return transpile(circuit, backend)异步执行:利用新版对异步的更好支持
async def run_async(circuit, backend, shots): transpiled = transpile(circuit, backend) job = await backend.run(transpiled, shots=shots) return await job.result()性能分析:使用transpile日志识别瓶颈
import logging logging.basicConfig(level=logging.INFO) transpiled_qc = transpile(qc, backend) # 查看详细优化日志自定义transpile:针对特定硬件优化
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager pm = generate_preset_pass_manager(3, backend) transpiled_qc = pm.run(qc)
在实际项目中,我发现将transpile阶段与业务逻辑分离可以显著提高代码可维护性。通常我会创建一个专门的circuit_utils.py文件来集中处理所有与transpile相关的逻辑。
