Non-sequential substitution in SymPy

后端 未结 5 884
醉梦人生
醉梦人生 2021-02-13 14:11

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

相关标签:
5条回答
  • 2021-02-13 14:41

    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)
    
    0 讨论(0)
  • 2021-02-13 14:46

    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
    
    0 讨论(0)
  • 2021-02-13 14:49

    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
    
    0 讨论(0)
  • 2021-02-13 14:51

    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.

    0 讨论(0)
  • 2021-02-13 15:04

    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
    
    0 讨论(0)
提交回复
热议问题