一般问题
假设我有ndarray
v
形状(nrow,ncols,3)
。我想计算形状outer_array
的ndarray (nrow,ncols,3,3)
,其中包含每个索引(3)
处形状(nrow,ncol)
的向量的所有外积。当然,这是numpy.einsum
存在的问题。
现在,我尝试过的是:
outer_array = numpy.einsum("xyi,xyj->xyij",v,v.conjugate())
现在,我不确定这是否会奏效:尽管outer_array
具有预期的形状,但外部产品矩阵的元素与我期望的不一致。
我认为这是由于einsum
表达式中标签的选择:产品应该在x
和y
上求和,因为索引会重复,但是因为我在输出表达式中重用它们,总和的结果以某种方式广播。
另一方面,如果我写:
outer_array = numpy.einsum("xyi,uvj->...ij",v,v.conjugate())
numpy将为每对(x,y)
和(u,v)
计算外部产品的所有可能组合,从而生成一个形状为(ncols,nrow,ncols,nrow,3,3)
的数组,其中对角线(u,v) = (x,y)
将包含期望的输出。
精确问题
如何选择einsum表示法中的前两个索引以获得一个数组,其中每个索引x,y
我得到了向量v
的外积而不必求助于第二个表达?
修改 显然,这种形式似乎也有效:
outer_array = numpy.einsum("...i,...j->...ij",v,v.conjugate())
我只能欣赏numpy广播的有用性!
答案 0 :(得分:5)
'xyi,xyj->xyij'
工作的关键是输出字符串中重复xy
。
让我们使用更简单的数组:
x = np.arange(6).reshape(3,2)
np.einsum.einsum('ij->j',x)
# array([6, 9]) # sums on the 1st dimension of `x`
现在有关此x
的外部产品:
In [20]: x[:,:,None]*x[:,None,:] # shape (3,2,2)
Out[20]:
array([[[ 0, 0],
[ 0, 1]],
[[ 4, 6],
[ 6, 9]],
[[16, 20],
[20, 25]]])
这是numpy广播的一个例子(即添加尺寸并扩展它们)
在"...i,...j->...ij"
中,...
更像是现有但匿名维度的占位符。
与einsum
等价的是:
np.einsum('ij,ik->ijk',x,x)
(我应该在最后2个维度中进行非对称的计算)。
我已经找到了einsum
的纯Python工作。重点是解析参数字符串,以及它如何为iter
对象创建输入。它可以在github上找到:https://github.com/hpaulj/numpy-einsum/blob/master/einsum_py.py欢迎你玩它。它有一个debug
标志来显示中间步骤。
我的einsum有调试输出:
In [23]: einsum_py.myeinsum('ij,ik->ijk',x,x,debug=True)
# ... some parsing information
[('ij', [105, 106], 'NONE'), ('ik', [105, 107], 'NONE')]
('ijk', [105, 106, 107], 'NONE')
iter labels: [105, 106, 107],'ijk'
op_axes [[0, 1, -1], [0, -1, 1], [0, 1, 2]]
op_axes
是用于创建iter
的关键参数,该对象迭代输入和输出数组的轴。它遍历所有数组的第1轴。对于第一个op和输出,第二个轴是1,对于第二个op,第二个轴是newaxis
( - 1)。
使用ellipsis
:
In [24]: einsum_py.myeinsum('...j,...k->...jk',x,x,debug=True)
...
iter labels: [0, 106, 107],'0jk'
op_axes [[0, 1, -1], [0, -1, 1], [0, 1, 2]]
这会生成相同的op_axes
,因此计算相同。