SymPy-1-13-中文文档-十六-

120 阅读1小时+

SymPy 1.13 中文文档(十六)

原文:docs.sympy.org/latest/index.html

序列的极限

原文:docs.sympy.org/latest/modules/series/limitseq.html

提供计算具有无穷大序列的项的极限的方法。

sympy.series.limitseq.difference_delta(expr, n=None, step=1)

差分操作符。

解释

差分操作符的离散模拟。给定一个序列 x[n],返回序列 x[n + step] - x[n]。

例子

>>> from sympy import difference_delta as dd
>>> from sympy.abc import n
>>> dd(n*(n + 1), n)
2*n + 2
>>> dd(n*(n + 1), n, 2)
4*n + 6 

参考文献

[R833]

reference.wolfram.com/language/ref/DifferenceDelta.html

sympy.series.limitseq.dominant(expr, n)

找到和中的支配项,即支配每个其他项的项。

解释

如果 limit(a/b, n, oo) 是 oo,则 a 支配 b。如果 limit(a/b, n, oo) 是 0,则 b 支配 a。否则,a 和 b 是可比较的。

如果没有唯一的支配项,则返回 None

例子

>>> from sympy import Sum
>>> from sympy.series.limitseq import dominant
>>> from sympy.abc import n, k
>>> dominant(5*n**3 + 4*n**2 + n + 1, n)
5*n**3
>>> dominant(2**n + Sum(k, (k, 0, n)), n)
2**n 

另请参阅

sympy.series.limitseq.dominant

sympy.series.limitseq.limit_seq(expr, n=None, trials=5)

找到序列随着索引 n 趋向于正无穷的极限。

参数:

expr : 表达式

SymPy 表达式用于序列的第 n-th

n : 符号,可选

序列的索引,一个趋向于正无穷的整数。如果为 None,则从表达式推断,除非表达式具有多个符号。

trials: int, optional :试验次数

算法高度递归。如果算法返回 None,则 trials 是防止无限递归的保护措施,请尝试增加 trials

可接受的项

该算法设计用于由有理函数、不定和、不定乘积构建的序列,该序列依赖于一个不定的 n。允许交替符号的项,但不支持更复杂的振荡行为。

例子

>>> from sympy import limit_seq, Sum, binomial
>>> from sympy.abc import n, k, m
>>> limit_seq((5*n**3 + 3*n**2 + 4) / (3*n**3 + 4*n - 5), n)
5/3
>>> limit_seq(binomial(2*n, n) / Sum(binomial(2*k, k), (k, 1, n)), n)
3/4
>>> limit_seq(Sum(k**2 * Sum(2**m/m, (m, 1, k)), (k, 1, n)) / (2**n*n), n)
4 

另请参阅

sympy.series.limitseq.dominant

参考文献

[R834]

计算序列的极限 - Manuel Kauers

简化

原文:docs.sympy.org/latest/modules/simplify/index.html

  • 简化

  • 超几何展开

  • 傅宏光的三角简化

简化

原文:docs.sympy.org/latest/modules/simplify/simplify.html

sympy.simplify.simplify.simplify(expr, ratio=1.7, measure=<function count_ops>, rational=False, inverse=False, doit=True, **kwargs)

简化给定的表达式。

解释

简化并不是一个明确定义的术语,而且此函数尝试的确切策略可能会在 SymPy 的未来版本中改变。如果你的算法依赖于“简化”(无论是什么),请尝试确定你确切需要什么 - 是powsimp()radsimp()together()logcombine()?还是其他什么?然后直接使用这个特定的函数,因为这些都是明确定义的,因此你的算法将更加健壮。

尽管如此,特别是对于交互式使用或者当你对表达式的结构一无所知时,simplify()会尝试应用智能启发式方法使输入表达式“更简单”。例如:

>>> from sympy import simplify, cos, sin
>>> from sympy.abc import x, y
>>> a = (x + x**2)/(x*sin(y)**2 + x*cos(y)**2)
>>> a
(x**2 + x)/(x*sin(y)**2 + x*cos(y)**2)
>>> simplify(a)
x + 1 

请注意,我们可以通过使用特定的简化函数获得相同的结果:

>>> from sympy import trigsimp, cancel
>>> trigsimp(a)
(x**2 + x)/x
>>> cancel(_)
x + 1 

在某些情况下,应用simplify()实际上可能会导致一些更复杂的表达式。默认情况下,ratio=1.7防止更极端的情况:如果(结果长度)/(输入长度)> ratio,则返回未修改的输入。measure参数允许您指定用于确定表达式复杂性的函数。该函数应接受一个表达式作为唯一参数,并返回一个数字,以便如果表达式a比表达式b更复杂,则measure(a) > measure(b)。默认的度量函数是count_ops(),它返回表达式中操作的总数。

例如,如果ratio=1simplify的输出不能比输入更长。

>>> from sympy import sqrt, simplify, count_ops, oo
>>> root = 1/(sqrt(2)+3) 

由于simplify(root)会导致略长一些的表达式,所以 root 将原样返回:

>>> simplify(root, ratio=1) == root
True 

如果ratio=oo,简化将无论如何被应用:

>>> count_ops(simplify(root, ratio=oo)) > count_ops(root)
True 

请注意,最短的表达式未必是最简单的,因此将ratio设置为 1 可能并不是一个好主意。从启发式的角度来看,默认值ratio=1.7似乎是一个合理的选择。

您可以根据您认为应该表示输入表达式的“大小”或“复杂性”的内容轻松定义自己的度量函数。请注意,某些选择,例如lambda expr: len(str(expr))可能看起来是良好的度量标准,但可能存在其他问题(在这种情况下,度量函数可能会因非常大的表达式而使简化变慢)。如果您不知道什么是一个好的度量标准,那么默认的count_ops是一个不错的选择。

例如:

>>> from sympy import symbols, log
>>> a, b = symbols('a b', positive=True)
>>> g = log(a) + log(b) + log(a)*log(1/b)
>>> h = simplify(g)
>>> h
log(a*b**(1 - log(a)))
>>> count_ops(g)
8
>>> count_ops(h)
5 

因此,您可以看到,使用count_ops度量标准时,hg更简单。然而,我们可能不喜欢简化(在这种情况下,使用logcombine)如何创建了b**(log(1/a) + 1)项。减少这种情况的简单方法是在count_ops中给予幂运算更多的权重。我们可以通过使用visual=True选项来实现这一点:

>>> print(count_ops(g, visual=True))
2*ADD + DIV + 4*LOG + MUL
>>> print(count_ops(h, visual=True))
2*LOG + MUL + POW + SUB 
>>> from sympy import Symbol, S
>>> def my_measure(expr):
...     POW = Symbol('POW')
...     # Discourage powers by giving POW a weight of 10
...     count = count_ops(expr, visual=True).subs(POW, 10)
...     # Every other operation gets a weight of 1 (the default)
...     count = count.replace(Symbol, type(S.One))
...     return count
>>> my_measure(g)
8
>>> my_measure(h)
14
>>> 15./8 > 1.7 # 1.7 is the default ratio
True
>>> simplify(g, measure=my_measure)
-log(a)*log(b) + log(a) + log(b) 

注意,因为simplify()内部尝试许多不同的简化策略,然后使用度量函数进行比较,所以通过这种方式得到一个完全不同的结果,仍然与输入表达式不同。

如果 rational=True,则在简化之前,浮点数将重新转换为有理数。如果 rational=None,则浮点数将转换为有理数,但结果将重新转换为浮点数。如果 rational=False(默认值),则浮点数将不做任何处理。

如果 inverse=True,将假定可以按任意顺序取消反函数的组合,例如,asin(sin(x))将返回 x,而不检查 x 是否属于此关系为真的集合。默认值为 False。

注意,simplify()会自动在最终表达式上调用 doit()。可以通过传递 doit=False 参数来避免这种行为。

此外,应注意简化布尔表达式并不是完全定义良好的。如果表达式偏向于自动评估(例如 Eq()Or()),简化将返回 TrueFalse,如果可以确定其真值。如果表达式默认不被评估(例如 Predicate()),简化将不会减少它,并且您应该使用 refine()ask() 函数。此不一致性将在将来版本中解决。

见也

sympy.assumptions.refine.refine

使用假设进行简化。

sympy.assumptions.ask.ask

使用假设查询布尔表达式。

sympy.simplify.simplify.separatevars(expr, symbols=[], dict=False, force=False)

如果可能,分离表达式中的变量。默认情况下,它会根据表达式中的所有符号进行分离,并收集与符号无关的常数系数。

解释

如果 dict=True,则分离的项将以符号为键返回为字典。默认情况下,表达式中的所有符号都会出现为键;如果提供了符号,则所有这些符号将被用作键,表达式中包含其他符号或非符号的任何项将以字符串 'coeff' 为键返回。(对于符号为 None 的情况,将返回以 'coeff' 为键的表达式字典。)

如果 force=True,则会分离幂的基数,而不考虑所涉及符号的假设。

注意

因此,因为乘法的顺序由 Mul 决定,分离的表达式可能不一定被分组在一起。

尽管在某些表达式中分解是必要的以分离变量,但并非所有情况都需要,因此不应指望返回的因子是分解的。

示例

>>> from sympy.abc import x, y, z, alpha
>>> from sympy import separatevars, sin
>>> separatevars((x*y)**y)
(x*y)**y
>>> separatevars((x*y)**y, force=True)
x**y*y**y 
>>> e = 2*x**2*z*sin(y)+2*z*x**2
>>> separatevars(e)
2*x**2*z*(sin(y) + 1)
>>> separatevars(e, symbols=(x, y), dict=True)
{'coeff': 2*z, x: x**2, y: sin(y) + 1}
>>> separatevars(e, [x, y, alpha], dict=True)
{'coeff': 2*z, alpha: 1, x: x**2, y: sin(y) + 1} 

如果表达式实际上不可分离,或者仅部分可分离,则 separatevars 将尽力使用分解进行分离。

>>> separatevars(x + x*y - 3*x**2)
-x*(3*x - y - 1) 

如果表达式不可分离,则返回未更改的 expr 或(如果 dict=True)则返回 None。

>>> eq = 2*x + y*sin(x)
>>> separatevars(eq) == eq
True
>>> separatevars(2*x + y*sin(x), symbols=(x, y), dict=True) is None
True 
sympy.simplify.simplify.nthroot(expr, n, max_len=4, prec=15)

计算和的根号的实数第 n 次根。

参数:

expr:根号和的和

n:整数

max_len:作为常数传递给 nsimplify 的根号的最大数目

算法

首先使用 nsimplify 获取一个候选根;如果它不是根,则计算最小多项式;答案是其根之一。

示例

>>> from sympy.simplify.simplify import nthroot
>>> from sympy import sqrt
>>> nthroot(90 + 34*sqrt(7), 3)
sqrt(7) + 3 
sympy.simplify.simplify.kroneckersimp(expr)

用 KroneckerDelta 简化表达式。

当前尝试的唯一简化是识别乘法取消:

示例

>>> from sympy import KroneckerDelta, kroneckersimp
>>> from sympy.abc import i
>>> kroneckersimp(1 + KroneckerDelta(0, i) * KroneckerDelta(1, i))
1 
sympy.simplify.simplify.besselsimp(expr)

简化贝塞尔类型函数。

解释

此例程试图简化贝塞尔类型函数。目前仅适用于 Bessel J 和 I 函数,然而。它通过依次查看所有这样的函数,并消除参数前的 “I” 和 “-1” 的因子(实际上是它们的极坐标等效物)。然后,使用三角函数重写半整数阶的函数,并使用低阶函数重写整数阶(> 1)的函数。最后,如果表达式已更改,则使用 factor() 计算结果的因式分解。

>>> from sympy import besselj, besseli, besselsimp, polar_lift, I, S
>>> from sympy.abc import z, nu
>>> besselsimp(besselj(nu, z*polar_lift(-1)))
exp(I*pi*nu)*besselj(nu, z)
>>> besselsimp(besseli(nu, z*polar_lift(-I)))
exp(-I*pi*nu/2)*besselj(nu, z)
>>> besselsimp(besseli(S(-1)/2, z))
sqrt(2)*cosh(z)/(sqrt(pi)*sqrt(z))
>>> besselsimp(z*besseli(0, z) + z*(besseli(2, z))/2 + besseli(1, z))
3*z*besseli(0, z)/2 
sympy.simplify.simplify.hypersimp(f, k)

给定组合项 f(k),简化其连续项比率即 f(k+1)/f(k)。输入项可以由具有 gamma 特殊函数等效表示的函数和整数序列组成。

解释

该算法执行三个基本步骤:

  1. 尽可能用 gamma 重写所有函数。

  2. 用整数、绝对常数指数的 gamma 的乘积重写所有 gamma 的出现。

  3. 对于嵌套分数、幂的简化操作,如果结果表达式是多项式的商,则减少它们的总次数。

如果 f(k) 是超几何的,则结果是最小次数的多项式商。否则返回 None。

有关实现算法的更多信息,请参考:

  1. W. Koepf,《m 倍超几何求和的算法》,符号计算杂志(1995)20,399-417
sympy.simplify.simplify.hypersimilar(f, g, k)

如果 fg 是超相似的,则返回 True。

解释

超几何意义上的相似性意味着 f(k) 和 g(k) 的商是 k 的有理函数。这个过程在解决递推关系中很有用。

有关详细信息,请参见 hypersimp()。

sympy.simplify.simplify.nsimplify(expr, constants=(), tolerance=None, full=False, rational=None, rational_conversion='base10')

为一个数字找到简单的表示,或者如果存在自由符号或者 rational=True,则用它们的有理数等价物替换 Floats。如果没有更改且 rational 不为 False,则 Floats 将至少被转换为 Rationals。

解释

对于数值表达式,寻找一个能够数值匹配给定数值表达式的简单公式(输入应至少能够 evalf 到 30 位精度)。

可选地,可以给出包含在公式中的(有理数独立的)常数列表。

可以设置更低的容差来寻找不那么精确的匹配。如果未给出容差,则最不精确的值将设置容差(例如,浮点数默认精度为 15 位数字,因此容差为 10**-15)。

使用 full=True 进行更广泛的搜索(当设置容差较低时寻找更简单的数字非常有用)。

在转换为有理数时,如果 rational_conversion='base10'(默认),则使用其基于十进制的浮点数表示来转换浮点数为有理数。当 rational_conversion='exact' 时,则使用精确的基于二进制的表示来转换。

示例

