physics / braket / tensor

physics 也许是 LaTeX 中被推荐得最起劲、同时又被警告得最响亮的宏包。\dv{f}{x} 写导数、\qty(...) 得到自动伸缩的括号、\ket{\psi} 写右矢——手感确实好。可这个宏包会悄悄改写你文档里已经在用的命令\div 不再是除号 ÷ 而变成散度,\Re 不再是花体 ℜ 而变成正体的「Re」。本页介绍 physics、它的重新设计版 physics2、专攻狄拉克记号的 braket,以及用于对齐指标的 tensor,并根据实际编译与测量的结果回答「如果今天从头开始写,该选哪一个」。

\usepackage{physics} 究竟带来什么

只需一行,物理公式中反复出现的形状就变成了短命令。Sergio C. de la Barrera 编写的 physics 内部依赖 xparseamsmath,并会自动加载 amsmath,因此不必另写。它提供的东西大致分五组:自动伸缩定界符\qty\abs\norm\eval\order)、向量分析\grad\div\curl\laplacian,点积 \vdot,叉积 \cross)、导数\dv\pdv\fdv\dd)、狄拉克记号\bra\ket\braket\ketbra\dyad\expectationvalue\matrixelement),以及矩阵与括号简写\mqty\smqty\pqty\bqty\Bqty\vqty,对易子 \comm,反对易子 \acomm,泊松括号 \pb,粗体向量 \vb\va\vu)。

latex
\usepackage{physics}

\[
  \dv{f}{x}, \qquad \dv[2]{f}{x}, \qquad \pdv{f}{x}{y},
  \qquad \int_0^1 f(x) \dd{x}
\]
\[
  \abs{\frac{a}{b}}, \qquad \norm{\vb{v}}, \qquad
  \eval{x^2}_0^\infty, \qquad \qty( \frac{1}{2} )
\]

招牌是导数。\dv 会随参数个数改变形态:\dv{x} 排出算子 d/dx,\dv{f}{x} 排出 df/dx。阶数写在可选参数里——\dv[2]{f}{x} 是 d²f/dx²,\dv[n]{f}{x} 是 dⁿf/dxⁿ。d 默认排成正体(罗马体),传入 \usepackage[italicdiff]{physics} 则切换为斜体 d。带星的 \dv*{f}{x} 输出由 \flatfrac 得到的斜线分数。偏导数 \pdv 用法相同,只有混合偏导取三个参数:\pdv{f}{x}{y} 给出 ∂²f/∂x∂y。微分元本身用 \dd\dd{x} 排出前后间距整齐的 dx,\dd[3]{x} 排出 d³x。定界符方面,\qty(...)\qty[...]\qty{...} 分别对应圆括号、方括号和花括号,都会随内容高度伸长。

physics 悄悄改写的命令 — \div\Re\sin

没有警告,没有报错,只有输出变了。 在 TeX Live 2024 下读取 \meaning 即可确认:加载 physics 之前,\div\mathchar"2204,即除号 ÷。加载之后,\div 变成散度,原本的 ÷ 被挪到别名 \divisionsymbol 之下。在同一文档里排出二者并从 PDF 提取文字,$a \div b$ 得到 「a∇ · b」,而 $a \divisionsymbol b$ 得到 「a ÷ b」。宽度也不同:26.79959pt 对 21.79968pt。也就是说,给一份原本用 \div 表示除法的旧文档加上 \usepackage{physics}式子的含义会被悄悄改掉

\Re\Im 的变化更加显眼。physics.sty 的相关一行是 \let\real\Re \DeclareDocumentCommand\Re{g}{…\operatorname{Re}…}——也就是说,花体的 ℜ 被换成了正体算子「Re」。在同一文档里并排排版,$\Re z$ 得到 「Re z」,而被挪走的 $\real z$ 得到 「ℜz」。命名函数也一样:\sin\cos\tan\log\ln\exp\det 都被换成会自动给参数加括号的版本,原定义则以更长的名字保留下来——\sine\cosine\tangent\logarithm\naturallogarithm\exponential\determinant。其中只有三角函数的替换可以用 \usepackage[notrig]{physics} 关掉。

