术语重写¶
术语重写是一种非常通用的功能类别,用于将一种类型的表达式转换为不同类型的表达式。例如,展开、组合和转换表达式适用于术语重写,简化例程也可以包含在这里。目前,SymPy 有几个函数和基本的内置方法来执行各种类型的重写。
扩展¶
最简单的重写规则是将表达式扩展为 _稀疏_ 形式。扩展有几种风格,包括扩展复数值表达式、乘积和幂的算术扩展,还可以根据更一般的函数扩展函数。下面列出了所有当前可用的扩展规则。
- 扩展涉及乘积和幂的算术表达式:
>>> from sympy import * >>> x, y, z = symbols('x,y,z') >>> ((x + y)*(x - y)).expand(basic=True) x**2 - y**2 >>> ((x + y + z)**2).expand(basic=True) x**2 + 2*x*y + 2*x*z + y**2 + 2*y*z + z**2
算术扩展在 expand()
中默认进行,因此关键字 basic
可以省略。然而,如果你使用下面描述的规则,你可以设置 basic=False
来避免这种类型的扩展。这让你完全控制表达式的处理方式。
另一种扩展规则是扩展复数值表达式并将其转换为标准形式。为此,使用了 complex
关键字。请注意,它将始终执行算术扩展以获得所需的标准形式:
>>> (x + I*y).expand(complex=True)
re(x) + I*re(y) + I*im(x) - im(y)
>>> sin(x + I*y).expand(complex=True)
sin(re(x) - im(y))*cosh(re(y) + im(x)) + I*cos(re(x) - im(y))*sinh(re(y) + im(x))
另请注意,通过使用 as_real_imag()
方法也可以获得相同的行为。然而,它将返回一个元组,其中实部位于第一个位置,虚部位于另一个位置。这也可以通过使用 collect
函数分两步完成:
>>> (x + I*y).as_real_imag()
(re(x) - im(y), re(y) + im(x))
>>> collect((x + I*y).expand(complex=True), I, evaluate=False)
{1: re(x) - im(y), I: re(y) + im(x)}
还可以根据不同类型的表达式来扩展表达式。这是一种非常普遍的扩展类型,通常你会使用 rewrite()
来进行特定类型的重写:
>>> GoldenRatio.expand(func=True)
1/2 + sqrt(5)/2
公共子表达式检测与收集¶
在评估一个大表达式之前,通常先识别出常见的子表达式,收集它们并一次性评估它们是很有用的。这在 cse
函数中得到了实现。例如:
>>> from sympy import cse, sqrt, sin, pprint
>>> from sympy.abc import x
>>> pprint(cse(sqrt(sin(x))), use_unicode=True)
⎛ ⎡ ________⎤⎞
⎝[], ⎣╲╱ sin(x) ⎦⎠
>>> pprint(cse(sqrt(sin(x)+5)*sqrt(sin(x)+4)), use_unicode=True)
⎛ ⎡ ________ ________⎤⎞
⎝[(x₀, sin(x))], ⎣╲╱ x₀ + 4 ⋅╲╱ x₀ + 5 ⎦⎠
>>> pprint(cse(sqrt(sin(x+1) + 5 + cos(y))*sqrt(sin(x+1) + 4 + cos(y))),
... use_unicode=True)
⎛ ⎡ ________ ________⎤⎞
⎝[(x₀, sin(x + 1) + cos(y))], ⎣╲╱ x₀ + 4 ⋅╲╱ x₀ + 5 ⎦⎠
>>> pprint(cse((x-y)*(z-y) + sqrt((x-y)*(z-y))), use_unicode=True)
⎛ ⎡ ____ ⎤⎞
⎝[(x₀, (x - y)⋅(-y + z))], ⎣╲╱ x₀ + x₀⎦⎠
在消除公共子表达式之前和之后要执行的优化可以通过 optimizations
可选参数传递。通过传递 optimizations='basic'
可以应用一组预定义的基本优化:
>>> pprint(cse((x-y)*(z-y) + sqrt((x-y)*(z-y)), optimizations='basic'),
... use_unicode=True)
⎛ ⎡ ____ ⎤⎞
⎝[(x₀, -(x - y)⋅(y - z))], ⎣╲╱ x₀ + x₀⎦⎠
然而,这些优化对于大型表达式来说可能会非常慢。此外,如果速度是一个问题,可以传递选项 order='none'
。此时,项的顺序将取决于哈希算法的实现,但速度将大大提高。
更多信息:
- sympy.simplify.cse_main.cse(
- exprs,
- symbols=None,
- optimizations=None,
- postprocess=None,
- order='canonical',
- ignore=(),
- list=True,
对表达式执行公共子表达式消除。
- 参数:
- 表达式SymPy 表达式的列表,或单个 SymPy 表达式
要简化的表达式。
- 符号无限迭代器生成唯一的符号
用于标记被提取出的公共子表达式的符号。
numbered_symbols
生成器很有用。默认是形式为“x0”、“x1”等的符号流。这必须是一个无限迭代器。- 优化(可调用对象,可调用对象)对的列表
外部优化函数的 (预处理器, 后处理器) 对。可选地,可以传递 ‘basic’ 以获取一组预定义的基本优化。这种 ‘basic’ 优化在旧实现中默认使用,但在较大的表达式上可能会非常慢。现在,默认情况下不会进行任何预处理或后处理优化。
- 后处理一个接受 cse 两个返回值的函数
从cse返回所需的输出形式,例如,如果你想要替换反转,函数可能是以下lambda:lambda r, e: return reversed(r), e
- 顺序字符串, ‘none’ 或 ‘canonical’
Mul 和 Add 参数的处理顺序。如果设置为 ‘canonical’,参数将按规范顺序排列。如果设置为 ‘none’,排序将更快,但依赖于表达式的哈希值,因此是机器依赖且可变的。对于速度是关注点的大型表达式,请使用 order=’none’ 设置。
- 忽略Symbols 的可迭代对象
包含
ignore
中任何符号的替换将被忽略。- 列表bool, (默认 True)
返回与输入类型相同的表达式列表或单个元素(当为 False 时)。
- 返回:
- 替换(符号, 表达式)对的列表
所有被替换的常见子表达式。本列表中较早的子表达式可能会出现在本列表中较晚的子表达式中。
- reduced_exprsSymPy 表达式列表
经过上述所有替换后的简化表达式。
示例
>>> from sympy import cse, SparseMatrix >>> from sympy.abc import x, y, z, w >>> cse(((w + x + y + z)*(w + y + z))/(w + x)**3) ([(x0, y + z), (x1, w + x)], [(w + x0)*(x0 + x1)/x1**3])
递归替换的表达式列表:
>>> m = SparseMatrix([x + y, x + y + z]) >>> cse([(x+y)**2, x + y + z, y + z, x + z + y, m]) ([(x0, x + y), (x1, x0 + z)], [x0**2, x1, y + z, x1, Matrix([ [x0], [x1]])])
注意:输入矩阵的类型和可变性保持不变。
>>> isinstance(_[1][-1], SparseMatrix) True
用户可以禁止包含某些符号的替换:
>>> cse([y**2*(x + 1), 3*y**2*(x + 1)], ignore=(y,)) ([(x0, x + 1)], [x0*y**2, 3*x0*y**2])
简化表达式的默认返回值是一个列表,即使只有一个表达式也是如此。\(list\) 标志保留了输出中输入的类型:
>>> cse(x) ([], [x]) >>> cse(x, list=False) ([], x)