YALV(又一个循环矢量化)

时间:2017-04-10 21:11:15

标签: python numpy vectorization

我可以沿着这些线写一些代码,为不同的频率计算一个符合初始条件的谐波函数。

from numpy import *
...
for i in range(n_freqs):
    # A, B so that X(t) = A cos(w t) + B sin(w t)
    # and X(t0) = x, dX/dt(t0) = v
    w = ws[i] # use a frequency
    solver = array(((+cos(w*t0), -sin(w*t0)),
                    (+sin(w*t0), +cos(w*t0)))) 
    AB = solver @ array((x[i], v[i]/w)) # and store somewhere the result

但我想写更像

的内容
Solver = array(((+cos(ws*t0), -sin(ws*t0)),
                (+sin(ws*t0), +cos(ws*t0))))
AB = Solver @ vstack((x,v/ws)

M(未)WE

from numpy import *

ws = array((1., 2., 3., 4.))
x = array((3., 6., 2., 1.))
v = x
t0 = 10.0
Solver = array(((+cos(ws*t0), -sin(ws*t0)),
                (+sin(ws*t0), +cos(ws*t0))))
AB = Solver @ vstack((x,v/ws)

给了我以下追踪

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: shapes (2,2,4) and (2,4) not aligned: 4 (dim 2) != 2 (dim 0)

,而

(Solver @ vstack((x,v/ws).T).shape # -> (2, 2, 2)

当我想要一个(2, 4)(或(4,2),我不太挑剔)时...

是否可以编写一个循环次表达式,立即计算不同三角函数的系数?

2 个答案:

答案 0 :(得分:2)

解释#1

假设:我们希望得到Solver @ b[0]Solver @ b[1]等等,即针对Solverb的第一个轴并使用{{1}进行迭代} operator。

鉴于:@,我们可以使用b = np.vstack((x,v/ws))einsummatmul/@

方法#1:使用np.einsum -

tensordot/dot

方法#2:使用np.matmul -

np.einsum('ijk,lk->lij',Solver, b).reshape(-1,2)

现在np.matmul(Solver, b.T).transpose(2,0,1).reshape(-1,2) 运算符,我相信,它取代了Python 3.5上的@,我们只需要将np.matmul替换为np.matmul(Solver, b.T)

方法#3:使用np.tensordot -

Solver @ b.T

方法#4:我们还可以使用更受欢迎的np.dot进行一些重塑,然后再将其提供给函数,就像这样 -

np.tensordot(b, Solver, axes=((-1),(-1))).reshape(-1,2)

示例运行 -

1)设置输入:

b.dot(Solver.reshape(4,-1).T).reshape(-1,2)

2)迭代获取输出:

In [7]: ws = np.array((1., 2., 3., 4.))
   ...: x = np.array((3., 6., 2., 1.))
   ...: v = x
   ...: t0 = 10.0
   ...: Solver = np.array(((+np.cos(ws*t0), -np.sin(ws*t0)), \
   ...:                     (np.sin(ws*t0), +np.cos(ws*t0))))
    ...: b = np.vstack((x,v/ws))
    ...: 

3)使用In [8]: Solver @ b[0] Out[8]: array([[-0.42715738, -2.61465808], [ 2.61465808, -0.42715738]]) In [9]: Solver @ b[1] Out[9]: array([[-1.35686862, -0.63436296], [ 0.63436296, -1.35686862]]) 并验证输出:

einsum

4)使用In [10]: np.einsum('ijk,lk->lij',Solver, b).reshape(-1,2) Out[10]: array([[-0.42715738, -2.61465808], [ 2.61465808, -0.42715738], [-1.35686862, -0.63436296], [ 0.63436296, -1.35686862]]) 并验证输出:

matmul

5)使用In [11]: np.matmul(Solver, b.T).transpose(2,0,1).reshape(-1,2) Out[11]: array([[-0.42715738, -2.61465808], [ 2.61465808, -0.42715738], [-1.35686862, -0.63436296], [ 0.63436296, -1.35686862]]) 运算符并验证输出:

@

