Skip to content

Slowdown in cholesky_solve between sympy 1.5 and 1.6 #19200

@oscarbenjamin

Description

@oscarbenjamin

See #19199.

I've used asv to bisect a significant slowdown in the cholesky_solve benchmark:

$ asv find sympy-1.5.1..master solve.TimeMatrixSolve2.time_cholesky_solve
...
· Greatest regression found: a8a3a3b0 <pull/18576/head~1>

That has bisected to commit a8a3a3b from #18564.

The benchmark can be tested easily with

# t.py

import sympy
from sympy import *

n = 3

A = sympy.Matrix(n, n, lambda i, j: sympy.Symbol('a{}{}'.format(i, j)))
b = sympy.Matrix(n, 1, lambda i, j: sympy.Symbol('b{}{}'.format(i, j)))
A_sym = sympy.Matrix(n, n, lambda i, j:
                     sympy.Symbol('a{}{}'.format(*sorted((i, j)))))

def time_cholesky_solve():
    A_sym.cholesky_solve(b)

# pprint(A_sym.cholesky_solve(b))

That matrix is:

In [2]: A_sym                                                                                                                                  
Out[2]: 
⎡a₀₀  a₀₁  a₀₂⎤
⎢             ⎥
⎢a₀₁  a₁₁  a₁₂⎥
⎢             ⎥
⎣a₀₂  a₁₂  a₂₂⎦

In [3]: b                                                                                                                                      
Out[3]: 
⎡b₀₀⎤
⎢   ⎥
⎢b₁₀⎥
⎢   ⎥
⎣b₂₀⎦

Both master and 1.5.1 give complicated results but on 1.5.1 it takes roughly a millisecond whereas on master it takes a couple of seconds:

$ git checkout master
$ python -m timeit -s 'from t import time_cholesky_solve' 'time_cholesky_solve()'
1 loop, best of 5: 2.36 sec per loop

$ git checkout sympy-1.5.1
$ python -m timeit -s 'from t import time_cholesky_solve' 'time_cholesky_solve()'
1 loop, best of 5: 1.61 msec per loop

Running cProfile gives:

   Ordered by: cumulative time

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
       58    0.004    0.000    9.004    0.155 __init__.py:1(<module>)
    749/1    0.019    0.000    8.908    8.908 {built-in method builtins.exec}
        1    0.000    0.000    8.908    8.908 t.py:1(<module>)
        1    0.000    0.000    6.444    6.444 matrices.py:2198(cholesky_solve)
        1    0.000    0.000    6.444    6.444 solvers.py:204(_cholesky_solve)
        1    0.000    0.000    6.149    6.149 matrices.py:390(is_positive_definite)
        1    0.000    0.000    6.149    6.149 eigen.py:675(_is_positive_definite)
      2/1    0.000    0.000    6.149    6.149 matrices.py:367(_eval_is_positive_definite)
      2/1    0.000    0.000    6.149    6.149 eigen.py:623(_eval_is_positive_definite)
        1    0.000    0.000    5.236    5.236 matrices.py:370(eigenvals)
        1    0.000    0.000    5.236    5.236 eigen.py:80(_eigenvals)
    579/3    0.006    0.000    3.203    1.068 <frozen importlib._bootstrap>:986(_find_and_load)
    579/3    0.004    0.000    3.202    1.067 <frozen importlib._bootstrap>:956(_find_and_load_unlocked)
    556/3    0.004    0.000    3.202    1.067 <frozen importlib._bootstrap>:650(_load_unlocked)
    528/3    0.006    0.000    3.202    1.067 <frozen importlib._bootstrap_external>:777(exec_module)
    979/5    0.001    0.000    3.199    0.640 <frozen importlib._bootstrap>:211(_call_with_frames_removed)
      750    0.001    0.000    2.983    0.004 densearith.py:1609(dmp_div)
 9537/750    0.065    0.000    2.981    0.004 densearith.py:1358(dmp_rr_div)
        1    0.000    0.000    2.968    2.968 polyroots.py:788(roots)
   465/61    0.002    0.000    2.870    0.047 euclidtools.py:1561(dmp_inner_gcd)
      714    0.001    0.000    2.839    0.004 densearith.py:1654(dmp_quo)
   414/61    0.001    0.000    2.833    0.046 euclidtools.py:1525(_dmp_inner_gcd)
   429/61    0.001    0.000    2.833    0.046 euclidtools.py:1269(dmp_zz_heu_gcd)
   429/61    0.001    0.000    2.833    0.046 euclidtools.py:891(_dmp_rr_trivial_gcd)
   306/19    0.002    0.000    2.832    0.149 euclidtools.py:938(_dmp_simplify_gcd)
   115/40    0.002    0.000    2.785    0.070 simplify.py:395(simplify)
        1    0.000    0.000    2.264    2.264 matrices.py:114(charpoly)
        1    0.000    0.000    2.264    2.264 determinant.py:329(_charpoly)
       60    0.001    0.000    2.234    0.037 polytools.py:6610(cancel)

It looks like the time is spent computing the eigenvalues in is_positive_definite.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      pFad - Phonifier reborn

      Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

      Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


      Alternative Proxies:

      Alternative Proxy

      pFad Proxy

      pFad v3 Proxy

      pFad v4 Proxy