>>> from sympy import nsimplify, sqrt, GoldenRatio, exp, I, pi
>>> nsimplify(4/(1+sqrt(5)), [GoldenRatio])
-2 + 2*GoldenRatio
>>> nsimplify((1/(exp(3*pi*I/5)+1)))
1/2 - I*sqrt(sqrt(5)/10 + 1/4)
>>> nsimplify(I**I, [pi])
exp(-pi/2)
>>> nsimplify(pi, tolerance=0.01)
22/7 
>>> nsimplify(0.333333333333333, rational=True, rational_conversion='exact')
6004799503160655/18014398509481984
>>> nsimplify(0.333333333333333, rational=True)
1/3 

另见

sympy.core.function.nfloat

sympy.simplify.simplify.posify(eq)

返回 eq(使通用符号变为正的)和包含旧符号与新符号映射的字典。

解释

任何具有 positive=None 的符号将被替换为具有相同名称的正虚拟符号。这种替换将允许更多的符号处理表达式,特别是涉及幂次和对数的表达式。

还返回一个可以发送到 subs 以将 eq 恢复为其原始符号的字典。

>>> from sympy import posify, Symbol, log, solve
>>> from sympy.abc import x
>>> posify(x + Symbol('p', positive=True) + Symbol('n', negative=True))
(_x + n + p, {_x: x}) 
>>> eq = 1/x
>>> log(eq).expand()
log(1/x)
>>> log(posify(eq)[0]).expand()
-log(_x)
>>> p, rep = posify(eq)
>>> log(p).expand().subs(rep)
-log(x) 

可以对表达式的可迭代项应用相同的转换方法:

>>> eq = x**2 - 4
>>> solve(eq, x)
[-2, 2]
>>> eq_x, reps = posify([eq, x]); eq_x
[_x**2 - 4, _x]
>>> solve(*eq_x)
[2] 
sympy.simplify.simplify.logcombine(expr, force=False)

使用以下规则取对数并将它们结合起来:

  • 如果两者均为正,则 log(x) + log(y) == log(x*y)

  • 如果 x 是正数且 a 是实数,则 a*log(x) == log(x**a)

如果 forceTrue,则假设以上假设将被认为在数量上不存在任何假设的情况下成立。例如,如果 a 是虚数或参数为负数,则 force 不会执行组合;但如果 a 是一个没有假设的符号,则变化将发生。

示例

>>> from sympy import Symbol, symbols, log, logcombine, I
>>> from sympy.abc import a, x, y, z
>>> logcombine(a*log(x) + log(y) - log(z))
a*log(x) + log(y) - log(z)
>>> logcombine(a*log(x) + log(y) - log(z), force=True)
log(x**a*y/z)
>>> x,y,z = symbols('x,y,z', positive=True)
>>> a = Symbol('a', real=True)
>>> logcombine(a*log(x) + log(y) - log(z))
log(x**a*y/z) 

转换仅限于包含对数的因子和/或项,因此结果取决于展开的初始状态:

>>> eq = (2 + 3*I)*log(x)
>>> logcombine(eq, force=True) == eq
True
>>> logcombine(eq.expand(), force=True)
log(x**2) + I*log(x**3) 

另见

posify

用具有正假设的符号替换所有符号。

sympy.core.function.expand_log

展开产品和幂的对数;与 logcombine 的相反操作。

sympy.simplify.radsimp.radsimp(expr, symbolic=True, max_terms=4)

通过去除平方根来有理化分母。

解释

注意,从radsimp返回的表达式必须谨慎使用,因为如果分母包含符号,可能会进行违反简化过程假设的替换:对于分母匹配a + b*sqrt(c)的情况,要求a != +/-b*sqrt(c)。(如果没有符号,通过收集sqrt(c)项来使得变量a不包含sqrt(c)来使该假设成立。)如果不希望对符号分母进行简化,请将symbolic设为False

如果超过max_terms个根式项,则返回原始表达式。

示例

>>> from sympy import radsimp, sqrt, Symbol, pprint
>>> from sympy import factor_terms, fraction, signsimp
>>> from sympy.simplify.radsimp import collect_sqrt
>>> from sympy.abc import a, b, c 
>>> radsimp(1/(2 + sqrt(2)))
(2 - sqrt(2))/2
>>> x,y = map(Symbol, 'xy')
>>> e = ((2 + 2*sqrt(2))*x + (2 + sqrt(8))*y)/(2 + sqrt(2))
>>> radsimp(e)
sqrt(2)*(x + y) 

除去 gcd 外不进行任何简化。但是,可以通过收集平方根项稍微优化结果:

>>> r2 = sqrt(2)
>>> r5 = sqrt(5)
>>> ans = radsimp(1/(y*r2 + x*r2 + a*r5 + b*r5)); pprint(ans)
 ___       ___       ___       ___
 \/ 5 *a + \/ 5 *b - \/ 2 *x - \/ 2 *y
------------------------------------------
 2               2      2              2
5*a  + 10*a*b + 5*b  - 2*x  - 4*x*y - 2*y 
>>> n, d = fraction(ans)
>>> pprint(factor_terms(signsimp(collect_sqrt(n))/d, radical=True))
 ___             ___
 \/ 5 *(a + b) - \/ 2 *(x + y)
------------------------------------------
 2               2      2              2
5*a  + 10*a*b + 5*b  - 2*x  - 4*x*y - 2*y 

如果分母中的根式无法移除或没有分母,则将返回原始表达式。

>>> radsimp(sqrt(2)*x + sqrt(2))
sqrt(2)*x + sqrt(2) 

带有符号的结果并不总是对所有替换有效:

>>> eq = 1/(a + b*sqrt(c))
>>> eq.subs(a, b*sqrt(c))
1/(2*b*sqrt(c))
>>> radsimp(eq).subs(a, b*sqrt(c))
nan 

如果symbolic=False,则符号分母将不会转换(但数字分母仍将被处理):

>>> radsimp(eq, symbolic=False)
1/(a + b*sqrt(c)) 
sympy.simplify.radsimp.rad_rationalize(num, den)

通过移除分母中的平方根来使num/den有理化;numden是其平方是正有理数的项的和。

示例

>>> from sympy import sqrt
>>> from sympy.simplify.radsimp import rad_rationalize
>>> rad_rationalize(sqrt(3), 1 + sqrt(2)/3)
(-sqrt(3) + sqrt(6)/3, -7/9) 
sympy.simplify.radsimp.collect(expr, syms, func=None, evaluate=None, exact=False, distribute_order_term=True)

收集表达式的加法项。

解释

此函数根据表达式中的符号(术语)的列表收集表达式的加法项,直到有理指数幂为止。这里的术语符号指的是任意表达式,可以包含幂、乘积、和等。换句话说,符号是一个模式,将在表达式的术语中搜索。

输入表达式不会被collect()扩展,因此用户应提供一个适当形式的表达式。这使得collect()更加可预测,因为没有任何神奇的背后操作。然而,需要注意的是,通过expand_power_base()函数将乘积的幂转换为幂的乘积。

有两种可能的输出类型。首先,如果设置了evaluate标志,则该函数将返回带有收集项的表达式,否则将返回带有直到有理幂为键的字典和收集系数为值。

示例

>>> from sympy import S, collect, expand, factor, Wild
>>> from sympy.abc import a, b, c, x, y 

此函数可以收集多项式或有理表达式中的符号系数。它将能够找到所有整数或有理的收集变量的幂:

>>> collect(a*x**2 + b*x**2 + a*x - b*x + c, x)
c + x**2*(a + b) + x*(a - b) 

结果可以以字典形式达到相同效果:

>>> d = collect(a*x**2 + b*x**2 + a*x - b*x + c, x, evaluate=False)
>>> d[x**2]
a + b
>>> d[x]
a - b
>>> d[S.One]
c 

您还可以处理多变量多项式。但请记住,此函数是贪婪的,因此它一次只关心一个符号,按照规定的顺序:

>>> collect(x**2 + y*x**2 + x*y + y + a*y, [x, y])
x**2*(y + 1) + x*y + y*(a + 1) 

还可以使用更复杂的表达式作为模式:

>>> from sympy import sin, log
>>> collect(a*sin(2*x) + b*sin(2*x), sin(2*x))
(a + b)*sin(2*x)

>>> collect(a*x*log(x) + b*(x*log(x)), x*log(x))
x*(a + b)*log(x) 

您可以在模式中使用通配符:

>>> w = Wild('w1')
>>> collect(a*x**y - b*x**y, w**y)
x**y*(a - b) 

还可以处理符号幂,尽管它具有更复杂的行为,因为在这种情况下,幂的基数和指数的符号部分被视为单个符号:

>>> collect(a*x**c + b*x**c, x)
a*x**c + b*x**c
>>> collect(a*x**c + b*x**c, x**c)
x**c*(a + b) 

但是,如果您将有理数合并到指数中,则会得到众所周知的行为:

>>> collect(a*x**(2*c) + b*x**(2*c), x**c)
x**(2*c)*(a + b) 

还请注意,关于 collect() 函数的所有先前陈述事实也适用于指数函数,因此您可以获得:

>>> from sympy import exp
>>> collect(a*exp(2*x) + b*exp(2*x), exp(x))
(a + b)*exp(2*x) 

如果您只想收集某些符号的特定幂次,则将 exact 标志设置为 True:

>>> collect(a*x**7 + b*x**7, x, exact=True)
a*x**7 + b*x**7
>>> collect(a*x**7 + b*x**7, x**7, exact=True)
x**7*(a + b) 

如果您想要对包含符号的任何对象进行收集,请将 exact 设置为 None:

>>> collect(x*exp(x) + sin(x)*y + sin(x)*2 + 3*x, x, exact=None)
x*exp(x) + 3*x + (y + 2)*sin(x)
>>> collect(a*x*y + x*y + b*x + x, [x, y], exact=None)
x*y*(a + 1) + x*(b + 1) 

您还可以将此函数应用于微分方程,其中可以收集任意阶导数。请注意,如果针对函数或函数的导数进行收集,则该函数的所有导数也将被收集。使用 exact=True 可以防止此情况发生:

>>> from sympy import Derivative as D, collect, Function
>>> f = Function('f') (x)

>>> collect(a*D(f,x) + b*D(f,x), D(f,x))
(a + b)*Derivative(f(x), x)

>>> collect(a*D(D(f,x),x) + b*D(D(f,x),x), f)
(a + b)*Derivative(f(x), (x, 2))

>>> collect(a*D(D(f,x),x) + b*D(D(f,x),x), D(f,x), exact=True)
a*Derivative(f(x), (x, 2)) + b*Derivative(f(x), (x, 2))

>>> collect(a*D(f,x) + b*D(f,x) + a*f + b*f, f)
(a + b)*f(x) + (a + b)*Derivative(f(x), x) 

或者您甚至可以同时匹配导数顺序和指数:

>>> collect(a*D(D(f,x),x)**2 + b*D(D(f,x),x)**2, D(f,x))
(a + b)*Derivative(f(x), (x, 2))**2 

最后,您可以对每个收集的系数应用一个函数。例如,您可以因式分解多项式的符号系数:

>>> f = expand((x + a + 1)**3)

>>> collect(f, x, factor)
x**3 + 3*x**2*(a + 1) + 3*x*(a + 1)**2 + (a + 1)**3 

注意

参数应当以展开形式给出,因此在调用该函数之前,可能需要调用 expand() 函数。

另见

collect_const, collect_sqrt, rcollect

sympy.simplify.radsimp.rcollect(expr, *vars)

递归地收集表达式中的和项。

示例

>>> from sympy.simplify import rcollect
>>> from sympy.abc import x, y 
>>> expr = (x**2*y + x*y + x + y)/(x + y) 
>>> rcollect(expr, y)
(x + y*(x**2 + x + 1))/(x + y) 

另见

collect, collect_const, collect_sqrt

sympy.simplify.radsimp.collect_sqrt(expr, evaluate=None)

将具有公共平方根的项合并在一起的表达式返回。如果 evaluate 参数为 False,则将返回包含有平方根项的 Add 的项数,如果非零,则将返回 Add 的项,否则将返回表达式本身作为单个项。如果 evaluate 参数为 True,则将返回包含任何已收集项的表达式。

注意:因为 I = sqrt(-1),它也被收集了。

示例

>>> from sympy import sqrt
>>> from sympy.simplify.radsimp import collect_sqrt
>>> from sympy.abc import a, b 
>>> r2, r3, r5 = [sqrt(i) for i in [2, 3, 5]]
>>> collect_sqrt(a*r2 + b*r2)
sqrt(2)*(a + b)
>>> collect_sqrt(a*r2 + b*r2 + a*r3 + b*r3)
sqrt(2)*(a + b) + sqrt(3)*(a + b)
>>> collect_sqrt(a*r2 + b*r2 + a*r3 + b*r5)
sqrt(3)*a + sqrt(5)*b + sqrt(2)*(a + b) 

如果 evaluate 参数为 False,则参数将被排序并作为列表返回,并返回包含平方根项的数量:

>>> collect_sqrt(a*r2 + b*r2 + a*r3 + b*r5, evaluate=False)
((sqrt(3)*a, sqrt(5)*b, sqrt(2)*(a + b)), 3)
>>> collect_sqrt(a*sqrt(2) + b, evaluate=False)
((b, sqrt(2)*a), 1)
>>> collect_sqrt(a + b, evaluate=False)
((a + b,), 0) 

另见

collect, collect_const, rcollect

sympy.simplify.radsimp.collect_const(expr, *vars, Numbers=True)

在 Add 表达式中,以非贪婪方式收集具有相似数值系数的项。如果提供了vars,则只会针对这些常数进行收集。尽管任何数值也可以被收集,如果不希望这样,请设置Numbers=False,则不会收集任何浮点数或有理数。

参数:

expr : SymPy 表达式

此参数定义了要从中收集具有相似系数的项的表达式。非 Add 表达式原样返回。

vars : 变量长度的数字集合,可选

指定要收集的常数。可以是多个。

Numbers : bool

指定是否针对所有sympy.core.numbers.Number类的实例。如果Numbers=False,则不会收集任何浮点数或有理数。

返回:

expr : 表达式

返回一个具有相似系数项的表达式。

示例

>>> from sympy import sqrt
>>> from sympy.abc import s, x, y, z
>>> from sympy.simplify.radsimp import collect_const
>>> collect_const(sqrt(3) + sqrt(3)*(1 + sqrt(2)))
sqrt(3)*(sqrt(2) + 2)
>>> collect_const(sqrt(3)*s + sqrt(7)*s + sqrt(3) + sqrt(7))
(sqrt(3) + sqrt(7))*(s + 1)
>>> s = sqrt(2) + 2
>>> collect_const(sqrt(3)*s + sqrt(3) + sqrt(7)*s + sqrt(7))
(sqrt(2) + 3)*(sqrt(3) + sqrt(7))
>>> collect_const(sqrt(3)*s + sqrt(3) + sqrt(7)*s + sqrt(7), sqrt(3))
sqrt(7) + sqrt(3)*(sqrt(2) + 3) + sqrt(7)*(sqrt(2) + 2) 

