2012-06-19 25 views
10

Tôi đã viết một kịch bản Python để tính toán khoảng cách giữa hai điểm trong không gian 3D trong khi tính toán các điều kiện biên định kỳ. Vấn đề là tôi cần thực hiện phép tính này cho nhiều, nhiều điểm và tính toán khá chậm. Đây là chức năng của tôi.Tối ưu hóa tính toán khoảng cách Python trong khi tính toán các điều kiện biên định kỳ

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 

sau đó tôi gọi hàm như vậy

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

Gần đây tôi đọc mà tôi có thể làm tăng đáng kể hiệu suất bằng cách sử dụng danh sách hiểu. Các công trình sau đây không áp dụng cho trường hợp không thuộc PBC, nhưng không áp dụng cho trường hợp 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ó cách nào để thực hiện tương đương với trường hợp PBC không? Có một giải pháp thay thế nào có hiệu quả hơn không?

+3

Bạn đang sử dụng NumPy, vì vậy bạn nên vectơ vòng lặp để cải thiện hiệu suất. Chính xác cấu trúc của 'coordlist' là gì? Nó phải là mảng NumPy hai chiều để có thể tối ưu hóa vòng lặp với NumPy ufuncs. –

+0

danh sách điều phối là một mảng numpy với hình dạng xấp xỉ (5711,3). bản thân danh sách điều phối đến từ một danh sách lớn hơn, vì vậy tôi có hiệu quả lặp lại trên danh sách điều phối 20.000 lần và danh sách các điều phối viên được lặp lại khoảng 50 lần ... bạn sẽ có được hình ảnh. – johnjax

+0

Tôi đã tra cứu hàm vectơ trong NumPy. Tài liệu nói: ["Chức năng vectơ được cung cấp chủ yếu để thuận tiện, không phải cho hiệu năng. Việc thực hiện về cơ bản là một vòng lặp for."] (Http://docs.scipy.org/doc/numpy/reference/generated/numpy. vectorize.html) – johnjax

Trả lời

12

Bạn nên viết của bạn distance() chức năng theo cách bạn có thể vectơ vòng lặp trên 5711 điểm. Việc thực hiện sau đây chấp nhận một loạt các điểm như một trong hai x0 hoặc x1 tham số:

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

Ví dụ:

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

Kết quả là hàng loạt các khoảng cách giữa các điểm thông qua như là tham số thứ hai để distance() và mỗi điểm trong points.

+0

Điều này dẫn đến tốc độ gấp 5 lần trong mã của tôi. Cảm ơn! Đối với những người tìm kiếm một giải pháp thay thế, câu trả lời của lazyr cũng được thực hiện tốt. – johnjax

+0

Nó không có sự khác biệt sau khi lấy tiêu chuẩn, nhưng tôi muốn có được dấu hiệu đúng trên delta bằng cách thay thế dòng thứ ba bằng 'delta = numpy.where (", delta - dimension, ")'. Cũng lưu ý rằng 'np.hypot' là tốt hơn để tránh tràn hơn sqrt (tổng (x ** 2)) – Patrick

+0

@Patrick' numpy.hypot() 'chỉ hoạt động cho hai chiều, trong khi mã OP cần thiết hoạt động cho ba điểm chiều. Và liên quan đến dấu hiệu của 'delta', tốt, nếu bạn quan tâm, hãy chỉnh sửa bài đăng. :) –

0

Hãy xem Ian Ozsvalds high performance python tutorial. Nó chứa rất nhiều gợi ý về nơi bạn có thể tiếp theo.

Bao gồm:

  • vector hóa
  • cython
  • PyPy
  • numexpr
  • pycuda
  • multiprocesing
  • python song song
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)) 

Đây ab là danh sách các vectơ bạn muốn để tính toán khoảng cách giữa và bounds là ranh giới của không gian (vì vậy ở đây cả ba chiều đi 0-10 và sau đó quấn). Nó tính toán khoảng cách giữa a[0]b[0], a[1]b[1], v.v.

tôi chắc chắn rằng các chuyên gia NumPy có thể làm tốt hơn, nhưng điều này có lẽ sẽ là một thứ tự cường độ nhanh hơn so với những gì bạn đang làm, vì hầu hết các công việc đang thực hiện trong C.

+0

Tôi cũng đã thử phương pháp này. Nó cũng dẫn đến tăng cường 5 lần trong mã. Thật không may, tôi chỉ có thể kiểm tra 1 câu trả lời là câu trả lời đúng:/ – johnjax

+0

@johnjax Đối với những gì nó đáng giá, tôi cũng đã chấp nhận câu trả lời của Sven Marnach, tôi đã ở trong giày của bạn chưa. Nó trực tiếp áp dụng hơn tôi. –

0

Tôi thấy rằng meshgrid rất hữu ích trong việc tạo khoảng cách.Ví dụ:

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 

bây giờ tôi có một mảng (radius_squared), nơi tất cả các mục quy định các bình phương khoảng cách từ vị trí mảng [x_coord, y_coord].

Để gởi thông tư mảng, tôi có thể làm như sau:

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 

bây giờ tôi có tất cả các khoảng cách mảng circularized với toán vectơ.

Các vấn đề liên quan