繁体   English   中英

Sympy:通过表达式替换进行简化

[英]Sympy : simplification with expression substitution

我有几个表达式涉及向量u的范数或范数平方。 我想通过用已知值代替u的范数来简化这些表达式。 然而,似乎涉及范数的简单倍数的明显表达式并没有被简化。

例如,这段代码符合我的预期:

import sympy as sp

u1,u2,u3 = sp.symbols('u_1, u_2, u_3',real=True,positive=True)

utu = u1**2 + u2**2 + u3**2

print("Ex. 1")
print(utu.subs(utu,1))

这会产生预期的 output

Ex. 1
1

但是, 2*utu并没有按照我期望的方式简化:

print("Ex 2")
print((2*utu).subs(utu,1))
Ex 2
2*u_1**2 + 2*u_2**2 + 2*u_3**2

我可以用这个明确地强制替换:

print("Ex 3")
print((2*utu).subs(2*utu,2))

产生预期的 output:

Ex 3
2

理想情况下,我想代入一个norm function,但遇到同样的问题。

u = sp.Matrix(3, 1, [u1,u2,u3])

print("Ex 4")
print(u.norm().subs(utu,1))

print("Ex 5")
print((2*u).norm().subs(utu,1))

print("Ex 6")
print((2*u).norm().subs(4*utu,4))

产生

Ex 4
1
Ex 5
sqrt(4*u_1**2 + 4*u_2**2 + 4*u_3**2)
Ex 6
2

是否有我遗漏的技巧可以抓住这些明显的(至少对我来说 - 也许不是 Sympy?) 简化? 我试过factorexpand ,运气不佳。

让我们分析一下这个表达式:

expr = 2*utu
# out: 2*u_1**2 + 2*u_2**2 + 2*u_3**2

已评估乘法。 这是 SymPy 的默认行为:它评估事物。 我们可以使用表达式操作函数来实现我们的目标。

例如:

expr = collect_const(expr)
# out: 2*(u_1**2 + u_2**2 + u_3**2)
expr.subs(utu, 1)
# out: 2

另一个例子:

expr = (2 * u).norm()
# out: sqrt(4*u_1**2 + 4*u_2**2 + 4*u_3**2)
expr = expr.simplify() # Note that expr.factor() produces the same result with this expression
# out: 2*sqrt(u_1**2 + u_2**2 + u_3**2)
expr.subs(utu, 1)
# out: 2

如果您尝试(和修改)这些示例,您将意识到可以使用不同的函数(factor、simplify、collect、collect_const,...)获得相同的结果,但即使是表达式中的一点小变化也可能会阻止 function从“做它的工作”,而其他人可能能够。 表情操作是一种应该练习(很多)的艺术。

为了完整起见,我将向您展示UnevaluatedExpr ,它允许特定表达式在表达式操作期间保持未计算状态,尽管它可能并不总是最佳选择:

n = UnevaluatedExpr(utu)
# out: u_1**2 + u_2**2 + u_3**2
expr = 4 * n
# out: 4*(u_1**2 + u_2**2 + u_3**2)

请注意,SymPy 没有进行完整的评估。 现在:

expr.subs(utu, 1)
# out: 4*1

为什么有一个4*1而不是 4? 1指的是我们之前创建的UnevaluateExpr object:要评估它,我们可以使用doit()方法:

expr.subs(utu, 1).doit()
# 4

请记住,在使用UnevaluateExpr时,表达式变得不可交换(我认为这是 SymPy 的错误),这将阻止其他函数产生预期结果。

替换复合表达式是有问题的。 在大多数情况下,如果已知要替换的表达式始终按字面意义作为您要替换的表达式树的一部分出现,则您应该只期望 subs 起作用。 如果可能,最好重新排列单个符号,例如:

In [10]: utu
Out[10]: 
  2     2     2
u₁  + u₂  + u₃ 

In [11]: (2*utu).subs(u1**2, 1 - u2**2 - u3**2)
Out[11]: 2

即使在这里,我们也用符号的幂 ( u1**2 ) 代替,如果我们不能确定该幂总是会出现在表达式中,那么它可能很脆弱。 更一般地说,有一些函数可以根据了解一些多项式关系来简化表达式,例如ratsimpmodprime

In [16]: e = (1 - u1**2) / (u1**2 + u2**2 + u3**2)

In [17]: e
Out[17]: 
          2    
    1 - u₁     
───────────────
  2     2     2
u₁  + u₂  + u₃ 

In [18]: ratsimpmodprime(e, [u1**2 + u2**2 + u3**2 - 1])
Out[18]: 
  2     2
u₂  + u₃ 

其他可能性可能是使用合成物或 Groebner 碱来做类似的事情。 请注意u.norm()有一个平方根,这在符号上很尴尬,因此最好使用范数的平方(与在笔和纸上工作时相同):

In [20]: ratsimpmodprime((2*u).norm()**2, [u1**2 + u2**2 + u3**2 - 1])
Out[20]: 4

此外,如果你只是想要一个更强大的 subs 版本,那么你可以使用替换但使用模式:

In [21]: a = Wild('a')

In [22]: p = a*u1**2 + a*u2**2 + a*u3**2

In [23]: (2*utu).replace(p, a)
Out[23]: 2

In [24]: (2*u).norm().replace(p, a)
Out[24]: 2

这两个答案都已经很可靠了。 如果你有一个任意表达式,你希望它成为另一个因素, factor_terms是我首先尝试使该因素出现的方法。 它将收集共同因素而不进行因式分解。 但是,如果这不起作用并且您知道自己有一个因子,那么div是检查和查看已删除因子的表达式的好方法:

>>> expr = 2*(x + y)
>>> factor_terms(expr)
2*(x + y)
>>> e2 = expand(expr*(x -y))  # 2*x**2 - y**2
>>> factor_terms(e2)
2*(x**2 - y**2)
>>> div(_,-x-y)
(-2*x + 2*y, 0)
>>> _[0]*z  # if you wanted to replace factor -x-y with z
z*(-2*x + 2*y)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM