4. 量子电路的可视化与调试

量子电路写完之后,如果只盯着 Python 代码看,很难判断门是否加对了位置、控制位和目标位有没有写反、多个量子比特之间的纠缠结构是否符合预期。Cirq 提供了一套从“文本速览”到“图形化展示”再到“结构化检查”的可视化与调试手段,本节围绕 print 输出、SVG 绘图、电路图导出,以及门序列与量子比特连接的检查展开。

一、用 print 快速查看电路结构

最轻量的查看方式是直接 print(circuit)。Cirq 会按“时刻(Moment)”为单位,把电路渲染成文本图:每一列是一个 Moment,每一行是一个量子比特,门用符号表示,控制位用 @、目标位用 X/Y/Z 等标记,测量用 M 表示。例如:

import cirq

q0, q1, q2 = cirq.LineQubit.range(3)
circuit = cirq.Circuit(
    cirq.H(q0),
    cirq.CNOT(q0, q1),
    cirq.X(q2),
    cirq.CZ(q1, q2),
    cirq.measure(q0, q1, q2, key='m')
)
print(circuit)

输出大致如下(示意):

0: ───H───@───────────M('m')───
          │           │
1: ───────X───@───────M────────
              │       │
2: ───X───────@───────M────────

文本图的价值在于“对齐”:同一列内的门属于同一个 Moment,会并行执行;跨列的门存在先后依赖。通过观察 @ 与 X 是否在同一列、连线是否跨越了不该跨越的比特,可以快速发现控制位写反、门顺序颠倒等低级错误。需要注意,文本图对多比特门的连线表达有限,当电路较宽或门较复杂时,应改用 SVG 图。

二、SVG 绘图:更直观的电路图

Cirq 内置了基于 SVG 的电路渲染,最常用的是 cirq.Circuit.to_text_diagram() 的图形化替代——在 Jupyter 环境中直接求值电路对象即可得到 SVG;在脚本中可调用 SVG 相关接口或使用 cirq.contrib.svg 导出。典型用法:

from cirq.contrib.svg import SVGCircuit

svg = SVGCircuit(circuit)
# 在 Jupyter 中直接显示 svg;或写入文件
with open('circuit.svg', 'w', encoding='utf-8') as f:
    f.write(str(svg))

SVG 图相比文本图有三个明显优势:一是多比特门的连线更清晰,控制位与目标位用不同图形区分;二是可以标注测量键名、参数化门的符号表达式;三是便于嵌入文档或网页。导出时建议使用 UTF-8 编码,避免中文标签乱码。若电路包含参数化门(如 cirq.rx(sympy.Symbol('t'))),SVG 中会显示符号名,便于检查参数是否绑定正确。

Cirq 电路可视化与调试:三条观察路径 Circuit 对象 门序列 + 量子比特 print(circuit) 文本时刻图 · 快速对齐检查 SVGCircuit / to_svg 图形化 · 连线与符号清晰 all_qubits / moments 门序列与连接关系检查 对照预期:门位置 / 控制目标 / 连接 发现偏差 → 回到电路定义修正 文本图看时刻对齐,SVG 看连线结构,结构化接口看门序列与量子比特集合

三、检查门序列与量子比特连接

图形化展示解决“看得见”的问题,结构化检查解决“查得准”的问题。Cirq 的 Circuit 提供了若干可直接用于调试的接口:

  • circuit.all_qubits():返回电路中出现的全部量子比特集合,用于确认是否有“幽灵比特”被意外引入,或某个比特是否真的被使用。
  • circuit.all_operations():按时间顺序迭代所有操作(Operation),每个操作包含门与作用比特,适合逐条核对门序列。
  • circuit.moments:按 Moment 迭代,可检查哪些门被安排在同一时刻并行执行。
  • circuit.findall_operations(predicate):按条件筛选操作,例如找出所有 CNOT 或所有测量门。

一个实用的检查片段如下,用于确认 CNOT 的控制位与目标位是否符合预期:

for op in circuit.all_operations():
    if isinstance(op.gate, cirq.CNOTPowGate):
        ctrl, targ = op.qubits
        print(f"CNOT 控制={ctrl} 目标={targ}")

print("使用到的量子比特:", sorted(circuit.all_qubits(), key=str))
print("Moment 数量:", len(circuit.moments))

输出会明确列出每个 CNOT 的控制与目标比特。若发现 CNOT 控制=q1 目标=q0 而预期是 q0→q1,说明门参数顺序写反了。类似地,all_qubits() 若返回了未在预期列表中的比特,通常意味着某处误用了新比特或变量名拼写错误。

四、导出与版本对比

调试往往需要对比“修改前”和“修改后”的电路。建议在关键节点把电路导出为 SVG 或文本文件,命名中带上版本或时间戳,例如 circuit_v1.svg、circuit_v2.svg。Cirq 也支持将电路序列化为 JSON(cirq.to_json(circuit)),便于程序化比对两个电路是否等价。对于参数化电路,导出前应确认符号是否已绑定具体数值,否则 SVG 中只会显示符号名,无法判断实际门角度。

调试顺序建议:先用 print 看整体时刻结构,再用 SVG 确认多比特门连线,最后用 all_operations() 和 all_qubits() 做逐条核对。三步结合,能覆盖绝大多数门位置、控制目标、比特使用方面的错误。
注意:print(circuit) 的文本图在电路较宽时会被换行或压缩,容易看错列对齐;此时不要仅凭文本图下结论,应切换到 SVG 或结构化接口确认。另外,测量门的 key 若重复,文本图中会显示相同标签,需通过 all_operations() 检查实际测量对象。

掌握这三类手段后,量子电路从“写完”到“确认写对”之间就有了可操作的检查路径:文本图负责快速定位时刻与并行关系,SVG 负责呈现连线与符号细节,结构化接口负责精确核对门序列与量子比特连接。实际调试时按“先整体、后局部、再逐条”的顺序推进,能显著减少因门顺序或控制目标写反而导致的错误结果。