通过组合尽可能多的自由符号来优化症状表达评估



想象一下,我有一个未知的、非常复杂的表达式,我需要反复用数字计算,例如:

my_expr = (a*b*c**2 - 2*sqrt(d*(a*b-c-e+x)))/(b - 1)

每次重新评估表达式时,唯一更改的符号是"x",因此预计算所有其他符号是有意义的(我最终将使用c代码生成(。

因此,我想要的是提前自动提取并组合尽可能多的自由符号,除了x。这将有点像cse,但使最终表达式包含尽可能少的计算。

例如,对于上述情况,我可能最终会得到一个与此等效的系统:

var1 = a*b*c**2
var2 = a*b-c-e
var3 = b - 1
my_new_expr = (var1-2*sqrt(d*(var2+x)))/var3

这意味着我可以预先计算var1、var2&var3,并且重复计算(my_new_expr(在计算上尽可能简单。

有没有什么办法我可以在症状时这样做?我已经看了所有的简化方法等,包括收集等,但没有一个能完全满足我的需要。如果做不到这一点,我可以对表达式进行遍历吗?

尽管我在sympy/smichr的model分支有一个更全面的解决方案,但下面的表达式可以很好地压缩那些不变的子表达式:

def condense(eq, *x):
"""collapse additive/multiplicative constants into single
variables, returning condensed expression and replacement
values.
Examples
========
Simple constants are left unchanged
>>> condense(2*x + 2, x)
(2*x + 2, {})
More complex constants are replaced by a single variable
>>> first = condense(eq, x); first
(c6*(c5 - 2*sqrt(d*(c4 + x))), {c4: a*b - c - e, c6: 1/(b - 1), c5: a*b*c**2})
If a condensed expression is expanded, there may be more simplification possible:
>>> second = condense(first[0].expand(), x); second
(c0 + c2*sqrt(c1 + d*x), {c1: c4*d, c2: -2*c6, c0: c5*c6})
>>> full_reps = {k: v.xreplace(first[1]) for k, v in second[1].items()}; full_reps
{c1: d*(a*b - c - e), c2: -2/(b - 1), c0: a*b*c**2/(b - 1)}
More than 1 variable can be designated:
>>> condense(eq, c, e)
(c4*(c**2*c1 - 2*sqrt(d*(-c + c2 - e))), {c4: 1/(b - 1), c1: a*b, c2: a*b + x})
"""
reps = {}
con = numbered_symbols('c')
free = eq.free_symbols
def c():
while True:
rv = next(con)
if rv not in free:
return rv
def do(e):
i, d = e.as_independent(*x)
if not i.args: return e
return e.func(reps.get(i, reps.setdefault(i, c())), d)
rv = eq.replace(lambda x: x.is_Add or x.is_Mul, lambda x: do(x))
reps = {v: k for k, v in reps.items()}
keep = rv.free_symbols & set(reps)
reps = {k: reps[k].xreplace(reps) for k in keep}
return rv, reps

最新更新