mirror of
https://github.com/SheffieldML/GPy.git
synced 2026-05-02 00:02:38 +02:00
ENH: implementing the Cholesky backpropagation through Scipy's BLAS
This commit is contained in:
parent
10c19d853f
commit
da7e4e3af6
5 changed files with 5156 additions and 3467 deletions
|
|
@ -7,6 +7,7 @@
|
|||
import numpy as np
|
||||
from cython.parallel import prange, parallel
|
||||
cimport numpy as np
|
||||
cimport scipy.linalg.cython_blas as cblas
|
||||
|
||||
def flat_to_triang(np.ndarray[double, ndim=2] flat, int M):
|
||||
"""take a matrix N x D and return a D X M x M array where
|
||||
|
|
@ -20,12 +21,13 @@ def flat_to_triang(np.ndarray[double, ndim=2] flat, int M):
|
|||
cdef int count = 0
|
||||
cdef np.ndarray[double, ndim=3] ret = np.zeros((D, M, M))
|
||||
cdef int d, m, mm
|
||||
for d in range(D):
|
||||
count = 0
|
||||
for m in range(M):
|
||||
for mm in range(m+1):
|
||||
ret[d, m, mm] = flat[count,d]
|
||||
count += 1
|
||||
with nogil:
|
||||
for d in range(D):
|
||||
count = 0
|
||||
for m in range(M):
|
||||
for mm in range(m+1):
|
||||
ret[d, m, mm] = flat[count,d]
|
||||
count += 1
|
||||
return ret
|
||||
|
||||
def triang_to_flat(np.ndarray[double, ndim=3] L):
|
||||
|
|
@ -33,75 +35,82 @@ def triang_to_flat(np.ndarray[double, ndim=3] L):
|
|||
cdef int M = L.shape[1]
|
||||
cdef int N = M*(M+1)/2
|
||||
cdef int count = 0
|
||||
cdef np.ndarray[double, ndim=2] flat = np.empty((N, D))
|
||||
cdef np.ndarray[double, ndim=2, mode='c'] flat = np.empty((N, D))
|
||||
cdef int d, m, mm
|
||||
for d in range(D):
|
||||
count = 0
|
||||
for m in range(M):
|
||||
for mm in range(m+1):
|
||||
flat[count,d] = L[d, m, mm]
|
||||
count += 1
|
||||
with nogil:
|
||||
for d in range(D):
|
||||
count = 0
|
||||
for m in range(M):
|
||||
for mm in range(m+1):
|
||||
flat[count,d] = L[d, m, mm]
|
||||
count += 1
|
||||
return flat
|
||||
|
||||
|
||||
def backprop_gradient(np.ndarray[double, ndim=2] dL, np.ndarray[double, ndim=2] L):
|
||||
cdef np.ndarray[double, ndim=2] dL_dK = np.tril(dL).copy()
|
||||
cdef np.ndarray[double, ndim=2, mode='c'] dL_dK = np.tril(dL).copy()
|
||||
cdef int N = L.shape[0]
|
||||
cdef int k, j, i
|
||||
for k in range(N - 1, -1, -1):
|
||||
for j in range(k + 1, N):
|
||||
for i in range(j, N):
|
||||
dL_dK[i, k] -= dL_dK[i, j] * L[j, k]
|
||||
dL_dK[j, k] -= dL_dK[i, j] * L[i, k]
|
||||
for j in range(k + 1, N):
|
||||
dL_dK[j, k] /= L[k, k]
|
||||
dL_dK[k, k] -= L[j, k] * dL_dK[j, k]
|
||||
dL_dK[k, k] /= (2. * L[k, k])
|
||||
with nogil:
|
||||
for k in range(N - 1, -1, -1):
|
||||
for j in range(k + 1, N):
|
||||
for i in range(j, N):
|
||||
dL_dK[i, k] -= dL_dK[i, j] * L[j, k]
|
||||
dL_dK[j, k] -= dL_dK[i, j] * L[i, k]
|
||||
for j in range(k + 1, N):
|
||||
dL_dK[j, k] /= L[k, k]
|
||||
dL_dK[k, k] -= L[j, k] * dL_dK[j, k]
|
||||
dL_dK[k, k] /= (2. * L[k, k])
|
||||
return dL_dK
|
||||
|
||||
def backprop_gradient_par(double[:,:] dL, double[:,:] L):
|
||||
cdef double[:,:] dL_dK = np.tril(dL).copy()
|
||||
cdef double[:,::1] dL_dK = np.tril(dL).copy()
|
||||
cdef int N = L.shape[0]
|
||||
cdef int k, j, i
|
||||
for k in range(N - 1, -1, -1):
|
||||
with nogil, parallel():
|
||||
for i in prange(k + 1, N):
|
||||
for j in range(k+1, i+1):
|
||||
dL_dK[i, k] -= dL_dK[i, j] * L[j, k]
|
||||
for j in range(i, N):
|
||||
dL_dK[i, k] -= dL_dK[j, i] * L[j, k]
|
||||
for j in range(k + 1, N):
|
||||
dL_dK[j, k] /= L[k, k]
|
||||
dL_dK[k, k] -= L[j, k] * dL_dK[j, k]
|
||||
dL_dK[k, k] /= (2. * L[k, k])
|
||||
with nogil:
|
||||
for k in range(N - 1, -1, -1):
|
||||
with parallel():
|
||||
for i in prange(k + 1, N):
|
||||
for j in range(k+1, i+1):
|
||||
dL_dK[i, k] -= dL_dK[i, j] * L[j, k]
|
||||
for j in range(i, N):
|
||||
dL_dK[i, k] -= dL_dK[j, i] * L[j, k]
|
||||
for j in range(k + 1, N):
|
||||
dL_dK[j, k] /= L[k, k]
|
||||
dL_dK[k, k] -= L[j, k] * dL_dK[j, k]
|
||||
dL_dK[k, k] /= (2. * L[k, k])
|
||||
return dL_dK
|
||||
|
||||
#here's a pure C version...
|
||||
cdef extern from "cholesky_backprop.h" nogil:
|
||||
void chol_backprop(int N, double* dL, double* L)
|
||||
cdef void chol_backprop(int N, double[:] dL, double[:] L) nogil:
|
||||
cdef int i, k, n
|
||||
|
||||
# DSYMV required constant arguments
|
||||
cdef double alpha=-1, beta=1
|
||||
cdef int incx=1
|
||||
|
||||
# DSCAL required arguments
|
||||
cdef double scale
|
||||
|
||||
dL[N*N - 1] /= (2. * L[N*N - 1])
|
||||
for k in range(N-2, -1, -1):
|
||||
n = N-k-1
|
||||
|
||||
cblas.dsymv(uplo='l', n=&n, alpha=&alpha, a=&dL[(N*(k+1) + k+1)], lda=&N, x=&L[k*N+k+1], incx=&incx,
|
||||
beta=&beta, y=&dL[N*(k+1)+k], incy=&N)
|
||||
|
||||
for i in xrange(0, N - k - 1):
|
||||
dL[N * (k + 1 + i) + k] -= dL[N * (k + 1) + k + i * (N + 1) + 1] * L[k * N + k + 1 + i]
|
||||
|
||||
scale = 1.0/L[k*N+k]
|
||||
cblas.dscal(&n, &scale , &dL[(k+1)*N+k], &N)
|
||||
|
||||
dL[k*N + k] -= cblas.ddot(&n, &dL[(k+1)*N+k], &N, &L[k*N+k+1], &incx)
|
||||
dL[k*N + k] /= (2.0 * L[k*N + k])
|
||||
|
||||
def backprop_gradient_par_c(np.ndarray[double, ndim=2] dL, np.ndarray[double, ndim=2] L):
|
||||
cdef np.ndarray[double, ndim=2] dL_dK = np.tril(dL) # makes a copy, c-contig
|
||||
cdef np.ndarray[double, ndim=2, mode='c'] dL_dK = np.tril(dL) # makes a copy, c-contig
|
||||
cdef int N = L.shape[0]
|
||||
with nogil:
|
||||
chol_backprop(N, <double*> dL_dK.data, <double*> L.data)
|
||||
chol_backprop(N, dL_dK, L)
|
||||
return dL_dK
|
||||
|
||||
cdef extern from "cholesky_backprop.h" nogil:
|
||||
void old_chol_backprop(int N, double* dL, double* L)
|
||||
|
||||
def backprop_gradient_par_c_old(np.ndarray[double, ndim=2] dL, np.ndarray[double, ndim=2] L):
|
||||
cdef np.ndarray[double, ndim=2] dL_dK = np.tril(dL) # makes a copy, c-contig
|
||||
cdef int N = L.shape[0]
|
||||
with nogil:
|
||||
old_chol_backprop(N, <double*> dL_dK.data, <double*> L.data)
|
||||
return dL_dK
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue