在 numpy 或 python 中将一个向量广播到另一个不同大小的向量

Broadcasting a vector into another vector of different size in numpy or python

我有 Np links(管道)和 Nj 连接点(节点)。每个linkk都有一个起始节点i和结束节点j,以及一个link值为b。如果给定节点是 link 的起始节点,我想通过添加 b 来计算 links 对每个节点的贡献,如果给定节点减去 b是一个link的结尾。我的代码是这样的:

for k in range(Np):   #iterate over pipes
    F[i[k]]=F[i[k]]+b[k]   #add value of b if pipe k is at the start of the node
    F[j[k]]=F[j[k]]-b[k]   #substract b if pipe k is at the end of the node

用于运行此代码的数据示例:

Np=7   #number of pipes
Nj=6  #number of junctions (nodes)
pipe=[0,1,2,3,4,5,6]  #number of pipes are consecutive, k=0:Np
i=[0,1,2,3,3,3,0]     #number of start node for each pipe, 0<=i<Nj
j=[1,5,1,1,2,4,5]     #number of end node for each pipe, 0<=j<Nj
b=[1.3,0.5,1.5,2.7,1.5,2.2,3.1]   #value for each pipe, Np elements
node=[0,1,2,3,4,5]   #node numbers are consecutive, 0:Nj
F=np.zeros(Nj)   #intializing F, size is equal to number of nodes

循环的结果是:

F=[+1.3+3.1,+0.5-1.3-1.5-2.7,+1.5-1.5,+2.7+1.5+2.2,-2.2,-0.5-3.1]

F=[4.4, -5.0, 0.0, 6.4, -2.2, -3.6]

在我自己的管网中,我有 Nj=150628 和 Np=157040,所以我创建的循环花费了太多时间(大约 0.6 秒)。所以我想问一下如何对其进行矢量化?谢谢! 我尝试执行以下矢量化代码:

F=np.zeros(Nj)
F[i]=F[i]+b
F[j]=F[j]-b
F=[ 3.1, -2.2,  0. ,  2.2, -2.2, -3.1,  0. ]

这给出了错误的结果,因为可能有多个管道位于给定节点的开始或结束节点,但它只计算其中一个在任一侧。 另外,如果我创建两个稀疏矩阵 Mat_i 和 Mat_j 来表示连接到开始节点/结束节点的所有管道,然后迭代它会更快吗? (我正在使用 python 3.7)。

我刚刚设法让它与这个一起工作:

F=np.bincount(i,weights=b,minlength=Nj)-np.bincount(j,weights=b,minlength=Nj)

我也愿意使用 Numba,因为我在代码的另一部分中使用了 @njit(parallel=True) 作为标量函数,它通过使用我的 CPU 的所有 8 个线程加快了速度.

用纯 NumPy 做这件事很棘手,因为你多次“select”相同的索引(即 ij 中的值不是唯一的)。但是 Numba 将加速代码而基本上没有变化(我冒昧地使用 += 来简化):

@numba.njit
def smoke(F, b, i, j):
    for k in range(len(i)):   #iterate over pipes
        F[i[k]] += b[k]   #add value of b if pipe k is at the start of the node
        F[j[k]] -= b[k]   #subtract b if pipe k is at the end of the node

运行 如果您的输入是列表,它会像这样:

smoke(F, np.asarray(b), np.asarray(i), np.asarray(j))