通过组合尽可能多的免费符号来优化 sympy 表达式评估

发布于 2025-01-10 22:58:30 字数 564 浏览 0 评论 0原文

想象一下,我有一个未知的、非常复杂的表达式,我需要重复地进行数值计算,例如:

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)在计算上尽可能简单。

无论如何,我可以以同情的方式做到这一点吗?我已经查看了所有简化方法等,包括收集等,但没有一个完全满足我的需要。如果做不到这一点,是否可以遍历表达式来实现此目的?

Imagine I have an unknown, quite complex expression that I need to repeatedly evaluate numerically, e.g.:

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

Each time I reevaluate the expression, the only symbol that changes is 'x', so it makes sense for me to precompute all the others (I will be using c code generation eventually).

So what I want is to automatically pull out and combine as many free symbols as possible in advance, except for x. This would work a bit like cse, but making the final expression contain as few calculations as possible.

e.g. for the above I might end up with a system equivalent to this:

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

This means I can precalculate var1,var2 & var3, and the repeated calculation (my_new_expr) is as simple as possible computationally.

Is there anyway I can do this in sympy? I've looked through all the simplification methods etc, including collect etc, and none quite do what I need. Failing that, is there any traversal of the expression I could do to achieve this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

岁月无声 2025-01-17 22:58:30

虽然我在 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

Although my model branch at sympy/smichr has a more comprehensive solution, the following will do pretty well at condensing those sub-expressions that are constant:

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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文