2010-06-20 18 views
13

Sto cercando di utilizzare [SymPy] [1] per sostituire termini multipli in un'espressione allo stesso tempo. Ho provato la [funzione subs] [2] con un dizionario come parametro, ma ho scoperto che sostituisce sequenzialmente.sostituzione non sequenziale in SymPy

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

Il problema è il primo cambio determinato un termine che può essere sostituito con il secondo cambio, ma non dovrebbe (per mia causa).

Qualche idea su come eseguire le sostituzioni contemporaneamente, senza interferire con l'altro?

Edit: Questo è un esempio reale

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) 

Solo la sostituzione appropriata dovrebbe accadere, in questo caso solo il primo. Così i risultati attesi dovrebbe essere la seguente:

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

Può fare un esempio reale delle tue sostituzioni così posso vedere qual è il problema. Il problema che hai sopra può essere risolto dalla risposta di ~ unutbu. –

+0

@PreludeAndFugue Ho aggiunto un esempio reale, i miei vecchi esempi erano troppo semplificati e non molto utili. –

risposta

13

La versione corrente del sympy fornisce la parola chiave contemporanea.Le operazioni complicate le risposte precedenti non sono più necessari:

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

Il metodo subs(self,*args) è definito (in parte) in questo modo:

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) 

Se si passa subs un dict, si perde il controllo sopra l'ordine del sostituzioni. Mentre se si passa subs una lista o tupla, è possibile controllare l'ordine.

Questo non consente di fare le sostituzioni simultanee. Ciò comporterebbe difficoltà se l'utente dovesse passare cose come x.subs([(x,y),(y,x)]). Quindi dubito che sympy abbia un metodo per fare sostituzioni simultanee. Invece credo che tutti substutions sono o non ordinata (se si passa un dict) o, nella migliore delle ipotesi, fatto da una sostituzione ordinata 1-pass (se si passa una lista o tupla):

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) è definito (in parte) in questo modo:

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

scorrimento l'ordine in cui vengono effettuati i sub This unghie.

+0

Ho aggiunto un secondo esempio, l'ordine delle sostituzioni sembra variare in base ad un principio che non capisco. –

+0

È possibile riprodurlo utilizzando OrderedDict http://stackoverflow.com/questions/3080450/non-sequential-substitution-in-sympy/3080803#3080803 – jfs

1

Esempio per @~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

Grazie, quindi è un po 'imprevedibile con i dit normali perché sono non ordinati. Purtroppo nel mio caso l'ordine non ha importanza, le sostituzioni si calpestano a vicenda indipendentemente dall'ordine. –

1

Rispondendo alla domanda modificato.

Nel tuo esempio è possibile utilizzare alcune variabili temporanee che non sarà superato scritto sia sostituzioni successive. Quindi, una volta che tutte le sostituzioni potenzialmente sovrapposte sono state apportate, è possibile sostituire le variabili temporanee con quelle reali.

Questo esempio funziona per la domanda, se il tuo problema completa contiene sostituzioni più complesse, penso che si dovrebbe comunque essere in grado di creare variabili temporanee per evitare sovrapposizioni sostituzioni.

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 

nota, è inoltre possibile eseguire due sostituzioni back to back:

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

Chiave simultaneous farà subs non contrastanti indipendentemente dall'ingresso (dict o sequenza):

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