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 i
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)
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
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
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.
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