命令未加载 physics加载 physics 后(原义去向)
\div÷,除号散度 ∇·;原来的 ÷ 移到 \divisionsymbol
\Reℜ,花体正体的 \operatorname{Re};原义移到 \real
\Imℑ,花体正体的 \operatorname{Im};原义移到 \imaginary
\sinsin,括号自己写自动给参数加括号;原义在 \sine;可用 notrig 关闭
\detdet同样自动加括号;原义在 \determinant

写成 \dv{f}{x}(g),那个 (g) 会消失

式子的一部分会从输出中消失,既不报错也不警告。 这比「样子不对」严重得多,所以这里给出实测。在 TeX Live 2024 下,\sbox0{$\dv{f}{x}$} 的宽度是 11.50076pt,而 \sbox0{$\dv{f}{x}(g)$} 也是 11.50076pt——完全相同。从 PDF 提取文字,两者都只有「df/dx」,(g) 不见踪影。

原因在参数声明。physics.sty 里的 \derivative 声明为 { s o m g d() },末尾的 d()把后面紧跟的圆括号当作参数吸走。它本是为「长形式」\dv{x}(\Psi) 准备的,即让 d/dx 作用于括号内的内容。可是当 g(此处是 x)已给出时所走的分支里,宏体根本没有引用 d() 这个参数。于是 (g) 被读走,然后被丢弃。有两种规避办法:在中间放一个空组 {},或插入显式空白。实测 $\dv{f}{x}{}(g)$ 为 24.40703pt,$\dv{f}{x}\,(g)$ 为 26.07365pt,两者的 (g) 都留在了输出中。同样的 d() 也挂在 \grad\div\curl\laplacian\dd\pdv\fdv 上,因此凡是后面紧跟括号的写法都要当心。

latex
% the (g) is read as the trailing d() argument and then thrown away:
% this prints df/dx and nothing else
$\dv{f}{x}(g)$

% either of these keeps the (g)
$\dv{f}{x}{}(g)$
$\dv{f}{x}\,(g)$

这一行为在 tex.stackexchange 上被反复讨论,甚至有一个名为 physics-patch 的宏包专门用来修正它。需要打补丁的状态持续这么久,本身就是选型时该掂量的信息。

与 siunitx 的 \qty 冲突 — 调换加载顺序解决不了

两种加载顺序结果一样,赢的都是 physics。 单位排版的标准宏包 siunitx 在近年引入 \qty 作为数量命令,与 physics 的 \qty 撞名。同时加载并编译,siunitx 会声明:Package siunitx Warning: Detected the "physics" package: omitting definition of \qty. 它甚至把规避办法一并给出,让你在导言区加上 \AtBeginDocument{\RenewCommandCopy\qty\SI}。在这种状态下写单位 \qty{2}{\metre},会被 physics 的 \qty 接走,并以 ! Missing $ inserted. 告终。把 \usepackage{physics} 放在前面或后面都没有区别:警告和报错逐字相同。

这里还有一个不该略过的细节。翻开 siunitx.sty 会看到一行 \@ifpackageloaded{physics}——siunitx 直接点名 physics 并为它让路。一个几千行规模的主要宏包,为另一个宏包写死了以其名字为条件的让步,这在 LaTeX 生态里并不常见,也说明这场冲突被踩到的次数有多广。

physics 与 mathtools 的 \abs — 星号含义正好相反

两个都加载时 \abs 会变成 physics 的那个,而且星号的含义反转。先用 mathtools 声明 \DeclarePairedDelimiter\abs{\lvert}{\rvert},再写 \usepackage{physics},一个错误也没有——physics 会默默覆盖它。但行为恰好相反。实测:mathtools 版的 \abs{\frac ab}7.5pt(固定),\abs*{\frac ab}8.50006pt(自动伸缩)。physics 版的 \abs{\frac ab}8.50006pt(自动伸缩),\abs*{\frac ab}7.5pt(固定)。也就是说,mathtools 用星号来拉伸,physics 用星号来停止拉伸

