pythonsagempmath

Why does installing sagemath improve the performance of mpmath in python?


I noticed that the performance of mpmath, as oddly as it sounds, depends on whether sagemath is installed or not, regardless of whether the sage module is loaded in the current session. In particular, I experienced this for operations with multiple precision floats.

Example:

from mpmath import mp
import time
mp.prec = 650

t = time.time()
for i in range(1000000):
    x_mpmath + y_mpmath
w = time.time()
print('plus:\t', (w-t), 'μs')
t = time.time()
for i in range(1000000):
    x_mpmath * y_mpmath
w = time.time()
print('times:\t', (w-t), 'μs')

# If sagemath is installed:
# plus:  0.12919950485229492 μs
# times: 0.17601895332336426 μs
#
# If sagemath is *not* installed:
# plus:  0.6239776611328125 μs
# times: 0.6283771991729736 μs

While in both cases the module mpmath is the exact same

import mpmath
print(mpmath.__file__)
# /usr/lib/python3.9/site-packages/mpmath/__init__.py

I thought that mpmath's backend would depend on some sagemath dependency, and if that is missing it falls back to a less optimized one, but I cannot figure out what it is precisely. My goal is to be able to install only the required packages to speed up mpmath instead of installing all of sagemath.

Since this may very well be dependent on how things are packaged, you might need to have details on my system: I am using Arch Linux and all packages are updated to the most recent versions (sagemath 9.3, mpmath 1.2.1, python 3.9.5).


Solution

  • I found the explanation. In /usr/lib/python3.9/site-packages/mpmath/libmp/backend.py at line 82 there is

    if 'MPMATH_NOSAGE' not in os.environ:
        try:
            import sage.all
            import sage.libs.mpmath.utils as _sage_utils
            sage = sage.all
            sage_utils = _sage_utils
            BACKEND = 'sage'
            MPZ = sage.Integer
        except:
            pass
    

    This loads all of sage if sagemath is installed and also sets it as a backend. This means that the following library is loaded next:

            import sage.libs.mpmath.ext_libmp as ext_lib
    

    From /usr/lib/python3.9/site-packages/mpmath/libmp/libmpf.py at line 1407. By looking at the __file__ of that module, one sees that it's a .so object, hence compiled, thus faster.

    This also means that by exporting MPMATH_NOSAGE to any nonempty value will force the backend to be the default one (python or gmpy) and indeed I can confirm that the code I wrote in the question does run slower in this case, even with sagemath installed.