可视化:线路图、测量统计图与时序图¶
量子程序本身是"看不见"的:线路是一串指令文本、测量结果是上千个比特串、执行过程更是纳秒级的物理操作。UnifiedQuantum 的可视化工具箱把这三类信息变成看得见的图——线路图回答"我的程序长什么样",测量统计图回答"我测到了什么、离理论有多远",时序图回答"这段程序在硬件上要跑多久、哪里在空转"。
理论背景(测量概率与玻恩规则、叠加态为何测出均匀分布)见量子计算算法教程·量子计算基础。本页示例基于 unified-quantum 0.1.0(含 visualization 扩展),所有输出均为实际运行结果;涉及随机采样的结果每次运行略有不同。
本课知识点
可视化工具箱与安装——能安装
visualization扩展,解释三类可视化各自回答的问题并选用合适的 API。测量结果直方图——能用
plot_histogram把采样计数画成直方图,并写出十进制计数键到二进制串键的转换。概率分布与均匀参考线——能用
plot_distribution画概率分布,解释红色参考线 \(1/2^n\) 的含义并估计涨落范围。静态 HTML 线路图——能用
circuit_to_html生成带悬停提示的静态 SVG 线路图文件,并读懂图中各元素。门时序调度——能用
schedule_circuit配合gate_durations计算编译后线路的总时长,解释虚拟 Z 门零时长与左对齐调度。时间表与时间轴图——能读懂
create_time_line_table的时间表,用plot_time_line(_html)生成时间轴图并指出并行与空闲。
1. 可视化工具箱与安装¶
可视化依赖 matplotlib 与 pandas,它们是可选依赖,安装时带上 visualization 扩展一次装齐:
pip install "unified-quantum[visualization]"
装好后从 uniqc.visualization 导入本章用到的全部函数并验证版本:
import uniqc
from uniqc.visualization import (
circuit_to_html,
create_time_line_table,
format_result,
plot_distribution,
plot_histogram,
plot_time_line,
plot_time_line_html,
schedule_circuit,
)
print(uniqc.__version__)
0.1.0
按"回答的问题"把工具箱分成三格:
回答的问题 |
API |
输出形态 |
|---|---|---|
我测到了什么? |
|
matplotlib 图 |
线路长什么样? |
|
静态 HTML/SVG |
要跑多久? |
|
调度对象 / 表格 / PDF / HTML |
2. 测量结果直方图¶
用 GHZ 线路做例子:h(0) 制造叠加,两个 CNOT 把纠缠"串"到第三个比特。simulate_shots 返回"十进制整数 → 计数",而 plot_histogram 要的是"二进制串 → 概率",转换只需一行:
import matplotlib
matplotlib.use("Agg") # 纯脚本环境用 Agg;Jupyter 里可省略
import matplotlib.pyplot as plt
from uniqc import Circuit
from uniqc.simulator import Simulator
from uniqc.visualization import plot_histogram
circuit = Circuit()
circuit.h(0)
circuit.cnot(0, 1)
circuit.cnot(1, 2)
circuit.measure(0, 1, 2)
counts = Simulator().simulate_shots(circuit.originir, shots=1024)
n = circuit.qubit_num
probs = {format(int(k), f"0{n}b"): v / 1024 for k, v in counts.items()}
print(probs)
{'111': 0.5146484375, '000': 0.4853515625}
(计数来自随机采样,每次运行略有不同。)两根柱子各约 0.5,正是 GHZ 态 \(\frac{1}{\sqrt2}(\lvert000\rangle+\lvert111\rangle)\) 的理论预测。画图并保存:
plot_histogram(probs, title="GHZ state, 1024 shots")
plt.gcf().savefig("ghz-histogram.png", dpi=150, bbox_inches="tight")

