加快SymPy中符号行列式的计算

时间:2016-05-04 11:48:39

标签: sympy symbolic-math

我有一个4x4矩阵A,在每个条目中都有相当长但很简单的符号表达式。涉及约30个不同的符号。 “简单”是指这些符号仅使用加法/减法,乘法/除法和整数幂组合。 “长”是指如果我打印出矩阵,它会覆盖三到四个屏幕值。

我需要这个矩阵的决定因素。或者,更具体地说,我知道行列式是一个特定符号中的四阶多项式,我需要这个多项式的系数。 A.det()在运行数小时后不会终止,因此我需要采用不同的方法。有任何想法吗?到目前为止,我已尝试在simplify的每个元素上抛出各种A函数,但没有成功。

我是否有一些策略可以让SymPy知道我的表达式的简单结构,或者我知道结果是其中一个符号中的多项式?

2 个答案:

答案 0 :(得分:3)

也许可以创建4x4行列式的通用表达式

In [30]: A = Matrix(4, 4, symbols('A:4:4'))

In [31]: A
Out[31]:
⎡A₀₀  A₀₁  A₀₂  A₀₃⎤
⎢                  ⎥
⎢A₁₀  A₁₁  A₁₂  A₁₃⎥
⎢                  ⎥
⎢A₂₀  A₂₁  A₂₂  A₂₃⎥
⎢                  ⎥
⎣A₃₀  A₃₁  A₃₂  A₃₃⎦

In [32]: A.det()
Out[32]:
A₀₀⋅A₁₁⋅A₂₂⋅A₃₃ - A₀₀⋅A₁₁⋅A₂₃⋅A₃₂ - A₀₀⋅A₁₂⋅A₂₁⋅A₃₃ + A₀₀⋅A₁₂⋅A₂₃⋅A₃₁ + A₀₀⋅A₁₃⋅A₂₁⋅A₃₂ - A₀₀⋅A₁₃⋅A₂₂⋅A₃₁ - A₀₁⋅A₁₀⋅A₂₂⋅A₃₃ + A₀₁⋅A₁₀⋅A₂₃⋅A₃₂ + A₀₁⋅A₁₂⋅A₂₀⋅
A₃₃ - A₀₁⋅A₁₂⋅A₂₃⋅A₃₀ - A₀₁⋅A₁₃⋅A₂₀⋅A₃₂ + A₀₁⋅A₁₃⋅A₂₂⋅A₃₀ + A₀₂⋅A₁₀⋅A₂₁⋅A₃₃ - A₀₂⋅A₁₀⋅A₂₃⋅A₃₁ - A₀₂⋅A₁₁⋅A₂₀⋅A₃₃ + A₀₂⋅A₁₁⋅A₂₃⋅A₃₀ + A₀₂⋅A₁₃⋅A₂₀⋅A₃₁ - A₀₂⋅A₁
₃⋅A₂₁⋅A₃₀ - A₀₃⋅A₁₀⋅A₂₁⋅A₃₂ + A₀₃⋅A₁₀⋅A₂₂⋅A₃₁ + A₀₃⋅A₁₁⋅A₂₀⋅A₃₂ - A₀₃⋅A₁₁⋅A₂₂⋅A₃₀ - A₀₃⋅A₁₂⋅A₂₀⋅A₃₁ + A₀₃⋅A₁₂⋅A₂₁⋅A₃₀

然后在条目中替换

之类的内容
A.det().subs(zip(list(A), list(your_matrix)))
但是,生成4x4行列式的SymPy很慢是一个错误。您应该在https://github.com/sympy/sympy/issues/new报告。

编辑(这不适合评论)

看起来Matrix.det正在调用简化函数。对于3x3和更小的矩阵,明确地写出行列式公式,但对于较大的矩阵,使用Bareis算法计算。您可以看到简化函数(cancel)被称为here的位置,这是计算的一部分,但最终会做很多工作,因为它试图简化您的非常大的表达式。仅进行取消决定因素本身所需的简化可能更为明智。我为此打开了an issue

加快这种速度的另一种可能性,即我不确定是否有效,将是选择不同的决定因素算法。选项为Matrix.det(method=alg),其中alg"bareis"(默认值),"berkowitz""det_LU"之一。

答案 1 :(得分:0)

您描述为多项式比率的就是所谓的有理函数: https://en.wikipedia.org/wiki/Rational_function

SymPy的polys模块确实具有表示有理函数的方法,尽管它们可能很慢,尤其是在有很多变量的情况下。

在sympy 1.7中有一个新的矩阵实现,该实现仍处于实验阶段,但是基于polys模块并且可以处理有理函数。我们可以通过快速创建随机矩阵来对其进行测试:

In [35]: import random                                                                                                            

In [36]: from sympy import random_poly, symbols, Matrix                                                                           

In [37]: randpoly = lambda : random_poly(random.choice(symbols('x:z')), 2, 0, 2)                                                  

In [38]: randfunc = lambda : randpoly() / randpoly()                                                                              

In [39]: M = Matrix([randfunc() for _ in range(16)]).reshape(4, 4)                                                                

In [40]: M                                                                                                                        
Out[40]: 
⎡     2              2            2              2       ⎤
⎢  2⋅z  + 1       2⋅z  + z     2⋅z  + z + 2     x  + 2   ⎥
⎢  ────────     ────────────   ────────────   ────────── ⎥
⎢   2            2                 2             2       ⎥
⎢  y  + 2⋅y     y  + 2⋅y + 1      x  + 1      2⋅z  + 2⋅z ⎥
⎢                                                        ⎥
⎢  2              2                  2        2          ⎥
⎢ y  + y + 1   2⋅x  + 2⋅x + 1       z        z  + 2⋅z + 1⎥
⎢ ──────────   ──────────────     ──────     ────────────⎥
⎢     2            2               2           2         ⎥
⎢  2⋅y  + 2       y  + 2⋅y        y  + 1      x  + x + 2 ⎥
⎢                                                        ⎥
⎢     2           2                2            2        ⎥
⎢  2⋅z  + 2    2⋅z  + 2⋅z + 2     y  + 1     2⋅y  + y + 2⎥
⎢────────────  ──────────────   ──────────   ────────────⎥
⎢   2             2                2             2       ⎥
⎢2⋅z  + z + 1  2⋅x  + 2⋅x + 2   2⋅y  + 2⋅y      x  + 2   ⎥
⎢                                                        ⎥
⎢    2               2            2             2        ⎥
⎢ 2⋅y  + 2⋅y      2⋅y  + y     2⋅x  + x + 1  2⋅x  + x + 1⎥
⎢ ──────────      ────────     ────────────  ────────────⎥
⎢    2              2                 2          2       ⎥
⎣   z  + 2         x  + 2          2⋅y          x  + 2   ⎦

如果将其转换为新的矩阵实现,则可以使用charpoly方法计算行列式:

In [41]: from sympy.polys.domainmatrix import DomainMatrix                                                                        

In [42]: dM = DomainMatrix.from_list_sympy(*M.shape, M.tolist())                                                                  

In [43]: dM.domain                                                                                                                
Out[43]: ZZ(x,y,z)

In [44]: dM.domain.field                                                                                                          
Out[44]: Rational function field in x, y, z over ZZ with lex order

In [45]: %time det = dM.charpoly()[-1] * (-1)**M.shape[0]                                                                         
CPU times: user 22 s, sys: 231 ms, total: 22.3 s
Wall time: 23 s

这比上面@asmeurer建议的方法要慢,但是它以规范形式输出作为扩展多项式的比率。特别是,这意味着您可以立即确定行列式是否为零(对于所有x,y,z)。时间也花费了等效的cancel,但是实现比Matrix.det更有效。

这花了多长时间很大程度上取决于最终输出的复杂程度,您可以从其字符串表示的长度中了解到这一点(我不会展示全部!):

In [46]: len(str(det))                                                                                                            
Out[46]: 54458

In [47]: str(det)[:80]                                                                                                            
Out[47]: '(16*x**16*y**7*z**4 + 48*x**16*y**7*z**2 + 32*x**16*y**7 + 80*x**16*y**6*z**4 + '

在某个时候,应该可以将其集成到主要的Matrix类中,或者以其他方式使DomainMatrix类更可公开访问。