在Python中实现交错乘积和求和而无循环开销

在Python中实现交错乘积和求和而无循环开销,python,numpy,Python,Numpy,为了构建一个函数,我尝试在python中实现逻辑嵌套的求和和和部分积。这样做的想法是不使用显式循环。所需的输出是一个列表(对于给定的矩阵J,由t索引) 公式是: 公式的简短口头描述:对于每个t,有3个指数i、j、k在0-(N-1)范围内。索引i,j构建一个矩阵(或2d数组),其每个元素都是索引k上某个(j,t)相关函数(不管是什么)的乘积,不包括i,j的特定值。函数仅为展平矩阵/数组上的numpy.sum 现在,下面的代码按预期工作: import numpy as np t_output

为了构建一个函数,我尝试在python中实现逻辑嵌套的求和和和部分积。这样做的想法是不使用显式循环。所需的输出是一个列表(对于给定的矩阵J,由t索引)

公式是:

公式的简短口头描述:对于每个t,有3个指数i、j、k在0-(N-1)范围内。索引i,j构建一个矩阵(或2d数组),其每个元素都是索引k上某个(j,t)相关函数(不管是什么)的乘积,不包括i,j的特定值。函数仅为展平矩阵/数组上的numpy.sum

现在,下面的代码按预期工作:

import numpy as np 
t_output = np.arange(0,10,100)
jmat = np.random.random(N**2).reshape(N,N)

def corr_mat(i,j,t,params):
  return np.prod(np.cos(\
    2.0 * t * np.delete(jmat[:,i] + jmat[:,j],(i,j)))) + \
      np.prod(np.cos(\
        2.0 * t * np.delete(jmat[:,i] - jmat[:,j],(i,j))))

def corr_time(t, jmat):
   return np.array([corr_mat(i,j,t,jmat) for i in xrange(N)\
     for j in xrange(N)]).reshape(N,N)

result = np.array([np.sum(corr_time(t,jmat)) for t in t_output])
但是“corr_time”函数中嵌套的for循环大大降低了执行速度。我试过这个

import numpy as np 
t_output = np.arange(0,10,100)
jmat = np.random.random(N**2).reshape(N,N)

def corr_mat(i,j,t,params):
  return np.prod(np.cos(\
    2.0 * t * np.delete(jmat[:,i] + jmat[:,j],(i,j)))) + \
      np.prod(np.cos(\
        2.0 * t * np.delete(jmat[:,i] - jmat[:,j],(i,j))))

i,j = np.meshgrid(range(0,N), range(0,N))

result = np.array([np.sum(corr_mat(i,j,t,params)) for t in t_output])

但这些函数并没有正确理解网格。有人能告诉我我遗漏了什么吗?提前感谢。

预先计算
jmat
sum/difference数组会在性能上产生很大的差异(45倍)

def precalc(jmat):
  JM1 = np.zeros((N,N,N))
  JM2 = np.zeros((N,N,N))
  for i in range(N):
    for j in range(N):
      for k in range(N):
        #JJ[i,j,k]=jmat[k,i]+jmat[k,j]
        if k!=i and k!=j:
          JM1[i,j,k]=jmat[k,i]+jmat[k,j]
          JM2[i,j,k]=jmat[k,i]-jmat[k,j]
  return JM1, JM2

def corr_time1(t, JM1, JM2):
    return np.prod(np.cos(2*JM1*t),axis=-1)+np.prod(np.cos(2*JM2*t),axis=-1)

JM1, JM2 = precalc(jmat)
result = np.array([np.sum(corr_time1(t,JM1,JM2)) for t in t_output])
还有进一步改进的余地。我对
precalc
采取了蛮力方法,因为我没有找到更矢量化的方法。也许还有这样的解决办法。仍然只做一次j,i,k迭代有很大帮助

我们可以通过在更大维度的数组上执行
np.prod
来矢量化最后一步,该数组使用了整个
t\u输出

def corr_time2(t, JM1, JM2):
    return np.prod(np.cos(2*JM1[None,...]*t[:,None,None,None]),axis=-1) +\
       np.prod(np.cos(2*JM2[None,...]*t[:,None,None,None]),axis=-1)
result = np.sum(corr_time2(t_output, JM1, JM2),axis=(1,2))
在这个测试用例中,节省的时间很少,仅为20%。我认为这是因为
t\u输出
只有10个元素<代码>np.arange(0100,10)。在最后一个版本中,
precalc
是最大的时间消耗者


快速
precalc
,加速比为28倍

def precalc1(jmat):
  # calc all the 'outer' sums/diffs, and zero the k=i,j terms
  ii = np.arange(jmat.shape[0])
  JM1 = jmat[:,:,None] + jmat[:,None,:]
  JM2 = jmat[:,:,None] - jmat[:,None,:]
  JM1[ii,ii,:] = 0
  JM2[ii,ii,:] = 0
  JM1[ii,:,ii] = 0
  JM2[ii,:,ii] = 0
  JM1 = JM1.transpose([1,2,0])
  JM2 = JM2.transpose([1,2,0])
  return JM1, JM2

您描述为按预期工作的代码对我来说似乎返回了
数组([2*N**2])
。是吗?
t_output=np.arange(0100,10)
将是一个更合理的
t_output
值。您需要以这样的方式编写
corr_mat
,它与
i
j
的向量值一起工作。我还尽量避免使用
np.delete
,尤其是反复使用。这是一个缓慢的操作。@DSM:它应该返回(在变量“result”中)一个大小为“t_output”的数组@hpaulj:那么索引t不需要跨越与其他索引相同的范围。原则上它可以是任何东西。非常感谢。那很好!数组切片中“无”的意义是什么?
None
np.newaxis
相同。它向数组中添加了一个新维度。通过广播
x[:,None]*y[None,:]
等于
z_ij=x_i*y_j
(对于所有i,j)