交换图 (tikz-cd / amscd)

“这个图是交换的”,也许是数学中最短却最吃重的一句话:写成等式要占好几行的内容,都收进了这一句。交换图把对象换成点、把映射换成箭头,断言的是连接同一对对象的任意两条有向路径,复合出来的映射相同。在 LaTeX 中,如今画交换图的标准做法是 \usepackage{tikz-cd}(注意环境名是不带连字符的 tikzcd);如果只需要普通的矩形格子,AMS 的 amscd 依然管用。本页从零搭出一个交换正方形,把标签分别挂到箭头两侧,再把箭头掰弯,最后回答该加载哪一个宏包。

“图是交换的”到底断言了什么

交换的意思是:起点与终点相同的每一条有向路径,复合出来的结果都一样。取一个四角为 A、B、C、D 的正方形:上边是从 A 到 B 的 f,右边是从 B 到 D 的 β,左边是从 A 到 C 的 α,下边是从 C 到 D 的 g。说这个正方形交换,恰好就等于一条等式 β∘f = g∘α。三角形装一条等式,正方形一条,立方体六条——每个面一条——全都折进一幅画里。图越大,画面所含的信息与写成等式所需的篇幅,差距就越大。交换图不是装饰,而是记号的压缩。

这种压缩最见功力的地方是 图追踪(diagram chasing)。同调代数中五引理、蛇引理的证明,做法都是在某个角上取一个元素,沿着箭头把它推出去,在图里走来走去。交换性正是那条规则:这趟行走的每一步,两条路都会落到同一个地方。于是图不只是一幅画,还是证明推进时的棋盘。这种画法进入数学写作,始于 1945 年创立范畴论的那篇论文;那段来历以及箭头符号本身,放在箭头页里讲。

图本身就是一段陈列式公式,所以要放进 \[\]equation 环境,让它独占一行并居中。历史上为此写过一整架宏包——xypicdiagramkuvio 等等——但要新写文档,实际的选择只剩两个:建在通用绘图宏包 TikZ 之上的 tikz-cd,以及 AMS 的 amscd。下面依次来看。

\usepackage{tikz-cd}tikzcd 环境:一个连字符的陷阱

在导言区写 \usepackage{tikz-cd},正文里用 \begin{tikzcd}\end{tikzcd} 包住图。宏包名带连字符,环境名不带。 弄反了就会得到 ! LaTeX Error: File 'tikzcd.sty' not found.;只写环境却忘了加载宏包,则会看到 ! LaTeX Error: Environment tikzcd undefined.。若已经加载了 TikZ,用 \usetikzlibrary{cd} 也能得到同样的东西。CTAN 上的现行版本是 1.0(2021 年 5 月 4 日),由 Augusto Stoffel 维护。箭头端点作为独立的库一同发布,形状是按 Computer Modern 字体中的箭头绘制的——正因如此,图里的箭头看上去才不像是从别的字体借来的。

tikzcd 的内容写法与 tabularmatrix 完全相同:用 & 分隔单元格,用 \\ 换行。每个单元格成为图中的一个节点,内容自动以数学模式排版,因此不需要 $。在单元格里加上 \arrow,就会长出一根箭头;短别名 \ar 是同一个命令。箭头写在它 出发的那个单元格 里,目标由方括号中的 方向键 给出:r 右、l 左、u 上、d 下。它们可以连成字符串——[rd] 斜着指向右下,[rr] 够到隔一格的位置。一个单元格想发出多少根箭头都可以。

