我使用np.einsum
来计算图形中的材料流量(在此示例中为1节点到4节点)。流量由amount
给出(amount.shape == (1, 1, 2)
维度定义了某些条件,我们称它们为a
,b
,c
)。
布尔值矩阵route
根据进入a
(b
; {的c
,y
,route.shape == (4, 1, 1, 2)
准则确定允许的流量{1}})。我标记了尺寸yabc
,y
,a
,b
。 c
等效于abc
的尺寸amount
,abc
是流的方向(0、1、2或3)。为了确定y
中的物料量,我计算了y
并得到流入np.einsum('abc,yabc->y', amount, route)
的y-dim向量。路线还有一个隐式优先级。例如,对于任何y
,任何route[0, ...] == True
都是False
,对于下一个更高的y-dim路线,任何y=1..3
是route[1, ...] == True
,依此类推。 False
(最后一个y-index)定义了包罗万象的路线,即,当先前的y-index值为False(route[3, ...]
)时,其值为True
。
这很好。但是,当我引入仅存在于(route[0] ^ route[1] ^ route[2] ^ route[3]).all() == True
而不存在于x
中的另一个条件(维度)route
时,这种逻辑似乎被打破了。下面的代码演示了该问题:
amount
是否有我可以应用到>>> import numpy as np
>>> amount = np.asarray([[[5000.0, 0.0]]])
>>> route = np.asarray([[[[[False, True]]], [[[False, True]]], [[[False, True]]]], [[[[True, False]]], [[[False, False]]], [[[False, False]]]], [[[[False, False]]], [[[True, False]]], [[[False, False]]]], [[[[False, False]]], [[[False, False]]], [[[True, False]]]]], dtype=bool)
>>> amount.shape
(1, 1, 2)
>>> Added dimension `x`
>>> # y,x,a,b,c
>>> route.shape
(4, 3, 1, 1, 2)
>>> # Attempt 1: `5000` can flow into y=1, 2 or 3. I expect
>>> # `flows1.sum() == amount.sum()` as it would be without `x`.
>>> # Correct solution would be `[0, 5000, 0, 0]` because material is routed
>>> # to y=1, and is not available for y=2 and y=3 as they are lower
>>> # priority (higher index)
>>> flows1 = np.einsum('abc,yxabc->y', amount, route)
>>> flows1
array([ 0., 5000., 5000., 5000.])
>>> # Attempt 2: try to collapse `x` => not much different, duplication
>>> np.einsum('abc,yabc->y', amount, route.any(1))
array([ 0., 5000., 5000., 5000.])
>>> # This is the flow by `y` and `x`. I'd only expect a `5000` in the
>>> # 2nd row (`[5000., 0., 0.]`) not the others.
>>> np.einsum('abc,yxabc->yx', amount, route)
array([[ 0., 0., 0.],
[5000., 0., 0.],
[ 0., 5000., 0.],
[ 0., 0., 5000.]])
的可行操作(route
也无效)来忽略x维?
另一个例子:
.all(1)
可以解释为>>> amount2 = np.asarray([[[5000.0, 1000.0]]])
>>> np.einsum('abc,yabc->y', amount2, route.any(1))
array([1000., 5000., 5000., 5000.])
被路由到1000.0
(并且没有其他y目的地)并且y=0
与目的地5000.0
,{{1} }和y=1
,但理想情况下,我只想在y=2
中显示y=3
(因为这是最低的索引和最高的目标优先级)。
解决方案尝试
以下内容有效,但不是很麻木。如果可以消除循环,那就太好了。
5000.0
换句话说,对于y=1
中的每个值,我正在寻找# Initialise destination
result = np.zeros((route.shape[0]))
# Calculate flow by maintaining all dimensions (this will cause
# double ups because `x` is not part of `amount2`
temp = np.einsum('abc,yxabc->yxabc', amount2, route)
temp_ixs = np.asarray(np.where(temp))
# For each original amount, find the destination (`y`)
for a, b, c in zip(*np.where(amount2)):
# Find where dimensions `abc` are equal in the destination.
# Take the first vector which contains `yxabc` (we get `yx` as result)
ix = np.where((temp_ixs[2:].T == [a, b, c]).all(axis=1))[0][0]
y_ix = temp_ixs.T[ix][0]
# ignored
x_ix = temp_ixs.T[ix][1]
v = amount2[a, b, c]
# build resulting destination
result[y_ix] += v
# result == array([1000., 5000., 0., 0.])
中最低的索引amount2
,以便可以将值写入yx
(x为忽略)。
temp
降低result[y] = value
维度的另一种尝试是:
>>> temp = np.einsum('abc,yxabc->yx', amount2, route)
>>> temp
# +--- value=1000 at y=0 => result[0] += 1000
# /
array([[1000., 1000., 1000.],
# +--- value=5000 at y=1 => result[1] += 5000
# /
[5000., 0., 0.],
[ 0., 5000., 0.],
[ 0., 0., 5000.]])
>>> result
array([1000., 5000., 0., 0.])
>>> amount2
array([[[5000., 1000.]]])
这基本上保留了route
的第一维给出的上述优先级。当较高优先级的数组在该子索引处已经具有True值时,任何较低优先级(较高索引)的数组都不能包含True值。尽管这比我的显式方法要好得多,但是如果将>>> r = route.any(1)
>>> for x in xrange(1, route.shape[0]):
r[x] = r[x] & (r[:x] == False).all(axis=0)
>>> np.einsum('abc,yabc->y', amount2, r)
array([1000., 5000., 0., 0.])
循环表示为numpy向量运算,那将是很棒的事情。
答案 0 :(得分:0)
我没有尝试遵循您对乘法问题的“流”式解释。我只关注计算选项。
去除不必要的尺寸后,您的数组为:
In [194]: amount
Out[194]: array([5000., 0.])
In [195]: route
Out[195]:
array([[[0, 1],
[0, 1],
[0, 1]],
[[1, 0],
[0, 0],
[0, 0]],
[[0, 0],
[1, 0],
[0, 0]],
[[0, 0],
[0, 0],
[1, 0]]])
yx
的计算公式为:
In [197]: np.einsum('a,yxa->yx',amount, route)
Out[197]:
array([[ 0., 0., 0.],
[5000., 0., 0.],
[ 0., 5000., 0.],
[ 0., 0., 5000.]])
这就是route
的这部分乘以5000。
In [198]: route[:,:,0]
Out[198]:
array([[0, 0, 0],
[1, 0, 0],
[0, 1, 0],
[0, 0, 1]])
省略einsum的RHS上的x
会导致整个维度的求和。
等效地,我们可以相乘(广播):
In [200]: (amount*route).sum(axis=2)
Out[200]:
array([[ 0., 0., 0.],
[5000., 0., 0.],
[ 0., 5000., 0.],
[ 0., 0., 5000.]])
In [201]: (amount*route).sum(axis=(1,2))
Out[201]: array([ 0., 5000., 5000., 5000.])
也许看amount*route
可以使问题可视化。您也可以使用max
,min
,argmax
等代替sum
,或在一个或多个轴上使用它。