6)使用In [14]: (Solver @ b.T).transpose(2,0,1).reshape(-1,2) Out[14]: array([[-0.42715738, -2.61465808], [ 2.61465808, -0.42715738], [-1.35686862, -0.63436296], [ 0.63436296, -1.35686862]]) 并验证输出:

tensordot

运行时测试 -

In [15]: np.tensordot(b, Solver, axes=((-1),(-1))).reshape(-1,2)
Out[15]: 
array([[-0.42715738, -2.61465808],
       [ 2.61465808, -0.42715738],
       [-1.35686862, -0.63436296],
       [ 0.63436296, -1.35686862]])

解释#2

假设:我们有类似的东西 -

In [123]: ws = np.random.randint(1,9,(10000))

In [124]: x = np.random.randint(1,9,(10000))

In [125]: v = x
     ...: t0 = 10.0
     ...: Solver = np.array(((+np.cos(ws*t0), -np.sin(ws*t0)), \
     ...:                     (np.sin(ws*t0), +np.cos(ws*t0))))
     ...: b = np.vstack((x,v/ws))
     ...: 

In [126]: %timeit np.einsum('ijk,lk->lij',Solver, b).reshape(-1,2)
     ...: %timeit np.matmul(Solver, b.T).transpose(2,0,1).reshape(-1,2)
     ...: %timeit np.tensordot(b, Solver, axes=((-1),(-1))).reshape(-1,2)
     ...: %timeit b.dot(Solver.reshape(4,-1).T).reshape(-1,2)
     ...: 
10000 loops, best of 3: 147 µs per loop
10000 loops, best of 3: 75.1 µs per loop
10000 loops, best of 3: 67.5 µs per loop
10000 loops, best of 3: 60 µs per loop

避免任何堆叠操作并直接使用for i in range(len(x)): v = x w = ws[i] solver = np.array(((+np.cos(w*t0), -np.sin(w*t0)), (+np.sin(w*t0), +np.cos(w*t0)))) p = np.array((x[i], v[i]/w)) p0 = np.matmul(solver, p) # output at each iteration sine条款的一种方法就是这样 -

cosine

答案 1 :(得分:2)

w(n,),因此cos(w*t0)也具有此形状。 Solver的布局为(2,2),但是(n,)元素为(2,2,n)。你正在用'(2,n)'点'。但是在n2

之一的哪个维度
solver(ws[i]) @ array((x[i], v[i]/ws[i]))

表示您希望最后一个维度“适合骑行”,并指向Solver的最后两个维度。在Einsum表示法中:

np.einsum('ijk,jk->ik', Solver, arr)

In [99]: Solver = np.array(((np.cos(wst),-np.sin(wst)),(np.sin(wst),np.cos(wst))))

In [101]: b = np.vstack((x,v/ws))
In [102]: b.shape
Out[102]: (2, 4)
In [103]: for i in range(4):
     ...:     print(Solver[:,:,i]@b[:,i])
     ...:     
[-0.88515125 -4.14927792]
[-0.29034338  6.70191769]
[ 0.96719065 -1.87322895]
[-0.85321635  0.57837865]
In [104]: np.einsum('ijk,jk->ik',Solver,b)
Out[104]: 
array([[-0.88515125, -0.29034338,  0.96719065, -0.85321635],
       [-4.14927792,  6.70191769, -1.87322895,  0.57837865]])

@这不是一个简单的例子,因为它假设数组堆叠在第一维上。例如Solver应为(n,2,2)b(n,2,1)`

In [106]: Solver.transpose(2,0,1)@(b.T[...,None])
Out[106]: 
array([[[-0.88515125],
        [-4.14927792]],

       [[-0.29034338],
        [ 6.70191769]],

       [[ 0.96719065],
        [-1.87322895]],

       [[-0.85321635],
        [ 0.57837865]]])
In [107]: _.shape   # need to squeeze out the last dim
Out[107]: (4, 2, 1)

如果我的推论是正确的,你的'点'维度是2,那么在该维度上迭代几乎同样快,特别是对于大的n:

res = np.zeros((2,n))
for i in range(2):
    res += Solver[:,i,:] * b[i,:]