三个使用要点:
键必须是等长二进制串(长度等于比特数)。整数键用
format(int(k), f"0{n}b")转换;注意最右侧字符对应c[0]——两比特线路x(0)后测量,占优的串是'01'。也可以直接传概率向量列表,如理想 GHZ 的
[0.5, 0, 0, 0, 0, 0, 0, 0.5],横轴标签会自动生成000…111。plot_histogram内部调用plt.show()(Jupyter 里直接内嵌显示);脚本里用plt.gcf().savefig(...)保存图片。
如果走统一执行入口,UnifiedResult.probabilities 的键本来就是二进制串,可以直接喂给 plot_histogram:
from uniqc import submit_task, wait_for_result
result = wait_for_result(submit_task(circuit, backend="dummy:local:simulator", shots=1024))
print(result.counts)
plot_histogram(result.probabilities, title="GHZ via dummy backend")
{'000': 512, '111': 512}
dummy 后端按理论概率确定性折算计数(恰为 512/512),而 simulate_shots 是按玻恩规则随机采样——所以上一张图的柱高有涨落、这里严格对半。真机测量结果的分布可视化见第 5 章。
3. 概率分布与均匀参考线¶
plot_distribution 的输入格式与 plot_histogram 完全相同,区别是会叠加一条均匀分布参考线 \(1/2^n\),一眼看出结果偏离"完全随机"有多远。对三比特均匀叠加态测量 2048 次:
from uniqc.visualization import plot_distribution
uni = Circuit()
uni.h(0)
uni.h(1)
uni.h(2)
uni.measure(0, 1, 2)
counts_u = Simulator().simulate_shots(uni.originir, shots=2048)
probs_u = {format(int(k), "03b"): v / 2048 for k, v in sorted(counts_u.items())}
print(probs_u)
{'000': 0.11865234375, '001': 0.1201171875, '010': 0.12109375, '011': 0.12939453125, '100': 0.13232421875, '101': 0.12890625, '110': 0.12109375, '111': 0.12841796875}
(每次运行略有不同。)画图:
plot_distribution(probs_u, title="Uniform superposition, 2048 shots")
plt.gcf().savefig("uniform-distribution.png", dpi=150, bbox_inches="tight")

红色虚线在 \(1/8 = 0.125\) 处,八根柱子都落在它附近——均匀叠加态 \(\lvert+\rangle^{\otimes 3}\) 的八种测量结果应当等概率。定量地说:每根柱子的期望计数是 \(2048\times\frac18 = 256\),标准差 \(\sqrt{2048\cdot\frac18\cdot\frac78}\approx 15\)(约 0.007 的概率幅度),上面观察到的波动都在一个标准差上下。
参考线的用途是快速判断"是否均匀":量子随机数要求分布贴着红线;Grover 这类搜索算法的答案峰则应当显著冒出红线;变分算法(如 VQE)靠对比不同迭代轮次的分布图判断收敛,见姊妹站 VQE 教程与本站第 7 章实操。
4. 静态 HTML 线路图¶
circuit_to_html 把线路渲染成一个静态 HTML/SVG 文件——不依赖任何可选库、不带 JavaScript,浏览器直接打开:
from uniqc.visualization import circuit_to_html
document = circuit_to_html(circuit, output_path="ghz-circuit.html", title="GHZ circuit")
print(len(document))
4112
返回值是完整 HTML 字符串(4112 字节),output_path 同时把它写入 ghz-circuit.html。渲染效果(本页把其中的 SVG 渲染成 PNG 嵌入):

