How to accelerate slow matrix multiplication in SymPy?
我当时正在写一个工具来用SymPy解决特定的递归方程,结果发现涉及矩阵乘法的步骤之一花费的时间特别长。 例如,如果我在iPython控制台中尝试以下操作,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | In [1]: from sympy import * In [2]: A = Matrix(500, 500, lambda i,j: 2 + abs(i-j) if i-j in [-1, 0, 1] else 0) In [3]: A[0:7, 0:7] Out[3]: Matrix([ [2, 3, 0, 0, 0, 0, 0], [3, 2, 3, 0, 0, 0, 0], [0, 3, 2, 3, 0, 0, 0], [0, 0, 3, 2, 3, 0, 0], [0, 0, 0, 3, 2, 3, 0], [0, 0, 0, 0, 3, 2, 3], [0, 0, 0, 0, 0, 3, 2]]) In [4]: A * A ... |
我从来没有等待足够长的时间来完成它。
我欣赏符号处理比数值计算要慢得多,但这似乎很荒谬。 使用八度或其他线性代数包可以在几分之一秒内执行此计算。
有谁有经验将SymPy的矩阵类用于带有Rational条目的约1000行和列?
我没有使用
我将建议您使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | In [1]: import numpy as np In [2]: A = np.matrix([[2 + abs(i-j) if i-j in [-1, 0, 1] else 0 for i in range(0, 500)] for j in range(0, 500)]) In [3]: A[0:7,0:7] Out[3]: matrix([[2, 3, 0, 0, 0, 0, 0], [3, 2, 3, 0, 0, 0, 0], [0, 3, 2, 3, 0, 0, 0], [0, 0, 3, 2, 3, 0, 0], [0, 0, 0, 3, 2, 3, 0], [0, 0, 0, 0, 3, 2, 3], [0, 0, 0, 0, 0, 3, 2]]) In [4]: A * A Out[4]: matrix([[13, 12, 9, ..., 0, 0, 0], [12, 22, 12, ..., 0, 0, 0], [ 9, 12, 22, ..., 0, 0, 0], ..., [ 0, 0, 0, ..., 22, 12, 9], [ 0, 0, 0, ..., 12, 22, 12], [ 0, 0, 0, ..., 9, 12, 13]]) |
为什么不使用sympy的稀疏矩阵而不是密集矩阵?解决(线性)递归时出现的矩阵通常是稀疏的。一种技术为您提供了一个矩阵,在第一个超对角线上带有1,在除底行(递归系数所在的位置)以外的其他所有位置为零。
任意精度都是必须的,速度是一个不错的选择
有一些用于此类目的的pythonic类,您可能会对您的任意精度计算感兴趣
-
decimal.Decimal() -
fractions.Fraction( numerator = 0, denominator = 1 )
这些对于精确的计算是必不可少的,对于大型天文学,DEP模拟或其他领域而言,在随着时间/事件/递归的长标度以及对标准数字表示形式的类似威胁下,精确度绝不能随计算策略的进步而降低。
我个人使用
Numpy可以在其matrix-dataStructures(
性能
作为对标准的numpy速度的初步观察(" dense"对于这个比例来说很有趣)2x2
1 2 3 4 5 6 | >>> aClk.start();m*m;aClk.stop() array([[Decimal('0.01524157875323883675019051999'), Decimal('5.502209507697009702374335655')], [Decimal('1.524157875323883675019051999'), Decimal('11.94939027587381419881628113')]], dtype=object) 5732L # 5.7 msec_______________________________________________________________ |
而
1 2 3 4 | >>> aClk.start();f*f;aClk.stop() array([[ 0.042788046, 0.74206772], [ 0.10081096, 0.46544855]], dtype=float96) 2979L # 2.9 msec_______________________________________________________________ |
对于500 x 500完全填充的
1 2 3 4 5 6 7 | >>> aClk.start();M*M;aClk.stop() array([[Fraction(9, 64), Fraction(1, 4), Fraction(64, 25), ..., Fraction(64, 81), Fraction(16, 81), Fraction(36, 1)], .., [Fraction(1, 1), Fraction(9, 4), Fraction(4, 1), ..., Fraction(1, 4), Fraction(25, 36), Fraction(1, 1)]], dtype=object) 2692088L # 2.7 sec_<<<_Fraction_______________________________vs. 19 msec float96 |
对于500 x 500完全填充的密集
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | >>> aClk.start();D*D;aClk.stop() array([[Decimal('0.140625'), Decimal('0.25'), Decimal('2.56'), ..., Decimal('0.7901234567901234567901234568'), Decimal('0.1975308641975308641975308642'), Decimal('36')], [Decimal('3.24'), Decimal('0.25'), Decimal('0.25'), ..., Decimal('0.02040816326530612244897959185'), Decimal('0.04'), Decimal('0.1111111111111111111111111111')], [Decimal('0.1111111111111111111111111111'), Decimal('0.25'), Decimal('2.25'), ..., Decimal('0.5102040816326530612244897959'), Decimal('0.25'), Decimal('0.0625')], ..., [Decimal('0'), Decimal('5.444444444444444444444444443'), Decimal('16'), ..., Decimal('25'), Decimal('0.81'), Decimal('0.04')], [Decimal('1'), Decimal('7.111111111111111111111111113'), Decimal('1'), ..., Decimal('0'), Decimal('81'), Decimal('2.25')], [Decimal('1'), Decimal('2.25'), Decimal('4'), ..., Decimal('0.25'), Decimal('0.6944444444444444444444444444'), Decimal('1')]], dtype=object) 4789338L # 4.8 sec_<<<_Decimal_______________________________vs. 19 msec float96 2692088L # 2.7 sec_<<<_Fraction______________________________vs. 19 msec float96 |
三对角矩阵1000x1000的期望值可以低于50毫秒吗?
由于存在3,000个非零(稀疏表示)元素,而不是上面测试的完全填充的500x500矩阵中的250,000个单元,因此使用这些pythonic类进行任意精度计算的性能会有巨大的提高。一旦引擎可能在MUL / DIV操作上使用
@tmyklebu提出的对1000x1000
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | >>> F = sympy.SparseMatrix( 1000, 1000, { (0,0): 1} ) # .Fraction() >>> D = sympy.SparseMatrix( 1000, 1000, { (0,0): 1} ) # .Decimal() >>> for i in range( 1000 ): # GEN to have F & D hold ... for j in range( 1000 ): # SAME values, ... if i-j in [-1,0,1]: # but DIFF representations ... num = int( 100 * numpy.random.random() ) # ... den = int( 100 * numpy.random.random() ) + 1 # + 1 to avoid DIV!0 ... F[i,j] = fractions.Fraction( numerator = num, denominator = den ) ... D[i,j] = decimal.Decimal( str( num ) ) / decimal.Decimal( str( den ) ) # called in Zig-Zag(F*F/D*D/F*F/D*D/...) order to avoid memory-access cache artifacts >>> aClk.start();VOID=F*F;aClk.stop() 770353L # notice the 1st eval took TRIPLE LONGER 205585L # notice the 2nd+ 205364L # 0.205 sec_<<<_Fraction()____________________________vs. 0.331 sec Decimal() >>> aClk.start();VOID=D*D;aClk.stop() 383137L # 0.383 sec_<<<_Decimal()____________________________vs. 0.770 sec 1st Fraction() 390164L # 0.390 sec_<<<_Decimal()____________________________vs. 0.205 sec 2nd Fraction() 331291L # 0.331 sec_<<<_Decimal()____________________________vs. 0.205 sec 3rd Fraction() >>> F[0:4,0:4] Matrix([ [ 1/52, 6/23, 0, 0], [42/29, 29/12, 1, 0], [ 0, 57/88, 39/62, 13/57], [ 0, 0, 34/83, 26/95]]) >>> D[0:4,0:4] Matrix([ [0.0192307692307692, 0.260869565217391, 0, 0], [ 1.44827586206897, 2.41666666666667, 1.0, 0], [ 0, 0.647727272727273, 0.629032258064516, 0.228070175438596], [ 0, 0, 0.409638554216867, 0.273684210526316]]) |