2014-11-11 3 views
7

Sto usando sympy per generare espressioni diverse per simulazioni cfd. Principalmente queste espressioni sono del tipo exp = f (x, y, z) ad esempio f (x, y, z) = sin (x) * cos (y) * sin (z). Per ottenere valori su una griglia, utilizzo simpy.lambdify. Per esempio:Come ottenere una funzione lambda veloce da un'espressione sympy in 3 dimensioni?

import numpy as np 
import sympy as sp 
from sympy.abc import x,y,z 

xg, yg, zg = np.mgrid[0:1:50*1j, 0:1:50*1j, 0:1:50*1j] 
f = sp.sin(x)*sp.cos(y)*sp.sin(z) 

lambda_f = sp.lambdify([x,y,z], f, "numpy") 
fn = lambda_f(xg, yg, zg) 

print fn 

Questo sembra funzionare abbastanza buono, ma purtroppo le mie espressioni stanno diventando sempre più complessi e il calcolo di griglia prende un sacco di tempo. La mia idea era che forse è possibile usare il metodo uFuncify (vedi http://docs.sympy.org/latest/modules/numeric-computation.html) per accelerare i calcoli, non sono sicuro che sia il modo giusto? E non sono nemmeno sicuro di come far funzionare ufunctify per le griglie 3D? Grazie per eventuali suggerimenti

+0

La documentazione 'ufuncify' afferma esplicitamente:' La funzione restituita può agire solo su un array alla volta, poiché solo il primo argomento accetta matrici come input. Sarai comunque costretto a chiudere la chiamata all'argomento 'ufuncify' in un ciclo double for. Questo * potrebbe * velocizzare alcune cose, ma dipende interamente dalle tue equazioni. –

+0

Sì, hai ragione, speravo che ci potesse essere qualche trucco per farlo. Ad ogni modo, immagino che un ciclo double for sarà anche lento, ma ci proverò. C'è forse qualche alternativa a ufuncify? – jrsm

risposta

6

Nelle versioni precedenti (0.7.5 e precedenti), ufuncify funzionava solo su matrici a dimensione singola per il primo argomento (non molto eccitante). A partire da 0.7.6 (non ancora rilasciato, ma dovrebbe essere in una settimana!) ufuncify crea istanze istanze di numpy.ufunc per impostazione predefinita (include codice C in numpy api). Il tuo codice sopra richiede solo una piccola modifica per farlo funzionare.

In [1]: import numpy as np 

In [2]: from sympy import sin, cos, lambdify 

In [3]: from sympy.abc import x,y,z 

In [4]: from sympy.utilities.autowrap import ufuncify 

In [5]: from sympy.printing.theanocode import theano_function 

In [6]: xg, yg, zg = np.mgrid[0:1:50*1j, 0:1:50*1j, 0:1:50*1j] 

In [7]: f = sym.sin(x)*sym.cos(y)*sym.sin(z) 

In [8]: ufunc_f = ufuncify([x,y,z], f) 

In [9]: theano_f = theano_function([x, y, z], f, dims={x: 3, y: 3, z: 3}) 

In [10]: lambda_f = lambdify([x, y, z], f) 

In [11]: type(ufunc_f) 
Out[11]: numpy.ufunc 

In [12]: type(theano_f) 
Out[12]: theano.compile.function_module.Function 

In [13]: type(lambda_f) 
Out[13]: function 

In [14]: %timeit ufunc_f(xg, yg, zg) 
10 loops, best of 3: 21 ms per loop 

In [15]: %timeit theano_f(xg, yg, zg) 
10 loops, best of 3: 20.7 ms per loop 

In [16]: %timeit lambda_f(xg, yg, zg) 
10 loops, best of 3: 22.3 ms per loop 

ufuncify e theano_function sono comparabili, e leggermente superiore lambdify per questa semplice espressione. La differenza è maggiore utilizzando l'espressione più complicato indicato di seguito:

In [17]: f = sin(x)*cos(y)*sin(z) + sin(4*(x - y**2*sin(z))) 

In [18]: ufunc_f = ufuncify([x,y,z], f) 

In [19]: theano_f = theano_function([x, y, z], f, dims={x: 3, y: 3, z: 3}) 

In [20]: lambda_f = lambdify([x, y, z], f) 

In [21]: %timeit ufunc_f(xg, yg, zg) 
10 loops, best of 3: 29.2 ms per loop 

In [22]: %timeit theano_f(xg, yg, zg) 
10 loops, best of 3: 29.2 ms per loop 

In [23]: %timeit lambda_f(xg, yg, zg) 
10 loops, best of 3: 42.1 ms per loop 

Questo è molto veloce rispetto all'utilizzo della versione pitone, come vengono creati array intermedi, il ciclo viene attraversato ed il calcolo corse in C. Theano produce velocità equivalenti, dato che vengono compilate anche in codice nativo. Per le espressioni di grandi dimensioni che vedo quando si esegue multibody dynamics, ufuncify (e il relativo autowrap) eseguire in modo significativo più veloce di lambdify. Non ho molta esperienza con theano, quindi non posso dire quanto bene sia il loro approccio in scala, ma suppongo che sarebbe simile.

Come ho detto sopra, questo è disponibile solo in sympy 0.7.6 e versioni successive. Dovrebbe essere rilasciato presto, ma fino ad allora è possibile prendere la fonte da github.Documenti su ufuncify nuovo comportamento here

+0

Se sei interessato, ho parlato un po 'della roba di generazione del codice in sympy qualche tempo fa. Le diapositive sono [qui] (https://speakerdeck.com/jcrist/generating-fast-and-correct-code-with-sympy), con un esempio di taccuino ipython [qui] (https://github.com/jcrist/ codegen_talk). Offre una buona panoramica di tutte le funzionalità interessanti disponibili in sympy 0.7.6. –

+0

Questo suona davvero fantastico, grazie! Credo che otterrò l'attuale dev-branch da github e verificarlo. Grazie anche per le diapositive, le darò un'occhiata. Saluti! – jrsm

+0

@jammycrisp, potresti aggiungere un benchmark di velocità confrontando la versione 'ufuncify' con' theano_function'? –

2

Forse potresti lavorare con sympy's theano_function. Secondo the link to the documentation you provided, ha velocità simile a ufuncify e può essere utilizzato con un mgrid:

import numpy as np 
import sympy as sp 
from sympy.printing.theanocode import theano_function 

x,y,z = sp.symbols('x y z') 
xg, yg, zg = np.mgrid[0:1:50*1j, 0:1:50*1j, 0:1:50*1j] 
f = sp.sin(x)*sp.cos(y)*sp.sin(z) 

ft = theano_function([x,y,z], [f], dims={x: 3, y: 3, z: 3}) 
ft(xg,yg,zg) # result is similar to your fn 

Per questa particolare funzione f, tuttavia, la velocità di esecuzione sul mio sistema della versione lambdified e la versione Theano-ized è simile:

In [24]: %timeit fn = lambda_f(xg, yg, zg) 
10 loops, best of 3: 53.2 ms per loop 

In [25]: %timeit fn = ft(xg,yg,zg) 
10 loops, best of 3: 52.7 ms per loop 

Rendere la funzione leggermente più difficile,

In [27]: f = sp.sin(x)*sp.cos(y)*sp.sin(z) + sp.sin(4*(x-y**2*sp.sin(z))) 

In [30]: %timeit fl(xg,yg,zg) # lambdified version 
10 loops, best of 3: 89.4 ms per loop 

In [31]: %timeit ft(xg,yg,zg) # Theano version 
10 loops, best of 3: 67.6 ms per loop 

rende le differenze di temporizzazione leggermente più grandi per me (e in favore di theano), ma forse sulle tue funzioni sperimenteresti differenze temporali molto più grandi?

+0

Grazie. Potrebbe essere promettente. .. Non sono sicuro che Theano supporti la doppia precisione? Proverò per un'espressione più ampia per vedere qual è la differenza di tempo. – jrsm

+0

Theano supporta la doppia precisione. La limitazione di precisione singola è solo se si desidera compilare la GPU. – MRocklin

Problemi correlati