深秋用tikzcd 绘制交换示图
Table of Contents
1. 背景
秋天是北京最好的季节,干净明亮,有时低垂天空湛蓝,天顶铅云聚集,空气中弥漫着麻酱涮羊肉的本来味道。回想去年重庆的秋天,阴郁的山岭丛林,火锅里漂浮着层层辣子(还所谓微辣)掩盖各种杂碎的本味,与人鼻口舌心起总总化学的反应,才激发出又香又辣的味道呢。
TikZ 与 LaTeX 世界,如秋天之与北京,干净明亮, TikZ 是最好的通用图形宏包,哪有之一呢。使用 TikZ 绘制交换示图有三种方式:
tikzcdmatrix摆放节点位置,tikz绘制箭头- 直接用
TikZ绘制所有
对我而言,只有 tikzcd 一种方式,或者说 tikzcd 是画交换图示最好的宏包,哪有之一的说法呢?两个原因。
一,用 matrix 摆放节点位置也是 tikzcd 的原理, tikzcd 其实,本质,背后也是用 matrix 摆放节点位置的。另外 tikzcd 还提供 \arrow 命令来画箭头。
二,纯粹用 TikZ 绘制的交换示图, tikzcd 也可以画,而且往往还更简单些。我在练习中,往往忍不住用 tikzcd 重画一遍。这也许就是『身怀利器,比起杀心』吧。
在正式进入这三种 TikZ 的方式之前,我们先大致看看非 TikZ 的情况,也就是
amscd 的古法交换示图的绘制方式,理解其用法;这样我们可以时不时地把
amscd 拎出来嘲笑一番,借以衬托 tikzcd 的优越。
这个例子几乎涵盖了 amscd 功能的全部:
@>a>>: 从左到右的箭头,标签a在箭头上面,把标签放下面可以@>>a>,@<<d<: 从右向左的箭头,标签a在箭头下面,把标签放上面可以@<d<<,@VVbV: 箭头朝下,可以把V想象为向下的箭头,@AcAA: 箭头朝上,可以把A想象为向上的箭头。
那有人问了,标签 b 换成 V ,标签 c 换成 A 会怎样?下例给出答案:用 {V}
与 {A} 替换。
那有人问了,从 A 到 D ,如何画一个斜的箭头呢?对 amscd 而言,这是不可能完成的任务,我们出的题超纲了。好, amscd 就到此为止,我们进入 TikZ
看看 tikzcd 的画法。
tikzcd 的箭头 \arrow{r}{a} 可以统一到更现代的写法 \ar[r,"a"] 。
后续牵涉 tikzcd 我们都会用这种更现代的写法。 tikzcd 画交换示图关键的两点:
\matrix节点布局
\arrow命令专画箭头,箭头诸如形状、方向高度定制。
说完 tikzcd 的总体思路再说说 tikzcd 的作者,奥古斯托·斯托费尔(Augusto
Stoffel),巴西人,巴西南大河联邦大学(Universidade Federal do Rio
Grande do Sul)计算机工程与数学专业本科及硕士,美国圣母大学(University of Notre Dame)博士,研究方向为代数拓扑及其在量子场论中的应用。约2016年赴德国从事博士后研究,期间经历新冠疫情(2019年),于2020
年转入工业界,加入DIDA (https://dida.do),深耕机器学习,涉足计算机视觉与自然语言处理。
Figure 1: 奥古斯托·斯托费尔(Augusto Stoffel)大致在新冠疫情期间(2018年至2020年之间),这段时期正对应了他密集开发 tikzcd 1.0 版本、在德国做博士后并遭遇疫情、最终决定转型加入工业界的关键阶段。
在J.S.Milne 的CDGuide1中,J.S.Milne指出 tikzcd 的作者是弗洛伦西奥·内维斯(Florêncio Neves),另外在 tikzcd github之前的早期提交中,署名作者也是弗洛伦西奥·内维斯。怀着这个疑问,我开issue询问2事情原委,作者回应说『弗洛伦西奥·内维斯』是他的曾用笔名。
Figure 2: 奥古斯托·斯托费尔(Augusto Stoffel)2021年(或稍早于2021年的宣传照)。这是他转入工业界、加入DIDA之后的第一个完整年份。黑板背景和条纹T恤,生动地定格了他作为“曾经的数学家、现在的机器学习科学家”的双重身份。这正是他在演讲中介绍自己从代数拓扑跨入机器学习领域的那个时间点。
好, tikzcd 简略介绍先到这,我们继续看看用 \matrix 绘制交换示图的大体情况。
\matrix 的画法也是先把四个节点(A,B,C,D)放进矩阵,于是四个节点就有了坐标
- A -
(m-1-1) - B -
(m-1-2) - C -
(m-2-1) - D -
(m-2-2)
然后用 \path 把他们连起来。似乎和纯的 TikZ 画法区别,就在于 \matrix 的使用。我们再看看所谓纯的,不借助其他宏包的 TikZ 画法。
果然, TikZ 不用 \matrix ,直接把节点放在相应坐标,之后用 \path 画箭头把节点连起来。纯 TikZ 画法感觉比矩阵的画法还简单。矩阵 \matrix 那些定制,初学者一下子不见得能够熟练掌握。
2. tikzcd 交换示图之旅
tikzcd 处理大对象要比 amscd 好很多。下面 tikzcd 的例子里,有一个很长的
6 A 连乘的大对象节点:
tikzcd 处理起来可谓毫不费力。我们把 amscd 拿出来看:
tikzcd 对比较高的标签处理的也很好。
我们拿 amscd 做个对比:
下面一个例子用于说明在一个交换示图里不同箭头的用法。
下面例子处理箭头标签。
不过太长的标签可能会惹麻烦:
不过可以如下修复:
对高度不同的节点, tikzcd 处理没毛病:
看看 amscd 的处理:
有说这看起来还好呀,请注意图片左侧的大段空白。
回到 tikzcd ,曲线的箭头也容易了:
最后两个例子,看 tikzcd 多么简洁优美。
这两个例子超出 amscd 能力范围,因此就不难为它了。
3. TikZ with matrix 交换示图之旅
tikz matrix 对长对象和高标签的处理要比 amscd 强。
下面看高的标签:
作为程序员所谓工程人员,可能对这些细节不很在意。可见数学家对细节的把控,已经到了令人发指的地步。我们再看一个例子,指定箭头的起点( tikzcd 叫
start anchor ,我们也给出 tikzcd 的解法):
再给两个较为复杂的例子。
这两个例子也就是对 tikz with matrix 复杂,毕竟要处理很多细节。对
tikzcd 并不复杂,我们在 tikzcd 一节已经给出答案。
4. 纯 TikZ 交换示图之旅
布局不用 matrix ,因此反倒更灵活。例如:
tikzcd 对此显然早有应对:给空的标签起个名字,箭头里用 from 指定起点,
to 指定终点。见下例:
下面看五边形公理的例子。
这次 tikzcd 也没有更好的办法,这似乎是 matrix 排布节点的软肋:很难把正五边形的顶点放进格点。不过 tikzcd 也有考虑,打不过干脆和 TikZ 站到一起了:
下面我们看多个箭头的例子。注意这些例子早超过 amscd 能力范畴了,因此不拿 amscd 再举例。例子里也顺便附带 tikzcd 的代码,说明 TikZ 的高度灵活性是不必要的,因为太高的灵活性会降低易用性:
最后一个例子的 TikZ 源码超过一页,就不放图片里与图SideBySide的看了,先看图:
其实这个例子不难,只是略嫌繁琐。最后我们用 tikzcd 画一下,掂掂分量,差不多13行代码,不算沉重。
5. 总结
最后最复杂例子纯 TikZ 源码3附在后面,由此结束 tikzcd 交换示图之旅,在北京这个略有冷意、干净明亮的秋天:
- 我们在用
tikzcd绘制交换示图, - 我们介绍了
tikzcd作者奥古斯托·斯托费尔(Augusto Stoffel)的生平, Augusto Stoffel还有几个作品,我们后续会介绍, - 我们一路嘲笑
amscd,说它简陋,能力不足;这很可能是不公平的, - 我们为了夸赞
tikzcd的优越,不惜忍受TikZ with matrix和纯TikZ灵活性带来的繁琐:但我们也学到了一些TikZ的知识,并且提高了slingshot tikz的鲁棒性和兼容性;架构上也在从tectonic向TeXLive转进。
Footnotes:
最复杂(繁琐)例子 TikZ 源码
\begin{tcblisting}{width=14cm, text only,
title={$TikZ$代码很长,困难点在 $Kerc$ 到 $Cokera$ 间的红色飞线},}
\begin{tikzpicture}[>=angle 90,
text height=1.5ex,
text depth=0.25ex]
%%First place the nodes
\node (k-1) at (0,3) {$0$};
\node (k0) [right=of k-1] {$Ker f$};
\node (k1) [right=of k0] {$Ker a$};
\node (k2) [right=of k1] {$Ker b$};
\node (k3) [right=of k2] {$Ker c$};
\node (a1) [below=of k1] {$A$};
\node (a2) [below=of k2] {$B$};
\node (a3) [below=of k3] {$C$};
\node (a4) [right=of a3] {$0$};
\node (b1) [below=of a1] {$A’$};
\node (b0) [left=of b1] {$0$};
\node (b2) [below=of a2] {$B’$};
\node (b3) [below=of a3] {$C’$};
\node (c1) [below=of b1] {$Coker a$};
\node (c2) [below=of b2] {$Coker b$};
\node (c3) [below=of b3] {$Coker c$};
\node (c4) [right=of c3] {$Coker g’$};
\node (c5) [right=of c4] {$0$};
%%Draw the red arrows
\draw[->,red,font=\scriptsize]
(k-1) edge (k0)
(k0) edge (k1)
(k1) edge (k2)
(k2) edge (k3)
(c1) edge (c2)
(c2) edge (c3)
(c3) edge (c4)
(c4) edge (c5);
%%Draw the curvy red arrow
\draw[->,red]
(k3) edge[out=0,in=180,red]
node[pos=0.55,yshift=5pt] {$d$} (c1);
%%Draw the black arrows
\draw[->]
(k1) edge (a1)
(k2) edge (a2)
(k3) edge (a3)
(b1) edge (c1)
(b2) edge (c2)
(b3) edge (c3);
%%Draw the thick blue arrows
\draw[->,font=\scriptsize,blue,thick]
(a1) edge node[auto] {$f$} (a2)
(a2) edge node[auto] {$g$} (a3)
(a3) edge (a4)
(a1) edge node[auto] {$a$} (b1)
(a2) edge node[auto] {$b$} (b2)
(a3) edge node[auto] {$c$} (b3)
(b0) edge (b1)
(b1) edge node[below] {$f’$} (b2)
(b2) edge node[below] {$g’$} (b3);
\end{tikzpicture}
\end{tcblisting}