physics 也许是 LaTeX 中被推荐得最起劲、同时又被警告得最响亮的宏包。\dv{f}{x} 写导数、\qty(...) 得到自动伸缩的括号、\ket{\psi} 写右矢——手感确实好。可这个宏包会悄悄改写你文档里已经在用的命令。\div 不再是除号 ÷ 而变成散度,\Re 不再是花体 ℜ 而变成正体的「Re」。本页介绍 physics、它的重新设计版 physics2、专攻狄拉克记号的 braket,以及用于对齐指标的 tensor,并根据实际编译与测量的结果回答「如果今天从头开始写,该选哪一个」。
\usepackage{physics} 究竟带来什么
只需一行,物理公式中反复出现的形状就变成了短命令。Sergio C. de la Barrera 编写的 physics 内部依赖 xparse 和 amsmath,并会自动加载 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)。
\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 |
\sin | sin,括号自己写 | 自动给参数加括号;原义在 \sine;可用 notrig 关闭 |
\det | det | 同样自动加括号;原义在 \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 上,因此凡是后面紧跟括号的写法都要当心。
% 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.braket、braket、diagmat(对角矩阵)、xmat(一般矩阵)、doubleprod,以及供 physics 迁移者使用的 ab.legacy、bm-um.legacy、nabla.legacy、op.legacy、qtext.legacy。不污染命名空间的代价,是命令名不像 physics 那样短:定界符写作 \ab(...)、\ab[...]、\ab\{...\}、\ab|...|、\ab<...>;对角矩阵是 \pdiagmat{1,2,3};矩阵是 \pxmat{a}{2}{2}。
\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.braket 与 braket 两个模块不能同时加载。 把两者一起列进 \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 中则是第一个 | 长高。需要双竖线时用 \|,或该环境内的别名 ||。
% 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} 即可。
\[
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 | 不会。名字由你决定,因此不会冲突 |