收集时保持符号敏感,给无符号值更高的优先级:

>>> collect_const(x - y - z)
x - (y + z)
>>> collect_const(-y - z)
-(y + z)
>>> collect_const(2*x - 2*y - 2*z, 2)
2*(x - y - z)
>>> collect_const(2*x - 2*y - 2*z, -2)
2*x - 2*(y + z) 

参见

collect, collect_sqrt, rcollect

sympy.simplify.radsimp.fraction(expr, exact=False)

返回一个表达式的分子和分母。如果给定的表达式不是分数,则该函数将返回元组(expr, 1)

此函数不会尝试简化嵌套分数或进行任何项重写。

如果只需要分子/分母中的一个,则分别使用numer(expr)denom(expr)函数。

>>> from sympy import fraction, Rational, Symbol
>>> from sympy.abc import x, y 
>>> fraction(x/y)
(x, y)
>>> fraction(x)
(x, 1) 
>>> fraction(1/y**2)
(1, y**2) 
>>> fraction(x*y/2)
(x*y, 2)
>>> fraction(Rational(1, 2))
(1, 2) 

此函数也可以与假设一起使用良好:

>>> k = Symbol('k', negative=True)
>>> fraction(x * y**k)
(x, y**(-k)) 

如果我们对某些指数的符号一无所知,并且未设置exact标志,则将分析指数的结构,并返回漂亮的分数:

>>> from sympy import exp, Mul
>>> fraction(2*x**(-y))
(2, x**y) 
>>> fraction(exp(-x))
(1, exp(x)) 
>>> fraction(exp(-x), exact=True)
(exp(-x), 1) 

exact标志还将保留任何未评估的乘积:

>>> u = Mul(2, x + 1, evaluate=False)
>>> fraction(u)
(2*x + 2, 1)
>>> fraction(u, exact=True)
(2*(x  + 1), 1) 
sympy.simplify.ratsimp.ratsimp(expr)

将表达式放置在公共分母上,取消并化简。

示例

>>> from sympy import ratsimp
>>> from sympy.abc import x, y
>>> ratsimp(1/x + 1/y)
(x + y)/(x*y) 
sympy.simplify.ratsimp.ratsimpmodprime(expr, G, *gens, quick=True, polynomial=False, **args)

通过使用由G生成的素理想归一化有理表达式exprG应为理想的 Groebner 基础。

示例

>>> from sympy.simplify.ratsimp import ratsimpmodprime
>>> from sympy.abc import x, y
>>> eq = (x + y**5 + y)/(x - y)
>>> ratsimpmodprime(eq, [x*y**5 - x - y], x, y, order='lex')
(-x**2 - x*y - x - y)/(-x**2 + x*y) 

如果polynomialFalse,算法计算一个有理化简,该简化最小化了分子和分母的总次数之和。

如果polynomialTrue,该函数只将分子和分母置于标准形式中。这样做速度更快,但结果可能更差。

引用

[R855]

M. Monagan, R. Pearce, 有理化简模多项式理想,dl.acm.org/doi/pdf/10.1145/1145768.1145809(特别是第二个算法)

sympy.simplify.trigsimp.trigsimp(expr, inverse=False, **opts)

通过已知的三角恒等式返回简化的表达式。

参数:

inverse : bool, 可选

如果 inverse=True,则将假定可以以任意顺序取消反函数的组合,例如 asin(sin(x)) 将在不检查 x 是否属于此关系为真的集合的情况下产生 x。默认为 False。默认值:True

method:字符串,可选

指定要使用的方法。有效选择为:

  • 'matching',默认
  • 'groebner'
  • 'combined'
  • 'fu'
  • 'old'

如果选择 'matching',则通过针对常见模式进行递归简化表达式。如果选择 'groebner',则应用试验性的 Groebner 基算法。在这种情况下,进一步的选项将被传递给 trigsimp_groebner,请参阅其文档字符串。如果选择 'combined',它首先使用默认较小的参数运行 Groebner 基础算法,然后运行 'matching' 算法。如果选择 'fu',则运行 Fu 等人描述的三角变换集合(参见fu() 的文档字符串)。如果选择 'old',则运行原始的 SymPy 三角简化函数。

opts:

可选的关键字参数传递给该方法。请查看每个方法的函数文档字符串以获取详细信息。

示例

>>> from sympy import trigsimp, sin, cos, log
>>> from sympy.abc import x
>>> e = 2*sin(x)**2 + 2*cos(x)**2
>>> trigsimp(e)
2 

简化发生在三角函数所在的任何地方。

>>> trigsimp(log(e))
log(2) 

使用 method='groebner'(或 method='combined')可能会导致更大的简化。

可以通过 method='old' 访问旧的 trigsimp 例程。

>>> from sympy import coth, tanh
>>> t = 3*tanh(x)**7 - 2/coth(x)**7
>>> trigsimp(t, method='old') == t
True
>>> trigsimp(t)
tanh(x)**7 
sympy.simplify.powsimp.powsimp(expr, deep=False, combine='all', force=False, measure=<function count_ops>)

通过合并具有相似基数和指数的幂来减少表达式。

解释

如果 deep=True,则 powsimp() 还将简化函数参数。默认情况下,deep 设置为 False

如果 forceTrue,则会在不检查假设的情况下合并基数,例如 sqrt(x)*sqrt(y) -> sqrt(x*y),如果 x 和 y 都为负数则不成立。

您可以通过更改 combine='base'combine='exp' 使 powsimp() 仅合并基数或仅合并指数。默认情况下,combine='all' 即同时进行两者合并。combine='base' 只会合并:

 a   a          a                          2x      x
x * y  =>  (x*y)   as well as things like 2   =>  4 

combine='exp' 只会合并

 a   b      (a + b)
x * x  =>  x 

combine='exp' 严格只会按照以前的自动方式合并指数。如果需要旧的行为,请同时使用 deep=True

combine='all' 时,首先评估 exp。考虑下面的第一个示例,以了解可能与此相关的歧义。这样做是为了能够完全合并第二个示例。如果希望首先合并 base,请执行像 powsimp(powsimp(expr, combine='base'), combine='exp') 这样的操作。

示例

>>> from sympy import powsimp, exp, log, symbols
>>> from sympy.abc import x, y, z, n
>>> powsimp(x**y*x**z*y**z, combine='all')
x**(y + z)*y**z
>>> powsimp(x**y*x**z*y**z, combine='exp')
x**(y + z)*y**z
>>> powsimp(x**y*x**z*y**z, combine='base', force=True)
x**y*(x*y)**z 
>>> powsimp(x**z*x**y*n**z*n**y, combine='all', force=True)
(n*x)**(y + z)
>>> powsimp(x**z*x**y*n**z*n**y, combine='exp')
n**(y + z)*x**(y + z)
>>> powsimp(x**z*x**y*n**z*n**y, combine='base', force=True)
(n*x)**y*(n*x)**z 
>>> x, y = symbols('x y', positive=True)
>>> powsimp(log(exp(x)*exp(y)))
log(exp(x)*exp(y))
>>> powsimp(log(exp(x)*exp(y)), deep=True)
x + y 

如果 combine='exp',则基数为 Mul 的根式将被合并

>>> from sympy import sqrt
>>> x, y = symbols('x y') 

两个根式将通过 Mul 自动连接:

>>> a=sqrt(x*sqrt(y))
>>> a*a**3 == a**4
True 

但是,如果该根式的整数幂已经自动展开,则 Mul 不会连接生成的因子:

>>> a**4 # auto expands to a Mul, no longer a Pow
x**2*y
>>> _*a # so Mul doesn't combine them
x**2*y*sqrt(x*sqrt(y))
>>> powsimp(_) # but powsimp will
(x*sqrt(y))**(5/2)
>>> powsimp(x*y*a) # but won't when doing so would violate assumptions
x*y*sqrt(x*sqrt(y)) 
sympy.simplify.powsimp.powdenest(eq, force=False, polar=False)

根据假设允许的幂来收集幂。

解释

给定 (bb**be)**e,可以按以下方式简化:

  • 如果 bb 为正数,或

  • e 是一个整数,或

  • 如果 |be| < 1,则简化为 bb**(be*e)

给定幂的乘积 (bb1**be1 * bb2**be2...)**e,可以按以下方式简化:

  • 如果 e 为正,则所有 bei 的 gcd 可以与 e 结合;

  • 所有非负的 bb 可以从那些是负的分开,并且它们的 gcd 可以与 e 结合;autosimplification 已处理此分离。

  • 具有分母指数中的整数的整数因子可以从任何项中移除,并且这些整数的 gcd 可以与 e 结合

force设置为True将使不显式为负的符号表现得像它们是正的,从而导致更多去嵌套。

polar设置为True将在对数的黎曼曲面上进行简化,从而导致更多去嵌套。

当 exp()中存在对数和的和时,可能会得到幂的乘积,例如exp(3*(log(a) + 2*log(b))) -> a**3*b**6

示例

>>> from sympy.abc import a, b, x, y, z
>>> from sympy import Symbol, exp, log, sqrt, symbols, powdenest 
>>> powdenest((x**(2*a/3))**(3*x))
(x**(2*a/3))**(3*x)
>>> powdenest(exp(3*x*log(2)))
2**(3*x) 

假设可能阻止扩展:

>>> powdenest(sqrt(x**2))
sqrt(x**2) 
>>> p = symbols('p', positive=True)
>>> powdenest(sqrt(p**2))
p 

不进行其他扩展。

>>> i, j = symbols('i,j', integer=True)
>>> powdenest((x**x)**(i + j)) # -X-> (x**x)**i*(x**x)**j
x**(x*(i + j)) 

但通过将所有非对数项移到函数外部来去嵌套 exp();这可能导致 exp 折叠成具有不同底数的幂:

>>> powdenest(exp(3*y*log(x)))
x**(3*y)
>>> powdenest(exp(y*(log(a) + log(b))))
(a*b)**y
>>> powdenest(exp(3*(log(a) + log(b))))
a**3*b**3 

如果假设允许,符号也可以移动到最外层的指数:

>>> i = Symbol('i', integer=True)
>>> powdenest(((x**(2*i))**(3*y))**x)
((x**(2*i))**(3*y))**x
>>> powdenest(((x**(2*i))**(3*y))**x, force=True)
x**(6*i*x*y) 
>>> powdenest(((x**(2*a/3))**(3*y/i))**x)
((x**(2*a/3))**(3*y/i))**x
>>> powdenest((x**(2*i)*y**(4*i))**z, force=True)
(x*y**2)**(2*i*z) 
>>> n = Symbol('n', negative=True) 
>>> powdenest((x**i)**y, force=True)
x**(i*y)
>>> powdenest((n**i)**x, force=True)
(n**i)**x 
sympy.simplify.combsimp.combsimp(expr)

简化组合表达式。

解释

此函数以包含阶乘、二项式系数、Pochhammer 符号和其他“组合”函数的表达式作为输入,并尝试最小化这些函数的数量并减少其参数的大小。

该算法通过将所有组合函数重写为伽玛函数并应用 gammasimp()来工作,除了可能使整数参数非整数的简化步骤。有关更多信息,请参阅 gammasimp 的文档字符串。

然后通过将伽玛函数重写为阶乘并将(a+b)!/a!b!转换为二项式来以阶乘和二项式的术语重写表达式。

如果表达式具有伽玛函数或具有非整数参数的组合函数,则将其自动传递给 gammasimp。

示例

>>> from sympy.simplify import combsimp
>>> from sympy import factorial, binomial, symbols
>>> n, k = symbols('n k', integer = True) 
>>> combsimp(factorial(n)/factorial(n - 3))
n*(n - 2)*(n - 1)
>>> combsimp(binomial(n+1, k+1)/binomial(n, k))
(n + 1)/(k + 1) 
sympy.simplify.sqrtdenest.sqrtdenest(expr, max_iter=3)

如果可能,对表达式中包含其他平方根的表达式进行去嵌套,否则返回未更改的表达式。这基于[1]的算法。

示例

>>> from sympy.simplify.sqrtdenest import sqrtdenest
>>> from sympy import sqrt
>>> sqrtdenest(sqrt(5 + 2 * sqrt(6)))
sqrt(2) + sqrt(3) 

请参见

sympy.solvers.solvers.unrad

参考

[R856]

web.archive.org/web/20210806201615/https://researcher.watson.ibm.com/researcher/files/us-fagin/symb85.pdf

[R857]

D. J. Jeffrey 和 A. D. Rich,《通过去嵌套简化平方根的平方根》(可在www.cybertester.com/data/denest.pdf找到)

sympy.simplify.cse_main.cse(exprs, symbols=None, optimizations=None, postprocess=None, order='canonical', ignore=(), list=True)

对表达式执行常见子表达式消除。

参数:

exprs:SymPy 表达式的列表或单个 SymPy 表达式

要简化的表达式。

symbols:无限迭代器,生成唯一的 Symbols

用于标记提取出的公共子表达式的符号。numbered_symbols生成器很有用。默认情况下是形如“x0”、“x1”等的符号流。这必须是一个无限迭代器。

optimizations:(callable,callable)对列表。

外部优化函数的预处理器、后处理器对。可选地,可以传递‘basic’以获取一组预定义的基本优化。这些‘basic’优化在旧实现中默认使用,但在处理较大表达式时可能非常慢。现在,默认情况下不进行任何预处理或后处理优化。

后处理:接受 CSE 的两个返回值的函数。

返回所需的 cse 输出形式,例如如果想要反转替换,则函数可能是以下 lambda:lambda r,e:return reversed(r),e

顺序:字符串,‘none’或‘canonical’。

处理 Mul 和 Add 参数的顺序。如果设置为‘canonical’,参数将按照规范顺序排列。如果设置为‘none’,排序将更快,但依赖于表达式哈希,因此是机器相关和可变的。对于大表达式而言,如果关注速度,使用 order=‘none’设置。

ignore:Symbol 的可迭代对象。

包含任何ignore中符号的替换将被忽略。

列表:布尔值,(默认为真)。

返回以列表形式或与输入相同类型的表达式(当为 False 时)。

返回:

替换:(Symbol,表达式)对列表。

所有被替换的公共子表达式。此列表中较早的子表达式可能会出现在较晚的子表达式中。

reduced_exprs:SymPy 表达式列表。

所有上述替换后的简化表达式。

示例。

>>> 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) 
sympy.simplify.cse_main.opt_cse(exprs, order='canonical')

查找 Adds、Muls、Pows 和负系数 Muls 中的优化机会。

参数:

exprs:SymPy 表达式列表。

要优化的表达式。

顺序:字符串,‘none’或‘canonical’。

处理 Mul 和 Add 参数的顺序。对于大表达式而言,如果关注速度,使用 order=‘none’设置。

