同情中的递归替换

时间:2016-03-24 09:52:28

标签: python sympy

我有一个需要替换的多个变量的表达式。问题是要替换的一些表达式还包含需要替换的变量实例。

from sympy import *
from sympy.abs import a,b, x,y

expr = a + b
replace = [[a, x+y], [b, 2*a]]

expr.subs(replace) # 2*a + x + y, I want 3*x + 3*y

如果替换列表的顺序正确,它将按顺序应用每个替换,但在我的实际应用程序中,我不知道哪个顺序是合适的:

expr.subs(reversed(replace)) # 3*x + 3*y

我可以通过将替换n次应用于exprreplace来强制替换,但这似乎在计算上浪费:

result = expr
for _ in replace:
    # Applying n times
    result = result.subs(replace)

我希望recursive选择subs,但这似乎不存在。有更好的选择吗?

3 个答案:

答案 0 :(得分:6)

如果按照正确的顺序进行,则替换将以迭代方式执行(除非您使用(old1, new1),它会立即执行所有替换)。

您的问题是正确订购替换件。你想要的是替换的topological sort。即,每个替换都是图表中的节点,如果(old2, new2)包含new1,则存在从old2topological_sort的边缘(即,应该首先替换它)。

SymPy在sympy.utilities.iterables中实施replace = [(y, z + 1), (x, y + z), (z, a)] 。它采用顶点列表和边缘列表(顶点元组)。说你有

from itertools import combinations
edges = [(i, j) for i, j in permutations(replace, 2) if i[1].has(j[0])]

我们可以使用

创建边缘列表
>>> from sympy import default_sort_key, topological_sort
>>> topological_sort([replace, edges], default_sort_key)
[(x, y + z), (y, z + 1), (z, a)]

对此进行排序

topological_sort

<的第三个参数是用来打破关系的关键。由于SymPy对象没有定义隐式排序(>TypeError一般提升default_sort_key),因此有一个名为topological_sort的排序键实现提供了SymPy对象的规范和一致(但任意)排序。

在404所示的情况下会出现无限循环,>>> replace = [(x, y+1), (y, x+1)] >>> edges = [(i, j) for i, j in permutations(replace, 2) if i[1].has(j[0])] >>> topological_sort([replace, edges], default_sort_key) Traceback (most recent call last): File "<ipython-input-51-72f3bfcfd4ad>", line 1, in <module> topological_sort([replace, edges], default_sort_key) File "/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/utilities/iterables.py", line 882, in topological_sort raise ValueError("cycle detected") ValueError: cycle detected 会提醒您有一个循环

subs

老实说,这应该通过关键字参数直接在 Public FlagToClose As Boolean 中实现。请参阅https://github.com/sympy/sympy/issues/6257

答案 1 :(得分:2)

如果存在递归选项,则可能会执行替换直到表达式停止更改。这是你自己可以做的事情;我不认为这是浪费,毕竟同情也是用Python编写的。

这是一个函数,它返回替换结果及其成功指标:替换后表达式是否达到稳定形式。对于导致无限循环的替换规则,这将是错误的,例如myDouble

replace = [[x, y+1], [y, x+1]]

现在def recursive_sub(expr, replace): for _ in range(0, len(replace) + 1): new_expr = expr.subs(replace) if new_expr == expr: return new_expr, True else: expr = new_expr return new_expr, False res, _ = recursive_sub(expr, replace)3*x + 3*y一起使用时会返回expr

答案 2 :(得分:0)

我遇到了同样的问题,目前看来SymPy中仍然没有针对此问题的简单通用解决方案。

也许我的快速简便的解决方法有任何帮助:

代码前言

import sympy
x, y = sympy.symbols("x, y")
reps = [(y, x**2), (x, 2)]

显示替换顺序重要的示例

直接从http://docs.sympy.org/dev/modules/core.html#sympy.core.basic.Basic.subs

的官方SymPy文档中删除
>>> (x + y).subs(reps)
6
>>> (x + y).subs(reversed(reps))
x**2 + 2

我的解决方法,适用于替换的任何顺序:

只需多次替换你的变量。

>>> (x + y).subs(100 * reps)
6
>>> (x + y).subs(reversed(100 * reps))
6

显然,这仅适用于固定的#34;递归深度&#34;我想如果你正在处理大表达式或许多替换,那么不必要的调用(在实际改变表达式的那些之上)可能非常耗时。