5

我昨天问了一个关于区分 python 函数的问题,然后当我发现发布的答案都不能满足我评估(在某些变量中)然后绘制导数的需要时,我能够找出自己的解决方案。

以前的代码:

import sympy as sym
import math


def f(x,y):
    return x**2 + x*y**2


x, y = sym.symbols('x y')

def fprime(x,y):
    return sym.diff(f(x,y),x)

print(fprime(x,y)) #This works.

print(fprime(1,1)) 

新代码:

import sympy as sym
import math


def f(x,y):
    return x**2 + x*y**2


x, y = sym.symbols('x y')

def fprime(x,y):
    return sym.diff(f(x,y),x)

print(fprime(x,y)) #This works.

DerivativeOfF = sym.lambdify((x,y),fprime(x,y),"numpy")

print(DerivativeOfF(1,1))

如您所见,我克服了无法评估导数 fprime 的问题,方法是创建一个新函数 DerivativeOfF,它是 fprime 的“lambdified”版本。从那里,我能够评估 DerivativeOfF,并将其绘制在其中一个变量中。

我的问题是:为什么这行得通?我到底做了什么?这种方法有什么缺点?我曾尝试阅读lambdify 文档,但这让我非常困惑(我是Python 的初学者)。我的猜测是我将 Python 函数 fprime 转换为 Sympy 表达式 DerivativeOfF 或类似的东西。任何帮助解释发生的事情和原因,以及lambdify究竟做了什么(用外行的话),将不胜感激。

4

3 回答 3

3

让我们看看我是否可以说明这个动作。我很了解 Python numpy,但使用sympy的不多(但使用过其他符号代数包,例如macsyma)。

在 ipython numpy 会话中:

In [1]: def f(x,y):
   ...:     return x**2 + x*y**2
   ...: 
In [2]: f(1,3)
Out[2]: 10
In [3]: f(np.arange(1,4), np.arange(10,13))
Out[3]: array([101, 246, 441])

f是一个python函数;它返回的内容取决于输入如何处理*,**和等操作+。标量和数组有效。列表处理+*(连接,复制)但不是**

In [4]: import sympy as sym
In [5]: x, y = sym.symbols('x y')
In [6]: type(x)
Out[6]: sympy.core.symbol.Symbol
In [7]: x+y
Out[7]: x + y
In [8]: type(_)
Out[8]: sympy.core.add.Add

定义symbols创建了几个新对象。+他们以自己的象征方式处理等。

In [9]: fsym = f(x,y)
In [10]: type(fsym)
Out[10]: sympy.core.add.Add
In [11]: print(fsym)
x**2 + x*y**2

使用这 2 个符号对象调用f会创建一个新的 sym 对象。我也可以用符号和数字甚至数组的其他组合来调用它。

In [12]: f(x,0)
Out[12]: x**2
In [13]: f(1,x)
Out[13]: x**2 + 1
In [14]: f(np.arange(3), x)
Out[14]: array([0, x**2 + 1, 2*x**2 + 4], dtype=object)

如果我将此Add对象传递给sym.diff我会得到一个新Add对象

In [15]: fprime = sym.diff(fsym,x)
In [16]: fprime
Out[16]: 2*x + y**2

既不fsym也不可fprime调用。它们不是 Python 函数。fsym(1,2)不起作用。

fsym.subs可用于替换x或/和y其他值,无论是数字还是其他符号:

In [19]: fsym.subs(x,1)
Out[19]: y**2 + 1
In [20]: fsym.subs(y,2*x)
Out[20]: 4*x**3 + x**2
In [21]: fsym.subs([(x,1),(y,2)])
Out[21]: 5
In [22]: fprime.subs([(x,1),(y,2)])
Out[22]: 6

