SymPy 量子电路绘图指南:使用 circuit_plot 与 CircuitPlot 可视化量子线路
【免费下载链接】sympyA computer algebra system written in pure Python项目地址: https://gitcode.com/GitHub_Trending/sy/sympy
SymPy 的sympy.physics.quantum.circuitplot模块提供了基于 Matplotlib 的量子电路绘图能力:只需把若干量子门(Gate)相乘组成电路,再调用circuit_plot,即可得到一张可读的量子线路图。本文以该模块的官方文档(circuitplot.rst)为主线,结合源码逐一讲解CircuitPlot、circuit_plot、labeller、render_label等核心 API,以及测量门、受控门、SWAP 门在绘图中的底层实现,读完你就能在自己的 SymPy 会话中绘制出带量子位标签与初始态的电路图。
模块定位与依赖
circuitplot是 SymPy 量子计算子包(sympy.physics.quantum)中的绘图模块,专门用于把"由 Gate 相乘构成的电路"渲染成 Matplotlib 图形。从源码 circuitplot.py 可以看到它的依赖关系:
- 电路本身是
sympy.core.mul.Mul(多个门的乘积)或单个Gate实例; - 门类型来自 gate.py:
Gate、OneQubitGate、CGate、CGateS; - 数值与绘图分别依赖
numpy与matplotlib,二者通过sympy.external.import_module惰性导入。
模块的__init__顶部明确说明,numpy与matplotlib缺失(或处于无显示环境)时会抛出ImportError;其中 Matplotlib 的导入特意捕获了RuntimeError,因为无显示环境(如纯服务器)会触发该异常。因此,使用本模块前请确保这两个依赖已安装。
模块对外暴露的公共 API(见源码中的__all__)为:
| 名称 | 说明 |
|---|---|
CircuitPlot | 负责管理电路绘图的类,封装了网格、图形、导线与门的全部绘制逻辑 |
circuit_plot | 便捷函数,直接返回一个CircuitPlot实例 |
labeller | 为量子位自动生成导线标签 |
render_label | 把标签渲染为 LaTeX 风格的 $\left|q_0\right\rangle$ 文本 |
Mz、Mx | 用于绘图的 z / x 测量门"占位符" |
CreateOneQubitGate | 元类,动态创建新的单量子位门 |
CreateCGate | 函数,通过闭包动态创建受控门 |
快速上手:绘制你的第一条量子电路
绘制电路最直接的方式是调用circuit_plot,其签名与参数在源码中的 docstring 中定义:
circuit_plot(c, nqubits, **kwargs)c:要绘制的电路,应为Gate实例的乘积(Mul)。注意在 SymPy 中,g1 * g2表示"先应用g2,再应用g1"(与量子电路自左向右的时间顺序相反),因此源码 circuitplot.py 的_gates方法使用reversed(self.circuit.args)逐个取出门,保证按正确顺序绘制;nqubits:电路包含的量子位总数,必须不小于所有门所需的最小量子位数(Gate.min_qubits);**kwargs:透传给CircuitPlot的实例属性,用于定制标签、初始态、缩放等。
一个最简单的示例(取自测试文件 test_circuitplot.py):
from sympy.physics.quantum.circuitplot import circuit_plot, labeller from sympy.physics.quantum.gate import CNOT # CNOT(1,0):控制位为 1,目标位为 0 c = circuit_plot(CNOT(1, 0), 2, labels=labeller(2))这里CNOT(1, 0)是一个受控非门,控制量子位是 1、目标量子位是 0。nqubits=2表示电路有两条导线。执行后 Matplotlib 会弹出电路图:两条水平导线,左侧带有q_1、q_0标签,控制位位置画实心圆点,目标位位置画"⊕"符号(受控 X 门的简化记号)。
核心类 CircuitPlot 的结构与绘制流程
CircuitPlot是绘图的"总指挥"。构造时依次执行以下步骤(见__init__与各私有方法):
update(kwargs):把用户传入的关键字参数直接写入实例字典,作为覆盖类级默认值的配置;_create_grid():用numpy.arange生成导线网格与门网格。导线位于y = 0, scale, 2*scale, ...,门位于x = 0, scale, 2*scale, ...,scale默认1.0;_create_figure():按(ngates*scale, nqubits*scale)创建 Matplotlib 图形,关闭坐标轴显示,设置aspect='equal';_plot_wires():逐条绘制水平导线,并在导线左端用render_label写出标签;_plot_gates():遍历门列表,调用每个门的plot_gate(self, gate_idx)完成绘制;_finish():关闭所有对象的裁剪(clip),使大电路平移缩放更顺滑。
类级可配置属性及其默认值(见 circuitplot.py):
| 属性 | 默认值 | 作用 |
|---|---|---|
scale | 1.0 | 网格与图形尺寸的整体缩放 |
fontsize | 20.0 | 标签与门名文本字号 |
linewidth | 1.0 | 导线与图形线条宽度 |
control_radius | 0.05 | 控制点(实心圆)半径 |
not_radius | 0.15 | NOT 门"⊕"空心圆半径 |
swap_delta | 0.05 | SWAP 叉号对角线长度 |
labels | [] | 导线标签列表,labels[i]对应第 i 根导线 |
inits | {} | 导线初始态字典,如{'q0': '0'} |
label_buffer | 0.5 | 标签与第一条导线起点的水平间距 |
这些默认值均可通过circuit_plot(..., scale=2.0, fontsize=14)之类的关键字参数覆盖。
门级绘制:从图元到完整电路
CircuitPlot本身只提供"图元"绘制方法,具体某个门长什么样由门的plot_gate决定(动态分派,见 gate.py)。
单量子位门:方框文本
OneQubitGate.plot_gate调用circ_plot.one_qubit_box(...),即在目标导线上画一个带黑色边框、白色填充的方框,框内是门的名称文本(如H、X、S、T)。UGate等其他单比特门同理。
受控门:控制线与控制点
CGate.plot_gate的流程是:
- 计算控制位与目标位的最小、最大导线编号;
- 调用
control_line画一条竖直控制线连接这些导线; - 在每个控制位上画实心控制点(
control_point); - 目标门按自身类型绘制。
关键开关是simplify_cgate(CGateS将其置为True,源码第 491-495 行):当它为真时,受控 X 门会调用XGate.plot_gate_plus画出经典的"⊕"(not_point),受控 Z 门则直接在目标位画控制点;否则目标门退化为普通方框绘制。测试文件 test_circuitplot.py 中的test_cnot正是验证了这一路径能正常渲染。
SWAP 门:交叉记号
SwapGate.plot_gate(SWAP的底层类)先在两条目标导线间画竖直控制线,再在两端各画一个"×"(swap_point)。
测量门:导线加倍
Mz与Mx是模块自定义的"测量门占位符"(非真实量子门,仅用于绘图),设置了measurement = True。CircuitPlot._measurements会收集每条被测导线及对应的测量门下标,_plot_measured_wires随后把被测后的导线向下平移dy = 0.04画一条"加倍"的粗化导线,并同步把该导线上后续受控门的控制线也偏移,以符合常见的测量后双线表示。测试 test_qasm.py 也导入了Mz,说明测量门占位符在解析流程中同样被复用。
标签与初始态渲染
labeller(n, symbol='q')用于生成导线标签:
>>> from sympy.physics.quantum.circuitplot import labeller >>> labeller(2) ['q_1', 'q_0'] >>> labeller(3, 'j') ['j_2', 'j_1', 'j_0']注意顺序:labeller按"量子位编号从大到小"排列('q_1', 'q_0'),这对应电路图中顶部导线是最高位。
render_label(label, inits={})把标签渲染成 LaTeX 风格文本,并支持附加初始态:
>>> from sympy.physics.quantum.circuitplot import render_label >>> render_label('q0') '$\\left|q0\\right\\rangle$' >>> render_label('q0', {'q0': '0'}) '$\\left|q0\\right\\rangle=\\left|0\\right\\rangle$'对应地,在circuit_plot中可传入labels=labeller(2)与inits={'q_1': '0', 'q_0': '1'}显示每个量子位的初始态(有初始态时标签左侧会额外空出0.25的缓冲,见_plot_wires)。
动态创建自定义门
模块提供了两个工具,用于快速定义"绘图专用"的新门(例如你只需要它画成方框,不关心它的酉矩阵实现):
from sympy.physics.quantum.circuitplot import CreateOneQubitGate, CreateCGate # 元类方式:创建单量子位门 Q Qgate = CreateOneQubitGate('Q') str(Qgate(0)) # 'Q(0)' # 闭包方式:创建受控门 CQ,返回的函数接收 (controls, target) CQ = CreateCGate('Q') str(CQ([1], 0)) # 'C((1),Q(0))'CreateOneQubitGate是元类,动态生成继承自OneQubitGate的新门类,gate_name与gate_name_latex由传入名称(及可选 LaTeX 名)填充;CreateCGate用闭包返回ControlledGate(ctrls, target),内部委托给CGate(tuple(ctrls), onequbitgate(target))。这两个工具的行为均有对应测试(test_create1、test_createc)。
完整示例:三量子位电路
综合测试 test_circuitplot.py 中的test_ex4,可以绘制一个包含 Hadamard、受控 S、受控 T 与 SWAP 的三量子位电路:
from sympy.physics.quantum.circuitplot import circuit_plot, labeller from sympy.physics.quantum.gate import CNOT, H, SWAP, CGate, S, T c = circuit_plot( SWAP(0, 2) * H(0) * CGate((0,), S(1)) * H(1) * CGate((0,), T(2)) * CGate((1,), S(2)) * H(2), 3, labels=labeller(3, 'j'), )该电路共 7 个门、3 根导线,标签为['j_2', 'j_1', 'j_0']。运行后会看到三条导线从左到右依次经过 H 方框、受控 S/T 的控制点与方框,以及 SWAP 的交叉记号——这正是"把量子门乘积变成可读电路图"的完整演示。
已知限制
模块源码 docstring 明确列出了待办事项,使用时需要知晓这些边界:
- 大电路的打印性能尚未优化;
- 单个门(非
Mul)虽然_gates中做了处理,但整体支持有限(docstring 标注"get this to work with single gates"); - 尚不能绘制多目标门(multi-target gates);
- 初始态与末态(ket 态)绘制未实现;
- 测量门的绘制属于"占位"方案,官方认为可能需要重新设计测量在门体系中的位置;
scale与figsize的协调方式有待改进。
此外,two_qubit_box方法目前仅打印网格坐标,尚未真正绘制双比特门方框(docstring 中标注"Does not work yet"),因此绘制多比特门时请依赖CGate/SWAP这类自带plot_gate的门类型。
小结
sympy.physics.quantum.circuitplot提供了一条"从符号电路到图形"的捷径:用labeller生成标签、用circuit_plot一行绘图、用inits标注初始态、用Mz/Mx表示测量、用CreateOneQubitGate/CreateCGate扩展自定义门。其实现以CircuitPlot为总控、以各Gate的plot_gate为分派点,辅以 Matplotlib 图元,构成了一个轻量但可扩展的量子电路可视化方案。对于需要把 SymPy 量子计算表达式渲染为论文插图或教学示意图的场景,这是一个开箱即用的选择。
【免费下载链接】sympyA computer algebra system written in pure Python项目地址: https://gitcode.com/GitHub_Trending/sy/sympy
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考