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와 함께 써도 경고 0, 오류 0이었고 \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} 처럼 씁니다.

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.braketab의 자동 확장 구조 위에 얹힌 판이고 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, 왼쪽 아래에 원자번호 6을 두어 ¹⁴₆C 를 조판합니다(숫자는 둘 다 생략 가능). 다만 첨자 문자열 안에서는 여러 토큰짜리 첨자를 반드시 중괄호로 묶어야 하고, \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을 직접 정의고쳐 쓰지 않음. 이름을 직접 정하므로 충돌하지 않음