lambdify是一个sympy函数,它接受一个 sympy 对象并返回一个 Python 函数,可能numpy兼容`。

In [24]: fl = sym.lambdify((x,y), fsym, "numpy")
In [25]: fl
Out[25]: <function numpy.<lambda>>
In [26]: fl(1,2)
Out[26]: 5
In [27]: fl(np.arange(1,4), np.arange(10,13))   # cf with f(same) above
Out[27]: array([101, 246, 441])

这个fl功能和原来的差不多f。它不完全相同,例如它有一个help/doc表达式。

lambdify应用于fprime做同样的事情,但使用不同的符号表达式:

In [28]: fpl = sym.lambdify((x,y), fprime, "numpy")
In [29]: fpl(1,2)
Out[29]: 6
In [30]: fpl(np.arange(1,4), np.arange(10,13))
Out[30]: array([102, 125, 150])

python/numpy 函数或表达式与 sympy 函数或表达式之间的这种透明度是有限制的。另一个(已删除)答案试图探索这些。例如math.sin,numpy.sin和之间有区别sym.sin

在这些示例中,微分是由sym.diff函数象征性地完成的。

In [35]: fsym
Out[35]: x**2 + x*y**2
In [36]: fprime
Out[36]: 2*x + y**2

sym.lambdify只是将这些 sympy 对象中的任何一个转换为 Python 函数的一种方式。

触发示例

在讨论中选择另一个答案的例子

定义一个使用symsin/cos 版本的函数:

In [53]: def f1(x,y):
    ...:     return sym.sin(x) + x*sym.sin(y)
    ...: 
In [54]: f1(x,y)
Out[54]: x*sin(y) + sin(x)
In [55]: f1(1,2)
Out[55]: sin(1) + sin(2)
In [56]: f1(1, np.arange(3)
...
SympifyError: Sympify of expression 'could not parse '[0 1 2]'' failed, because of exception being raised:
SyntaxError: invalid syntax (<string>, line 1)

我认为那是因为sym.sin(<array>)不起作用;它必须是np.sin(...),但这不适用于符号。

和以前一样,我们可以取符号导数:

In [57]: sym.diff(f1(x,y),x)
Out[57]: sin(y) + cos(x)
In [58]: sym.diff(f1(x,y),y)
Out[58]: x*cos(y)
In [59]: sym.diff(sym.diff(f1(x,y),x),y)
Out[59]: cos(y)

同样,这些都没有功能。必须使用subsor进行评估lambdify

In [60]: f2 = sym.lambdify((x,y),f1(x,y),"numpy")
In [61]: f2
Out[61]: <function numpy.<lambda>>
In [62]: f2(1, np.arange(3))
Out[62]: array([ 0.84147098,  1.68294197,  1.75076841])

我可以使用数组输入进行评估f2,而我无法使用f1. 大概sympy是代替np.sinsym.sin

实际上,当我尝试f2使用符号进行评估时,numpy抱怨:

In [63]: f2(1,y)
...
/usr/local/lib/python3.5/dist-packages/numpy/__init__.py in <lambda>(_Dummy_30, _Dummy_31)
AttributeError: 'Symbol' object has no attribute 'sin'

In [66]: sym.diff(f2(x,y),x)
 ....
AttributeError: 'Symbol' object has no attribute 'sin'
于 2017-05-31T20:34:22.927 回答
1

在您的示例sym.lambdify中创建了一个看起来像

def DerivativeOff(x,y):
    return 2*x + y**2

这里的 numpy 后端没有任何作用,因为乘法、加法和取电是原始的 python 函数。因此,您可以将任何参数传递给DerivativeOff,尤其是 sympy 符号。DeravativeOff(x,y)在代码末尾尝试。

现在,如果您的函数包含 python 本身无法处理的更复杂的表达式,情况就会发生变化。举个例子:

def f2(x,y):
    return sym.sin(x)*y

def fprime2(x,y):
    return sym.diff(f2(x,y),x)

DerivativeOff2 = sym.lambdify((x,y), fprime2(x,y), "numpy")

print(DerivativeOff2(1,2)) #will return a number

print(DerivativeOff2(x,y)) #will give an error

在这个例子中,lambdify 需要用一些非标准的 python 替换 sin 函数。为此,它将求助于 numpy(您为这种情况指定了“numpy”)。因此 DerivatifeOff2 看起来像

def DerivativeOff2(x,y):
    return numpy.cos(x)*y

显然 numpy 不能处理 sympy 符号......

现在,如果您只想绘图,sympy 有一些绘图模块(依赖于 matplotlib):http ://docs.sympy.org/latest/modules/plotting.html

你甚至可以用它制作 3d 图。

编辑:以下工作,太:

import sympy as sym
def f(x,y):
    return sym.sin(x) + x*sym.sin(y)
def fprime(x,y):
    return sym.diff(f(x,y),y)
x, y = sym.symbols('x y')
print(fprime(1,y)) #works perfectly fine
print(fprime(x,1)) #does not work because it would mean to derive with respect to 1
print(fprime(x,y).subs(y,1)) #works, derives with respect to y, then substitutes 1 for y
于 2017-05-31T12:42:32.067 回答
0

lambdify如果要将 SymPy 表达式转换为可以数值计算的函数,则应使用。由于您在这里的兴趣是进行符号计算(微分),因此您不应该使用lambdify(无论如何,在您的计算中)。

问题在于这段代码

x, y = sym.symbols('x y')

def fprime(x,y):
    return sym.diff(f(x,y),x)

当您将变量名称设置为函数时,例如“def fprime( x , y )”,这些变量名称有效地覆盖了函数内部任何代码的变量名称x并在函数上方定义。y因此,代码sym.diff(f(x,y),x)不会对从返回的 Symbol 对象进行操作symbols('x y'),而是对传入的任何值进行操作fprime。当然,如果你将这些对象作为参数传入,它会是一样的,但你可以传入任何东西。对于f(x, y).

相反,我会完全避免使用函数。相反,创建两个符号表达式。

x, y = sym.symbols('x y')
expr = x**2 + x*y**2
fprime = diff(expr, x)

现在,要评估fprime一个数字,您可以使用subs.

fprime.subs({x: 1, y: 1})

如果此时您想创建一个使用 NumPy 将表达式计算为数字或数字数组的快速函数,那么您可以使用lambdify.

f = lambdify((x, y), expr, 'numpy')
import numpy as np
f(np.array([1]), np.array([2]))

(此外,作为一般规则,如果您lambdify使用 的表达式'numpy',则应该将 NumPy 数组作为参数传递给lambdified 函数)

于 2017-06-01T18:34:55.407 回答