术语重写

术语重写是一种非常通用的功能类别,用于将一种类型的表达式转换为不同类型的表达式。例如,展开、组合和转换表达式适用于术语重写,简化例程也可以包含在这里。目前,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)