将sympy Matrix转化为LaTeX代码——快速绘制大型LaTeX矩阵

\(\LaTeX\)中绘制矩阵的基本语法是:

1
2
3
4
5
6
7
\left[
\begin{array}{ccc}
0 & y & \alpha \\
\omega g & x^2 & 5 \\
e^{i\pi} & ab & \omega \\
\end{array}
\right]

image
此外使用bmatrix等环境也ok。这种语法对于小型矩阵比如例子中的\(3\times 3\)矩阵来说,是很方便的。然而对于比较大型的矩阵,比如\(12\times 12\)的矩阵,直接用\(\LaTeX\)来写就显得有点蛋疼了,光是实现有效编写大型\(\LaTeX\)矩阵的前提——&对齐,就足以使人痛不欲生。如果能指定矩阵的逻辑,然后自动生成\(\LaTeX\)代码就好了!Pythonsympy库就可以很好地完成这个任务。

举例来说,如果我要绘制一个\(12\times 12\)的矩阵,按中线分为四块,每一块有不同的逻辑,可以首先用sympy构建这个矩阵:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import sympy as sp
dim = 12
x, y, z = sp.symbols('x, y, z')
matrix = sp.Matrix.zeros(dim, dim)
for i in range(dim):
for j in range(dim):
left = i < dim // 2
up = j < dim // 2
if left and up:
elem = x * y
elif left and not up:
elem = (-1) ** (i + j) * y
elif not left and up:
elem = z ** (i - j)
else:
elem = -z
matrix[i, j] = elem

sympy操作矩阵的语法和常见的numpy相似而又有所不同,不同之处在这一小段代码中体现在两个方面。

  1. 调用zeros方法初始化一个矩阵元都是0的矩阵时,numpy要求把矩阵大小写在一个tuple里,而sympy中没有这个要求。其原因是sympy中的Matrix都是只有两个指标的,而numpy中的ndarray可能有多个指标。实际上对于方阵sympyzeros方法可以只接受一个参数,numpy如果也这么做就显得意义不明。
  2. 操作矩阵元时sympy需要将两个指标写在同一个中括号内,比如m[0, 0],而numpy需要两个(依据指标数量而定)的中括号,比如m[0][0]numpy的逻辑是每一个中括号都取出了矩阵的一个特定维度,比如m[0]就取出了第一行,然后取出第一行第一列m[0][0]就显得很自然。而sympym[0]就直接取出了矩阵第一个元素即第一行第一列,至于m[0][0]则是对矩阵元做index了,大多数情况下是未定义的。这么来看好像numpy的接口要好一些,其实不然。numpy在做多维数组切片的时候还是要回到把指标都放在同一个中括号内的写法,否则永远只能对第一个维度做切片,相当于对indexing做了两套接口,对初学者来说还是有点confusing的。所以这两套接口哪个更优可能是见仁见智的问题。

言归正传,在获得sympy矩阵m后,接下来执行:

1
print(sp.printing.latex(matrix))

就可以得到如下输出,即我们所绘制矩阵的\(\LaTeX\)代码:

1
\left[\begin{array}{cccccccccccc}x y & x y & x y & x y & x y & x y & y & - y & y & - y & y & - y\\x y & x y & x y & x y & x y & x y & - y & y & - y & y & - y & y\\x y & x y & x y & x y & x y & x y & y & - y & y & - y & y & - y\\x y & x y & x y & x y & x y & x y & - y & y & - y & y & - y & y\\x y & x y & x y & x y & x y & x y & y & - y & y & - y & y & - y\\x y & x y & x y & x y & x y & x y & - y & y & - y & y & - y & y\\z^{6} & z^{5} & z^{4} & z^{3} & z^{2} & z & - z & - z & - z & - z & - z & - z\\z^{7} & z^{6} & z^{5} & z^{4} & z^{3} & z^{2} & - z & - z & - z & - z & - z & - z\\z^{8} & z^{7} & z^{6} & z^{5} & z^{4} & z^{3} & - z & - z & - z & - z & - z & - z\\z^{9} & z^{8} & z^{7} & z^{6} & z^{5} & z^{4} & - z & - z & - z & - z & - z & - z\\z^{10} & z^{9} & z^{8} & z^{7} & z^{6} & z^{5} & - z & - z & - z & - z & - z & - z\\z^{11} & z^{10} & z^{9} & z^{8} & z^{7} & z^{6} & - z & - z & - z & - z & - z & - z\end{array}\right]

这段代码放在equation环境里编译即可得到:
image
比起手写来显然让人愉悦得多了~
题外话,如果我们再引入\(\LaTeX\)的arydshln包,对sympy输出的\(\LaTeX\)代码做点小修改:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
\left[\
begin{array}{cccccc;{2pt/2pt}cccccc}
x y & x y & x y & x y & x y & x y & y & - y & y & - y & y & - y\\
x y & x y & x y & x y & x y & x y & - y & y & - y & y & - y & y\\
x y & x y & x y & x y & x y & x y & y & - y & y & - y & y & - y\\
x y & x y & x y & x y & x y & x y & - y & y & - y & y & - y & y\\
x y & x y & x y & x y & x y & x y & y & - y & y & - y & y & - y\\
x y & x y & x y & x y & x y & x y & - y & y & - y & y & - y & y\\
\hdashline[2pt/2pt]
z^{6} & z^{5} & z^{4} & z^{3} & z^{2} & z & - z & - z & - z & - z & - z & - z\\
z^{7} & z^{6} & z^{5} & z^{4} & z^{3} & z^{2} & - z & - z & - z & - z & - z & - z\\
z^{8} & z^{7} & z^{6} & z^{5} & z^{4} & z^{3} & - z & - z & - z & - z & - z & - z\\
z^{9} & z^{8} & z^{7} & z^{6} & z^{5} & z^{4} & - z & - z & - z & - z & - z & - z\\
z^{10} & z^{9} & z^{8} & z^{7} & z^{6} & z^{5} & - z & - z & - z & - z & - z & - z\\
z^{11} & z^{10} & z^{9} & z^{8} & z^{7} & z^{6} & - z & - z & - z & - z & - z & - z
\end{array}
\right]

就可以让分块的结构更加清晰:
image