基本操作¶
在这里,我们讨论SymPy中表达式操作所需的一些最基本的操作。一些更高级的操作将在 高级表达式操作 部分中讨论。
>>> from sympy import *
>>> x, y, z = symbols("x y z")
替换¶
你可能最常想对数学表达式做的事情之一就是替换。替换将表达式中所有某物实例替换为其他东西。它通过使用 subs
方法来完成。例如
>>> expr = cos(x) + 1
>>> expr.subs(x, y)
cos(y) + 1
替换通常出于以下两个原因之一:
在某个点上评估一个表达式。例如,如果我们的表达式是
cos(x) + 1
,而我们想在点x = 0
处评估它,所以我们得到cos(0) + 1
,即 2。>>> expr.subs(x, 0) 2
用另一个子表达式替换子表达式。我们可能想要这样做有两个原因。第一个原因是如果我们试图构建一个具有某种对称性的表达式,例如 \(x^{x^{x^x}}\)。为了构建这个,我们可能会从
x**y
开始,并用x**y
替换y
。然后我们会得到x**(x**y)
。如果在这个新表达式中用x**x
替换y
,我们会得到x**(x**(x**x))
,即所需的表达式。>>> expr = x**y >>> expr x**y >>> expr = expr.subs(y, x**y) >>> expr x**(x**y) >>> expr = expr.subs(y, x**x) >>> expr x**(x**(x**x))
第二种情况是如果我们想要进行一种非常受控的简化,或者可能是SymPy无法做到的简化。例如,假设我们有 \(\sin(2x) + \cos(2x)\),我们想要用 \(2\sin(x)\cos(x)\) 替换 \(\sin(2x)\)。正如我们稍后将学到的,函数
expand_trig
可以做到这一点。然而,这个函数也会展开 \(\cos(2x)\),这可能不是我们想要的。虽然有办法进行这种精确的简化,我们将在 高级表达式操作 部分学习其中的一些方法,但一个简单的方法就是直接用 \(2\sin(x)\cos(x)\) 替换 \(\sin(2x)\)。>>> expr = sin(2*x) + cos(2*x) >>> expand_trig(expr) 2*sin(x)*cos(x) + 2*cos(x)**2 - 1 >>> expr.subs(sin(2*x), 2*sin(x)*cos(x)) 2*sin(x)*cos(x) + cos(2*x)
关于 subs
有两点需要注意。首先,它返回一个新的表达式。SymPy 对象是不可变的。这意味着 subs
不会就地修改它。例如
>>> expr = cos(x)
>>> expr.subs(x, 0)
1
>>> expr
cos(x)
>>> x
x
在这里,我们看到执行 expr.subs(x, 0)
后 expr
保持不变。事实上,由于 SymPy 表达式是不可变的,没有任何函数会就地改变它们。所有函数都将返回新的表达式。
要一次性执行多个替换,请将 (旧, 新)
对的列表传递给 subs
。
>>> expr = x**3 + 4*x*y - z
>>> expr.subs([(x, 2), (y, 4), (z, 0)])
40
通常将此与列表推导结合使用非常有用,以便一次性完成大量类似的替换。例如,假设我们有 \(x^4 - 4x^3 + 4x^2 - 2x + 3\),我们希望将所有偶数次幂的 \(x\) 替换为 \(y\),以得到 \(y^4 - 4x^3 + 4y^2 - 2x + 3\)。
>>> expr = x**4 - 4*x**3 + 4*x**2 - 2*x + 3
>>> replacements = [(x**i, y**i) for i in range(5) if i % 2 == 0]
>>> expr.subs(replacements)
-4*x**3 - 2*x + y**4 + 4*y**2 + 3
将字符串转换为 SymPy 表达式¶
sympify
函数(即 sympify
,不要与 simplify
混淆)可以用来将字符串转换为 SymPy 表达式。
例如
>>> str_expr = "x**2 + 3*x - 1/2"
>>> expr = sympify(str_expr)
>>> expr
x**2 + 3*x - 1/2
>>> expr.subs(x, 2)
19/2
警告
sympify
使用了 eval
。不要在未净化的输入上使用它。
evalf
¶
要将数值表达式计算为浮点数,请使用 evalf
。
>>> expr = sqrt(8)
>>> expr.evalf()
2.82842712474619
SymPy 可以以任意精度计算浮点表达式。默认情况下,使用 15 位精度,但你可以将任意数字作为参数传递给 evalf
。让我们计算 \(\pi\) 的前 100 位。
>>> pi.evalf(100)
3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117068
要在某点对包含符号的表达式进行数值评估,我们可能会先使用 subs
再使用 evalf
,但更高效且数值稳定的方法是将替换传递给 evalf
使用 subs
标志,该标志接受一个 Symbol: point
对的字典。
>>> expr = cos(2*x)
>>> expr.evalf(subs={x: 2.4})
0.0874989834394464
有时在表达式求值后,会留下小于所需精度的舍入误差。用户可以通过将 chop
标志设置为 True 来选择删除这些数字。
>>> one = cos(1)**2 + sin(1)**2
>>> (one - 1).evalf()
-0.e-124
>>> (one - 1).evalf(chop=True)
0
lambdify
¶
subs
和 evalf
适合进行简单的评估,但如果你打算在多个点上评估一个表达式,有更高效的方法。例如,如果你想在一个千点上评估一个表达式,使用 SymPy 会比实际需要的慢得多,特别是如果你只关心机器精度。相反,你应该使用像 NumPy 和 SciPy 这样的库。
将 SymPy 表达式转换为可进行数值计算的表达式的最简单方法是使用 lambdify
函数。lambdify
的作用类似于 lambda
函数,只不过它将 SymPy 名称转换为给定数值库的名称,通常是 NumPy。例如
>>> import numpy
>>> a = numpy.arange(10)
>>> expr = sin(x)
>>> f = lambdify(x, expr, "numpy")
>>> f(a)
[ 0. 0.84147098 0.90929743 0.14112001 -0.7568025 -0.95892427
-0.2794155 0.6569866 0.98935825 0.41211849]
警告
lambdify
使用 eval
。不要在未经净化的输入上使用它。
你可以使用其他库而不是 NumPy。例如,要使用标准库的 math 模块,请使用 "math"
。
>>> f = lambdify(x, expr, "math")
>>> f(0.1)
0.0998334166468
要在 lambdify 中使用它不了解的数值库,请传递一个 sympy_name:numerical_function
对的字典。例如
>>> def mysin(x):
... """
... My sine. Note that this is only accurate for small x.
... """
... return x
>>> f = lambdify(x, expr, {"sin":mysin})
>>> f(0.1)
0.1