SymPy 中的非顺序替换

发布于 2024-09-06 12:36:13 字数 1105 浏览 12 评论 0原文

我正在尝试使用 [SymPy][1] 同时替换表达式中的多个术语。我尝试使用字典作为参数的 [subs 函数][2],但发现它是按顺序替换的。

In : a.subs({a:b, b:c})
Out: c

问题是第一次替换产生的术语可以被第二次替换所替换,但它不应该(出于我的原因)。

知道如何同时执行替换而不互相干扰吗?

编辑: 这是一个真实的例子,

In [1]: I_x, I_y, I_z = Symbol("I_x"), Symbol("I_y"), Symbol("I_z")

In [2]: S_x, S_y, S_z = Symbol("S_x"), Symbol("S_y"), Symbol("S_z")

In [3]: J_is = Symbol("J_IS")

In [4]: t = Symbol("t")

In [5]: substitutions = (
(2 * I_x * S_z, 2 * I_x * S_z * cos(2 * pi * J_is * t) + I_y * sin(2 * pi * J_is * t)),
(I_x,  I_x * cos(2 * pi * J_is * t) + 2 * I_x * S_z * sin(2 * pi * J_is * t)),
(I_y,  I_y * cos(2 * pi * J_is * t) - 2 * I_x * S_z * sin(2 * pi * J_is * t))
)

In [6]: (2 * I_x * S_z).subs(substitutions)
Out[7]: (I_y*cos(2*pi*J_IS*t) - 2*I_x*S_z*sin(2*pi*J_IS*t))*sin(2*pi*J_IS*t) + 2*S_z*(I_x*cos(2*pi*J_IS*t) + 2*I_x*S_z*sin(2*pi*J_IS*t))*cos(2*pi*J_IS*t)

只应进行适当的替换,在本例中仅进行第一个替换。所以预期的输出应该如下:

In [6]: (2 * I_x * S_z).subs(substitutions)
Out[7]: I_y*sin(2*pi*J_IS*t) + 2*I_x*S_z*cos(2*pi*J_IS*t)

I'm trying to use [SymPy][1] to substitute multiple terms in an expression at the same time. I tried the [subs function][2] with a dictionary as parameter, but found out that it substitutes sequentially.

In : a.subs({a:b, b:c})
Out: c

The problem is the first substitution resulted in a term that can be substituted by the second substitution, but it should not (for my cause).

Any idea on how to perform the substitutions simultaneously, without them interfering with each other?

Edit:
This is a real example

In [1]: I_x, I_y, I_z = Symbol("I_x"), Symbol("I_y"), Symbol("I_z")

In [2]: S_x, S_y, S_z = Symbol("S_x"), Symbol("S_y"), Symbol("S_z")

In [3]: J_is = Symbol("J_IS")

In [4]: t = Symbol("t")

In [5]: substitutions = (
(2 * I_x * S_z, 2 * I_x * S_z * cos(2 * pi * J_is * t) + I_y * sin(2 * pi * J_is * t)),
(I_x,  I_x * cos(2 * pi * J_is * t) + 2 * I_x * S_z * sin(2 * pi * J_is * t)),
(I_y,  I_y * cos(2 * pi * J_is * t) - 2 * I_x * S_z * sin(2 * pi * J_is * t))
)

In [6]: (2 * I_x * S_z).subs(substitutions)
Out[7]: (I_y*cos(2*pi*J_IS*t) - 2*I_x*S_z*sin(2*pi*J_IS*t))*sin(2*pi*J_IS*t) + 2*S_z*(I_x*cos(2*pi*J_IS*t) + 2*I_x*S_z*sin(2*pi*J_IS*t))*cos(2*pi*J_IS*t)

Only the appropriate substitution should happen, in this case only the first one. So the expected output should be the following:

In [6]: (2 * I_x * S_z).subs(substitutions)
Out[7]: I_y*sin(2*pi*J_IS*t) + 2*I_x*S_z*cos(2*pi*J_IS*t)

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

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

