兰姆迪菲#

This module provides convenient functions to transform SymPy expressions to lambda functions which can be used to calculate numerical values very fast.

sympy.utilities.lambdify.implemented_function(symfunc, implementation)[源代码]#

添加数字 implementation 发挥作用 symfunc .

symfunc 可以是 UndefinedFunction 实例或名称字符串。在后一种情况下,我们创建一个 UndefinedFunction 具有该名称的实例。

请注意,这是一种快速的解决方法,而不是创建特殊符号函数的通用方法。如果你想创建一个符号函数供SymPy的所有机器使用,你应该将 Function 班级。

参数:

符号函数strUndefinedFunction 实例

如果 str ,然后新建 UndefinedFunction 以这个作为名字。如果 symfunc 是未定义的函数,请使用相同名称创建新函数并附加实现的函数。

实施 :可调用

由调用的数值实现 evalf()lambdify

返回:

afunc公司 : sympy.FunctionClass系列实例

带附加实现的函数

实例

>>> from sympy.abc import x
>>> from sympy.utilities.lambdify import implemented_function
>>> from sympy import lambdify
>>> f = implemented_function('f', lambda x: x+1)
>>> lam_f = lambdify(x, f(x))
>>> lam_f(4)
5
sympy.utilities.lambdify.lambdastr(args, expr, printer=None, dummify=None)[源代码]#

返回可计算为lambda函数的字符串。

实例

>>> from sympy.abc import x, y, z
>>> from sympy.utilities.lambdify import lambdastr
>>> lambdastr(x, x**2)
'lambda x: (x**2)'
>>> lambdastr((x,y,z), [z,y,x])
'lambda x,y,z: ([z, y, x])'

虽然元组在Python 3中可能不会作为lambda的参数出现,但是lambdastr将创建一个lambda函数,该函数将解压原始参数,以便可以处理嵌套参数:

>>> lambdastr((x, (y, z)), x + y)
'lambda _0,_1: (lambda x,y,z: (x + y))(_0,_1[0],_1[1])'
sympy.utilities.lambdify.lambdify(args, expr, modules=None, printer=None, use_imps=True, dummify=False, cse=False, docstring_limit=1000)[源代码]#

将SymPy表达式转换为允许快速数值计算的函数。

警告

This function uses exec, and thus should not be used on unsanitized input.

自 1.7 版本弃用: 传递一组 args 参数已弃用,因为集合是无序的。使用有序iterable,如列表或元组。

参数:

args 名单 [符号]

一个变量或变量列表,其嵌套表示将传递给函数的参数的嵌套。

变量可以是符号、未定义函数或矩阵符号。

>>> from sympy import Eq
>>> from sympy.abc import x, y, z

变量列表应该与如何将参数传递给函数的结构相匹配。只需将参数括起来,因为它们将被传递到一个列表中。

调用像 f(x) 然后 [x] 应该是 lambdify ;在这种情况下 x 也可以使用:

>>> f = lambdify(x, x + 1)
>>> f(1)
2
>>> f = lambdify([x], x + 1)
>>> f(1)
2

调用像 f(x, y) 然后 [x, y] 将是 lambdify

>>> f = lambdify([x, y], x + y)
>>> f(1, 1)
2

用一个三元元组调用函数 f((x, y, z)) 然后 [(x, y, z)] 将是 lambdify

>>> f = lambdify([(x, y, z)], Eq(z**2, x**2 + y**2))
>>> f((3, 4, 5))
True

如果将传递两个参数,第一个是标量,第二个是带两个参数的元组,则列表中的项应与该结构匹配:

>>> f = lambdify([x, (y, z)], x + y + z)
>>> f(1, (2, 3))
6

expr :表达式

要求值的表达式、表达式列表或矩阵。

列表可以嵌套。如果表达式是列表,则输出也将是列表。

>>> f = lambdify(x, [x, [x + 1, x + 2]])
>>> f(1)
[1, [2, 3]]

如果是矩阵,则返回一个数组(对于NumPy模块)。

>>> from sympy import Matrix
>>> f = lambdify(x, Matrix([x, x + 1]))
>>> f(1)
[[1]
[2]]