返回:

opt_subs:表达式替换的字典。

用于优化公共子表达式消除的表达式替换。

示例。

>>> from sympy.simplify.cse_main import opt_cse
>>> from sympy.abc import x
>>> opt_subs = opt_cse([x**-2])
>>> k, v = list(opt_subs.keys())[0], list(opt_subs.values())[0]
>>> print((k, v.as_unevaluated_basic()))
(x**(-2), 1/(x**2)) 
sympy.simplify.cse_main.tree_cse(exprs, symbols, opt_subs=None, order='canonical', ignore=())

在考虑 opt_subs 的情况下在表达式树上执行原始 CSE。

参数:

exprs:SymPy 表达式列表。

要减少的表达式。

symbols:生成唯一 Symbol 的无限迭代器。

用于标记提取出的公共子表达式的符号。

opt_subs:表达式替换的字典。

在执行任何 CSE 操作之前要替换的表达式。

顺序:字符串,‘none’或‘canonical’。

处理 Mul 和 Add 参数的顺序。对于速度是关键的大型表达式,请使用 order='none' 设置。

ignore:Symbol 的可迭代对象

包含来自 ignore 的任何符号的替换将被忽略。

sympy.simplify.hyperexpand.hyperexpand(f, allow_hyper=False, rewrite='default', place=None)

展开超几何函数。如果 allow_hyper 为 True,则允许部分简化(即与输入不同但仍包含超几何函数)。

如果 G 函数在零点和无穷远处均有展开,可以将 place 设置为 0zoo 表示首选选择。

示例

>>> from sympy.simplify.hyperexpand import hyperexpand
>>> from sympy.functions import hyper
>>> from sympy.abc import z
>>> hyperexpand(hyper([], [], z))
exp(z) 

表达式的非超几何部分和未被识别的超几何表达式保持不变:

>>> hyperexpand(1 + hyper([1, 1, 1], [], z))
hyper((1, 1, 1), (), z) + 1 
class sympy.simplify.epathtools.EPath(path)

使用路径操作表达式。

EPath 的 EBNF 表示法语法:

literal   ::= /[A-Za-z_][A-Za-z_0-9]*/
number    ::= /-?\d+/
type      ::= literal
attribute ::= literal "?"
all       ::= "*"
slice     ::= "[" number? (":" number? (":" number?)?)? "]"
range     ::= all | slice
query     ::= (type | attribute) ("|" (type | attribute))*
selector  ::= range | query range?
path      ::= "/" selector ("/" selector)* 

参见 epath() 函数的文档字符串。

apply(expr, func, args=None, kwargs=None)

修改由路径选择的表达式的部分。

示例

>>> from sympy.simplify.epathtools import EPath
>>> from sympy import sin, cos, E
>>> from sympy.abc import x, y, z, t 
>>> path = EPath("/*/[0]/Symbol")
>>> expr = [((x, 1), 2), ((3, y), z)] 
>>> path.apply(expr, lambda expr: expr**2)
[((x**2, 1), 2), ((3, y**2), z)] 
>>> path = EPath("/*/*/Symbol")
>>> expr = t + sin(x + 1) + cos(x + y + E) 
>>> path.apply(expr, lambda expr: 2*expr)
t + sin(2*x + 1) + cos(2*x + 2*y + E) 
select(expr)

检索由路径选择的表达式的部分。

示例

>>> from sympy.simplify.epathtools import EPath
>>> from sympy import sin, cos, E
>>> from sympy.abc import x, y, z, t 
>>> path = EPath("/*/[0]/Symbol")
>>> expr = [((x, 1), 2), ((3, y), z)] 
>>> path.select(expr)
[x, y] 
>>> path = EPath("/*/*/Symbol")
>>> expr = t + sin(x + 1) + cos(x + y + E) 
>>> path.select(expr)
[x, x, y] 
sympy.simplify.epathtools.epath(path, expr=None, func=None, args=None, kwargs=None)

操作表达式中路径选择的部分。

参数:

path:字符串 | EPath

作为字符串或编译的 EPath 的路径。

expr:基本类型 | 可迭代对象

表达式或表达式容器。

func:可调用对象(可选)

将应用于匹配部分的可调用对象。

args:元组(可选)

func 的额外位置参数。

kwargs:字典(可选)

func 的额外关键字参数。

解释

此函数允许在单行代码中操作大型嵌套表达式,利用在 XML 处理标准中应用的技术(例如 XPath)。

如果 funcNoneepath() 检索由 path 选择的元素。否则,将 func 应用于每个匹配的元素。

请注意,创建一个 EPath 对象并使用该对象的 select 和 apply 方法更有效,因为这样只会编译一次路径字符串。这个函数应该只用作交互式使用的便捷快捷方式。

这是支持的语法:

  • 选择全部:/*

    等同于 for arg in args:

  • 选择切片:/[0]/[1:5]/[1:5:2]

    支持标准 Python 的切片语法。

  • 按类型选择:/list/list|tuple

    模拟 isinstance()

  • 按属性选择:/__iter__?

    模拟 hasattr()

示例

>>> from sympy.simplify.epathtools import epath
>>> from sympy import sin, cos, E
>>> from sympy.abc import x, y, z, t 
>>> path = "/*/[0]/Symbol"
>>> expr = [((x, 1), 2), ((3, y), z)] 
>>> epath(path, expr)
[x, y]
>>> epath(path, expr, lambda expr: expr**2)
[((x**2, 1), 2), ((3, y**2), z)] 
>>> path = "/*/*/Symbol"
>>> expr = t + sin(x + 1) + cos(x + y + E) 
>>> epath(path, expr)
[x, x, y]
>>> epath(path, expr, lambda expr: 2*expr)
t + sin(2*x + 1) + cos(2*x + 2*y + E) 

超几何展开

原文:docs.sympy.org/latest/modules/simplify/hyperexpand.html

本页面描述了函数 hyperexpand() 及其相关代码的工作原理。有关使用方法,请参阅 symplify 模块的文档。

超几何函数展开算法

本节描述了扩展超几何函数所使用的算法。其中大部分基于文献 [Roach1996][Roach1997]

回顾超几何函数(最初)定义为

[\begin{split}{}pF_q\left(\begin{matrix} a_1, \cdots, a_p \ b_1, \cdots, b_q \end{matrix} \middle| z \right) = \sum{n=0}^\infty \frac{(a_1)_n \cdots (a_p)_n}{(b_1)_n \cdots (b_q)_n} \frac{z^n}{n!}.\end{split}]

结果表明,有些微分算子可以将 (a_p) 和 (b_q) 参数按整数改变。如果已知这样一系列算子,可以将索引集 (a_r⁰) 和 (b_s⁰) 转换为 (a_p) 和 (b_q),则我们将说从 (a_r⁰, b_s⁰) 到 (a_p, b_q) 的对是可达的。因此,我们的一般策略如下:给定一组参数 (a_p, b_q),尝试查找一个起点 (a_r⁰, b_s⁰),我们知道一个表达式,然后应用一系列微分算子到已知表达式,以找到我们感兴趣的超几何函数的表达式。

符号

在以下内容中,符号 (a) 总是表示分子参数,符号 (b) 总是表示分母参数。下标 (p, q, r, s) 表示具有该长度的向量,例如 (a_p) 表示具有 (p) 个分子参数的向量。下标 (i) 和 (j) 表示“运行索引”,因此它们通常应与“对所有 (i)”一起使用。例如,对于所有 (i),(a_i < 4)。大写下标 (I) 和 (J) 表示选择的固定索引。因此,例如,如果不等式对我们当前感兴趣的一个索引 (I) 成立,则 (a_I > 0) 为真。

增加和减少指数

假设 (a_i \ne 0). 设 (A(a_i) = \frac{z}{a_i}\frac{\mathrm{d}}{dz}+1)。可以轻松地证明 (A(a_i) {}_p F_q\left({a_p \atop b_q} \middle| z \right) = {}_p F_q\left({a_p + e_i \atop b_q} \middle| z \right)),其中 (e_i) 是第 (i) 个单位向量。类似地,对于 (b_j \ne 1),我们设 (B(b_j) = \frac{z}{b_j-1} \frac{\mathrm{d}}{dz}+1) 并找到 (B(b_j) {}_p F_q\left({a_p \atop b_q} \middle| z \right) = {}_p F_q\left({a_p \atop b_q - e_i} \middle| z \right))。因此,我们可以随意增加上标和减少下标,只要我们不经过零。(A(a_i)) 和 (B(b_j)) 被称为移位算子。

还可以轻松地证明 (\frac{\mathrm{d}}{dz} {}p F_q\left({a_p \atop b_q} \middle| z \right) = \frac{a_1 \cdots a_p}{b_1 \cdots b_q} {}p F_q\left({a_p + 1 \atop b_q + 1} \middle| z \right)),其中 (a_p + 1) 是向量 (a_1 + 1, a_2 + 1, \ldots),对 (b_q + 1) 同样适用。将此与移位算子结合起来,我们得到超几何微分方程的一种形式:(\left[ \frac{\mathrm{d}}{dz} \prod{j=1}^q B(b_j) - \frac{a_1 \cdots a_p}{(b_1-1) \cdots (b_q-1)} \prod{i=1}^p A(a_i) \right] {}p F_q\left({a_p \atop b_q} \middle| z \right) = 0)。如果所有移位算子都被定义,即没有 (a_i = 0) 且没有 (b_j = 1),则此等式成立。清除分母并通过 (z) 乘以我们得到以下方程:(\left[ z\frac{\mathrm{d}}{dz} \prod{j=1}^q \left(z\frac{\mathrm{d}}{dz} + b_j-1 \right) - z \prod_{i=1}^p \left( z\frac{\mathrm{d}}{\mathrm{d}z} + a_i \right) \right] {}_p F_q\left({a_p \atop b_q} \middle| z\right) = 0)。尽管我们的推导没有显示出它,但可以检查到只要 ({}_p F_q) 被定义,此方程成立。

注意,在 (a_I, b_J) 的适当条件下,每个算子 (A(a_i)),(B(b_j)) 和 (z\frac{\mathrm{d}}{\mathrm{d}z}) 可以用 (A(a_I)) 或 (B(b_J)) 的术语表达。我们的下一个目标是将超几何微分方程写为如下形式:([X A(a_I) - r] {}_p F_q\left({a_p \atop b_q} \middle| z\right) = 0),其中 (X) 是某个算子,(r) 是待定常数。如果 (r \ne 0),则我们可以写成 (\frac{-1}{r} X {}_p F_q\left({a_p + e_I \atop b_q} \middle| z\right) = {}_p F_q\left({a_p \atop b_q} \middle| z\right)),因此 (\frac{-1}{r}X) 消除了 (A(a_I)) 的移位,因此它被称为逆移位算子。

现在,如果 (a_I \ne 0),则 (A(a_I)) 存在,并且 (z\frac{\mathrm{d}}{\mathrm{d}z} = a_I A(a_I) - a_I)。还要注意所有算子 (A(a_i)),(B(b_j)) 和 (z\frac{\mathrm{d}}{\mathrm{d}z}) 是可交换的。我们有 (\prod_{i=1}^p \left( z\frac{\mathrm{d}}{\mathrm{d}z} + a_i \right) = \left(\prod_{i=1, i \ne I}^p \left( z\frac{\mathrm{d}}{\mathrm{d}z} + a_i \right)\right) a_I A(a_I)),因此这给了我们 (X) 的前半部分。另一半没有这样漂亮的表达式。我们找到 (z\frac{\mathrm{d}}{dz} \prod_{j=1}^q \left(z\frac{\mathrm{d}}{dz} + b_j-1 \right) = \left(a_I A(a_I) - a_I\right) \prod_{j=1}^q \left(a_I A(a_I) - a_I + b_j - 1\right))。由于前半部分没有常数项,我们推断 (r = -a_I\prod_{j=1}^q(b_j - 1 -a_I))。

这告诉我们可以“取消移位” (A(a_I)) 的条件,即当 (a_I \ne 0) 且 (r \ne 0) 时。将 (a_I - 1) 替换为 (a_I) 然后告诉我们可以减少索引 (a_I) 的条件。对 (B(a_J)) 进行类似分析,我们得到以下规则:

  • 可以减小索引 (a_I),如果 (a_I \ne 1) 且对所有 (b_j) 都有 (a_I \ne b_j)。

  • 如果(b_J \ne -1)且对所有(a_i)都有(b_J \ne a_i),则可以增加索引(b_J)。

结合存在移位算子的条件(如上所述),我们已经建立了游戏规则!

降阶

注意,如果(a_I = b_J),我们有({}p F_q\left({a_p \atop b_q} \middle| z \right) = {}{p-1} F_{q-1}\left({a_p^* \atop b_q^} \middle| z \right)),其中(a_p^)表示省略(a_I)后的(a_p),(b_q^*)类似。我们称之为降阶。

实际上,我们可以做得更好。如果(a_I - b_J \in \mathbb{Z}_{>0}),那么很容易看出(\frac{(a_I)_n}{(b_J)_n})实际上是(n)的多项式。同时,很容易看出((z\frac{\mathrm{d}}{\mathrm{d}z})^k z^n = n^k z^n)。结合这两点,我们得出:

如果(a_I - b_J \in \mathbb{Z}_{>0}),则存在一个多项式(p(n) = p_0 + p_1 n + \cdots)(次数为(a_I - b_J))使得(\frac{(a_I)_n}{(b_J)n} = p(n)),并且({}p F_q\left({a_p \atop b_q} \middle| z \right) = \left(p_0 + p_1 z\frac{\mathrm{d}}{\mathrm{d}z} + p_2 \left(z\frac{\mathrm{d}}{\mathrm{d}z}\right)² + \cdots \right) {}{p-1} F{q-1}\left({a_p^* \atop b_q^*} \middle| z \right))。

因此,任意一组参数(a_p, b_q)都可以从一组参数(c_r, d_s)到达,其中(c_i - d_j \in \mathbb{Z})意味着(c_i < d_j)。这样的一组参数(c_r, d_s)称为合适。我们已知的公式数据库应仅包含合适的起源。原因有二:首先,从合适的起源工作更容易;其次,可以从低阶公式推导出非合适起源的公式,我们应该将后者放入数据库中。

在参数空间中移动

现在需要研究以下问题:假设(a_p, b_q)和(a_p⁰, b_q⁰)都合适,并且(a_i - a_i⁰ \in \mathbb{Z}),(b_j - b_j⁰ \in \mathbb{Z})。那么(a_p, b_q)从(a_p⁰, b_q⁰)可达的条件是什么?显然,我们可以独立处理所有模 1 不同余的参数。因此假设对于所有(i)和(j),(a_i)和(b_j)对模 1 同余于(r)。对于(a_i⁰)和(b_j⁰)也是如此。