发布评论

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

评论(5

忆梦 2024-09-13 12:36:13

当前版本的 sympy 提供了关键字同时。前面答案中的复杂操作不再需要:

In [1]: (x*sin(y)).subs([(x,y),(y,x)],simultaneous=True)
Out[1]: y⋅sin(x)

The current version of sympy provides the keyword simultaneous. The complicated operations in the previous answers are no more necessary:

In [1]: (x*sin(y)).subs([(x,y),(y,x)],simultaneous=True)
Out[1]: y⋅sin(x)
极致的悲 2024-09-13 12:36:13

subs(self,*args) 方法(部分)是这样定义的:

In [11]: x.subs??
...
sequence = args[0]
if isinstance(sequence, dict):
    return self._subs_dict(sequence)
elif isinstance(sequence, (list, tuple)):
    return self._subs_list(sequence)

如果你传递 subs 一个字典,你就失去了对替换顺序的控制。
而如果您传递 subs 列表或元组,则可以控制顺序。

这不允许您同时进行替换。如果用户传递像 x.subs([(x,y),(y,x)]) 这样的东西,这会导致困难。所以我怀疑 sympy 是否有同时进行替换的方法。相反,我相信所有替换要么是无序的(如果您传递一个字典),要么最多通过 1 遍有序替换完成(如果您传递一个列表或元组):

In [17]: x.subs([(x,y),(y,z)])
Out[18]: z

In [19]: x.subs([(y,z),(x,y)])
Out[19]: y

PS。 _subs_list(self,equence) 的定义(部分)如下:

In [14]: x._subs_list??
...
    for old, new in sequence:
        result = result.subs(old, new)

这确定了 subs 完成的顺序。

The subs(self,*args) method is defined (in part) this way:

In [11]: x.subs??
...
sequence = args[0]
if isinstance(sequence, dict):
    return self._subs_dict(sequence)
elif isinstance(sequence, (list, tuple)):
    return self._subs_list(sequence)

If you pass subs a dict, you lose control over the order of the substitutions.
While if you pass subs a list or tuple, you can control the order.

This doesn't allow you to do simultaneous substitutions. That would lead to difficulties if the user were to pass stuff like x.subs([(x,y),(y,x)]). So I doubt sympy has a method for doing simultaneous substitutions. Instead I believe all substutions are either unordered (if you pass a dict) or, at best, done by a 1-pass ordered substitution (if you pass a list or tuple):

In [17]: x.subs([(x,y),(y,z)])
Out[18]: z

In [19]: x.subs([(y,z),(x,y)])
Out[19]: y

PS. _subs_list(self, sequence) is defined (in part) like this:

In [14]: x._subs_list??
...
    for old, new in sequence:
        result = result.subs(old, new)

This nails down the order in which the subs are done.

意犹 2024-09-13 12:36:13

@~unutbu 的回答示例:

>>> import ordereddict # collections.OrderedDict in Python 2.7+
>>> from sympy import *
>>> x,y,z = symbols('xyz')
>>> x.subs(ordereddict.OrderedDict([(x,y),(y,z)]))
y
>>> x.subs(ordereddict.OrderedDict([(y,z),(x,y)]))
z

Example for @~unutbu's answer:

>>> import ordereddict # collections.OrderedDict in Python 2.7+
>>> from sympy import *
>>> x,y,z = symbols('xyz')
>>> x.subs(ordereddict.OrderedDict([(x,y),(y,z)]))
y
>>> x.subs(ordereddict.OrderedDict([(y,z),(x,y)]))
z
她说她爱他 2024-09-13 12:36:13

回答编辑后的问题。

在您的示例中,您可以使用一些临时变量,这些变量不会被后续替换覆盖。然后,一旦完成所有可能重叠的替换,您就可以用真实变量替换临时变量。

这个示例适用于这个问题,如果您的完整问题包含更复杂的替换,我认为您仍然应该能够创建临时变量以避免重叠替换。

from sympy import Symbol, sin, cos, pi

I_x, I_y, I_z = Symbol("I_x"), Symbol("I_y"), Symbol("I_z")
S_x, S_y, S_z = Symbol("S_x"), Symbol("S_y"), Symbol("S_z")
J_is = Symbol("J_IS")
t = Symbol("t")
I_x_temp, I_y_temp, I_z_temp = Symbol("I_x_temp"), Symbol("I_y_temp"), Symbol("I_z_temp")

f = 2*I_x*S_z
answer = I_y*sin(2*pi*J_is*t) + 2*I_x*S_z*cos(2*pi*J_is*t)

subs1a = [
    (2*I_x*S_z, 2*I_x_temp*S_z*cos(2*pi*J_is*t) + I_y_temp*sin(2*pi*J_is*t)),
    (I_x,  I_x_temp*cos(2* pi*J_is*t) + 2*I_x_temp*S_z*sin(2*pi*J_is*t)),
    (I_y,  I_y_temp*cos(2*pi*J_is*t) - 2*I_x_temp*S_z* sin(2*pi*J_is*t))
]

subs_temp = [(I_x_temp, I_x), (I_y_temp, I_y), (I_z_temp, I_z)]

print f
f = f.subs(subs1a)
print f
f = f.subs(subs_temp)
print f
print f == answer # True

请注意,您还可以连续执行两次替换:

f.subs(subs1a).subs(subs_temp) == answer

Answering the edited question.

In your example you can use some temporary variables which will not be over-written be subsequent substitutions. Then, once all of the potentially overlapping substitutions have been made, you can replace the temporary variables with the real ones.

This example works for the question, if your full problem contains more complex substitutions, I think you should still be able to create temporary variables to avoid overlapping substitutions.

from sympy import Symbol, sin, cos, pi

I_x, I_y, I_z = Symbol("I_x"), Symbol("I_y"), Symbol("I_z")
S_x, S_y, S_z = Symbol("S_x"), Symbol("S_y"), Symbol("S_z")
J_is = Symbol("J_IS")
t = Symbol("t")
I_x_temp, I_y_temp, I_z_temp = Symbol("I_x_temp"), Symbol("I_y_temp"), Symbol("I_z_temp")

f = 2*I_x*S_z
answer = I_y*sin(2*pi*J_is*t) + 2*I_x*S_z*cos(2*pi*J_is*t)

subs1a = [
    (2*I_x*S_z, 2*I_x_temp*S_z*cos(2*pi*J_is*t) + I_y_temp*sin(2*pi*J_is*t)),
    (I_x,  I_x_temp*cos(2* pi*J_is*t) + 2*I_x_temp*S_z*sin(2*pi*J_is*t)),
    (I_y,  I_y_temp*cos(2*pi*J_is*t) - 2*I_x_temp*S_z* sin(2*pi*J_is*t))
]

subs_temp = [(I_x_temp, I_x), (I_y_temp, I_y), (I_z_temp, I_z)]

print f
f = f.subs(subs1a)
print f
f = f.subs(subs_temp)
print f
print f == answer # True

Note, you can also perform two substitutions back to back:

f.subs(subs1a).subs(subs_temp) == answer
晨光如昨 2024-09-13 12:36:13

无论输入(字典或序列)如何,关键字 simultaneous 都会执行非冲突的替换:

>>> x.subs([(x,y),(y,z)],simultaneous=1)
y
>>> x.subs([(y,z),(x,y)],simultaneous=1)
y

The Keyword simultaneous will do non-clashing subs regardless of the input (dict or sequence):

>>> x.subs([(x,y),(y,z)],simultaneous=1)
y
>>> x.subs([(y,z),(x,y)],simultaneous=1)
y
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文