数值计算#

象SymPy这样的符号计算机代数系统有助于构造和操作数学表达式。不幸的是,当需要在数值数据上计算这些表达式时,符号系统的性能往往很差。

Fortunately SymPy offers a number of easy-to-use hooks into other numeric systems, allowing you to create mathematical expressions in SymPy and then ship them off to the numeric system of your choice. This page documents many of the options available including the math library, the popular array computing package numpy, code generation in Fortran or C, and the use of the array compiler Aesara.

潜艇/救生艇#

潜艇是最慢但最简单的选择。它以相同的速度运行。这个 .subs(...).evalf() 方法可以用数值替换符号值,然后在SymPy中计算结果。

>>> from sympy import *
>>> from sympy.abc import x
>>> expr = sin(x)/x
>>> expr.evalf(subs={x: 3.14})
0.000507214304613640

这种方法很慢。只有在性能不是问题时才应使用此方法生成。你可以期待 .subs 需要几十微秒。它可以在原型制作时很有用,或者如果您只想看到一个值一次。

兰姆迪菲#

这个 lambdify 函数将SymPy表达式转换为Python函数,利用各种数值库。其用途如下:

>>> from sympy import *
>>> from sympy.abc import x
>>> expr = sin(x)/x
>>> f = lambdify(x, expr)
>>> f(3.14)
0.000507214304614

这里lambdify生成了一个函数 f(x) = sin(x)/x . 默认情况下,lambdify依赖于 math 标准库。这个数值计算需要几百纳秒,大约比 .subs 方法。这就是SymPy和原始Python之间的速度差异。

Lambdify可以利用各种数字后端。默认情况下,它使用 math 类库。但是它也支持 mpmath 最值得注意的是, numpy . 使用 numpy 库使生成的函数可以访问由编译的C代码支持的强大的矢量化ufunc。

>>> from sympy import *
>>> from sympy.abc import x
>>> expr = sin(x)/x
>>> f = lambdify(x, expr, "numpy")
>>> import numpy
>>> data = numpy.linspace(1, 10, 10000)
>>> f(data)
[ 0.84147098  0.84119981  0.84092844 ... -0.05426074 -0.05433146
 -0.05440211]

如果你有基于数组的数据,这可以提供相当大的加速,每一个元素10纳秒。不幸的是,numpy需要一些启动时间,并引入了几微秒的开销。

CuPy is a NumPy-compatible array library that mainly runs on CUDA, but has increasing support for other GPU manufacturers. It can in many cases be used as a drop-in replacement for numpy.

>>> f = lambdify(x, expr, "cupy")
>>> import cupy as cp
>>> data = cp.linspace(1, 10, 10000)
>>> y = f(data) # perform the computation
>>> cp.asnumpy(y) # explicitly copy from GPU to CPU / numpy array
[ 0.84147098  0.84119981  0.84092844 ... -0.05426074 -0.05433146
 -0.05440211]

JAX is a similar alternative to CuPy that provides GPU and TPU acceleration via just-in-time compilation to XLA. It too, can in some cases, be used as a drop-in replacement for numpy.

>>> f = lambdify(x, expr, "jax")
>>> import jax.numpy as jnp
>>> data = jnp.linspace(1, 10, 10000)
>>> y = f(data) # perform the computation
>>> numpy.asarray(y) # explicitly copy to CPU / numpy array
array([ 0.84147096,  0.8411998 ,  0.84092844, ..., -0.05426079,
   -0.05433151, -0.05440211], dtype=float32)

乌芬奇#

这个 autowrap 帮助中包含高效计算的模块。

  • autowrap 方法来编译由 codegen 模块,并包装二进制文件以便在python中使用。

  • binary_function 方法自动执行自动包装SymPy表达式并将其附加到 Function 对象与 implemented_function() .

  • ufuncify 生成一个二进制函数,该函数支持在numpy数组上使用比 subs/evalflambdify .

以上所有API参考如下: sympy.utilities.autowrap() .

Aesara#

SymPy has a strong connection with Aesara, a mathematical array compiler. SymPy expressions can be easily translated to Aesara graphs and then compiled using the Aesara compiler chain.

>>> from sympy import *
>>> from sympy.abc import x
>>> expr = sin(x)/x
>>> from sympy.printing.aesaracode import aesara_function
>>> f = aesara_function([x], [expr])

If array broadcasting or types are desired then Aesara requires this extra information

>>> f = aesara_function([x], [expr], dims={x: 1}, dtypes={x: 'float64'})

Aesara has a more sophisticated code generation system than SymPy's C/Fortran code printers. Among other things it handles common sub-expressions and compilation onto the GPU. Aesara also supports SymPy Matrix and Matrix Expression objects.

那我该用哪一个呢?#

The options here were listed in order from slowest and least dependencies to fastest and most dependencies. For example, if you have Aesara installed then that will often be the best choice. If you don't have Aesara but do have f2py then you should use ufuncify. If you have been comfortable using lambdify with the numpy module, but have a GPU, CuPy and JAX can provide substantial speedups with little effort.

工具

速度

品质

依赖关系

潜艇/救生艇

50美元

简单的

没有

兰姆迪菲

1美国

标量型函数

数学

小羔羊

10纳秒

向量函数

numpy

乌芬奇

10纳秒

复向量表达式

f2py,赛顿

lambdify-cupy

10纳秒

Vector functions on GPUs

cupy

lambdify-jax

10纳秒

Vector functions on CPUs, GPUs and TPUs

jax

Aesara

10纳秒

多输出,CSE,GPU

Aesara