Last active
June 29, 2021 12:09
-
-
Save jjerphan/4906c0a0ae35c76e83b409741fa8c13a to your computer and use it in GitHub Desktop.
Python interaction when using memory views as attributes on Cython classes
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
%%cython --annotate | |
#cython: boundscheck=False | |
#cython: wraparound=False | |
#cython: cdivision=True | |
## Adapted from sklearn.neighbors.Mahalanobis | |
# https://github.com/scikit-learn/scikit-learn/blob/main/sklearn/neighbors/_dist_metrics.pyx#L669 | |
import numpy as np | |
cimport numpy as np | |
np.import_array() | |
ctypedef np.float64_t DTYPE_t | |
ctypedef np.intp_t ITYPE_t | |
from libc.math cimport sqrt | |
DTYPE = np.float64 | |
ITYPE = np.intp | |
cdef class SnippedMahalanobisDistance(): | |
cdef: | |
DTYPE_t[:, ::1] mat | |
DTYPE_t[::1] vec | |
DTYPE_t p | |
ITYPE_t size | |
def __cinit__(self): | |
self.p = 2 | |
self.vec = np.zeros(1, dtype=DTYPE, order='c') | |
self.mat = np.zeros((1, 1), dtype=DTYPE, order='c') | |
self.size = 1 | |
def __init__(self, V=None, VI=None): | |
if VI is None: | |
if V is None: | |
raise ValueError("Must provide either V or VI " | |
"for Mahalanobis distance") | |
VI = np.linalg.inv(V) | |
if VI.ndim != 2 or VI.shape[0] != VI.shape[1]: | |
raise ValueError("V/VI must be square") | |
self.mat = np.asarray(VI, dtype=float, order='C') | |
self.size = self.mat.shape[0] | |
# we need vec as a work buffer | |
self.vec = np.zeros(self.size, dtype=DTYPE) | |
cdef inline DTYPE_t rdist(self, | |
const DTYPE_t* x1, | |
const DTYPE_t* x2, | |
ITYPE_t size) nogil except -1: | |
cdef DTYPE_t tmp, d = 0 | |
cdef np.intp_t i, j | |
# compute (x1 - x2).T * VI * (x1 - x2) | |
# There is interaction with CPython from here... | |
for i in range(size): | |
self.vec[i] = x1[i] - x2[i] | |
for i in range(size): | |
tmp = 0 | |
for j in range(size): | |
tmp += self.mat[i, j] * self.vec[j] | |
d += tmp * self.vec[i] | |
# ... to there | |
return d | |
cdef inline DTYPE_t snipped_mahalanobis(self, | |
DTYPE_t[:, ::1] mat, | |
DTYPE_t[::1] vec, | |
const DTYPE_t* x1, | |
const DTYPE_t* x2, | |
ITYPE_t size) nogil except -1: | |
# A variant which does not have any interaction | |
# with CPython | |
cdef DTYPE_t tmp, d = 0 | |
cdef np.intp_t i, j | |
# compute (x1 - x2).T * VI * (x1 - x2) | |
for i in range(size): | |
vec[i] = x1[i] - x2[i] | |
for i in range(size): | |
tmp = 0 | |
for j in range(size): | |
tmp += mat[i, j] * vec[j] | |
d += tmp * vec[i] | |
return d |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Generated code for
SnippedMahalanobisDistance.rdist
: