文章目录
(三)基本操作
In [32]: from sympy import *
In [33]: x, y, z = symbols("x y z")
1 替换
对于数学表达式,最常见的一件事情是替换。替换是将表达式中某个对象替换为其他对象。它是使用subs
方法完成的。例如
In [34]: expr = cos(x) + 1
In [35]: expr.subs(x, y)
Out[35]: cos(y) + 1
替换通常有以下两个作用:
1 在某一点上计算表达式的值。例如,已知表达式是cos(x)+1
,如果我们想在x=0
的点上计算它,那么我们得到cos(0)+1
,也就是2。
In [36]: expr.subs(x, 0)
Out[36]: 2
2 用另一个子表达式替换表达式的子表达式。这么做主要有两个原因:
第一个原因是构建一个具有某种对称性的表达式
x
x
x
x
x^{x^{x^{x}}}
xxxx,我们可以从x**y
开始,用x**y
替换y
。然后我们得到x**(x**y)
。然后我们用x**x
替换这个新表达式中的y,我们将得到所需的表达式x**(x**(x**x))
。
In [38]: expr = x**y
In [39]: expr
Out[39]: x**y
In [40]: expr = expr.subs(y, x**y)
In [41]: expr
Out[41]: x**(x**y)
In [42]: expr = expr.subs(y, x**x)
In [43]: expr
Out[43]: x**(x**(x**x))
第二个原因是执行SymPy无法默认执行的简化。例如,有表达式sin(2x)+cos(2x)
,我们想用2 sin(x)cos(x)
替换sin(2x)
。
虽然函数SymPy中的expand_trig
可以实现这一点:
In [44]: expr = sin(2*x) + cos(2*x)
In [45]: expand_trig(expr)
Out[45]: 2*sin(x)*cos(x) + 2*cos(x)**2 - 1
但是这个函数还将同时扩展cos(2x)
,这可能是我们不需要的。
此时subs
就派上用场了。
In [46]: expr.subs(sin(2*x), 2*sin(x)*cos(x))
Out[46]: 2*sin(x)*cos(x) + cos(2*x)
关于sub
,有两点要注意。
首先,SymPy对象是不可变的,这意味着subs
不会修改它。例如
In [50]: expr = cos(x)
In [51]: expr.subs(x, 0)
Out[51]: 1
In [52]: expr
Out[52]: cos(x)
In [53]: x
Out[53]: x
若要同时执行多个替换,可将(旧值、新值)
配对的列表传递给sub
。
In [54]: expr = x**3 + 4*x*y - z
In [55]: expr.subs([(x, 2), (y, 4), (z, 0)])
Out[55]: 40
还可以与列表结合,一次完成大量相似的替换:
In [59]: expr = x**4 - 4*x**3 + 4*x**2 - 2*x + 3
In [60]: replacements = [(x**i, y**i) for i in range(5) if i % 2 == 0]
In [61]: replacements
Out[61]: [(1, 1), (x**2, y**2), (x**4, y**4)]
In [62]: expr.subs(replacements)
Out[62]: -4*x**3 - 2*x + y**4 + 4*y**2 + 3
2 将字符串转换为SymPy表达式
sympify
函数(注意不要与simplify
混淆)可用于将字符串转换为SymPy表达式。
例如:
In [64]: str_expr = "x**2 + 3*x - 1/2"
In [65]: expr = sympify(str_expr)
In [66]: expr
Out[66]: x**2 + 3*x - 1/2
In [67]: expr.subs(x, 2)
Out[67]: 19/2
警告:sympify
使用了eval
。输入必须初始化。
反例:
expr = sympify(“x**2 + 3*x - 1/2”)
3 evalf
要将数值表达式求值为浮点数,则使用evalf
。
In [69]: expr = sqrt(8)
In [70]: expr.evalf()
Out[70]: 2.82842712474619
可以指定小数点位数,比如 π \pi π小数点后100位:
In [72]: pi.evalf(100)
Out[72]: 3.1415926535897932384626433832795028841971693993751058209749445923078164
06286208998628034825342117068
要对符号的表达式在某一个点上进行数值计算,我们可以使用subs
后跟evalf
,但是使用subs
flag将替换传递给evalf
更有效,数值上更稳定,它需要一个符号字典:点对( point pairs)。
In [73]: expr = cos(2*x)
In [74]: expr.evalf(subs={x: 2.4})
Out[74]: 0.0874989834394464
有时,在计算表达式之后,仍存在小于所需精度的舍入误差。用户可以通过将chop
flag设置为True
来自行删除这些数字。
In [77]: one = cos(1)**2 + sin(1)**2
In [78]: (one - 1).evalf()
Out[78]: -0.e-124
In [79]: (one - 1).evalf(chop=True)
Out[79]: 0
3 lambdify
如果你想做简单的求值,使用subs
和evalf
就够了,但是如果你想在许多点上求值表达式,有更有效的方法。
例如,如果你想在一千个点上计算一个表达式,使用SymPy会很慢,特别是在你只关心机器精度的时候。此时,你应该使用NumPy和SciPy这样的库。
将SymPy表达式转换为可进行数值计算的表达式的最简单方法是使用lambdify
函数。lambdify
的作用类似于lambda
函数,只是表达不同,例如
In [85]: import numpy
In [86]: a = numpy.arange(10)
In [87]: expr = sin(x)
In [89]: f = lambdify(x, expr, "numpy")
In [90]: f(a)
Out[90]:
array([ 0. , 0.84147098, 0.90929743, 0.14112001, -0.7568025 ,
-0.95892427, -0.2794155 , 0.6569866 , 0.98935825, 0.41211849])
注意:lambdify
使用eval
。输入必须初始化。
你也可以使用NumPy以外的其他库。例如math
。
In [93]: f = lambdify(x, expr, "math")
In [94]: f(0.1)
Out[94]: 0.09983341664682815
要将lambdify
用于自定义的数值函数,可传递一个字典:数值函数对(numerical_function pairs)。例如
In [99]: def mysin(x):
...: """
...: My sine. Note that this is only accurate for small x.
...: """
...: return x
...:
In [100]: f = lambdify(x, expr, {"sin":mysin})
In [101]: f(0.1)
Out[101]: 0.1
(四)打印
从前面的文章我们知道,SymPy可以使用Unicode字符打印出好看的输出( Unicode pretty printer)。本文将对SymPy中最常见的打印选项的简短介绍。
1 打印机
SymPy有几种打印机。最常见的是
• str
• srepr
• ASCII pretty printer
• Unicode pretty printer
• LaTeX
• MathML
• Dot
2 设置 Pretty Printing
如果你只想要最好的Pretty Printing,可使用 init_printing()
函数。这将自动启用环境中可用的最佳打印机。
如果你打算在交互式计算器类型会话( interactive calculator-type session)中工作, init_session()
函数将自动导入SymPy中的所有内容,创建一些公共符号,设置绘图,并运行init_printing()
In [104]: from sympy import init_session
In [105]: init_session()
IPython console for SymPy 1.1.1 (Python 3.6.6-64-bit) (ground types: python)
These commands were executed:
>>> from __future__ import division
>>> from sympy import *
>>> x, y, z, t = symbols('x y z t')
>>> k, m, n = symbols('k m n', integer=True)
>>> f, g, h = symbols('f g h', cls=Function)
>>> init_printing()
之后可能会有以下的几种情况:
- 在IPython QTConsole中,如果安装了
LaTeX
\LaTeX
LATEX,它将使用
LaTeX
\LaTeX
LATEX的打印机。
如果未安装 LaTeX \LaTeX LATEX,但安装了Matplotlib,则它将使用Matplotlib渲染引擎。如果未安装Matplotlib,它将使用 Unicode pretty printer。
- 在 IPython notebook中,它将使用MathJax渲染
LaTeX
\LaTeX
LATEX。
• 在IPython控制台会话或常规Python会话中,如果终端支持Unicode,它将使用Unicode pretty printer。
- 在不支持Unicode的终端中,使用ASCII pretty printer。
若要显式不使用
LaTeX
\LaTeX
LATEX,则将use_latex=False
传递给init_printing()
或init_session()
.。若要显式不使用Unicode,则传递 use_unicode=False
。
3 打印函数
除了自动打印之外,你还可以通过调用适当的函数显式使用任何一种打印机。
3.1 str
要获取表达式的字符串形式,则使用str(expr)
。
In [1]: from sympy import *
In [2]: x, y, z = symbols('x y z')
In [3]: str(Integral(sqrt(1/x), x))
Out[3]: 'Integral(sqrt(1/x), x)'
In [4]: print(Integral(sqrt(1/x), x))
Integral(sqrt(1/x), x)
3.2 srepr
表达式的srepr
形式旨在显示表达式的精确形式。
In [5]: srepr(Integral(sqrt(1/x), x))
Out[5]: "Integral(Pow(Pow(Symbol('x'), Integer(-1)), Rational(1, 2)), Tuple(Symbol('x')))"
3.3 ASCII Pretty Printer
pprint()
使用了 ASCII Pretty Printer。如果终端不支持Unicode,则默认使用 ASCII Pretty Printer。否则,必须通过use_unicode=False
.
In [6]: pprint(Integral(sqrt(1/x), x), use_unicode=False)
/
|
| ___
| / 1
| / - dx
| \/ x
|
/
pprint()
将输出打印到屏幕上。如果需要字符串形式,可使用pretty()
。
In [7]: pretty(Integral(sqrt(1/x), x), use_unicode=False)
Out[7]: ' / \n | \n | ___ \n | / 1 \n | / - d
x\n | \\/ x \n | \n/ '
In [8]: print(pretty(Integral(sqrt(1/x), x), use_unicode=False))
/
|
| ___
| / 1
| / - dx
| \/ x
|
/
3.4 Unicode Pretty Printer
Unicode Pretty Printer也可以从pprint()
和pretty()
访问。如果终端支持Unicode,则会自动使用。如果pprint()
无法检测到终端支持的unicode,则可以传递use_unicode=True
以强制其使用unicode。
In [9]: pprint(Integral(sqrt(1/x), x), use_unicode=True)
...:
⌠
⎮ ___
⎮ ╱ 1
⎮ ╱ ─ dx
⎮ ╲╱ x
⌡
3.5 LaTeX \LaTeX LATEX
要获得表达式的
LaTeX
\LaTeX
LATEX代码,则使用 latex()
。
In [10]: print(latex(Integral(sqrt(1/x), x)))
\int \sqrt{\frac{1}{x}}\, dx
3.6 MathML
使用print_mathml()
In [11]: from sympy.printing.mathml import print_mathml
In [12]: print_mathml(Integral(sqrt(1/x), x))
<apply>
<int/>
<bvar>
<ci>x</ci>
</bvar>
<apply>
<root/>
<apply>
<power/>
<ci>x</ci>
<cn>-1</cn>
</apply>
</apply>
</apply>
3.7 Dot
SymPy中的dotprint()
函数同类型打印.dot
以点格式打印输出,可以用Graphviz呈现。
In [13]: from sympy.printing.dot import dotprint
In [14]: from sympy.abc import x
In [15]: print(dotprint(x+2))
digraph{
# Graph style
"ordering"="out"
"rankdir"="TD"
#########
# Nodes #
#########
"Add(Integer(2), Symbol(x))_()" ["color"="black", "label"="Add", "shape"="ellipse
"];
"Integer(2)_(0,)" ["color"="black", "label"="2", "shape"="ellipse"];
"Symbol(x)_(1,)" ["color"="black", "label"="x", "shape"="ellipse"];
#########
# Edges #
#########
"Add(Integer(2), Symbol(x))_()" -> "Integer(2)_(0,)";
"Add(Integer(2), Symbol(x))_()" -> "Symbol(x)_(1,)";
}
未完待续:
【SymPy】(五)简化
【SymPy】(六)微积分
【SymPy】(七)方程求解
【SymPy】(八)矩阵
【SymPy】(九)高级表达式操作