如果(r \ne 0),则任何这样的(a_p, b_q)都可以从任何(a_p⁰, b_q⁰)到达。要看到这一点,请注意存在同余于 1 的常数(c, c⁰),使得对于所有(i)和(j),(a_i < c < b_j),类似地(a_i⁰ < c⁰ < b_j⁰)。如果(n = c - c⁰ > 0),那么我们首先向上反向移位所有(b_j⁰) (n)次,然后类似地向上移位所有(a_i⁰) (n)次。如果(n < 0),那么我们首先向下反向移位(a_i⁰),然后向下移位(b_j⁰)。这归结为情况(c = c⁰)。但显然,我们现在可以任意移位或反向移位(a_i⁰),只要保持它们小于(c),并对(b_j⁰)也是如此。因此,(a_p, b_q)可以从(a_p⁰, b_q⁰)到达。

如果 (r = 0),那么问题会稍微复杂一些。不失一般性地,没有参数为零。现在我们有一个额外的复杂性:没有参数可以穿过零点。因此 (a_p, b_q) 从 (a_p⁰, b_q⁰) 可达,当且仅当 (a_i < 0) 的个数等于 (a_i⁰ < 0) 的个数,并且类似地对于 (b_i) 和 (b_i⁰)。但在适当的参数集中,所有 (b_j > 0)!这是因为如果其中一个 (b_j) 是非正整数且所有 (a_i) 都小于 (b_j),那么超几何函数是未定义的。因此 (b_j \le 0) 的个数总是零。

因此,我们可以将每个适当的参数集 (a_p, b_q) 关联到以下不变量:

  • 对于每个 (r \in [0, 1)),参数 (a_i \equiv r \pmod{1}) 的数量 (\alpha_r),以及类似地参数 (b_i \equiv r \pmod{1}) 的数量 (\beta_r)。
  • 整数 (\gamma) 满足 (a_i < 0) 的个数。

上述推理表明,(a_p, b_q) 从 (a_p⁰, b_q⁰) 可达,当且仅当不变量 (\alpha_r, \beta_r, \gamma) 全部一致。因此特别是“可达性”在适当的参数上是一个对称关系,没有零点。

应用操作符

如果一切顺利,那么对于给定的参数集,我们可以在我们的数据库中找到一个良好的公式的起源。现在我们必须将(可能)许多微分操作符应用于它。如果我们这样做得盲目,结果将会非常凌乱。这是因为在超几何类型函数中,导数通常被表达为两个连续函数的和。因此,如果我们计算 (N) 个导数,那么答案将涉及 (2N) 个连续函数!显然这是不可取的。事实上,从超几何微分方程我们知道,我们最多需要 (\max(p, q+1)) 个连续函数来表达所有的导数。

因此,与其盲目进行不同 iating,我们将与一个 (\mathbb{C}(z))-模块基础一起工作:对于原点 (a_r⁰, b_s⁰),我们要么存储(对于特别漂亮的答案),要么计算一组 (N) 函数(通常 (N = \max(r, s+1))),具有以下属性:任何其中之一的导数都是它们的 (\mathbb{C}(z))-线性组合。在公式中,我们存储了一个 (N) 函数的向量 (B),一个矩阵 (M) 和一个向量 (C)(后两者的条目在 (\mathbb{C}(z)) 中),具有以下属性:

  • ({}_r F_s\left({a_r⁰ \atop b_s⁰} \middle| z \right) = C B)

  • (z\frac{\mathrm{d}}{\mathrm{d}z} B = M B)。

然后我们可以计算任意多的导数,我们将总是得到至多 (N) 个特殊函数的 (\mathbb{C}(z))-线性组合。

如上所示,(B),(M) 和 (C) 可以全部存储(用于特别漂亮的答案)或者从单个 ({}_p F_q) 公式计算得到。

收尾工作

这描述了超几何函数算法的大部分。在 hyperexpand.py 源文件中还有一些进一步的技巧。Meijer G-函数的扩展也在那里描述。

有限合流的 Meijer G-Functions

Slater 定理本质上将 (G)-函数评估为残余和。如果所有极点都是简单的,则得到的级数可以识别为超几何级数。因此,(G)-函数可以评估为一些超几何函数的和。

如果极点不是简单的,得到的级数就不是超几何级数。这被称为“合流”或“对数”情况(后者因其结果级数中包含对数而得名)。答案以复杂的方式取决于各种极点的重数,并且没有公认的符号表示方法(据我所知)。然而,如果仅有有限多个多重极点,我们可以将 (G) 函数评估为一些超几何函数的和,再加上有限多项。我找不到关于此的好参考资料,这就是我在这里工作的原因。

回顾一般的设置。我们定义

[G(z) = \frac{1}{2\pi i} \int_L \frac{\prod_{j=1}^m \Gamma(b_j - s) \prod_{j=1}^n \Gamma(1 - a_j + s)}{\prod_{j=m+1}^q \Gamma(1 - b_j + s) \prod_{j=n+1}^p \Gamma(a_j - s)} z^s \mathrm{d}s,]

其中 (L) 是一个从 (+\infty) 开始并结束的轮廓,负方向上环绕所有 (\Gamma(b_j - s)) 的极点,(j = 1, \ldots, n),并且没有其他极点。还假定积分是绝对收敛的。

在接下来的讨论中,对于任意复数 (a, b),我们写成 (a \equiv b \pmod{1}),当且仅当存在整数 (k) 使得 (a - b = k)。因此,当且仅当存在某些 (i \ne j \le n) 使得 (a_i \equiv a_j \pmod{1}) 时,存在双极点。

现在我们假设每当对于 (i \le m) 有 (b_j \equiv a_i \pmod{1}),对于 (j > n) 则 (b_j < a_i)。这意味着相关伽玛函数的任何商都不是多项式,并且可以通过“阶降”来实现。固定复数 (c),使得 ({b_i | b_i \equiv c \pmod{1}, i \le m}) 不为空。将这个集合枚举为 (b, b+k_1, \ldots, b+k_u),其中 (k_i) 是非负整数。类似地将 ({a_j | a_j \equiv c \pmod{1}, j > n}) 枚举为 (b + l_1, \ldots, b + l_v)。然后对于所有这样的 (c),都需要假设 (v \ge u) 以实现有限合流。

让 (c_1, \ldots, c_w) 是不同的 (\pmod{1}),并穷尽了 (b_i) 的同余类。我声明

[G(z) = -\sum_{j=1}^w (F_j(z) + R_j(z)),]

其中 (F_j(z)) 是一个超几何函数,(R_j(z)) 是一个有限和,两者稍后会具体说明。事实上,对应于每个 (c_j),大多数情况下存在一系列极点,其中大多数是多重极点。这就是第 (j) 项的来源。

因此再次修复(c),将相关的(b_i)枚举为(b, b + k_1, \ldots, b + k_u)。我们将看看与(a + l_1, \ldots, a + l_u)对应的(a_j)。其他的(a_i)不作特殊处理。相应的伽马函数在(s = b + r)(可能)处有极点。对于(r \ge l_u),被积函数的极点是简单的。因此,我们设定

[R(z) = \sum_{r=0}^{l_u - 1} res_{s = r + b}.]

我们最终需要调查其他的极点。设定(r = l_u + t),(t \ge 0)。计算显示

[\frac{\Gamma(k_i - l_u - t)}{\Gamma(l_i - l_u - t)} = \frac{1}{(k_i - l_u - t){l_i - k_i}} = \frac{(-1)^{\delta_i}}{(l_u - l_i + 1){\delta_i}} \frac{(l_u - l_i + 1)_t}{(l_u - k_i + 1)_t},]

其中(\delta_i = l_i - k_i).

[ \begin{align}\begin{aligned}\begin{split}\Gamma(b_j - l_u - b - t) = \frac{\Gamma(b_j - l_u - b)}{(-1)^t(l_u + b + 1 - b_j)_t}, \\end{split}\\Gamma(1 - a_j + l_u + b + t) = \Gamma(1 - a_j + l_u + b) (1 - a_j + l_u + b)_t\end{aligned}\end{align} ]

[res_{s = b + l_u + t} \Gamma(b - s) = -\frac{(-1)^{l_u + t}}{(l_u + t)!} = -\frac{(-1)^{l_u}}{l_u!} \frac{(-1)^t}{(l_u+1)_t}.]

因此

[\begin{split}res_{s = b + l_u + t} =& -z^{b + l_u} \frac{(-1)^{l_u}}{l_u!} \prod_{i=1}^{u} \frac{(-1)^{\delta_i}}{(l_u - k_i + 1){\delta_i}} \frac{\prod{j=1}^n \Gamma(1 - a_j + l_u + b) \prod_{j=1}^m \Gamma(b_j - l_u - b)^} {\prod_{j=n+1}^p \Gamma(a_j - l_u - b)^ \prod_{j=m+1}^q \Gamma(1 - b_j + l_u + b)} \ &\times z^t \frac{(-1)^t}{(l_u+1)t} \prod{i=1}^{u} \frac{(l_u - l_i + 1)_t}{(l_u - k_i + 1)t} \frac{\prod{j=1}^n (1 - a_j + l_u + b)t \prod{j=n+1}^p (-1)^t (l_u + b + 1 - a_j)t^*} {\prod{j=1}^m (-1)^t (l_u + b + 1 - b_j)t^* \prod{j=m+1}^q (1 - b_j + l_u + b)_t},\end{split}]

其中(*)表示我们特别处理的项。

我们因此得到

[\begin{split}F(z) = C \times {}{p+1}F{q}\left( \begin{matrix} 1, (1 + l_u - l_i), (1 + l_u + b - a_i)^* \ 1 + l_u, (1 + l_u - k_i), (1 + l_u + b - b_i)^* \end{matrix} \middle| (-1)^{p-m-n} z\right),\end{split}]

其中(C)指定了与(t)无关的残留因子。(这个结果也可以稍微简化,将所有的(l_u)等转换回(a_* - b_*),但这样做将需要更多的符号仍然不利于计算。)

扩展超几何表

向表格添加新的公式非常简单。在文件sympy/simplify/hyperexpand.py的顶部,有一个名为add_formulae()的函数。在其中嵌套定义了两个辅助函数add(ap, bq, res)addb(ap, bq, B, C, M),以及虚拟变量abcz

添加新公式的第一步是使用add(ap, bq, res)。这声明了hyper(ap, bq, z) == res。在这里,apbq可以使用虚拟变量abc作为自由符号。例如,众所周知的公式(\sum_0^\infty \frac{(-a)_n z^n}{n!} = (1-z)^a)由以下行声明:add((-a, ), (), (1-z)**a).

根据提供的信息,矩阵(B)、(C)和(M)将会被计算,并且在展开超几何函数时,公式现在是可用的。接下来应该运行测试文件sympy/simplify/tests/test_hyperexpand.py,特别是测试test_formulae()。这将对新添加的公式进行数值测试。如果测试失败,则可能是输入中有拼写错误。

由于所有新增的公式可能相对复杂,自动计算的基可能不够优化(没有很好的测试方法,除非观察非常混乱的输出)。在这种情况下,矩阵(B)、(C)和(M)应该手动计算。然后可以使用助手addb来声明一个使用手动计算基础的超几何公式。

例如

因为到目前为止这个解释可能非常理论化且难以理解,我们现在通过一个明确的例子来详细说明。我们取弗雷内尔函数(C(z)),它遵循以下超几何表示:

[\begin{split}C(z) = z \cdot {}{1}F{2}\left.\left( \begin{matrix} \frac{1}{4} \ \frac{1}{2}, \frac{5}{4} \end{matrix} \right| -\frac{\pi² z⁴}{16}\right) ,.\end{split}]

首先,我们尝试通过使用(更简单的)函数add(ap, bq, res)将此公式添加到查找表中。前两个参数只是包含({}{1}F{2})的参数集合的列表。参数res稍微复杂一些。我们只知道(C(z))关于({}{1}F{2}(\ldots | f(z)))的表达式,其中(f)是(z)的函数,即我们的情况下

[f(z) = -\frac{\pi² z⁴}{16} ,.]

我们需要的是一个公式,其中超几何函数只有(z)作为参数({}{1}F{2}(\ldots | z))。我们引入新的复数符号(w)并寻找一个函数(g(w)),使得

[f(g(w)) = w]

成立。然后我们可以用(g(w))替换(C(z))中的每一个(z)。在我们的例子中,函数(g)可能看起来像

[g(w) = \frac{2}{\sqrt{\pi}} \exp\left(\frac{i \pi}{4}\right) w^{\frac{1}{4}} ,.]

我们主要通过猜测和测试结果来获取这些函数。因此,我们继续计算(f(g(w)))(并且朴素化简)

[\begin{split}f(g(w)) &= -\frac{\pi² g(w)⁴}{16} \ &= -\frac{\pi² g\left(\frac{2}{\sqrt{\pi}} \exp\left(\frac{i \pi}{4}\right) w^{\frac{1}{4}}\right)⁴}{16} \ &= -\frac{\pi² \frac{2⁴}{\sqrt{\pi}⁴} \exp\left(\frac{i \pi}{4}\right)⁴ {w^{\frac{1}{4}}}⁴}{16} \ &= -\exp\left(i \pi\right) w \ &= w\end{split}]

并且确实得到(w)。对于分支函数的情况,我们必须注意分支切割。在这种情况下,我们将(w)取为正实数并检查公式。如果我们找到的适用于正(w),那么只需在任何分支函数内用exp替换为exp_polar,我们得到的就是所有(w)都正确的。因此,我们可以将公式写成

[\begin{split}C(g(w)) = g(w) \cdot {}{1}F{2}\left.\left( \begin{matrix} \frac{1}{4} \ \frac{1}{2}, \frac{5}{4} \end{matrix} \right| w\right) ,.\end{split}]

并且显然

[\begin{split}{}{1}F{2}\left.\left( \begin{matrix} \frac{1}{4} \ \frac{1}{2}, \frac{5}{4} \end{matrix} \right| w\right) = \frac{C(g(w))}{g(w)} = \frac{C\left(\frac{2}{\sqrt{\pi}} \exp\left(\frac{i \pi}{4}\right) w^{\frac{1}{4}}\right)} {\frac{2}{\sqrt{\pi}} \exp\left(\frac{i \pi}{4}\right) w^{\frac{1}{4}}}\end{split}]

这正是add函数中第三个参数res所需要的。最后,将此规则添加到表中的整个函数调用看起来像:

add([S(1)/4],
    [S(1)/2, S(5)/4],
    fresnelc(exp(pi*I/4)*root(z,4)*2/sqrt(pi)) / (exp(pi*I/4)*root(z,4)*2/sqrt(pi))
   ) 

使用这个规则,我们将发现它确实有效,但结果在简洁性和包含特殊函数实例数量方面并不理想。通过另一种方法将公式添加到查找表中,我们可以获得更好的结果。为此,我们使用(更复杂的)函数addb(ap, bq, B, C, M)。前两个参数再次是包含({}{1}F{2})参数集的列表。剩余的三个是本页早些时候提到的矩阵。

我们知道(n = \max{\left(p, q+1\right)})次导数可以表示为低阶导数的线性组合。矩阵(B)包含基础({B_0, B_1, \ldots}),形状为(n \times 1)。获取(B_i)的最佳方式是对({}_p F_q)的表达式进行前(n = \max(p, q+1))次导数,提取有用的部分。在我们的情况下,我们发现(n = \max{\left(1, 2+1\right)} = 3)。为了计算导数,我们必须使用操作符(z\frac{\mathrm{d}}{\mathrm{d}z})。第一个基础元素(B_0)设定为从上述({}_1 F_2)的表达式:

[B_0 = \frac{ \sqrt{\pi} \exp\left(-\frac{\mathbf{\imath}\pi}{4}\right) C\left( \frac{2}{\sqrt{\pi}} \exp\left(\frac{\mathbf{\imath}\pi}{4}\right) z^{\frac{1}{4}}\right)} {2 z^{\frac{1}{4}}}]

接下来计算(z\frac{\mathrm{d}}{\mathrm{d}z} B_0)。对此,我们可以直接使用 SymPy!

>>> from sympy import Symbol, sqrt, exp, I, pi, fresnelc, root, diff, expand
>>> z = Symbol("z")
>>> B0 = sqrt(pi)*exp(-I*pi/4)*fresnelc(2*root(z,4)*exp(I*pi/4)/sqrt(pi))/\
...          (2*root(z,4))
>>> z * diff(B0, z)
z*(cosh(2*sqrt(z))/(4*z) - sqrt(pi)*exp(-I*pi/4)*fresnelc(2*z**(1/4)*exp(I*pi/4)/sqrt(pi))/(8*z**(5/4)))
>>> expand(_)
cosh(2*sqrt(z))/4 - sqrt(pi)*exp(-I*pi/4)*fresnelc(2*z**(1/4)*exp(I*pi/4)/sqrt(pi))/(8*z**(1/4)) 

格式化这个结果得到

[B_1^\prime = - \frac{1}{4} \frac{ \sqrt{\pi} \exp\left(-\frac{\mathbf{\imath}\pi}{4}\right) C\left( \frac{2}{\sqrt{\pi}} \exp\left(\frac{\mathbf{\imath}\pi}{4}\right) z^{\frac{1}{4}}\right) } {2 z^{\frac{1}{4}}} + \frac{1}{4} \cosh{\left( 2 \sqrt{z} \right )}]

计算第二阶导数我们得到

>>> from sympy import (Symbol, cosh, sqrt, pi, exp, I, fresnelc, root,
...                    diff, expand)
>>> z = Symbol("z")
>>> B1prime = cosh(2*sqrt(z))/4 - sqrt(pi)*exp(-I*pi/4)*\
...           fresnelc(2*root(z,4)*exp(I*pi/4)/sqrt(pi))/(8*root(z,4))
>>> z * diff(B1prime, z)
z*(-cosh(2*sqrt(z))/(16*z) + sinh(2*sqrt(z))/(4*sqrt(z)) + sqrt(pi)*exp(-I*pi/4)*fresnelc(2*z**(1/4)*exp(I*pi/4)/sqrt(pi))/(32*z**(5/4)))
>>> expand(_)
sqrt(z)*sinh(2*sqrt(z))/4 - cosh(2*sqrt(z))/16 + sqrt(pi)*exp(-I*pi/4)*fresnelc(2*z**(1/4)*exp(I*pi/4)/sqrt(pi))/(32*z**(1/4)) 

可以打印为

[B_2^\prime = \frac{1}{16} \frac{ \sqrt{\pi} \exp\left(-\frac{\mathbf{\imath}\pi}{4}\right) C\left( \frac{2}{\sqrt{\pi}} \exp\left(\frac{\mathbf{\imath}\pi}{4}\right) z^{\frac{1}{4}}\right) } {2 z^{\frac{1}{4}}} - \frac{1}{16} \cosh{\left(2\sqrt{z}\right)} + \frac{1}{4} \sinh{\left(2\sqrt{z}\right)} \sqrt{z}]

我们看到了共同的模式,并且可以收集这些部分。因此,选择(B_1)和(B_2)如下所示是有意义的

[\begin{split}B = \left( \begin{matrix} B_0 \ B_1 \ B_2 \end{matrix} \right) = \left( \begin{matrix} \frac{ \sqrt{\pi} \exp\left(-\frac{\mathbf{\imath}\pi}{4}\right) C\left( \frac{2}{\sqrt{\pi}} \exp\left(\frac{\mathbf{\imath}\pi}{4}\right) z^{\frac{1}{4}}\right) }{2 z^{\frac{1}{4}}} \ \cosh\left(2\sqrt{z}\right) \ \sinh\left(2\sqrt{z}\right) \sqrt{z} \end{matrix} \right)\end{split}]

(这与基础(B = \left(B_0, B_1^\prime, B_2^\prime\right))的计算方法形成对比,如果我们只是使用add(ap, bq, res),它会自动计算。)

因为必须满足({}_p F_q\left(\cdots \middle| z \right) = C B),所以(C)的条目显然是

[\begin{split}C = \left( \begin{matrix} 1 \ 0 \ 0 \end{matrix} \right)\end{split}]

最后,我们必须计算(3 \times 3)矩阵(M)的条目,使得(z\frac{\mathrm{d}}{\mathrm{d}z} B = M B)成立。这很容易。我们已经计算了第一部分(z\frac{\mathrm{d}}{\mathrm{d}z} B_0)。这给我们了(M)的第一行。对于第二行,我们有:

>>> from sympy import Symbol, cosh, sqrt, diff
>>> z = Symbol("z")
>>> B1 = cosh(2*sqrt(z))
>>> z * diff(B1, z)
sqrt(z)*sinh(2*sqrt(z)) 

而对于第三个

>>> from sympy import Symbol, sinh, sqrt, expand, diff
>>> z = Symbol("z")
>>> B2 = sinh(2*sqrt(z))*sqrt(z)
>>> expand(z * diff(B2, z))
sqrt(z)*sinh(2*sqrt(z))/2 + z*cosh(2*sqrt(z)) 

现在我们已经计算出了该矩阵的条目为

[\begin{split}M = \left( \begin{matrix} -\frac{1}{4} & \frac{1}{4} & 0 \ 0 & 0 & 1 \ 0 & z & \frac{1}{2} \ \end{matrix} \right)\end{split}]

注意(C)和(M)的条目通常应该是(z)的有理函数,带有有理系数。这是我们为了将新的公式添加到hyperexpand查找表中所需做的一切。

实现了超几何公式

算法的一个重要部分是超几何函数表示的一个相对较大的表。以下是 SymPy 中实现的所有自动生成的表示(当然,还有更多来自它们的推导)。这些公式主要来自于[Luke1969][Prudnikov1990]。它们都经过了数值测试。

[\begin{split}{{}{0}F{0}\left(\begin{matrix} \ \end{matrix}\middle| {z} \right)} = e^{z}\end{split}][\begin{split}{{}{1}F{0}\left(\begin{matrix} a \ \end{matrix}\middle| {z} \right)} = \left(1 - z\right)^{- a}\end{split}][\begin{split}{{}{2}F{1}\left(\begin{matrix} a, a - \frac{1}{2} \ 2 a \end{matrix}\middle| {z} \right)} = 2^{2 a - 1} \left(\sqrt{1 - z} + 1\right)^{1 - 2 a}\end{split}][\begin{split}{{}{2}F{1}\left(\begin{matrix} 1, 1 \ 2 \end{matrix}\middle| {z} \right)} = - \frac{\log{\left(1 - z \right)}}{z}\end{split}][\begin{split}{{}{2}F{1}\left(\begin{matrix} \frac{1}{2}, 1 \ \frac{3}{2} \end{matrix}\middle| {z} \right)} = \frac{\operatorname{atanh}{\left(\sqrt{z} \right)}}{\sqrt{z}}\end{split}][\begin{split}{{}{2}F{1}\left(\begin{matrix} \frac{1}{2}, \frac{1}{2} \ \frac{3}{2} \end{matrix}\middle| {z} \right)} = \frac{\operatorname{asin}{\left(\sqrt{z} \right)}}{\sqrt{z}}\end{split}][\begin{split}{{}{2}F{1}\left(\begin{matrix} a, a + \frac{1}{2} \ \frac{1}{2} \end{matrix}\middle| {z} \right)} = \frac{\left(\sqrt{z} + 1\right)^{- 2 a}}{2} + \frac{\left(1 - \sqrt{z}\right)^{- 2 a}}{2}\end{split}][\begin{split}{{}{2}F{1}\left(\begin{matrix} a, - a \ \frac{1}{2} \end{matrix}\middle| {z} \right)} = \cos{\left(2 a \operatorname{asin}{\left(\sqrt{z} \right)} \right)}\end{split}][\begin{split}{{}{2}F{1}\left(\begin{matrix} 1, 1 \ \frac{3}{2} \end{matrix}\middle| {z} \right)} = \frac{\operatorname{asin}{\left(\sqrt{z} \right)}}{\sqrt{z} \sqrt{1 - z}}\end{split}][\begin{split}{{}{2}F{1}\left(\begin{matrix} \frac{1}{2}, \frac{1}{2} \ 1 \end{matrix}\middle| {z} \right)} = \frac{2 K\left(z\right)}{\pi}\end{split}][\begin{split}{{}{2}F{1}\left(\begin{matrix} - \frac{1}{2}, \frac{1}{2} \ 1 \end{matrix}\middle| {z} \right)} = \frac{2 E\left(z\right)}{\pi}\end{split}][\begin{split}{{}{3}F{2}\left(\begin{matrix} - \frac{1}{2}, 1, 1 \ \frac{1}{2}, 2 \end{matrix}\middle| {z} \right)} = - \frac{2 \sqrt{z} \operatorname{atanh}{\left(\sqrt{z} \right)}}{3} + \frac{2}{3} - \frac{\log{\left(1 - z \right)}}{3 z}\end{split}][\begin{split}{{}{3}F{2}\left(\begin{matrix} - \frac{1}{2}, 1, 1 \ 2, 2 \end{matrix}\middle| {z} \right)} = \left(\frac{4}{9} - \frac{16}{9 z}\right) \sqrt{1 - z} + \frac{4 \log{\left(\frac{\sqrt{1 - z}}{2} + \frac{1}{2} \right)}}{3 z} + \frac{16}{9 z}\end{split}][\begin{split}{{}{1}F{1}\left(\begin{matrix} 1 \ b \end{matrix}\middle| {z} \right)} = z^{1 - b} \left(b - 1\right) e^{z} \gamma\left(b - 1, z\right)\end{split}][\begin{split}{{}{1}F{1}\left(\begin{matrix} a \ 2 a \end{matrix}\middle| {z} \right)} = 4^{a - \frac{1}{2}} z^{\frac{1}{2} - a} e^{\frac{z}{2}} I_{a - \frac{1}{2}}\left(\frac{z}{2}\right) \Gamma\left(a + \frac{1}{2}\right)\end{split}][\begin{split}{{}{1}F{1}\left(\begin{matrix} a \ a + 1 \end{matrix}\middle| {z} \right)} = a \left(z e^{i \pi}\right)^{- a} \gamma\left(a, z e^{i \pi}\right)\end{split}][\begin{split}{{}{1}F{1}\left(\begin{matrix} - \frac{1}{2} \ \frac{1}{2} \end{matrix}\middle| {z} \right)} = \sqrt{z} i \sqrt{\pi} \operatorname{erf}{\left(\sqrt{z} i \right)} + e^{z}\end{split}][\begin{split}{{}{1}F{2}\left(\begin{matrix} 1 \ \frac{3}{4}, \frac{5}{4} \end{matrix}\middle| {z} \right)} = \frac{\sqrt{\pi} \left(i \sinh{\left(2 \sqrt{z} \right)} S\left(\frac{2 \sqrt[4]{z} e^{\frac{i \pi}{4}}}{\sqrt{\pi}}\right) + \cosh{\left(2 \sqrt{z} \right)} C\left(\frac{2 \sqrt[4]{z} e^{\frac{i \pi}{4}}}{\sqrt{\pi}}\right)\right) e^{- \frac{i \pi}{4}}}{2 \sqrt[4]{z}}\end{split}][\begin{split}{{}{2}F{2}\left(\begin{matrix} \frac{1}{2}, a \ \frac{3}{2}, a + 1 \end{matrix}\middle| {z} \right)} = - \frac{a i \sqrt{\pi} \sqrt{\frac{1}{z}} \operatorname{erf}{\left(\sqrt{z} i \right)}}{2 a - 1} - \frac{a \left(z e^{i \pi}\right)^{- a} \gamma\left(a, z e^{i \pi}\right)}{2 a - 1}\end{split}][\begin{split}{{}{2}F{2}\left(\begin{matrix} 1, 1 \ 2, 2 \end{matrix}\middle| {z} \right)} = \frac{- \log{\left(z \right)} + \operatorname{Ei}{\left(z \right)}}{z} - \frac{\gamma}{z}\end{split}][\begin{split}{{}{0}F{1}\left(\begin{matrix} \ \frac{1}{2} \end{matrix}\middle| {z} \right)} = \cosh{\left(2 \sqrt{z} \right)}\end{split}][\begin{split}{{}{0}F{1}\left(\begin{matrix} \ b \end{matrix}\middle| {z} \right)} = z^{\frac{1}{2} - \frac{b}{2}} I_{b - 1}\left(2 \sqrt{z}\right) \Gamma\left(b\right)\end{split}][\begin{split}{{}{0}F{3}\left(\begin{matrix} \ \frac{1}{2}, a, a + \frac{1}{2} \end{matrix}\middle| {z} \right)} = 2^{- 2 a} z^{\frac{1}{4} - \frac{a}{2}} \left(I_{2 a - 1}\left(4 \sqrt[4]{z}\right) + J_{2 a - 1}\left(4 \sqrt[4]{z}\right)\right) \Gamma\left(2 a\right)\end{split}][\begin{split}{{}{0}F{3}\left(\begin{matrix} \ a, 2 a, a + \frac{1}{2} \end{matrix}\middle| {z} \right)} = \left(2 \sqrt{z} e^{\frac{i \pi}{2}}\right)^{1 - 2 a} I_{2 a - 1}\left(2 \sqrt{2} \sqrt[4]{z} e^{\frac{i \pi}{4}}\right) J_{2 a - 1}\left(2 \sqrt{2} \sqrt[4]{z} e^{\frac{i \pi}{4}}\right) \Gamma^{2}\left(2 a\right)\end{split}][\begin{split}{{}{1}F{2}\left(\begin{matrix} a \ 2 a, a - \frac{1}{2} \end{matrix}\middle| {z} \right)} = 2 \cdot 4^{a - 1} z^{1 - a} I_{a - \frac{3}{2}}\left(\sqrt{z}\right) I_{a - \frac{1}{2}}\left(\sqrt{z}\right) \Gamma\left(a - \frac{1}{2}\right) \Gamma\left(a + \frac{1}{2}\right) - 4^{a - \frac{1}{2}} z^{\frac{1}{2} - a} I^{2}{a - \frac{1}{2}}\left(\sqrt{z}\right) \Gamma^{2}\left(a + \frac{1}{2}\right)\end{split}][\begin{split}{{}{1}F_{2}\left(\begin{matrix} \frac{1}{2} \ b, 2 - b \end{matrix}\middle| {z} \right)} = \frac{\pi \left(1 - b\right) I_{1 - b}\left(\sqrt{z}\right) I_{b - 1}\left(\sqrt{z}\right)}{\sin{\left(b \pi \right)}}\end{split}][\begin{split}{{}{1}F{2}\left(\begin{matrix} \frac{1}{2} \ \frac{3}{2}, \frac{3}{2} \end{matrix}\middle| {z} \right)} = \frac{\operatorname{Shi}{\left(2 \sqrt{z} \right)}}{2 \sqrt{z}}\end{split}][\begin{split}{{}{1}F{2}\left(\begin{matrix} \frac{3}{4} \ \frac{3}{2}, \frac{7}{4} \end{matrix}\middle| {z} \right)} = \frac{3 \sqrt{\pi} e^{- \frac{3 i \pi}{4}} S\left(\frac{2 \sqrt[4]{z} e^{\frac{i \pi}{4}}}{\sqrt{\pi}}\right)}{4 z^{\frac{3}{4}}}\end{split}][\begin{split}{{}{1}F{2}\left(\begin{matrix} \frac{1}{4} \ \frac{1}{2}, \frac{5}{4} \end{matrix}\middle| {z} \right)} = \frac{\sqrt{\pi} e^{- \frac{i \pi}{4}} C\left(\frac{2 \sqrt[4]{z} e^{\frac{i \pi}{4}}}{\sqrt{\pi}}\right)}{2 \sqrt[4]{z}}\end{split}][\begin{split}{{}{2}F{3}\left(\begin{matrix} a, a + \frac{1}{2} \ b, 2 a, 2 a - b + 1 \end{matrix}\middle| {z} \right)} = \left(\frac{\sqrt{z}}{2}\right)^{1 - 2 a} I_{2 a - b}\left(\sqrt{z}\right) I_{b - 1}\left(\sqrt{z}\right) \Gamma\left(b\right) \Gamma\left(2 a - b + 1\right)\end{split}][\begin{split}{{}{2}F{3}\left(\begin{matrix} 1, 1 \ \frac{3}{2}, 2, 2 \end{matrix}\middle| {z} \right)} = \frac{- \log{\left(2 \sqrt{z} \right)} + \operatorname{Chi}\left(2 \sqrt{z}\right)}{z} - \frac{\gamma}{z}\end{split}][\begin{split}{{}{3}F{3}\left(\begin{matrix} 1, 1, a \ 2, 2, a + 1 \end{matrix}\middle| {z} \right)} = \frac{a \left(- z\right)^{- a} \left(\Gamma\left(a\right) - \Gamma\left(a, - z\right)\right)}{\left(a - 1\right)^{2}} + \frac{a \left(1 - a\right) \left(\log{\left(- z \right)} + \operatorname{E}_{1}\left(- z\right) + \gamma\right)}{z \left(a^{2} - 2 a + 1\right)} - \frac{a e^{z}}{z \left(a^{2} - 2 a + 1\right)} + \frac{a}{z \left(a^{2} - 2 a + 1\right)}\end{split}]

