2014-09-12 14 views
5

Sto usando sympy per generare alcune funzioni per i calcoli numerici. Pertanto io lambdifico un'espressione e la vettorializzo per usarla con gli array numpy. Ecco un esempio:Speedup sympy-lamdified e funzione vettoriale

import numpy as np 
import sympy as sp 

def numpy_function(): 
    x, y, z = np.mgrid[0:1:40*1j, 0:1:40*1j, 0:1:40*1j] 
    T = (1 - np.cos(2*np.pi*x))*(1 - np.cos(2*np.pi*y))*np.sin(np.pi*z)*0.1 
    return T 

def sympy_function(): 
    x, y, z = sp.Symbol("x"), sp.Symbol("y"), sp.Symbol("z") 
    T = (1 - sp.cos(2*sp.pi*x))*(1 - sp.cos(2*sp.pi*y))*sp.sin(sp.pi*z)*0.1 
    lambda_function = np.vectorize(sp.lambdify((x, y, z), T, "numpy")) 
    x, y, z = np.mgrid[0:1:40*1j, 0:1:40*1j, 0:1:40*1j] 
    T = lambda_function(x,y,z) 
    return T 

Il problema tra la versione sympy e una versione pura numpy è la velocità cioè

In [3]: timeit test.numpy_function() 
100 loops, best of 3: 11.9 ms per loop 

vs.

In [4]: timeit test.sympy_function() 
1 loops, best of 3: 634 ms per loop 

Quindi non v'è alcun modo per ottenere più vicino alla velocità della versione numpy? Penso che np.vectorize sia piuttosto lento ma in qualche modo parte del mio codice non funziona senza di essa. Grazie per eventuali suggerimenti.

EDIT: Così ho trovato il motivo per cui la funzione vectorize è necessario, vale a dire:

In [35]: y = np.arange(10) 

In [36]: f = sp.lambdify(x,sin(x),"numpy") 

In [37]: f(y) 
Out[37]: 
array([ 0.  , 0.84147098, 0.90929743, 0.14112001, -0.7568025 , 
     -0.95892427, -0.2794155 , 0.6569866 , 0.98935825, 0.41211849]) 

questo sembra funzionare bene comunque:

In [38]: y = np.arange(10) 

In [39]: f = sp.lambdify(x,1,"numpy") 

In [40]: f(y) 
Out[40]: 1 

Così, per semplice espressione come 1 questa funzione non restituisce un array. C'è un modo per risolvere questo problema e non si tratta di una specie di bug o almeno di un design inconsistente?

risposta

3

lambdify restituisce un singolo valore per costanti perché nessuna funzione NumPy sono coinvolti. Questo è dovuto al modo in cui funziona lambdify (vedere https://stackoverflow.com/a/25514007/161801).

Tuttavia, in genere questo non rappresenta un problema poiché una costante verrà automaticamente trasmessa alla forma corretta in qualsiasi operazione utilizzata in un array. D'altra parte, se si lavorasse esplicitamente con un array della stessa costante, sarebbe molto meno efficiente perché si calcolerebbero le stesse operazioni più volte.

+0

Grazie per la risposta. Il mio problema è che ho qualcosa come self.T = T (x, y, z) quindi in questo caso la trasmissione non funziona. Quale sarebbe il modo migliore per farlo ? – jrsm

+0

Sembra che 'numpy.array' sia un no-op quando viene chiamato con un array, quindi puoi usare' np.array (T (x, y, z)) 'per assicurarti che il risultato sia sempre un array. – asmeurer

+0

Anche se non riesco ancora a capire perché il caso d'uso non funzioni con la trasmissione. – asmeurer

3

Utilizzando np.vectorize() in questo caso è come loop sopra la prima dimensione di x, y e z, ed è per questo diventa più lento. Non è necessario np.vectorize()se devi dire a lambdify() di utilizzare le funzioni di NumPy, che è esattamente quello che stai facendo. Quindi, utilizzando:

def sympy_function(): 
    x, y, z = sp.Symbol("x"), sp.Symbol("y"), sp.Symbol("z") 
    T = (1 - sp.cos(2*sp.pi*x))*(1 - sp.cos(2*sp.pi*y))*sp.sin(sp.pi*z)*0.1 
    lambda_function = sp.lambdify((x, y, z), T, "numpy") 
    x, y, z = np.mgrid[0:1:40*1j, 0:1:40*1j, 0:1:40*1j] 
    T = lambda_function(x,y,z) 
    return T 

rende le prestazioni paragonabili:

In [26]: np.allclose(numpy_function(), sympy_function()) 
Out[26]: True 

In [27]: timeit numpy_function() 
100 loops, best of 3: 4.08 ms per loop 

In [28]: timeit sympy_function() 
100 loops, best of 3: 5.52 ms per loop 
+0

Hai ragione ma questo non funziona per una soluzione costante. – jrsm

+0

@jrsm hai ragione ... Ho provato anche 'f = lambdify (x, (cte * np.ones_like (x)))' e 'lambdify' restituisce sempre il valore' cte' ... questo è probabilmente un bug , Aprirò un problema nella loro lista dei problemi ... spero che la mia risposta ti abbia aiutato comunque ... –