TL; DR:不要使用numpy的公司或SciPy的的solve
當你有一個三角形的系統,只需使用scipy.linalg.solve_triangular
至少有快速和非破壞性的解決方案check_finite=False
關鍵字參數。
我發現這個線程numpy.linalg.solve
和scipy.linalg.solve
(和scipy's lu_solve
等)之間遇到一些磕磕絆絆的差異後。我沒有Enthought的基於MKL的Numpy/Scipy,但我希望我的發現能以某種方式幫助你。
隨着與NumPy和SciPy的預構建的二進制文件(32位,在Windows 7上運行):
我看到numpy.linalg.solve
和scipy.linalg.solve
之間的顯著差異的矢量X
解決時(即X
是1乘160)。 Scipy運行時是1.23x numpy的,這是我認爲很重要的。
但是,大部分差異似乎是由於scipy的solve
檢查無效條目。當傳遞check_finite=False
到scipy.linalg.solve時,scipy的solve
運行時是1.02x numpy的。
Scipy的解決方案使用破壞性更新,即overwrite_a=True, overwrite_b=True
比numpy的解決方案(非破壞性)要快一些。 Numpy的求解運行時是1.021x破壞性的scipy.linalg.solve。只有check_finite=False
的Scipy具有運行時1.04x的破壞性情況。總之,破壞性的scipy.linalg.solve
比這兩種情況都要快得多。
以上是矢量X
。如果我製作X
的廣泛陣列,特別是160乘10000,scipy.linalg.solve
與check_finite=False
基本上與check_finite=False, overwrite_a=True, overwrite_b=True
一樣快。 Scipy的solve
(沒有任何特殊關鍵字)運行時是1.09x這個「不安全」(check_finite=False
)調用。 Numpy的solve
擁有運行時1.03x scipy在這個陣列X
案例中最快的速度。
scipy.linalg.solve_triangular
在這兩種情況下都提供了顯着的加速,但是您必須關閉輸入檢查,即通過check_finite=False
。最快解決方案的運行時間分別爲5.68x和1.76x solve_triangular
,對於矢量和陣列X
,分別爲check_finite=False
。
solve_triangular
具有破壞性的計算(overwrite_b=True
)爲您提供了對check_finite=False
頂部沒有加速(實際上爲陣列X
情況略有傷害)。
I,無知,是以前不知道的solve_triangular
並使用scipy.linalg.lu_solve
作爲三角解算器是,即,代替solve_triangular(cholS, X)
做lu_solve((cholS, numpy.arange(160)), X)
(都產生相同的答案)。但我發現lu_solve
這種方式使用運行時1.07x不安全solve_triangular
爲矢量X
的情況下,而它的運行時爲1.76x的數組X
的情況。我不知道爲什麼lu_solve
對於X
數組比慢很多,但是我們的教訓是使用solve_triangular
(沒有無限檢查)。
將數據複製到Fortran格式似乎根本沒有關係。也沒有轉換爲numpy.matrix
。
我不妨將我的非MKL Python庫與單線程(maxNumCompThreads=1
)Matlab 2013a進行比較。上述最快的Python實施方案的矢量X
案例的運行時間延長了4.5倍,胖矩陣X
案例的運行時間延長了6.3倍。
但是,這裏是我用來對這些進行基準測試的Python腳本,也許有人使用MKL加速的Numpy/Scipy可以發佈他們的數字。請注意,我只是註釋掉行n = 10000
來禁用胖矩陣X
大小寫,並執行n=1
矢量大小寫。 (對不起。)
import scipy.linalg as sla
import numpy.linalg as nla
from numpy.random import RandomState
from timeit import timeit
import numpy as np
RNG = RandomState(69)
m=160
n=1
#n=10000
Ac = RNG.randn(m,m)
if 1:
Ac = np.triu(Ac)
bc = RNG.randn(m,n)
Af = Ac.copy("F")
bf = bc.copy("F")
if 0: # Save to Matlab format
import scipy.io as io
io.savemat("b_%d.mat"%(n,), dict(A=Ac, b=bc))
import sys
sys.exit(0)
def lapper(fn, source, **kwargs):
Alocal = source[0].copy()
blocal = source[1].copy()
fn(Alocal, blocal,**kwargs)
laps = (1000 if n<=1 else 100)
def printer(t, s=''):
print ("%g seconds, %d laps, " % (t/float(laps), laps)) + s
return t/float(laps)
t=[]
print "C"
t.append(printer(timeit(lambda: lapper(sla.solve, (Ac,bc)), number=laps),
"scipy.solve"))
t.append(printer(timeit(lambda: lapper(sla.solve, (Ac,bc), check_finite=False),
number=laps), "scipy.solve, infinite-ok"))
t.append(printer(timeit(lambda: lapper(nla.solve, (Ac,bc)), number=laps),
"numpy.solve"))
#print "F" # Doesn't seem to matter
#printer(timeit(lambda: lapper(sla.solve, (Af,bf)), number=laps))
#printer(timeit(lambda: lapper(nla.solve, (Af,bf)), number=laps))
print "sla with tweaks"
t.append(printer(timeit(lambda: lapper(sla.solve, (Ac,bc), overwrite_a=True,
overwrite_b=True, check_finite=False),
number=laps), "scipy.solve destructive"))
print "Tri"
t.append(printer(timeit(lambda: lapper(sla.solve_triangular, (Ac,bc)),
number=laps), "scipy.solve_triangular"))
t.append(printer(timeit(lambda: lapper(sla.solve_triangular, (Ac,bc),
check_finite=False), number=laps),
"scipy.solve_triangular, inf-ok"))
t.append(printer(timeit(lambda: lapper(sla.solve_triangular, (Ac,bc),
overwrite_b=True, check_finite=False),
number=laps), "scipy.solve_triangular destructive"))
print "LU"
piv = np.arange(m)
t.append(printer(timeit(lambda: lapper(
lambda X,b: sla.lu_solve((X, piv),b,check_finite=False),
(Ac,bc)), number=laps), "LU"))
print "all times:"
print t
輸出的矢量情況下,上述腳本,n=1
:上述腳本爲矩陣的情況下n=10000
C
0.000739405 seconds, 1000 laps, scipy.solve
0.000624746 seconds, 1000 laps, scipy.solve, infinite-ok
0.000590003 seconds, 1000 laps, numpy.solve
sla with tweaks
0.000608365 seconds, 1000 laps, scipy.solve destructive
Tri
0.000208711 seconds, 1000 laps, scipy.solve_triangular
9.38371e-05 seconds, 1000 laps, scipy.solve_triangular, inf-ok
9.37682e-05 seconds, 1000 laps, scipy.solve_triangular destructive
LU
0.000100215 seconds, 1000 laps, LU
all times:
[0.0007394047886284343, 0.00062474593940593, 0.0005900030818282472, 0.0006083650710913095, 0.00020871054023307778, 9.383710445114923e-05, 9.37682389063692e-05, 0.00010021534750467032]
輸出:
C
0.118985 seconds, 100 laps, scipy.solve
0.113687 seconds, 100 laps, scipy.solve, infinite-ok
0.115569 seconds, 100 laps, numpy.solve
sla with tweaks
0.113122 seconds, 100 laps, scipy.solve destructive
Tri
0.0725959 seconds, 100 laps, scipy.solve_triangular
0.0634396 seconds, 100 laps, scipy.solve_triangular, inf-ok
0.0638423 seconds, 100 laps, scipy.solve_triangular destructive
LU
0.1115 seconds, 100 laps, LU
all times:
[0.11898513112988955, 0.11368747217793944, 0.11556863916356903, 0.11312182352918797, 0.07259593807427585, 0.0634396208970783, 0.06384230931663318, 0.11150022257648459]
注意上面的Python腳本可以將它的數組保存爲Matlab .MAT數據文件。這是目前禁用(if 0
,對不起),但如果啓用,您可以測試Matlab的速度完全相同的數據。下面是Matlab的時序腳本:
clear
q = load('b_10000.mat');
A=q.A;
b=q.b;
clear q
matrix_time = timeit(@() A\b)
q = load('b_1.mat');
A=q.A;
b=q.b;
clear q
vector_time = timeit(@() A\b)
你需要從Mathworks的文件交換的timeit
功能:http://www.mathworks.com/matlabcentral/fileexchange/18798-timeit-benchmarking-function。這將產生以下的輸出:
matrix_time =
0.0099989
vector_time =
2.2487e-05
這種實證分析,在Python,至少,不要當你有一個三角形的系統使用numpy的公司或SciPy的的solve
的結果,只是用scipy.linalg.solve_triangular
與至少check_finite=False
關鍵詞爭取快速和無損解決方案。
來自[_數字食譜:科學計算的藝術_,最新版本的第41頁](http://www.nr.com/oldverswitcher.html)「如果我們得到矩陣逆,無論如何後來讓它乘以一個新的右手邊來獲得一個額外的解決方案?這是行得通的,但它給出了一個非常容易出現舍入誤差的答案,並且不像新的矢量包含在集合中那樣好首先是右手邊向量「。 – Jaime 2013-03-28 05:32:39
@Jaime實際上它的[準確度](http://arxiv.org/pdf/1201.6035v1.pdf)並不像通常認爲的那麼糟糕,但仍然不能解決任何線性系統問題。 「一些廣泛使用的教科書引導讀者相信,通過將b乘以計算的反向inv(A)來求解線性方程組Ax = b是不準確的。[...]實際上,在合理的假設下,是計算出來的,x = inv(A)* b與最佳反向穩定求解器計算的解相同。「 – jorgeca 2013-03-28 12:41:11
我可以證實,使用顯式反轉似乎比調用「solve」快至少2倍。由於持久的民間智慧,我甚至沒有嘗試過這種解決方案,即使用明顯的逆過程很容易出現不準確。我會去嘗試一下,看看是否有明顯的準確性問題。謝謝。 – 2013-03-28 13:18:15