10

この数学関数の目的は、二面角を使用して 2 つ (またはそれ以上) のタンパク質構造間の距離を計算することです。

ここに画像の説明を入力

たとえば、構造生物学で非常に役立ちます。そして、私はすでに numpy を使用して Python でこの関数をコーディングしていますが、目標は実装を高速化することです。計算時間の基準として、scikit-learn パッケージで利用可能なユークリッド距離関数を使用します。

ここに私が今持っているコード:

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 非常に高速ですが、100 万回実行する必要がある場合は非常に遅くなります。さて、問題は、それをどのように行うかです。次のステップは何ですか?シトン?PyOpenCL? 私は PyOpenCL の経験がありますが、これほど精巧なものをコーディングしたことはありません。numpy で行っているように、GPU で二面体距離を 1 ステップで計算できるかどうか、およびどのように処理するかはわかりません。

助けてくれてありがとう!

編集:ありがとうございます!現在、完全なソリューションに取り組んでおり、完成したらコードをここに配置します。

CYTHON バージョン:

%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

したがって、リンクをたどって、二面体距離関数の cython バージョンを作成します。速度はそれほど速くはありませんが、numexpr バージョンよりはまだ遅いです (17ms 対 9.44ms)。そのため、prange を使用して関数を並列化しようとしましたが、さらに悪化しました (37.1ms 対 17ms 対 9.4ms)!

私は何かが恋しいですか?

4

2 に答える 2

3

http://pythran.readthedocs.io/を使用する場合は、numpy 実装を利用して、その場合の cython よりも優れたパフォーマンスを得ることができます。

#pythran export np_cos_norm(float[], float[])
import numpy as np
def np_cos_norm(a, b):
    val = np.sum(1. - np.cos(a-b))
    return np.sqrt(val / 2. / a.shape[0])

そしてそれをコンパイルします:

pythran fast.py

cython バージョンの平均 x2 を取得します。

使用する場合:

pythran fast.py -march=native -DUSE_BOOST_SIMD -fopenmp

少し高速に実行されるベクトル化された並列バージョンが得られます。

100000 loops, best of 3: 2.54 µs per loop
1000000 loops, best of 3: 674 ns per loop

100000 loops, best of 3: 16.9 µs per loop
100000 loops, best of 3: 4.31 µs per loop

10000 loops, best of 3: 176 µs per loop
10000 loops, best of 3: 42.9 µs per loop

(ev-br と同じテストベッドを使用)

于 2015-08-28T13:42:09.193 に答える