参考文献

[Roach1996]

Kelly B. Roach. 超几何函数表示。在:1996 年国际符号和代数计算研讨会论文集,页码 301-308,纽约,1996 年。ACM。

[Roach1997]

Kelly B. Roach. Meijer G 函数表示。在:1997 年国际符号和代数计算研讨会论文集,页码 205-211,纽约,1997 年。ACM。

[Luke1969]

Luke, Y. L. (1969),特殊函数及其近似,第 1 卷。

[Prudnikov1990]

A. P. Prudnikov, Yu. A. Brychkov 和 O. I. Marichev (1990)。积分和级数:更多特殊函数,第 3 卷,Gordon and Breach Science Publisher。

《洪光·傅的三角简化》

原文链接:docs.sympy.org/latest/modules/simplify/fu.html

Fu 等人的三角简化算法实现

Fu 算法的核心思想是利用学生在预微积分课程中学到的一系列规则。这些规则是启发式应用的,它使用贪婪算法同时应用多个规则,并选择具有最少叶子节点的结果。

存在一些转换规则,其中单个规则应用于表达树。以下仅仅是助记性质的;详细示例请参见文档字符串。

  • TR0() - 简化表达式

  • TR1() - sec-csc 到 cos-sin

  • TR2() - tan-cot 到 sin-cos 的比率

  • TR2i() - sin-cos 的比率到 tan

  • TR3() - 角度规范化

  • TR4() - 特定角度上的函数

  • TR5() - sin 的幂到 cos 的幂

  • TR6() - cos 的幂到 sin 的幂

  • TR7() - 减少 cos 的幂(增加角度)

  • TR8() - 展开 sin-cos 的乘积为和

  • TR9() - 将 sin-cos 的和约简为乘积

  • TR10() - 分离 sin-cos 的参数

  • TR10i() - 收集 sin-cos 的参数

  • TR11() - 减少双角度

  • TR12() - 分离 tan 的参数

  • TR12i() - 收集 tan 的参数

  • TR13() - 展开 tan-cot 的乘积

  • TRmorrie() - prod(cos(x2i), (i, 0, k - 1)) -> sin(2kx)/(2**k*sin(x))

  • TR14() - sin 或 cos 的幂的因式分解到 cos 或 sin 的幂

  • TR15() - sin 的负幂到 cot 的幂

  • TR16() - cos 的负幂到 tan 的幂

  • TR22() - tan-cot 的幂到 sec-csc 函数的负幂

  • TR111() - sin-cos-tan 的负幂到 csc-sec-cot

存在 4 种组合转换(CTR1 - CTR4),其中应用一系列转换,并从几个选项中选择最简表达式。

最后,有两个规则列表(RL1 和 RL2),它们应用一系列转换和组合转换,以及fu算法本身,它应用规则和规则列表并选择最佳表达式。还有一个函数L,它计算表达式中出现的三角函数的数量。

除了 TR0 外,转换不会重新编写表达式。例如,TR10i 找到了一个形式为cos(x)*cos(y) + sin(x)*sin(y)的和的项对。这类表达式在对表达式进行自下而上的遍历时被针对,但不会尝试操纵它们使其出现。例如,

为下面的示例做准备:

>>> from sympy.simplify.fu import fu, L, TR9, TR10i, TR11
>>> from sympy import factor, sin, cos, powsimp
>>> from sympy.abc import x, y, z, a
>>> from time import time 
>>> eq = cos(x + y)/cos(x)
>>> TR10i(eq.expand(trig=True))
-sin(x)*sin(y)/cos(x) + cos(y) 

如果将表达式放在“正常”形式(具有共同的分母)中,则转换是成功的:

>>> TR10i(_.normal())
cos(x + y)/cos(x) 

TR11 的行为类似。它将双角重新写成较小的角度,但不对结果进行任何简化。

>>> TR11(sin(2)**a*cos(1)**(-a), 1)
(2*sin(1)*cos(1))**a/cos(1)**a
>>> powsimp(_)
(2*sin(1))**a 

诱惑是尝试使这些 TR 规则“更智能化”,但实际上应该在更高的层次上完成;TR 规则应尝试保持“专注于一件事”的原则。然而,有一个例外。在 TR10i 和 TR9 中,即使它们各自乘以一个公因子,也会识别到术语:

>>> fu(a*cos(x)*cos(y) + a*sin(x)*sin(y))
a*cos(x - y) 

使用factor_terms进行因式分解,但它类似于“即时”执行,直到被认为有必要才执行。此外,如果因式分解对简化没有帮助,则不会保留它,因此a*cos(x)*cos(y) + a*sin(x)*sin(z)不会变成一个因式分解(但在三角函数意义上未简化)的表达式:

>>> fu(a*cos(x)*cos(y) + a*sin(x)*sin(z))
a*sin(x)*sin(z) + a*cos(x)*cos(y) 

在某些情况下,因式分解可能是一个好主意,但用户需要自行决定。例如:

>>> expr=((15*sin(2*x) + 19*sin(x + y) + 17*sin(x + z) + 19*cos(x - z) +
... 25)*(20*sin(2*x) + 15*sin(x + y) + sin(y + z) + 14*cos(x - z) +
... 14*cos(y - z))*(9*sin(2*y) + 12*sin(y + z) + 10*cos(x - y) + 2*cos(y -
... z) + 18)).expand(trig=True).expand() 

在展开状态下,有近 1000 个三角函数:

>>> L(expr)
932 

如果首先对表达式进行因式分解,这将花费时间,但生成的表达式将非常快速地被转换:

>>> def clock(f, n=2):
...    t=time(); f(); return round(time()-t, n)
...
>>> clock(lambda: factor(expr))  
0.86
>>> clock(lambda: TR10i(expr), 3)  
0.016 

如果使用未展开的表达式,则转换时间较长,但不如因式分解和转换所需的时间长:

>>> clock(lambda: TR10i(expr), 2)  
0.28 

因此,在TR10i中既不使用展开也不使用因式分解:如果表达式已经被因式分解(或部分因式分解),那么带有trig=True的展开会破坏已知内容并且需要更长时间;如果表达式已经展开,进行因式分解可能比简单应用转换本身还要花费更长时间。

尽管算法应该是规范的,总是给出相同的结果,但它们可能不会产生最佳结果。这一般是简化的本质,因为搜索所有可能的转换路径非常昂贵。这里有一个简单的例子。以下和有 6 项的和:

>>> expr = (sin(x)**2*cos(y)*cos(z) + sin(x)*sin(y)*cos(x)*cos(z) +
... sin(x)*sin(z)*cos(x)*cos(y) + sin(y)*sin(z)*cos(x)**2 + sin(y)*sin(z) +
... cos(y)*cos(z))
>>> args = expr.args 

出乎意料地,fu提供了最佳结果:

>>> fu(expr)
3*cos(y - z)/2 - cos(2*x + y + z)/2 

但是,如果合并不同的项,则可能得到一个次优结果,需要额外的工作来获得更好的简化,但仍然不是最优的。以下显示了一种expr的另一种形式,一旦采取某个步骤,则阻碍最优简化,因为它导致了死胡同:

>>> TR9(-cos(x)**2*cos(y + z) + 3*cos(y - z)/2 +
...     cos(y + z)/2 + cos(-2*x + y + z)/4 - cos(2*x + y + z)/4)
sin(2*x)*sin(y + z)/2 - cos(x)**2*cos(y + z) + 3*cos(y - z)/2 + cos(y + z)/2 

这里有一个展示相同行为的较小表达式:

>>> a = sin(x)*sin(z)*cos(x)*cos(y) + sin(x)*sin(y)*cos(x)*cos(z)
>>> TR10i(a)
sin(x)*sin(y + z)*cos(x)
>>> newa = _
>>> TR10i(expr - a)  # this combines two more of the remaining terms
sin(x)**2*cos(y)*cos(z) + sin(y)*sin(z)*cos(x)**2 + cos(y - z)
>>> TR10i(_ + newa) == _ + newa  # but now there is no more simplification
True 

在没有侥幸或尝试所有可能的参数对的情况下,最终结果可能不太理想,并且没有更好的启发法或所有可能性的暴力试验,无法找到。

规则

sympy.simplify.fu.TR0(rv)

简化有理多项式,尝试简化表达式,例如组合像 3x + 2x 这样的东西等……

sympy.simplify.fu.TR1(rv)

用 1/cos 和 1/sin 替换 sec 和 csc

例子

>>> from sympy.simplify.fu import TR1, sec, csc
>>> from sympy.abc import x
>>> TR1(2*csc(x) + sec(x))
1/cos(x) + 2/sin(x) 
sympy.simplify.fu.TR2(rv)

用 sin/cos 和 cos/sin 替换 tan 和 cot

例子

>>> from sympy.simplify.fu import TR2
>>> from sympy.abc import x
>>> from sympy import tan, cot, sin, cos
>>> TR2(tan(x))
sin(x)/cos(x)
>>> TR2(cot(x))
cos(x)/sin(x)
>>> TR2(tan(tan(x) - sin(x)/cos(x)))
0 
sympy.simplify.fu.TR2i(rv, half=False)

将涉及 sin 和 cos 的比率转换为:

sin(x)/cos(x) -> tan(x) sin(x)/(cos(x) + 1) -> tan(x/2),如果 half=True

例子

>>> from sympy.simplify.fu import TR2i
>>> from sympy.abc import x, a
>>> from sympy import sin, cos
>>> TR2i(sin(x)/cos(x))
tan(x) 

分子和分母的幂也会被识别

>>> TR2i(sin(x)**2/(cos(x) + 1)**2, half=True)
tan(x/2)**2 

除非假设允许(即基数必须为正或指数必须为分子和分母的整数),否则转换不会发生。

>>> TR2i(sin(x)**a/(cos(x) + 1)**a)
sin(x)**a/(cos(x) + 1)**a 
sympy.simplify.fu.TR3(rv)

引出的公式:例子 sin(-a) = -sin(a)

例子

>>> from sympy.simplify.fu import TR3
>>> from sympy.abc import x, y
>>> from sympy import pi
>>> from sympy import cos
>>> TR3(cos(y - x*(y - x)))
cos(x*(x - y) + y)
>>> cos(pi/2 + x)
-sin(x)
>>> cos(30*pi/2 + x)
-cos(x) 
sympy.simplify.fu.TR4(rv)

识别特殊角度的值。

A= 0 Pi/6 Pi/4 Pi/3 Pi/2

sin(a) 0 1/2 sqrt(2)/2 sqrt(3)/2 1 cos(a) 1 sqrt(3)/2 sqrt(2)/2 1/2 0 tan(a) 0 sqt(3)/3 1 sqrt(3) –

例子

