线路执行与工具流程
概述
本页整合了线路执行、结果查看、线路分析与绘图、以及 OpenQASM 导入导出的核心用法,适合作为完整工作流的快速参考。
执行线路与查看结果
普通用户直接调用 Circuit.execute(),返回 ExecutionResult 对象:
from unitarylab import Circuit
qc = Circuit(2)
qc.h(0)
qc.cx(0, 1)
result = qc.execute()ExecutionResult 核心字段与方法
result.num_qubits:结果所表示的量子比特数。result.shots:本次线路执行的 shots 数。result.state:最终状态向量,只读的一维 NumPy 数组(每次访问都会从后端状态实时转换,不做缓存);多 shots 执行时表示最后一个 shot 的状态。result.backend_state:底层后端原始状态对象(如 GPU 上的 torch 张量),未做设备拷贝,性能敏感场景可直接使用。result.probabilities:包含零概率结果的完整计算基概率分布字典,键为 little-endian 二进制字符串,值为对应概率。result.probability(bitstring, qubits=None):查询单个计算基态结果的概率。result.marginal_probabilities(qubits=None, threshold=0.0):指定子集比特的边际概率分布;传入正阈值可省略更小的值。result.sample(shots=1, qubits=None, seed=None):不坍缩状态,按概率分布采样若干计算基态结果,返回字符串列表。result.expectation(observable, qubits=None):计算可观测量期望值 ,支持"Z"、"XX"、完整长度 Pauli 串、或(系数, 算符, 比特)项列表等多种输入形式。result.measure(target_indices, seed=None):对指定比特执行投影测量并坍缩内部状态,返回测量结果字符串;这个种子独立于Circuit.execute(seed=...)。result.counts:汇总所有 shots 的经典比特串计数。result.classical_results_map:最后一个 shot 的经典比特索引到测量值(0或1)的字典,仅在线路中添加了measure操作后才有值。result.classical_registers:按寄存器名分组的最后一个 shot 测量结果。
print(result.state)
# [0.70710678+0.j 0.+0.j 0.+0.j 0.70710678+0.j]
print(result.probabilities)
# {'00': 0.4999..., '10': 0.0, '01': 0.0, '11': 0.4999...}
print(result.expectation("ZZ", qubits=(0, 1)))
# Bell 态下 ZZ 期望值应接近 1(两比特完全关联)Pauli 字符串中的字符按给定的 qubits 顺序逐一对应量子比特;省略 qubits 时,第一个字符作用于 qubit 0,第二个字符作用于 qubit 1,以此类推。该算符顺序不同于概率字符串的显示顺序:概率字符串中 qubit 0 位于最右侧。
后端、设备与执行参数
execute() 支持 backend、device、dtype、shots、seed、backend_options 参数:
result = qc.execute(backend='torch', device='cpu')| 参数组合 | 适用场景 |
|---|---|
backend='torch', device='cpu'(默认) | 适合大多数情况 |
backend='torch', device='gpu' | 使用兼容的 CUDA 或 Apple MPS PyTorch 环境加速 |
backend='numpy', device='cpu' | 纯 NumPy 环境 |
backend='cpp', device='cpu' | 使用编译的 C++ 门核加速 CPU 执行 |
backend='cpp', device='gpu' | 使用可选 CUDA C++ 后端;需安装 unitarylab[cuda] |
backend='tensornet', device='cpu' | 大比特数、低纠缠线路,使用 MPS 表示 |
通常直接调用 qc.execute() 无需指定参数;Torch 可使用兼容的 CUDA 或 Apple MPS GPU。安装 unitarylab[cuda] 后,C++ 后端也可通过 device='gpu' 使用 CUDA;该 extra 支持 Linux x86_64、Linux aarch64 服务器环境和 Windows x86-64,会安装适配平台的 unitarylab-cu12 wheel。运行需要兼容的 NVIDIA 驱动,但不要求本地安装 CUDA Toolkit。若可选 CUDA 扩展未安装或无法加载,请改用 Torch GPU、C++ CPU、Torch CPU 或 NumPy。比特数很大但纠缠有限时可尝试 backend='tensornet' 并通过 backend_options 控制 max_bond/cutoff/routing。seed 默认固定为 42(结果可复现),需要真正随机采样时传 seed=None。参数完整说明见 快速开始 与 核心线路接口。
密度矩阵与噪声模型
需要模拟混态或噪声时,使用 Circuit.execute_density()。这是独立于 execute() 状态向量后端的密度矩阵接口:device="cpu" 使用原生 cppdensity 后端;安装 CUDA extra 后,device="gpu"、"cuda" 或 "cuda:N" 使用 CUDA 密度矩阵后端。
import numpy as np
from unitarylab import Circuit
qc = Circuit(2)
qc.h(0)
qc.cx(0, 1)
result = qc.execute_density(
initial_state=None,
device="cpu",
dtype=np.complex128,
)
print(result.state) # shape: (2**n, 2**n)
print(result.trace) # 约等于 1
print(result.purity) # Bell 纯态为 1
print(result.probabilities) # {'00': 0.5, '10': 0.0, '01': 0.0, '11': 0.5}
print(result.expectation("ZZ"))initial_state 可以是 None、状态向量、密度矩阵或 DensityMatrixState。返回的 DensityMatrixResult 提供 state、backend_state、trace、purity、probabilities、probability()、marginal_probabilities() 和 expectation()。当前密度矩阵后端支持 unitary 演化,但不支持电路测量、状态坍缩或 shots/counts。
内置单量子比特噪声通道包括 BitFlip、PhaseFlip、DepolarizingNoise、AmplitudeDamping 和 PhaseDamping。使用 NoiseOperator 绑定门或目标比特规则,再将 NoiseModel 传给 execute_density():
from unitarylab.backend import (
AmplitudeDamping,
BitFlip,
NoiseModel,
NoiseOperator,
)
noise_model = NoiseModel([
NoiseOperator(BitFlip(0.02), gate_ids=("x", "rx")),
NoiseOperator(AmplitudeDamping(0.05), qubits=(1,)),
])
noisy_result = qc.execute_density(noise_model=noise_model)规则固定在匹配的 primitive gate 之后应用。NoiseOperator 的 qubits 过滤条件仅匹配门的目标量子比特,不匹配控制量子比特;对于多目标门,匹配的单量子比特通道会独立应用于每个匹配目标。gate_ids=None 匹配所有实体 primitive gate,qubits=None 表示对匹配门的每个目标应用通道。恒等门(i)和全局相位门(gp、gphase、global_phase)不会触发噪声。当前不支持公开的自定义 Kraus 通道、门前噪声、相关多比特噪声或 readout error。
优化、编译与硬件提交
Circuit 提供常用的优化、门展开、mapping 和硬件提交入口:
from unitarylab import Circuit
TOKEN = "your-provider-token"
qc = Circuit(2)
qc.h(0)
qc.cx(0, 1)
optimized = qc.optimize(optimization_level=1)
transpiled = qc.transpile(basis="default")
# 编译到目标硬件格式,但不提交任务
compiled = qc.compile(provider="guodun", token=TOKEN)
# 编译并提交真实硬件任务
hardware_result = qc.submit(
provider="guodun",
machine="gd_qc1", # 国盾量子“骁鸿一号”
token=TOKEN,
shots=1024,
)optimize() 和 transpile() 返回 Circuit;compile() 返回 CompilationResult,包含映射后线路和 QCIS,但不会提交任务或消耗任务积分;submit() 会提交硬件任务,并统一返回 HardwareResult。传入 machine 可选择目标机器。Guodun 和 Tianyan 省略 machine(或传入 None)时会查询机器列表并自动选择;未匹配首选别名时使用列表中的第一台,例如 gd_qc1 对应国盾量子“骁鸿一号”。其他硬件适配器通常要求显式指定 machine。
compile() 仅支持 local、guodun 和 tianyan;其他 provider adapter 会在 submit() 中完成编译。真机编译和提交需要 provider 凭据,可以通过 token 显式传入。省略时,Guodun 和 Tianyan 分别回退到 GUODUN_LOGIN_KEY 和 TIANYAN_LOGIN_KEY;高级 QCompiler 配置也可使用 hardware.login_key。
需要以 OpenQASM 为输入或精细配置各阶段时,可直接使用顶层延迟导出的 QCompiler:
from unitarylab import QCompiler
# 无高级配置时直接构造;也可传入 CompileConfig、配置映射或配置文件路径
compiler = QCompiler()
optimized = compiler.optimize(circuit_or_qasm)
transpiled = compiler.transpile(qc, basis="default")
compiled = compiler.compile(circuit_or_qasm, provider="guodun", token=TOKEN)
hardware_result = compiler.submit(
circuit_or_qasm,
provider="guodun",
machine="gd_qc1",
token=TOKEN,
shots=1024,
)底层产物流为 raw_qasm -> optimized_qasm -> mapped_qcis -> hardware_result。Guodun/Tianyan 与其他厂商均由 QCompiler.submit() 内部分流。LQCloud 需要单独安装 lqcloud SDK,QPanda3 需要 pyqpanda3,Quafu 需要 QuarkStudio(quarkstudio);缺少所需 SDK 时会在提交阶段报错。
线路分析与绘图
draw()
qc.draw() # 弹出图窗(默认 output='mpl')
qc.draw(filename='circuit.png') # 保存为文件
qc.draw(filename='circuit.png', title='Bell State') # 添加标题
qc.draw(output='text') # 纯文本线路图
qc.draw(output='latex') # LaTeX(quantikz)线路图
qc.draw(compact=False) # 关闭相邻单比特门合并在 Jupyter Notebook 中图形会内联显示,在普通脚本中会弹出独立窗口。output 支持 'mpl'(默认,Matplotlib)、'text'、'latex' 三种模式,分别对应内部的 MatplotlibCircuit、TextCircuitDrawer、LatexCircuitDrawer。
CircuitInfo
CircuitInfo 提供线路静态分析,可通过 qc.analyze() 快捷调用:
from unitarylab.circuit_analysis import CircuitInfo
info = CircuitInfo(qc) # 等价于 info = qc.analyze(show=False)
info.show() # 打印概览、指令列表、层结构常用方法:
| 方法 | 说明 |
|---|---|
size() | 门总数 |
depth() | build_layers() 贪心分组产生的无比特冲突层数;它是静态并行分组指标,不等同于严格保持原门顺序的数据依赖最长路径 |
count_ops() | 各门类型出现次数,返回 dict |
count_single_qubit_gates() / count_two_qubit_gates() / count_multi_qubit_gates() | 按比特数分类的门计数 |
count_parameterized_gates() | 带参数(旋转角等)的门数量 |
get_qubit_usage() | 各量子比特的操作次数 |
get_coupling_map() | 量子比特耦合关系(双比特门连接对),例如 [(0, 1), (1, 2)] |
get_qubit_history(qubit) | 指定比特的完整操作历史 |
get_instructions() / get_layers() / get_parameters() | 分别返回指令列表、按 build_layers() 贪心规则分组的门、参数化门的参数汇总 |
is_parameterized() | 线路是否包含参数化门 |
get_summary() / to_dict() | 结构化概览 / 将分析结果整体导出为字典 |
show(sections=None, qubit=None) | 按章节打印分析结果 |
show() 支持的 sections 值:'overview'(或别名 'summary')、'instructions'、'layers'、'qubit_usage'、'coupling_map'、'parameters'、'qubit_history'(需同时指定 qubit= 参数)。不传 sections 时默认打印 ['overview', 'instructions', 'layers']。
info.show(sections=['overview', 'coupling_map'])
info.show(sections='qubit_history', qubit=0)OpenQASM 导入导出
OpenQASM 2.0 / 3.0 均受支持,适合保存、交换线路,或与其他工具互操作。公开方法与参数速查见 核心线路接口;这里重点说明底层接口和兼容性限制。
# 使用前文已经构建的 qc;默认导出 OpenQASM 3.0
qasm3_str = qc.to_qasm()
qasm2_str = qc.to_qasm2()
# 导入(自动识别 2.0 / 3.0)
qc2 = Circuit.from_qasm(qasm3_str)底层函数(高级用法)
Circuit.to_qasm() / from_qasm() 内部委托给 unitarylab.backend.qasm 模块的一组函数,直接操作 GateSequence 而非 Circuit。仅在需要脱离 Circuit 单独处理 GateSequence,或需要显式指定寄存器结构时才需要直接调用:
from unitarylab.backend.qasm import (
gate_sequence_to_qasm, # 统一入口,默认导出 QASM 3.0
gate_sequence_to_qasm2,
gate_sequence_to_qasm3,
gate_sequence_from_qasm, # 统一入口,自动识别版本
qasm2_to_gate_sequence,
qasm3_to_gate_sequence,
circuit_from_gate_sequence, # 将 GateSequence 重建为 Circuit
)
qasm_str = gate_sequence_to_qasm3(qc.gate_sequence, qreg_name='q')
gs = qasm3_to_gate_sequence(qasm_str)
qc3 = circuit_from_gate_sequence(Circuit, gs)支持范围与限制
转换器覆盖常用单比特门、参数门、受控门、SWAP、测量和自定义 gate 定义。QASM 3.0 还使用 ctrl @ / negctrl @ 修饰符表示正、负控制;QASM 2.0 会按其可表示范围改写受控门。具体可往返的语法和门集合以两个转换模块 unitarylab.backend.qasm.qasm2、unitarylab.backend.qasm.qasm3 的实现为准,两种版本并非拥有完全相同的表示能力。
主要限制:
- 同时支持 OpenQASM 2.0 和 3.0,默认导出/自动识别均以 3.0 为准;需要 2.0 时显式调用
to_qasm2()/gate_sequence_to_qasm2()。 - 门参数必须是数值,不支持符号表达式(如
pi/2会被数值化)。 - 不支持经典控制流(
if、while等)。 - 并非所有自定义门都能完整往返导出;非连续块门会内联展开。
- 携带矩阵信息的
unitary门(即通过qc.unitary(matrix, target)添加、g.matrix属性非空的门)会以 UnitaryLab 扩展语句形式导出(矩阵元素编码为交替的实部/虚部参数),并可通过from_qasm()完整还原——这是 UnitaryLab 专属扩展,并非标准 OpenQASM 3.0 互操作格式,若目标是导入第三方工具,仍建议先transpile()。只有当unitary门的矩阵信息缺失(g.matrix is None,一般出现在部分中间处理路径)时,to_qasm()才会抛出NotImplementedError并在错误信息中给出重试建议(transpile=True或decompose=True, transpile=True)。
综合示例
以下是一个包含执行、查看结果、分析、绘图和导出 OpenQASM 的最小完整示例:
from unitarylab import Circuit
# 1. 创建 Bell 线路
qc = Circuit(2)
qc.h(0)
qc.cx(0, 1)
# 2. 执行线路
result = qc.execute()
# 3. 查看概率
print(result.probabilities)
# {'00': 0.4999..., '10': 0.0, '01': 0.0, '11': 0.4999...}
# 4. 线路分析
qc.analyze(show=True, sections=["summary"])
# 5. 绘图
qc.draw(title="Bell State")
# 6. 导出 OpenQASM 3.0(详细说明见上文)
qasm_str = qc.to_qasm()注意事项
- Qubit 顺序:
probabilities的键为 little-endian 二进制字符串,最右位对应 qubit 0;默认结果包含零概率项。 - 投影测量会坍缩状态:
result.measure()会修改内部状态(投影坍缩)。如需在同一个未坍缩状态上重复查询不同子系统的分布,使用result.marginal_probabilities()或result.sample(),它们都不会修改result.state;只有result.measure()会坍缩。 - GPU 与后端:默认设置适用于大多数场景,仅在性能调优时需要调整
backend、device、dtype、backend_options。 - 编译与提交不同:
compile()只生成目标硬件产物,submit()才会提交真实硬件任务。 - MDX 特殊字符:在 MDX 文件中,表格单元格内容中的
|应写为|;{}、\等特殊字符应放入代码块或行内代码。