指定大小的写法也不同。mathtools 用方括号传入 \abs[\big]{x};physics 则直接跟在后面写 \abs\big{x}。在加载了 physics 的状态下用 mathtools 的写法,会得到 ! Missing delimiter (. inserted).,页面上留下「[] ||x」这样的坏输出。若打算混用两者,这种不兼容躲不开。实务上的答案是二选一。

physics2 — 什么都不覆盖的重新设计版

对 physics2 做同样的检查,没有任何一个标准命令被改写。 加载 Zhang Tingxuan 编写的 physics2,读取 \meaning\div 仍是 \mathchar"2204(依旧是 ÷),\Re\mathchar"23C\Im\mathchar"23D(依旧是花体),\sin 保持标准定义,\qty 未定义。实际排版时,$a\div b$ 得到「a ÷ b」,$\Re z$ 得到「ℜz」,$\sin(x)$ 得到「sin(x)」——与纯 LaTeX 的输出完全一致。与 siunitx 同用时零警告零报错,\qty{2}{\metre} 正确排成「2 m」。TeX Live 2024 中收录的是 v1.0.1(日期 2024-01-10)。

设计上的差别体现在加载方式。physics 一次性把所有东西搬进来,physics2 则让你逐个启用模块。在 \usepackage{physics2} 之后写上 \usephysicsmodule{ab,braket} 之类,只引入想要的功能。TeX Live 2024 随附的模块有 ab(自动伸缩定界符)、ab.braketbraketdiagmat(对角矩阵)、xmat(一般矩阵)、doubleprod,以及供 physics 迁移者使用的 ab.legacybm-um.legacynabla.legacyop.legacyqtext.legacy。不污染命名空间的代价,是命令名不像 physics 那样短:定界符写作 \ab(...)\ab[...]\ab\{...\}\ab|...|\ab<...>;对角矩阵是 \pdiagmat{1,2,3};矩阵是 \pxmat{a}{2}{2}

latex
\usepackage{physics2}
\usephysicsmodule{ab,braket,diagmat,xmat}

\[
  \ab(\frac{a}{b}), \quad \ab[\frac{a}{b}], \quad \ab|\frac{a}{b}|
\]
\[
  \bra{\phi}, \quad \ket{\psi}, \quad \braket{a}{b}, \quad \ketbra{a}{b}
\]
\[
  \pdiagmat{1,2,3}, \qquad \pxmat{a}{2}{2}
\]

有一条实务上的提醒。ab.braketbraket 两个模块不能同时加载。 把两者一起列进 \usephysicsmodule,会得到 Package physics2 Warning: You cannot load 'ab.braket' and 'braket' modules together.,只有其中一个保持有效。二者都提供狄拉克记号:ab.braket 建立在 ab 的自动伸缩机制之上,braket 则自成一体。总结实务判断——若是现在开始写的新文档,选 physics2 更稳妥;若要维护已有的 physics 文档,则要在改写成本与 \div\Re\qty 的副作用之间权衡。如果只为导数而来,还有能妥善处理高阶与多变量的 derivative 宏包,以及整理微分算子排法的 fixdif

braket 宏包 — 大写会伸缩,小写不会

若只需要狄拉克记号,Donald Arseneau 的 braket 是最小的答案。一行 \usepackage{braket},命令只有两族——固定大小的小写版 \bra{ }\ket{ }\braket{ }\set{ },以及自动伸缩的大写版 \Bra{ }\Ket{ }\Braket{ }\Set{ }。大写版的好处在于:直接写在参数内部的竖线 | 会随外层定界符一起伸长。在 \Braket 中,内部所有 | 都会长到参数的高度;在 \Set 中则是第一个 | 长高。需要双竖线时用 \|,或该环境内的别名 ||

