2012-06-19 14 views
10

Ho scritto uno script Python per calcolare la distanza tra due punti nello spazio 3D tenendo conto delle condizioni al contorno periodiche. Il problema è che ho bisogno di fare questo calcolo per molti, molti punti e il calcolo è piuttosto lento. Ecco la mia funzione.Ottimizzazione del calcolo della distanza Python tenendo conto delle condizioni al contorno periodiche

def PBCdist(coord1,coord2,UC): 
    dx = coord1[0] - coord2[0] 
    if (abs(dx) > UC[0]*0.5): 
     dx = UC[0] - dx 
    dy = coord1[1] - coord2[1] 
    if (abs(dy) > UC[1]*0.5): 
     dy = UC[1] - dy 
    dz = coord1[2] - coord2[2] 
    if (abs(dz) > UC[2]*0.5): 
     dz = UC[2] - dz 
    dist = np.sqrt(dx**2 + dy**2 + dz**2) 
    return dist 

ho quindi chiamare la funzione in modo

for i, coord2 in enumerate(coordlist): 
    if (PBCdist(coord1,coord2,UC) < radius): 
     do something with i 

Di recente ho letto che posso aumentare notevolmente le prestazioni utilizzando la lista di comprensione. I seguenti lavori per il caso non PBC, ma non per il caso PBC

coord_indices = [i for i, y in enumerate([np.sqrt(np.sum((coord2-coord1)**2)) for coord2 in coordlist]) if y < radius] 
for i in coord_indices: 
    do something 

C'è qualche modo per fare l'equivalente di questo per il caso PBC? C'è un'alternativa che funzionerebbe meglio?

+3

Si utilizza NumPy, quindi è necessario vettorializzare il ciclo per migliorare le prestazioni. Qual è esattamente la struttura di 'coordlist'? Dovrebbe essere un array NumPy bidimensionale per essere in grado di ottimizzare il loop con gli ufunc NumPy. –

+0

coordlist è una matrice numpy con forma approssimativamente (5711,3). lo stesso coordlist proviene da una lista più ampia, quindi faccio un loop su coordlist 20.000 volte e quell'elenco di coordlist viene ripetuto circa 50 volte ... si ottiene l'immagine. – johnjax

+0

Ho cercato la funzione vectorize in NumPy. La documentazione dice: ["La funzione vectorize viene fornita principalmente per comodità, non per prestazioni. L'implementazione è essenzialmente un ciclo for."] (Http://docs.scipy.org/doc/numpy/reference/generated/numpy. vectorize.html) – johnjax

risposta

12

si dovrebbe scrivere il distance() in modo che sia possibile vettorizzare il loop sui 5711 punti. La seguente implementazione accetta una matrice di punti come sia la x0 o x1 parametro:

def distance(x0, x1, dimensions): 
    delta = numpy.abs(x0 - x1) 
    delta = numpy.where(delta > 0.5 * dimensions, delta - dimensions, delta) 
    return numpy.sqrt((delta ** 2).sum(axis=-1)) 

Esempio:

>>> dimensions = numpy.array([3.0, 4.0, 5.0]) 
>>> points = numpy.array([[2.7, 1.5, 4.3], [1.2, 0.3, 4.2]]) 
>>> distance(points, [1.5, 2.0, 2.5], dimensions) 
array([ 2.22036033, 2.42280829]) 

Il risultato è la matrice delle distanze tra i punti passato come secondo parametro distance() e ciascun punto in points.

+0

Ciò ha portato a circa 5 volte più veloce nel mio codice. Grazie! Per chi è alla ricerca di un'alternativa, anche la risposta di Lazyr si è rivelata altrettanto valida. – johnjax

+0

Non fa alcuna differenza dopo aver preso la norma, ma mi piace ottenere il segno giusto su delta sostituendo la terza riga con 'delta = numpy.where (", delta - dimensions, ")'. Si noti inoltre che 'np.hypot' è meglio evitare l'overflow di sqrt (sum (x ** 2)) – Patrick

+0

@Patrick' numpy.hypot() 'funziona solo per due dimensioni, mentre il codice OP necessario che funziona per tre- punti dimensionali. E riguardo al segno di "delta", beh, se ti interessa, sentiti libero di modificare il post. :) –

0

Dai un'occhiata a Ian Ozsvalds high performance python tutorial. Contiene molti suggerimenti su dove puoi andare dopo.

compreso:

  • vettorizzazione
  • Cython
  • PyPy
  • numexpr
  • pycuda
  • multiprocesing
  • pitone parallelo
4
import numpy as np 

bounds = np.array([10, 10, 10]) 
a = np.array([[0, 3, 9], [1, 1, 1]]) 
b = np.array([[2, 9, 1], [5, 6, 7]]) 

min_dists = np.min(np.dstack(((a - b) % bounds, (b - a) % bounds)), axis = 2) 
dists = np.sqrt(np.sum(min_dists ** 2, axis = 1)) 

Qui a e b sono liste di vettori che si desidera calcolare la distanza tra e bounds sono i confini dello spazio (ecco tutte le tre dimensioni vanno 0-10 e poi avvolgere). Calcola le distanze tra a[0] e b[0], a[1] e b[1] e così via.

Sono sicuro che gli esperti NumPy potuto fare di meglio, ma questo sarà probabilmente un ordine di grandezza più veloce di quello che stai facendo, poiché la maggior parte del lavoro è ora fatto in C.

+0

Ho provato anche questo approccio. Ha anche portato a circa 5 volte il miglioramento del codice. Purtroppo posso solo controllare 1 risposta come quella corretta:/ – johnjax

+0

@johnjax Per quello che vale, avrei accettato anche la risposta di Sven Marnach, se fossi stato nei tuoi panni. È più direttamente applicabile del mio. –

0

Ho trovato che meshgrid è molto utile per la generazione di distanze.Ad esempio:

import numpy as np 
row_diff, col_diff = np.meshgrid(range(7), range(8)) 
radius_squared = (row_diff - x_coord)**2 + (col_diff - y_coord)**2 

ora ho un array (radius_squared) dove ogni voce specifica il quadrato della distanza dalla posizione di matrice [x_coord, y_coord].

Per circularize matrice, posso effettuare le seguenti operazioni:

row_diff, col_diff = np.meshgrid(range(7), range(8)) 
row_diff = np.abs(row_diff - x_coord) 
row_circ_idx = np.where(row_diff > row_diff.shape[1]/2) 
row_diff[row_circ_idx] = (row_diff[row_circ_idx] - 
         2 * (row_circ_idx + x_coord) + 
         row_diff.shape[1]) 
row_diff = np.abs(row_diff) 
col_diff = np.abs(col_diff - y_coord) 
col_circ_idx = np.where(col_diff > col_diff.shape[0]/2) 
col_diff[row_circ_idx] = (row_diff[col_circ_idx] - 
         2 * (col_circ_idx + y_coord) + 
         col_diff.shape[0]) 
col_diff = np.abs(row_diff) 
circular_radius_squared = (row_diff - x_coord)**2 + (col_diff - y_coord)**2 

Ora ho tutte le distanze matrice circolarizzato con matematica vettoriale.

Problemi correlati