2012-10-19 51 views
4

我有一些運行緩慢的Python/Numpy代碼,我認爲這是因爲使用了double for循環。這是代碼。如何在Numpy中對這個double for循環進行矢量化?

def heat(D,u0,q,tdim): 
    xdim = np.size(u0) 
    Z = np.zeros([xdim,tdim]) 
    Z[:,0]=u0; 
    for i in range(1,tdim): 
     for j in range (1,xdim-1): 
      Z[j,i]=Z[j,i-1]+D*q*(Z[j-1,i-1]-2*Z[j,i-1]+Z[j+1,i-1]) 
    return Z 

我想刪除雙循環和矢量化Z.這是我的嘗試。

def heat(D,u0,q,tdim): 
    xdim = np.size(u0) 
    Z = np.zeros([xdim,tdim]) 
    Z[:,0]=u0; 
    Z[1:,1:-1]=Z[1:-1,:-1]+D*q*(Z[:-2,:-1]-2*Z[1:-1,:-1]+Z[2:,:-1]) 
    return Z 

這不工作 - 我得到以下錯誤:

operands could not be broadcast together with shapes (24,73) (23,74) 

所以某處試圖向量化Z,我搞砸了。你能幫我發現我的錯誤嗎?

+0

當我遇到這些錯誤時,我總是試着打印一些或多或少所有東西的「len」。檢查'Z [1:,1:-1]'的長度是否正確,依此類推。 –

+0

您是否嘗試用cProfile或其他方法來分析您的代碼?我認爲你應該首先確定你真正的瓶頸。 –

+0

瓶頸在加熱,這佔用了程序時間的99.9%。所以是的,這是我需要優化的地方,我已經閱讀了矢量化會大大加快我的代碼。我剛剛在尺寸上偏離1:/ – Zack

回答

1

您不能在問題的時間維度上將擴散計算向量化,這仍然需要循環。這裏唯一明顯的優化是在numpy.diff功能(這是預編譯的C)的調用來取代拉普拉斯計算,所以你的熱傳導方程求解變成:

def heat(D,u0,q,tdim): 
    xdim = np.size(u0) 
    Z = np.zeros([xdim,tdim]) 
    Z[:,0]=u0; 

    for i in range(1,tdim): 
     Z[1:-1,i]=Z[1:-1,i-1] + D*q*np.diff(Z[:,i-1], 2) 

    return Z 

對於非平凡的空間大小,你應該會看到相當快的速度向上。

+0

I' m試圖使用http://technicaldiscovery.blogspot.com/2011/06/speeding-up-python-numpy-cython-and.html作爲指導,在那裏他們矢量化了一個類似於我的東西的double循環。在我原來的代碼中,'D * q * np.diff(Z [:,i-1],2)'實際上代替了什麼。 – Zack

+0

用'diff'計算出的第二個差值代替了Z [j-1,i-1] -2 * Z [j,i-1] + Z [j + 1,i-1])'上的內環。儘管表面上有些相似之處,但你引用的博客文章並沒有像你這樣的循環向量化。 – talonmies

0

您將無法刪除兩個for循環,因爲計算列i依賴於第i-1列(除了第一列之外)(在您的第二位代碼中)僅爲零。

你可以做的是:

def heat(D,u0,q,tdim): 
    xdim = np.size(u0) 
    Z = np.zeros([xdim,tdim]) 
    Z[:,0]=u0; 
    for i in range(1,tdim): 
     Z[1:-1,i] = Z[1:-1,i-1] + D*q*(Z[:-2,i-1] - 2*Z[1:-1,i-1] + Z[2:,i-1]) 
    return Z 

要回到你的代碼: 您填寫Z [1,1:-1]有(僅第一項)Z [1:-1, :-1]。這裏的形狀不匹配很明顯。不管第二個索引如何(因爲你將不得不循環),你的矢量化解決方案使用與非矢量化方法不同的假設:在第一個版本中,你有一個邊u0(Z [:,0])和兩個邊0(Z [0 ,:]和Z [-1 ,:]),而在向量化解中,你試圖通過填充Z [1:,i]來設置Z [-1 ,:]爲0以外的值。 。你試圖模擬哪種情況?