我有一个需要替换的多个变量的表达式。问题是要替换的一些表达式还包含需要替换的变量实例。
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次应用于expr
或replace
来强制替换,但这似乎在计算上浪费:
result = expr
for _ in replace:
# Applying n times
result = result.subs(replace)
我希望recursive
选择subs
,但这似乎不存在。有更好的选择吗?
答案 0 :(得分:6)
如果按照正确的顺序进行,则替换将以迭代方式执行(除非您使用(old1, new1)
,它会立即执行所有替换)。
您的问题是正确订购替换件。你想要的是替换的topological sort。即,每个替换都是图表中的节点,如果(old2, new2)
包含new1
,则存在从old2
到topological_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;我想如果你正在处理大表达式或许多替换,那么不必要的调用(在实际改变表达式的那些之上)可能非常耗时。