2012-06-19 27 views
10

定期的な境界条件を考慮しながら、3D空間内の2点間の距離を計算するPythonスクリプトを作成しました。問題は、多くの点でこの計算を行う必要があり、計算が非常に遅いことです。ここに私の機能があります。定期的な境界条件を考慮しながらPython距離計算を最適化する

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 

ので

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

最近、私は非常にリストの内包表記を使用してパフォーマンスを向上させることができることを読んで、私は、関数を呼び出します。非PBCケースについて、次の作品ではなく、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 

PBC場合は、この相当を行うにはいくつかの方法がありますか?より良い選択肢がありますか?

+3

NumPyを使用しているため、パフォーマンスを向上させるためにループをベクトル化する必要があります。 'coordlist'の構造はどういうものですか? NumPy ufuncsでループを最適化できるようにするには、2次元のNumPy配列でなければなりません。 –

+0

coordlistは、形状が約(5711,3)のnumpy配列です。 coordlist自体は大きなリストから来ているので、私は実際にcoordlistを20,000回ループし、coordlistのリストは約50回にわたってループします。あなたは画像を取得します。 – johnjax

+0

私はNumPyでベクトル化関数を調べました。ドキュメンテーションには次のように書かれています:["vectorize関数は、主に便宜のために提供されているものであり、パフォーマンスのためではありません。 vectorize.html) – johnjax

答えて

12

に行われるので、これはおそらく、あなたはあなたのを書く必要があり、より速くあなたがやっているものよりも大きさの順になりますdistance()は、5711ポイントでループをベクトル化できるように機能します。以下の実装は、x0又はx1パラメータのいずれかのような点の配列を受け入れ:

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

例:

>>> 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]) 

結果はdistance()及び各第2のパラメータとして渡された点間の距離のアレイでありますpointsを指す。

+0

これは私のコードで約5倍のスピードアップにつながった。ありがとう!代わりのものを探している人には、lazyrの答えも同様に実行されました。 – johnjax

+0

これはノルムを取った後でも違いはありませんが、3行目を 'delta = numpy.where("、delta - dimensions、 ")'に置き換えることでデルタ上で正しい記号を得たいのです。また、 'np.hypot'はsqrt(sum(x ** 2))よりもオーバーフローを避ける方が良いことに注意してください。 – Patrick

+0

@Patrick' numpy.hypot() 'は2次元でのみ機能しますが、三次元の点。デルタのサインについては、あなたが気にしているのであれば、投稿を編集してください。 :) –

0

Ian Ozsvalds high performance python tutorialをご覧ください。それはあなたが次に行くことができるところにたくさんの提案を含んでいます。

含む:

  • ベクトルmultiprocesing
  • cython
  • pypy
  • numexpr
  • pycuda
  • 平行パイソン
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)) 
ここ

abは、あなたが間とbounds距離を計算したいのベクトルのリストである(ので、ここですべての3つの次元が0から10まで行き、その後ラップ)空間の境界です。 a[0]b[0]a[1]b[1]の間の距離を計算します。

私はnumpyの専門家が、やれること確信しているが、仕事のほとんどは今C.

+0

私はこのアプローチも試みました。また、コードの約5倍の向上をもたらしました。残念ながら私は正しい答えとして1つしか答えられません:/ – johnjax

+0

@johnjaxそれが価値があることについては、私はあなたの靴の中にいたとしても、Sven Marnachの答えを受け入れるでしょう。それは私よりも直接的に適用可能です。 –

0

私はmeshgridが距離を生成するのに非常に有用であることを発見しました。例えば:

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 

私は今、すべてのエントリは、アレイ位置[x_coord, y_coord]からの距離の2乗を指定する配列(radius_squared)を有します。配列を環状化する

、私は、次の操作を行うことができます。

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 

私は今、ベクトル数学と環状アレイのすべての距離を持っています。

関連する問題