Der Zweck dieser mathematischen Funktion ist ein Abstand zwischen zwei (oder mehr) Proteinstrukturen mit Diederwinkeln zu berechnen:Optimierung und Beschleunigung einer mathematischen Funktion in Python
Es ist sehr nützlich in Struktur Biologie zum Beispiel. Und ich schreibe diese Funktion bereits in Python mit numpy, aber das Ziel ist eine schnellere Implementierung. Als Berechnungszeitreferenz verwende ich die euklidische Abstandsfunktion, die im scikit-learn-Paket verfügbar ist.
Hier ist der Code, den ich für den Moment haben:
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 es sehr schnell ist, aber es ist sehr langsam, wenn Sie es eine Million Mal ausgeführt werden müssen. Jetzt ist die Frage, wie man das macht? Was ist der nächste Schritt? Cython? PyOpenCL? Ich habe einige Erfahrung mit PyOpenCL, aber ich kodiere nie etwas so ausführlich wie dieses. Ich weiß nicht, ob es möglich ist, auf einem GPU die Zweiflächendistanzen in einem Schritt zu berechnen, wie ich es mit numpy mache und wie es weitergeht.
Vielen Dank für Ihre Hilfe!
EDIT: Danke Jungs! Ich arbeite gerade an der vollständigen Lösung und sobald es fertig ist, werde ich den Code hier einfügen.
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
So folge ich Ihrer link die cython Version der V-Form Entfernungen Funktion zu erstellen. Wir gewinnen etwas an Geschwindigkeit, nicht so sehr, aber es ist immer noch langsamer als die NUMEXPR-Version (17ms vs 9,44ms). Also habe ich versucht, die Funktion mit prange zu parallelisieren und es ist schlimmer (37.1ms vs 17ms vs 9.4ms)!
Vermisse ich etwas?
Ein Paar von kleinen Verbesserungen sind: 1) setzen Sie das '* 0,5 'außerhalb der Summe; und 2) summiere das "cos" vor dem Subtrahieren von "1" (was auch genauer sein wird, da die Summe um 1 hängen bleibt). Für mich bringen diese von 25ms auf 17ms. Ich weiß, dass du nach mehr suchst, aber das ist, was ich bekam und dachte, dass es ein bisschen helfen könnte. – tom10
Ausprobieren von Cython ist einfach und kann eine ganze Beschleunigung (YMMV, natürlich): https://jakevdp.github.io/blog/2012/08/08/memoryview-benchmarks/ –
@ tom10 Effektiv mit 1) Ich gewinne mehr als 1ms, aber 2) geben Sie mir einen Fehler "RuntimeWarning: ungültiger Wert in sqrt gefunden". – NoExiT