读图:每条横线是一个量子比特的导线,方块是门——单比特门落在自己的导线上,双比特门用竖线连接两条导线,末端的 M 是测量。把鼠标悬停在任一门上,会弹出该门的比特、参数等原始信息;这是嵌在 SVG 里的原生提示,无需联网、无需插件,适合放进报告或网页。
两个近亲入口:
draw_html(circuit.originir, output_path=...):等价写法,第一个参数是线路文本(OriginIR 或 QASM,后者用language="QASM");draw(circuit.originir):在终端打印纯文本线路图,需要pip install "unified-quantum[originq]"(提供 pyqpanda3)后可用。
5. 门时序调度:这段程序要跑多久¶
逻辑线路里没有时间——"跑多久"取决于硬件上每个门实际消耗多少纳秒。schedule_circuit 做两件事:
编译到基门集(默认
{cz, sx, rz}):H 被分解为 RZ–SX–RZ 三步旋转(角度由编译器决定),CNOT 变成 CZ 加单比特旋转——旋转门的数学见姊妹站参数化门附录;左对齐调度:每个门在它涉及的所有比特都空闲的最早时刻开工,不同比特上的门并行执行。
门时长由 gate_durations 给出——键可以是精确门名,也可以用 1q / 2q / measure 三个通配键(接真机时也可改由后端元数据提供):
from uniqc.visualization import schedule_circuit
schedule = schedule_circuit(circuit, gate_durations={"1q": 20, "2q": 40, "measure": 100})
for g in schedule.gates:
print(f"{g.name:8s} q={list(g.qubits)} start={g.start:6.1f} duration={g.duration:5.1f} layer={g.layer}")
print("total_duration:", schedule.total_duration, schedule.unit)
RZ q=[0] start= 0.0 duration= 0.0 layer=0
SX q=[0] start= 0.0 duration= 20.0 layer=0
RZ q=[0] start= 20.0 duration= 0.0 layer=1
RZ q=[1] start= 0.0 duration= 0.0 layer=0
SX q=[1] start= 0.0 duration= 20.0 layer=0
RZ q=[1] start= 20.0 duration= 0.0 layer=1
CZ q=[1, 0] start= 20.0 duration= 40.0 layer=1
SX q=[0] start= 60.0 duration= 20.0 layer=2
RZ q=[0] start= 80.0 duration= 0.0 layer=3
RZ q=[2] start= 0.0 duration= 0.0 layer=0
SX q=[2] start= 0.0 duration= 20.0 layer=0
RZ q=[2] start= 20.0 duration= 0.0 layer=1
CZ q=[0, 2] start= 80.0 duration= 40.0 layer=3
SX q=[2] start= 120.0 duration= 20.0 layer=4
RZ q=[2] start= 140.0 duration= 0.0 layer=5
MEASURE q=[1] start= 60.0 duration=100.0 layer=2
MEASURE q=[0] start= 120.0 duration=100.0 layer=4
MEASURE q=[2] start= 140.0 duration=100.0 layer=5
total_duration: 240.0 ns
每个 TimelineGate 都带 qubits / params / start / duration / end / layer 字段,可以直接做程序化分析。输出里有四个值得注意的细节:
RZ 时长为 0:RZ 是"虚拟 Z 门"——只改动后续脉冲的相位参考、不发射脉冲,因此不占时长(上面所有
RZ的duration都是 0.0)。并行度:t=0 时刻三个比特的
SX同时开工;q[1]的CZ一结束(t=60)它的MEASURE立刻开始,与q[0]、q[2]上还在跑的门重叠。18 个门时长串行相加是 480 ns,调度后总时长只要 240 ns。编译会重排比特:第二个逻辑 CNOT 作用在 (1,2) 上,时间轴里却是
CZ q=[0, 2]——编译器在全连接虚拟拓扑上等价地置换了逻辑比特(对应地MEASURE q[1], c[0]等测量映射也被重排)。对编译后的线路再做模拟,测量分布仍是 GHZ(000/111 各半),结果不变。时长数据必须有来源:既不给
gate_durations、也没有后端元数据时,调度器无法确定门时长,直接抛错:
from uniqc.exceptions import TimelineDurationError
try:
schedule_circuit(circuit)
except TimelineDurationError as e:
print(str(e).splitlines()[0])
No duration is available for gate 'SX'. Provide backend_info.extra timing data, chip_characterization.global_info gate times, or gate_durations with either the exact gate name or generic key '1q'.
6. 时间表与时间轴图¶
format_result 把调度结果整理成"层 → 门列表"的表格数据,create_time_line_table 再变成以时刻为列、比特为行的时间表(装了 pandas 时是 DataFrame,没装也有内置的简化表格):
from uniqc.visualization import format_result, create_time_line_table
layers, qubits, time_points = format_result(
circuit, gate_durations={"1q": 20, "2q": 40, "measure": 100}
)
table = create_time_line_table(layers, qubits, time_points)
print(table)
0 20 60 80 120 140
qubit 0 SX 0.0 CZ 0.0 SX 0.0 CZ 0.0 MEASURE idle
qubit 1 SX 0.0 CZ 0.0 MEASURE idle idle idle
qubit 2 SX 0.0 RZ 3.142 idle CZ 0.0 SX 0.0 MEASURE
读表:单元格标签是"门名 + 首参数"(圆周率显示成 3.142);列标题是各门的起始时刻;没有门的格子标 idle。同一比特同一时刻叠了多个门时,后写入的标签覆盖先写入的——例如 qubit 0 在 20 ns 列同时有一个零时长 RZ 和一个 CZ,表格里显示 CZ 0.0。可以看出 qubit 1 在 160 ns 之后就全部空闲,整条线路的总时长由 qubit 2 的关键路径决定:CZ(80–120) → SX(120–140) → MEASURE(140–240)。
plot_time_line 把这张表画成分色表格图(超过 20 列自动分页),输出为 PDF 文件:
from uniqc.visualization import plot_time_line
plot_time_line(
circuit,
figure_save_path="timeline_out",
gate_durations={"1q": 20, "2q": 40, "measure": 100},
)
生成 timeline_out/timeline_1.pdf,效果(本页转成 PNG 嵌入):

