2013-12-09 152 views
6

我有我要乘在一起的兩個N×N的矩陣:A和B.在NumPy的,我用:NumPy的矩陣乘法效率的矩陣結構已知

import numpy as np 
C = np.dot(A, B) 

但是,我碰巧知道矩陣B只有第n行和第n列非零(這直接來自產生矩陣的分析公式,毫無疑問總是這種情況)。

希望能利用這一事實的優點和減少以生成C需要的乘法的數量,我取代了以上:

import numpy as np 
for row in range(0, N): 
    for col in range(0, N): 
     if col != n: 
      C[row, col] = A[row, n]*B[n, col] #Just one scalar multiplication 
     else: 
      C[row, col] = np.dot(A[row], B[:, n]) 

分析上,這應該減少總複雜性如下:在一般的情況下(不使用任何花哨的技巧,只是基本的矩陣乘法)C = AB,其中A和B都是NxN,應該是O(N^3)。也就是說,所有N行必須乘以所有N列,並且這些點積中的每一個都包含N次乘法=> O(N N N)= O(N^3)#

利用B的結構作爲我做過上面的事情,但是應該是O(N^2 + N^2)= O(2N^2)= O(N^2)。也就是說,所有N行必須乘以所有N列,但是,對於所有這些(除了涉及'B [:,n]'的那些行),只需要一個標量乘法:只有'B [:, m]'中的一個元素對於m!= n是非零的。當n == m時,會發生N次(每次A行必須乘以B的列n),必須發生N次標量乘法。#

但是,第一塊代碼(使用np.dot (A,B))實質上更快。我知道(通過如下信息:Why is matrix multiplication faster with numpy than with ctypes in Python?)np.dot的低層實現細節很可能會歸咎於此。所以我的問題是:如何在不犧牲NumPy的實現效率的情況下利用矩陣B的結構來提高乘法效率,而無需在c中構建自己的低級矩陣乘法?

該方法是對許多變量進行數值優化的一部分,因此,O(N^3)是棘手的,而O(N^2)可能會完成工作。

謝謝你的幫助。另外,我是新手,所以請原諒任何新手錯誤。

+3

你有沒有考慮用'cython'或其他方式將你的乘法函數直接編譯成機器代碼?在好日子裏,我可能會用'f2py'來做這件事,但我知道不是每個人都想在fortran中編寫代碼;-) – mgilson

+1

我對此也不完全確定,但是scipy可能已經解決了一些問題類似的問題使用稀疏矩陣。任何scipy大師都知道嗎? – mgilson

+2

看看'scipy.sparse',如果通過稀疏結果來稀疏結果,可以使'B'爲稀疏矩陣'B = scipy.sparse.csr_matrix(B)',然後只做'A * B'密集。我的直覺是我沒有測試過,所以這樣更有效率。 – Akavall

回答

6

如果我的理解AB正確的,那麼我不明白for循環,爲什麼你不只是由兩個非零向量相乘:

# say A & B are like this: 
n, N = 3, 5 
A = np.array(np.random.randn(N, N)) 

B = np.zeros_like(A) 
B[ n ] = np.random.randn(N) 
B[:, n] = np.random.randn(N) 

採取非零行和列B的:

rowb, colb = B[n,:], np.copy(B[:,n]) 
colb[ n ] = 0 

乘法A由這兩個向量:

X = np.outer(A[:,n], rowb) 
X[:,n] += np.dot(A, colb) 

驗證檢查:

X - np.dot(A, B) 

N=100

%timeit np.dot(A, B) 
1000 loops, best of 3: 1.39 ms per loop 

%timeit colb = np.copy(B[:,n]); colb[ n ] = 0; X = np.outer(A[:,n], B[n,:]); X[:,n] += np.dot(A, colb) 
10000 loops, best of 3: 98.5 µs per loop 
+1

啊哈!我相信你是正確的,我不需要手動進行標量乘法!所以,在實施之前我沒有足夠簡化數學/理論。感謝您的見解! – NLi10Me

1

我計時,以及如何使用sparse更快:

import numpy as np 
from scipy import sparse 

from timeit import timeit 

A = np.random.rand(100,100) 
B = np.zeros(A.shape, dtype=np.float) 

B[3] = np.random.rand(100) 
B[:,3] = np.random.rand(100) 

sparse_B = sparse.csr_matrix(B) 

n = 1000 

t1 = timeit('np.dot(A, B)', 'from __main__ import np, A, B', number=n) 
print 'dense way : {}'.format(t1) 
t2 = timeit('A * sparse_B', 'from __main__ import A, sparse_B',number=n) 
print 'sparse way : {}'.format(t2) 

結果:

dense way : 1.15117192268 
sparse way : 0.113152980804 
>>> np.allclose(np.dot(A, B), A * sparse_B) 
True 

隨着B的行數增加,使用稀疏矩陣乘法的時間優勢也應該增加。

+0

非常感謝!我注意到上面的解決方案稍微快一點,不需要額外的(稀疏)庫,但是這個解決方案更加靈活。而且,上面的解決方案實際上只是指出了我的實現中的一個缺陷,而不是直接解決這個更接近的原始問題。謝謝! – NLi10Me