如何在SymPy中加速慢矩阵乘法?

时间:2014-10-07 23:02:34

标签: performance matrix multiplication sympy

我正在编写一个用SymPy来解决特定递归方程的工具,并发现涉及矩阵乘法的其中一个步骤需要非常长的时间。例如,如果我在iPython控制台中尝试以下操作,

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
...

我从没等过足够长的时间才能完成。

我很欣赏符号操作比数值评估慢得多,但这看起来很荒谬。使用Octave或其他线性代数包可以在几分之一秒内执行此计算。

是否有人使用SymPy的矩阵类进行行和放大列~1000,有Rational条目?

3 个答案:

答案 0 :(得分:1)

作为必须的任意精度,速度作为一个很好的

有一些pythonic类用于此目的,可能会对您的任意精度计算感兴趣

  • <强> decimal.Decimal()

  • <强> fractions.Fraction( numerator = 0, denominator = 1 )

这些对于精确计算是必不可少的,无论是大规模天文学,DEP模拟还是其他领域,精确度不得随着时间/事件/递归的长时间尺度以及对标准数字的类似威胁的计算策略进展而降低-representations。

就我个人而言,我使用decimal.Decimal()(加密序列随机性分析的精确度高达5.000.000个数字),但并不专注于“ numpy 矩阵。

Numpy可以在其matrix-dataStructures(dtype = decimal.Decimal语法等)中承载这些类型,但需要进行测试,以验证它的矢量化函数操作以及处理这些可爱的pythonic类实例后的整体速度。

效果

初步观察标准上的numpy速度(“密集”听起来很有趣)2x2 decimal.Decimal - s:

>>> 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_______________________________________________________________

dtype = numpy.float96上的内容相同

>>> aClk.start();f*f;aClk.stop()
array([[ 0.042788046,  0.74206772],
       [ 0.10081096,  0.46544855]], dtype=float96)
2979L # 2.9 msec_______________________________________________________________

对于500 x 500完全填充的 dtype = fractions.Fraction

>>> 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完全填充的密集 dtype = decimal.Decimal

>>> 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类进行任意精度计算会有巨大的性能提升。一旦发动机可以使用numerator / denominator结构进行MUL / DIV操作而不是十进制,分数有更大的空间,但是在实际情况下,您的计算方法应在体内测试精确的性能包络正在使用。

SparseMatrix&amp;的Sympy语法测试1000x1000 Tri-Diagonals

根据@tmyklebu的建议,对1000x1000 SparseMatrix进行了真正的测试,由于安装问题需要更长时间才能得到详细说明,但可能会让您对现实实施项目有更深入的了解:< /强>

>>> 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]])

答案 1 :(得分:1)

为什么不使用sympy's sparse matrices而不是密集矩阵?解决(线性)重现时出现的矩阵通常是稀疏的;一种技术为你提供了一个在第一个超对角线上具有1的矩阵,在除了底行之外的其他地方都为零,其中递归系数为。

答案 2 :(得分:1)

我没有使用sympy进行矩阵运算,但我可以通过此代码重现您遇到的缓慢情况。看来,同情中的矩阵运算并不是那么好。

我建议你使用numpy,它具有很好的矩阵运算并且非常快。这是你的代码在numpy中的副本,它在我的笔记本电脑上以1秒的速度进行乘法运算:

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]])