注意这里的参数顺序(变量然后是表达式)用于模拟Python lambda 关键字。 lambdify(x, expr) 工作(大致)像 lambda x: expr (见 它是如何工作的 下面)。

模块 :str,可选

指定要使用的数字库。

如果未指定, 模块 默认为:

  • ["scipy", "numpy"] 如果安装了SciPy

  • ["numpy"] 如果只安装了NumPy

  • ["math", "mpmath", "sympy"] 如果两者都没有安装。

也就是说,sypy函数尽可能由以下两种方法之一替换 scipynumpy 函数,以及Python的标准库 mathmpmath 其他功能。

模块 可以是以下类型之一:

  • The strings "math", "mpmath", "numpy", "numexpr", "scipy", "sympy", or "tensorflow" or "jax". This uses the corresponding printer and namespace mapping for that module.

  • 模块(例如。, math ). 这将使用模块的全局命名空间。如果模块是上述已知模块之一,它也将使用相应的打印机和命名空间映射(即。, modules=numpy 等于 modules="numpy"

  • 一种字典,它将sypy函数的名称映射到任意函数(例如。, {{'sin': custom_sin}}

  • 一种包含上述参数组合的列表,对最先出现的条目赋予更高的优先级(例如,使用NumPy模块,但重写 sin 函数的自定义版本,可以使用 [{{'sin': custom_sin}}, 'numpy']

傻瓜 :bool,可选

提供的表达式中不是有效Python标识符的变量是否被伪符号替换。

这允许未定义的函数,如 Function('f')(t) 作为参数提供。默认情况下,只有当变量不是有效的Python标识符时,才会将它们变为dummife。

集合 dummify=True 用伪符号替换所有参数(如果 args 不是字符串)-例如,确保参数不会重新定义任何内置名称。

cse : bool, or callable, optional

Large expressions can be computed more efficiently when common subexpressions are identified and precomputed before being used multiple time. Finding the subexpressions will make creation of the 'lambdify' function slower, however.

When True, sympy.simplify.cse is used, otherwise (the default) the user may pass a function matching the cse signature.

docstring_limit : int or None

When lambdifying large expressions, a significant proportion of the time spent inside lambdify is spent producing a string representation of the expression for use in the automatically generated docstring of the returned function. For expressions containing hundreds or more nodes the resulting docstring often becomes so long and dense that it is difficult to read. To reduce the runtime of lambdify, the rendering of the full expression inside the docstring can be disabled.

When None, the full expression is rendered in the docstring. When 0 or a negative int, an ellipsis is rendering in the docstring instead of the expression. When a strictly positive int, if the number of nodes in the expression exceeds docstring_limit an ellipsis is rendered in the docstring, otherwise a string representation of the expression is rendered as normal. The default is 1000.

解释

例如,转换SymPy表达式 sin(x) + cos(x) 对数值计算它的等效NumPy函数:

>>> from sympy import sin, cos, symbols, lambdify
>>> import numpy as np
>>> x = symbols('x')
>>> expr = sin(x) + cos(x)
>>> expr
sin(x) + cos(x)
>>> f = lambdify(x, expr, 'numpy')
>>> a = np.array([1, 2])
>>> f(a)
[1.38177329 0.49315059]

此函数的主要用途是提供从SymPy表达式到NumPy、SciPy、NumExpr、mpmath和tensorflow等数值库的桥梁。一般来说,SymPy函数不适用于其他库中的对象,例如NumPy数组,而NumPy或mpmath等数值库中的函数不适用于SymPy表达式。 lambdify 通过将SymPy表达式转换为等效的数值函数,将两者连接起来。

基本工作流 lambdify 首先创建一个SymPy表达式,表示您希望计算的任何数学函数。这应该只使用SymPy函数和表达式来完成。然后,使用 lambdify 将其转换为数值计算的等效函数。例如,在我们创建的上面 expr 使用symphy符号 x 和symphy函数 sincos ,然后将其转换为等效的NumPy函数 f ,并在NumPy数组上调用它 a .

实例

>>> from sympy.utilities.lambdify import implemented_function
>>> from sympy import sqrt, sin, Matrix
>>> from sympy import Function
>>> from sympy.abc import w, x, y, z
>>> f = lambdify(x, x**2)
>>> f(2)
4
>>> f = lambdify((x, y, z), [z, y, x])
>>> f(1,2,3)
[3, 2, 1]
>>> f = lambdify(x, sqrt(x))
>>> f(4)
2.0
>>> f = lambdify((x, y), sin(x*y)**2)
>>> f(0, 5)
0.0
>>> row = lambdify((x, y), Matrix((x, x + y)).T, modules='sympy')
>>> row(1, 2)
Matrix([[1, 3]])

lambdify 可用于将SymPy表达式转换为mpmath函数。这可能比使用 evalf (在后端使用mpmath)在某些情况下。

>>> f = lambdify(x, sin(x), 'mpmath')
>>> f(1)
0.8414709848078965

处理元组参数,调用lambdified函数时应使用与创建函数时相同的参数类型:

>>> f = lambdify((x, (y, z)), x + y)
>>> f(1, (2, 4))
3

这个 flatten 函数可用于始终使用展平参数:

>>> from sympy.utilities.iterables import flatten
>>> args = w, (x, (y, z))
>>> vals = 1, (2, (3, 4))
>>> f = lambdify(flatten(args), w + x + y + z)
>>> f(*flatten(vals))
10

中存在的函数 expr 也可以携带自己的数值实现,在一个可调用的附加到 _imp_ 属性。这可以用于未定义的函数,使用 implemented_function 工厂:

>>> f = implemented_function(Function('f'), lambda x: x+1)
>>> func = lambdify(x, f(x))
>>> func(4)
5

lambdify 总是喜欢 _imp_ 实现到其他命名空间中的实现,除非 use_imps 输入参数为False。

与Tensorflow一起使用:

>>> import tensorflow as tf
>>> from sympy import Max, sin, lambdify
>>> from sympy.abc import x
>>> f = Max(x, sin(x))
>>> func = lambdify(x, f, 'tensorflow')

在tensorflow v2之后,默认情况下启用了紧急执行。如果您想获得与本教程相同的tensorflowv1和v2的兼容结果,请运行以下代码行。

>>> tf.compat.v1.enable_eager_execution()

如果启用了eager execution,就可以像使用numpy一样立即得到结果。

如果传递tensorflow对象,则可能会得到 EagerTensor 对象而不是值。

>>> result = func(tf.constant(1.0))
>>> print(result)
tf.Tensor(1.0, shape=(), dtype=float32)
>>> print(result.__class__)
<class 'tensorflow.python.framework.ops.EagerTensor'>

你可以使用 .numpy() 得到张量的numpy值。

>>> result.numpy()
1.0
>>> var = tf.Variable(2.0)
>>> result = func(var) # also works for tf.Variable and tf.Placeholder
>>> result.numpy()
2.0

它适用于任何形状数组。

>>> tensor = tf.constant([[1.0, 2.0], [3.0, 4.0]])
>>> result = func(tensor)
>>> result.numpy()
[[1. 2.]
 [3. 4.]]

笔记

  • 对于涉及大数组计算的函数,numexpr可以提供比numpy更大的加速。请注意,numexpr的可用函数比numpy更为有限,但是可以使用 implemented_function 以及用户定义的函数子类。如果指定,numexpr可能是模块中的唯一选项。numexpr函数的官方列表可以在以下位置找到:https://numexpr.readthedocs.io/en/latest/user_guide.html受支持-功能

  • 在上面的示例中,生成的函数可以接受标量值或numpy数组作为参数。但是,在某些情况下,生成的函数依赖于作为numpy数组的输入:

    >>> import numpy
    >>> from sympy import Piecewise
    >>> from sympy.testing.pytest import ignore_warnings
    >>> f = lambdify(x, Piecewise((x, x <= 1), (1/x, x > 1)), "numpy")
    
    >>> with ignore_warnings(RuntimeWarning):
    ...     f(numpy.array([-1, 0, 1, 2]))
    [-1.   0.   1.   0.5]
    
    >>> f(0)
    Traceback (most recent call last):
        ...
    ZeroDivisionError: division by zero
    

    在这种情况下,输入应该包装在numpy数组中:

    >>> with ignore_warnings(RuntimeWarning):
    ...     float(f(numpy.array([0])))
    0.0
    

    或者,如果不需要numpy功能,则可以使用另一个模块:

    >>> f = lambdify(x, Piecewise((x, x <= 1), (1/x, x > 1)), "math")
    >>> f(0)
    0
    

它是如何工作的

使用此函数时,了解它在做什么非常有帮助。在它的核心,lambdify只不过是一个名称空间转换,在一个特殊的打印机上,它可以使一些角盒正常工作。

要理解lambdify,首先我们必须正确理解Python名称空间是如何工作的。假设我们有两份文件。一个叫 sin_cos_sympy.py

# sin_cos_sympy.py

from sympy.functions.elementary.trigonometric import (cos, sin)

def sin_cos(x):
    return sin(x) + cos(x)

还有一个叫 sin_cos_numpy.py 具有

# sin_cos_numpy.py

from numpy import sin, cos

def sin_cos(x):
    return sin(x) + cos(x)

这两个文件定义了相同的函数 sin_cos . 但是,在第一个文件中, sincos 被定义为 sincos . 在第二个版本中,它们被定义为NumPy版本。

如果我们要导入第一个文件并使用 sin_cos 函数,我们会得到

>>> from sin_cos_sympy import sin_cos 
>>> sin_cos(1) 
cos(1) + sin(1)

另一方面,如果我们进口 sin_cos 从第二个文件中,我们可以得到

>>> from sin_cos_numpy import sin_cos 
>>> sin_cos(1) 
1.38177329068

在第一个例子中,我们得到了一个符号输出,因为它使用符号 sincos 来自SymPy的函数。在第二个例子中,我们得到了一个数值结果,因为 sin_cos 使用数字 sincos NumPy的函数。但是请注意 sincos 使用的不是 sin_cos 函数定义。两者兼而有之 sin_cos 定义完全相同。相反,它是基于在 sin_cos 函数已定义。

这里的关键点是,当Python中的函数引用未在函数中定义的名称时,该名称将在定义该函数的模块的“全局”命名空间中查找。

现在,在Python中,我们无需使用 exec 功能。 exec 接受一个包含Python代码块的字符串和一个应包含模块全局变量的字典。然后它执行字典中的代码,就好像它是模块全局变量一样。以下相当于 sin_cos 定义在 sin_cos_sympy.py

>>> import sympy
>>> module_dictionary = {'sin': sympy.sin, 'cos': sympy.cos}
>>> exec('''
... def sin_cos(x):
...     return sin(x) + cos(x)
... ''', module_dictionary)
>>> sin_cos = module_dictionary['sin_cos']
>>> sin_cos(1)
cos(1) + sin(1)

同样的 sin_cos_numpy

>>> import numpy
>>> module_dictionary = {'sin': numpy.sin, 'cos': numpy.cos}
>>> exec('''
... def sin_cos(x):
...     return sin(x) + cos(x)
... ''', module_dictionary)
>>> sin_cos = module_dictionary['sin_cos']
>>> sin_cos(1)
1.38177329068

现在我们可以知道 lambdify 作品。“lambdify”这个名字是因为我们可以想到 lambdify(x, sin(x) + cos(x), 'numpy') 作为 lambda x: sin(x) + cos(x) 在哪里 sincos 来自 numpy 命名空间。这也是为什么symbols参数首先出现在 lambdify ,与大多数SymPy函数不同的是,它位于表达式之后:为了更好地模拟 lambda 关键字。

lambdify 获取输入表达式(比如 sin(x) + cos(x)

  1. 将其转换为字符串

  2. 基于传入的模块创建模块全局字典(默认情况下,它使用NumPy模块)

  3. 创建字符串 "def func({{vars}}): return {{expr}}" 在哪里 {{vars}} 是用逗号分隔的变量列表,并且 {{expr}} 是在步骤1中创建的字符串,则 exec 使用module globals命名空间字符串并返回 func .

实际上,函数 lambdify 支持检查。因此,您可以通过使用 inspect.getsource?? 如果你使用的是IPython或Jupyter笔记本。

>>> f = lambdify(x, sin(x) + cos(x))
>>> import inspect
>>> print(inspect.getsource(f))
def _lambdifygenerated(x):
    return sin(x) + cos(x)

这向我们展示了函数的源代码,但没有显示它在其中定义的命名空间。我们可以通过查看 __globals__ 属性 f

>>> f.__globals__['sin']
<ufunc 'sin'>
>>> f.__globals__['cos']
<ufunc 'cos'>
>>> f.__globals__['sin'] is numpy.sin
True

这说明我们 sincos 在的命名空间中 fnumpy.sinnumpy.cos .

请注意,在每个步骤中都有一些便利层,但在核心部分,这就是 lambdify 作品。步骤1使用 LambdaPrinter 在打印模块中定义的打印机(请参阅 sympy.printing.lambdarepr ). 这允许不同的SymPy表达式为不同的模块定义如何将它们转换为字符串。您可以更改哪台打印机 lambdify 通过将自定义打印机传入 printer 争论。

第2步由某些翻译进行扩充。每个模块都有默认的翻译,但是您可以通过将列表传递给 modules 争论。例如,

>>> def mysin(x):
...     print('taking the sin of', x)
...     return numpy.sin(x)
...
>>> f = lambdify(x, sin(x), [{'sin': mysin}, 'numpy'])
>>> f(1)
taking the sin of 1
0.8414709848078965

全局字典是通过合并字典从列表中生成的 {{'sin': mysin}} 和NumPy的模块字典。合并是为了使前面的项目优先,这就是为什么 mysin 使用的是上面的,而不是 numpy.sin .

如果你想改变方式 lambdify 适用于给定函数,通常最简单的方法是修改全局字典。在更复杂的情况下,可能需要创建并传入自定义打印机。

最后,第3步增加了一些方便的操作,比如添加一个docstring。

了解如何 lambdify 在使用它时,works可以更容易地避免某些陷阱。例如,一个常见的错误是为一个模块(比如NumPy)创建一个lambdified函数,然后从另一个模块(比如SymPy表达式)向它传递对象。

例如,假设我们创建

>>> from sympy.abc import x
>>> f = lambdify(x, x + 1, 'numpy')

现在如果我们传入一个NumPy数组,我们得到这个数组加1

>>> import numpy
>>> a = numpy.array([1, 2])
>>> f(a)
[2 3]

但是如果您错误地传递了一个symphy表达式而不是NumPy数组,会发生什么情况:

>>> f(x + 1)
x + 2

这起作用了,但这只是偶然的。现在使用不同的lambdified函数:

>>> from sympy import sin
>>> g = lambdify(x, x + sin(x), 'numpy')

这在NumPy数组上工作正常:

>>> g(a)
[1.84147098 2.90929743]

但是如果我们试图传递一个同情的表达式,它就失败了

>>> g(x + 1)
Traceback (most recent call last):
...
TypeError: loop of ufunc does not support argument 0 of type Add which has
           no callable sin method

现在,让我们看看发生了什么。失败的原因是 g 电话 numpy.sin 在输入表达式上 numpy.sin 不知道如何操作SymPy对象。 一般来说,NumPy函数不知道如何对SymPy表达式进行操作,SymPy函数也不知道如何对NumPy数组进行操作。这就是lambdify存在的原因:在SymPy和NumPy之间架起了一座桥梁。

However, why is it that f did work? That's because f does not call any functions, it only adds 1. So the resulting function that is created, def _lambdifygenerated(x): return x + 1 does not depend on the globals namespace it is defined in. Thus it works, but only by accident. A future version of lambdify may remove this behavior.

请注意,这里描述的某些实现细节可能会在SymPy的未来版本中发生更改。传入自定义模块和打印机的API不会改变,但是lambda函数创建方式的细节可能会改变。但是,基本思想将保持不变,理解它将有助于理解lambdify的行为。

一般来说:应该为一个模块(比如NumPy)创建lambdified函数,并且只传递与该模块兼容的输入类型(比如NumPy数组)。 记住默认情况下,如果 module 未提供参数, lambdify 使用NumPy和SciPy命名空间创建函数。