latex
\usepackage{tikz-cd}   % package has a hyphen; environment does not
\[
\begin{tikzcd}
  A \arrow[r, "f"] \arrow[d, "\alpha"'] & B \arrow[d, "\beta"] \\
  C \arrow[r, "g"']                     & D
\end{tikzcd}
\]

这就是交换正方形。A、B、C、D 分列四角;上边是向右的 f,下边是向右的 g,左右两边分别是向下的 α 和 β。& 分列,\\ 分行,所以骨架不过是 A & B \\ C & D 这几个字符。箭头只是事后插进骨架里的注记,对象的位置完全不受箭头写法的影响——这正是 tikzcd 设计中最舒服的一点。

把标签放在箭头上方还是下方:"f""f"' 的区别

在选项中写一个 带引号的字符串 就得到标签,在右引号后紧跟一个 撇号 ' 就把它移到另一侧。定位的规则不是“上或下”,而是 相对前进方向的左侧。对向右的箭头来说,左侧就是上方——所以 \arrow[r, "f"] 把 f 放上方,\arrow[r, "f"'] 放下方。对向下的箭头来说,左侧就是左边——所以 \arrow[d, "\alpha"] 把 α 放左边,\arrow[d, "\alpha"'] 放右边。不必死记四种情况,“前进方向的左侧”这一条规则就能解释全部。

一根箭头可以带任意多个标签,每个标签还能有自己的选项。"\phi" near start 靠近起点,"\eta" near end 靠近终点,而 "h" description 则把标签 压在箭头上,用背景色把线条挖空——短箭头要配长名字时,这是通行做法。若标签正文或其选项中含逗号,就用花括号保护起来:"{(x,y)}"。由于内部是数学模式,要写普通词句就得用 amsmath\text{…}

latex
\[
\begin{tikzcd}
  A \arrow[r, "\phi" near start, "\psi"', "\eta" near end] & B \\
  X \arrow[r, "h" description] \arrow[rd] & Y \\
                                          & Z
\end{tikzcd}
\]

让箭头弯曲,以及在同一对对象之间画两根

bend leftbend right,需要指定角度就写成 bend left=25。要弯曲的理由几乎总是同一个:同一对对象之间有两个映射。伴随、同伦等价、余等化子的那对映射,都需要两根箭头;直着画就会互相压住,看上去只有一根。弯一下,两根就都读得出来。若只想让它们平行错开而不弯曲,shift leftshift right(默认 0.56ex)更合适,它们把直箭头整体往侧面挪。照例,“左”始终相对于前进方向,所以 bend left 是朝箭头自身的左侧鼓出去。

在立体图中——把三维立方体压平到纸面上——远处的棱会与近处的棱交叉。给箭头加上 crossing over,就会先在它下面画一条背景色的粗线,于是它看起来从前面通过。箭头是 按书写顺序绘制 的,所以当应当被压在下面的那根先出现时,就从后面的单元格反向指定,例如 from=uu,以此调整绘制次序。立方体图能不能画好,全看是否知道这两点。

latex
\[
\begin{tikzcd}
  A \arrow[r, bend left=25, "f"] \arrow[r, bend right=25, "g"'] & B
\end{tikzcd}
\qquad
\begin{tikzcd}
  A \arrow[dr] & B \arrow[dl, crossing over] \\
  C            & D
\end{tikzcd}
\]

线型和箭头端点同样只要在 \arrow 的选项里加键就能切换。命名分两路:一路是 hooktwo heads 这样 描述形状的简短名字,另一路是把现成的 LaTeX 命令 去掉反斜杠 得到的名字,例如 dashrightarrow。知道第二条约定,就能猜出键名而不必去查。键还能叠加:\arrow[r, tail, two heads, dashed] 一次就画出带尾、双头且为虚线的箭头。常用的键汇总如下。

效果
hook在尾端加钩(⊂);用于包含与单射
hook'同样的钩子,但弯向另一侧
tail在起点加尾巴;单射的另一种标记
two heads把箭头端点变成双层;用于满射
dashed改为虚线;惯例上表示唯一确定的映射
dotted改为点线;来自 TikZ 的键
squiggly波浪形箭头
equal画双线(等号)而不是箭头
maps to在尾端加一竖(相当于 \mapsto 的 ↦)
Rightarrow双线箭头;用于 2-胞腔与自然变换
dashrightarrow\dashrightarrow 同形(去掉反斜杠命名的一例)
bend left朝前进方向的左侧画弧;bend left=25 指定角度
bend right朝前进方向的右侧画弧
shift left不弯曲,只把直箭头横向平移(默认 0.56ex);用于平行的两根
crossing over在下方铺一条背景色的粗线,使箭头看起来从前面通过
phantom让箭头隐形;只留下标签,因而可以把符号放到格点之外
description标签选项:把标签压在线上,并用背景色挖空线条

把同构符号作为标签放到箭头上也是常见写法:\arrow[r, dashed, "\simeq"] 会在虚线箭头上方放一个 ≃(用 \sim 同理)。而由于 tikz-cd 建在 TikZ 之上,它可以直接接受 TikZ 自己的键——color=…dottedin=…/out=… 等等。它的表达范围实际上覆盖了整个 TikZ;反过来说,卡住的时候要翻的手册可能不是 tikz-cd 的,而是 TikZ 的。

调整间距,并给拉回方块打上角标

间距在环境的可选参数中设定:\begin{tikzcd}[column sep=large, row sep=small]。既接受尺寸(如 2em),也接受名称(tinysmalllargehuge);若要同时设置行列,就用 sep。只想把带长标签的那个方向拉宽,这种需求相当常见,所以这两个键值得记住。

标记拉回(纤维积)方块的那个小角标 ⌜ 并没有专门的键。通行做法是 给一根隐形箭头挂标签\arrow[rd, phantom, "\ulcorner", very near start]phantom 抹掉箭头,留下的标签就落在左上角内侧(若是推出,则把 \lrcorner 放到靠右下的位置)。这里有个坑:\ulcorner 不是 LaTeX 标准符号,而是来自 AMSFonts,忘了 \usepackage{amssymb} 就会以 ! Undefined control sequence. 中断。另外,phantom 箭头上的标签会居中对齐并按 \textstyle 原尺寸排版,想让它小一些就要显式加 \scriptstyle

latex
\usepackage{amssymb}   % \ulcorner comes from AMSFonts, not core LaTeX
\[
\begin{tikzcd}[column sep=large, row sep=large]
  P \arrow[r, "p"] \arrow[d, "q"'] \arrow[rd, phantom, "\ulcorner", very near start]
    & X \arrow[d, "f"] \\
  Y \arrow[r, "g"'] & Z
\end{tikzcd}
\]

什么时候 amscd 就够用:CD 环境

当图是没有斜向箭头的矩形格子,而你又不想把 TikZ 拖进编译流程时,amscd 正合适。\usepackage{amscd} 只带来一样东西:CD 环境。它属于 amsmath 套件,现行版本为 v2.1(2017 年 4 月 14 日),除 amsgen 外别无依赖。有意思的是它自己的文档:先说明这是在复现 AMS-TeX 2.x 的交换图功能,接着明确写道只支持简单的矩形图,没有斜向箭头或更复杂的功能,然后补上一句“许多用户改用更强大的图式宏包会更合适”,还点名了 diagramxypickuvio。会在开篇就把读者送去别处的宏包,并不多见。

内容与 tikz-cd 一样是由 &\\ 组成的矩阵,但所有箭头都写成以 @ 开头的特殊记号。水平方向,@>>> 向右、@<<< 向左;垂直方向,@VVV 向下(V 取自 vertical)、@AAA 向上(把 A 看作朝上的箭头端点)。双线等号水平为 @=,垂直为 @|。不需要箭头的网格点放上“空箭头”@.,以保持列对齐。水平箭头写在同一行两个对象之间,垂直箭头写在对象正下方那一行。

标签夹在构成箭头的字符 之间。这里有一条事实,能一口气解释 amscd 标签的全部行为:按 AMS 的说明,第一个 > 与第二个 > 之间的内容排成 上标,第二个与第三个之间的内容排成 下标。所以 @>f>> 把 f 放上方,@>>g> 把 g 放下方,@>f>g> 则上下各一个。标签之所以显得小,原因也在这里:amscd 的箭头实质上就是 一个带上下标的、极宽的关系符。垂直箭头遵循同一规则,只是标记出现在左右两侧——@VfVV 把 f 放在向下箭头的左边,@VVfV 放在右边。

记号含义
@>>>向右箭头
@<<<向左箭头
@VVV向下箭头(V 取自 vertical)
@AAA向上箭头(把 A 看作朝上的箭头端点)
@>f>>向右箭头,f 在上方(上标位置)
@>>g>向右箭头,g 在下方(下标位置)
@>f>g>上方 f,下方 g
@VfVV向下箭头,f 在左侧
@VVfV向下箭头,f 在右侧
@=水平双线(等号)
@|垂直双线(@\vert 同义)
@.空箭头:为不带箭头的网格点占位
latex
\usepackage{amscd}
\[
\begin{CD}
  A            @>f>>      B \\
  @V\alpha VV              @VV\beta V \\
  C            @>>g>      D
\end{CD}
\]

这与 tikz-cd 的例子是同一个正方形。左边用 @V\alpha VV 把 α 放在箭头左侧,右边用 @VV\beta V 把 β 放在箭头右侧,让两个标签都朝正方形外侧。下边写作 @>>g>,所以 g 在箭头下方。CD 环境同样是数学模式,因此和 tikz-cd 一样要放进陈列式公式里。下面是 AMS 手册中的例子,它假定 \End 已被定义为算子名(\operatorname 那一类)。

latex
% from the AMS amscd manual; \End must be defined as an operator name
\[
\begin{CD}
  S^{{\mathcal{W}}_\Lambda}\otimes T   @>j>>   T \\
  @VVV                                 @VV{\End P}V \\
  (S\otimes T)/I                       @=      (Z\otimes T)/J
\end{CD}
\]

第一行从左侧对象画一根箭头到右侧的 T,上方是 j。中间一行生成两根垂直箭头:左列无标签,右列在箭头右侧写 End P。最后一行用 @= 把两个对象以双线相连。{\End P} 的花括号用于把多记号的标签捆成一个整体。另外,如果你想在 amscd 的框架内用上粗箭头、虚线箭头、双向箭头或颜色,扩展宏包 amscdx(v2.2x,2019 年)正好补上这些——但 只能画矩形的限制依然保留

tikz-cdamscd 该如何取舍

判断几乎只归结为一个问题——你需要斜向箭头吗? 需要,amscd 就出局。若只要矩形格子,amscd 依赖更轻、写起来也更短。下面是把这一个问题拆得细一点。

  • 需要斜向箭头或曲线tikz-cdamscd 只有水平和垂直,倾斜不是难看,而是根本画不出来。
  • 想精细控制箭头端点与线型(单射 hooktail、满射 two heads、虚线、双线) → tikz-cd
  • 普通矩形图已足够,且希望依赖保持轻量amscd。不加载 TikZ,排版更快,导言区也更短。
  • 记法偏好amscd@>f>> 简短却独特;tikz-cd\arrow[r, "f"] 更长,但半年后回头看仍读得懂。
  • 输出路径tikz-cd 的图在 DVI 查看器中无法正确显示,需要 pdflatexlualatexxelatex 这类直接输出 PDF 的引擎,或者走 DVI 转 PDF/PS 的流程。amscd 没有这个限制。
  • 到底需不需要图:只有两个对象、一根箭头时,在正文里写 amsmath\xrightarrow{f} 就够了。

最后两条提醒,供接手旧文档时参考。tikz-cd 里仍保留着早于引号语法的写法 \arrow[选项]{方向}{标签},例如 \arrow{r}{f},出于向后兼容至今可用。还有一组简写命令 \rar\dar\uar\lar(以及 \drar 之类的斜向版本),其中 \rar 就等于 \arrow{r}。新写文档时两者都建议避开;一路只用 \arrow[r, "f"],是在替将来的自己省事。