想要真实时间刻度的矢量图,用 plot_time_line_html:门的宽度正比于时长、顶部标出时间轴、悬停可见每个门的起止时间:
from uniqc.visualization import plot_time_line_html
plot_time_line_html(
circuit,
output_path="ghz-timeline.html",
gate_durations={"1q": 20, "2q": 40, "measure": 100},
)

悬停第一个 RZ 时看到的提示内容(真实输出):
gate: RZ
qubits: [0]
start: 0 ns
duration: 0 ns
end: 0 ns
params: [1.5707963267948966]
raw: ('RZ', 0, None, 1.5707963267948966, False, None)
从时间轴图能直观看到改线路的方向:把读出(MEASURE,往往是最慢的操作)从关键路径上挪开、或缩短两比特门,总时长就会跟着降——这正是第 6 章校准数据(真实门时长)接进来之后时序图的价值。
下一步¶
把测量统计图用进真实算法:第 7 章 VQE 实操,理论推导见姊妹站 VQE 教程
云端/真机任务的结果长什么样、回来后怎么后处理:第 5 章 云端执行
练习题¶
练习 1【可视化工具箱与安装】(→ 第 1 节)
在全新虚拟环境中安装
unified-quantum[visualization],运行本节导入代码,确认打印0.1.0且没有 ImportError。卸载 pandas(
pip uninstall -y pandas)后重跑第 6 节的时间表代码,先预测输出的形态会怎样退化,再验证。
提示:
create_time_line_table内置了不依赖 pandas 的简化表格。
练习 2【测量结果直方图】(→ 第 2 节)
把 GHZ 线路中的
cnot(1, 2)改成cnot(0, 2),先笔算测量分布,再画直方图验证。分别用 128 次和 8192 次采样画直方图,预测哪一张更接近 50/50,并用二项分布标准差解释。
提示:柱高的典型涨落约为 \(\sqrt{Np(1-p)}/N\)。
练习 3【概率分布与均匀参考线】(→ 第 3 节)
把
uni.h(1)改成uni.x(1),预测哪几根柱子会消失、剩下的柱子大约多高,再运行验证。构造 4 比特均匀叠加态并画
plot_distribution,先算出红色参考线的纵坐标再验证。
提示:\(n\) 比特时参考线在 \(1/2^n\) 处。
练习 4【静态 HTML 线路图】(→ 第 4 节)
为 4 比特 GHZ 线路生成 HTML 线路图,数出图中双比特门的个数,与 OriginIR 中的 CNOT 行数对照。
对比
circuit_to_html与第 6 节plot_time_line_html的输出,指出两者最显著的视觉差异及各自强调的信息。
提示:一个按"层"等宽排布,一个按"纳秒"等比排布。
练习 5【门时序调度】(→ 第 5 节)
把
measure时长从 100 改成 40,先笔算新的total_duration,再运行验证。去掉
gate_durations重跑schedule_circuit,先预测哪个门最先触发TimelineDurationError,再验证;解释为什么报错的永远不会是 RZ。
提示:虚拟 Z 门在调度器里的时长恒为 0,不需要查表。
练习 6【时间表与时间轴图】(→ 第 6 节)
对照第 6 节的时间表,写出
qubit 1的空闲区间,并说明总时长由哪条关键路径决定。构造
x(0)加测量的单比特线路,先预测时间表有几行几列、非 idle 的门各在什么时刻,再生成验证。
提示:X 门同样会被编译分解为 RZ–SX–RZ。