mirror of
https://github.com/SheffieldML/GPy.git
synced 2026-05-01 15:52:39 +02:00
114 lines
3.6 KiB
Cython
114 lines
3.6 KiB
Cython
#cython: wraparaound=False
|
|
#cython: boundscheck=False
|
|
#cython: nonecheck=False
|
|
|
|
# Copyright James Hensman and Alan Saul 2015
|
|
|
|
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(double[:, :] flat, int M):
|
|
"""take a matrix N x D and return a D X M x M array where
|
|
|
|
N = M(M+1)/2
|
|
|
|
the lower triangluar portion of the d'th slice of the result is filled by the d'th column of flat.
|
|
"""
|
|
cdef int D = flat.shape[1]
|
|
cdef int N = flat.shape[0]
|
|
cdef int count = 0
|
|
cdef double[:, :, ::1] ret = np.zeros((D, M, M))
|
|
cdef int d, m, mm
|
|
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(double[:, :, :] L):
|
|
cdef int D = L.shape[0]
|
|
cdef int M = L.shape[1]
|
|
cdef int N = M*(M+1)/2
|
|
cdef int count = 0
|
|
cdef double[:, ::1] flat = np.empty((N, D))
|
|
cdef int d, m, mm
|
|
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(double[:, :] dL, double[:, :] L):
|
|
cdef double[:, ::1] dL_dK = np.tril(dL)
|
|
cdef int N = L.shape[0]
|
|
cdef int k, j, i
|
|
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[:,::1] dL_dK = np.tril(dL)
|
|
cdef int N = L.shape[0]
|
|
cdef int k, j, i
|
|
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
|
|
|
|
cdef void chol_backprop(int N, double[:, ::1] dL, double[:, ::1] 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 - 1, N - 1] /= (2. * L[N - 1, N - 1])
|
|
for k in range(N-2, -1, -1):
|
|
n = N-k-1
|
|
cblas.dsymv(uplo='l', n=&n, alpha=&alpha, a=&dL[k + 1, k + 1], lda=&N, x=&L[k, k + 1], incx=&incx,
|
|
beta=&beta, y=&dL[k + 1, k], incy=&N)
|
|
|
|
for i in xrange(0, N - k - 1):
|
|
dL[k + 1 + i, k] -= dL[k + i+ 1, k + i + 1] * L[k, k + 1 + i]
|
|
|
|
scale = 1.0 / L[k, k]
|
|
cblas.dscal(&n, &scale , &dL[k + 1, k], &N)
|
|
|
|
dL[k, k] -= cblas.ddot(&n, &dL[k + 1, k], &N, &L[k, k], &incx)
|
|
dL[k, k] /= (2.0 * L[k, k])
|
|
|
|
def backprop_gradient_par_c(double[:, :] dL, double[:, :] L):
|
|
cdef double[:, ::1] dL_dK = np.tril(dL) # makes a copy, c-contig
|
|
cdef double[:, ::1] L_cont = np.ascontiguousarray(L)
|
|
cdef int N = L.shape[0]
|
|
with nogil:
|
|
chol_backprop(N, dL_dK, L_cont)
|
|
return np.asarray(dL_dK)
|