SymPy教程之三——基本运算

在本文中,我们将讨论使用SymPy进行表达式操作的一些最为基本的操作。对于更高级的操作,可以参考advanced expression manipulation一文。
首先我们要在Python中导入SymPy库:

from sympy import *
x, y, z = symbols("x y z")

如果你对这两行代码尚不太熟悉,可以参考我之前的两篇基础教程:
SymPy教程之一——SymPy介绍
SymPy教程之二——基本概念

表达式赋值

对于数学表达式,最基础的一项操作就是赋值。表达式赋值,就是将表达式中所包含的某个变量的所有实例,都替换为另一个数、变量或者表达式。这个操作需要使用SymPy库的subs 函数。
例如,我们先创建一个表达式,之后将x赋值为y:

expr = cos(x) + 1
expr.subs(x, y)

将会得到结果:
cos(y) + 1
对于上面我们创建的表达式expr,若要求取x为0时的值:
expr.subs(x, 0)
有的时候,我们需要将表达式中的某一个变量,用另一个表达式来代替。我在平时的科研中常常使用这种方法。有时候一个数学模型中的一个变量,往往代表着另外一个小的数学模型,通过subs方法,就可以实现合二为一。
举一个简单的例子:

d = sqrt(r)
r2 = x**2 + y**2
r3 = x**2 + y**2 +z**2
d.subs(r, r2)
d.subs(r, r3)

通过给d赋予不同的表达式r2r3,就可以求取二维、三维空间中一点到原点的距离。
运行效果如下:
image
如何给多个变量赋值?
例如我们在d.subs(r, r2)得到的公式中,如何计算点(2,3)到原点距离?
d.subs(r, r2).subs({x:2, y:3})

表达式不可改变

首先一点要注意的是,SymPy中的表达式是不可改变的(immutable),当我们对表达式执行操作时,例如执行expr.subs(x, y)时,会回返回一个新的表达式,而expr本身,是不能改变的。

字符串转化为表达式

在前面的例子中,我们使用了Python语句来生成表达式,除此之外,我们还可以使用字符串来生成表达式,这时要使用SymPy库中的sympify函数:

str_expr = "x**2 + 3*x – 1/2"
expr = sympify(str_expr)

观察字符串,我们可以发现,与使用Python语句无异。
这种方式增强了构建表达式的灵活性,使得我们不仅仅可以在编程时硬编码表达式,更可以在运行时从文件中加载表达式。

浮点求值

要对一个表达式进行求值,并得出浮点数结果,我们可以使用evalf函数。
例如,我们可以对pi进行取值:
pi.evalf()
evalf函数默认求取15位精度,但是我们可以对其进行修改,例如,对pi求取100位有效数字:
pi.evalf(100)
对于表达式,可以通过下面方式浮点求值:
expr.evalf(subs={x:0.001})
其中,将变量的值封装到字典结构中,附给evalf函数的subs

lambdify

如果你只想对表达式进行简单的取值操作的话,subsevalf就足够了。但是如果你想对表达式在多个点进行取值,例如,想得到表达式在1000个点上的结果,此时若只使用SymPy,效率就不太高。更加有效的方法是,使用NumPy库或者SciPy库。
SymPy表达式转换为能够代数求解的最简单方法,就是使用lambdify函数。
lambdify有点像lambda函数,它将SymPy中的术语转为给定数值库(通常是NumPy库)中的术语,例如:

a = numpy.arange(10)
f = lambdify(x, expr, "numpy") 
f(a) 

其中,a为要取值的点的横坐标,x为表达式expr中的变量,指定转换为NumPy库中的术语。f(a)表示进行取值,返回一个含有纵坐标的列表。
这么做的主要目的,就是提高执行效率。

总结

本文是对SymPy官网教程Basic Operations一章的翻译。我翻译了其中的大部分内容,并结合自己的理解,对内容加以调整。