爱因斯坦求和约定

列维-奇维塔符号(Levi-Civita symbol)和 爱因斯坦标记(Einstein notation)是张量运算的常用标记。

爱因斯坦求和约定(Einstein summation convention)又称为爱因斯坦标记法(Einstein notation),可以让表达式更加简洁明了。里面主要涉及两个概念:哑标(dummy index),自由标(free index)

定义

哑标:在表达式的某项中,若某下标重复出现两次,则表示要把该项指标在取值范围内遍历求和,该重复指标称为哑标,未被求和的下标被称为自由标

自由标:在表达式某项中,若某下标只出现一次,若在取值范围内轮流取该下表的任意值时,关系式恒成立,则该下标被称为自由标。

哑标只能成对出现,头则要加上特别符号说明

若重复标号不求和,应特别声明

einsum表达式

我们先看下面一个矩阵乘法的表达式

np.einsum(‘ij,jk->ik’, A, B)

我们把’->’看作输入和输出的分隔符,左侧两个输入用’, ‘ 隔开,熟悉线形代数的话,就很容易从下标理解操作了:

回顾我们前面提到的概念,’ij,jk’ 中 j 重复用了两次, 1次表示A的第二个轴,1ci表示B的第一个轴,要保证输入合法,意味着A的第二个轴和B的第一个轴做乘积,就需要A行长和B列长一致。

如果某个轴标签在输出标签消失了,则表示要沿着该轴做求和运算。同时我们也可以获取任意轴序的结果输出。了解基本规则之后,我们可以理解einsum是非常节约空间的计算函数,einsum不构建中间临时的array,直接累加的到最终结果。

einsum操作

如果A和B是1D array,A和B的形状总是合适的

Call signatureNumPy equivalentDescription
(‘i’, A)AA
(‘i->’, A)sum(A)A的所有元素和
(‘i,i->i’, A, B)A * BA和B逐元素乘积
(‘i,i’, A, B)inner(A, B)A和B的内积
(‘i,j->ij’, A, B)outer(A, B)A和B的外积

如果A和B是两个2D arrays, 假设在相应操作中,A和B的形状是合适的,那么:

Call signatureNumPy equivalentDescription
(‘ij’, A)AA
(‘ji’, A)A.TA的转置
(‘ii->i’, A)diag(A)A 的对角
(‘ii’, A)trace(A)A的迹
(‘ij->’, A)sum(A)A的所有元素和
(‘ij->j’, A)sum(A, axis=0)A的沿着axis=0的和
(‘ij->i’, A)sum(A, axis=1)A的沿着axis=1的和
(‘ij,ij->ij’, A, B)A * BA和B逐元素乘积
(‘ij,ji->ij’, A, B)A * B.TA 和 B.T 逐元素乘积
(‘ij,jk’, A, B)dot(A, B)A 和 B 的矩阵乘法
(‘ij,kj->ik’, A, B)inner(A, B)A 和 B 的内积
(‘ij,kj->ikj’, A, B)A[:, None] * BA的每一行与B的乘积
(‘ij,kl->ijkl’, A, B)A[:, :, None, None] * BA的每一个元素与B的乘积

‘->’ 符号省略时,一般时看作A末尾下表和B前面下标相同,进行的省略

einsum中的 ‘…’ 符号,在处理比较多维度时,可以像numpy array 一样使用 ‘…’ 符号省略一些维度的显式表示。例如:

np.einsum(‘…ij,ji->…’, a, b)

注意,einsum求和时,不会提升数据类型,因此处理尾款比较小的数据可能得不到预期的结果

einsum在numpy计算库中并不一定总是最快的,dot和inner 函数一般使用快速计算库blas,计算速度更快一些。

参考链接:https://blog.popkx.com/A-basic-introduction-to-NumPy-s-einsum/

发表评论