量子算术文献的问题不是”没有线路图”,而是每篇论文用一套自己的画法:Qiskit 的 mpl 风格画 CNOT 为 ⊕,Dürr–Høyer 论文用实心点,目标符号的直径、行距、寄存器括号、经典线全都随作者而变。跨论文比较门数与深度时,这种不一致会直接传导成错误结论。本站的做法是把”线路”定义成一个与渲染实现无关的数据结构(下称 IR),再由一个通用渲染器把 IR 编译成 SVG。所有词条、所有重绘的论文图、以及浏览器里的交互试验台,读的都是同一个 IR、调用的是同一份渲染器代码。

为什么是「数据 + 渲染」而不是直接画

把线路当成数据而不是图片,有四个直接的好处:

  1. 可比较:两条线路可以程序化地相减——逐列数非 Clifford 门、统计最大深度、检查是否有未清零的辅助比特。做成图片就只剩人眼。
  2. 可复用:同一个 IR 渲染出的图,既能嵌在词条里,也能喂给资源账本统计(见resource-accounting),不必维护两份描述。
  3. 可访问:SVG 里的门标签是真实文本,浏览器 Ctrl+F 能搜到 MAJ、π/8、MOD-REDUCE。
  4. 一致:明暗配色由站点 CSS 变量接管,切换主题不重绘;打印视图与屏幕视图完全一致。

选择 JSON 而不是 LaTeX/QASM 作为规范形式,是权衡后的结果:QASM 的 measure q[3] -> c[0]; 与 if (c==1) X q[0]; 表达条件操作很自然,但同一算术线路用 QASM 写需要几十行样板;LaTeX 的 quantikz 控制符很漂亮,却要额外依赖 TeX 引擎,且难以在浏览器端复用。JSON 在三者中兼顾了机器可读、手写可读、以及零运行时依赖。

IR 的顶层结构

{
  "title": "CDKM 精简加法器", // 渲染为 <figcaption>
  "nq": 3, // 量子线数(必需),索引 0 在最上
  "nc": 1, // 经典线数,默认 0
  "labels": ["a", "b", "z", "c"], // 线名,先量子后经典
  "groups": [{ "start": 0, "end": 1, "label": "in" }], // 左侧寄存器括号
  "gates": [/* 见下 */],
  "boxes": [{ "from": { "t": 0, "q": 0 }, "to": { "t": 4, "q": 2 }, "label": "MAJ" }],
}

只有 nq 与 gates 是必需的。ncols 缺省时由最右侧的门自动推出,因此作者不需要手工数列数——这是实践中最容易出错、又最难在代码审查中发现的字段。

门:对象式与元组简写

同一个门有两种写法。对象式字段语义完全展开,适合程序生成:

{ "t": 2, "q": 1, "op": "cx", "c": 0, "dagger": false, "repeat": 4 }

元组简写适合手写词条,能把一条门压到十几个字符:

形式含义例子
[op, t, q]单比特门["h", 0, 0]
[op, t, q, ctrl]受控门,ctrl 可为数组表示多控["cx",0,1,0]、["x",3,4,[0,1,2]]
[op, t, q, angle]旋转 / 相位门["rz",0,0,"π/8"]
[op, t, q, ctrl, angle…]受控旋转["crz",0,1,0,"π/8"]
["m", t, q, cbit]测量并写入经典线["m", 4, 2, 0]
["swap", t, q, other]交换["swap", 1, 0, 1]
["cswap", t, q, ctrl, other]Fredkin 门["cswap",4,2,0,3]
["barrier", t]隔断(对象式可用 q:[a,b] 限定范围)["barrier", 5]
["box", t1, q1, t2, q2, label?]虚线分组框["box",0,0,4,2,"MAJ"]
["label", t, q, text]线上自由文本块["label",6,0,"进位已归约"]

一个容易踩的坑:第 4 位是控制还是角度?

["rz", 0, 1, 0.785] 和 ["cx", 0, 1, 0] 的第 4 位都是数字,但前者是角度、后者是控制线。渲染器不靠 JS 类型去猜,而是查一张固定的 op 类别表(parse.mjs 里的 CONTROLLED / PARAMETRIC):

  • 只在 PARAMETRIC 里 → 第 4 位是角度;
  • 只在 CONTROLLED 里 → 第 4 位是控制线;
  • 同时在两张表里(crz、cp、cu…)→ 第 4 位是控制线,第 5 位起是角度。

这条规则值得单独强调,因为它正是”看起来能跑但语义反了”的典型错误来源。举个真实后果:把 ["crz", 0, 1, 0] 想写成”对第 0 号线做零角度旋转”,会被解析成”以第 0 号线为控制的 “——图上多出一个控制点,而门的作用对象完全不同。

支持的门型

类别op
单比特固定符号h x y z s sdg t tdg sx sxdg id
单比特旋转rx ry rz p/u1 u/u2/u3
受控cx cy cz ch cs csdg crx cry crz cp cr cu
多控制ccx(Toffoli)ccz rccx rc3x,或直接 ["x",t,q,[c1,c2,…]]
交换swap cswap/fredkin
测量m/measure/mz、mx、my;reset
结构barrier、box、label、text、note

