SymPy
是开源免费的python
库,具有强大的符号运算和数值计算功能,覆盖方程求根、线性代数、微积分、常/偏微分方程求解等科学计算内容,完全由python
语言编写并完全运用于python
语言当中1。
相关信息与学习资料:
SymPy GitHub网站 ;SymPy 官网 ;SymPy基础应用 ;
SymPy版本信息 ;SymPy官方PDF文档下载v1.11.1
以anaconda
为例。
SymPy
及其依赖库已经集成在anaconda
中,不需要安装;如果使用其他集成开发环境,需要先安装mpmath
库,再安装SymPy
库。如果已经下载安装了anaconda
并且想更新SymPy
库,在命令行中输入
conda update sympy
在脚本中导入SymPy
库:
import sympy
通过sympy.init_session()
函数可以导入一些常用的常量和变量
from sympy import init_session
sympy.init_session()
# sympy.init_session() 等价于执行下列语句
from __future__ import division # 使得1/2 = 0.5
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()
在SymPy
中,总有如下假设
所有符号如未指定、均默认为复数( C \mathbb{C} C),因此简化运算必须在复数满足的情况下才会进行
符号的类型可以通过定义来指定:symbols('x y', positive=True)
在后面的介绍中,若未重定义,总假设
x, y
为正数,a
和b
为实数,t
和c
为复数 ,即x, y = symbols('x y', positive=True) a, b = symbols('a b', real=True) z, t, c = symbols('z t c')
定义单个变量用sympy.Symbol('')
,空格、其他符号均计入变量名的一部分(包括转义符、#
号等),这种方法是定义并初始化了一个Symbol
类的对象
class Symbol(sympy.core.expr.AtomicExpr, sympy.logic.boolalg.Boolean)
定义多个变量用sympy.symbols(
,变量名之间用空格/逗号分隔,但都在引号内,这种方法可以一次性定义多个Symbol
类的对象
symbols(names, *, cls=
, **args)
# 定义单个自变量x
x1 = sympy.Symbol('x') # x1 = x
x2 = sympy.Symbol('x y') # x2 = x y
# 定义多个变量
y1, y2 = sympy.symbols('y1 y2#') # y1 = y1, y2 = y2#
y3, y4 = sympy.symbols('y3,y4@') # y3 = y3, y4 = y4@
y5 = sympy.symbols('y5,y6 ') # y5 = tuple(y5, y6)
y6 = sympy.symbols('y6') # y6 = y6
# 定义一个加粗符号
u = sympy.Symbol('\\boldsymbol u')
# 输出简单表达式
print(x1)
print(2*x1 + 3)
sympy.symbols()
中第一个参数可以为元组、列表和字典:
sympy.symbols(('a', 'b', 'c'))
sympy.symbols(['a', 'b', 'c'])
sympy.symbols({'a', 'b', 'c'})
利用冒号:
定义一系列自变量
sympy.symbols('x:10') # (x0, x1, x2, x3, x4, x5, x6, x7, x8, x9)
sympy.symbols('x5:10') # (x5, x6, x7, x8, x9)
sympy.symbols('x5(:2)') # (x50, x51)
sympy.symbols('x5:10,y:5') # (x5, x6, x7, x8, x9, y0, y1, y2, y3, y4)
sympy.symbols(('x5:10', 'y:5')) # ((x5, x6, x7, x8, x9), (y0, y1, y2, y3, y4))
sympy.symbols('x:z') # (x, y, z)
sympy.symbols('x:c') # ()
sympy.symbols('x(:c)') # (xa, xb, xc)
sympy.symbols(':c') # (a, b, c)
sympy.symbols('a:d, x:z') # (a, b, c, d, x, y, z)
sympy.symbols(('a:d', 'x:z')) # ((a, b, c, d), (x, y, z))
sympy.symbols('x:2(1:3)') # (x01, x02, x11, x12)
sympy.symbols('x((a:b))') # (x(a), x(b))
sympy.symbols(r'x(:1\,:2)') # (x(0,0), x(0,1))
定义整数变量
a = symbols('a', integer=True)
a.is_integer # True
在SymPy
中,函数也是一种符号。定义函数:
# 方法1
f = Function('f') # f
f = Function('f')(x) # f(x)
# 方法2
g, h = symbols('g,h', cls=Function)
被定义为函数的符号继承了基类class sympy.core.function.Function(*args)
的一些函数,后面慢慢介绍。当函数被指定了自变量后,就可以做一些符号运算了:
f = Function('x')
f(x).diff(x)
# Derivative(f(x), x)
# 或
f = Function('x')(x)
f.diff(x)
# Derivative(f(x), x)
由符号经过运算符或函数处理返回的结果是表达式,单个符号也可以看作表达式。如
from sympy import symbols
x, y = symbols('x y')
expr = x + 2*y
expr
# x + 2*y
SymPy
可以将字符串转化为表达式,使用sympy.simpify()
函数
x = sympy.symbols('x')
str1 = 'x**3 + 2'
expr1 = sympy.sympify(str1)
print(f"{expr1} = {expr1.subs(x, 2)}, when x=2")
查询表达式是否为正数
x = Symbol('x', positive=True)
expr = 1 + x**2
expr.is_positive
# True
expr.is_negative
# False
在SymPy
中,等式不用=
表示,而用Eq
函数表示
expr = Eq(x, y)
在SymPy
中,任何没有用Eq
组成等式的表达式,在求解方程时其值默认为零,即下列三个语句具有相同的输出
solveset(Eq(x**2, 1), x)
solveset(Eq(x**2 - 1, 0), x)
solveset(x**2 - 1, x)
# {-1, 1}
在SymPy
中,大于、小于、大于等于、小于等于可以直接用>
、<
、>=
、<=
符号表示
expr = x > y
expr
# x > y
SymPy
假设与限制SymPy
中,当存在特殊情况使得表达式不能被简单地化简时,simplify()
函数不起作用。因此在定义符号时,最好加上对符号/变量的限制
from sympy import Symbol, simplify
x = Symbol('x') # 默认为复数
sqrt(x**2) # x为正则sqrt(x**2)=x, 为负时sqrt(x**2)=-x
# sqrt(x**2)
# 对符号进行限制,排除特殊情况后,简化正常进行
y = Symbol('y', positive=True)
sqrt(y**2)
# y
# 在程序中,也可以查询某个符号是否为正(或其他限制 is_*)
y.is_positive
# True
用is_*
查询变量/符号/表达式具有某种性质时,复杂的表达式不能被完全分析,栗子:下面的完全平方使被展开后,SymPy
就给出了无法判断的结果None
。这是由于is_*
判断在SymPy
中先于许多初等运算执行,对效率要求很高,完全解析将会使整体运算效率下降。
x = Symbol('x', real=True)
expr = 1 + (x - 2)**2
expr
# (x - 2)**2 + 1
expr.is_positive
# True
expr2 = expr.expand()
expr
# x**2 - 4*x + 5
print(expr2.is_positive)
# None
在SymPy
中定义两个不同的符号、但其初始化内容相同时,会被认作是相等的
x1 = Symbol('x')
x2 = Symbol('x')
x1 == x2
# True
# 如果限制不同,则不相等
x1 = Symbol('x')
x2 = Symbol('x', positive=True)
x1 == x2
# False
当使用字符串形式的表达式输入时,字符串内的符号会被隐式地定义为符号,此时不会对符号做任何的假设
from sympy import solve
solve('x**2 - 1')
# [-1, 1]
此时可以通过parse_expr()
显式地作假设
from sympy import parse_expr
parse_expr('x**2 - 1')
eq = parse_expr('x**2 - 1', {'x':Symbol('x', positive=True)})
solve(eq)
# [1]
关于符号的所有限制,可以通过*.assumptions0
属性查看
x.assumptions0
''' 输出
'algebraic': True,
'commutative': True, # 可交换的,默认True,即a*b==b*a为True
'complex': True,
'extended_negative': False,
'extended_nonnegative': True,
'extended_nonpositive': False,
'extended_nonzero': True,
'extended_positive': True,
'extended_real': True, # 实数+正负无穷
'finite': True,
'hermitian': True,
'imaginary': False,
'infinite': False, # oo,-oo,或zoo
'integer': True,
'irrational': False,
'negative': False,
'noninteger': False,
'nonnegative': True,
'nonpositive': False,
'nonzero': True,
'positive': True,
'rational': True,
'real': True,
'transcendental': False,
'zero': False}
'''
此外还有很多参数,如偶数(odd
)、正整数(prime
)等,详见官方文档。
暗示(implications),或隐含的限制,如当一个符号是实数、非负、非零时,它也是正数;当各个限制存在冲突时,会报错InconsistentAssumptions
。
x = Symbol('x', real=True, negative=False, zero=False)
x.is_positive
# True
SymPy
表达式使用函数时返回副本,原表达式不进行原位更改
+
, -
, *
, /
, **
sqrt(var)
root(8, n)
factorial(4)
I
E
oo
pi
sin()
,余弦函数:cos()
tan()
,余切函数:cot()
sec()
,余割函数:csc()
asin()
, 反余弦:acos()
atan()
, 反余切:acot()
asec()
, 反余割:acsc()
sinc
函数:sinc()
sinh()
, cosh()
, tanh()
, coth()
及其反函数等gamma()
zeta()
erf()
KroneckerDelta()
指数运算:exp()
自然对数:log()
以十为底的对数:log(var, 10)
自然对数:ln(x)
或log(x)
假设
Identity | Sufficient conditions to hold | Counterexample when conditions are not met | Important consequences |
---|---|---|---|
x a x b = x a + b x^ax^b=x^{a+b} xaxb=xa+b | Always true | None | None |
x a y a = ( x y ) a x^a y^a = (xy)^a xaya=(xy)a | x , y ≥ 0 x,y≥0 x,y≥0 and a ∈ R a\in R a∈R | ( − 1 ) 1 / 2 ( − 1 ) 1 / 2 ≠ ( − 1 ⋅ − 1 ) 1 / 2 (-1)^{1/2}(-1)^{1/2} \ne (-1\cdot -1)^1/2 (−1)1/2(−1)1/2=(−1⋅−1)1/2 | x y ≠ x y \sqrt{x} \sqrt{y} \ne \sqrt{xy} xy=xy in general |
( x a ) b = x a b (x^a)^b=x^{ab} (xa)b=xab | b ∈ Z b\in Z b∈Z | ( ( − 1 ) 2 ) 1 / 2 = ( − 1 ) 2 ⋅ 1 / 2 ((-1)^2)^{1/2} = (-1)^{2\cdot 1/2} ((−1)2)1/2=(−1)2⋅1/2 | x 2 ≠ x \sqrt{x^2}\ne x x2=x and 1 x ≠ 1 x \sqrt{\dfrac{1}{x}} \ne \dfrac{1}{\sqrt{x}} x1=x1 in general |
后续对于指数展开(expand
)部分,这些假设成为了限制展开的因素。
Meurer A, Smith CP, Paprocki M, Čertík O, Kirpichev SB, Rocklin M, Kumar A, Ivanov S, Moore JK, Singh S, Rathnayake T, Vig S, Granger BE, Muller RP, Bonazzi F, Gupta H, Vats S, Johansson F, Pedregosa F, Curry MJ, Terrel AR, Roučka Š, Saboo A, Fernando I, Kulal S, Cimrman R, Scopatz A. (2017) SymPy: symbolic computing in Python. PeerJ Computer Science 3:e103 https://doi.org/10.7717/peerj-cs.103 ↩︎