Mục đích của chức năng toán học này là để tính toán khoảng cách giữa hai (hoặc nhiều) cấu trúc protein sử dụng những góc nhị diện:Tối ưu hóa và tăng tốc của một hàm toán học trong python
Nó rất hữu ích trong việc cấu trúc sinh học, ví dụ. Và tôi đã mã hóa hàm này trong python bằng cách sử dụng numpy, nhưng mục đích là để có một triển khai nhanh hơn. Là tham chiếu thời gian tính toán, tôi sử dụng hàm khoảng cách euclide có sẵn trong gói tìm hiểu scikit.
Dưới đây là đoạn code tôi có cho thời điểm này:
import numpy as np
import numexpr as ne
from sklearn.metrics.pairwise import euclidean_distances
# We have 10000 structures with 100 dihedral angles
n = 10000
m = 100
# Generate some random data
c = np.random.rand(n,m)
# Generate random int number
x = np.random.randint(c.shape[0])
print c.shape, x
# First version with numpy of the dihedral_distances function
def dihedral_distances(a, b):
l = 1./a.shape[0]
return np.sqrt(l* np.sum((0.5)*(1. - np.cos(a-b)), axis=1))
# Accelerated version with numexpr
def dihedral_distances_ne(a, b):
l = 1./a.shape[0]
tmp = ne.evaluate('sum((0.5)*(1. - cos(a-b)), axis=1)')
return ne.evaluate('sqrt(l* tmp)')
# The function of reference I try to be close as possible
# in term of computation time
%timeit euclidean_distances(c[x,:], c)[0]
1000 loops, best of 3: 1.07 ms per loop
# Computation time of the first version of the dihedral_distances function
# We choose randomly 1 structure among the 10000 structures.
# And we compute the dihedral distance between this one and the others
%timeit dihedral_distances(c[x,:], c)
10 loops, best of 3: 21.5 ms per loop
# Computation time of the accelerated function with numexpr
%timeit dihedral_distances_ne(c[x,:], c)
100 loops, best of 3: 9.44 ms per loop
9,44 ms nó rất nhanh, nhưng nó rất chậm nếu bạn cần phải chạy nó một triệu lần. Bây giờ câu hỏi là, làm thế nào để làm điều đó? Bước tiếp theo là gì? Cython? PyOpenCL? Tôi có một số kinh nghiệm với PyOpenCL, tuy nhiên tôi không bao giờ viết một cái gì đó phức tạp như thế này. Tôi không biết nếu nó có thể tính toán khoảng cách dihedral trong một bước trên GPU như tôi làm với numpy và làm thế nào để tiến hành.
Cảm ơn bạn đã giúp tôi!
EDIT: Cảm ơn các bạn! Tôi hiện đang làm việc trên các giải pháp đầy đủ và một khi nó đã hoàn thành, tôi sẽ đặt mã ở đây.
Cython VERSION:
%load_ext cython
import numpy as np
np.random.seed(1234)
n = 10000
m = 100
c = np.random.rand(n,m)
x = np.random.randint(c.shape[0])
print c.shape, x
%%cython --compile-args=-fopenmp --link-args=-fopenmp --force
import numpy as np
cimport numpy as np
from libc.math cimport sqrt, cos
cimport cython
from cython.parallel cimport parallel, prange
# Define a function pointer to a metric
ctypedef double (*metric)(double[: ,::1], np.intp_t, np.intp_t)
cdef extern from "math.h" nogil:
double cos(double x)
double sqrt(double x)
@cython.boundscheck(False)
@cython.wraparound(False)
@cython.cdivision(True)
cdef double dihedral_distances(double[:, ::1] a, np.intp_t i1, np.intp_t i2):
cdef double res
cdef int m
cdef int j
res = 0.
m = a.shape[1]
for j in range(m):
res += 1. - cos(a[i1, j] - a[i2, j])
res /= 2.*m
return sqrt(res)
@cython.boundscheck(False)
@cython.wraparound(False)
@cython.cdivision(True)
cdef double dihedral_distances_p(double[:, ::1] a, np.intp_t i1, np.intp_t i2):
cdef double res
cdef int m
cdef int j
res = 0.
m = a.shape[1]
with nogil, parallel(num_threads=2):
for j in prange(m, schedule='dynamic'):
res += 1. - cos(a[i1, j] - a[i2, j])
res /= 2.*m
return sqrt(res)
@cython.boundscheck(False)
@cython.wraparound(False)
def pairwise(double[: ,::1] c not None, np.intp_t x, p = True):
cdef metric dist_func
if p:
dist_func = &dihedral_distances_p
else:
dist_func = &dihedral_distances
cdef np.intp_t i, n_structures
n_samples = c.shape[0]
cdef double[::1] res = np.empty(n_samples)
for i in range(n_samples):
res[i] = dist_func(c, x, i)
return res
%timeit pairwise(c, x, False)
100 loops, best of 3: 17 ms per loop
# Parallel version
%timeit pairwise(c, x, True)
10 loops, best of 3: 37.1 ms per loop
Vì vậy, tôi làm theo bạn link để tạo ra các phiên bản cython của quãng đường Hàm nhị diện. Chúng tôi đạt được một số tốc độ, không quá nhiều, nhưng nó vẫn còn chậm hơn so với phiên bản numexpr (17ms so với 9.44ms). Vì vậy, tôi đã cố gắng để song song chức năng bằng cách sử dụng prange và nó là tồi tệ hơn (37.1ms vs 17ms vs 9.4ms)!
Tôi có bỏ lỡ điều gì đó không?
Một cặp vợ chồng các cải tiến nhỏ là: 1) đặt '* 0,5' bên ngoài tổng; và, 2) tổng hợp 'cos' trước khi trừ đi từ' 1' (cũng sẽ chính xác hơn, vì tổng số sẽ treo quanh 1). Đối với tôi, chúng mang nó từ 25ms đến 17ms. Tôi biết bạn đang tìm kiếm nhiều hơn, nhưng đây là những gì tôi nhận được và nghĩ rằng nó có thể giúp một chút. – tom10
Việc thử nghiệm cython thật dễ dàng và có thể tăng tốc khá nhanh (YMMV, tất nhiên): https://jakevdp.github.io/blog/2012/08/08/memoryview-benchmarks/ –
@ tom10 Hiệu quả với 1) Tôi đạt được nhiều hơn 1ms nhưng 2) cho tôi một lỗi 'RuntimeWarning: giá trị không hợp lệ gặp phải trong sqrt'. – NoExiT