"dagger": true 适用于任何带符号的门,画出 T†、Rz(π/8)†。

rccx(相对相位 Toffoli)刻意画成半透明的 ⊕:它与普通 Toffoli 只差一个全局相位,在算术线路里却常常能省掉一整层 ,因此值得在图上可区分——否则读者无法从图判断”这里到底用了哪个”。

两个专门为算术线路设计的特性

repeat:重复块与 {×n} 记号

教科书画 比特加法器时不会真画 列,而是画一列加一个左侧的 {×n}。IR 直接把这个记法做成一等公民:

{ "t": 0, "q": 0, "op": "cx", "c": 5, "repeat": 5 }

它会展开成 t = 0…4 共 5 列,并在左侧画出括号与 ×5 标记。这样做的意义不只是省字符:它把”这一段随比特数线性增长”这个事实显式写进了图里,读者一眼能看出哪些部分是 、哪些是常数。

boxes:模块级分组

["box", 0, 0, 4, 2, "MAJ"]

画成带标签的虚线框。CDKM 加法器的 MAJ/UMA、Kutin 并行进位树的每一层、模加法的 ADD/MOD-REDUCE 两段,都靠它表达模块边界——这正是读懂一张算术线路图最需要的信息,而纯线框画法完全丢失了它。

布局:为什么长标签不会挤走邻居

渲染器做两趟布局:

  1. 量宽——逐门估算自然宽度(文字宽度 + 内边距),取每列的最大值;
  2. 前缀和——把列宽累加成列中心坐标。

于是 MOD-REDUCE 这样的长标签只会撑开自己那一列,右边的门自动右移,不会被压扁或叠字。这一点对算术线路尤其关键:这类图满屏都是 ADD、MAJ、MOD-REDUCE、INTERPRET 这类长块名,如果用等宽网格,每一列都得按最长标签留白,一张图会横向膨胀到没法看。

同一个渲染器跑在两个地方

IR 与渲染器被拆成三个零依赖的 ES 模块:

plugins-local/qcircuit/lib/parse.mjs      规范化与校验
plugins-local/qcircuit/lib/renderer.mjs   布局 + SVG 生成
plugins-local/qcircuit/lib/style.mjs      样式表(以字符串导出)
  • 构建期:Quartz 插件扫描 qcircuit 围栏块,在构建时就编译成静态 SVG。文章本身不携带任何线路相关的客户端 JS,SPA 视图、打印视图、RSS 与下游镜像站表现一致。
  • 浏览器期:交互式试验台 /static/qcircuit/playground.html 直接 import 上面三个文件,左栏编辑、右栏实时渲染,并带 CDKM 加法器、进位链、模加法、Karatsuba 骨架、QFT 门序列、多控门六个预置样例。

因为是同一份源码,试验台能画出来的线路,构建一定画得出来——不存在”演示里好好的、词条里空白”的可能。

错误不会中断整站构建

一个写错的围栏不会让 quartz build 失败,而是变成页面上一个可见的错误框。比如把某条门写到不存在的线上时,页面会出现一个红边框的盒子,写着 wire 5 is out of range: circuit declares nq=2,并附上原始 JSON 片段。

对内容作者来说,这意味着一个拼写错误是在页面上直接看见的,而不必去翻构建日志找哪一行炸了。(本站不故意在正文里保留坏例子,但试验台左栏可以随便写坏,立刻就能看到同样的错误框。)

除了构建期的软失败,本站还提供一个硬闸门:npm run check:circuits 会用同一个解析器遍历 content/ 下的每个围栏块,报出 文件:行号 + 错误原文,退出码非零。CI 与发布前都应跑它。

在词条里怎么写

```qcircuit
{
  "title": "CDKM 精简加法器",
  "nq": 3, "nc": 1,
  "labels": ["a", "b", "z", "c"],
  "groups": [{"start": 0, "end": 1, "label": "in"}],
  "gates": [
    ["box", 0, 0, 4, 2, "MAJ"],
    ["h", 1, 0], ["cx", 2, 1, 0], ["cx", 2, 2, 0],
    ["m", 8, 2, 0]
  ]
}
```

围栏内必须是严格 JSON:不支持注释、不支持尾逗号。

与其他线路格式的关系

格式强项在本站的角色
QASM条件操作、经典控制、贴近真实编译产物输入端:把 Qiskit/Cirq 线路翻译成 IR
OpenQASM 3同上 + 浮点/定时指令输入端
quantikz / Qcircuit排版漂亮、LaTeX 生态论文原图重绘时的参照
本站的 IR人类可读、零依赖、可程序化统计规范形式:词条、试验台、账本三方共用

真实做法是三者并用:从 QASM 导入已有实现(如 Qiskit 的 CDKMRippleAdder)用于核对数字,从 quantikz 抄论文里的经典画法用于讲解,而凡是本站新画的图一律走 IR。

延伸阅读