量子算术文献的问题不是”没有线路图”,而是每篇论文用一套自己的画法:Qiskit 的 mpl 风格画 CNOT 为 ⊕,Dürr–Høyer 论文用实心点,目标符号的直径、行距、寄存器括号、经典线全都随作者而变。跨论文比较门数与深度时,这种不一致会直接传导成错误结论。本站的做法是把”线路”定义成一个与渲染实现无关的数据结构(下称 IR),再由一个通用渲染器把 IR 编译成 SVG。所有词条、所有重绘的论文图、以及浏览器里的交互试验台,读的都是同一个 IR、调用的是同一份渲染器代码。
为什么是「数据 + 渲染」而不是直接画
把线路当成数据而不是图片,有四个直接的好处:
- 可比较:两条线路可以程序化地相减——逐列数非 Clifford 门、统计最大深度、检查是否有未清零的辅助比特。做成图片就只剩人眼。
- 可复用:同一个 IR 渲染出的图,既能嵌在词条里,也能喂给资源账本统计(见resource-accounting),不必维护两份描述。
- 可访问:SVG 里的门标签是真实文本,浏览器
Ctrl+F能搜到MAJ、π/8、MOD-REDUCE。 - 一致:明暗配色由站点 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 两段,都靠它表达模块边界——这正是读懂一张算术线路图最需要的信息,而纯线框画法完全丢失了它。
布局:为什么长标签不会挤走邻居
渲染器做两趟布局:
- 量宽——逐门估算自然宽度(文字宽度 + 内边距),取每列的最大值;
- 前缀和——把列宽累加成列中心坐标。
于是 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。
延伸阅读
- circuit-atlas:本站渲染器支持的全部门型一览
- circuits-gallery:按功能归类的实际算术线路图谱
- qubit-ordering-conventions:IR 里
q的编号与比特序、寄存器约定如何对应 - resource-accounting:怎么在 IR 上程序化地数门
- index:这些门在真实加法器里怎么排布