NumPy参考 >例行程序 >线性代数(numpy.linalg) > numpy.einsum
numpy.
einsum
(下标,* operands,out = None,dtype = None,order ='K',casting ='safe',optimize = False )[源代码] ¶评估操作数上的爱因斯坦求和约定。
使用爱因斯坦求和约定,可以以简单的方式表示许多常见的多维线性代数数组运算。在隐式模式下,einsum
将计算这些值。
在显式模式下,einsum
通过禁用或强制对指定的下标标签求和,可以提供更大的灵活性来计算其他数组操作,而这些操作可能不被视为经典的爱因斯坦求和操作。
请参阅注释和示例以进行澄清。
将要求和的下标指定为下标标签的逗号分隔列表。除非包含显式指示符“->”以及精确输出形式的下标标签,否则将执行隐式(经典的爱因斯坦求和)计算。
这些是用于操作的数组。
如果提供的话,将在此数组中进行计算。
如果提供,则强制计算使用指定的数据类型。请注意,您可能还必须提供一个更宽松的转换 参数以允许进行转换。默认为无。
控制输出的内存布局。“ C”表示它应该是连续的。“ F”表示它应该是Fortran连续的,如果输入全为“ F”,则“ A”表示它应该是“ F”,否则为“ C”。“ K”表示它应尽可能靠近输入,包括任意排列的轴。默认值为“ K”。
控制可能发生的数据类型转换。不建议将其设置为“不安全”,因为这可能会对累积产生不利影响。
“否”表示完全不应该转换数据类型。
“ equiv”表示仅允许字节顺序更改。
“安全”表示只允许保留值的强制类型转换。
“ same_kind”表示仅允许安全类型转换或同一类型内的类型转换,例如float64到float32。
“不安全”表示可能会进行任何数据转换。
默认为“安全”。
控制是否应进行中间优化。如果False和True将默认设置为“贪婪”算法,则不会进行优化。还接受该np.einsum_path
函数的显式收缩列表。请参阅np.einsum_path
以获取更多详细信息。默认为False。
基于爱因斯坦求和约定的计算。
笔记
1.6.0版中的新功能。
爱因斯坦求和约定可用于计算许多多维的线性代数数组运算。einsum
提供了一种简洁的表示方式。
这些操作的非详尽列表,可以通过进行计算einsum
,下面列出了示例:
跟踪数组numpy.trace
。
返回对角线numpy.diag
。
数组轴求和numpy.sum
。
换位和排列numpy.transpose
。
矩阵乘法和点积。numpy.matmul
numpy.dot
向量内部和外部乘积。numpy.inner
numpy.outer
广播,逐元素和标量乘法numpy.multiply
。
张量收缩numpy.tensordot
。
链式数组操作,按有效的计算顺序,numpy.einsum_path
。
下标字符串是用逗号分隔的下标标签列表,其中每个标签均指相应操作数的维。每当重复标签时,都会对其进行求和,因此
等效于。如果标签仅出现一次,则不会对其进行求和,因此将生成一个视图,且没有任何更改。另一个示例
描述了传统的矩阵乘法,并且等效于
。一个操作数中重复的下标标签取对角线。例如,等效于。np.einsum('i,i', a, b)
np.inner(a,b)
np.einsum('i', a)
a
np.einsum('ij,jk', a, b)
np.matmul(a,b)
np.einsum('ii', a)
np.trace(a)
在隐式模式下,选择的下标很重要,因为输出的轴按字母顺序重新排序。这意味着在进行转置时不会影响2D数组
。此外,由于下标'h'在下标'i'之前,因此
返回矩阵乘法,而返回乘法
的转置。np.einsum('ij', a)
np.einsum('ji', a)
np.einsum('ij,jk', a, b)
np.einsum('ij,jh', a, b)
在显式模式下,可以通过指定输出下标标签直接控制输出。这需要标识符“->”以及输出下标标签的列表。此功能增加了功能的灵活性,因为在需要时可以禁用或强制求和。呼叫
就像和一样。区别在于默认情况下不允许广播。另外,直接指定输出下标标签的顺序,因此返回矩阵乘法,这与上面的隐式模式示例不同。np.einsum('i->', a)
np.sum(a, axis=-1)
np.einsum('ii->i', a)
np.diag(a)
einsum
np.einsum('ij,jh->ih', a, b)
要启用和控制广播,请使用省略号。默认的NumPy样式广播是通过在每个术语的左侧添加省略号来完成的,例如。要沿第一个轴和最后一个轴进行跟踪,您可以这样做,或者使用最左边的索引而不是最右边的索引来做矩阵矩阵乘积,一个人可以这样做
。np.einsum('...ii->...i', a)
np.einsum('i...i', a)
np.einsum('ij...,jk...->ik...', a, b)
当只有一个操作数时,没有轴求和,也没有提供输出参数,则返回操作数视图,而不是新的数组。因此,将对角线设为
会产生视图(在1.10.0版中进行了更改)。np.einsum('ii->i', a)
einsum
还提供了另一种方式将下标和操作数提供为。如果未提供此格式的输出形状,则将以隐式模式计算,否则将显式执行。下面的示例使用两个参数方法进行了相应的调用。einsum(op0, sublist0, op1, sublist1, ..., [sublistout])
einsum
einsum
1.10.0版中的新功能。
现在,只要输入数组可写,从einsum返回的视图就可以写。例如,现在将具有与2D阵列对角线相同的效果
,并将返回2D阵列对角线的可写视图。np.einsum('ijk...->kji...', a)
np.swapaxes(a, 0, 2)
np.einsum('ii->i', a)
1.12.0版中的新功能。
添加了optimize
将优化einsum表达式的收缩顺序的参数。对于具有三个或更多操作数的压缩,这可以大大增加计算效率,但需要在计算过程中增加内存占用量。
通常,采用“贪心”算法,根据经验测试,该算法在大多数情况下会返回最佳路径。在某些情况下,“最优”将通过更昂贵,更详尽的搜索返回最高级路径。对于迭代计算,建议一次计算最佳路径并通过将其作为参数提供来重用该路径。下面给出一个例子。
请参阅numpy.einsum_path
以获取更多详细信息。
例子
>>> a = np.arange(25).reshape(5,5)
>>> b = np.arange(5)
>>> c = np.arange(6).reshape(2,3)
矩阵的痕迹:
>>> np.einsum('ii', a)
60
>>> np.einsum(a, [0,0])
60
>>> np.trace(a)
60
提取对角线(需要显式形式):
>>> np.einsum('ii->i', a)
array([ 0, 6, 12, 18, 24])
>>> np.einsum(a, [0,0], [0])
array([ 0, 6, 12, 18, 24])
>>> np.diag(a)
array([ 0, 6, 12, 18, 24])
轴上的总和(要求使用显式形式):
>>> np.einsum('ij->i', a)
array([ 10, 35, 60, 85, 110])
>>> np.einsum(a, [0,1], [0])
array([ 10, 35, 60, 85, 110])
>>> np.sum(a, axis=1)
array([ 10, 35, 60, 85, 110])
对于更高维的数组,可以使用省略号来求和单个轴:
>>> np.einsum('...j->...', a)
array([ 10, 35, 60, 85, 110])
>>> np.einsum(a, [Ellipsis,1], [Ellipsis])
array([ 10, 35, 60, 85, 110])
计算矩阵转置,或重新排列任意数量的轴:
>>> np.einsum('ji', c)
array([[0, 3],
[1, 4],
[2, 5]])
>>> np.einsum('ij->ji', c)
array([[0, 3],
[1, 4],
[2, 5]])
>>> np.einsum(c, [1,0])
array([[0, 3],
[1, 4],
[2, 5]])
>>> np.transpose(c)
array([[0, 3],
[1, 4],
[2, 5]])
矢量内积:
>>> np.einsum('i,i', b, b)
30
>>> np.einsum(b, [0], b, [0])
30
>>> np.inner(b,b)
30
矩阵向量乘法:
>>> np.einsum('ij,j', a, b)
array([ 30, 80, 130, 180, 230])
>>> np.einsum(a, [0,1], b, [1])
array([ 30, 80, 130, 180, 230])
>>> np.dot(a, b)
array([ 30, 80, 130, 180, 230])
>>> np.einsum('...j,j', a, b)
array([ 30, 80, 130, 180, 230])
广播和标量乘法:
>>> np.einsum('..., ...', 3, c)
array([[ 0, 3, 6],
[ 9, 12, 15]])
>>> np.einsum(',ij', 3, c)
array([[ 0, 3, 6],
[ 9, 12, 15]])
>>> np.einsum(3, [Ellipsis], c, [Ellipsis])
array([[ 0, 3, 6],
[ 9, 12, 15]])
>>> np.multiply(3, c)
array([[ 0, 3, 6],
[ 9, 12, 15]])
矢量外积:
>>> np.einsum('i,j', np.arange(2)+1, b)
array([[0, 1, 2, 3, 4],
[0, 2, 4, 6, 8]])
>>> np.einsum(np.arange(2)+1, [0], b, [1])
array([[0, 1, 2, 3, 4],
[0, 2, 4, 6, 8]])
>>> np.outer(np.arange(2)+1, b)
array([[0, 1, 2, 3, 4],
[0, 2, 4, 6, 8]])
张量收缩:
>>> a = np.arange(60.).reshape(3,4,5)
>>> b = np.arange(24.).reshape(4,3,2)
>>> np.einsum('ijk,jil->kl', a, b)
array([[4400., 4730.],
[4532., 4874.],
[4664., 5018.],
[4796., 5162.],
[4928., 5306.]])
>>> np.einsum(a, [0,1,2], b, [1,0,3], [2,3])
array([[4400., 4730.],
[4532., 4874.],
[4664., 5018.],
[4796., 5162.],
[4928., 5306.]])
>>> np.tensordot(a,b, axes=([1,0],[0,1]))
array([[4400., 4730.],
[4532., 4874.],
[4664., 5018.],
[4796., 5162.],
[4928., 5306.]])
可写的返回数组(从版本1.10.0开始):
>>> a = np.zeros((3, 3))
>>> np.einsum('ii->i', a)[:] = 1
>>> a
array([[1., 0., 0.],
[0., 1., 0.],
[0., 0., 1.]])
省略号用法示例:
>>> a = np.arange(6).reshape((3,2))
>>> b = np.arange(12).reshape((4,3))
>>> np.einsum('ki,jk->ij', a, b)
array([[10, 28, 46, 64],
[13, 40, 67, 94]])
>>> np.einsum('ki,...k->i...', a, b)
array([[10, 28, 46, 64],
[13, 40, 67, 94]])
>>> np.einsum('k...,jk', a, b)
array([[10, 28, 46, 64],
[13, 40, 67, 94]])
链式数组操作。对于更复杂的收缩,可以通过重复计算“贪婪”路径或预先计算“最佳”路径并使用einsum_path
插入(自1.12.0版开始)重复应用来
加快速度。对于较大的阵列,性能改进尤其重要:
>>> a = np.ones(64).reshape(2,4,8)
基本einsum
:〜1520ms(在3.1GHz Intel i5上达到基准)。
>>> for iteration in range(500):
... _ = np.einsum('ijk,ilm,njm,nlk,abc->',a,a,a,a,a)
次优einsum
(由于重复的路径计算时间):〜330ms
>>> for iteration in range(500):
... _ = np.einsum('ijk,ilm,njm,nlk,abc->',a,a,a,a,a, optimize='optimal')
贪婪einsum
(更快的最佳路径近似):〜160ms
>>> for iteration in range(500):
... _ = np.einsum('ijk,ilm,njm,nlk,abc->',a,a,a,a,a, optimize='greedy')
最佳einsum
(某些情况下为最佳使用模式):〜110ms
>>> path = np.einsum_path('ijk,ilm,njm,nlk,abc->',a,a,a,a,a, optimize='optimal')[0]
>>> for iteration in range(500):
... _ = np.einsum('ijk,ilm,njm,nlk,abc->',a,a,a,a,a, optimize=path)