我只是想知道在 python 的 Sympy 模块中是否存在将代数幂扩展为x**2
乘法形式(即)的现有方法?x**2 -> x*x
谢谢!
没有直接的支持。SymPy 自动组合乘法中的常用项以求幂。避免这种情况发生的唯一方法是使用该evaluate=False
机制。例如
>>> Mul(x, x, evaluate=False)
x*x
不久前在 SymPy 邮件列表上讨论了这个确切的问题(https://groups.google.com/d/topic/sympy/qaJGesRbX_0/discussion)。我在那里发布了一些代码可以做到这一点。我会在这里重复一遍:
def pow_to_mul(expr):
"""
Convert integer powers in an expression to Muls, like a**2 => a*a.
"""
pows = list(expr.atoms(Pow))
if any(not e.is_Integer for b, e in (i.as_base_exp() for i in pows)):
raise ValueError("A power contains a non-integer exponent")
repl = zip(pows, (Mul(*[b]*e,evaluate=False) for b,e in (i.as_base_exp() for i in pows)))
return expr.subs(repl)
这是它的工作原理
>>> a = Symbol('a')
>>> exp = a**2
>>> print(exp)
a**2
>>> print(pow_to_mul(exp))
a*a
我将在邮件列表中提出相同的警告:“evaluate=False 有点像 hack,所以请注意它很脆弱。一些函数会重新计算表达式,将其转换回 Pow。其他函数会中断,因为一些预期的不变量将被 evaluate=False 表达式破坏(例如,我怀疑 factor() 会正常工作)。”
似乎没有这样的事情,它只做相反的事情。
sympy 总是以最简单的方式显示输出,所以它总是会说:
(x**2).expand() -> x**2
simplify(x**2) -> x**2
replace 方法非常适合用于简单表达式的此任务:
>>> expr = (x**2 + 1)/(x**3 - 2*x)
>>> expr.replace(
... lambda x: x.is_Pow and x.exp > 0,
... lambda x: Mul(*[x.base]*x.exp, evaluate=False))
(x*x + 1)/(-2*x + x*x*x)
调整是必要的,以处理诸如1/x**3
或之类的事情x**2*(1 + x**2)
。但是,如果您扩展表达式的分子和分母并分别处理它们,这可能会满足您的需求。如果基础总是符号,那么这个符号黑客可能会做得更好:
>>> def sack(expr):
... return expr.replace(
... lambda x: x.is_Pow and x.exp > 0,
... lambda x: Symbol('*'.join([x.base.name]*x.exp)))
...
>>> sack(-x**2)
-x*x
>>> sack(x**2*(1 + x**3)
x*x*(x*x*x + 1)
继 Aaron 接受的答案和我对此的评论之后,这是xreplace
我使用的版本,而不是最后subs
一行,以避免评估子表达式(从而失去对乘法链的扩展能力)。
def non_eval_xreplace(expr, rule):
"""
Duplicate of sympy's xreplace but with non-evaluate statement included
"""
if expr in rule:
return rule[expr]
elif rule:
args = []
altered = False
for a in expr.args:
try:
new_a = non_eval_xreplace(a, rule)
except AttributeError:
new_a = a
if new_a != a:
altered = True
args.append(new_a)
args = tuple(args)
if altered:
return expr.func(*args, evaluate=False)
return expr
我在想这个功能可以添加到xreplace
SymPy 库中的现有功能中,方法是让它接受**kwargs
传递给expr.func
调用的功能。这是您有兴趣做的事情,还是对于大多数用户来说这会变得不必要地复杂?(或者我误解了你上面的评论,有没有更简单的方法来做到这一点?)
其他答案无法处理-x**2
,因此我使用正则表达式仅解决 2 的幂。我知道这有点 hacky,但它对我有用。
from sympy.printing import ccode
import re
CPOW = re.compile(r'pow\((?P<var>[A-Za-z_]\w*)\s*,\s*2\s*\)')
def to_c_code(expr):
code = ccode(expr)
# sympy has a hard time unsimplifying x**2 to x*x
# replace all pow(var,2) with var*var
code = re.sub(CPOW, r'\g<var>*\g<var>', code)
return code