2010-06-20 6 views
13

J'essaie d'utiliser [SymPy] [1] pour substituer plusieurs termes dans une expression en même temps. J'ai essayé la [subs function] [2] avec un dictionnaire comme paramètre, mais j'ai découvert qu'il se substitue séquentiellement.Substitution non-séquentielle dans SymPy

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

Le problème est la première substitution a donné lieu à un terme qui peut être substitué par un deuxième changement, mais il ne faut pas (pour ma cause).

Une idée sur la façon d'effectuer les substitutions simultanément, sans interférer les uns avec les autres?

Edit: Ceci est un exemple réel

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) 

Seule la substitution appropriée devrait se produire, dans ce cas, seul le premier. Ainsi, le résultat attendu devrait être le suivant:

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) 
+0

Pouvez-vous donner un exemple réel de vos substitutions afin que je puisse voir quel est le problème. Le problème que vous avez ci-dessus peut être résolu par la réponse de ~ unutbu. –

+0

@PreludeAndFugue J'ai ajouté de vrais exemples, mes anciens exemples étaient trop simplifiés et pas vraiment utiles. –

Répondre

13

La version actuelle de sympy fournit le mot-clé simultanée.Les opérations compliquées dans les réponses précédentes ne sont plus nécessaires:

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

La méthode subs(self,*args) est définie (en partie) de cette façon:

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) 

Si vous passez subs un dict, vous perdez le contrôle de l'ordre du substitutions. Si vous transmettez une liste ou un tuple, vous pouvez contrôler la commande.

Cela ne vous permet pas d'effectuer des substitutions simultanées. Cela conduirait à des difficultés si l'utilisateur devait passer des choses comme x.subs([(x,y),(y,x)]). Je doute donc que Sympy ait une méthode pour faire des substitutions simultanées. Au lieu de cela, je crois que tous sont soit non ordonnée substitutions par (si vous passez un dict) ou, au mieux, fait par un 1-pass substitution ordonnée (si vous passez une liste ou 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) est défini (en partie) comme ceci:

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

Ce cloue l'ordre dans lequel les sous-marins sont effectués.

+0

J'ai ajouté un deuxième exemple, l'ordre des substitutions semble varier selon un principe que je ne comprends pas. –

+0

Vous pouvez le reproduire en utilisant OrderedDict http://stackoverflow.com/questions/3080450/non-sequential-substitution-in-sympy/3080803#3080803 – jfs

1

Exemple @~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

Merci, donc c'est quelque peu imprévisible avec les dicts normaux car ils ne sont pas triés. Malheureusement dans mon cas l'ordre n'a pas d'importance, les substitutions se chevauchent indépendamment de l'ordre. –

1

Répondant à la question éditée.

Dans votre exemple, vous pouvez utiliser des variables temporaires qui ne seront pas écrasées lors de substitutions ultérieures. Ensuite, une fois toutes les substitutions qui se chevauchent, vous pouvez remplacer les variables temporaires par les vraies.

Cet exemple fonctionne pour la question, si votre problème complet contient des substitutions plus complexes, je pense que vous devriez toujours être capable de créer des variables temporaires pour éviter les substitutions qui se chevauchent.

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 

Remarque, vous pouvez également effectuer deux substitutions dos à dos:

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

Cle simultaneous fera sous-marins non-discordants quelle que soit l'entrée (dict ou d'une séquence):

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