latex
% braket: one argument, the bars written inside it
\Braket{ \phi | \frac{\partial^2}{\partial t^2} | \psi }

\Set{ x \in \mathbf{R}^2 | 0 < {|x|} < 5 }

请注意参数的数法与 physics 不同。physics 的 \braket{a}{b}两个参数,而 braket 的 \braket{a|b}一个参数,分隔用的竖线要自己写。上例中第一条排出矩阵元 ⟨φ| ∂²/∂t² |ψ⟩,外层尖括号和内部两条竖线都伸到算子的高度。第二条排出集合 { x ∈ R² | 0 < |x| < 5 },外层花括号与分隔竖线伸长,而内部的 |x| 因为包成了 {|x|} 而保持固定大小。另外 braket 没有定义外积的 \ketbra——作者建议改写成 \ket{ }\bra{ }

tensor 宏包 — 让上下指标按正确顺序排开

在张量中,指标的横向位置本身就带有含义。像 Γ^μ_{νρ} 或 R^μ_ν{}^ρ_σ,上下指标必须从左到右按正确顺序排列。可是把 ^_ 直接连写,它们会叠在同一位置,读者就分不清哪个指标在前。解决这个问题的是 Philip G. Ratcliffe 的 tensor(对 Mike Piff 原作的全面修订,v2.2)。它不需要任何外部宏包,\usepackage{tensor} 即可。

latex
\[
  M\indices{^a_b^{cd}_e}, \qquad
  \tensor{M}{^a_b^{cd}_e}, \qquad
  \tensor[^a_b^c_d]{M}{^e_f^g_h}
\]

% works in text mode as well as math mode
\nuclide[14][6]{C}

核心命令有两个。轻量的 \indices 紧跟在张量主体之后,只取指标字符串——写 M\indices{^a_b^{cd}_e},就会得到 M 后面依次是上 a、下 b、上 cd、下 e,各自保持正确的横向位置。字符串内部直接使用 ^_,多字符指标用花括号括起来,如 ^{cd}\sp\sb 作用相同)。另一个命令 \tensor 与 Mike Piff 的原作向后兼容,把主体与指标字符串分作两个参数。\tensor{M}{^a_b^{cd}_e} 的输出与上面 \indices 的例子相同,但 \tensor 的长处在于还能把指标放在主体之前,即在可选的第一个参数里传入前置指标字符串。两者都有带星形式 \indices*\tensor*,会收紧指标之间的间距,得到更标准的外观。此外,在指标字符串开头放一个 *,可让连续的上下配对左对齐。

此外还定义了 \nuclide,它在正文模式和数学模式下都能用\nuclide[14][6]{C} 会排出 ¹⁴₆C,把质量数 14 放在元素符号 C 的左上,原子序数 6 放在左下(两个数字都可省略)。另需注意:在指标字符串中,多标记的指标必须用花括号括起来,像 \mathrm 这类结构也要整体包住——写成 \indices{_{\mathrm{H}}^x}

那么究竟该用哪一个

从实测结果得出的结论是:新文档不要以 physics 作为起点。 参数被悄悄丢弃、\div\Re 含义改变、与 siunitx 的冲突无法靠调换加载顺序解决——这三件事都在无警告的情况下发生。话虽如此,physics 仍被广泛使用,合作者交来的稿件也可能正是用它写的。那时现实的做法不是改写,而是在知道 \divisionsymbol\real\imaginary 的存在以及 (g) 陷阱的前提下继续用。下表按用途给出选择。

宏包用途是否改写标准命令
physics一次性提供导数、定界符、向量分析与狄拉克记号会。\div\Re\Im\sin\det
physics2同一领域,通过 \usephysicsmodule 按模块引入不会——实测 \div\Re\sin 保持原样
braket只提供狄拉克记号与集合记号不会。小写为固定大小,大写自动伸缩
tensor保持上下指标的横向位置正确不会。只增加 \indices\tensor\nuclide
mathtools\DeclarePairedDelimiter 自定义 \abs\norm不会。名字由你决定,因此不会冲突