>>> from sympy import pi
>>> from sympy import cos, sin, tan, cot
>>> for s in (0, pi/6, pi/4, pi/3, pi/2):
...    print('%s  %s  %s  %s' % (cos(s), sin(s), tan(s), cot(s)))
...
1 0 0 zoo
sqrt(3)/2 1/2 sqrt(3)/3 sqrt(3)
sqrt(2)/2 sqrt(2)/2 1 1
1/2 sqrt(3)/2 sqrt(3) sqrt(3)/3
0 1 zoo 0 
sympy.simplify.fu.TR5(rv, max=4, pow=False)

用 1 - cos(x)2 替换 sin2。

查看 _TR56 文档字符串以了解maxpow的高级用法。

例子

>>> from sympy.simplify.fu import TR5
>>> from sympy.abc import x
>>> from sympy import sin
>>> TR5(sin(x)**2)
1 - cos(x)**2
>>> TR5(sin(x)**-2)  # unchanged
sin(x)**(-2)
>>> TR5(sin(x)**4)
(1 - cos(x)**2)**2 
sympy.simplify.fu.TR6(rv, max=4, pow=False)

用 1 - sin(x)2 替换 cos2。

查看 _TR56 文档字符串以了解maxpow的高级用法。

例子

>>> from sympy.simplify.fu import TR6
>>> from sympy.abc import x
>>> from sympy import cos
>>> TR6(cos(x)**2)
1 - sin(x)**2
>>> TR6(cos(x)**-2)  #unchanged
cos(x)**(-2)
>>> TR6(cos(x)**4)
(1 - sin(x)**2)**2 
sympy.simplify.fu.TR7(rv)

降低 cos(x)**2 的度数。

例子

>>> from sympy.simplify.fu import TR7
>>> from sympy.abc import x
>>> from sympy import cos
>>> TR7(cos(x)**2)
cos(2*x)/2 + 1/2
>>> TR7(cos(x)**2 + 1)
cos(2*x)/2 + 3/2 
sympy.simplify.fu.TR8(rv, first=True)

cos和/或sin的乘积转换为cos和/或sin项的和或差。

例子

>>> from sympy.simplify.fu import TR8
>>> from sympy import cos, sin
>>> TR8(cos(2)*cos(3))
cos(5)/2 + cos(1)/2
>>> TR8(cos(2)*sin(3))
sin(5)/2 + sin(1)/2
>>> TR8(sin(2)*sin(3))
-cos(5)/2 + cos(1)/2 
sympy.simplify.fu.TR9(rv)

cossin项的和作为cossin的乘积。

例子

>>> from sympy.simplify.fu import TR9
>>> from sympy import cos, sin
>>> TR9(cos(1) + cos(2))
2*cos(1/2)*cos(3/2)
>>> TR9(cos(1) + 2*sin(1) + 2*sin(2))
cos(1) + 4*sin(3/2)*cos(1/2) 

如果 TR9 没有进行任何更改,则不会重新排列表达式。例如,尽管尝试因式分解公共项,但如果因式分解的表达式没有改变,将返回原始表达式:

>>> TR9(cos(3) + cos(3)*cos(2))
cos(3) + cos(2)*cos(3) 
sympy.simplify.fu.TR10(rv, first=True)

cossin中分离求和。

例子

>>> from sympy.simplify.fu import TR10
>>> from sympy.abc import a, b, c
>>> from sympy import cos, sin
>>> TR10(cos(a + b))
-sin(a)*sin(b) + cos(a)*cos(b)
>>> TR10(sin(a + b))
sin(a)*cos(b) + sin(b)*cos(a)
>>> TR10(sin(a + b + c))
(-sin(a)*sin(b) + cos(a)*cos(b))*sin(c) +     (sin(a)*cos(b) + sin(b)*cos(a))*cos(c) 
sympy.simplify.fu.TR10i(rv)

产品的和到函数的简化。

例子

>>> from sympy.simplify.fu import TR10i
>>> from sympy import cos, sin, sqrt
>>> from sympy.abc import x 
>>> TR10i(cos(1)*cos(3) + sin(1)*sin(3))
cos(2)
>>> TR10i(cos(1)*sin(3) + sin(1)*cos(3) + cos(3))
cos(3) + sin(4)
>>> TR10i(sqrt(2)*cos(x)*x + sqrt(6)*sin(x)*x)
2*sqrt(2)*x*sin(x + pi/6) 
sympy.simplify.fu.TR11(rv, base=None)

双角函数到乘积的函数。base参数可用于指示未加倍的参数,例如,如果 3pi/7 是基础,则参数为 6pi/7 的 cosine 和 sine 函数将被替换。

例子

>>> from sympy.simplify.fu import TR11
>>> from sympy import cos, sin, pi
>>> from sympy.abc import x
>>> TR11(sin(2*x))
2*sin(x)*cos(x)
>>> TR11(cos(2*x))
-sin(x)**2 + cos(x)**2
>>> TR11(sin(4*x))
4*(-sin(x)**2 + cos(x)**2)*sin(x)*cos(x)
>>> TR11(sin(4*x/3))
4*(-sin(x/3)**2 + cos(x/3)**2)*sin(x/3)*cos(x/3) 

如果参数只是整数,则不会进行任何更改,除非提供基数:

>>> TR11(cos(2))
cos(2)
>>> TR11(cos(4), 2)
-sin(2)**2 + cos(2)**2 

这里有一个微妙的问题,即自动简化将一些更高的角度转换为较低的角度。

>>> cos(6*pi/7) + cos(3*pi/7)
-cos(pi/7) + cos(3*pi/7) 

6pi/7 角现在是 pi/7,但可以通过提供 3pi/7 的基数目标到 TR11:

>>> TR11(_, 3*pi/7)
-sin(3*pi/7)**2 + cos(3*pi/7)**2 + cos(3*pi/7) 
sympy.simplify.fu.TR12(rv, first=True)

tan中分离求和。

例子

>>> from sympy.abc import x, y
>>> from sympy import tan
>>> from sympy.simplify.fu import TR12
>>> TR12(tan(x + y))
(tan(x) + tan(y))/(-tan(x)*tan(y) + 1) 
sympy.simplify.fu.TR12i(rv)

将 tan(y) + tan(x))/(tan(x)*tan(y) - 1)组合为-tan(x + y)的参数。

例子

>>> from sympy.simplify.fu import TR12i
>>> from sympy import tan
>>> from sympy.abc import a, b, c
>>> ta, tb, tc = [tan(i) for i in (a, b, c)]
>>> TR12i((ta + tb)/(-ta*tb + 1))
tan(a + b)
>>> TR12i((ta + tb)/(ta*tb - 1))
-tan(a + b)
>>> TR12i((-ta - tb)/(ta*tb - 1))
tan(a + b)
>>> eq = (ta + tb)/(-ta*tb + 1)**2*(-3*ta - 3*tc)/(2*(ta*tc - 1))
>>> TR12i(eq.expand())
-3*tan(a + b)*tan(a + c)/(2*(tan(a) + tan(b) - 1)) 
sympy.simplify.fu.TR13(rv)

更改tancot的产品。

例子

>>> from sympy.simplify.fu import TR13
>>> from sympy import tan, cot
>>> TR13(tan(3)*tan(2))
-tan(2)/tan(5) - tan(3)/tan(5) + 1
>>> TR13(cot(3)*cot(2))
cot(2)*cot(5) + 1 + cot(3)*cot(5) 
sympy.simplify.fu.TRmorrie(rv)

返回 cos(x)cos(2x)cos(2(k-1)x) -> sin(2**kx)/(2**k*sin(x))

例子

>>> from sympy.simplify.fu import TRmorrie, TR8, TR3
>>> from sympy.abc import x
>>> from sympy import Mul, cos, pi
>>> TRmorrie(cos(x)*cos(2*x))
sin(4*x)/(4*sin(x))
>>> TRmorrie(7*Mul(*[cos(x) for x in range(10)]))
7*sin(12)*sin(16)*cos(5)*cos(7)*cos(9)/(64*sin(1)*sin(3)) 

有时,自动简化会导致某个幂不被识别。例如,在以下情况中,cos(4pi/7)会自动简化为-cos(3pi/7),因此只有 3 个术语中的 2 个会被识别:

>>> TRmorrie(cos(pi/7)*cos(2*pi/7)*cos(4*pi/7))
-sin(3*pi/7)*cos(3*pi/7)/(4*sin(pi/7)) 

TR8 轻触将表达式解决为有理数

>>> TR8(_)
-1/8 

在这种情况下,如果未简化等式,将直接获得答案:

>>> eq = cos(pi/9)*cos(2*pi/9)*cos(3*pi/9)*cos(4*pi/9)
>>> TRmorrie(eq)
1/16 

但是如果角度通过 TR3 变为规范化,则未简化的答案将直接获得:

>>> TR3(eq)
sin(pi/18)*cos(pi/9)*cos(2*pi/9)/2
>>> TRmorrie(_)
sin(pi/18)*sin(4*pi/9)/(8*sin(pi/9))
>>> TR8(_)
cos(7*pi/18)/(16*sin(pi/9))
>>> TR3(_)
1/16 

原始表达式将直接解决为 1/16,但是:

>>> TR8(eq)
1/16 

参考

[R852]

en.wikipedia.org/wiki/Morrie%27s_law 的内容。

sympy.simplify.fu.TR14(rv, first=True)

将 sin 和 cos 的因式分解幂转换为更简单的表达式。

示例

>>> from sympy.simplify.fu import TR14
>>> from sympy.abc import x, y
>>> from sympy import cos, sin
>>> TR14((cos(x) - 1)*(cos(x) + 1))
-sin(x)**2
>>> TR14((sin(x) - 1)*(sin(x) + 1))
-cos(x)**2
>>> p1 = (cos(x) + 1)*(cos(x) - 1)
>>> p2 = (cos(y) - 1)*2*(cos(y) + 1)
>>> p3 = (3*(cos(y) - 1))*(3*(cos(y) + 1))
>>> TR14(p1*p2*p3*(x - 1))
-18*(x - 1)*sin(x)**2*sin(y)**4 
sympy.simplify.fu.TR15(rv, max=4, pow=False)

将 sin(x)**-2 转换为 1 + cot(x)**2。

查看 _TR56 的 docstring 以了解 maxpow 的高级用法。

示例

>>> from sympy.simplify.fu import TR15
>>> from sympy.abc import x
>>> from sympy import sin
>>> TR15(1 - 1/sin(x)**2)
-cot(x)**2 
sympy.simplify.fu.TR16(rv, max=4, pow=False)

将 cos(x)**-2 转换为 1 + tan(x)**2。

查看 _TR56 的 docstring 以了解 maxpow 的高级用法。

示例

>>> from sympy.simplify.fu import TR16
>>> from sympy.abc import x
>>> from sympy import cos
>>> TR16(1 - 1/cos(x)**2)
-tan(x)**2 
sympy.simplify.fu.TR111(rv)

将 f(x)**-i 转换为 g(x)**i,其中 i 是整数或基数为正且 f、g 是:tan、cot;sin、csc;或 cos、sec。

示例

>>> from sympy.simplify.fu import TR111
>>> from sympy.abc import x
>>> from sympy import tan
>>> TR111(1 - 1/tan(x)**2)
1 - cot(x)**2 
sympy.simplify.fu.TR22(rv, max=4, pow=False)

将 tan(x)**2 转换为 sec(x)**2 - 1,cot(x)**2 转换为 csc(x)**2 - 1。

查看 _TR56 的 docstring 以了解 maxpow 的高级用法。

示例

>>> from sympy.simplify.fu import TR22
>>> from sympy.abc import x
>>> from sympy import tan, cot
>>> TR22(1 + tan(x)**2)
sec(x)**2
>>> TR22(1 + cot(x)**2)
csc(x)**2 
sympy.simplify.fu.TRpower(rv)

将 sin(x)**n 和 cos(x)**n(其中 n 为正数)转换为和的形式。

示例

>>> from sympy.simplify.fu import TRpower
>>> from sympy.abc import x
>>> from sympy import cos, sin
>>> TRpower(sin(x)**6)
-15*cos(2*x)/32 + 3*cos(4*x)/16 - cos(6*x)/32 + 5/16
>>> TRpower(sin(x)**3*cos(2*x)**4)
(3*sin(x)/4 - sin(3*x)/4)*(cos(4*x)/2 + cos(8*x)/8 + 3/8) 

参考文献

[R853]

en.wikipedia.org/wiki/List_of_trigonometric_identities#Power-reduction_formulae 的内容。

sympy.simplify.fu.fu(rv, measure=<function <lambda>>)

尝试使用 Fu 等人算法中给出的转换规则简化表达式。

fu() 将尝试通过最小化目标函数 measure 来简化表达式。默认情况下,首先最小化三角函数的数量,然后最小化总操作数。

示例

>>> from sympy.simplify.fu import fu
>>> from sympy import cos, sin, tan, pi, S, sqrt
>>> from sympy.abc import x, y, a, b 
>>> fu(sin(50)**2 + cos(50)**2 + sin(pi/6))
3/2
>>> fu(sqrt(6)*cos(x) + sqrt(2)*sin(x))
2*sqrt(2)*sin(x + pi/3) 

CTR1 示例

>>> eq = sin(x)**4 - cos(y)**2 + sin(y)**2 + 2*cos(x)**2
>>> fu(eq)
cos(x)**4 - 2*cos(y)**2 + 2 

CTR2 示例

>>> fu(S.Half - cos(2*x)/2)
sin(x)**2 

CTR3 示例

>>> fu(sin(a)*(cos(b) - sin(b)) + cos(a)*(sin(b) + cos(b)))
sqrt(2)*sin(a + b + pi/4) 

CTR4 示例

>>> fu(sqrt(3)*cos(x)/2 + sin(x)/2)
sin(x + pi/3) 

示例 1

>>> fu(1-sin(2*x)**2/4-sin(y)**2-cos(x)**4)
-cos(x)**2 + cos(y)**2 

示例 2

>>> fu(cos(4*pi/9))
sin(pi/18)
>>> fu(cos(pi/9)*cos(2*pi/9)*cos(3*pi/9)*cos(4*pi/9))
1/16 

示例 3

>>> fu(tan(7*pi/18)+tan(5*pi/18)-sqrt(3)*tan(5*pi/18)*tan(7*pi/18))
-sqrt(3) 

目标函数示例

>>> fu(sin(x)/cos(x))  # default objective function
tan(x)
>>> fu(sin(x)/cos(x), measure=lambda x: -x.count_ops()) # maximize op count
sin(x)/cos(x) 

参考文献

[R854]

www.sciencedirect.com/science/article/pii/S0895717706001609 的内容。

注意

这项工作由 Dimitar Vlahovski 在 “Electronic systems” 技术学校(2011 年 11 月 30 日)开始。

超出 TR13,其他规则不是来自原始论文,而是在 SymPy 中扩展的。

参考文献